From 14cb7dc2512d6798afb11866e0c54b0eb3c6d4d4 Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Thu, 27 Apr 2017 13:34:23 -0700 Subject: [PATCH] caught up with latest gitlab commits, added a new tests directory leveraging pytest based tests for testing core functionality --- daemon/core/broker.py | 47 ++-- daemon/core/corehandlers.py | 23 +- daemon/core/emane/emanemanager.py | 19 ++ daemon/core/mobility.py | 49 ++-- daemon/core/netns/nodes.py | 2 +- daemon/core/netns/vif.py | 17 +- daemon/core/sdt.py | 3 +- daemon/core/session.py | 22 +- daemon/examples/netns/basicrange.py | 1 + daemon/requirements.txt | 2 + daemon/setup.cfg | 2 + daemon/setup.py | 2 + daemon/tests/conftest.py | 142 ++++++++++++ daemon/tests/test_core.py | 335 ++++++++++++++++++++++++++++ 14 files changed, 570 insertions(+), 96 deletions(-) create mode 100644 daemon/setup.cfg create mode 100644 daemon/tests/conftest.py create mode 100644 daemon/tests/test_core.py diff --git a/daemon/core/broker.py b/daemon/core/broker.py index 5672ce3d..ac264ea0 100644 --- a/daemon/core/broker.py +++ b/daemon/core/broker.py @@ -38,6 +38,7 @@ from core.phys.pnodes import PhysicalNode logger = log.get_logger(__name__) +# TODO: name conflict with main core server, probably should rename class CoreServer(object): def __init__(self, name, host, port): self.name = name @@ -93,7 +94,6 @@ class CoreBroker(ConfigurableManager): self.nodemap_lock = threading.Lock() # reference counts of nodes on servers self.nodecounts = {} - self.bootcount = 0 # set of node numbers that are link-layer nodes (networks) self.network_nodes = set() # set of node numbers that are PhysicalNode nodes @@ -224,11 +224,6 @@ class CoreBroker(ConfigurableManager): nodenum = msg.get_tlv(NodeTlvs.NUMBER.value) if nodenum is not None: count = self.delnodemap(server, nodenum) - # snoop node add response to increment booted node count - # (only CoreNodes send these response messages) - elif msgflags & (MessageFlags.ADD.value | MessageFlags.LOCAL.value): - self.incrbootcount() - self.session.check_runtime() elif msgtype == MessageTypes.LINK.value: # this allows green link lines for remote WLANs msg = coreapi.CoreLinkMessage(msgflags, msghdr, msgdata) @@ -485,19 +480,6 @@ class CoreBroker(ConfigurableManager): self.nodecounts[server] = count return count - def incrbootcount(self): - """ - Count a node that has booted. - """ - self.bootcount += 1 - return self.bootcount - - def getbootcount(self): - """ - Return the number of booted nodes. - """ - return self.bootcount - def getserversbynode(self, nodenum): """ Retrieve a set of emulation servers given a node number. @@ -588,13 +570,14 @@ class CoreBroker(ConfigurableManager): :rtype: bool """ servers = set() + handle_locally = False # Do not forward messages when in definition state # (for e.g. configuring services) if self.session.state == EventTypes.DEFINITION_STATE.value: return False # Decide whether message should be handled locally or forwarded, or both if message.message_type == MessageTypes.NODE.value: - servers = self.handlenodemsg(message) + handle_locally, servers = self.handlenodemsg(message) elif message.message_type == MessageTypes.EVENT.value: # broadcast events everywhere servers = self.getservers() @@ -611,7 +594,7 @@ class CoreBroker(ConfigurableManager): servers = self.getservers() if message.message_type == MessageTypes.LINK.value: # prepare a server list from two node numbers in link message - servers, message = self.handlelinkmsg(message) + handle_locally, servers, message = self.handlelinkmsg(message) elif len(servers) == 0: # check for servers based on node numbers in all messages but link nn = message.node_numbers() @@ -626,7 +609,7 @@ class CoreBroker(ConfigurableManager): handler(message) # Perform any message forwarding - handle_locally = self.forwardmsg(message, servers) + handle_locally |= self.forwardmsg(message, servers) return not handle_locally def setupserver(self, servername): @@ -694,6 +677,7 @@ class CoreBroker(ConfigurableManager): :return: """ servers = set() + handle_locally = False serverfiletxt = None # snoop Node Message for emulation server TLV and record mapping n = message.tlv_data[NodeTlvs.NUMBER.value] @@ -704,25 +688,21 @@ class CoreBroker(ConfigurableManager): nodecls = nodeutils.get_node_class(NodeTypes(nodetype)) except KeyError: logger.warn("broker invalid node type %s" % nodetype) - return servers + return handle_locally, servers if nodecls is None: logger.warn("broker unimplemented node type %s" % nodetype) - return servers + return handle_locally, servers if issubclass(nodecls, PyCoreNet) and nodetype != NodeTypes.WIRELESS_LAN.value: # network node replicated on all servers; could be optimized # don"t replicate WLANs, because ebtables rules won"t work servers = self.getservers() + handle_locally = True self.addnet(n) for server in servers: self.addnodemap(server, n) # do not record server name for networks since network # nodes are replicated across all server - return servers - if issubclass(nodecls, PyCoreNet) and nodetype == NodeTypes.WIRELESS_LAN.value: - # special case where remote WLANs not in session._objs, and no - # node response message received, so they are counted here - if message.get_tlv(NodeTlvs.EMULATION_SERVER.value) is not None: - self.incrbootcount() + return handle_locally, servers elif issubclass(nodecls, PyCoreNode): name = message.get_tlv(NodeTlvs.NAME.value) if name: @@ -743,7 +723,7 @@ class CoreBroker(ConfigurableManager): # hook to update coordinates of physical nodes if n in self.physical_nodes: self.session.mobility.physnodeupdateposition(message) - return servers + return handle_locally, servers def handlelinkmsg(self, message): """ @@ -804,10 +784,11 @@ class CoreBroker(ConfigurableManager): self.addtunnel(host, nn[0], nn[1], localn) elif message.flags & MessageFlags.DELETE.value: self.deltunnel(nn[0], nn[1]) + handle_locally = False else: servers = servers1.union(servers2) - return servers, message + return handle_locally, servers, message def addlinkendpoints(self, message, servers1, servers2): """ @@ -946,7 +927,7 @@ class CoreBroker(ConfigurableManager): if server is not None: server.instantiation_complete = True - if self.session.is_connected(): + if self.session_handler: tlvdata = "" tlvdata += coreapi.CoreEventTlv.pack(EventTlvs.TYPE.value, EventTypes.INSTANTIATION_COMPLETE.value) msg = coreapi.CoreEventMessage.pack(0, tlvdata) diff --git a/daemon/core/corehandlers.py b/daemon/core/corehandlers.py index 8932d30b..7283eb3c 100644 --- a/daemon/core/corehandlers.py +++ b/daemon/core/corehandlers.py @@ -71,6 +71,7 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler): } self.message_queue = Queue.Queue() self.node_status_request = {} + self._shutdown_lock = threading.Lock() self.handler_threads = [] num_threads = int(server.config["numthreads"]) @@ -602,22 +603,16 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler): self.node_status_request[node_id] = True elif message.flags & MessageFlags.DELETE.value: - # TODO: logic below seems pointless, when the deletion is attempted regardless - node = None - try: - node = self.session.get_object(node_id) - except KeyError: - logger.exception("error retrieving object: %s", node_id) + with self._shutdown_lock: + self.session.delete_object(node_id) - self.session.delete_object(node_id) + if message.flags & MessageFlags.STRING.value: + tlvdata = "" + tlvdata += coreapi.CoreNodeTlv.pack(NodeTlvs.NUMBER.value, node_id) + flags = MessageFlags.DELETE.value | MessageFlags.LOCAL.value + replies.append(coreapi.CoreNodeMessage.pack(flags, tlvdata)) - if message.flags & MessageFlags.STRING.value: - tlv_data = "" - tlv_data += coreapi.CoreNodeTlv.pack(NodeTlvs.NUMBER.value, node_id) - flags = MessageFlags.DELETE.value | MessageFlags.LOCAL.value - replies.append(coreapi.CoreNodeMessage.pack(flags, tlv_data)) - - self.session.check_shutdown() + self.session.check_shutdown() # Node modify message (no add/del flag) else: try: diff --git a/daemon/core/emane/emanemanager.py b/daemon/core/emane/emanemanager.py index 914f0d8a..6717bc4f 100644 --- a/daemon/core/emane/emanemanager.py +++ b/daemon/core/emane/emanemanager.py @@ -457,6 +457,7 @@ class EmaneManager(ConfigurableManager): servers.append(s) self._objslock.release() + servers.sort(key=lambda x: x.name) for server in servers: if server.name == "localhost": continue @@ -1139,6 +1140,24 @@ class EmaneManager(ConfigurableManager): return True + def emanerunning(self, node): + """ + Return True if an EMANE process associated with the given node + is running, False otherwise. + """ + status = -1 + cmd = ['pkill', '-0', '-x', 'emane'] + + try: + if self.version < emane.EMANE092: + status = subprocess.call(cmd) + else: + status = node.cmd(cmd, wait=True) + except IOError: + logger.exception("error checking if emane is running") + + return status == 0 + class EmaneGlobalModel(EmaneModel): """ diff --git a/daemon/core/mobility.py b/daemon/core/mobility.py index 00ad3b2b..268f4298 100644 --- a/daemon/core/mobility.py +++ b/daemon/core/mobility.py @@ -422,33 +422,34 @@ class BasicRangeModel(WirelessModel): """ if netif == netif2: return - try: - (x, y, z) = self._netifs[netif] - (x2, y2, z2) = self._netifs[netif2] - except KeyError: - return - if x2 is None or y2 is None: - return - d = self.calcdistance((x, y, z), (x2, y2, z2)) - # ordering is important, to keep the wlan._linked dict organized - a = min(netif, netif2) - b = max(netif, netif2) try: - self.wlan._linked_lock.acquire() - linked = self.wlan.linked(a, b) + x, y, z = self._netifs[netif] + x2, y2, z2 = self._netifs[netif2] + + if x2 is None or y2 is None: + return + + d = self.calcdistance((x, y, z), (x2, y2, z2)) + + # ordering is important, to keep the wlan._linked dict organized + a = min(netif, netif2) + b = max(netif, netif2) + + with self.wlan._linked_lock: + linked = self.wlan.linked(a, b) + + logger.info("checking if link distance is out of range: %s > %s", d, self.range) + if d > self.range: + if linked: + self.wlan.unlink(a, b) + self.sendlinkmsg(a, b, unlink=True) + else: + if not linked: + self.wlan.link(a, b) + self.sendlinkmsg(a, b) except KeyError: - return - finally: - self.wlan._linked_lock.release() - if d > self.range: - if linked: - self.wlan.unlink(a, b) - self.sendlinkmsg(a, b, unlink=True) - else: - if not linked: - self.wlan.link(a, b) - self.sendlinkmsg(a, b) + logger.exception("error getting interfaces during calclinkS") @staticmethod def calcdistance(p1, p2): diff --git a/daemon/core/netns/nodes.py b/daemon/core/netns/nodes.py index d76b5e12..8cb22ebb 100644 --- a/daemon/core/netns/nodes.py +++ b/daemon/core/netns/nodes.py @@ -312,7 +312,7 @@ class WlanNode(LxBrNet): for netif in self.netifs(): netif.poshook = self.model.position_callback if netif.node is not None: - (x, y, z) = netif.node.position.get() + x, y, z = netif.node.position.get() netif.poshook(netif, x, y, z) self.model.setlinkparams() elif model.config_type == RegisterTlvs.MOBILITY.value: diff --git a/daemon/core/netns/vif.py b/daemon/core/netns/vif.py index 7841eed5..1552d8ef 100644 --- a/daemon/core/netns/vif.py +++ b/daemon/core/netns/vif.py @@ -8,7 +8,9 @@ import time from core import constants from core.coreobj import PyCoreNetIf +from core.enumerations import NodeTypes from core.misc import log +from core.misc import nodeutils from core.misc import utils logger = log.get_logger(__name__) @@ -116,7 +118,20 @@ class TunTap(PyCoreNetIf): cmd = (constants.IP_BIN, 'link', 'show', self.name) return self.node.cmd(cmd) - self.waitfor(nodedevexists) + count = 0 + while True: + try: + self.waitfor(nodedevexists) + break + except RuntimeError as e: + # check if this is an EMANE interface; if so, continue + # waiting if EMANE is still running + # TODO: remove emane code + if count < 5 and nodeutils.is_node(self.net, NodeTypes.EMANE) and \ + self.node.session.emane.emanerunning(self.node): + count += 1 + else: + raise e def install(self): """ diff --git a/daemon/core/sdt.py b/daemon/core/sdt.py index 788b69aa..d89a1e6d 100644 --- a/daemon/core/sdt.py +++ b/daemon/core/sdt.py @@ -40,8 +40,7 @@ class Sdt(object): DEFAULT_SDT_URL = "tcp://127.0.0.1:50000/" # default altitude (in meters) for flyto view DEFAULT_ALT = 2500 - # TODO: read in user's nodes.conf here; below are default node types - # from the GUI + # TODO: read in user's nodes.conf here; below are default node types from the GUI DEFAULT_SPRITES = [ ('router', 'router.gif'), ('host', 'host.gif'), ('PC', 'pc.gif'), ('mdr', 'mdr.gif'), diff --git a/daemon/core/session.py b/daemon/core/session.py index 4bfe2c7c..d72015bb 100644 --- a/daemon/core/session.py +++ b/daemon/core/session.py @@ -636,12 +636,9 @@ class Session(object): with self._objects_lock: try: obj = self.objects.pop(object_id) + obj.shutdown() except KeyError: logger.error("failed to remove object, object with id was not found: %s", object_id) - obj = None - - if obj: - obj.shutdown() def delete_objects(self): """ @@ -840,23 +837,6 @@ class Session(object): logger.info("valid runtime state found, returning") return - # session_node_count = self.get_node_count() - # - # node_count = self.get_node_count() - - # count booted nodes not emulated on this server - # TODO: let slave server determine RUNTIME and wait for Event Message - # broker.getbootocunt() counts all CoreNodes from status reponse - # messages, plus any remote WLANs; remote EMANE, hub, switch, etc. - # are already counted in self._objs - # node_count += self.broker.getbootcount() - # logger.info("Checking for runtime with %d of %d session nodes", - # node_count, session_node_count) - # if node_count < session_node_count: - # return # do not have information on all nodes yet - - # information on all nodes has been received and they have been started enter the runtime state - # check to verify that all nodes and networks are running if not self.broker.instantiation_complete(): return diff --git a/daemon/examples/netns/basicrange.py b/daemon/examples/netns/basicrange.py index 474dfa6e..d8fdd869 100755 --- a/daemon/examples/netns/basicrange.py +++ b/daemon/examples/netns/basicrange.py @@ -60,6 +60,7 @@ def test(options): # launches terminal for the first node # n[0].term("bash") + n[0].icmd(["ping", "-c", "5", "127.0.0.1"]) # wait for rate seconds to allow ebtables commands to commit time.sleep(EbtablesQueue.rate) diff --git a/daemon/requirements.txt b/daemon/requirements.txt index 4597649a..897df186 100644 --- a/daemon/requirements.txt +++ b/daemon/requirements.txt @@ -4,3 +4,5 @@ grpcio-tools==1.0.0 pycco==0.5.1 sphinx==1.4.8 sphinx_rtd_theme==0.1.9 +pytest==3.0.7 +pytest-runner==2.11.1 diff --git a/daemon/setup.cfg b/daemon/setup.cfg new file mode 100644 index 00000000..9af7e6f1 --- /dev/null +++ b/daemon/setup.cfg @@ -0,0 +1,2 @@ +[aliases] +test=pytest \ No newline at end of file diff --git a/daemon/setup.py b/daemon/setup.py index 429922b0..e610ab86 100644 --- a/daemon/setup.py +++ b/daemon/setup.py @@ -27,6 +27,8 @@ setup(name="core-python", install_requires=[ "enum34" ], + setup_requires=["pytest-runner"], + tests_require=["pytest"], description="Python components of CORE", url="http://www.nrl.navy.mil/itd/ncs/products/core", author="Boeing Research & Technology", diff --git a/daemon/tests/conftest.py b/daemon/tests/conftest.py new file mode 100644 index 00000000..60d20829 --- /dev/null +++ b/daemon/tests/conftest.py @@ -0,0 +1,142 @@ +""" +Unit test fixture module. +""" + +import os +import pytest + +from core.session import Session +from core.misc import ipaddress +from core.misc import nodemaps +from core.misc import nodeutils +from core.netns import nodes + + +class Core(object): + def __init__(self, session, ip_prefix): + self.session = session + self.ip_prefix = ip_prefix + self.current_ip = 1 + self.nodes = {} + self.node_ips = {} + + def create_node(self, name): + node = self.session.add_object(cls=nodes.CoreNode, name=name) + self.nodes[name] = node + + def add_interface(self, network, name): + node_ip = self.ip_prefix.addr(self.current_ip) + self.current_ip += 1 + self.node_ips[name] = node_ip + node = self.nodes[name] + interface_id = node.newnetif(network, ["%s/%s" % (node_ip, self.ip_prefix.prefixlen)]) + return node.netif(interface_id) + + def get_node(self, name): + """ + Retrieve node from current session. + + :param str name: name of node to retrieve + :return: core node + :rtype: core.netns.nodes.CoreNode + """ + return self.nodes[name] + + def get_ip(self, name): + return self.node_ips[name] + + def link(self, network, from_interface, to_interface): + network.link(from_interface, to_interface) + + def configure_link(self, network, interface_one, interface_two, values, unidirectional=False): + network.linkconfig( + netif=interface_one, + netif2=interface_two, + **values + ) + + if not unidirectional: + network.linkconfig( + netif=interface_two, + netif2=interface_one, + **values + ) + + def ping(self, from_name, to_name): + from_node = self.nodes[from_name] + to_ip = str(self.get_ip(to_name)) + return from_node.cmd(["ping", "-c", "3", to_ip]) + + def ping_output(self, from_name, to_name): + from_node = self.nodes[from_name] + to_ip = str(self.get_ip(to_name)) + vcmd, stdin, stdout, stderr = from_node.popen(["ping", "-i", "0.05", "-c", "3", to_ip]) + return stdout.read().strip() + + def iping(self, from_name, to_name): + from_node = self.nodes[from_name] + to_ip = str(self.get_ip(to_name)) + from_node.icmd(["ping", "-i", "0.01", "-c", "10", to_ip]) + + def iperf(self, from_name, to_name): + from_node = self.nodes[from_name] + to_node = self.nodes[to_name] + to_ip = str(self.get_ip(to_name)) + + # run iperf server, run client, kill iperf server + vcmd, stdin, stdout, stderr = to_node.popen(["iperf", "-s", "-u", "-y", "C"]) + from_node.cmd(["iperf", "-u", "-t", "5", "-c", to_ip]) + to_node.cmd(["killall", "-9", "iperf"]) + + return stdout.read().strip() + + def assert_nodes(self): + for node in self.nodes.itervalues(): + assert os.path.exists(node.nodedir) + + def create_link_network(self): + # create switch + ptp_node = self.session.add_object(cls=nodes.PtpNet) + + # create nodes + self.create_node("n1") + self.create_node("n2") + + # add interfaces + interface_one = self.add_interface(ptp_node, "n1") + interface_two = self.add_interface(ptp_node, "n2") + + # instantiate session + self.session.instantiate() + + # assert node directories created + self.assert_nodes() + + return ptp_node, interface_one, interface_two + + +@pytest.fixture() +def session(): + # configure default nodes + node_map = nodemaps.CLASSIC_NODES + nodeutils.set_node_map(node_map) + + # create and return session + session_fixture = Session(1, persistent=True) + assert os.path.exists(session_fixture.session_dir) + yield session_fixture + + # cleanup + print "shutting down session" + session_fixture.shutdown() + assert not os.path.exists(session_fixture.session_dir) + + +@pytest.fixture(scope="module") +def ip_prefix(): + return ipaddress.Ipv4Prefix("10.83.0.0/16") + + +@pytest.fixture() +def core(session, ip_prefix): + return Core(session, ip_prefix) diff --git a/daemon/tests/test_core.py b/daemon/tests/test_core.py new file mode 100644 index 00000000..1f0c01aa --- /dev/null +++ b/daemon/tests/test_core.py @@ -0,0 +1,335 @@ +""" +Unit tests for testing with a CORE switch. +""" +from core.mobility import BasicRangeModel +from core.netns import nodes + + +class TestCore: + def test_ptp(self, core): + """ + Test ptp node network. + + :param conftest.Core core: core fixture to test with + """ + + # create switch + ptp_node = core.session.add_object(cls=nodes.PtpNet) + + # create nodes + core.create_node("n1") + core.create_node("n2") + + # add interfaces + core.add_interface(ptp_node, "n1") + core.add_interface(ptp_node, "n2") + + # instantiate session + core.session.instantiate() + + # assert node directories created + core.assert_nodes() + + # ping n2 from n1 and assert success + status = core.ping("n1", "n2") + assert not status + + def test_hub(self, core): + """ + Test basic hub network. + + :param conftest.Core core: core fixture to test with + """ + + # create switch + hub_node = core.session.add_object(cls=nodes.HubNode) + + # create nodes + core.create_node("n1") + core.create_node("n2") + + # add interfaces + core.add_interface(hub_node, "n1") + core.add_interface(hub_node, "n2") + + # instantiate session + core.session.instantiate() + + # assert node directories created + core.assert_nodes() + + # ping n2 from n1 and assert success + status = core.ping("n1", "n2") + assert not status + + def test_switch(self, core): + """ + Test basic switch network. + + :param conftest.Core core: core fixture to test with + """ + + # create switch + switch_node = core.session.add_object(cls=nodes.SwitchNode) + + # create nodes + core.create_node("n1") + core.create_node("n2") + + # add interfaces + core.add_interface(switch_node, "n1") + core.add_interface(switch_node, "n2") + + # instantiate session + core.session.instantiate() + + # assert node directories created + core.assert_nodes() + + # ping n2 from n1 and assert success + status = core.ping("n1", "n2") + assert not status + + def test_wlan_basic_range_good(self, core): + """ + Test basic wlan network. + + :param conftest.Core core: core fixture to test with + """ + + # create wlan + wlan_node = core.session.add_object(cls=nodes.WlanNode) + values = BasicRangeModel.getdefaultvalues() + wlan_node.setmodel(BasicRangeModel, values) + + # create nodes + core.create_node("n1") + core.create_node("n2") + + # add interfaces + interface_one = core.add_interface(wlan_node, "n1") + interface_two = core.add_interface(wlan_node, "n2") + + # link nodes in wlan + core.link(wlan_node, interface_one, interface_two) + + # mark node position as together + core.get_node("n1").setposition(0, 0) + core.get_node("n2").setposition(0, 0) + + # instantiate session + core.session.instantiate() + + # assert node directories created + core.assert_nodes() + + # ping n2 from n1 and assert success + status = core.ping("n1", "n2") + assert not status + + def test_wlan_basic_range_bad(self, core): + """ + Test basic wlan network with leveraging basic range model. + + :param conftest.Core core: core fixture to test with + """ + + # create wlan + wlan_node = core.session.add_object(cls=nodes.WlanNode) + values = BasicRangeModel.getdefaultvalues() + wlan_node.setmodel(BasicRangeModel, values) + + # create nodes + core.create_node("n1") + core.create_node("n2") + + # add interfaces + interface_one = core.add_interface(wlan_node, "n1") + interface_two = core.add_interface(wlan_node, "n2") + + # link nodes in wlan + core.link(wlan_node, interface_one, interface_two) + + # move nodes out of range, default range check is 275 + core.get_node("n1").setposition(0, 0) + core.get_node("n2").setposition(500, 500) + + # instantiate session + core.session.instantiate() + + # assert node directories created + core.assert_nodes() + + # ping n2 from n1 and assert failure + import time + time.sleep(1) + status = core.ping("n1", "n2") + assert status + + def test_link_bandwidth(self, core): + """ + Test ptp node network with modifying link bandwidth. + + :param conftest.Core core: core fixture to test with + """ + + # create switch + ptp_node = core.session.add_object(cls=nodes.PtpNet) + + # create nodes + core.create_node("n1") + core.create_node("n2") + + # add interfaces + interface_one = core.add_interface(ptp_node, "n1") + interface_two = core.add_interface(ptp_node, "n2") + + # instantiate session + core.session.instantiate() + + # assert node directories created + core.assert_nodes() + + # output csv index + bandwidth_index = 8 + + # run iperf, validate normal bandwidth + stdout = core.iperf("n1", "n2") + assert stdout + value = int(stdout.split(',')[bandwidth_index]) + print "bandwidth before: %s" % value + assert 900000 <= value <= 1100000 + + # change bandwidth in bits per second + bandwidth = 500000 + core.configure_link(ptp_node, interface_one, interface_two, { + "bw": bandwidth + }) + + # run iperf again + stdout = core.iperf("n1", "n2") + assert stdout + value = int(stdout.split(',')[bandwidth_index]) + assert 400000 <= value <= 600000 + + def test_link_loss(self, core): + """ + Test ptp node network with modifying link packet loss. + + :param conftest.Core core: core fixture to test with + """ + + # create switch + ptp_node = core.session.add_object(cls=nodes.PtpNet) + + # create nodes + core.create_node("n1") + core.create_node("n2") + + # add interfaces + interface_one = core.add_interface(ptp_node, "n1") + interface_two = core.add_interface(ptp_node, "n2") + + # instantiate session + core.session.instantiate() + + # assert node directories created + core.assert_nodes() + + # output csv index + loss_index = -2 + + # run iperf, validate normal bandwidth + stdout = core.iperf("n1", "n2") + assert stdout + value = float(stdout.split(',')[loss_index]) + assert 0 <= value <= 0.5 + + # change bandwidth in bits per second + loss = 50 + core.configure_link(ptp_node, interface_one, interface_two, { + "loss": loss + }) + + # run iperf again + stdout = core.iperf("n1", "n2") + assert stdout + value = float(stdout.split(',')[loss_index]) + assert 45 <= value <= 55 + + def test_link_delay(self, core): + """ + Test ptp node network with modifying link packet delay. + + :param conftest.Core core: core fixture to test with + """ + + # create switch + ptp_node = core.session.add_object(cls=nodes.PtpNet) + + # create nodes + core.create_node("n1") + core.create_node("n2") + + # add interfaces + interface_one = core.add_interface(ptp_node, "n1") + interface_two = core.add_interface(ptp_node, "n2") + + # instantiate session + core.session.instantiate() + + # assert node directories created + core.assert_nodes() + + # run ping for delay information + stdout = core.ping_output("n1", "n2") + assert stdout + rtt_line = stdout.split("\n")[-1] + rtt_values = rtt_line.split("=")[1].split("ms")[0].strip() + rtt_avg = float(rtt_values.split("/")[2]) + assert 0 <= rtt_avg <= 0.1 + + # change delay in microseconds + delay = 1000000 + core.configure_link(ptp_node, interface_one, interface_two, { + "delay": delay + }) + + # run ping for delay information again + stdout = core.ping_output("n1", "n2") + assert stdout + rtt_line = stdout.split("\n")[-1] + rtt_values = rtt_line.split("=")[1].split("ms")[0].strip() + rtt_avg = float(rtt_values.split("/")[2]) + assert 1800 <= rtt_avg <= 2200 + + def test_link_jitter(self, core): + """ + Test ptp node network with modifying link packet jitter. + + :param conftest.Core core: core fixture to test with + """ + + # create link network + ptp_node, interface_one, interface_two = core.create_link_network() + + # output csv index + jitter_index = 9 + + # run iperf + stdout = core.iperf("n1", "n2") + assert stdout + value = float(stdout.split(",")[jitter_index]) + assert -0.5 <= value <= 0.05 + + # change jitter in microseconds + jitter = 1000000 + core.configure_link(ptp_node, interface_one, interface_two, { + "jitter": jitter + }) + + # run iperf again + stdout = core.iperf("n1", "n2") + assert stdout + value = float(stdout.split(",")[jitter_index]) + assert 200 <= value <= 500