From b37eb2fe74340085b7104b09cbb619c3d0d35e9f Mon Sep 17 00:00:00 2001 From: bharnden Date: Thu, 11 Oct 2018 21:22:57 -0700 Subject: [PATCH] updated xml tests and removed unused xml code --- README.md | 15 +- daemon/core/corehandlers.py | 3 +- daemon/core/emulator/coreemu.py | 3 +- daemon/core/xml/xmldeployment.py | 191 ------ daemon/core/xml/xmlparser.py | 54 -- daemon/core/xml/xmlparser0.py | 420 ------------- daemon/core/xml/xmlparser1.py | 889 --------------------------- daemon/core/xml/xmlsession.py | 36 -- daemon/core/xml/xmlutils.py | 350 ----------- daemon/core/xml/xmlwriter.py | 12 - daemon/core/xml/xmlwriter0.py | 386 ------------ daemon/core/xml/xmlwriter1.py | 998 ------------------------------- daemon/tests/test_xml.py | 30 +- 13 files changed, 18 insertions(+), 3369 deletions(-) delete mode 100644 daemon/core/xml/xmldeployment.py delete mode 100644 daemon/core/xml/xmlparser.py delete mode 100644 daemon/core/xml/xmlparser0.py delete mode 100644 daemon/core/xml/xmlparser1.py delete mode 100644 daemon/core/xml/xmlsession.py delete mode 100644 daemon/core/xml/xmlutils.py delete mode 100644 daemon/core/xml/xmlwriter.py delete mode 100644 daemon/core/xml/xmlwriter0.py delete mode 100644 daemon/core/xml/xmlwriter1.py diff --git a/README.md b/README.md index 25ffc6d3..6e93e4f7 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ scripting network emulation. ## Documentation and Examples * Documentation hosted on GitHub - * http://coreemu.github.io/core/ + * * Basic Script Examples * [Examples](daemon/examples/api) * Custom Service Example @@ -30,7 +30,7 @@ scripting network emulation. We are leveraging Discord for persistent chat rooms, voice chat, and GitHub integration. This allows for more dynamic conversations and the capability to respond faster. Feel free to join us at the link below. -https://discord.gg/AKd7kmP + You can also get help with questions, comments, or trouble, by using the CORE mailing lists: @@ -47,8 +47,7 @@ make sudo make install ``` -Building Documentation ----------------------- +### Building Documentation ```shell ./bootstrap.sh @@ -56,10 +55,9 @@ Building Documentation make doc ``` -Building Packages ------------------ +### Building Packages -Install fpm: http://fpm.readthedocs.io/en/latest/installing.html +Install fpm: Build package commands, DESTDIR is used for gui packaging only @@ -81,8 +79,7 @@ This will produce: * python-core-sysv_$VERSION_$ARCH * python-core-systemd_$VERSION_$ARCH -Running CORE ------------- +### Running CORE First start the CORE services: diff --git a/daemon/core/corehandlers.py b/daemon/core/corehandlers.py index 24847aa3..12e84f46 100644 --- a/daemon/core/corehandlers.py +++ b/daemon/core/corehandlers.py @@ -1475,8 +1475,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): return () elif event_type == EventTypes.FILE_SAVE: filename = event_data.name - xml_version = self.session.options.get_config("xmlfilever") - self.session.save_xml(filename, xml_version) + self.session.save_xml(filename) elif event_type == EventTypes.SCHEDULED: etime = event_data.time node = event_data.node diff --git a/daemon/core/emulator/coreemu.py b/daemon/core/emulator/coreemu.py index 75782d71..a77b49b7 100644 --- a/daemon/core/emulator/coreemu.py +++ b/daemon/core/emulator/coreemu.py @@ -669,12 +669,11 @@ class EmuSession(Session): self.file_name = file_name self.instantiate() - def save_xml(self, file_name, version): + def save_xml(self, file_name): """ Export a session to the EmulationScript XML format. :param str file_name: file name to write session xml to - :param str version: xml version type :return: nothing """ CoreXmlWriter(self).write(file_name) diff --git a/daemon/core/xml/xmldeployment.py b/daemon/core/xml/xmldeployment.py deleted file mode 100644 index 65b496e9..00000000 --- a/daemon/core/xml/xmldeployment.py +++ /dev/null @@ -1,191 +0,0 @@ -import os -import socket - -from core import constants -from core import logger -from core.enumerations import NodeTypes -from core.misc import ipaddress -from core.misc import nodeutils -from core.misc import utils -from core.netns import nodes -from core.xml import xmlutils - - -class CoreDeploymentWriter(object): - def __init__(self, dom, root, session): - self.dom = dom - self.root = root - self.session = session - self.hostname = socket.gethostname() - - @staticmethod - def get_ipv4_addresses(hostname): - if hostname == 'localhost': - addr_list = [] - args = [constants.IP_BIN, '-o', '-f', 'inet', 'addr', 'show'] - output = utils.check_cmd(args) - for line in output.split(os.linesep): - split = line.split() - if not split: - continue - addr = split[3] - if not addr.startswith('127.'): - addr_list.append(addr) - return addr_list - else: - # TODO: handle other hosts - raise NotImplementedError - - @staticmethod - def get_interface_names(hostname): - """ - Uses same methodology of get_ipv4_addresses() to get - parallel list of interface names to go with ... - """ - if hostname == 'localhost': - iface_list = [] - args = [constants.IP_BIN, '-o', '-f', 'inet', 'addr', 'show'] - output = utils.check_cmd(args) - for line in output.split(os.linesep): - split = line.split() - if not split: - continue - interface_name = split[1] - addr = split[3] - if not addr.startswith('127.'): - iface_list.append(interface_name) - return iface_list - else: - # TODO: handle other hosts - raise NotImplementedError - - @staticmethod - def find_device(scenario, name): - tag_name = ('device', 'host', 'router') - for d in xmlutils.iter_descendants_with_attribute(scenario, tag_name, 'name', name): - return d - return None - - @staticmethod - def find_interface(device, name): - for i in xmlutils.iter_descendants_with_attribute(device, 'interface', 'name', name): - return i - return None - - def add_deployment(self): - testbed = self.dom.createElement('container') - testbed.setAttribute('name', 'TestBed') - testbed.setAttribute('id', 'TestBed') - self.root.base_element.appendChild(testbed) - nodelist = [] - for obj in self.session.objects.itervalues(): - if isinstance(obj, nodes.PyCoreNode): - nodelist.append(obj) - name = self.hostname - ipv4_addresses = self.get_ipv4_addresses('localhost') - iface_names = self.get_interface_names('localhost') - testhost = self.add_physical_host(testbed, name, ipv4_addresses, iface_names) - for n in nodelist: - self.add_virtual_host(testhost, n) - # TODO: handle other servers - # servers = self.session.broker.getservernames() - # servers.remove('localhost') - - def add_child_element(self, parent, tag_name): - el = self.dom.createElement(tag_name) - parent.appendChild(el) - return el - - def add_child_element_with_nameattr(self, parent, tag_name, name, setid=True): - el = self.add_child_element(parent, tag_name) - el.setAttribute('name', name) - if setid: - el.setAttribute('id', '%s/%s' % (parent.getAttribute('id'), name)) - return el - - def add_address(self, parent, address_type, address_str, address_iface=None): - el = self.add_child_element(parent, 'address') - el.setAttribute('type', address_type) - if address_iface is not None: - el.setAttribute('iface', address_iface) - el.appendChild(self.dom.createTextNode(address_str)) - return el - - def add_type(self, parent, type_str): - el = self.add_child_element(parent, 'type') - el.appendChild(self.dom.createTextNode(type_str)) - return el - - def add_platform(self, parent, name): - el = self.add_child_element_with_nameattr(parent, 'emanePlatform', name) - return el - - def add_transport(self, parent, name): - el = self.add_child_element_with_nameattr(parent, 'transport', name) - return el - - def add_nem(self, parent, name): - el = self.add_child_element_with_nameattr(parent, 'nem', name) - return el - - def add_parameter(self, parent, name, val): - el = self.add_child_element_with_nameattr(parent, 'parameter', name, False) - el.appendChild(self.dom.createTextNode(val)) - return el - - def add_mapping(self, parent, maptype, mapref): - el = self.add_child_element(parent, 'mapping') - el.setAttribute('type', maptype) - el.setAttribute('ref', mapref) - return el - - def add_host(self, parent, name): - el = self.add_child_element_with_nameattr(parent, 'testHost', name) - return el - - def add_physical_host(self, parent, name, ipv4_addresses, iface_names): - el = self.add_host(parent, name) - self.add_type(el, 'physical') - for i in range(0, len(ipv4_addresses)): - addr = ipv4_addresses[i] - if iface_names: - interface_name = iface_names[i] - else: - interface_name = None - self.add_address(el, 'IPv4', addr, interface_name) - return el - - def add_virtual_host(self, parent, obj): - assert isinstance(obj, nodes.PyCoreNode) - el = self.add_host(parent, obj.name) - device = self.find_device(self.root.base_element, obj.name) - if device is None: - logger.warn('corresponding XML device not found for %s' % obj.name) - return - self.add_mapping(device, 'testHost', el.getAttribute('id')) - self.add_type(el, 'virtual') - for netif in obj.netifs(): - for address in netif.addrlist: - addr, slash, prefixlen = address.partition('/') - if ipaddress.is_ipv4_address(addr): - addr_type = 'IPv4' - elif ipaddress.is_ipv6_address(addr): - addr_type = 'IPv6' - else: - raise NotImplementedError - self.add_address(el, addr_type, address, netif.name) - if nodeutils.is_node(netif.net, NodeTypes.EMANE): - nem = self.add_emane_interface(parent, el, netif) - interface = self.find_interface(device, netif.name) - self.add_mapping(interface, 'nem', nem.getAttribute('id')) - return el - - def add_emane_interface(self, physical_host, virtual_host, netif, platform_name='p1', transport_name='t1'): - nemid = netif.net.nemidmap[netif] - platform = self.add_platform(virtual_host, name=platform_name) - transport = self.add_transport(virtual_host, name=transport_name) - nem_name = 'nem%s' % nemid - nem = self.add_nem(platform, nem_name) - self.add_parameter(nem, 'nemid', str(nemid)) - self.add_mapping(transport, 'nem', nem.getAttribute('id')) - return nem diff --git a/daemon/core/xml/xmlparser.py b/daemon/core/xml/xmlparser.py deleted file mode 100644 index 9492ed27..00000000 --- a/daemon/core/xml/xmlparser.py +++ /dev/null @@ -1,54 +0,0 @@ -from xml.dom.minidom import parse - -from core.xml.xmlparser0 import CoreDocumentParser0 -from core.xml.xmlparser1 import CoreDocumentParser1 -from core.xml.xmlutils import get_first_child_by_tag_name - - -class CoreVersionParser(object): - """ - Helper class to check the version of Network Plan document. This - simply looks for a "Scenario" element; when present, this - indicates a 0.0 version document. The dom member is set in order - to prevent parsing a file twice (it can be passed to the - appropriate CoreDocumentParser class.) - """ - - DEFAULT_SCENARIO_VERSION = '1.0' - - def __init__(self, filename, options): - if 'dom' in options: - self.dom = options['dom'] - else: - self.dom = parse(filename) - scenario = get_first_child_by_tag_name(self.dom, 'scenario') - if scenario: - version = scenario.getAttribute('version') - if not version: - version = self.DEFAULT_SCENARIO_VERSION - self.version = version - elif get_first_child_by_tag_name(self.dom, 'Scenario'): - self.version = '0.0' - else: - self.version = 'unknown' - - -def core_document_parser(session, filename, options): - """ - Retrieves the xml document parser. - - :param core.session.Session session: core - :param str filename: name of file to save to or load from - :param dict options: parsing options - :return: xml document parser - """ - vp = CoreVersionParser(filename, options) - if 'dom' not in options: - options['dom'] = vp.dom - if vp.version == '0.0': - doc = CoreDocumentParser0(session, filename, options) - elif vp.version == '1.0': - doc = CoreDocumentParser1(session, filename, options) - else: - raise ValueError('unsupported document version: %s' % vp.version) - return doc diff --git a/daemon/core/xml/xmlparser0.py b/daemon/core/xml/xmlparser0.py deleted file mode 100644 index 0d759b9f..00000000 --- a/daemon/core/xml/xmlparser0.py +++ /dev/null @@ -1,420 +0,0 @@ -from xml.dom.minidom import parse - -from core import logger -from core.conf import ConfigShim -from core.enumerations import NodeTypes -from core.misc import nodeutils -from core.service import ServiceManager, ServiceShim -from core.xml import xmlutils - - -class CoreDocumentParser0(object): - def __init__(self, session, filename, options): - self.session = session - self.filename = filename - if 'dom' in options: - # this prevents parsing twice when detecting file versions - self.dom = options['dom'] - else: - self.dom = parse(filename) - self.start = options['start'] - self.nodecls = options['nodecls'] - - self.np = xmlutils.get_one_element(self.dom, "NetworkPlan") - if self.np is None: - raise ValueError, "missing NetworkPlan!" - self.mp = xmlutils.get_one_element(self.dom, "MotionPlan") - self.sp = xmlutils.get_one_element(self.dom, "ServicePlan") - self.meta = xmlutils.get_one_element(self.dom, "CoreMetaData") - - self.coords = self.getmotiondict(self.mp) - # link parameters parsed in parsenets(), applied in parsenodes() - self.linkparams = {} - - self.parsedefaultservices() - self.parseorigin() - self.parsenets() - self.parsenodes() - self.parseservices() - self.parsemeta() - - def getmotiondict(self, mp): - """ - Parse a MotionPlan into a dict with node names for keys and coordinates - for values. - """ - if mp is None: - return {} - coords = {} - for node in mp.getElementsByTagName("Node"): - nodename = str(node.getAttribute("name")) - if nodename == '': - continue - for m in node.getElementsByTagName("motion"): - if m.getAttribute("type") != "stationary": - continue - point = m.getElementsByTagName("point") - if len(point) == 0: - continue - txt = point[0].firstChild - if txt is None: - continue - xyz = map(int, txt.nodeValue.split(',')) - z = None - x, y = xyz[0:2] - if len(xyz) == 3: - z = xyz[2] - coords[nodename] = (x, y, z) - return coords - - @staticmethod - def getcommonattributes(obj): - """ - Helper to return tuple of attributes common to nodes and nets. - """ - node_id = obj.getAttribute("id") - try: - node_id = int(node_id) - except: - logger.debug("parsing node without integer id: %s", node_id) - - name = str(obj.getAttribute("name")) - node_type = str(obj.getAttribute("type")) - return node_id, name, node_type - - def parsenets(self): - linkednets = [] - for net in self.np.getElementsByTagName("NetworkDefinition"): - node_id, name, node_type = self.getcommonattributes(net) - nodecls = xmlutils.xml_type_to_node_class(node_type) - if not nodecls: - logger.warn("skipping unknown network node '%s' type '%s'", name, node_type) - continue - n = self.session.add_object(cls=nodecls, objid=node_id, name=name, start=self.start) - if name in self.coords: - x, y, z = self.coords[name] - n.setposition(x, y, z) - xmlutils.get_params_set_attrs(net, ("icon", "canvas", "opaque"), n) - if hasattr(n, "canvas") and n.canvas is not None: - n.canvas = int(n.canvas) - # links between two nets (e.g. switch-switch) - for ifc in net.getElementsByTagName("interface"): - netid = str(ifc.getAttribute("net")) - ifcname = str(ifc.getAttribute("name")) - linkednets.append((n, netid, ifcname)) - self.parsemodels(net, n) - # link networks together now that they all have been parsed - for n, netid, ifcname in linkednets: - try: - n2 = n.session.get_object_by_name(netid) - except KeyError: - logger.warn("skipping net %s interface: unknown net %s", n.name, netid) - continue - upstream = False - netif = n.getlinknetif(n2) - if netif is None: - netif = n2.linknet(n) - else: - netif.swapparams('_params_up') - upstream = True - key = (n2.name, ifcname) - if key in self.linkparams: - for k, v in self.linkparams[key]: - netif.setparam(k, v) - if upstream: - netif.swapparams('_params_up') - - def parsenodes(self): - for node in self.np.getElementsByTagName("Node"): - id, name, type = self.getcommonattributes(node) - if type == "rj45": - nodecls = nodeutils.get_node_class(NodeTypes.RJ45) - else: - nodecls = self.nodecls - n = self.session.add_object(cls=nodecls, objid=id, name=name, start=self.start) - if name in self.coords: - x, y, z = self.coords[name] - n.setposition(x, y, z) - n.type = type - xmlutils.get_params_set_attrs(node, ("icon", "canvas", "opaque"), n) - if hasattr(n, "canvas") and n.canvas is not None: - n.canvas = int(n.canvas) - for ifc in node.getElementsByTagName("interface"): - self.parseinterface(n, ifc) - - def parseinterface(self, n, ifc): - """ - Parse a interface block such as: - -
00:00:00:aa:00:01
-
10.0.0.2/24
-
2001::2/64
-
- """ - name = str(ifc.getAttribute("name")) - netid = str(ifc.getAttribute("net")) - hwaddr = None - addrlist = [] - try: - net = n.session.get_object_by_name(netid) - except KeyError: - logger.warn("skipping node %s interface %s: unknown net %s", n.name, name, netid) - return - for addr in ifc.getElementsByTagName("address"): - addrstr = xmlutils.get_text_child(addr) - if addrstr is None: - continue - if addr.getAttribute("type") == "mac": - hwaddr = addrstr - else: - addrlist.append(addrstr) - i = n.newnetif(net, addrlist=addrlist, hwaddr=hwaddr, ifindex=None, ifname=name) - for model in ifc.getElementsByTagName("model"): - self.parsemodel(model, n, n.objid) - key = (n.name, name) - if key in self.linkparams: - netif = n.netif(i) - for k, v in self.linkparams[key]: - netif.setparam(k, v) - - def parsemodels(self, dom, obj): - """ - Mobility/wireless model config is stored in a ConfigurableManager's - config dict. - """ - nodenum = int(dom.getAttribute("id")) - for model in dom.getElementsByTagName("model"): - self.parsemodel(model, obj, nodenum) - - def parsemodel(self, model, obj, nodenum): - """ - Mobility/wireless model config is stored in a ConfigurableManager's - config dict. - """ - name = model.getAttribute("name") - if name == '': - return - type = model.getAttribute("type") - # convert child text nodes into key=value pairs - kvs = xmlutils.get_text_elements_to_list(model) - - mgr = self.session.mobility - # TODO: the session.confobj() mechanism could be more generic; - # it only allows registering Conf Message callbacks, but here - # we want access to the ConfigurableManager, not the callback - if name[:5] == "emane": - mgr = self.session.emane - elif name[:5] == "netem": - mgr = None - self.parsenetem(model, obj, kvs) - - # TODO: assign other config managers here - if mgr: - for k, v in kvs: - mgr.set_config(k, v, node_id=nodenum, config_type=name) - - def parsenetem(self, model, obj, kvs): - """ - Determine interface and invoke setparam() using the parsed - (key, value) pairs. - """ - ifname = model.getAttribute("netif") - peer = model.getAttribute("peer") - key = (peer, ifname) - # nodes and interfaces do not exist yet, at this point of the parsing, - # save (key, value) pairs for later - try: - kvs = map(self.numericvalue, kvs) - except ValueError: - logger.warn("error parsing link parameters for '%s' on '%s'", ifname, peer) - self.linkparams[key] = kvs - - @staticmethod - def numericvalue(keyvalue): - (key, value) = keyvalue - if '.' in str(value): - value = float(value) - else: - value = int(value) - return key, value - - def parseorigin(self): - """ - Parse any origin tag from the Mobility Plan and set the CoreLocation - reference point appropriately. - """ - origin = xmlutils.get_one_element(self.mp, "origin") - if not origin: - return - location = self.session.location - geo = [] - attrs = ("lat", "lon", "alt") - for i in xrange(3): - a = origin.getAttribute(attrs[i]) - if a is not None: - a = float(a) - geo.append(a) - location.setrefgeo(geo[0], geo[1], geo[2]) - scale = origin.getAttribute("scale100") - if scale is not None and scale: - location.refscale = float(scale) - point = xmlutils.get_one_element(origin, "point") - if point is not None and point.firstChild is not None: - xyz = point.firstChild.nodeValue.split(',') - if len(xyz) == 2: - xyz.append('0.0') - if len(xyz) == 3: - xyz = map(lambda (x): float(x), xyz) - location.refxyz = (xyz[0], xyz[1], xyz[2]) - - def parsedefaultservices(self): - """ - Prior to parsing nodes, use session.services manager to store - default services for node types - """ - for node in self.sp.getElementsByTagName("Node"): - type = node.getAttribute("type") - if type == '': - continue # node-specific service config - services = [] - for service in node.getElementsByTagName("Service"): - services.append(str(service.getAttribute("name"))) - self.session.services.default_services[type] = services - logger.info("default services for type %s set to %s" % (type, services)) - - def parseservices(self): - """ - After node objects exist, parse service customizations and add them - to the nodes. - """ - svclists = {} - # parse services and store configs into session.services.configs - for node in self.sp.getElementsByTagName("Node"): - name = node.getAttribute("name") - if name == '': - continue # node type without name - n = self.session.get_object_by_name(name) - if n is None: - logger.warn("skipping service config for unknown node '%s'" % name) - continue - for service in node.getElementsByTagName("Service"): - svcname = service.getAttribute("name") - if self.parseservice(service, n): - if n.objid in svclists: - svclists[n.objid] += "|" + svcname - else: - svclists[n.objid] = svcname - # nodes in NetworkPlan but not in ServicePlan use the - # default services for their type - for node in self.np.getElementsByTagName("Node"): - id, name, type = self.getcommonattributes(node) - if id in svclists: - continue # custom config exists - else: - svclists[int(id)] = None # use defaults - - # associate nodes with services - for objid in sorted(svclists.keys()): - n = self.session.get_object(objid) - services = svclists[objid] - if services: - services = services.split("|") - self.session.services.add_services(node=n, node_type=n.type, services=services) - - def parseservice(self, service, n): - """ - Use session.services manager to store service customizations before - they are added to a node. - """ - name = service.getAttribute("name") - svc = ServiceManager.get(name) - if svc is None: - return False - values = [] - startup_idx = service.getAttribute("startup_idx") - if startup_idx is not None: - values.append("startidx=%s" % startup_idx) - startup_time = service.getAttribute("start_time") - if startup_time is not None: - values.append("starttime=%s" % startup_time) - dirs = [] - for dir in service.getElementsByTagName("Directory"): - dirname = dir.getAttribute("name") - dirs.append(dirname) - if len(dirs): - values.append("dirs=%s" % dirs) - - startup = [] - shutdown = [] - validate = [] - for cmd in service.getElementsByTagName("Command"): - type = cmd.getAttribute("type") - cmdstr = xmlutils.get_text_child(cmd) - if cmdstr is None: - continue - if type == "start": - startup.append(cmdstr) - elif type == "stop": - shutdown.append(cmdstr) - elif type == "validate": - validate.append(cmdstr) - if len(startup): - values.append("cmdup=%s" % startup) - if len(shutdown): - values.append("cmddown=%s" % shutdown) - if len(validate): - values.append("cmdval=%s" % validate) - - files = [] - for file in service.getElementsByTagName("File"): - filename = file.getAttribute("name") - files.append(filename) - data = xmlutils.get_text_child(file) - self.session.services.set_service_file(node_id=n.objid, service_name=name, file_name=filename, data=data) - - if len(files): - values.append("files=%s" % files) - if not bool(service.getAttribute("custom")): - return True - self.session.services.set_service(n.objid, svc) - # set custom values for custom service - svc = self.session.services.get_service(n.objid, None) - if not svc: - raise ValueError("custom service(%s) for node(%s) does not exist", svc.name, n.objid) - values = ConfigShim.str_to_dict("|".join(values)) - for name, value in values.iteritems(): - ServiceShim.setvalue(svc, name, value) - return True - - def parsehooks(self, hooks): - ''' Parse hook scripts from XML into session._hooks. - ''' - for hook in hooks.getElementsByTagName("Hook"): - filename = hook.getAttribute("name") - state = hook.getAttribute("state") - data = xmlutils.get_text_child(hook) - if data is None: - data = "" # allow for empty file - type = "hook:%s" % state - self.session.set_hook(type, file_name=filename, source_name=None, data=data) - - def parsemeta(self): - opt = xmlutils.get_one_element(self.meta, "SessionOptions") - if opt: - for param in opt.getElementsByTagName("param"): - k = str(param.getAttribute("name")) - v = str(param.getAttribute("value")) - if v == '': - v = xmlutils.get_text_child(param) # allow attribute/text for newlines - self.session.options.set_config(k, v) - hooks = xmlutils.get_one_element(self.meta, "Hooks") - if hooks: - self.parsehooks(hooks) - meta = xmlutils.get_one_element(self.meta, "MetaData") - if meta: - for param in meta.getElementsByTagName("param"): - k = str(param.getAttribute("name")) - v = str(param.getAttribute("value")) - if v == '': - v = xmlutils.get_text_child(param) - self.session.metadata.set_config(k, v) diff --git a/daemon/core/xml/xmlparser1.py b/daemon/core/xml/xmlparser1.py deleted file mode 100644 index 42d01cf5..00000000 --- a/daemon/core/xml/xmlparser1.py +++ /dev/null @@ -1,889 +0,0 @@ -import random -from xml.dom.minidom import Node -from xml.dom.minidom import parse - -from core import constants -from core import logger -from core.conf import ConfigShim -from core.enumerations import NodeTypes -from core.misc import nodeutils -from core.misc.ipaddress import MacAddress -from core.service import ServiceManager, ServiceShim -from core.xml import xmlutils - - -class CoreDocumentParser1(object): - layer2_device_types = 'hub', 'switch' - layer3_device_types = 'host', 'router' - device_types = layer2_device_types + layer3_device_types - - # TODO: support CORE interface classes: - # RJ45Node - # TunnelNode - - def __init__(self, session, filename, options): - """ - Creates an CoreDocumentParser1 object. - - :param core.session.Session session: - :param str filename: file name to open and parse - :param dict options: parsing options - :return: - """ - logger.info("creating xml parser: file (%s) options(%s)", filename, options) - self.session = session - self.filename = filename - if 'dom' in options: - # this prevents parsing twice when detecting file versions - self.dom = options['dom'] - else: - self.dom = parse(filename) - self.start = options['start'] - self.nodecls = options['nodecls'] - self.scenario = self.get_scenario(self.dom) - self.location_refgeo_set = False - self.location_refxyz_set = False - # saved link parameters saved when parsing networks and applied later - self.link_params = {} - # map from id-string to objid, for files having node names but - # not node numbers - self.objidmap = {} - self.objids = set() - self.default_services = {} - if self.scenario: - self.parse_scenario() - - @staticmethod - def get_scenario(dom): - scenario = xmlutils.get_first_child_by_tag_name(dom, 'scenario') - if not scenario: - raise ValueError('no scenario element found') - version = scenario.getAttribute('version') - if version and version != '1.0': - raise ValueError('unsupported scenario version found: \'%s\'' % version) - return scenario - - def parse_scenario(self): - self.parse_default_services() - self.parse_session_config() - self.parse_network_plan() - - def assign_id(self, idstr, idval): - if idstr in self.objidmap: - assert self.objidmap[idstr] == idval and idval in self.objids - return - self.objidmap[idstr] = idval - self.objids.add(idval) - - def rand_id(self): - while True: - x = random.randint(0, 0xffff) - if x not in self.objids: - return x - - def get_id(self, idstr): - """ - Get a, possibly new, object id (node number) corresponding to - the given XML string id. - """ - if not idstr: - idn = self.rand_id() - self.objids.add(idn) - return idn - elif idstr in self.objidmap: - return self.objidmap[idstr] - else: - try: - idn = int(idstr) - except ValueError: - idn = self.rand_id() - self.assign_id(idstr, idn) - return idn - - def get_common_attributes(self, node): - """ - Return id, name attributes for the given XML element. These - attributes are common to nodes and networks. - """ - idstr = node.getAttribute('id') - # use an explicit set COREID if it exists - coreid = self.find_core_id(node) - - if coreid: - idn = int(coreid) - if idstr: - self.assign_id(idstr, idn) - else: - idn = self.get_id(idstr) - - # TODO: consider supporting unicode; for now convert to an - # ascii string - namestr = str(node.getAttribute('name')) - return idn, namestr - - def iter_network_member_devices(self, element): - # element can be a network or a channel - for interface in xmlutils.iter_children_with_attribute(element, 'member', 'type', 'interface'): - if_id = xmlutils.get_child_text_trim(interface) - assert if_id # XXX for testing - if not if_id: - continue - device, if_name = self.find_device_with_interface(if_id) - assert device, 'no device for if_id: %s' % if_id # XXX for testing - if device: - yield device, if_name - - def network_class(self, network, network_type): - """ - Return the corresponding CORE network class for the given - network/network_type. - """ - if network_type in ['ethernet', 'satcom']: - return nodeutils.get_node_class(NodeTypes.PEER_TO_PEER) - elif network_type == 'wireless': - channel = xmlutils.get_first_child_by_tag_name(network, 'channel') - if channel: - # use an explicit CORE type if it exists - coretype = xmlutils.get_first_child_text_trim_with_attribute(channel, 'type', 'domain', 'CORE') - if coretype: - if coretype == 'basic_range': - return nodeutils.get_node_class(NodeTypes.WIRELESS_LAN) - elif coretype.startswith('emane'): - return nodeutils.get_node_class(NodeTypes.EMANE) - else: - logger.warn('unknown network type: \'%s\'', coretype) - return xmlutils.xml_type_to_node_class(coretype) - return nodeutils.get_node_class(NodeTypes.WIRELESS_LAN) - logger.warn('unknown network type: \'%s\'', network_type) - return None - - def create_core_object(self, objcls, objid, objname, element, node_type): - obj = self.session.add_object(cls=objcls, objid=objid, name=objname, start=self.start) - logger.info('added object objid=%s name=%s cls=%s' % (objid, objname, objcls)) - self.set_object_position(obj, element) - self.set_object_presentation(obj, element, node_type) - return obj - - def get_core_object(self, idstr): - if idstr and idstr in self.objidmap: - objid = self.objidmap[idstr] - return self.session.get_object(objid) - return None - - def parse_network_plan(self): - # parse the scenario in the following order: - # 1. layer-2 devices - # 2. other networks (ptp/wlan) - # 3. layer-3 devices - self.parse_layer2_devices() - self.parse_networks() - self.parse_layer3_devices() - - def set_ethernet_link_parameters(self, channel, link_params, mobility_model_name, mobility_params): - # save link parameters for later use, indexed by the tuple - # (device_id, interface_name) - for dev, if_name in self.iter_network_member_devices(channel): - if self.device_type(dev) in self.device_types: - dev_id = dev.getAttribute('id') - key = (dev_id, if_name) - self.link_params[key] = link_params - if mobility_model_name or mobility_params: - raise NotImplementedError - - def set_wireless_link_parameters(self, channel, link_params, mobility_model_name, mobility_params): - network = self.find_channel_network(channel) - network_id = network.getAttribute('id') - if network_id in self.objidmap: - nodenum = self.objidmap[network_id] - else: - logger.warn('unknown network: %s', network.toxml('utf-8')) - assert False # XXX for testing - model_name = xmlutils.get_first_child_text_trim_with_attribute(channel, 'type', 'domain', 'CORE') - if not model_name: - model_name = 'basic_range' - if model_name == 'basic_range': - mgr = self.session.mobility - elif model_name.startswith('emane'): - mgr = self.session.emane - else: - # TODO: any other config managers? - raise NotImplementedError - logger.info("setting wireless link params: node(%s) model(%s) mobility_model(%s)", - nodenum, model_name, mobility_model_name) - mgr.set_model_config(node_id=nodenum, model_name=model_name, config=link_params) - - if mobility_model_name and mobility_params: - self.session.mobility.set_model_config(node_id=nodenum, model_name=mobility_model_name, - config=mobility_params) - - def link_layer2_devices(self, device1, ifname1, device2, ifname2): - """ - Link two layer-2 devices together. - """ - devid1 = device1.getAttribute('id') - dev1 = self.get_core_object(devid1) - devid2 = device2.getAttribute('id') - dev2 = self.get_core_object(devid2) - assert dev1 and dev2 # XXX for testing - if dev1 and dev2: - # TODO: review this - if nodeutils.is_node(dev2, NodeTypes.RJ45): - # RJ45 nodes have different linknet() - netif = dev2.linknet(dev1) - else: - netif = dev1.linknet(dev2) - self.set_wired_link_parameters(dev1, netif, devid1, ifname1) - - @classmethod - def parse_xml_value(cls, valtext): - if not valtext: - return None - try: - if not valtext.translate(None, '0123456789'): - val = int(valtext) - else: - val = float(valtext) - except ValueError: - val = str(valtext) - return val - - @classmethod - def parse_parameter_children(cls, parent): - params = {} - for parameter in xmlutils.iter_children_with_name(parent, 'parameter'): - param_name = parameter.getAttribute('name') - assert param_name # XXX for testing - if not param_name: - continue - # TODO: consider supporting unicode; for now convert - # to an ascii string - param_name = str(param_name) - param_val = cls.parse_xml_value(xmlutils.get_child_text_trim(parameter)) - # TODO: check if the name already exists? - if param_name and param_val: - params[param_name] = param_val - return params - - def parse_network_channel(self, channel): - element = self.search_for_element(channel, 'type', lambda x: not x.hasAttributes()) - channel_type = xmlutils.get_child_text_trim(element) - link_params = self.parse_parameter_children(channel) - - mobility = xmlutils.get_first_child_by_tag_name(channel, 'CORE:mobility') - if mobility: - mobility_model_name = xmlutils.get_first_child_text_trim_by_tag_name(mobility, 'type') - mobility_params = self.parse_parameter_children(mobility) - else: - mobility_model_name = None - mobility_params = None - - if channel_type == 'wireless': - self.set_wireless_link_parameters(channel, link_params, mobility_model_name, mobility_params) - elif channel_type == 'ethernet': - # TODO: maybe this can be done in the loop below to avoid - # iterating through channel members multiple times - self.set_ethernet_link_parameters(channel, link_params, mobility_model_name, mobility_params) - else: - raise NotImplementedError - - layer2_device = [] - for dev, if_name in self.iter_network_member_devices(channel): - if self.device_type(dev) in self.layer2_device_types: - layer2_device.append((dev, if_name)) - - assert len(layer2_device) <= 2 - if len(layer2_device) == 2: - self.link_layer2_devices(layer2_device[0][0], layer2_device[0][1], - layer2_device[1][0], layer2_device[1][1]) - - def parse_network(self, network): - """ - Each network element should have an 'id' and 'name' attribute - and include the following child elements: - - type (one) - member (zero or more with type="interface" or type="channel") - channel (zero or more) - """ - layer2_members = set() - layer3_members = 0 - for dev, if_name in self.iter_network_member_devices(network): - if not dev: - continue - devtype = self.device_type(dev) - if devtype in self.layer2_device_types: - layer2_members.add(dev) - elif devtype in self.layer3_device_types: - layer3_members += 1 - else: - raise NotImplementedError - - if len(layer2_members) == 0: - net_type = xmlutils.get_first_child_text_trim_by_tag_name(network, 'type') - if not net_type: - logger.warn('no network type found for network: \'%s\'', network.toxml('utf-8')) - assert False # XXX for testing - net_cls = self.network_class(network, net_type) - objid, net_name = self.get_common_attributes(network) - logger.info('parsing network: name=%s id=%s' % (net_name, objid)) - if objid in self.session.objects: - return - n = self.create_core_object(net_cls, objid, net_name, network, None) - - # handle channel parameters - for channel in xmlutils.iter_children_with_name(network, 'channel'): - self.parse_network_channel(channel) - - def parse_networks(self): - """ - Parse all 'network' elements. - """ - for network in xmlutils.iter_descendants_with_name(self.scenario, 'network'): - self.parse_network(network) - - def parse_addresses(self, interface): - mac = [] - ipv4 = [] - ipv6 = [] - hostname = [] - for address in xmlutils.iter_children_with_name(interface, 'address'): - addr_type = address.getAttribute('type') - if not addr_type: - msg = 'no type attribute found for address ' \ - 'in interface: \'%s\'' % interface.toxml('utf-8') - logger.warn(msg) - assert False # XXX for testing - addr_text = xmlutils.get_child_text_trim(address) - if not addr_text: - msg = 'no text found for address ' \ - 'in interface: \'%s\'' % interface.toxml('utf-8') - logger.warn(msg) - assert False # XXX for testing - if addr_type == 'mac': - mac.append(addr_text) - elif addr_type == 'IPv4': - ipv4.append(addr_text) - elif addr_type == 'IPv6': - ipv6.append(addr_text) - elif addr_type == 'hostname': - hostname.append(addr_text) - else: - msg = 'skipping unknown address type \'%s\' in ' \ - 'interface: \'%s\'' % (addr_type, interface.toxml('utf-8')) - logger.warn(msg) - assert False # XXX for testing - return mac, ipv4, ipv6, hostname - - def parse_interface(self, node, device_id, interface): - """ - Each interface can have multiple 'address' elements. - """ - if_name = interface.getAttribute('name') - network = self.find_interface_network_object(interface) - if not network: - msg = 'skipping node \'%s\' interface \'%s\': ' \ - 'unknown network' % (node.name, if_name) - logger.warn(msg) - assert False # XXX for testing - mac, ipv4, ipv6, hostname = self.parse_addresses(interface) - if mac: - hwaddr = MacAddress.from_string(mac[0]) - else: - hwaddr = None - ifindex = node.newnetif(network, addrlist=ipv4 + ipv6, hwaddr=hwaddr, ifindex=None, ifname=if_name) - # TODO: 'hostname' addresses are unused - msg = 'node \'%s\' interface \'%s\' connected ' \ - 'to network \'%s\'' % (node.name, if_name, network.name) - logger.info(msg) - # set link parameters for wired links - if nodeutils.is_node(network, (NodeTypes.HUB, NodeTypes.PEER_TO_PEER, NodeTypes.SWITCH)): - netif = node.netif(ifindex) - self.set_wired_link_parameters(network, netif, device_id) - - def set_wired_link_parameters(self, network, netif, device_id, netif_name=None): - if netif_name is None: - netif_name = netif.name - key = (device_id, netif_name) - if key in self.link_params: - link_params = self.link_params[key] - if self.start: - bw = link_params.get('bw') - delay = link_params.get('delay') - loss = link_params.get('loss') - duplicate = link_params.get('duplicate') - jitter = link_params.get('jitter') - network.linkconfig(netif, bw=bw, delay=delay, loss=loss, duplicate=duplicate, jitter=jitter) - else: - for k, v in link_params.iteritems(): - netif.setparam(k, v) - - @staticmethod - def search_for_element(node, tag_name, match=None): - """ - Search the given node and all ancestors for an element named - tagName that satisfies the given matching function. - """ - while True: - for child in xmlutils.iter_children(node, Node.ELEMENT_NODE): - if child.tagName == tag_name and (match is None or match(child)): - return child - node = node.parentNode - if not node: - break - return None - - @classmethod - def find_core_id(cls, node): - def match(x): - domain = x.getAttribute('domain') - return domain == 'COREID' - - alias = cls.search_for_element(node, 'alias', match) - if alias: - return xmlutils.get_child_text_trim(alias) - return None - - @classmethod - def find_point(cls, node): - return cls.search_for_element(node, 'point') - - @staticmethod - def find_channel_network(channel): - p = channel.parentNode - if p and p.tagName == 'network': - return p - return None - - def find_interface_network_object(self, interface): - network_id = xmlutils.get_first_child_text_trim_with_attribute(interface, 'member', 'type', 'network') - if not network_id: - # support legacy notation: - - - - - """ - for item in iterable: - element = dom.createElement(name) - element.setAttribute(attr_name, item) - parent.appendChild(element) - - -def add_text_elements_from_list(dom, parent, iterable, name, attrs): - """ - XML helper to iterate through a list and add items to parent using tags - of the given name, attributes specified in the attrs tuple, and having the - text of the item within the tags. - Example: addtextelementsfromlist(dom, parent, ('a','b','c'), "letter", - (('show','True'),)) - - a - b - c - - """ - for item in iterable: - element = dom.createElement(name) - for k, v in attrs: - element.setAttribute(k, v) - parent.appendChild(element) - txt = dom.createTextNode(item) - element.appendChild(txt) - - -def add_text_elements_from_tuples(dom, parent, iterable, attrs=()): - """ - XML helper to iterate through a list of tuples and add items to - parent using tags named for the first tuple element, - attributes specified in the attrs tuple, and having the - text of second tuple element. - Example: addtextelementsfromtuples(dom, parent, - (('first','a'),('second','b'),('third','c')), - (('show','True'),)) - - a - b - c - - """ - for name, value in iterable: - element = dom.createElement(name) - for k, v in attrs: - element.setAttribute(k, v) - parent.appendChild(element) - txt = dom.createTextNode(value) - element.appendChild(txt) - - -def get_text_elements_to_list(parent): - """ - XML helper to parse child text nodes from the given parent and return - a list of (key, value) tuples. - """ - r = [] - for n in parent.childNodes: - if n.nodeType != Node.ELEMENT_NODE: - continue - k = str(n.nodeName) - # sometimes want None here? - v = '' - for c in n.childNodes: - if c.nodeType != Node.TEXT_NODE: - continue - v = str(c.nodeValue) - break - r.append((k, v)) - return r - - -def add_param_to_parent(dom, parent, name, value): - """ - XML helper to add a tag to the parent - element, when value is not None. - """ - if value is None: - return None - p = dom.createElement("param") - parent.appendChild(p) - p.setAttribute("name", name) - p.setAttribute("value", "%s" % value) - return p - - -def add_text_param_to_parent(dom, parent, name, value): - """ - XML helper to add a value tag to the parent - element, when value is not None. - """ - if value is None: - return None - p = dom.createElement("param") - parent.appendChild(p) - p.setAttribute("name", name) - txt = dom.createTextNode(value) - p.appendChild(txt) - return p - - -def add_param_list_to_parent(dom, parent, name, values): - """ - XML helper to return a parameter list and optionally add it to the - parent element: - - - - - """ - if values is None: - return None - p = dom.createElement("paramlist") - if parent: - parent.appendChild(p) - p.setAttribute("name", name) - for v in values: - item = dom.createElement("item") - item.setAttribute("value", str(v)) - p.appendChild(item) - return p - - -def get_one_element(dom, name): - e = dom.getElementsByTagName(name) - if len(e) == 0: - return None - return e[0] - - -def iter_descendants(dom, max_depth=0): - """ - Iterate over all descendant element nodes in breadth first order. - Only consider nodes up to max_depth deep when max_depth is greater - than zero. - """ - nodes = [dom] - depth = 0 - current_depth_nodes = 1 - next_depth_nodes = 0 - while nodes: - n = nodes.pop(0) - for child in n.childNodes: - if child.nodeType == Node.ELEMENT_NODE: - yield child - nodes.append(child) - next_depth_nodes += 1 - current_depth_nodes -= 1 - if current_depth_nodes == 0: - depth += 1 - if max_depth > 0 and depth == max_depth: - return - current_depth_nodes = next_depth_nodes - next_depth_nodes = 0 - - -def iter_matching_descendants(dom, match_function, max_depth=0): - """ - Iterate over descendant elements where matchFunction(descendant) - returns true. Only consider nodes up to max_depth deep when - max_depth is greater than zero. - """ - for d in iter_descendants(dom, max_depth): - if match_function(d): - yield d - - -def iter_descendants_with_name(dom, tag_name, max_depth=0): - """ - Iterate over descendant elements whose name is contained in - tagName (or is named tagName if tagName is a string). Only - consider nodes up to max_depth deep when max_depth is greater than - zero. - """ - if isinstance(tag_name, basestring): - tag_name = (tag_name,) - - def match(d): - return d.tagName in tag_name - - return iter_matching_descendants(dom, match, max_depth) - - -def iter_descendants_with_attribute(dom, tag_name, attr_name, attr_value, max_depth=0): - """ - Iterate over descendant elements whose name is contained in - tagName (or is named tagName if tagName is a string) and have an - attribute named attrName with value attrValue. Only consider - nodes up to max_depth deep when max_depth is greater than zero. - """ - if isinstance(tag_name, basestring): - tag_name = (tag_name,) - - def match(d): - return d.tagName in tag_name and \ - d.getAttribute(attr_name) == attr_value - - return iter_matching_descendants(dom, match, max_depth) - - -def iter_children(dom, node_type): - """ - Iterate over all child elements of the given type. - """ - for child in dom.childNodes: - if child.nodeType == node_type: - yield child - - -def get_text_child(dom): - """ - Return the text node of the given element. - """ - for child in iter_children(dom, Node.TEXT_NODE): - return str(child.nodeValue) - return None - - -def get_child_text_trim(dom): - text = get_text_child(dom) - if text: - text = text.strip() - return text - - -def get_params_set_attrs(dom, param_names, target): - """ - XML helper to get tags and set - the attribute in the target object. String type is used. Target object - attribute is unchanged if the XML attribute is not present. - """ - params = dom.getElementsByTagName("param") - for param in params: - param_name = param.getAttribute("name") - value = param.getAttribute("value") - if value is None: - # never reached? - continue - if param_name in param_names: - setattr(target, param_name, str(value)) - - -def xml_type_to_node_class(node_type): - """ - Helper to convert from a type string to a class name in nodes.*. - """ - logger.error("xml type to node type: %s", node_type) - if hasattr(nodes, node_type): - return _NODE_MAP[node_type] - else: - return None - - -def iter_children_with_name(dom, tag_name): - return iter_descendants_with_name(dom, tag_name, 1) - - -def iter_children_with_attribute(dom, tag_name, attr_name, attr_value): - return iter_descendants_with_attribute(dom, tag_name, attr_name, attr_value, 1) - - -def get_first_child_by_tag_name(dom, tag_name): - """ - Return the first child element whose name is contained in tagName - (or is named tagName if tagName is a string). - """ - for child in iter_children_with_name(dom, tag_name): - return child - return None - - -def get_first_child_text_by_tag_name(dom, tag_name): - """ - Return the corresponding text of the first child element whose - name is contained in tagName (or is named tagName if tagName is a - string). - """ - child = get_first_child_by_tag_name(dom, tag_name) - if child: - return get_text_child(child) - return None - - -def get_first_child_text_trim_by_tag_name(dom, tag_name): - text = get_first_child_text_by_tag_name(dom, tag_name) - if text: - text = text.strip() - return text - - -def get_first_child_with_attribute(dom, tag_name, attr_name, attr_value): - """ - Return the first child element whose name is contained in tagName - (or is named tagName if tagName is a string) that has an attribute - named attrName with value attrValue. - """ - for child in \ - iter_children_with_attribute(dom, tag_name, attr_name, attr_value): - return child - return None - - -def get_first_child_text_with_attribute(dom, tag_name, attr_name, attr_value): - """ - Return the corresponding text of the first child element whose - name is contained in tagName (or is named tagName if tagName is a - string) that has an attribute named attrName with value attrValue. - """ - child = get_first_child_with_attribute(dom, tag_name, attr_name, attr_value) - if child: - return get_text_child(child) - return None - - -def get_first_child_text_trim_with_attribute(dom, tag_name, attr_name, attr_value): - text = get_first_child_text_with_attribute(dom, tag_name, attr_name, attr_value) - if text: - text = text.strip() - return text diff --git a/daemon/core/xml/xmlwriter.py b/daemon/core/xml/xmlwriter.py deleted file mode 100644 index 14b49458..00000000 --- a/daemon/core/xml/xmlwriter.py +++ /dev/null @@ -1,12 +0,0 @@ -from core.xml.xmlwriter0 import CoreDocumentWriter0 -from core.xml.xmlwriter1 import CoreDocumentWriter1 - - -def core_document_writer(session, version): - if version == '0.0': - doc = CoreDocumentWriter0(session) - elif version == '1.0': - doc = CoreDocumentWriter1(session) - else: - raise ValueError('unsupported document version: %s' % version) - return doc diff --git a/daemon/core/xml/xmlwriter0.py b/daemon/core/xml/xmlwriter0.py deleted file mode 100644 index b97e361a..00000000 --- a/daemon/core/xml/xmlwriter0.py +++ /dev/null @@ -1,386 +0,0 @@ -import os -from xml.dom.minidom import Document - -import pwd - -from core import logger -from core.coreobj import PyCoreNet -from core.coreobj import PyCoreNode -from core.enumerations import RegisterTlvs, EventTypes -from core.xml import xmlutils - - -class CoreDocumentWriter0(Document): - """ - Utility class for writing a CoreSession to XML. The init method builds - an xml.dom.minidom.Document, and the writexml() method saves the XML file. - """ - - def __init__(self, session): - """ - Create an empty Scenario XML Document, then populate it with - objects from the given session. - """ - Document.__init__(self) - self.session = session - self.scenario = self.createElement("Scenario") - self.np = self.createElement("NetworkPlan") - self.mp = self.createElement("MotionPlan") - self.sp = self.createElement("ServicePlan") - self.meta = self.createElement("CoreMetaData") - - self.appendChild(self.scenario) - self.scenario.appendChild(self.np) - self.scenario.appendChild(self.mp) - self.scenario.appendChild(self.sp) - self.scenario.appendChild(self.meta) - - self.populatefromsession() - - def populatefromsession(self): - if self.session.state != EventTypes.RUNTIME_STATE.value: - self.session.emane.setup() # not during runtime? - self.addorigin() - self.adddefaultservices() - self.addnets() - self.addnodes() - self.addmetadata() - - def writexml(self, filename): - logger.info("saving session XML file %s", filename) - f = open(filename, "w") - Document.writexml(self, writer=f, indent="", addindent=" ", newl="\n", encoding="UTF-8") - f.close() - if self.session.user is not None: - uid = pwd.getpwnam(self.session.user).pw_uid - gid = os.stat(self.session.sessiondir).st_gid - os.chown(filename, uid, gid) - - def addnets(self): - """ - Add PyCoreNet objects as NetworkDefinition XML elements. - """ - with self.session._objects_lock: - for net in self.session.objects.itervalues(): - if not isinstance(net, PyCoreNet): - continue - self.addnet(net) - - def addnet(self, net): - """ - Add one PyCoreNet object as a NetworkDefinition XML element. - """ - n = self.createElement("NetworkDefinition") - self.np.appendChild(n) - n.setAttribute("name", net.name) - # could use net.brname - n.setAttribute("id", "%s" % net.objid) - n.setAttribute("type", "%s" % net.__class__.__name__) - self.addnetinterfaces(n, net) - # key used with tunnel node - if hasattr(net, 'grekey') and net.grekey is not None: - n.setAttribute("key", "%s" % net.grekey) - # link parameters - for netif in net.netifs(sort=True): - self.addnetem(n, netif) - # wireless/mobility models - modelconfigs = net.session.mobility.get_models(net) - modelconfigs += net.session.emane.get_models(net) - self.addmodels(n, modelconfigs) - self.addposition(net) - - def addnetem(self, n, netif): - """ - Similar to addmodels(); used for writing netem link effects - parameters. TODO: Interface parameters should be moved to the model - construct, then this separate method shouldn't be required. - """ - params = netif.getparams() - if len(params) == 0: - return - model = self.createElement("model") - model.setAttribute("name", "netem") - model.setAttribute("netif", netif.name) - if hasattr(netif, "node") and netif.node is not None: - model.setAttribute("peer", netif.node.name) - # link between switches uses one veth interface - elif hasattr(netif, "othernet") and netif.othernet is not None: - if netif.othernet.name == n.getAttribute("name"): - model.setAttribute("peer", netif.net.name) - else: - model.setAttribute("peer", netif.othernet.name) - model.setAttribute("netif", netif.localname) - # hack used for upstream parameters for link between switches - # (see LxBrNet.linknet()) - if netif.othernet.objid == int(n.getAttribute("id")): - netif.swapparams('_params_up') - params = netif.getparams() - netif.swapparams('_params_up') - has_params = False - for k, v in params: - # default netem parameters are 0 or None - if v is None or v == 0: - continue - if k == "has_netem" or k == "has_tbf": - continue - key = self.createElement(k) - key.appendChild(self.createTextNode("%s" % v)) - model.appendChild(key) - has_params = True - if has_params: - n.appendChild(model) - - def addmodels(self, n, configs): - """ - Add models from a list of model-class, config values tuples. - """ - for m, conf in configs: - model = self.createElement("model") - n.appendChild(model) - model.setAttribute("name", m.name) - type = "wireless" - if m.config_type == RegisterTlvs.MOBILITY.value: - type = "mobility" - model.setAttribute("type", type) - - for k, value in conf.iteritems(): - key = self.createElement(k) - if value is None: - value = "" - key.appendChild(self.createTextNode("%s" % value)) - model.appendChild(key) - - def addnodes(self): - """ - Add PyCoreNode objects as node XML elements. - """ - with self.session._objects_lock: - for node in self.session.objects.itervalues(): - if not isinstance(node, PyCoreNode): - continue - self.addnode(node) - - def addnode(self, node): - """ - Add a PyCoreNode object as node XML elements. - """ - n = self.createElement("Node") - self.np.appendChild(n) - n.setAttribute("name", node.name) - n.setAttribute("id", "%s" % node.objid) - if node.type: - n.setAttribute("type", node.type) - self.addinterfaces(n, node) - self.addposition(node) - xmlutils.add_param_to_parent(self, n, "icon", node.icon) - xmlutils.add_param_to_parent(self, n, "canvas", node.canvas) - self.addservices(node) - - def addinterfaces(self, n, node): - """ - Add PyCoreNetIfs to node XML elements. - """ - for ifc in node.netifs(sort=True): - i = self.createElement("interface") - n.appendChild(i) - i.setAttribute("name", ifc.name) - netmodel = None - if ifc.net: - i.setAttribute("net", ifc.net.name) - if hasattr(ifc.net, "model"): - netmodel = ifc.net.model - if ifc.mtu and ifc.mtu != 1500: - i.setAttribute("mtu", "%s" % ifc.mtu) - # could use ifc.params, transport_type - self.addaddresses(i, ifc) - # per-interface models - if netmodel and netmodel.name[:6] == "emane_": - cfg = self.session.emane.getifcconfig(node.objid, ifc, netmodel.name) - if cfg: - self.addmodels(i, ((netmodel, cfg),)) - - def addnetinterfaces(self, n, net): - """ - Similar to addinterfaces(), but only adds interface elements to the - supplied XML node that would not otherwise appear in the Node elements. - These are any interfaces that link two switches/hubs together. - """ - for ifc in net.netifs(sort=True): - if not hasattr(ifc, "othernet") or not ifc.othernet: - continue - i = self.createElement("interface") - n.appendChild(i) - if net.objid == ifc.net.objid: - i.setAttribute("name", ifc.localname) - i.setAttribute("net", ifc.othernet.name) - else: - i.setAttribute("name", ifc.name) - i.setAttribute("net", ifc.net.name) - - def addposition(self, node): - """ - Add object coordinates as location XML element. - """ - (x, y, z) = node.position.get() - if x is None or y is None: - return - # - mpn = self.createElement("Node") - mpn.setAttribute("name", node.name) - self.mp.appendChild(mpn) - - # - motion = self.createElement("motion") - motion.setAttribute("type", "stationary") - mpn.appendChild(motion) - - # $X$,$Y$,$Z$ - pt = self.createElement("point") - motion.appendChild(pt) - coordstxt = "%s,%s" % (x, y) - if z: - coordstxt += ",%s" % z - coords = self.createTextNode(coordstxt) - pt.appendChild(coords) - - def addorigin(self): - """ - Add origin to Motion Plan using canvas reference point. - The CoreLocation class maintains this reference point. - """ - refgeo = self.session.location.refgeo - origin = self.createElement("origin") - attrs = ("lat", "lon", "alt") - have_origin = False - for i in xrange(3): - if refgeo[i] is not None: - origin.setAttribute(attrs[i], str(refgeo[i])) - have_origin = True - if not have_origin: - return - if self.session.location.refscale != 1.0: # 100 pixels = refscale m - origin.setAttribute("scale100", str(self.session.location.refscale)) - if self.session.location.refxyz != (0.0, 0.0, 0.0): - pt = self.createElement("point") - origin.appendChild(pt) - x, y, z = self.session.location.refxyz - coordstxt = "%s,%s" % (x, y) - if z: - coordstxt += ",%s" % z - coords = self.createTextNode(coordstxt) - pt.appendChild(coords) - - self.mp.appendChild(origin) - - def adddefaultservices(self): - """ - Add default services and node types to the ServicePlan. - """ - for type in self.session.services.default_services: - defaults = self.session.services.get_default_services(type) - spn = self.createElement("Node") - spn.setAttribute("type", type) - self.sp.appendChild(spn) - for svc in defaults: - s = self.createElement("Service") - spn.appendChild(s) - s.setAttribute("name", str(svc.name)) - - def addservices(self, node): - """ - Add services and their customizations to the ServicePlan. - """ - if len(node.services) == 0: - return - defaults = self.session.services.get_default_services(node.type) - if node.services == defaults: - return - spn = self.createElement("Node") - spn.setAttribute("name", node.name) - self.sp.appendChild(spn) - - for svc in node.services: - s = self.createElement("Service") - spn.appendChild(s) - s.setAttribute("name", str(svc.name)) - # only record service names if not a customized service - if not svc.custom: - continue - s.setAttribute("custom", str(svc.custom)) - xmlutils.add_elements_from_list(self, s, svc.dirs, "Directory", "name") - - for fn in svc.configs: - if len(fn) == 0: - continue - f = self.createElement("File") - f.setAttribute("name", fn) - # all file names are added to determine when a file has been deleted - s.appendChild(f) - data = svc.config_data.get(fn) - if data is None: - # this includes only customized file contents and skips - # the auto-generated files - continue - txt = self.createTextNode(data) - f.appendChild(txt) - - xmlutils.add_text_elements_from_list(self, s, svc.startup, "Command", (("type", "start"),)) - xmlutils.add_text_elements_from_list(self, s, svc.shutdown, "Command", (("type", "stop"),)) - xmlutils.add_text_elements_from_list(self, s, svc.validate, "Command", (("type", "validate"),)) - - def addaddresses(self, i, netif): - """ - Add MAC and IP addresses to interface XML elements. - """ - if netif.hwaddr: - h = self.createElement("address") - i.appendChild(h) - h.setAttribute("type", "mac") - htxt = self.createTextNode("%s" % netif.hwaddr) - h.appendChild(htxt) - for addr in netif.addrlist: - a = self.createElement("address") - i.appendChild(a) - # a.setAttribute("type", ) - atxt = self.createTextNode("%s" % addr) - a.appendChild(atxt) - - def addhooks(self): - """ - Add hook script XML elements to the metadata tag. - """ - hooks = self.createElement("Hooks") - for state in sorted(self.session._hooks.keys()): - for filename, data in self.session._hooks[state]: - hook = self.createElement("Hook") - hook.setAttribute("name", filename) - hook.setAttribute("state", str(state)) - txt = self.createTextNode(data) - hook.appendChild(txt) - hooks.appendChild(hook) - if hooks.hasChildNodes(): - self.meta.appendChild(hooks) - - def addmetadata(self): - """ - Add CORE-specific session meta-data XML elements. - """ - # options - options = self.createElement("SessionOptions") - defaults = self.session.options.default_values() - for name, current_value in self.session.options.get_configs().iteritems(): - default_value = defaults[name] - if current_value != default_value: - xmlutils.add_text_param_to_parent(self, options, name, current_value) - - if options.hasChildNodes(): - self.meta.appendChild(options) - - # hook scripts - self.addhooks() - - # meta - meta = self.createElement("MetaData") - self.meta.appendChild(meta) - for name, current_value in self.session.metadata.get_configs().iteritems(): - xmlutils.add_text_param_to_parent(self, meta, name, current_value) diff --git a/daemon/core/xml/xmlwriter1.py b/daemon/core/xml/xmlwriter1.py deleted file mode 100644 index 4641d5c7..00000000 --- a/daemon/core/xml/xmlwriter1.py +++ /dev/null @@ -1,998 +0,0 @@ -import collections -import os -from xml.dom.minidom import Document - -import pwd - -from core import coreobj -from core import logger -from core.enumerations import EventTypes -from core.enumerations import NodeTypes -from core.enumerations import RegisterTlvs -from core.misc import ipaddress -from core.misc import nodeutils -from core.netns import nodes -from core.xml import xmlutils -from core.xml.xmldeployment import CoreDeploymentWriter - - -class Alias: - ID = "COREID" - - -class MembType: - INTERFACE = "interface" - CHANNEL = "channel" - SWITCH = "switch" - HUB = "hub" - TUNNEL = "tunnel" - NETWORK = "network" - - -class NodeType: - ROUTER = "router" - HOST = "host" - MDR = "mdr" - PC = "PC" - RJ45 = "rj45" - SWITCH = "lanswitch" - HUB = "hub" - - -class DevType: - HOST = "host" - ROUTER = "router" - SWITCH = "switch" - HUB = "hub" - - -class NetType: - WIRELESS = "wireless" - ETHERNET = "ethernet" - PTP_WIRED = "point-to-point-wired" - PTP_WIRELESS = "point-to-point-wireless" - - -""" -A link endpoint in CORE -net: the network that the endpoint belongs to -netif: the network interface at this end -id: the identifier for the endpoint -l2devport: if the other end is a layer 2 device, this is the assigned port in that device -params: link/interface parameters -""" -Endpoint = collections.namedtuple('Endpoint', - ['net', 'netif', 'type', 'id', 'l2devport', 'params']) - - -class CoreDocumentWriter1(Document): - """ - Utility class for writing a CoreSession to XML in the NMF scenPlan schema. The init - method builds an xml.dom.minidom.Document, and the writexml() method saves the XML file. - """ - - def __init__(self, session): - """ - Create an empty Scenario XML Document, then populate it with - objects from the given session. - """ - Document.__init__(self) - logger.debug('Exporting to NMF XML version 1.0') - with session._objects_lock: - self.scenarioPlan = ScenarioPlan(self, session) - if session.state == EventTypes.RUNTIME_STATE.value: - deployment = CoreDeploymentWriter(self, self.scenarioPlan, session) - deployment.add_deployment() - self.scenarioPlan.setAttribute('deployed', 'true') - - def writexml(self, filename): - """ - Commit to file - """ - logger.info("saving session XML file %s", filename) - f = open(filename, "w") - Document.writexml(self, writer=f, indent="", addindent=" ", newl="\n", encoding="UTF-8") - f.close() - if self.scenarioPlan.coreSession.user is not None: - uid = pwd.getpwnam(self.scenarioPlan.coreSession.user).pw_uid - gid = os.stat(self.scenarioPlan.coreSession.session_dir).st_gid - os.chown(filename, uid, gid) - - -class XmlElement(object): - """ - The base class for all XML elements in the scenario plan. Includes - convenience functions. - """ - - def __init__(self, document, parent, element_type): - self.document = document - self.parent = parent - self.base_element = document.createElement("%s" % element_type) - if self.parent is not None: - self.parent.appendChild(self.base_element) - - def createElement(self, element_tag): - return self.document.createElement(element_tag) - - def getTagName(self): - return self.base_element.tagName - - def createTextNode(self, node_tag): - return self.document.createTextNode(node_tag) - - def appendChild(self, child): - if isinstance(child, XmlElement): - self.base_element.appendChild(child.base_element) - else: - self.base_element.appendChild(child) - - @staticmethod - def add_parameter(doc, parent, key, value): - if key and value: - parm = doc.createElement("parameter") - parm.setAttribute("name", str(key)) - parm.appendChild(doc.createTextNode(str(value))) - parent.appendChild(parm) - - def addParameter(self, key, value): - """ - Add a parameter to the xml element - """ - self.add_parameter(self.document, self, key, value) - - def setAttribute(self, name, val): - self.base_element.setAttribute(name, val) - - def getAttribute(self, name): - return self.base_element.getAttribute(name) - - -class NamedXmlElement(XmlElement): - """ - The base class for all "named" xml elements. Named elements are - xml elements in the scenario plan that have an id and a name attribute. - """ - - def __init__(self, scen_plan, parent, element_type, element_name): - XmlElement.__init__(self, scen_plan.document, parent, element_type) - - self.scenPlan = scen_plan - self.coreSession = scen_plan.coreSession - - element_path = '' - self.id = None - if self.parent is not None and isinstance(self.parent, XmlElement) and self.parent.getTagName() != "scenario": - element_path = "%s/" % self.parent.getAttribute("id") - - self.id = "%s%s" % (element_path, element_name) - self.setAttribute("name", element_name) - self.setAttribute("id", self.id) - - def addPoint(self, core_object): - """ - Add position to an object - """ - (x, y, z) = core_object.position.get() - if x is None or y is None: - return - lat, lon, alt = self.coreSession.location.getgeo(x, y, z) - - pt = self.createElement("point") - pt.setAttribute("type", "gps") - pt.setAttribute("lat", "%s" % lat) - pt.setAttribute("lon", "%s" % lon) - if z: - pt.setAttribute("z", "%s" % alt) - self.appendChild(pt) - - def createAlias(self, domain, value_str): - """ - Create an alias element for CORE specific information - """ - a = self.createElement("alias") - a.setAttribute("domain", "%s" % domain) - a.appendChild(self.createTextNode(value_str)) - return a - - -class ScenarioPlan(XmlElement): - """ - Container class for ScenarioPlan. - """ - - def __init__(self, document, session): - XmlElement.__init__(self, document, parent=document, element_type='scenario') - - self.coreSession = session - - self.setAttribute('version', '1.0') - self.setAttribute("name", "%s" % session.name) - - self.setAttribute('xmlns', 'nmfPlan') - self.setAttribute('xmlns:CORE', 'coreSpecific') - self.setAttribute('compiled', 'true') - - self.all_channel_members = {} - self.last_network_id = 0 - self.addNetworks() - self.addDevices() - self.addDefaultServices() - self.addSessionConfiguration() - - def addNetworks(self): - """ - Add networks in the session to the scenPlan. - """ - for net in self.coreSession.objects.itervalues(): - if not isinstance(net, coreobj.PyCoreNet): - continue - - if nodeutils.is_node(net, NodeTypes.CONTROL_NET): - continue - - # Do not add switches and hubs that belong to another network - if nodeutils.is_node(net, (NodeTypes.SWITCH, NodeTypes.HUB)): - if in_other_network(net): - continue - - try: - NetworkElement(self, self, net) - except: - logger.exception("error adding node") - if hasattr(net, "name") and net.name: - logger.warn('Unsupported net name: %s, class: %s, type: %s', - net.name, net.__class__.__name__, net.type) - else: - logger.warn('Unsupported net class: %s', net.__class__.__name__) - - def addDevices(self): - """ - Add device elements to the scenario plan. - """ - for node in self.coreSession.objects.itervalues(): - if not isinstance(node, nodes.PyCoreNode): - continue - - try: - DeviceElement(self, self, node) - except: - logger.exception("error adding device") - if hasattr(node, "name") and node.name: - logger.warn('Unsupported device name: %s, class: %s, type: %s', - node.name, node.__class__.__name__, node.type) - else: - logger.warn('Unsupported device: %s', node.__class__.__name__) - - def addDefaultServices(self): - """ - Add default services and node types to the ServicePlan. - """ - defaultservices = self.createElement("CORE:defaultservices") - for type in self.coreSession.services.default_services: - defaults = self.coreSession.services.get_default_services(type) - spn = self.createElement("device") - spn.setAttribute("type", type) - defaultservices.appendChild(spn) - for svc in defaults: - s = self.createElement("service") - spn.appendChild(s) - s.setAttribute("name", str(svc.name)) - if defaultservices.hasChildNodes(): - self.appendChild(defaultservices) - - def addSessionConfiguration(self): - """ - Add CORE-specific session configuration XML elements. - """ - config = self.createElement("CORE:sessionconfig") - - # origin: geolocation of cartesian coordinate 0,0,0 - refgeo = self.coreSession.location.refgeo - origin = self.createElement("origin") - attrs = ("lat", "lon", "alt") - have_origin = False - for i in xrange(3): - if refgeo[i] is not None: - origin.setAttribute(attrs[i], str(refgeo[i])) - have_origin = True - if have_origin: - if self.coreSession.location.refscale != 1.0: # 100 pixels = refscale m - origin.setAttribute("scale100", str(self.coreSession.location.refscale)) - if self.coreSession.location.refxyz != (0.0, 0.0, 0.0): - pt = self.createElement("point") - origin.appendChild(pt) - x, y, z = self.coreSession.location.refxyz - coordstxt = "%s,%s" % (x, y) - if z: - coordstxt += ",%s" % z - coords = self.createTextNode(coordstxt) - pt.appendChild(coords) - config.appendChild(origin) - - # options - options = self.createElement("options") - options_config = self.coreSession.options.get_configs() - for _id, default_value in self.coreSession.options.default_values().iteritems(): - value = options_config[_id] - if value != default_value: - XmlElement.add_parameter(self.document, options, _id, value) - - if options.hasChildNodes(): - config.appendChild(options) - - # hook scripts - hooks = self.createElement("hooks") - for state in sorted(self.coreSession._hooks.keys()): - for filename, data in self.coreSession._hooks[state]: - hook = self.createElement("hook") - hook.setAttribute("name", filename) - hook.setAttribute("state", str(state)) - txt = self.createTextNode(data) - hook.appendChild(txt) - hooks.appendChild(hook) - if hooks.hasChildNodes(): - config.appendChild(hooks) - - # metadata - meta = self.createElement("metadata") - for k, v in self.coreSession.metadata.get_configs().iteritems(): - XmlElement.add_parameter(self.document, meta, k, v) - if meta.hasChildNodes(): - config.appendChild(meta) - - if config.hasChildNodes(): - self.appendChild(config) - - -class NetworkElement(NamedXmlElement): - def __init__(self, scen_plan, parent, network_object): - """ - Add one PyCoreNet object as one network XML element. - """ - element_name = self.getNetworkName(scen_plan, network_object) - NamedXmlElement.__init__(self, scen_plan, parent, "network", element_name) - - self.scenPlan = scen_plan - - self.addPoint(network_object) - - network_type = None - if nodeutils.is_node(network_object, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)): - network_type = NetType.WIRELESS - elif nodeutils.is_node(network_object, (NodeTypes.SWITCH, NodeTypes.HUB, - NodeTypes.PEER_TO_PEER, NodeTypes.TUNNEL)): - network_type = NetType.ETHERNET - else: - network_type = "%s" % network_object.__class__.__name__ - - type_element = self.createElement("type") - type_element.appendChild(self.createTextNode(network_type)) - self.appendChild(type_element) - - # Gather all endpoints belonging to this network - self.endpoints = get_endpoints(network_object) - - # Special case for a network of switches and hubs - create_alias = True - self.l2devices = [] - if nodeutils.is_node(network_object, (NodeTypes.SWITCH, NodeTypes.HUB)): - create_alias = False - self.appendChild(type_element) - self.addL2Devices(network_object) - - if create_alias: - a = self.createAlias(Alias.ID, "%d" % int(network_object.objid)) - self.appendChild(a) - - # XXXX TODO: Move this to channel? - # key used with tunnel node - if hasattr(network_object, 'grekey') and network_object.grekey is not None: - a = self.createAlias("COREGREKEY", "%s" % network_object.grekey) - self.appendChild(a) - - self.addNetMembers(network_object) - self.addChannels(network_object) - - presentation_element = self.createElement("CORE:presentation") - add_presentation_element = False - if network_object.icon and not network_object.icon.isspace(): - presentation_element.setAttribute("icon", network_object.icon) - add_presentation_element = True - if network_object.canvas: - presentation_element.setAttribute("canvas", str(network_object.canvas)) - add_presentation_element = True - if add_presentation_element: - self.appendChild(presentation_element) - - def getNetworkName(self, scenario_plan, network_object): - """ - Determine the name to use for this network element - - :param ScenarioPlan scenario_plan: - :param network_object: - :return: - """ - if nodeutils.is_node(network_object, (NodeTypes.PEER_TO_PEER, NodeTypes.TUNNEL)): - name = "net%s" % scenario_plan.last_network_id - scenario_plan.last_network_id += 1 - elif network_object.name: - name = str(network_object.name) # could use net.brname for bridges? - elif nodeutils.is_node(network_object, (NodeTypes.SWITCH, NodeTypes.HUB)): - name = "lan%s" % network_object.objid - else: - name = '' - return name - - def addL2Devices(self, network_object): - """ - Add switches and hubs - """ - - # Add the netObj as a device - self.l2devices.append(DeviceElement(self.scenPlan, self, network_object)) - - # Add downstream switches/hubs - l2devs = [] - neweps = [] - for ep in self.endpoints: - if ep.type and ep.net.objid != network_object.objid: - l2s, eps = get_dowmstream_l2_devices(ep.net) - l2devs.extend(l2s) - neweps.extend(eps) - - for l2dev in l2devs: - self.l2devices.append(DeviceElement(self.scenPlan, self, l2dev)) - - self.endpoints.extend(neweps) - - # XXX: Optimize later - def addNetMembers(self, network_object): - """ - Add members to a network XML element. - """ - - for ep in self.endpoints: - if ep.type: - MemberElement(self.scenPlan, self, referenced_type=ep.type, referenced_id=ep.id) - - if ep.l2devport: - MemberElement(self.scenPlan, - self, - referenced_type=MembType.INTERFACE, - referenced_id="%s/%s" % (self.id, ep.l2devport)) - - # XXX Revisit this - # Create implied members given the network type - if nodeutils.is_node(network_object, NodeTypes.TUNNEL): - MemberElement(self.scenPlan, self, referenced_type=MembType.TUNNEL, - referenced_id="%s/%s" % (network_object.name, network_object.name)) - - # XXX: Optimize later - def addChannels(self, network_object): - """ - Add channels to a network XML element - """ - - if nodeutils.is_node(network_object, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)): - modelconfigs = network_object.session.mobility.get_models(network_object) - modelconfigs += network_object.session.emane.get_models(network_object) - chan = None - - for model, conf in modelconfigs: - # Handle mobility parameters below - if model.config_type == RegisterTlvs.MOBILITY.value: - continue - - # Create the channel - if chan is None: - name = "wireless" - chan = ChannelElement(self.scenPlan, self, network_object, - channel_type=model.name, - channel_name=name, - channel_domain="CORE") - - # Add wireless model parameters - for key, value in conf.iteritems(): - if value is not None: - chan.addParameter(key, value) - - for model, conf in modelconfigs: - if model.config_type == RegisterTlvs.MOBILITY.value: - # Add wireless mobility parameters - mobility = XmlElement(self.scenPlan, chan, "CORE:mobility") - # Add a type child - type_element = self.createElement("type") - type_element.appendChild(self.createTextNode(model.name)) - mobility.appendChild(type_element) - - for key, value in conf.iteritems(): - if value is not None: - mobility.addParameter(key, value) - - # Add members to the channel - if chan is not None: - chan.addChannelMembers(self.endpoints) - self.appendChild(chan.base_element) - elif nodeutils.is_node(network_object, NodeTypes.PEER_TO_PEER): - if len(self.endpoints) < 2: - if len(self.endpoints) == 1: - logger.warn('Pt2Pt network with only 1 endpoint: %s', self.endpoints[0].id) - else: - logger.warn('Pt2Pt network with no endpoints encountered in %s', network_object.name) - return - name = "chan%d" % (0) - chan = ChannelElement(self.scenPlan, self, network_object, - channel_type=NetType.ETHERNET, - channel_name=name) - - # Add interface parameters - if self.endpoints[0].params != self.endpoints[1].params: - logger.warn('Pt2Pt Endpoint parameters do not match in %s', network_object.name) - for key, value in self.endpoints[0].params: - # XXX lifted from original addnetem function. revisit this. - # default netem parameters are 0 or None - if value is None or value == 0: - continue - if key == "has_netem" or key == "has_tbf": - continue - chan.addParameter(key, value) - - # Add members to the channel - chan.addChannelMembers(self.endpoints) - self.appendChild(chan) - - elif nodeutils.is_node(network_object, (NodeTypes.SWITCH, NodeTypes.HUB, NodeTypes.TUNNEL)): - cidx = 0 - channels = [] - for ep in self.endpoints: - # Create one channel member per ep - if ep.type: - name = "chan%d" % cidx - chan = ChannelElement(self.scenPlan, self, network_object, channel_type=NetType.ETHERNET, - channel_name=name) - - # Add interface parameters - for key, value in ep.params: - # XXX lifted from original addnetem function. revisit this. - # default netem parameters are 0 or None - if value is None or value == 0: - continue - if key == "has_netem" or key == "has_tbf": - continue - chan.addParameter(key, value) - - # Add members to the channel - chan.addChannelMembers(ep) - channels.append(chan) - cidx += 1 - - for chan in channels: - self.appendChild(chan) - - -class DeviceElement(NamedXmlElement): - """ - A device element in the scenario plan. - """ - - def __init__(self, scen_plan, parent, device_object): - """ - Add a PyCoreNode object as a device element. - """ - - device_type = None - core_device_type = None - if hasattr(device_object, "type") and device_object.type: - core_device_type = device_object.type - if device_object.type in [NodeType.ROUTER, NodeType.MDR]: - device_type = DevType.ROUTER - elif device_object.type == NodeType.HUB: - device_type = DevType.HUB - elif device_object.type == NodeType.SWITCH: - device_type = DevType.SWITCH - # includes known node types (HOST, PC, RJ45) - # Default custom types (defined in ~/.core/nodes.conf) to HOST - else: - device_type = DevType.HOST - - if device_type is None: - raise ValueError("unknown device type: %s" % core_device_type) - - NamedXmlElement.__init__(self, scen_plan, parent, device_type, device_object.name) - - if core_device_type is not None: - type_element = self.createElement("type") - type_element.setAttribute("domain", "CORE") - type_element.appendChild(self.createTextNode("%s" % core_device_type)) - self.appendChild(type_element) - - self.interfaces = [] - self.addInterfaces(device_object) - alias = self.createAlias(Alias.ID, "%s" % device_object.objid) - self.appendChild(alias) - self.addPoint(device_object) - self.addServices(device_object) - - presentation_element = self.createElement("CORE:presentation") - add_presentation_element = False - if device_object.icon and not device_object.icon.isspace(): - presentation_element.setAttribute("icon", device_object.icon) - add_presentation_element = True - if device_object.canvas: - presentation_element.setAttribute("canvas", str(device_object.canvas)) - add_presentation_element = True - if add_presentation_element: - self.appendChild(presentation_element) - - def addInterfaces(self, device_object): - """ - Add interfaces to a device element. - """ - idx = 0 - for interface_object in device_object.netifs(sort=True): - if interface_object.net and nodeutils.is_node(interface_object.net, NodeTypes.CONTROL_NET): - continue - if isinstance(device_object, nodes.PyCoreNode): - interface_element = InterfaceElement(self.scenPlan, self, device_object, interface_object) - else: # isinstance(node, (nodes.HubNode nodes.SwitchNode)): - interface_element = InterfaceElement(self.scenPlan, self, device_object, interface_object, idx) - idx += 1 - - netmodel = None - if interface_object.net: - if hasattr(interface_object.net, "model"): - netmodel = interface_object.net.model - if interface_object.mtu and interface_object.mtu != 1500: - interface_element.setAttribute("mtu", "%s" % interface_object.mtu) - - # The interfaces returned for Switches and Hubs are the interfaces of the nodes connected to them. - # The addresses are for those interfaces. Don't include them here. - if isinstance(device_object, nodes.PyCoreNode): - # could use ifcObj.params, transport_type - interface_element.addAddresses(interface_object) - # per-interface models - # XXX Remove??? - if netmodel and netmodel.name[:6] == "emane_": - cfg = self.coreSession.emane.getifcconfig(device_object.objid, interface_object, netmodel.name) - if cfg: - interface_element.addModels(((netmodel, cfg),)) - - self.interfaces.append(interface_element) - - def addServices(self, device_object): - """ - Add services and their customizations to the ServicePlan. - """ - if not hasattr(device_object, "services"): - return - - if len(device_object.services) == 0: - return - - defaults = self.coreSession.services.get_default_services(device_object.type) - if device_object.services == defaults: - return - spn = self.createElement("CORE:services") - spn.setAttribute("name", device_object.name) - self.appendChild(spn) - - for svc in device_object.services: - s = self.createElement("service") - spn.appendChild(s) - s.setAttribute("name", str(svc.name)) - # only record service names if not a customized service - if not svc.custom: - continue - s.setAttribute("custom", str(svc.custom)) - xmlutils.add_elements_from_list(self, s, svc.dirs, "directory", "name") - - for fn in svc.configs: - if len(fn) == 0: - continue - f = self.createElement("file") - f.setAttribute("name", fn) - # all file names are added to determine when a file has been deleted - s.appendChild(f) - data = svc.config_data.get(fn) - if data is None: - # this includes only customized file contents and skips - # the auto-generated files - continue - txt = self.createTextNode("\n" + data) - f.appendChild(txt) - - xmlutils.add_text_elements_from_list(self, s, svc.startup, "command", (("type", "start"),)) - xmlutils.add_text_elements_from_list(self, s, svc.shutdown, "command", (("type", "stop"),)) - xmlutils.add_text_elements_from_list(self, s, svc.validate, "command", (("type", "validate"),)) - - -class ChannelElement(NamedXmlElement): - """ - A channel element in the scenario plan - """ - - def __init__(self, scen_plan, parent, network_object, channel_type, channel_name, channel_domain=None): - NamedXmlElement.__init__(self, scen_plan, parent, "channel", channel_name) - ''' - Create a channel element and append a member child referencing this channel element - in the parent element. - ''' - # Create a member element for this channel in the parent - MemberElement(self.scenPlan, parent, referenced_type=MembType.CHANNEL, referenced_id=self.id) - - # Add a type child - type_element = self.createElement("type") - if channel_domain is not None: - type_element.setAttribute("domain", "%s" % channel_domain) - type_element.appendChild(self.createTextNode(channel_type)) - self.appendChild(type_element) - - def addChannelMembers(self, endpoints): - """ - Add network channel members referencing interfaces in the channel - """ - if isinstance(endpoints, list): - # A list of endpoints is given. Create one channel member per endpoint - idx = 0 - for ep in endpoints: - self.addChannelMember(ep.type, ep.id, idx) - idx += 1 - else: - # A single endpoint is given. Create one channel member for the endpoint, - # and if the endpoint is associated with a Layer 2 device port, add the - # port as a second member - ep = endpoints - self.addChannelMember(ep.type, ep.id, 0) - if ep.l2devport is not None: - member_id = "%s/%s" % (self.parent.getAttribute("id"), ep.l2devport) - self.addChannelMember(ep.type, member_id, 1) - - def addChannelMember(self, member_interface_type, member_interface_id, member_index): - """ - add a member to a given channel - """ - - m = MemberElement(self.scenPlan, - self, - referenced_type=member_interface_type, - referenced_id=member_interface_id, - index=member_index) - self.scenPlan.all_channel_members[member_interface_id] = m - - -class InterfaceElement(NamedXmlElement): - """ - A network interface element - """ - - def __init__(self, scen_plan, parent, device_object, interface_object, interface_index=None): - """ - Create a network interface element with references to channel that this - interface is used. - """ - element_name = None - if interface_index is not None: - element_name = "e%d" % interface_index - else: - element_name = interface_object.name - NamedXmlElement.__init__(self, scen_plan, parent, "interface", element_name) - self.ifcObj = interface_object - self.addChannelReference() - - def addChannelReference(self): - """ - Add a reference to the channel that uses this interface - """ - # cm is None when an interface belongs to a switch - # or a hub within a network and the channel is yet to be defined - cm = self.scenPlan.all_channel_members.get(self.id) - if cm is not None: - ch = cm.base_element.parentNode - if ch is not None: - net = ch.parentNode - if net is not None: - MemberElement(self.scenPlan, - self, - referenced_type=MembType.CHANNEL, - referenced_id=ch.getAttribute("id"), - index=int(cm.getAttribute("index"))) - MemberElement(self.scenPlan, - self, - referenced_type=MembType.NETWORK, - referenced_id=net.getAttribute("id")) - - def addAddresses(self, interface_object): - """ - Add MAC and IP addresses to interface XML elements. - """ - if interface_object.hwaddr: - h = self.createElement("address") - self.appendChild(h) - h.setAttribute("type", "mac") - htxt = self.createTextNode("%s" % interface_object.hwaddr) - h.appendChild(htxt) - for addr in interface_object.addrlist: - a = self.createElement("address") - self.appendChild(a) - (ip, sep, mask) = addr.partition('/') - # mask = int(mask) XXX? - if ipaddress.is_ipv4_address(ip): - a.setAttribute("type", "IPv4") - else: - a.setAttribute("type", "IPv6") - - # a.setAttribute("type", ) - atxt = self.createTextNode("%s" % addr) - a.appendChild(atxt) - - # XXX Remove? - def addModels(self, configs): - """ - Add models from a list of model-class, config values tuples. - """ - for m, conf in configs: - node_element = self.createElement("model") - node_element.setAttribute("name", m.name) - type_string = "wireless" - if m.config_type == RegisterTlvs.MOBILITY.value: - type_string = "mobility" - node_element.setAttribute("type", type_string) - for i, k in enumerate(m.getnames()): - key = self.createElement(k) - value = conf[i] - if value is None: - value = "" - key.appendChild(self.createTextNode("%s" % value)) - node_element.appendChild(key) - self.appendChild(node_element) - - -class MemberElement(XmlElement): - """ - Member elements are references to other elements in the network plan elements of the scenario. - They are used in networks to reference channels, in channels to reference interfaces, - and in interfaces to reference networks/channels. Member elements provided allow bi-directional - traversal of network plan components. - """ - - def __init__(self, scene_plan, parent, referenced_type, referenced_id, index=None): - """ - Create a member element - """ - XmlElement.__init__(self, scene_plan.document, parent, "member") - self.setAttribute("type", "%s" % referenced_type) - # See'Understanding the Network Modeling Framework document' - if index is not None: - self.setAttribute("index", "%d" % index) - self.appendChild(self.createTextNode("%s" % referenced_id)) - - -# -# ======================================================================================= -# Helpers -# ======================================================================================= - -def get_endpoint(network_object, interface_object): - """ - Create an Endpoint object given the network and the interface of interest - """ - ep = None - l2devport = None - - # skip if either are none - if not network_object or not interface_object: - return ep - - # if ifcObj references an interface of a node and is part of this network - if interface_object.net.objid == network_object.objid and hasattr(interface_object, - 'node') and interface_object.node: - params = interface_object.getparams() - if nodeutils.is_node(interface_object.net, (NodeTypes.HUB, NodeTypes.SWITCH)): - l2devport = "%s/e%d" % (interface_object.net.name, interface_object.net.getifindex(interface_object)) - ep = Endpoint(network_object, - interface_object, - type=MembType.INTERFACE, - id="%s/%s" % (interface_object.node.name, interface_object.name), - l2devport=l2devport, - params=params) - - # else if ifcObj references another node and is connected to this network - elif hasattr(interface_object, "othernet"): - if interface_object.othernet.objid == network_object.objid: - # #hack used for upstream parameters for link between switches - # #(see LxBrNet.linknet()) - interface_object.swapparams('_params_up') - params = interface_object.getparams() - interface_object.swapparams('_params_up') - owner = interface_object.net - l2devport = "%s/e%d" % ( - interface_object.othernet.name, interface_object.othernet.getifindex(interface_object)) - - # Create the endpoint. - # XXX the interface index might not match what is shown in the gui. For switches and hubs, - # The gui assigns its index but doesn't pass it to the daemon and vice versa. - # The gui stores it's index in the IMN file, which it reads and writes without daemon intervention. - # Fix this! - ep = Endpoint(owner, - interface_object, - type=MembType.INTERFACE, - id="%s/%s/e%d" % (network_object.name, owner.name, owner.getifindex(interface_object)), - l2devport=l2devport, - params=params) - # else this node has an interface that belongs to another network - # i.e. a switch/hub interface connected to another switch/hub and CORE has the other switch/hub - # as the containing network - else: - ep = Endpoint(network_object, interface_object, type=None, id=None, l2devport=None, params=None) - - return ep - - -def get_endpoints(network_object): - """ - Gather all endpoints of the given network - """ - # Get all endpoints - endpoints = [] - - # XXX TODO: How to represent physical interfaces. - # - # NOTE: The following code works except it would be missing physical (rj45) interfaces from Pt2pt links - # TODO: Fix data in net.netifs to include Pt2Pt physical interfaces - # - # Iterate through all the nodes in the scenario, then iterate through all the interface for each node, - # and check if the interface is connected to this network. - - for interface_object in network_object.netifs(sort=True): - try: - ep = get_endpoint(network_object, interface_object) - if ep is not None: - endpoints.append(ep) - except: - logger.debug("error geting endpoints, was skipped before") - - return endpoints - - -def get_dowmstream_l2_devices(network_object): - """ - Helper function for getting a list of all downstream layer 2 devices from the given netObj - """ - l2_device_objects = [network_object] - allendpoints = [] - myendpoints = get_endpoints(network_object) - allendpoints.extend(myendpoints) - for ep in myendpoints: - if ep.type and ep.net.objid != network_object.objid: - l2s, eps = get_dowmstream_l2_devices(ep.net) - l2_device_objects.extend(l2s) - allendpoints.extend(eps) - - return l2_device_objects, allendpoints - - -def get_all_network_interfaces(session): - """ - Gather all network interfacecs in the session - """ - netifs = [] - for node in session.objects.itervalues(): - for netif in node.netifs(sort=True): - if netif not in netifs: - netifs.append(netif) - return netifs - - -def in_other_network(network_object): - """ - Determine if CORE considers a given network object to be part of another network. - Note: CORE considers layer 2 devices to be their own networks. However, if a l2 device - is connected to another device, it is possible that one of its ports belong to the other - l2 device's network (thus, "othernet"). - """ - for netif in network_object.netifs(sort=True): - if hasattr(netif, "othernet"): - if netif.othernet.objid != network_object.objid: - return True - return False diff --git a/daemon/tests/test_xml.py b/daemon/tests/test_xml.py index 3b7c6730..058a5e7b 100644 --- a/daemon/tests/test_xml.py +++ b/daemon/tests/test_xml.py @@ -8,15 +8,9 @@ from core.enumerations import NodeTypes from core.mobility import BasicRangeModel from core.services.utility import SshService -_XML_VERSIONS = [ - "0.0", - "1.0" -] - class TestXml: - @pytest.mark.parametrize("version", _XML_VERSIONS) - def test_xml_hooks(self, session, tmpdir, version): + def test_xml_hooks(self, session, tmpdir): """ Test save/load hooks in xml. @@ -32,7 +26,7 @@ class TestXml: # save xml xml_file = tmpdir.join("session.xml") file_path = xml_file.strpath - session.save_xml(file_path, version) + session.save_xml(file_path) # verify xml file was created and can be parsed assert xml_file.isfile() @@ -51,8 +45,7 @@ class TestXml: assert file_name == runtime_hook[0] assert data == runtime_hook[1] - @pytest.mark.parametrize("version", _XML_VERSIONS) - def test_xml_ptp(self, session, tmpdir, version, ip_prefixes): + def test_xml_ptp(self, session, tmpdir, ip_prefixes): """ Test xml client methods for a ptp network. @@ -83,7 +76,7 @@ class TestXml: # save xml xml_file = tmpdir.join("session.xml") file_path = xml_file.strpath - session.save_xml(file_path, version) + session.save_xml(file_path) # verify xml file was created and can be parsed assert xml_file.isfile() @@ -105,8 +98,7 @@ class TestXml: assert session.get_object(n1_id) assert session.get_object(n2_id) - @pytest.mark.parametrize("version", _XML_VERSIONS) - def test_xml_ptp_services(self, session, tmpdir, version, ip_prefixes): + def test_xml_ptp_services(self, session, tmpdir, ip_prefixes): """ Test xml client methods for a ptp neetwork. @@ -144,7 +136,7 @@ class TestXml: # save xml xml_file = tmpdir.join("session.xml") file_path = xml_file.strpath - session.save_xml(file_path, version) + session.save_xml(file_path) # verify xml file was created and can be parsed assert xml_file.isfile() @@ -170,8 +162,7 @@ class TestXml: assert session.get_object(n2_id) assert service.config_data.get(service_file) == file_data - @pytest.mark.parametrize("version", _XML_VERSIONS) - def test_xml_mobility(self, session, tmpdir, version, ip_prefixes): + def test_xml_mobility(self, session, tmpdir, ip_prefixes): """ Test xml client methods for mobility. @@ -206,7 +197,7 @@ class TestXml: # save xml xml_file = tmpdir.join("session.xml") file_path = xml_file.strpath - session.save_xml(file_path, version) + session.save_xml(file_path) # verify xml file was created and can be parsed assert xml_file.isfile() @@ -233,8 +224,7 @@ class TestXml: assert session.get_object(wlan_id) assert value == "1" - @pytest.mark.parametrize("version", ["1.0"]) - def test_xml_emane(self, session, tmpdir, version, ip_prefixes): + def test_xml_emane(self, session, tmpdir, ip_prefixes): """ Test xml client methods for emane. @@ -274,7 +264,7 @@ class TestXml: # save xml xml_file = tmpdir.join("session.xml") file_path = xml_file.strpath - session.save_xml(file_path, version) + session.save_xml(file_path) # verify xml file was created and can be parsed assert xml_file.isfile()