diff --git a/daemon/core/api/grpc/client.py b/daemon/core/api/grpc/client.py index a1d218be..9a4f0d63 100644 --- a/daemon/core/api/grpc/client.py +++ b/daemon/core/api/grpc/client.py @@ -246,7 +246,7 @@ class CoreGrpcClient(object): :rtype: core_pb2.SetSessionLocationResponse :raises grpc.RpcError: when session doesn't exist """ - position = core_pb2.Position(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) + position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) request = core_pb2.SetSessionLocationRequest(session_id=session_id, position=position, scale=scale) return self.stub.SetSessionLocation(request) diff --git a/daemon/core/api/grpc/server.py b/daemon/core/api/grpc/server.py index a7955cb1..120eb98e 100644 --- a/daemon/core/api/grpc/server.py +++ b/daemon/core/api/grpc/server.py @@ -195,7 +195,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): session = self.get_session(request.session_id, context) x, y, z = session.location.refxyz lat, lon, alt = session.location.refgeo - position = core_pb2.Position(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) + position = core_pb2.SessionPosition(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) return core_pb2.GetSessionLocationResponse(position=position, scale=session.location.refscale) def SetSessionLocation(self, request, context): diff --git a/daemon/core/api/tlv/corehandlers.py b/daemon/core/api/tlv/corehandlers.py index 5a145704..efb2a6bc 100644 --- a/daemon/core/api/tlv/corehandlers.py +++ b/daemon/core/api/tlv/corehandlers.py @@ -324,6 +324,9 @@ class CoreHandler(socketserver.BaseRequestHandler): per = "" if link_data.per is not None: per = str(link_data.per) + dup = "" + if link_data.dup is not None: + dup = str(link_data.dup) tlv_data = structutils.pack_values(coreapi.CoreLinkTlv, [ (LinkTlvs.N1_NUMBER, link_data.node1_id), @@ -331,7 +334,7 @@ class CoreHandler(socketserver.BaseRequestHandler): (LinkTlvs.DELAY, link_data.delay), (LinkTlvs.BANDWIDTH, link_data.bandwidth), (LinkTlvs.PER, per), - (LinkTlvs.DUP, link_data.dup), + (LinkTlvs.DUP, dup), (LinkTlvs.JITTER, link_data.jitter), (LinkTlvs.MER, link_data.mer), (LinkTlvs.BURST, link_data.burst), diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index 556fa88e..de2a38c3 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -367,8 +367,7 @@ class Session(object): if node_two: node_two.lock.release() - def update_link(self, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None, - link_options=LinkOptions()): + def update_link(self, node_one_id, node_two_id, interface_one_id=None, interface_two_id=None, link_options=None): """ Update link information between nodes. @@ -379,6 +378,9 @@ class Session(object): :param core.emulator.emudata.LinkOptions link_options: data to update link with :return: nothing """ + if not link_options: + link_options = LinkOptions() + # get node objects identified by link data node_one, node_two, net_one, net_two, _tunnel = self._link_nodes(node_one_id, node_two_id) @@ -441,7 +443,6 @@ class Session(object): link_config(net_one, interface_one, link_options, interface_two=interface_two) if not link_options.unidirectional: link_config(net_one, interface_two, link_options, interface_two=interface_one) - finally: if node_one: node_one.lock.release() diff --git a/daemon/core/location/mobility.py b/daemon/core/location/mobility.py index 8303813f..4c74447a 100644 --- a/daemon/core/location/mobility.py +++ b/daemon/core/location/mobility.py @@ -318,11 +318,11 @@ class BasicRangeModel(WirelessModel): name = "basic_range" options = [ Configuration(_id="range", _type=ConfigDataTypes.UINT32, default="275", label="wireless range (pixels)"), - Configuration(_id="bandwidth", _type=ConfigDataTypes.UINT32, default="54000000", label="bandwidth (bps)"), - Configuration(_id="jitter", _type=ConfigDataTypes.FLOAT, default="0.0", label="transmission jitter (usec)"), - Configuration(_id="delay", _type=ConfigDataTypes.FLOAT, default="5000.0", + Configuration(_id="bandwidth", _type=ConfigDataTypes.UINT64, default="54000000", label="bandwidth (bps)"), + Configuration(_id="jitter", _type=ConfigDataTypes.UINT64, default="0", label="transmission jitter (usec)"), + Configuration(_id="delay", _type=ConfigDataTypes.UINT64, default="5000", label="transmission delay (usec)"), - Configuration(_id="error", _type=ConfigDataTypes.FLOAT, default="0.0", label="error rate (%)") + Configuration(_id="error", _type=ConfigDataTypes.STRING, default="0", label="error rate (%)") ] @classmethod @@ -358,19 +358,19 @@ class BasicRangeModel(WirelessModel): :param dict config: values to convert :return: nothing """ - self.range = float(config["range"]) + self.range = int(config["range"]) logging.info("basic range model configured for WLAN %d using range %d", self.wlan.id, self.range) self.bw = int(config["bandwidth"]) - if self.bw == 0.0: + if self.bw == 0: self.bw = None - self.delay = float(config["delay"]) - if self.delay == 0.0: + self.delay = int(config["delay"]) + if self.delay == 0: self.delay = None - self.loss = float(config["error"]) - if self.loss == 0.0: + self.loss = int(config["error"]) + if self.loss == 0: self.loss = None - self.jitter = float(config["jitter"]) - if self.jitter == 0.0: + self.jitter = int(config["jitter"]) + if self.jitter == 0: self.jitter = None def setlinkparams(self): diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index 5520e375..b2be9fd8 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -459,10 +459,10 @@ class CoreNetwork(CoreNetworkBase): netem = ["netem"] changed = max(changed, netif.setparam("delay", delay)) if loss is not None: - loss = float(loss) + loss = int(loss) changed = max(changed, netif.setparam("loss", loss)) if duplicate is not None: - duplicate = float(duplicate) + duplicate = int(duplicate) changed = max(changed, netif.setparam("duplicate", duplicate)) changed = max(changed, netif.setparam("jitter", jitter)) if not changed: diff --git a/daemon/core/nodes/openvswitch.py b/daemon/core/nodes/openvswitch.py index 12923f4a..33bea7f3 100644 --- a/daemon/core/nodes/openvswitch.py +++ b/daemon/core/nodes/openvswitch.py @@ -225,11 +225,11 @@ class OvsNet(CoreNetworkBase): delay_changed = netif.setparam("delay", delay) if loss is not None: - loss = float(loss) + loss = int(loss) loss_changed = netif.setparam("loss", loss) if duplicate is not None: - duplicate = float(duplicate) + duplicate = int(duplicate) duplicate_changed = netif.setparam("duplicate", duplicate) jitter_changed = netif.setparam("jitter", jitter) diff --git a/daemon/core/xml/corexml.py b/daemon/core/xml/corexml.py index ce15873e..83f9f22e 100644 --- a/daemon/core/xml/corexml.py +++ b/daemon/core/xml/corexml.py @@ -755,9 +755,11 @@ class CoreXmlReader(object): if link_elements is None: return + node_sets = set() for link_element in link_elements.iterchildren(): node_one = get_int(link_element, "node_one") node_two = get_int(link_element, "node_two") + node_set = frozenset((node_one, node_two)) interface_one_element = link_element.find("interface_one") interface_one = None @@ -772,15 +774,15 @@ class CoreXmlReader(object): options_element = link_element.find("options") link_options = LinkOptions() if options_element is not None: - link_options.bandwidth = get_float(options_element, "bandwidth") - link_options.burst = get_float(options_element, "burst") - link_options.delay = get_float(options_element, "delay") - link_options.dup = get_float(options_element, "dup") - link_options.mer = get_float(options_element, "mer") - link_options.mburst = get_float(options_element, "mburst") - link_options.jitter = get_float(options_element, "jitter") - link_options.key = get_float(options_element, "key") - link_options.per = get_float(options_element, "per") + link_options.bandwidth = get_int(options_element, "bandwidth") + link_options.burst = get_int(options_element, "burst") + link_options.delay = get_int(options_element, "delay") + link_options.dup = get_int(options_element, "dup") + link_options.mer = get_int(options_element, "mer") + link_options.mburst = get_int(options_element, "mburst") + link_options.jitter = get_int(options_element, "jitter") + link_options.key = get_int(options_element, "key") + link_options.per = get_int(options_element, "per") link_options.unidirectional = get_int(options_element, "unidirectional") link_options.session = options_element.get("session") link_options.emulation_id = get_int(options_element, "emulation_id") @@ -788,5 +790,11 @@ class CoreXmlReader(object): link_options.opaque = options_element.get("opaque") link_options.gui_attributes = options_element.get("gui_attributes") - logging.info("reading link node_one(%s) node_two(%s)", node_one, node_two) - self.session.add_link(node_one, node_two, interface_one, interface_two, link_options) + if link_options.unidirectional == 1 and node_set in node_sets: + logging.info("updating link node_one(%s) node_two(%s): %s", node_one, node_two, link_options) + self.session.update_link(node_one, node_two, interface_one, interface_two, link_options) + else: + logging.info("adding link node_one(%s) node_two(%s): %s", node_one, node_two, link_options) + self.session.add_link(node_one, node_two, interface_one, interface_two, link_options) + + node_sets.add(node_set) diff --git a/daemon/proto/core/api/grpc/core.proto b/daemon/proto/core/api/grpc/core.proto index 8bbf676b..6f34070f 100644 --- a/daemon/proto/core/api/grpc/core.proto +++ b/daemon/proto/core/api/grpc/core.proto @@ -172,13 +172,13 @@ message GetSessionLocationRequest { } message GetSessionLocationResponse { - Position position = 1; + SessionPosition position = 1; float scale = 2; } message SetSessionLocationRequest { int32 session_id = 1; - Position position = 2; + SessionPosition position = 2; float scale = 3; } @@ -787,15 +787,15 @@ message Link { message LinkOptions { string opaque = 1; - float jitter = 2; - string key = 3; - float mburst = 4; - float mer = 5; - float per = 6; - float bandwidth = 7; - float burst = 8; - float delay = 9; - float dup = 10; + int64 jitter = 2; + int32 key = 3; + int32 mburst = 4; + int32 mer = 5; + int32 per = 6; + int64 bandwidth = 7; + int32 burst = 8; + int64 delay = 9; + int32 dup = 10; bool unidirectional = 11; } @@ -812,7 +812,7 @@ message Interface { int32 mtu = 10; } -message Position { +message SessionPosition { float x = 1; float y = 2; float z = 3; @@ -820,3 +820,12 @@ message Position { float lon = 5; float alt = 6; } + +message Position { + int32 x = 1; + int32 y = 2; + int32 z = 3; + float lat = 4; + float lon = 5; + float alt = 6; +} diff --git a/daemon/tests/test_xml.py b/daemon/tests/test_xml.py index e5e8cf99..11bb2706 100644 --- a/daemon/tests/test_xml.py +++ b/daemon/tests/test_xml.py @@ -463,3 +463,68 @@ class TestXml: assert link_options.jitter == link.jitter assert link_options.delay == link.delay assert link_options.dup == link.dup + + def test_link_options_bidirectional(self, session, tmpdir, ip_prefixes): + """ + Test xml client methods for a ptp network. + + :param session: session for test + :param tmpdir: tmpdir to create data in + :param ip_prefixes: generates ip addresses for nodes + """ + # create nodes + node_one = session.add_node() + interface_one = ip_prefixes.create_interface(node_one) + node_two = session.add_node() + interface_two = ip_prefixes.create_interface(node_two) + + # create link + link_options_one = LinkOptions() + link_options_one.bandwidth = 5000 + link_options_one.unidirectional = 1 + session.add_link(node_one.id, node_two.id, interface_one, interface_two, link_options_one) + link_options_two = LinkOptions() + link_options_two.bandwidth = 10000 + link_options_two.unidirectional = 1 + session.update_link(node_two.id, node_one.id, interface_two.id, interface_one.id, link_options_two) + + # instantiate session + session.instantiate() + + # get ids for nodes + n1_id = node_one.id + n2_id = node_two.id + + # save xml + xml_file = tmpdir.join("session.xml") + file_path = xml_file.strpath + session.save_xml(file_path) + + # verify xml file was created and can be parsed + assert xml_file.isfile() + assert ElementTree.parse(file_path) + + # stop current session, clearing data + session.shutdown() + + # verify nodes have been removed from session + with pytest.raises(KeyError): + assert not session.get_node(n1_id) + with pytest.raises(KeyError): + assert not session.get_node(n2_id) + + # load saved xml + session.open_xml(file_path, start=True) + + # verify nodes have been recreated + assert session.get_node(n1_id) + assert session.get_node(n2_id) + links = [] + for node_id in session.nodes: + node = session.nodes[node_id] + links += node.all_link_data(0) + assert len(links) == 2 + link_one = links[0] + link_two = links[1] + assert link_options_one.bandwidth == link_one.bandwidth + assert link_options_two.bandwidth == link_two.bandwidth