From a95c3b376053ed00f0e7184dbd228598a9aaf50f Mon Sep 17 00:00:00 2001 From: Marco Capitani Date: Wed, 28 Mar 2018 15:46:38 +0200 Subject: model: make link quality changes scriptable Added method accepting only ints and floats to allow scripting link quality changes (see example-script additions) Also added general purpose `sb.schedule_action` method --- examples/example-script.rsb | 4 ++ rumba/model.py | 93 +++++++++++++++++++++++++++++++++++++++++++++ rumba/storyboard.py | 54 ++++++++++++++++++++++---- 3 files changed, 143 insertions(+), 8 deletions(-) diff --git a/examples/example-script.rsb b/examples/example-script.rsb index f00ff42..1d49a66 100644 --- a/examples/example-script.rsb +++ b/examples/example-script.rsb @@ -37,6 +37,10 @@ echo2, 18 &ev4| $sb run_client_of $Server.server_b 14 | $Node.node_a set_link_state $ShimEthDIF.e1 'up' +16 | $ShimEthDIF.e1 set_delay 30 10 + +28 | $ShimEthDIF.e1 set_loss 2 + diff --git a/rumba/model.py b/rumba/model.py index 6d0af58..a8d4664 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -189,6 +189,65 @@ class ShimEthDIF(DIF): def link_quality(self): self._link_quality.deactivate(self) + def set_delay(self, delay=0, + jitter=None, + correlation=None, + distribution=None): + """ + Set the delay parameters of the underlying link. + Parameters as in :py:meth:`.Delay.__init__` + :param delay: average delay in ms + :type delay: :py:class:`int` + :param jitter: jitter in ms + :type jitter: :py:class:`int` + :param correlation: correlation in % + :type correlation: :py:class:`int` + :param distribution: delay distribution, defaults to a Normal + distribution + :type distribution: :py:class:`.Distribution` + """ + new_delay = Delay(delay, jitter, correlation, distribution) + new_quality = LinkQuality.clone(self.link_quality, delay=new_delay) + self.link_quality = new_quality + + def set_loss(self, + loss=0, + correlation=None): + """ + Set the loss parameter of the underlying link. + Parameters as in :py:meth:`.Loss.__init__` + :param loss: loss in percentage + :type loss: :py:class:`int` or :py:class:`float` + :param correlation: correlation in percentage + :type correlation: :py:class:`int` or :py:class:`float` + """ + new_loss = Loss(loss, correlation) + new_quality = LinkQuality.clone(self.link_quality, loss=new_loss) + self.link_quality = new_quality + + def set_rate(self, rate=None): + """ + Set the rate parameter of the underlying link. + :param rate: The desired rate in mbps + :type rate: :py:class:`int` + """ + new_quality = LinkQuality.clone(self.link_quality, rate=rate) + self.link_quality = new_quality + + def set_quality(self, delay, loss, rate): + """ + Configure the basic quality parameters of the + underlying link + :param delay: the link delay, in ms + :type delay: :py:class:`int` + :param loss: the link loss, as a percentage + :type loss: :py:class:`float` or :py:class:`int` + :param rate: the link rate in mbps + :type rate: :py:class:`int` + """ + new_quality = LinkQuality(delay, loss, rate) + self.link_quality = new_quality + # Normal DIF # @@ -231,10 +290,14 @@ class Delay(object): """ Configure link delay :param delay: average delay in ms + :type delay: :py:class:`int` :param jitter: jitter in ms + :type jitter: :py:class:`int` :param correlation: correlation in % + :type correlation: :py:class:`int` :param distribution: delay distribution, defaults to a Normal distribution + :type distribution: :py:class:`.Distribution` """ if delay < 0: @@ -290,7 +353,9 @@ class Loss(object): """ Configure link loss :param loss: loss in percentage + :type loss: :py:class:`int` or :py:class:`float` :param correlation: correlation in percentage + :type correlation: :py:class:`int` or :py:class:`float` """ if loss and (loss < 0 or loss > 100): raise ValueError("Loss needs to be between 0 and 100") @@ -321,6 +386,34 @@ class Loss(object): class LinkQuality(object): _active = set() + @classmethod + def clone(cls, old_quality, delay=None, loss=None, rate=None): + """ + Clone old_quality, updating it with the provided parameters + if present + + :param old_quality: A :py:class`.LinkQuality` instance to + use as a base + :type old_quality: :py:class`.LinkQuality` + :param delay: Delay object holding delay configuration + or number corresponding to delay in ms + :type delay: :py:class:`.Delay` or :py:class:`int` + :param loss: Loss object holding delay configuration or + number corresponding to loss percentage + :type loss: :py:class:`.Loss` or :py:class:`float` + :param rate: The rate of the link in mbit + :type rate: :py:class:`int` + :return: a new :py:class`.LinkQuality` instance + :rtype :py:class`LinkQuality` + """ + if delay is None: + delay = old_quality.delay + if loss is None: + loss = old_quality.loss + if rate is None: + rate = old_quality.rate + return LinkQuality(delay, loss, rate) + def __init__(self, delay=None, loss=None, rate=None): """ Link quality configuration diff --git a/rumba/storyboard.py b/rumba/storyboard.py index 9aff375..ba6883d 100644 --- a/rumba/storyboard.py +++ b/rumba/storyboard.py @@ -423,6 +423,39 @@ class StoryBoard(SBEntity): del self.server_apps[server.id] self._build_nodes_lists() + def schedule_action(self, + call, + args=None, + kwargs=None, + c_time=None, + trigger=None, + ev_id=None,): + """ + Calls a function with the specified triggers and arguments. + :param call: the function to run + :type call: function (methods included) + :param c_time: the function will not be called before `c_time` + seconds have passed + :type c_time: :py:class:`float` + :param trigger: the function must not be called before the event + `trigger` has completed + :type trigger: :py:class:`.Event` or :py:class:`str` + :param ev_id: the ID to assign to the generated event + :type ev_id: :py:class:`str` + :param args: arguments to pass to the function + :param kwargs: keyword arguments to be passed + :return: the event representing the calling of the function + :rtype: :py:class:`.Event` + """ + if args is None: + args = [] + if kwargs is None: + kwargs = {} + action = functools.partial(call, *args, **kwargs) + event = Event(action, ev_id=ev_id, ev_time=c_time, trigger=trigger) + self.add_event(event) + return event + def schedule_command(self, t, node, command): """ Schedules the given command to be run at t seconds from the start. @@ -846,14 +879,19 @@ class StoryBoard(SBEntity): str(uuid.uuid4())[0:4] + ".pcap" tcpd_client = Client(ap="tcpdump", options="-i %s -w %s" \ - % (ipcp.ifname, pcap_file)) - duration = end - start - cb = functools.partial(node.fetch_file, pcap_file, - self.experiment.log_dir, sudo=True) - action = functools.partial(self.run_client, tcpd_client, - duration=duration, node=node, - callback = cb) - self._script.add_event(Event(action, ev_time=start)) + % (ipcp.ifname, pcap_file))\ + .process(end-start, node, 'tcpdump_proc') + + self.schedule_action(tcpd_client.run, c_time=start) + end_event = self.schedule_action(tcpd_client.stop, c_time=end) + + self.schedule_action( + node.fetch_file, + args=[pcap_file, self.experiment.log_dir], + kwargs={'sudo': True}, + trigger=end_event + ) + class Event(object): -- cgit v1.2.3