merge changes from master

This commit is contained in:
Alexey Lavrenuke 2016-01-26 14:50:01 +03:00
commit aeba6befe7
31 changed files with 639 additions and 132 deletions

View File

@ -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) [![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 * test autostop plugin
* customizable and extendable monitoring that works over SSH * customizable and extendable monitoring that works over SSH
## Install from PyPI ## Installation and configuration
You will need some packages that are required for building different python libraries: Installation at [ReadTheDocs](http://yandextank.readthedocs.org/en/latest/install.html)
```
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).
## Get help ## Get help
Documentation at [ReadTheDocs](https://yandextank.readthedocs.org/en/latest/) Documentation at [ReadTheDocs](https://yandextank.readthedocs.org/en/latest/)

View File

@ -19,8 +19,4 @@ _yandex_tank()
fi fi
COMPREPLY=() COMPREPLY=()
} && } &&
complete -o default -F _yandex_tank yandex-tank && 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

View File

@ -35,11 +35,7 @@ cp -arp Tank %{buildroot}/%{_libdir}/yandex-tank/
cp -ap tankcore.py %{buildroot}/%{_libdir}/yandex-tank/ cp -ap tankcore.py %{buildroot}/%{_libdir}/yandex-tank/
cp -ap tank.py %{buildroot}/%{_libdir}/yandex-tank/ cp -ap tank.py %{buildroot}/%{_libdir}/yandex-tank/
cp -ap *.sh %{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/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 %clean
rm -rf %{buildroot} rm -rf %{buildroot}
@ -50,9 +46,6 @@ rm -rf %{buildroot}
%{_sysconfdir}/bash_completion.d/yandex-tank.completion %{_sysconfdir}/bash_completion.d/yandex-tank.completion
%{_bindir}/lunapark %{_bindir}/lunapark
%{_bindir}/yandex-tank %{_bindir}/yandex-tank
%{_bindir}/yandex-tank-ab
%{_bindir}/yandex-tank-jmeter
%{_bindir}/yandex-tank-bfg
%{_libdir}/yandex-tank %{_libdir}/yandex-tank
%changelog %changelog

46
debian/changelog vendored
View File

@ -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 yandextank (1.7.23) trusty; urgency=medium
* update pandora plugin * update pandora plugin

2
debian/control vendored
View File

@ -7,7 +7,7 @@ Standards-Version: 3.9.3
Package: yandex-tank Package: yandex-tank
Architecture: all 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 Conflicts: yandex-load-tank-base
Description: performance measurement tool Description: performance measurement tool
Find out about your apps performance limits and bottlenecks in order Find out about your apps performance limits and bottlenecks in order

View File

@ -4,17 +4,15 @@ Advanced usage
Command line options Command line options
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
There are three executables in Yandex.Tank package: ``yandex-tank``, Yandex.Tank has an obviously named executable ``yandex-tank``.
``yandex-tank-ab`` and ``yandex-tank-jmeter``. Last two of them just use Here are available command line options:
different kind of load generation utilities, ``ab`` (Apache Benchmark) and
``jmeter`` (Apache JMeter), accordingly. Command line options are common
for all three.
- **-h, --help** - show 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`` - **-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 - **-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. - **-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`` - **-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`` - **-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"`` - **-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 - **-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] [phantom]
address=example.com:80 address=example.com:80
rps_schedule=const(100,60s) rps_schedule=const(100,60s)
[autostop] [autostop]
autostop=instances(80%,10) autostop=instances(80%,10)
@ -160,6 +158,71 @@ there.
Modules 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 Phantom
^^^^^^^ ^^^^^^^
@ -281,7 +344,15 @@ Options that apply only for main section: buffered_seconds, writelog, phantom_mo
JMeter 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]** INI file section: **[jmeter]**
@ -291,6 +362,7 @@ Options
* **args** - additional commandline arguments for JMeter * **args** - additional commandline arguments for JMeter
* **jmeter_path** - path to JMeter, allows to use alternative JMeter installation. Default: 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 * **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 * **all other options in the section** - they will be passed as User Defined Variables to JMeter
Artifacts Artifacts
@ -303,7 +375,7 @@ Artifacts
BFG BFG
^^^ ^^^
(`What is BFG <http://en.wikipedia.org/wiki/BFG_(weapon)>`_) (`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_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 * **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
^^^^^^^ ^^^^^^^
`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 `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. 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``). 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 loop = 1000000
startup_schedule = periodic(2, 10, 50) startup_schedule = periodic(2, 10, 50)
user_schedule = unlimited user_schedule = unlimited()
shared_schedule = 0 shared_schedule = 0
Start 2 users every 10 seconds. Every user will shoot without any limits (next request is sended 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 * **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) * **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 * **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" 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
^^^^^^^^ ^^^^^^^^
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 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 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 the on-line statistics for the tests with ab. The data are reviewed
after the test. 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]** INI file section: **[ab]**
@ -783,37 +965,6 @@ Options
* **pass** - list of acceptable codes, delimiter - whitespace. Default: empty, no check is performed. * **pass** - list of acceptable codes, delimiter - whitespace. Default: empty, no check is performed.
* **fail_code** - exit code when check fails, integer number. Default: 10 * **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 Tips&Tricks
^^^^^^^^^^^ ^^^^^^^^^^^
@ -829,7 +980,7 @@ Options
Sources 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 load.ini example
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~

View File

@ -8,7 +8,8 @@ Welcome to Yandex.Tank's documentation!
:Homepage: `Yandex.Tank Homepage on Github :Homepage: `Yandex.Tank Homepage on Github
<https://github.com/yandex-load/yandex-tank>`_ <https://github.com/yandex-load/yandex-tank>`_
:Download: `Launchpad PPA (ubuntu packages) :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 :Documentation: `PDF Documentation
<https://media.readthedocs.org/pdf/yandextank/latest/yandextank.pdf>`_ <https://media.readthedocs.org/pdf/yandextank/latest/yandextank.pdf>`_
:License: `GNU LGPLv3 :License: `GNU LGPLv3
@ -36,4 +37,4 @@ Indices and tables
.. image:: .. image::
http://mc.yandex.ru/watch/23073253 http://mc.yandex.ru/watch/23073253
:align: right :align: right

View File

@ -1,8 +1,38 @@
Installation and Configuration 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. You should add proper repositories on Debian-based environment.
@ -10,8 +40,8 @@ For instance, add following repos to ``sources.list`` :
.. code-block:: bash .. code-block:: bash
deb 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 precise main deb-src http://ppa.launchpad.net/yandex-load/main/ubuntu trusty main
or this way or this way
@ -21,11 +51,11 @@ or this way
sudo apt-get install software-properties-common sudo apt-get install software-properties-common
sudo add-apt-repository ppa:yandex-load/main 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 .. 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 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 Ubuntu (10.04/.../13.10) would be sufficient. The tank could be easily

View File

@ -9,6 +9,9 @@ See also
Evgeniy Mamchits' `phantom <https://github.com/mamchits/phantom>`_ - Evgeniy Mamchits' `phantom <https://github.com/mamchits/phantom>`_ -
Phantom scalable IO Engine Phantom scalable IO Engine
Alexey Lavrenuke's `pandora <https://github.com/yandex/pandora>`_ -
A load generator in Go language
Gregory Komissarov's Gregory Komissarov's
`firebat <https://github.com/greggyNapalm/firebat-console>`_ - test tool `firebat <https://github.com/greggyNapalm/firebat-console>`_ - test tool
based on Phantom based on Phantom

View File

@ -4,6 +4,8 @@ Usage
So, you've installed Yandex.Tank to a proper machine, it is close to target, 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? access is permitted and server is tuned. How to make a test?
This guide is for ``phantom`` load generator.
First Steps 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`` 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. - 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 3. ``const (load,dur)`` makes constant load. ``load`` - rps amount, ``dur``
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 - load duration. You can set fractional load like this: ``line(1.1, 2.5, 10)``
in a test. -- 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 ``step`` and ``line`` could be used with increasing and decreasing
intensity: intensity:
@ -39,7 +42,9 @@ intensity:
minutes ``line(1, 100, 10m)`` - linear load from 1 to 100 rps, duration minutes ``line(1, 100, 10m)`` - linear load from 1 to 100 rps, duration
- 10 minutes - 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). Time duration could be defined in seconds, minutes (m) and hours (h).
For example: ``27h103m645`` For example: ``27h103m645``
@ -60,13 +65,29 @@ Voilà, Yandex.Tank setup is done.
Preparing requests Preparing requests
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
There are several ways to set up requests: Access mode, URI-style and request-style. 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 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 Access mode
'''''''''''' ''''''''''''
You can use access.log file from your webserver as a source of requests. 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. 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 /sdfbv/swdfvs/ssfsf
Parameter ``uris`` contains uri, which should be used for requests generation. 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 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. 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 ``/``. Every URI must begin from a new line, with leading ``/``.
Each line that begins from ``[`` is considered as a header. 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``. Headers could be (re)defined in the middle of URIs, as in sample above.
Request may be marked by tag, you can specify it with whitespace following URI. 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 URI+POST-style
'''''''''''''' ''''''''''''''
@ -172,7 +196,10 @@ is:
where ``size_of_request`` request size in bytes. '\r\n' symbols after where ``size_of_request`` request size in bytes. '\r\n' symbols after
``body`` are ignored and not sent anywhere, but it is required to ``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)** **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 distribution, progressbar and other interesting data. At the same time
file ``phout.txt`` is being written, which could be analyzed later. 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 Tags
~~~~ ~~~~
@ -354,6 +384,7 @@ requests and tags:
User-Agent: xxx (shell 1) User-Agent: xxx (shell 1)
``good``, ``bad`` and ``unknown`` here are the tags. ``good``, ``bad`` and ``unknown`` here are the tags.
**RESTRICTION: utf-8 symbols only** **RESTRICTION: utf-8 symbols only**
SSL SSL
@ -422,9 +453,8 @@ Logging
Looking into target's answers is quite useful in debugging. For doing Looking into target's answers is quite useful in debugging. For doing
that add ``writelog = 1`` to ``load.ini``. that add ``writelog = 1`` to ``load.ini``.
**ATTENTION: Writing answers on **ATTENTION: Writing answers on high load leads to intensive disk i/o
high load leads to intensive disk i/o usage and can affect test usage and can affect test accuracy.**
accuracy.**
Log format: Log format:
@ -502,7 +532,7 @@ installed on your Yandex.Tank system.
Graph and statistics 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 OR
use your favorite stats packet, R, for example. use your favorite stats packet, R, for example.
@ -521,6 +551,8 @@ parameter like this:
[aggregator] [aggregator]
time_periods = 10 45 50 100 150 300 500 1s 1500 2s 3s 10s ; the last value - 10s is considered as connect timeout. 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 Thread limit
~~~~~~~~~~~~ ~~~~~~~~~~~~

View File

@ -1,2 +0,0 @@
#! /bin/sh
yandex-tank -o "tank.plugin_phantom=" -o "tank.plugin_ab=Tank/Plugins/ApacheBenchmark.py" "$@"

View File

@ -1,2 +0,0 @@
#! /bin/sh
yandex-tank -o "tank.plugin_phantom=" -o "tank.plugin_bfg=Tank/Plugins/BFG.py" "$@"

View File

@ -1,2 +0,0 @@
#! /bin/sh
yandex-tank -o "tank.plugin_phantom=" -o "tank.plugin_jmeter=Tank/Plugins/JMeter.py" "$@"

View File

@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup( setup(
name='yandextank', name='yandextank',
version='1.7.23', version='1.7.30',
description='a performance measurement tool', description='a performance measurement tool',
longer_description=''' longer_description='''
Yandex.Tank is a performance measurement and load testing automatization tool. 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', 'psutil>=1.2.1',
'ipaddr', 'ipaddr',
'progressbar', 'progressbar',
'requests', 'requests>=2.5.1',
'paramiko>=1.16.0', 'paramiko>=1.16.0',
'pandas', 'pandas',
'pyzmq',
], ],
license='LGPLv2', license='LGPLv2',
classifiers=[ classifiers=[
@ -45,6 +46,7 @@ analytic tools for the results they produce.
}, },
package_data={ package_data={
'yandextank.core': ['config/*'], 'yandextank.core': ['config/*'],
'yandextank.api': ['config/*'],
'yandextank.plugins.GraphiteUploader': ['config/*'], 'yandextank.plugins.GraphiteUploader': ['config/*'],
'yandextank.plugins.JMeter': ['config/*'], 'yandextank.plugins.JMeter': ['config/*'],
'yandextank.plugins.Monitoring': ['config/*'], 'yandextank.plugins.Monitoring': ['config/*'],

View File

@ -21,7 +21,11 @@ class PhantomConfigTestCase(TankTestCase):
foo = PhantomConfig(core) foo = PhantomConfig(core)
foo.read_config() 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): def test_double(self):
core = self.get_core() 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_io "), 2)
self.assertEquals(conf_str.count("benchmark_io1 "), 2) self.assertEquals(conf_str.count("benchmark_io1 "), 2)
self.assertEquals(conf_str.count("benchmark_io2 "), 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() conf_str = open(config).read()
logging.info(conf_str) logging.info(conf_str)

View File

@ -0,0 +1,4 @@
'''
Package contains all tank tool core code
'''
from apiworker import *

169
yandextank/api/apiworker.py Normal file
View 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

View 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

View 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)

View File

@ -29,7 +29,7 @@ class InfluxUplinkPlugin(AbstractPlugin, AggregateResultListener):
self.decoder = None self.decoder = None
def get_available_options(self): def get_available_options(self):
return ["address", "port", "tank_tag"] return ["address", "port", "tank_tag", "grafana_root", "grafana_dashboard"]
def start_test(self): def start_test(self):
self.start_time = datetime.datetime.now() self.start_time = datetime.datetime.now()

View File

@ -41,7 +41,9 @@ class JMeterPlugin(AbstractPlugin):
return __file__ return __file__
def get_available_options(self): 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): def configure(self):
self.original_jmx = self.get_option("jmx") self.original_jmx = self.get_option("jmx")
@ -60,10 +62,15 @@ class JMeterPlugin(AbstractPlugin):
self.jmx = self.__add_jmeter_components( self.jmx = self.__add_jmeter_components(
self.original_jmx, self.jtl_file, self._get_variables()) self.original_jmx, self.jtl_file, self._get_variables())
self.core.add_artifact_file(self.jmx) self.core.add_artifact_file(self.jmx)
self.connect_time = self.get_option('connect_time', '') not in ['', '0']
def prepare_test(self): def prepare_test(self):
self.args = [self.jmeter_path, "-n", "-t", self.jmx, '-j', self.jmeter_log, self.args = [self.jmeter_path, "-n", "-t", self.jmx, '-j', self.jmeter_log,
'-Jjmeter.save.saveservice.default_delimiter=\\t'] '-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) self.args += tankcore.splitstring(self.user_args)
aggregator = None aggregator = None
@ -311,13 +318,18 @@ class JMeterReader(AbstractReader):
else: else:
self.data_queue.append(cur_time) self.data_queue.append(cur_time)
self.data_buffer[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 # marker, threads, overallRT, httpCode, netCode
data_item = [ data_item = [
data[2], int(data[7]), int(data[1]), self.exc_to_http(data[3]), netcode] data[2], int(data[7]), int(data[1]), self.exc_to_http(data[3]), netcode]
# bytes: sent received # bytes: sent received
data_item += [0, int(data[5])] data_item += [0, int(data[5])]
# connect send latency receive # 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 # accuracy
data_item += [0] data_item += [0]
self.data_buffer[cur_time].append(data_item) self.data_buffer[cur_time].append(data_item)

View File

@ -18,6 +18,7 @@ from ApacheBenchmark import ApacheBenchmarkPlugin
from JMeter import JMeterPlugin from JMeter import JMeterPlugin
from Monitoring import MonitoringPlugin from Monitoring import MonitoringPlugin
from Phantom import PhantomPlugin from Phantom import PhantomPlugin
from Pandora import PandoraPlugin
from yandextank.core import AbstractPlugin from yandextank.core import AbstractPlugin
@ -119,6 +120,13 @@ class LoadosophiaPlugin(AbstractPlugin, AggregateResultListener):
except KeyError: except KeyError:
self.log.debug("AB not found") 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: if not main_file:
self.log.warn("No file to upload to Loadosophia") self.log.warn("No file to upload to Loadosophia")
@ -162,7 +170,7 @@ class LoadosophiaClient:
""" Send files to loadosophia """ """ Send files to loadosophia """
if not self.token: if not self.token:
msg = "Loadosophia.org uploading disabled, please set loadosophia.token option to enable it, " 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) self.log.warning(msg)
else: else:
if not self.address: if not self.address:

View File

@ -57,12 +57,38 @@ class SecuredShell(object):
client = SSHClient() client = SSHClient()
client.load_system_host_keys() client.load_system_host_keys()
client.set_missing_host_key_policy(AutoAddPolicy()) client.set_missing_host_key_policy(AutoAddPolicy())
client.connect(
self.host, try:
port=self.port, client.connect(
username=self.username, self.host,
timeout=self.timeout, 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 return client
def execute(self, cmd): def execute(self, cmd):
@ -383,7 +409,7 @@ class MonitoringCollector(object):
'CPU': 'user,system,iowait', 'CPU': 'user,system,iowait',
'Memory': 'free,cached,used', 'Memory': 'free,cached,used',
'Disk': 'read,write', 'Disk': 'read,write',
'Net': 'recv,send', 'Net': 'recv,send,rx,tx',
} }
default_metric = ['CPU', 'Memory', 'Disk', 'Net'] default_metric = ['CPU', 'Memory', 'Disk', 'Net']

View File

@ -64,7 +64,7 @@ class PandoraConfig(object):
return {"Pools": [p.data() for p in self.pools]} return {"Pools": [p.data() for p in self.pools]}
def json(self): def json(self):
return json.dumps(self.data()) return json.dumps(self.data(), indent=2)
def add_pool(self, pool_config): def add_pool(self, pool_config):
self.pools.append(pool_config) self.pools.append(pool_config)
@ -101,7 +101,7 @@ class PoolConfig(object):
self.config["Gun"]["Parameters"]["SSL"] = ssl self.config["Gun"]["Parameters"]["SSL"] = ssl
def set_gun_type(self, gun_type): def set_gun_type(self, gun_type):
self.config["Gun"]["Type"] = gun_type self.config["Gun"]["GunType"] = gun_type
def data(self): def data(self):
return self.config return self.config

View File

@ -79,7 +79,7 @@ class PandoraPlugin(AbstractPlugin):
pool_config.set_target(target) pool_config.set_target(target)
gun_type = self.get_option("gun_type", "http") gun_type = self.get_option("gun_type", "http")
if gun_type is 'https': if gun_type == 'https':
pool_config.set_ssl(True) pool_config.set_ssl(True)
self.log.info("SSL is on") self.log.info("SSL is on")
gun_type = "http" gun_type = "http"

View File

@ -2,12 +2,10 @@ setup_t module_setup = setup_module_t {
dir = "$phantom_modules_path" dir = "$phantom_modules_path"
list = { list = {
io_monitor io_monitor
ssl
io_benchmark io_benchmark
io_benchmark_method_stream io_benchmark_method_stream
io_benchmark_method_stream_ipv4 io_benchmark_method_stream_ipv4
io_benchmark_method_stream_ipv6 io_benchmark_method_stream_ipv6
io_benchmark_method_stream_transport_ssl
io_benchmark_method_stream_source_log io_benchmark_method_stream_source_log
io_benchmark_method_stream_proto_none io_benchmark_method_stream_proto_none
io_benchmark_method_stream_proto_http io_benchmark_method_stream_proto_http

View File

@ -189,6 +189,8 @@ class PhantomPlugin(AbstractPlugin):
self.log.warn( self.log.warn(
"Terminating phantom process with PID %s", self.process.pid) "Terminating phantom process with PID %s", self.process.pid)
self.process.terminate() self.process.terminate()
if self.process:
self.process.communicate()
else: else:
self.log.debug("Seems phantom finished OK") self.log.debug("Seems phantom finished OK")
if self.phantom_stderr: if self.phantom_stderr:

View File

@ -84,6 +84,9 @@ class PhantomConfig:
for stream in self.streams: for stream in self.streams:
stream.read_config() 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): def compose_config(self):
""" Generate phantom tool run config """ """ Generate phantom tool run config """
streams_config = '' streams_config = ''

View File

@ -10,6 +10,7 @@ from sqlalchemy import create_engine
from sqlalchemy import exc from sqlalchemy import exc
import requests import requests
import imp
requests_logger = logging.getLogger('requests') requests_logger = logging.getLogger('requests')
requests_logger.setLevel(logging.WARNING) requests_logger.setLevel(logging.WARNING)
@ -124,11 +125,15 @@ class CustomGun(AbstractPlugin):
def __init__(self, core): def __init__(self, core):
self.log = logging.getLogger(__name__) self.log = logging.getLogger(__name__)
AbstractPlugin.__init__(self, core) 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") module_name = self.get_option("module_name")
if module_path: #imp allows to avoid custom module caching
sys.path.append(module_path) fp, pathname, description = imp.find_module(module_name, module_path)
self.module = __import__(module_name) try:
self.module = imp.load_module(module_name, fp, pathname, description)
finally:
if fp:
fp.close()
def shoot(self, missile, marker, results): def shoot(self, missile, marker, results):
self.module.shoot(missile, marker, results) self.module.shoot(missile, marker, results)

View File

@ -226,14 +226,14 @@ class StepperWrapper(object):
if self.use_caching: if self.use_caching:
sep = "|" sep = "|"
hasher = hashlib.md5() hasher = hashlib.md5()
hashed_str = "cache version 5" + sep + \ hashed_str = "cache version 6" + sep + \
';'.join(self.instances_schedule) + sep + str(self.loop_limit) ';'.join(self.instances_schedule) + sep + str(self.loop_limit)
hashed_str += sep + str(self.ammo_limit) + sep + ';'.join( hashed_str += sep + str(self.ammo_limit) + sep + ';'.join(
self.rps_schedule) + sep + str(self.autocases) self.rps_schedule) + sep + str(self.autocases)
hashed_str += sep + \ hashed_str += sep + \
";".join(self.uris) + sep + ";".join( ";".join(self.uris) + sep + ";".join(
self.headers) + sep + self.http_ver + sep + ";".join(self.chosen_cases) 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: if self.instances_schedule:
hashed_str += sep + str(self.instances) hashed_str += sep + str(self.instances)
if self.ammo_file: if self.ammo_file:

View File

@ -35,6 +35,7 @@ def parse_duration(duration):
def parse_token(time, multiplier): def parse_token(time, multiplier):
multipliers = { multipliers = {
'd': 86400,
'h': 3600, 'h': 3600,
'm': 60, 'm': 60,
's': 1, 's': 1,
@ -171,7 +172,7 @@ class HttpOpener(object):
"Ammofile has already been downloaded to %s . Using it..", tmpfile_path) "Ammofile has already been downloaded to %s . Using it..", tmpfile_path)
else: else:
logging.info("Downloading ammofile to %s", tmpfile_path) 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 = open(tmpfile_path, "wb")
f.write(data.content) f.write(data.content)
f.close() f.close()
@ -190,7 +191,7 @@ class HttpOpener(object):
"Ammofile has already been downloaded to %s . Using it..", tmpfile_path) "Ammofile has already been downloaded to %s . Using it..", tmpfile_path)
else: else:
logging.info("Downloading ammofile to %s", tmpfile_path) 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 = open(tmpfile_path, "wb")
f.write(data.content) f.write(data.content)
f.close() f.close()