Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

trio subprocs launching #87

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ cache:
- pip

python:
- 2.7
- 3.5
- 3.6
# - 3.7
Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ but (want to) use it for automated testing because it gets the job done...


## What is it?
Python configuring and launching the infamous
[SIPp](http://sipp.sourceforge.net/) using an api inspired by
[requests](http://docs.python-requests.org/)
Python 3.6+ configuring and launching the infamous
[SIPp](http://sipp.sourceforge.net/) using a simple API to
generate commands and spawn them in subprocesses.

Command subprocess launching now uses
[`trio`](https://trio.readthedocs.io/en/stable/reference-io.html#spawning-subprocesses)!

## It definitely lets you

Expand All @@ -25,7 +28,7 @@ Python configuring and launching the infamous


## Basic Usage
Launching the default UAC scenario is a short line:
Launching the default UAC script is a short line:

```python
import pysipp
Expand Down
64 changes: 6 additions & 58 deletions pysipp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Authors : Tyler Goodlet

"""
pysipp - a python wrapper for launching SIPp
pysipp - a Python wrapper for launching SIPp

"""
import sys
from os.path import dirname

from . import agent
from . import launch
from . import netplug
from . import plugin
from . import report
from .agent import client
from .agent import server
from .load import iter_scen_dirs
Expand Down Expand Up @@ -107,7 +107,9 @@ def scenario(dirpath=None, proxyaddr=None, autolocalsocks=True, **scenkwargs):

# same as above
scen = plugin.mng.hook.pysipp_conf_scen_protocol(
agents=[uas, uac], confpy=None, scenkwargs=scenkwargs
agents=[uas, uac],
confpy=None,
scenkwargs=scenkwargs,
)

if proxyaddr:
Expand Down Expand Up @@ -202,59 +204,5 @@ def pysipp_conf_scen(agents, scen):
ua.rtp_echo = True


@plugin.hookimpl
def pysipp_new_runner():
"""Provision and assign a default cmd runner"""
return launch.PopenRunner()


@plugin.hookimpl
def pysipp_run_protocol(scen, runner, block, timeout, raise_exc):
""" "Run all rendered commands with the provided runner or the built-in
PopenRunner which runs commands locally.
"""
# use provided runner or default provided by hook
runner = runner or plugin.mng.hook.pysipp_new_runner()
agents = scen.prepare()

def finalize(cmds2procs=None, timeout=180, raise_exc=True):
"""Wait for all remaining agents in the scenario to finish executing
and perform error and logfile reporting.
"""
cmds2procs = cmds2procs or runner.get(timeout=timeout)
agents2procs = list(zip(agents, cmds2procs.values()))
msg = report.err_summary(agents2procs)
if msg:
# report logs and stderr
report.emit_logfiles(agents2procs)
if raise_exc:
# raise RuntimeError on agent failure(s)
# (HINT: to rerun type `scen()` from the debugger)
raise SIPpFailure(msg)

return cmds2procs

try:
# run all agents (raises RuntimeError on timeout)
cmds2procs = runner(
(ua.render() for ua in agents), block=block, timeout=timeout
)
except launch.TimeoutError: # sucessful timeout
cmds2procs = finalize(timeout=0, raise_exc=False)
if raise_exc:
raise
else:
# async
if not block:
# XXX async run must bundle up results for later processing
scen.finalize = finalize
return finalize

# sync
finalize(cmds2procs, raise_exc=raise_exc)

return runner


# register the default hook set
plugin.mng.register(sys.modules[__name__])
58 changes: 32 additions & 26 deletions pysipp/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
from collections import namedtuple
from collections import OrderedDict
from copy import deepcopy
from functools import partial
from os import path

import trio
from distutils import spawn

from . import command
from . import launch
from . import plugin
from . import utils

Expand Down Expand Up @@ -66,26 +69,18 @@ def name(self):
ipcaddr = tuple_property(("ipc_host", "ipc_port"))
call_load = tuple_property(("rate", "limit", "call_count"))

def __call__(
self, block=True, timeout=180, runner=None, raise_exc=True, **kwargs
):
def __call__(self, *args, **kwargs):
return self.run(*args, **kwargs)

def run(self, timeout=180, **kwargs):

# create and configure a temp scenario
scen = plugin.mng.hook.pysipp_conf_scen_protocol(
agents=[self],
confpy=None,
scenkwargs={},
)
# run the standard protocol
# (attach allocted runner for reuse/post-portem)
return plugin.mng.hook.pysipp_run_protocol(
scen=scen,
block=block,
timeout=timeout,
runner=runner,
raise_exc=raise_exc,
**kwargs
)
return scen.run(timeout=timeout, **kwargs)

def is_client(self):
return "uac" in self.name.lower()
Expand Down Expand Up @@ -174,8 +169,12 @@ def ua(logdir=None, **kwargs):
"""Default user agent factory.
Returns a command string instance with sensible default arguments.
"""
bin_path = spawn.find_executable("sipp")
if not bin_path:
raise RuntimeError("SIPp binary (sipp) does not seem to be accessible")

defaults = {
"bin_path": spawn.find_executable("sipp"),
"bin_path": bin_path,
}
# drop any built-in scen if a script file is provided
if "scen_file" in kwargs:
Expand Down Expand Up @@ -277,6 +276,9 @@ def __init__(
confpy=None,
enable_screen_file=True,
):
# placeholder for process "runner"
self._runner = None

# agents iterable in launch-order
self._agents = agents
ua_attrs = UserAgent.keys()
Expand Down Expand Up @@ -452,21 +454,25 @@ def from_agents(self, agents=None, autolocalsocks=True, **scenkwargs):
self.prepare(agents), self._defaults, confpy=self.mod
)

def __call__(
async def arun(
self,
agents=None,
block=True,
timeout=180,
runner=None,
raise_exc=True,
copy_agents=False,
**kwargs
):
return plugin.mng.hook.pysipp_run_protocol(
scen=self,
block=block,
agents = self.prepare()
runner = runner or launch.TrioRunner()

return await launch.run_all_agents(
runner,
agents,
timeout=timeout,
runner=runner,
raise_exc=raise_exc,
**kwargs
)

def run(self, timeout=180, **kwargs):
"""Run scenario blocking to completion."""
return trio.run(partial(self.arun, timeout=timeout, **kwargs))

def __call__(self, *args, **kwargs):
# TODO: deprecation warning here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kwargs.pop("block", None)

I had to add this to avoid getting an error

kwargs.pop("block", None)
return self.run(*args, **kwargs)
15 changes: 0 additions & 15 deletions pysipp/hookspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,3 @@ def pysipp_conf_scen(agents, scen):
socket arguments. It it the recommended hook for applying a default
scenario configuration.
"""


@hookspec(firstresult=True)
def pysipp_new_runner():
"""Create and return a runner instance to be used for invoking
multiple SIPp commands. The runner must be callable and support both a
`block` and `timeout` kwarg.
"""


@hookspec(firstresult=True)
def pysipp_run_protocol(scen, runner, block, timeout, raise_exc):
"""Perform steps to execute all SIPp commands usually by calling a
preconfigured command launcher/runner.
"""
Loading