mirror of
https://github.com/valitydev/yandex-tank.git
synced 2024-11-06 02:15:22 +00:00
merge changes from master
This commit is contained in:
commit
aeba6befe7
25
README.md
25
README.md
@ -1,4 +1,4 @@
|
||||
# Yandex Tank [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/yandex/yandex-tank?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Gittask](https://gittask.com/yandex/yandex-tank.svg)](https://gittask.com/yandex/yandex-tank)
|
||||
# Yandex Tank [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/yandex/yandex-tank?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/yandex/yandex-tank.png?branch=master)](http://travis-ci.org/yandex/yandex-tank)
|
||||
|
||||
@ -18,27 +18,8 @@ Yandex.Tank is an extendable open source load testing tool for advanced linux us
|
||||
* test autostop plugin
|
||||
* customizable and extendable monitoring that works over SSH
|
||||
|
||||
## Install from PyPI
|
||||
You will need some packages that are required for building different python libraries:
|
||||
```
|
||||
libxml2-dev libxslt1-dev python-dev zlib1g-dev
|
||||
```
|
||||
You will also need a GNU make for building them. In Ubuntu you can install a ```build-essential``` package. You should also install pip if you don't have it.
|
||||
Full command for Ubuntu looks like this:
|
||||
```
|
||||
sudo apt-get install python-pip build-essential libxml2-dev libxslt1-dev python-dev zlib1g-dev
|
||||
```
|
||||
You can do similar thing for your distribution. After you've installed all the packages, it is easy to install the Tank itself:
|
||||
```
|
||||
sudo pip install yandextank
|
||||
```
|
||||
Remember that if you want to use ```phantom``` as a load generator you should install it separately. On Ubuntu you can do that by adding our PPA and installing ```phantom``` and ```phantom-ssl``` packages. On other distros you will maybe need to build it from sources.
|
||||
```
|
||||
sudo add-apt-repository ppa:yandex-load/main && sudo apt-get update
|
||||
sudo apt-get install phantom phantom-ssl
|
||||
```
|
||||
|
||||
**Report plugin** is a distinct project. You can found it [here](https://github.com/yandex-load/yatank-online).
|
||||
## Installation and configuration
|
||||
Installation at [ReadTheDocs](http://yandextank.readthedocs.org/en/latest/install.html)
|
||||
|
||||
## Get help
|
||||
Documentation at [ReadTheDocs](https://yandextank.readthedocs.org/en/latest/)
|
||||
|
@ -19,8 +19,4 @@ _yandex_tank()
|
||||
fi
|
||||
COMPREPLY=()
|
||||
} &&
|
||||
complete -o default -F _yandex_tank yandex-tank &&
|
||||
complete -o default -F _yandex_tank yandex-tank-jmeter &&
|
||||
complete -o default -F _yandex_tank yandex-tank-udp &&
|
||||
complete -o default -F _yandex_tank yandex-tank-bfg &&
|
||||
complete -o default -F _yandex_tank yandex-tank-elliptics
|
||||
complete -o default -F _yandex_tank yandex-tank
|
||||
|
@ -35,11 +35,7 @@ cp -arp Tank %{buildroot}/%{_libdir}/yandex-tank/
|
||||
cp -ap tankcore.py %{buildroot}/%{_libdir}/yandex-tank/
|
||||
cp -ap tank.py %{buildroot}/%{_libdir}/yandex-tank/
|
||||
cp -ap *.sh %{buildroot}/%{_libdir}/yandex-tank/
|
||||
ln -s %{_libdir}/yandex-tank/lunapark %{buildroot}/%{_bindir}/lunapark
|
||||
ln -s %{_libdir}/yandex-tank/tank.py %{buildroot}/%{_bindir}/yandex-tank
|
||||
ln -s %{_libdir}/yandex-tank/ab.sh %{buildroot}/%{_bindir}/yandex-tank-ab
|
||||
ln -s %{_libdir}/yandex-tank/jmeter.sh %{buildroot}/%{_bindir}/yandex-tank-jmeter
|
||||
ln -s %{_libdir}/yandex-tank/bfg.sh %{buildroot}/%{_bindir}/yandex-tank-bfg
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
@ -50,9 +46,6 @@ rm -rf %{buildroot}
|
||||
%{_sysconfdir}/bash_completion.d/yandex-tank.completion
|
||||
%{_bindir}/lunapark
|
||||
%{_bindir}/yandex-tank
|
||||
%{_bindir}/yandex-tank-ab
|
||||
%{_bindir}/yandex-tank-jmeter
|
||||
%{_bindir}/yandex-tank-bfg
|
||||
%{_libdir}/yandex-tank
|
||||
|
||||
%changelog
|
||||
|
46
debian/changelog
vendored
46
debian/changelog
vendored
@ -1,3 +1,49 @@
|
||||
yandextank (1.7.30) trusty; urgency=medium
|
||||
|
||||
* support Pandora in Loadosophia plugin
|
||||
|
||||
-- Alexey Lavrenuke (load testing) <direvius@yandex-team.ru> Thu, 21 Jan 2016 16:13:32 +0300
|
||||
|
||||
yandextank (1.7.29) trusty; urgency=medium
|
||||
|
||||
* pandora plugin https gun type fix
|
||||
* invalidate ammo cache if ammo_type changed
|
||||
* API worker [Timur Torubarov]
|
||||
|
||||
-- Alexey Lavrenuke (load testing) <direvius@yandex-team.ru> Mon, 18 Jan 2016 15:59:32 +0300
|
||||
|
||||
yandextank (1.7.28) trusty; urgency=medium
|
||||
|
||||
* add pyzmq dependency [Timur Torubarov]
|
||||
* fix re-import problem in bfg [Timur Torubarov]
|
||||
|
||||
-- Alexey Lavrenuke (load testing) <direvius@yandex-team.ru> Wed, 13 Jan 2016 19:37:37 +0300
|
||||
|
||||
yandextank (1.7.27) trusty; urgency=medium
|
||||
|
||||
* connect_time jmeter option [Ilya Krylov]
|
||||
* Fix Zombie Apocalypse (phantom zombie processes leakage) [Timur Torubarov]
|
||||
|
||||
-- Alexey Lavrenuke (load testing) <direvius@yandex-team.ru> Tue, 29 Dec 2015 13:06:57 +0300
|
||||
|
||||
yandextank (1.7.26) trusty; urgency=medium
|
||||
|
||||
* PyCrypto bug workaround
|
||||
|
||||
-- Alexey Lavrenuke (load testing) <direvius@yandex-team.ru> Wed, 16 Dec 2015 15:02:25 +0300
|
||||
|
||||
yandextank (1.7.25) trusty; urgency=medium
|
||||
|
||||
* Dependencies in debian/control
|
||||
|
||||
-- Alexey Lavrenuke (load testing) <direvius@yandex-team.ru> Wed, 16 Dec 2015 14:19:21 +0300
|
||||
|
||||
yandextank (1.7.24) trusty; urgency=medium
|
||||
|
||||
* SHA-2 workaround
|
||||
|
||||
-- Alexey Lavrenuke (load testing) <direvius@yandex-team.ru> Wed, 09 Dec 2015 14:19:21 +0300
|
||||
|
||||
yandextank (1.7.23) trusty; urgency=medium
|
||||
|
||||
* update pandora plugin
|
||||
|
2
debian/control
vendored
2
debian/control
vendored
@ -7,7 +7,7 @@ Standards-Version: 3.9.3
|
||||
|
||||
Package: yandex-tank
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, ${python:Depends}, python-psutil (>=1.2.1), phantom (>=0.14.0~pre65), phantom-ssl(>=0.14.0~pre65)
|
||||
Depends: ${misc:Depends}, ${python:Depends}, python-psutil (>=1.2.1), python-paramiko(>=1.16.0), phantom (>=0.14.0~pre65), python-requests(>=2.5.1), phantom-ssl(>=0.14.0~pre65)
|
||||
Conflicts: yandex-load-tank-base
|
||||
Description: performance measurement tool
|
||||
Find out about your app’s performance limits and bottlenecks in order
|
||||
|
@ -4,17 +4,15 @@ Advanced usage
|
||||
Command line options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are three executables in Yandex.Tank package: ``yandex-tank``,
|
||||
``yandex-tank-ab`` and ``yandex-tank-jmeter``. Last two of them just use
|
||||
different kind of load generation utilities, ``ab`` (Apache Benchmark) and
|
||||
``jmeter`` (Apache JMeter), accordingly. Command line options are common
|
||||
for all three.
|
||||
Yandex.Tank has an obviously named executable ``yandex-tank``.
|
||||
Here are available command line options:
|
||||
|
||||
- **-h, --help** - show command line options
|
||||
- **-c CONFIG, --config=CONFIG** - read options from INI file. It is possible to set multiple INI files by specifying the option serveral times. Default: ``./load.ini``
|
||||
- **-i, --ignore-lock** - ignore lock files
|
||||
- **-f, --fail-lock** - don't wait for lock file, quit if it's busy. The default behaviour is to wait for lock file to become free.
|
||||
- **-l LOG, --log=LOG** - main log file location. Default: ``./tank.log``
|
||||
- **-m, --manual-start** - tank will prepare for test and wait for Enter key to start the test.
|
||||
- **-n, --no-rc** - don't read ``/etc/yandex-tank/*.ini`` and ``~/.yandex-tank``
|
||||
- **-o OPTION, --option=OPTION** - set an option from command line. Options set in cmd line override those have been set in configuration files. Multiple times for multiple options. Format: ``<section>.<option>=value`` Example: ``yandex-tank -o "console.short_only=1" --option="phantom.force_stepping=1"``
|
||||
- **-s SCHEDULED_START, --scheduled-start=SCHEDULED_START** - run test on specified time, date format YYYY-MM-DD hh:mm:ss or hh:mm:ss
|
||||
@ -37,7 +35,7 @@ example:
|
||||
[phantom]
|
||||
address=example.com:80
|
||||
rps_schedule=const(100,60s)
|
||||
|
||||
|
||||
[autostop]
|
||||
autostop=instances(80%,10)
|
||||
|
||||
@ -160,6 +158,71 @@ there.
|
||||
Modules
|
||||
~~~~~~~
|
||||
|
||||
TankCore
|
||||
^^^^^^^
|
||||
|
||||
Core class. Represents basic steps of test execution. Simplifies plugin configuration,
|
||||
configs reading, artifacts storing. Represents parent class for modules/plugins.
|
||||
|
||||
INI file section: **[tank]**
|
||||
|
||||
Options
|
||||
'''''''
|
||||
|
||||
Basic options:
|
||||
|
||||
* **lock_dir** - directory for lockfile. Default: ``/var/lock/``
|
||||
* **plugin_<pluginname>** - path to plugin. Empty path interpreted as disable of plugin.
|
||||
* **artifacts_base_dir** - base directory for artifacts storing. Temporary artifacts files are stored here. Default: current directory
|
||||
* **artifacts_dir** - directory where to keep artifacts after test. Default: directory in ``artifacts_base_dir`` named in Date/Time format.
|
||||
* **flush_config_to** - dump configuration options after each tank step (`yandex.tank steps. sorry, russian only <http://clubs.ya.ru/yandex-tank/replies.xml?item_no=6>`_) to that file
|
||||
* **taskset_path** - path to taskset command. Default: taskset
|
||||
* **affinity** - set a yandex-tank's (python process and load generator process) CPU affinity. Example: '0-3' enabling first 4 cores, '0,1,2,16,17,18' enabling 6 cores. Default: empty
|
||||
|
||||
consoleworker - cmd-line interface
|
||||
'''''''
|
||||
|
||||
Worker class that runs and configures TankCore accepting cmdline parameters.
|
||||
Human-friendly unix-way interface for yandex-tank.
|
||||
Command-line options described above.
|
||||
|
||||
apiworker - python interface
|
||||
'''''''
|
||||
|
||||
Worker class for python. Runs and configures TankCore accepting ``dict()``.
|
||||
Python-frinedly interface for yandex-tank.
|
||||
|
||||
Usage sample:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from yandextank.api.apiworker import ApiWorker
|
||||
import logging
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
logger = logging.getLogger('')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
#not mandatory options below:
|
||||
options = dict()
|
||||
options['config'] = '/path/to/config/load.ini'
|
||||
options['manual_start'] = "1"
|
||||
options['user_options'] = [
|
||||
'phantom.ammofile=/path/to/ammofile',
|
||||
'phantom.rps_schedule=const(1,2m)',
|
||||
]
|
||||
log_filename = '/path/to/log/tank.log'
|
||||
#======================================
|
||||
|
||||
apiworker = ApiWorker()
|
||||
apiworker.init_logging(log_filename)
|
||||
try:
|
||||
apiworker.configure(options)
|
||||
apiworker.perform_test()
|
||||
except Exception, ex:
|
||||
logger.error('Error trying to perform a test: %s', ex)
|
||||
|
||||
Phantom
|
||||
^^^^^^^
|
||||
|
||||
@ -281,7 +344,15 @@ Options that apply only for main section: buffered_seconds, writelog, phantom_mo
|
||||
|
||||
JMeter
|
||||
^^^^^^
|
||||
JMeter module uses JMeter as a load generator
|
||||
JMeter module uses JMeter as a load generator. To enable it, disable phantom first (unless you really want to keep it active alongside at your own risk), enable JMeter plugin and then specify the parameters for JMeter:
|
||||
|
||||
::
|
||||
|
||||
[tank]
|
||||
; Disable phantom:
|
||||
plugin_phantom=
|
||||
; Enable JMeter instead:
|
||||
plugin_jmeter=yandextank.plugins.JMeter
|
||||
|
||||
INI file section: **[jmeter]**
|
||||
|
||||
@ -291,6 +362,7 @@ Options
|
||||
* **args** - additional commandline arguments for JMeter
|
||||
* **jmeter_path** - path to JMeter, allows to use alternative JMeter installation. Default: jmeter
|
||||
* **buffered_seconds** - amount of seconds to which delay aggregator, to be sure that everything were read from jmeter's results file
|
||||
* **connect_time** - it sets jmeter.save.saveservice.connect_time=false if the value is '0' or empty string, jmeter.save.saveservice.connect_time=true in any other cases, empty string by default
|
||||
* **all other options in the section** - they will be passed as User Defined Variables to JMeter
|
||||
|
||||
Artifacts
|
||||
@ -303,7 +375,7 @@ Artifacts
|
||||
BFG
|
||||
^^^
|
||||
(`What is BFG <http://en.wikipedia.org/wiki/BFG_(weapon)>`_)
|
||||
BFG is a generic gun that is able to use different kinds of cannons to shoot. To enable it, disable phantom first, enable BFG plugin and then specify the parameters for BFG and for the cannon you select. For example, if you want to kill an SQL db:
|
||||
BFG is a generic gun that is able to use different kinds of cannons to shoot. To enable it, disable phantom first (unless you really want to keep it active alongside at your own risk), enable BFG plugin and then specify the parameters for BFG and for the cannon you select. For example, if you want to kill an SQL db:
|
||||
|
||||
::
|
||||
|
||||
@ -383,6 +455,70 @@ INI file section: **[custom_gun]**
|
||||
* **module_path** - path to your module
|
||||
* **module_name** - module name, has to provide function shoot, which will be called by bfg's threads to fullfill rps_schedule
|
||||
|
||||
Sample custom gun module:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# coding=utf-8
|
||||
import sys
|
||||
import os
|
||||
from Queue import Queue
|
||||
|
||||
import socket
|
||||
import logging
|
||||
import time
|
||||
|
||||
from contextlib import contextmanager
|
||||
from collections import namedtuple
|
||||
|
||||
Sample = namedtuple(
|
||||
'Sample', 'marker,threads,overallRT,httpCode,netCode,sent,received,connect,send,latency,receive,accuracy')
|
||||
|
||||
@contextmanager
|
||||
def measure(marker, queue):
|
||||
start_ms = time.time()
|
||||
|
||||
resp_code = 0
|
||||
try:
|
||||
yield
|
||||
except Exception as e:
|
||||
print marker, e
|
||||
resp_code = 110
|
||||
|
||||
response_time = int((time.time() - start_ms) * 1000)
|
||||
|
||||
data_item = Sample(
|
||||
marker, # tag
|
||||
1, # threadsв
|
||||
rt_time, # overallRT
|
||||
200, # httCode
|
||||
resp_code, # netCode
|
||||
0, # sent
|
||||
0, # received
|
||||
connect_time, # connect
|
||||
0, # send
|
||||
latency_time, # latency
|
||||
0, # receive
|
||||
0, # accuracy
|
||||
)
|
||||
queue.put((int(time.time()), data_item), timeout=5)
|
||||
if resp_code != 0:
|
||||
raise RuntimeError
|
||||
|
||||
|
||||
def shoot(missile, marker, results):
|
||||
sock = socket.socket()
|
||||
try:
|
||||
#prepare actions
|
||||
<...some work...>
|
||||
#test logic with metrics counting
|
||||
with measure("markerOfRequest", results):
|
||||
<...some useful work...>
|
||||
except RuntimeError as e:
|
||||
print "Scenario %s failed with %s" % (marker, e)
|
||||
finally:
|
||||
<...some finishing work...>
|
||||
|
||||
Pandora
|
||||
^^^^^^^
|
||||
`Pandora <https://github.com/yandex/pandora>`_ is a load generator written in Go. For now it supports only SPDY/3 and HTTP(S). Plugins for other protocols
|
||||
@ -390,7 +526,7 @@ Pandora
|
||||
|
||||
First of all you'll need to obtain a binary of pandora and place it somewhere on your machine.
|
||||
By default, Yandex.Tank will try to just run ``pandora`` (or you could specify a path to binary in ``pandora_cmd``).
|
||||
Disable phantom first, enable Pandora plugin and then specify the parameters.
|
||||
Disable phantom first (unless you really want to keep it active alongside at your own risk), enable Pandora plugin and then specify the parameters.
|
||||
|
||||
::
|
||||
|
||||
@ -469,7 +605,7 @@ to stop eventually. Example:
|
||||
|
||||
loop = 1000000
|
||||
startup_schedule = periodic(2, 10, 50)
|
||||
user_schedule = unlimited
|
||||
user_schedule = unlimited()
|
||||
shared_schedule = 0
|
||||
|
||||
Start 2 users every 10 seconds. Every user will shoot without any limits (next request is sended
|
||||
@ -499,6 +635,7 @@ Basic criteria types:
|
||||
* **instances** - available when phantom module is included. Stop the test if instance count is larger then specified value. Example: ``instances(80%, 30) instances(50,1m)``. Exit code - 24
|
||||
* **metric_lower** and **metric_higher** - stop test if monitored metrics are lower/higher than specified for time period. Example: metric_lower(127.0.0.1,Memory_free,500,10). Exit code - 31 and 32. **Note**: metric names (except customs) are written with underline. For hostnames masks are allowed (i.e target-\*.load.net)
|
||||
* **steady_cumulative** - Stops the test if cumulative percentiles does not change for specified interval. Example: ``steady_cumulative(1m)``. Exit code - 33
|
||||
* **limit** - Will stop test after specified period of time. Example: ``limit(1m)``.
|
||||
|
||||
Basic criteria aren't aggregated, they are tested for each second in specified period. For example autostop=time(50,15) means "stop if average responce time for every second in 15s interval is higher than 50ms"
|
||||
|
||||
@ -513,7 +650,41 @@ Advanced criteria types:
|
||||
|
||||
Graphite
|
||||
^^^^^^^^
|
||||
Graphite plugin uploads data to `Graphite <http://graphite.readthedocs.org/en/0.9.12/index.html>`_ monitoring tool.
|
||||
Graphite plugin uploads data to `Graphite <http://graphite.readthedocs.org/en/0.9.12/index.html>`_ monitoring tool. Config file section: ```[graphite]```
|
||||
|
||||
Options
|
||||
'''''''
|
||||
|
||||
* **address** - graphite server
|
||||
* **port** - graphite backend port (where to send data), default: 2003
|
||||
* **web_port** - graphite frontend port, default: 8080
|
||||
* **template** - template file. Default: Tank/Plugins/graphite.tpl
|
||||
|
||||
InfluxDB
|
||||
^^^^^^^^
|
||||
Influx uplink plugin uploads data to `InfluxDB <https://influxdata.com>`_ storage.
|
||||
Different tests will be tagged with unique IDs.
|
||||
|
||||
Configuration:
|
||||
|
||||
::
|
||||
|
||||
[tank]
|
||||
; Enable InfluxDB plugin:
|
||||
plugin_influx=yandextank.plugins.InfluxUplink
|
||||
|
||||
[influx]
|
||||
; Tank name (to distinguish data from different tanks):
|
||||
tank_tag = MyTank
|
||||
; Address and of InfluxDB instance:
|
||||
address = example.org
|
||||
port = 8086
|
||||
|
||||
; If you have grafana connected to your InfluxDB, you
|
||||
; can specify grafana parameters and tank will generate
|
||||
; a link to your test:
|
||||
grafana_root = http://example.org/grafana/
|
||||
grafana_dashboard=tank-dashboard
|
||||
|
||||
Options
|
||||
'''''''
|
||||
@ -742,6 +913,17 @@ Apache Benchmark load generator module. As the ab utility writes results
|
||||
to file only after the test is finished, Yandex.Tank is unable to show
|
||||
the on-line statistics for the tests with ab. The data are reviewed
|
||||
after the test.
|
||||
To enable it, disable phantom first (unless you really want to keep it active alongside at your own risk), enable AB plugin and then specify
|
||||
the parameters for AB:
|
||||
|
||||
::
|
||||
|
||||
[tank]
|
||||
; Disable phantom:
|
||||
plugin_phantom=
|
||||
; Enable AB module instead:
|
||||
plugin_ab=yandextank.plugins.ApacheBenchmark
|
||||
|
||||
|
||||
INI file section: **[ab]**
|
||||
|
||||
@ -783,37 +965,6 @@ Options
|
||||
* **pass** - list of acceptable codes, delimiter - whitespace. Default: empty, no check is performed.
|
||||
* **fail_code** - exit code when check fails, integer number. Default: 10
|
||||
|
||||
Web Online
|
||||
^^^^^^^^^^
|
||||
|
||||
Module starts local web sever that shows online graphics. Enabled by ``plugin_web=Tank/Plugins/WebOnline.py`` in ``[tank]`` section.
|
||||
|
||||
INI file section: **[web]**
|
||||
|
||||
Options
|
||||
'''''''
|
||||
|
||||
* **port** - a port to bind to on localhost. Default: 8080
|
||||
* **interval** - graphics' interval that will be shown, in seconds. Default: 60 seconds
|
||||
* **redirect** - address where to redirect browser after test stop.
|
||||
* **manual_stop** - flag 0/1. If '1' then webserver will wait for key press from keyboard to exit
|
||||
|
||||
Yandex.Tank kernel
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Python-object, that loads and execs tank modules.
|
||||
|
||||
INI file section: **[tank]**
|
||||
|
||||
Options
|
||||
'''''''
|
||||
|
||||
* **artifacts_base_dir** - base directory for artifacts storing. Temporary artifacts files are stored here. Default: current directory
|
||||
* **artifacts_dir** - directory where to keep artifacts after test. Default: directory in ``artifacts_base_dir`` named in Date/Time format.
|
||||
* **flush_config_to** - dump configuration options after each tank step (`yandex.tank steps. sorry, russian only <http://clubs.ya.ru/yandex-tank/replies.xml?item_no=6>`_) to that file
|
||||
* **taskset_path** - path to taskset command. Default: taskset
|
||||
* **affinity** - set a yandex-tank's (python process and load generator process) CPU affinity. Example: '0-3' enabling first 4 cores, '0,1,2,16,17,18' enabling 6 cores. Default: empty
|
||||
|
||||
Tips&Tricks
|
||||
^^^^^^^^^^^
|
||||
|
||||
@ -829,7 +980,7 @@ Options
|
||||
Sources
|
||||
~~~~~~~
|
||||
|
||||
Yandex.Tank sources ((https://github.com/yandex-load/yandex-tank here)).
|
||||
Yandex.Tank sources ((https://github.com/yandex/yandex-tank here)).
|
||||
|
||||
load.ini example
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
@ -8,7 +8,8 @@ Welcome to Yandex.Tank's documentation!
|
||||
:Homepage: `Yandex.Tank Homepage on Github
|
||||
<https://github.com/yandex-load/yandex-tank>`_
|
||||
:Download: `Launchpad PPA (ubuntu packages)
|
||||
<https://launchpad.net/~yandex-load/+archive/main>`_
|
||||
<https://launchpad.net/~yandex-load/+archive/main>`_ `Pypi
|
||||
<https://pypi.python.org/pypi/yandextank/>`_
|
||||
:Documentation: `PDF Documentation
|
||||
<https://media.readthedocs.org/pdf/yandextank/latest/yandextank.pdf>`_
|
||||
:License: `GNU LGPLv3
|
||||
@ -36,4 +37,4 @@ Indices and tables
|
||||
|
||||
.. image::
|
||||
http://mc.yandex.ru/watch/23073253
|
||||
:align: right
|
||||
:align: right
|
||||
|
@ -1,8 +1,38 @@
|
||||
Installation and Configuration
|
||||
------------------------------
|
||||
|
||||
Installation
|
||||
Installation, from PyPi
|
||||
~~~~~~~~~~~~
|
||||
You will need some packages that are required for building different python libraries:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
libxml2-dev libxslt1-dev python-dev zlib1g-dev
|
||||
|
||||
You will also need a GNU make for building them. In Ubuntu you can install a build-essential package. You should also install pip if you don't have it. Full command for Ubuntu looks like this:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt-get install python-pip build-essential libxml2-dev libxslt1-dev python-dev zlib1g-dev
|
||||
|
||||
You can do similar thing for your distribution. After you've installed all the packages, it is easy to install the Tank itself:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo pip install yandextank
|
||||
|
||||
Remember that if you want to use phantom as a load generator you should install it separately. On Ubuntu you can do that by adding our PPA and installing phantom and phantom-ssl packages. On other distros you will maybe need to build it from sources.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo add-apt-repository ppa:yandex-load/main && sudo apt-get update
|
||||
sudo apt-get install phantom phantom-ssl
|
||||
|
||||
Report plugin is a distinct project. You can found it `here via github <https://github.com/yandex-load/yatank-online>>`_
|
||||
|
||||
|
||||
Installation, .deb packages
|
||||
~~~~~~~~~~~
|
||||
|
||||
You should add proper repositories on Debian-based environment.
|
||||
|
||||
@ -10,8 +40,8 @@ For instance, add following repos to ``sources.list`` :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
deb http://ppa.launchpad.net/yandex-load/main/ubuntu precise main
|
||||
deb-src http://ppa.launchpad.net/yandex-load/main/ubuntu precise main
|
||||
deb http://ppa.launchpad.net/yandex-load/main/ubuntu trusty main
|
||||
deb-src http://ppa.launchpad.net/yandex-load/main/ubuntu trusty main
|
||||
|
||||
or this way
|
||||
|
||||
@ -21,11 +51,11 @@ or this way
|
||||
sudo apt-get install software-properties-common
|
||||
sudo add-apt-repository ppa:yandex-load/main
|
||||
|
||||
Then update package list and install ``yandex-load-tank-base`` package:
|
||||
Then update package list and install ``yandex-tank`` package:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt-get update && sudo apt-get install yandex-load-tank-base
|
||||
sudo apt-get update && sudo apt-get install yandex-tank
|
||||
|
||||
For mild load tests (less then 1000rps) an average laptop with 64bit
|
||||
Ubuntu (10.04/.../13.10) would be sufficient. The tank could be easily
|
||||
|
@ -9,6 +9,9 @@ See also
|
||||
Evgeniy Mamchits' `phantom <https://github.com/mamchits/phantom>`_ -
|
||||
Phantom scalable IO Engine
|
||||
|
||||
Alexey Lavrenuke's `pandora <https://github.com/yandex/pandora>`_ -
|
||||
A load generator in Go language
|
||||
|
||||
Gregory Komissarov's
|
||||
`firebat <https://github.com/greggyNapalm/firebat-console>`_ - test tool
|
||||
based on Phantom
|
||||
|
@ -4,6 +4,8 @@ Usage
|
||||
So, you've installed Yandex.Tank to a proper machine, it is close to target,
|
||||
access is permitted and server is tuned. How to make a test?
|
||||
|
||||
This guide is for ``phantom`` load generator.
|
||||
|
||||
First Steps
|
||||
~~~~~~~~~~~
|
||||
|
||||
@ -24,9 +26,10 @@ values, step - increment value, dur - step duration.
|
||||
2. ``line (a,b,dur)`` makes linear load, where ``a,b`` are start/end load, ``dur``
|
||||
- the time for linear load increase from a to b.
|
||||
|
||||
3. ``const (load,dur)`` makes constant load. ``load`` - rps amount, ``dur`` - load duration. You can set
|
||||
fractional load like this: ``line(1.1, 2.5, 10)`` -- from 1.1rps to 2.5 for 10 seconds. Note: ``const(0, 10)`` - 0 rps for 10 seconds, in fact 10s pause
|
||||
in a test.
|
||||
3. ``const (load,dur)`` makes constant load. ``load`` - rps amount, ``dur``
|
||||
- load duration. You can set fractional load like this: ``line(1.1, 2.5, 10)``
|
||||
-- from 1.1rps to 2.5 for 10 seconds. Note: ``const(0, 10)`` - 0 rps for 10 seconds,
|
||||
in fact 10s pause in a test.
|
||||
|
||||
``step`` and ``line`` could be used with increasing and decreasing
|
||||
intensity:
|
||||
@ -39,7 +42,9 @@ intensity:
|
||||
minutes ``line(1, 100, 10m)`` - linear load from 1 to 100 rps, duration
|
||||
- 10 minutes
|
||||
|
||||
You can specify complex load schemes using those primitives, for example: ``rps_schedule=line(1,10,10m) const(10,10m)`` - linear load from 1 to 10, duration 10 mins and then 10 mins of 10 RPS constant load.
|
||||
You can specify complex load schemes using those primitives,
|
||||
for example: ``rps_schedule=line(1,10,10m) const(10,10m)``
|
||||
- linear load from 1 to 10, duration 10 mins and then 10 mins of 10 RPS constant load.
|
||||
|
||||
Time duration could be defined in seconds, minutes (m) and hours (h).
|
||||
For example: ``27h103m645``
|
||||
@ -60,13 +65,29 @@ Voilà, Yandex.Tank setup is done.
|
||||
Preparing requests
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are several ways to set up requests: Access mode, URI-style and request-style.
|
||||
Regardless of the chosen format, resulted file with requests could be gzipped - tank supports archived ammo files
|
||||
There are several ways to set up requests: Access mode, URI-style, URI+POST and request-style.
|
||||
Regardless of the chosen format, resulted file with requests could be gzipped - tank supports
|
||||
archived ammo files.
|
||||
|
||||
To specify external ammo file use ``ammofile`` option. You can specify URL to ammofile, http(s).
|
||||
Small ammofiles (~<100MB) will be downloaded as is, to directory ``/tmp/<hash>``,
|
||||
large files will be readed from stream.
|
||||
|
||||
::
|
||||
|
||||
[phantom]
|
||||
address=203.0.113.1 ; Target's address
|
||||
ammofile=https://yourhost.tld/path/to/ammofile.txt
|
||||
|
||||
If ammo type is uri-style or request-style, tank will try to guess it.
|
||||
|
||||
Use ``ammo_type`` option to explicitly specify ammo format. Don't forget to change ``ammo_type`` option
|
||||
if you switch format of your ammo, otherwise you might get errors.
|
||||
|
||||
Access mode
|
||||
''''''''''''
|
||||
You can use access.log file from your webserver as a source of requests.
|
||||
Just add to load.ini options `ammo_type=access` and `ammofile=/tmp/access.log`
|
||||
Just add to load.ini options ``ammo_type=access`` and ``ammofile=/tmp/access.log``
|
||||
where /tmp/access.log is a path to access.log file.
|
||||
|
||||
::
|
||||
@ -104,6 +125,7 @@ Update configuration file with HTTP headers and URIs:
|
||||
/sdfbv/swdfvs/ssfsf
|
||||
|
||||
Parameter ``uris`` contains uri, which should be used for requests generation.
|
||||
Pay attention to sample above, because whitespaces in ``uris`` and ``headers`` options are important.
|
||||
|
||||
URI-style, URIs in file
|
||||
'''''''''''''''''''''''
|
||||
@ -124,8 +146,10 @@ Create a file with declared requests: **ammo.txt**
|
||||
File consist of list of URIs and headers to be added to every request defined below.
|
||||
Every URI must begin from a new line, with leading ``/``.
|
||||
Each line that begins from ``[`` is considered as a header.
|
||||
Headers could be (re)defined in the middle of URIs, as in sample above. I.e request ``/buy/?rt=0&station_to=7&station_from=9`` will be sent with ``Cookies: test``, not ``Cookies: None``.
|
||||
Request may be marked by tag, you can specify it with whitespace following URI.
|
||||
Headers could be (re)defined in the middle of URIs, as in sample above.
|
||||
I.e request ``/buy/?rt=0&station_to=7&station_from=9`` will be sent
|
||||
with ``Cookies: test``, not ``Cookies: None``. Request may be marked by tag,
|
||||
you can specify it with whitespace following URI.
|
||||
|
||||
URI+POST-style
|
||||
''''''''''''''
|
||||
@ -172,7 +196,10 @@ is:
|
||||
|
||||
where ``size_of_request`` – request size in bytes. '\r\n' symbols after
|
||||
``body`` are ignored and not sent anywhere, but it is required to
|
||||
include them in a file after each request. '\r' is also required.
|
||||
include them in a file after each request. Pay attention to the sample above
|
||||
because '\r' symbols are strictly required.
|
||||
|
||||
Parameter ``ammo_type`` is unnecessary, request-style is default ammo type.
|
||||
|
||||
**sample GET requests (null body)**
|
||||
|
||||
@ -330,6 +357,9 @@ During test execution you'll see HTTP and net errors, answer times
|
||||
distribution, progressbar and other interesting data. At the same time
|
||||
file ``phout.txt`` is being written, which could be analyzed later.
|
||||
|
||||
If you need more human-readable report, you can try Report plugin,
|
||||
You can found it `here <https://github.com/yandex-load/yatank-online>`_
|
||||
|
||||
Tags
|
||||
~~~~
|
||||
|
||||
@ -354,6 +384,7 @@ requests and tags:
|
||||
User-Agent: xxx (shell 1)
|
||||
|
||||
``good``, ``bad`` and ``unknown`` here are the tags.
|
||||
|
||||
**RESTRICTION: utf-8 symbols only**
|
||||
|
||||
SSL
|
||||
@ -422,9 +453,8 @@ Logging
|
||||
Looking into target's answers is quite useful in debugging. For doing
|
||||
that add ``writelog = 1`` to ``load.ini``.
|
||||
|
||||
**ATTENTION: Writing answers on
|
||||
high load leads to intensive disk i/o usage and can affect test
|
||||
accuracy.**
|
||||
**ATTENTION: Writing answers on high load leads to intensive disk i/o
|
||||
usage and can affect test accuracy.**
|
||||
|
||||
Log format:
|
||||
|
||||
@ -502,7 +532,7 @@ installed on your Yandex.Tank system.
|
||||
Graph and statistics
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Use included charting tool that runs as a webservice on localhost
|
||||
Use `Report plugin <https://github.com/yandex-load/yatank-online>`_
|
||||
OR
|
||||
use your favorite stats packet, R, for example.
|
||||
|
||||
@ -521,6 +551,8 @@ parameter like this:
|
||||
[aggregator]
|
||||
time_periods = 10 45 50 100 150 300 500 1s 1500 2s 3s 10s ; the last value - 10s is considered as connect timeout.
|
||||
|
||||
According to this "buckets", tanks' aggregator will aggregate test results.
|
||||
|
||||
Thread limit
|
||||
~~~~~~~~~~~~
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
#! /bin/sh
|
||||
yandex-tank -o "tank.plugin_phantom=" -o "tank.plugin_ab=Tank/Plugins/ApacheBenchmark.py" "$@"
|
@ -1,2 +0,0 @@
|
||||
#! /bin/sh
|
||||
yandex-tank -o "tank.plugin_phantom=" -o "tank.plugin_bfg=Tank/Plugins/BFG.py" "$@"
|
@ -1,2 +0,0 @@
|
||||
#! /bin/sh
|
||||
yandex-tank -o "tank.plugin_phantom=" -o "tank.plugin_jmeter=Tank/Plugins/JMeter.py" "$@"
|
6
setup.py
6
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='yandextank',
|
||||
version='1.7.23',
|
||||
version='1.7.30',
|
||||
description='a performance measurement tool',
|
||||
longer_description='''
|
||||
Yandex.Tank is a performance measurement and load testing automatization tool.
|
||||
@ -18,9 +18,10 @@ analytic tools for the results they produce.
|
||||
'psutil>=1.2.1',
|
||||
'ipaddr',
|
||||
'progressbar',
|
||||
'requests',
|
||||
'requests>=2.5.1',
|
||||
'paramiko>=1.16.0',
|
||||
'pandas',
|
||||
'pyzmq',
|
||||
],
|
||||
license='LGPLv2',
|
||||
classifiers=[
|
||||
@ -45,6 +46,7 @@ analytic tools for the results they produce.
|
||||
},
|
||||
package_data={
|
||||
'yandextank.core': ['config/*'],
|
||||
'yandextank.api': ['config/*'],
|
||||
'yandextank.plugins.GraphiteUploader': ['config/*'],
|
||||
'yandextank.plugins.JMeter': ['config/*'],
|
||||
'yandextank.plugins.Monitoring': ['config/*'],
|
||||
|
@ -21,7 +21,11 @@ class PhantomConfigTestCase(TankTestCase):
|
||||
|
||||
foo = PhantomConfig(core)
|
||||
foo.read_config()
|
||||
foo.compose_config()
|
||||
config = foo.compose_config()
|
||||
conf_str = open(config).read()
|
||||
logging.info(conf_str)
|
||||
self.assertEquals(conf_str.count("io_benchmark_method_stream_transport_ssl"), 0)
|
||||
|
||||
|
||||
def test_double(self):
|
||||
core = self.get_core()
|
||||
@ -43,6 +47,8 @@ class PhantomConfigTestCase(TankTestCase):
|
||||
self.assertEquals(conf_str.count("benchmark_io "), 2)
|
||||
self.assertEquals(conf_str.count("benchmark_io1 "), 2)
|
||||
self.assertEquals(conf_str.count("benchmark_io2 "), 2)
|
||||
self.assertEquals(conf_str.count("io_benchmark_method_stream_transport_ssl"), 1)
|
||||
|
||||
|
||||
conf_str = open(config).read()
|
||||
logging.info(conf_str)
|
||||
|
4
yandextank/api/__init__.py
Normal file
4
yandextank/api/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
'''
|
||||
Package contains all tank tool core code
|
||||
'''
|
||||
from apiworker import *
|
169
yandextank/api/apiworker.py
Normal file
169
yandextank/api/apiworker.py
Normal file
@ -0,0 +1,169 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Provides class to run TankCore from python """
|
||||
from yandextank.core import tankcore
|
||||
import logging
|
||||
from pkg_resources import resource_filename
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
|
||||
class ApiWorker:
|
||||
""" Worker class that runs tank core via python """
|
||||
def __init__(self):
|
||||
self.core = tankcore.TankCore()
|
||||
self.baseconfigs_location = '/etc/yandex-tank'
|
||||
self.log = logging.getLogger(__name__)
|
||||
|
||||
def init_logging(self, log_filename="tank.log"):
|
||||
""" Set up logging """
|
||||
logger = logging.getLogger('')
|
||||
self.log_filename = log_filename
|
||||
self.core.add_artifact_file(self.log_filename)
|
||||
|
||||
file_handler = logging.FileHandler(self.log_filename)
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
file_handler.setFormatter(
|
||||
logging.Formatter(
|
||||
"%(asctime)s [%(levelname)s] %(name)s %(message)s"))
|
||||
logger.addHandler(file_handler)
|
||||
console_handler = logging.StreamHandler(sys.stdout)
|
||||
stderr_hdl = logging.StreamHandler(sys.stderr)
|
||||
|
||||
fmt_verbose = logging.Formatter(
|
||||
"%(asctime)s [%(levelname)s] %(name)s %(message)s")
|
||||
fmt_regular = logging.Formatter(
|
||||
"%(asctime)s %(levelname)s: %(message)s", "%H:%M:%S")
|
||||
|
||||
console_handler.setLevel(logging.INFO)
|
||||
console_handler.setFormatter(fmt_regular)
|
||||
stderr_hdl.setFormatter(fmt_regular)
|
||||
|
||||
f_err = SingleLevelFilter(logging.ERROR, True)
|
||||
f_warn = SingleLevelFilter(logging.WARNING, True)
|
||||
f_crit = SingleLevelFilter(logging.CRITICAL, True)
|
||||
console_handler.addFilter(f_err)
|
||||
console_handler.addFilter(f_warn)
|
||||
console_handler.addFilter(f_crit)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
f_info = SingleLevelFilter(logging.INFO, True)
|
||||
f_debug = SingleLevelFilter(logging.DEBUG, True)
|
||||
stderr_hdl.addFilter(f_info)
|
||||
stderr_hdl.addFilter(f_debug)
|
||||
logger.addHandler(stderr_hdl)
|
||||
|
||||
def __add_user_options(self):
|
||||
""" override config options with user specified options"""
|
||||
if self.options.get('user_options', None):
|
||||
self.core.apply_shorthand_options(self.options['user_options'])
|
||||
|
||||
def configure(self, options):
|
||||
""" Make preparations before running Tank """
|
||||
self.options = options
|
||||
if self.options.get('lock_dir', None):
|
||||
self.core.set_option(
|
||||
self.core.SECTION, "lock_dir", self.options['lock_dir'])
|
||||
|
||||
while True:
|
||||
try:
|
||||
self.core.get_lock(self.options.get('ignore_lock', None))
|
||||
break
|
||||
except Exception, exc:
|
||||
if self.options.get('lock_fail', None):
|
||||
raise RuntimeError("Lock file present, cannot continue")
|
||||
self.log.info("Couldn't get lock. Will retry in 5 seconds... (%s)", str(exc))
|
||||
time.sleep(5)
|
||||
|
||||
configs = self.get_default_configs()
|
||||
if self.options.get('config', None):
|
||||
configs.append(self.options['config'])
|
||||
self.core.load_configs(configs)
|
||||
self.__add_user_options()
|
||||
self.core.load_plugins()
|
||||
|
||||
if self.options.get('ignore_lock', None):
|
||||
self.core.set_option(self.core.SECTION, self.IGNORE_LOCKS, "1")
|
||||
|
||||
def perform_test(self):
|
||||
""" Run the test and wait for finish """
|
||||
self.log.info("Performing test...")
|
||||
retcode = 1
|
||||
try:
|
||||
self.core.plugins_configure()
|
||||
self.core.plugins_prepare_test()
|
||||
if self.options.get('manual_start', None):
|
||||
self.log.info("Manual start option specified, waiting for user actions")
|
||||
raw_input("Press Enter key to start test")
|
||||
|
||||
self.core.plugins_start_test()
|
||||
retcode = self.core.wait_for_finish()
|
||||
retcode = self.core.plugins_end_test(retcode)
|
||||
retcode = self.core.plugins_post_process(retcode)
|
||||
except KeyboardInterrupt as ex:
|
||||
self.log.info(
|
||||
"Do not press Ctrl+C again, the test will be broken otherwise")
|
||||
self.log.debug(
|
||||
"Caught KeyboardInterrupt: %s", traceback.format_exc(ex))
|
||||
try:
|
||||
retcode = self.__graceful_shutdown()
|
||||
except KeyboardInterrupt as ex:
|
||||
self.log.debug(
|
||||
"Caught KeyboardInterrupt again: %s", traceback.format_exc(ex))
|
||||
self.log.info(
|
||||
"User insists on exiting, aborting graceful shutdown...")
|
||||
retcode = 1
|
||||
|
||||
except Exception as ex:
|
||||
self.log.info("Exception: %s", traceback.format_exc(ex))
|
||||
self.log.error("%s", ex)
|
||||
retcode = self.__graceful_shutdown()
|
||||
self.core.release_lock()
|
||||
|
||||
self.log.info("Done performing test with code %s", retcode)
|
||||
return retcode
|
||||
|
||||
def get_default_configs(self):
|
||||
""" returns default configs list, from /etc, home dir and package_data"""
|
||||
# initialize basic defaults
|
||||
configs = [resource_filename(__name__, 'config/00-base.ini')]
|
||||
try:
|
||||
conf_files = os.listdir(self.baseconfigs_location)
|
||||
conf_files.sort()
|
||||
for filename in conf_files:
|
||||
if fnmatch.fnmatch(filename, '*.ini'):
|
||||
configs += [
|
||||
os.path.realpath(self.baseconfigs_location + os.sep + filename)]
|
||||
except OSError:
|
||||
self.log.warn(
|
||||
self.baseconfigs_location + ' is not acessible to get configs list')
|
||||
|
||||
configs += [os.path.expanduser('~/.yandex-tank')]
|
||||
return configs
|
||||
|
||||
def __graceful_shutdown(self):
|
||||
""" call shutdown routines """
|
||||
retcode = 1
|
||||
self.log.info("Trying to shutdown gracefully...")
|
||||
retcode = self.core.plugins_end_test(retcode)
|
||||
retcode = self.core.plugins_post_process(retcode)
|
||||
self.log.info("Done graceful shutdown")
|
||||
return retcode
|
||||
|
||||
|
||||
class SingleLevelFilter(logging.Filter):
|
||||
|
||||
"""Exclude or approve one msg type at a time. """
|
||||
|
||||
def __init__(self, passlevel, reject):
|
||||
logging.Filter.__init__(self)
|
||||
self.passlevel = passlevel
|
||||
self.reject = reject
|
||||
|
||||
def filter(self, record):
|
||||
if self.reject:
|
||||
return record.levelno != self.passlevel
|
||||
else:
|
||||
return record.levelno == self.passlevel
|
||||
|
18
yandextank/api/config/00-base.ini
Normal file
18
yandextank/api/config/00-base.ini
Normal file
@ -0,0 +1,18 @@
|
||||
### base config with Yandex-specific tool settings ###
|
||||
[tank]
|
||||
plugin_rcheck=yandextank.plugins.ResourceCheck
|
||||
plugin_ShellExec=yandextank.plugins.ShellExec
|
||||
plugin_phantom=yandextank.plugins.Phantom
|
||||
plugin_aggreg=yandextank.plugins.Aggregator
|
||||
plugin_autostop=yandextank.plugins.Autostop
|
||||
plugin_monitoring=yandextank.plugins.Monitoring
|
||||
plugin_console=yandextank.plugins.ConsoleOnline
|
||||
plugin_tips=yandextank.plugins.TipsAndTricks
|
||||
plugin_totalautostop=yandextank.plugins.TotalAutostop
|
||||
plugin_loadosophia=yandextank.plugins.Loadosophia
|
||||
plugin_graphite=yandextank.plugins.GraphiteUploader
|
||||
plugin_rcassert=yandextank.plugins.RCAssert
|
||||
artifacts_base_dir=logs
|
||||
|
||||
[console]
|
||||
short_only=1
|
26
yandextank/api/example/shoot.py
Normal file
26
yandextank/api/example/shoot.py
Normal file
@ -0,0 +1,26 @@
|
||||
from yandextank.api.apiworker import ApiWorker
|
||||
import logging
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
logger = logging.getLogger('')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
#not mandatory options below:
|
||||
options = dict()
|
||||
options['config'] = '/path/to/config/load.ini'
|
||||
options['manual_start'] = "1"
|
||||
options['user_options'] = [
|
||||
'phantom.ammofile=/path/to/ammofile',
|
||||
'phantom.rps_schedule=const(1,2m)',
|
||||
]
|
||||
log_filename = '/path/to/log/tank.log'
|
||||
#======================================
|
||||
|
||||
apiworker = ApiWorker()
|
||||
apiworker.init_logging(log_filename)
|
||||
try:
|
||||
apiworker.configure(options)
|
||||
apiworker.perform_test()
|
||||
except Exception, ex:
|
||||
logger.error('Error trying to perform a test: %s', ex)
|
@ -29,7 +29,7 @@ class InfluxUplinkPlugin(AbstractPlugin, AggregateResultListener):
|
||||
self.decoder = None
|
||||
|
||||
def get_available_options(self):
|
||||
return ["address", "port", "tank_tag"]
|
||||
return ["address", "port", "tank_tag", "grafana_root", "grafana_dashboard"]
|
||||
|
||||
def start_test(self):
|
||||
self.start_time = datetime.datetime.now()
|
||||
|
@ -41,7 +41,9 @@ class JMeterPlugin(AbstractPlugin):
|
||||
return __file__
|
||||
|
||||
def get_available_options(self):
|
||||
return ["jmx", "args", "jmeter_path", "buffer_size", "buffered_seconds", "use_argentum", "exclude_markers", ]
|
||||
return ["jmx", "args", "jmeter_path", "buffer_size",
|
||||
"buffered_seconds", "use_argentum", "exclude_markers",
|
||||
"connect_time"]
|
||||
|
||||
def configure(self):
|
||||
self.original_jmx = self.get_option("jmx")
|
||||
@ -60,10 +62,15 @@ class JMeterPlugin(AbstractPlugin):
|
||||
self.jmx = self.__add_jmeter_components(
|
||||
self.original_jmx, self.jtl_file, self._get_variables())
|
||||
self.core.add_artifact_file(self.jmx)
|
||||
self.connect_time = self.get_option('connect_time', '') not in ['', '0']
|
||||
|
||||
def prepare_test(self):
|
||||
self.args = [self.jmeter_path, "-n", "-t", self.jmx, '-j', self.jmeter_log,
|
||||
'-Jjmeter.save.saveservice.default_delimiter=\\t']
|
||||
connect_str = 'true'
|
||||
if not self.connect_time:
|
||||
connect_str = 'false'
|
||||
self.args += ['-Jjmeter.save.saveservice.connect_time=%s' % connect_str]
|
||||
self.args += tankcore.splitstring(self.user_args)
|
||||
|
||||
aggregator = None
|
||||
@ -311,13 +318,18 @@ class JMeterReader(AbstractReader):
|
||||
else:
|
||||
self.data_queue.append(cur_time)
|
||||
self.data_buffer[cur_time] = []
|
||||
|
||||
connect_value = 0
|
||||
if self.jmeter.connect_time:
|
||||
connect_value = int(data[9])
|
||||
|
||||
# marker, threads, overallRT, httpCode, netCode
|
||||
data_item = [
|
||||
data[2], int(data[7]), int(data[1]), self.exc_to_http(data[3]), netcode]
|
||||
# bytes: sent received
|
||||
data_item += [0, int(data[5])]
|
||||
# connect send latency receive
|
||||
data_item += [0, 0, int(data[8]), int(data[1]) - int(data[8])]
|
||||
data_item += [connect_value, 0, int(data[8]), int(data[1]) - int(data[8])]
|
||||
# accuracy
|
||||
data_item += [0]
|
||||
self.data_buffer[cur_time].append(data_item)
|
||||
|
@ -18,6 +18,7 @@ from ApacheBenchmark import ApacheBenchmarkPlugin
|
||||
from JMeter import JMeterPlugin
|
||||
from Monitoring import MonitoringPlugin
|
||||
from Phantom import PhantomPlugin
|
||||
from Pandora import PandoraPlugin
|
||||
from yandextank.core import AbstractPlugin
|
||||
|
||||
|
||||
@ -119,6 +120,13 @@ class LoadosophiaPlugin(AbstractPlugin, AggregateResultListener):
|
||||
except KeyError:
|
||||
self.log.debug("AB not found")
|
||||
|
||||
# pandora
|
||||
try:
|
||||
pandora = self.core.get_plugin_of_type(PandoraPlugin)
|
||||
main_file = pandora.sample_log
|
||||
except KeyError:
|
||||
self.log.debug("Pandora not found")
|
||||
|
||||
|
||||
if not main_file:
|
||||
self.log.warn("No file to upload to Loadosophia")
|
||||
@ -162,7 +170,7 @@ class LoadosophiaClient:
|
||||
""" Send files to loadosophia """
|
||||
if not self.token:
|
||||
msg = "Loadosophia.org uploading disabled, please set loadosophia.token option to enable it, "
|
||||
msg += "get token at https://loadosophia.org/service/upload/token/"
|
||||
msg += "get token at https://loadosophia.org/gui/settings/"
|
||||
self.log.warning(msg)
|
||||
else:
|
||||
if not self.address:
|
||||
|
@ -57,12 +57,38 @@ class SecuredShell(object):
|
||||
client = SSHClient()
|
||||
client.load_system_host_keys()
|
||||
client.set_missing_host_key_policy(AutoAddPolicy())
|
||||
client.connect(
|
||||
self.host,
|
||||
port=self.port,
|
||||
username=self.username,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
|
||||
try:
|
||||
client.connect(
|
||||
self.host,
|
||||
port=self.port,
|
||||
username=self.username,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
except ValueError, e:
|
||||
logger.error(e)
|
||||
logger.warning("""
|
||||
Patching Crypto.Cipher.AES.new and making another attempt.
|
||||
|
||||
See here for the details:
|
||||
http://uucode.com/blog/2015/02/20/workaround-for-ctr-mode-needs-counter-parameter-not-iv/
|
||||
""")
|
||||
client.close()
|
||||
import Crypto.Cipher.AES
|
||||
orig_new = Crypto.Cipher.AES.new
|
||||
|
||||
def fixed_AES_new(key, *ls):
|
||||
if Crypto.Cipher.AES.MODE_CTR == ls[0]:
|
||||
ls = list(ls)
|
||||
ls[1] = ''
|
||||
return orig_new(key, *ls)
|
||||
Crypto.Cipher.AES.new = fixed_AES_new
|
||||
client.connect(
|
||||
self.host,
|
||||
port=self.port,
|
||||
username=self.username,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
return client
|
||||
|
||||
def execute(self, cmd):
|
||||
@ -383,7 +409,7 @@ class MonitoringCollector(object):
|
||||
'CPU': 'user,system,iowait',
|
||||
'Memory': 'free,cached,used',
|
||||
'Disk': 'read,write',
|
||||
'Net': 'recv,send',
|
||||
'Net': 'recv,send,rx,tx',
|
||||
}
|
||||
|
||||
default_metric = ['CPU', 'Memory', 'Disk', 'Net']
|
||||
|
@ -64,7 +64,7 @@ class PandoraConfig(object):
|
||||
return {"Pools": [p.data() for p in self.pools]}
|
||||
|
||||
def json(self):
|
||||
return json.dumps(self.data())
|
||||
return json.dumps(self.data(), indent=2)
|
||||
|
||||
def add_pool(self, pool_config):
|
||||
self.pools.append(pool_config)
|
||||
@ -101,7 +101,7 @@ class PoolConfig(object):
|
||||
self.config["Gun"]["Parameters"]["SSL"] = ssl
|
||||
|
||||
def set_gun_type(self, gun_type):
|
||||
self.config["Gun"]["Type"] = gun_type
|
||||
self.config["Gun"]["GunType"] = gun_type
|
||||
|
||||
def data(self):
|
||||
return self.config
|
||||
|
@ -79,7 +79,7 @@ class PandoraPlugin(AbstractPlugin):
|
||||
pool_config.set_target(target)
|
||||
|
||||
gun_type = self.get_option("gun_type", "http")
|
||||
if gun_type is 'https':
|
||||
if gun_type == 'https':
|
||||
pool_config.set_ssl(True)
|
||||
self.log.info("SSL is on")
|
||||
gun_type = "http"
|
||||
|
@ -2,12 +2,10 @@ setup_t module_setup = setup_module_t {
|
||||
dir = "$phantom_modules_path"
|
||||
list = {
|
||||
io_monitor
|
||||
ssl
|
||||
io_benchmark
|
||||
io_benchmark_method_stream
|
||||
io_benchmark_method_stream_ipv4
|
||||
io_benchmark_method_stream_ipv6
|
||||
io_benchmark_method_stream_transport_ssl
|
||||
io_benchmark_method_stream_source_log
|
||||
io_benchmark_method_stream_proto_none
|
||||
io_benchmark_method_stream_proto_http
|
||||
|
@ -189,6 +189,8 @@ class PhantomPlugin(AbstractPlugin):
|
||||
self.log.warn(
|
||||
"Terminating phantom process with PID %s", self.process.pid)
|
||||
self.process.terminate()
|
||||
if self.process:
|
||||
self.process.communicate()
|
||||
else:
|
||||
self.log.debug("Seems phantom finished OK")
|
||||
if self.phantom_stderr:
|
||||
|
@ -84,6 +84,9 @@ class PhantomConfig:
|
||||
for stream in self.streams:
|
||||
stream.read_config()
|
||||
|
||||
if any(stream.ssl for stream in self.streams):
|
||||
self.additional_libs+=' ssl io_benchmark_method_stream_transport_ssl'
|
||||
|
||||
def compose_config(self):
|
||||
""" Generate phantom tool run config """
|
||||
streams_config = ''
|
||||
|
@ -10,6 +10,7 @@ from sqlalchemy import create_engine
|
||||
from sqlalchemy import exc
|
||||
|
||||
import requests
|
||||
import imp
|
||||
|
||||
requests_logger = logging.getLogger('requests')
|
||||
requests_logger.setLevel(logging.WARNING)
|
||||
@ -124,11 +125,15 @@ class CustomGun(AbstractPlugin):
|
||||
def __init__(self, core):
|
||||
self.log = logging.getLogger(__name__)
|
||||
AbstractPlugin.__init__(self, core)
|
||||
module_path = self.get_option("module_path", "")
|
||||
module_path = self.get_option("module_path", "").split()
|
||||
module_name = self.get_option("module_name")
|
||||
if module_path:
|
||||
sys.path.append(module_path)
|
||||
self.module = __import__(module_name)
|
||||
#imp allows to avoid custom module caching
|
||||
fp, pathname, description = imp.find_module(module_name, module_path)
|
||||
try:
|
||||
self.module = imp.load_module(module_name, fp, pathname, description)
|
||||
finally:
|
||||
if fp:
|
||||
fp.close()
|
||||
|
||||
def shoot(self, missile, marker, results):
|
||||
self.module.shoot(missile, marker, results)
|
||||
|
@ -226,14 +226,14 @@ class StepperWrapper(object):
|
||||
if self.use_caching:
|
||||
sep = "|"
|
||||
hasher = hashlib.md5()
|
||||
hashed_str = "cache version 5" + sep + \
|
||||
hashed_str = "cache version 6" + sep + \
|
||||
';'.join(self.instances_schedule) + sep + str(self.loop_limit)
|
||||
hashed_str += sep + str(self.ammo_limit) + sep + ';'.join(
|
||||
self.rps_schedule) + sep + str(self.autocases)
|
||||
hashed_str += sep + \
|
||||
";".join(self.uris) + sep + ";".join(
|
||||
self.headers) + sep + self.http_ver + sep + ";".join(self.chosen_cases)
|
||||
hashed_str += sep + str(self.enum_ammo)
|
||||
hashed_str += sep + str(self.enum_ammo) + sep + str(self.ammo_type)
|
||||
if self.instances_schedule:
|
||||
hashed_str += sep + str(self.instances)
|
||||
if self.ammo_file:
|
||||
|
@ -35,6 +35,7 @@ def parse_duration(duration):
|
||||
|
||||
def parse_token(time, multiplier):
|
||||
multipliers = {
|
||||
'd': 86400,
|
||||
'h': 3600,
|
||||
'm': 60,
|
||||
's': 1,
|
||||
@ -171,7 +172,7 @@ class HttpOpener(object):
|
||||
"Ammofile has already been downloaded to %s . Using it..", tmpfile_path)
|
||||
else:
|
||||
logging.info("Downloading ammofile to %s", tmpfile_path)
|
||||
data = requests.get(self.url)
|
||||
data = requests.get(self.url, verify=False)
|
||||
f = open(tmpfile_path, "wb")
|
||||
f.write(data.content)
|
||||
f.close()
|
||||
@ -190,7 +191,7 @@ class HttpOpener(object):
|
||||
"Ammofile has already been downloaded to %s . Using it..", tmpfile_path)
|
||||
else:
|
||||
logging.info("Downloading ammofile to %s", tmpfile_path)
|
||||
data = requests.get(self.url)
|
||||
data = requests.get(self.url, verify=False)
|
||||
f = open(tmpfile_path, "wb")
|
||||
f.write(data.content)
|
||||
f.close()
|
||||
|
Loading…
Reference in New Issue
Block a user