{"id":20188279,"url":"https://github.com/gaainf/tanktools","last_synced_at":"2025-10-14T10:10:04.755Z","repository":{"id":46058396,"uuid":"144871272","full_name":"gaainf/tanktools","owner":"gaainf","description":"Yandex-tank tools","archived":false,"fork":false,"pushed_at":"2022-01-05T22:39:26.000Z","size":43,"stargazers_count":9,"open_issues_count":2,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-08T20:55:31.598Z","etag":null,"topics":["ammo","har","har2ammo","load","pcap","pcap2ammo","phout","statistics","testing","yandex-tank"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gaainf.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-15T15:33:49.000Z","updated_at":"2025-02-06T10:57:20.000Z","dependencies_parsed_at":"2022-09-26T17:40:48.352Z","dependency_job_id":null,"html_url":"https://github.com/gaainf/tanktools","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/gaainf/tanktools","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaainf%2Ftanktools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaainf%2Ftanktools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaainf%2Ftanktools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaainf%2Ftanktools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gaainf","download_url":"https://codeload.github.com/gaainf/tanktools/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaainf%2Ftanktools/sbom","scorecard":{"id":416137,"data":{"date":"2025-08-11","repo":{"name":"github.com/gaainf/tanktools","commit":"751db36faa31f2b54d64cdde2c9e0cf9186fbc70"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.7,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact 1.0.7 not signed: https://api.github.com/repos/gaainf/tanktools/releases/16082831","Warn: release artifact 1.0.5 not signed: https://api.github.com/repos/gaainf/tanktools/releases/14948628","Warn: release artifact 1.0.4 not signed: https://api.github.com/repos/gaainf/tanktools/releases/14695495","Warn: release artifact 1.0.3 not signed: https://api.github.com/repos/gaainf/tanktools/releases/13997652","Warn: release artifact 1.0.2 not signed: https://api.github.com/repos/gaainf/tanktools/releases/13045363","Warn: release artifact 1.0.7 does not have provenance: https://api.github.com/repos/gaainf/tanktools/releases/16082831","Warn: release artifact 1.0.5 does not have provenance: https://api.github.com/repos/gaainf/tanktools/releases/14948628","Warn: release artifact 1.0.4 does not have provenance: https://api.github.com/repos/gaainf/tanktools/releases/14695495","Warn: release artifact 1.0.3 does not have provenance: https://api.github.com/repos/gaainf/tanktools/releases/13997652","Warn: release artifact 1.0.2 does not have provenance: https://api.github.com/repos/gaainf/tanktools/releases/13045363"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-18T23:54:08.754Z","repository_id":46058396,"created_at":"2025-08-18T23:54:08.755Z","updated_at":"2025-08-18T23:54:08.755Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279018769,"owners_count":26086449,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ammo","har","har2ammo","load","pcap","pcap2ammo","phout","statistics","testing","yandex-tank"],"created_at":"2024-11-14T03:28:32.432Z","updated_at":"2025-10-14T10:10:04.737Z","avatar_url":"https://github.com/gaainf.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"====================================\nYandex-tank input/output file parser\n====================================\n\n.. image:: https://travis-ci.org/travis-ci/travis-web.svg?branch=master\n    :target: https://travis-ci.org/travis-ci/travis-web\n\n.. image:: https://codecov.io/gh/gaainf/tanktools/branch/master/graph/badge.svg\n    :target: https://codecov.io/gh/gaainf/tanktools/\n\n.. image:: https://img.shields.io/badge/python-2.7-blue.svg\n    :target: https://www.python.org/downloads/release/python-270/\n\n.. image:: https://img.shields.io/badge/python-3.5-blue.svg\n    :target: https://www.python.org/downloads/release/python-350/\n\n.. image:: https://img.shields.io/badge/python-3.6-blue.svg\n    :target: https://www.python.org/downloads/release/python-360/\n\n.. image:: https://img.shields.io/pypi/l/tanktools.svg\n    :target: https://github.com/gaainf/tanktools/blob/master/LICENSE\n\n\nYandex-tank prepare `phout` file with statistics after load testing.\n**tanktools** module helps to parse such files and convert to DataFrame.\nYou can use **pandas** module in manual mode to handle DataFrame\nor use build-in functions.\n\nGenerate Yandex-tank ammo from pcap or har files using `pcap2ammo`\nor `har2ammo` scrips. HTTP requests are extracted completely\nwith headers and body.\n\nSo you can:\n\n- calc quantiles\n\n- get information about timings, latency, status codes\n\n- extract requests by timestamp, tag and other columns\n\n- group and analyze specific data like total/partial RPS,\n  average request/response size\n\n- calc statistical metrics\n\n- convert pcap file to ammo\n\n- convert har file to ammo\n\n- filter out and modify requests on ammo generating\n\n\n************\nInstallation\n************\n.. code:: python\n\n    pip install tanktools\n\n********\nExamples\n********\n\nSelect DataFrame by timestamp\n*****************************\n\nIt is possible to parse a part of staistics by time and overal count\n\n.. code:: python\n\n    from tanktools import phout\n    flags = {\n        'from_date': '2018-01-18 20:09:50.123',\n        'to_date'  : '2018-01-18 20:10:00.456',\n        'limit': 100\n    }\n    data = phout.parse_phout('phout.log', flags)\n    print(\"Total count: %d\" % phout.size(data))\n\n.. code::\n\n    Total count: 100\n\nPrint percentiles\n*****************\n.. code:: python\n\n        data = phout.parse_phout('phout.log')\n        phout.print_quantiles(data, 'receive_time')\n\n.. code::\n\n    Percentiles for 150030 requests\n        from 2018-01-18 20:09:42.983\n        to   2018-01-18 20:10:55.108:\n    quantile (%)  receive_time (mks)\n            10.0                   9\n            20.0                   9\n            30.0                  10\n            40.0                  10\n            50.0                  10\n            60.0                  10\n            70.0                  11\n            80.0                  12\n            90.0                  13\n            95.0                  14\n            98.0                  16\n            99.0                  17\n            100.0                 716\n\n\n.. note::\n\n    Pay attention, timings are calculated in microseconds.\n\nPrint latency median\n************************\n\n.. code:: python\n\n    data = phout.parse_phout('phout.log')\n    # Convert and print timing in milliseconds\n    print(\"\\n\\nLatency median: %d ms\" % int(data.latency.median() / 1000))\n\n.. code::\n\n    Latency median: 30 ms\n\nGet RPS\n*******\n\n.. code:: python\n\n    data = phout.parse_phout('phout.log')\n    rps = phout.get_rps(data)\n\nPrint HTTP response statistics\n*******************************\n\n.. code:: python\n\n    data = phout.parse_phout('phout.log')\n    phout.print_http_reponses(data)\n\n.. code::\n\n    HTTP code   count  percent (%)\n         500   83429        56.38\n         200   61558        41.60\n         502    2944         1.99\n           0      41         0.03\n\nSelect 200 OK responses and print latency median\n************************************************\n\n.. code:: python\n\n    data = phout.parse_phout('phout.log')\n    selected_http_responses = data[data.proto_code == 200]\n    print(\"Latency median for 200 OK: %d\" %\n          selected_http_responses.latency.median())\n\n.. code::\n\n    Latency median for 200 OK: 3539\n\nPrint average request/response size\n***********************************\n\n.. code:: python\n\n    print(\"Avg. Request / Response: %d / %d bytes\" % (\n        data.size_in.astype(float).mean(),\n        data.size_out.astype(float).mean()\n    ))\n\n.. code::\n\n    Avg. Request / Response: 364 / 26697 bytes\n\n.. note::\n\n    Pay attention it is required to convert data to float for correct work of ``mean`` function\n\nPrint RPS at Nth request\n************************\n\n.. code:: python\n\n    print(\"RPS at request:\")\n    chunk_size = int(phout.size(data) / 2)\n    for start in range(0, phout.size(data), chunk_size):\n        data_subset = phout.subset(data, start, chunk_size)\n        print(\"\\t%s: %.2f\" %\n              (start + chunk_size, phout.get_rps(data_subset)))\n\n.. code::\n\n    RPS at request:\n        73986: 2062.50\n        147972: 2530.56\n\n\n*********\npcap2ammo\n*********\n\nConvert pcap file to Yandex-tank ammo\n*************************************\n\n.. code:: bash\n\n    pcap2ammo file.pcap\n\n.. code::\n\n    73\n    GET https://rambler.ru/ HTTP/1.1\\r\\n\n    Host: rambler.ru\\r\\n\n    Content-Length: 0\\r\\n\\r\\n\n\nCount statistics about HTTP requests\n***************************************\n\n.. code:: bash\n\n    pcap2ammo -S file.pcap\n\n    Stats:\n        total: 1\n        complete: 1\n        incorrect: 0\n        incomplete: 0\n\nPrint to file\n*************************************\n\n.. code:: bash\n\n    pcap2ammo -o out.ammo file.pcap\n\nAdd or delete headers\n*********************\nApplyed for all requests, containing specified headers\n\n.. code:: bash\n\n    pcap2ammo --add-header 'Referer: http://domain.com' --add-header 'X-Ip: 1.1.1.1' file.pcap\n\n.. code:: bash\n\n    pcap2ammo --delete-header 'Content-Length' file.pcap\n    pcap2ammo --delete-header 'Connection' --add-header 'Connection: keep-alive' file.pcap\n\nFilter TCP/IP packets\n*********************\n\n.. code:: bash\n\n    pcap2ammo -f 'ip.src==10.10.10.10 and tcp.dport==8080' file.pcap\n\nFilter HTTP packets\n*********************\n\n.. code:: bash\n\n    pcap2ammo -F '\"rambler.ru\" in http.uri' file.pcap\n\nYou can use logical expressions in filters\n\n.. code:: bash\n\n    pcap2ammo -F '\"keep-alive\" in http.headers[\"connection\"] or \"Keep-alive\" in http.headers[\"connection\"]' file.pcap\n\nString functions over HTTP headers\n\n.. code:: bash\n\n    pcap2ammo -F '\"keep-alive\" in http.headers[\"connection\"].lower()' file.pcap\n\nUse excluding filters also\n\n.. code:: bash\n\n    pcap2ammo -F '\"rambler.ru\" != http.headers[\"host\"]' file.pcap\n\n*********\nhar2ammo\n*********\n\nConvert pcap file to Yandex-tank ammo\n*************************************\n\n.. code:: bash\n\n    har2ammo file.har\n\n.. code::\n\n    73\n    GET https://rambler.ru/ HTTP/1.1\\r\\n\n    Host: rambler.ru\\r\\n\n    Content-Length: 0\\r\\n\\r\\n\n\nCount statistics about HTTP requests\n***************************************\n\n.. code:: bash\n\n    har2ammo -S file.har\n\n    Stats:\n        total: 1\n        complete: 1\n        incorrect: 0\n        incomplete: 0\n\nPrint to file\n*************************************\n\n.. code:: bash\n\n    har2ammo -o out.ammo file.har\n\nAdd or delete headers\n*********************\nApplyed for all requests, containing specified headers\n\n.. code:: bash\n\n    har2ammo --add-header 'Referer: http://domain.com' --add-header 'X-Ip: 1.1.1.1' file.har\n\n.. code:: bash\n\n    har2ammo --delete-header 'Content-Length' file.har\n    har2ammo --delete-header 'Connection' --add-header 'Connection: keep-alive' file.har\n\nFilter HTTP packets\n*********************\n\n.. code:: bash\n\n    har2ammo -F '\"rambler.ru\" in http.uri' file.har\n\nYou can use logical expressions and python functions in filters\n\n.. code:: bash\n\n    har2ammo -F '\"keep-alive\" in http.headers[\"connection\"] or \"Keep-alive\" in http.headers[\"connection\"]' file.har\n    har2ammo -F '\"keep-alive\" not in http.headers[\"connection\"].lower()' file.har\n\nPlease, see more information about filters in `pcaper \u003chttps://github.com/gaainf/pcaper/\u003e`_ package description.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaainf%2Ftanktools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgaainf%2Ftanktools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaainf%2Ftanktools/lists"}