From 3ffdf7c420f9d8c2c73120b7b16fa3696402a9ce Mon Sep 17 00:00:00 2001 From: tgoff0 Date: Mon, 15 Dec 2014 18:22:46 +0000 Subject: [PATCH] daemon: Some code reorganization to make XML support more modular. --- daemon/core/misc/xmlparser.py | 419 +++++++++++++++++ daemon/core/misc/xmlsession.py | 33 ++ daemon/core/misc/xmlutils.py | 797 +-------------------------------- daemon/core/misc/xmlwriter.py | 377 ++++++++++++++++ daemon/sbin/core-daemon | 2 +- 5 files changed, 832 insertions(+), 796 deletions(-) create mode 100644 daemon/core/misc/xmlparser.py create mode 100644 daemon/core/misc/xmlsession.py create mode 100644 daemon/core/misc/xmlwriter.py diff --git a/daemon/core/misc/xmlparser.py b/daemon/core/misc/xmlparser.py new file mode 100644 index 00000000..64763271 --- /dev/null +++ b/daemon/core/misc/xmlparser.py @@ -0,0 +1,419 @@ +# +# CORE +# Copyright (c)2011-2013 the Boeing Company. +# See the LICENSE file included in this distribution. +# +# author: Jeff Ahrenholz +# + +from core.netns import nodes +from xml.dom.minidom import parse +from xml.dom.minidom import Document +from xmlutils import * + +class CoreDocumentParser(object): + def __init__(self, session, filename, start=False, + nodecls=nodes.CoreNode): + self.session = session + self.verbose = self.session.getcfgitembool('verbose', False) + self.filename = filename + self.dom = parse(filename) + self.start = start + self.nodecls = nodecls + + #self.scenario = getoneelement(self.dom, "Scenario") + self.np = getoneelement(self.dom, "NetworkPlan") + if self.np is None: + raise ValueError, "missing NetworkPlan!" + self.mp = getoneelement(self.dom, "MotionPlan") + self.sp = getoneelement(self.dom, "ServicePlan") + self.meta = getoneelement(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 warn(self, msg): + if self.session: + warnstr = "XML parsing '%s':" % (self.filename) + self.session.warn("%s %s" % (warnstr, msg)) + + 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. + ''' + id = int(obj.getAttribute("id")) + name = str(obj.getAttribute("name")) + type = str(obj.getAttribute("type")) + return(id, name, type) + + def parsenets(self): + linkednets = [] + for net in self.np.getElementsByTagName("NetworkDefinition"): + id, name, type = self.getcommonattributes(net) + nodecls = xmltypetonodeclass(self.session, type) + if not nodecls: + self.warn("skipping unknown network node '%s' type '%s'" % \ + (name, type)) + continue + n = self.session.addobj(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) + getparamssetattrs(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.objbyname(netid) + except KeyError: + n.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 = nodes.RJ45Node + else: + nodecls = self.nodecls + n = self.session.addobj(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 + getparamssetattrs(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.objbyname(netid) + except KeyError: + n.warn("skipping node %s interface %s: unknown net %s" % \ + (n.name, name, netid)) + return + for addr in ifc.getElementsByTagName("address"): + addrstr = gettextchild(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 = gettextelementstolist(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) + + elif name[:3] == "xen": + mgr = self.session.xen + # TODO: assign other config managers here + if mgr: + mgr.setconfig_keyvalues(nodenum, name, kvs) + + 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(lambda(k, v): (int(v)), kvs) + kvs = map(self.numericvalue, kvs) + except ValueError: + self.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 = getoneelement(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: + location.refscale = float(scale) + point = getoneelement(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.defaultservices[type] = services + self.session.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.objbyname(name) + if n is None: + self.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.obj(objid) + self.session.services.addservicestonode(node=n, nodetype=n.type, + services_str=svclists[objid], + verbose=self.verbose) + + 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 = self.session.services.getservicebyname(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 = gettextchild(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 = gettextchild(file) + typestr = "service:%s:%s" % (name, filename) + self.session.services.setservicefile(nodenum=n.objid, type=typestr, + filename=filename, + srcname=None, data=data) + if len(files): + values.append("files=%s" % files) + if not bool(service.getAttribute("custom")): + return True + self.session.services.setcustomservice(n.objid, svc, values) + 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 = gettextchild(hook) + if data is None: + data = "" # allow for empty file + type = "hook:%s" % state + self.session.sethook(type, filename=filename, + srcname=None, data=data) + + def parsemeta(self): + opt = getoneelement(self.meta, "SessionOptions") + if opt: + for param in opt.getElementsByTagName("param"): + k = str(param.getAttribute("name")) + v = str(param.getAttribute("value")) + if v == '': + v = gettextchild(param) # allow attribute/text for newlines + setattr(self.session.options, k, v) + hooks = getoneelement(self.meta, "Hooks") + if hooks: + self.parsehooks(hooks) + meta = getoneelement(self.meta, "MetaData") + if meta: + for param in meta.getElementsByTagName("param"): + k = str(param.getAttribute("name")) + v = str(param.getAttribute("value")) + if v == '': + v = gettextchild(param) + self.session.metadata.additem(k, v) diff --git a/daemon/core/misc/xmlsession.py b/daemon/core/misc/xmlsession.py new file mode 100644 index 00000000..77d9ba9c --- /dev/null +++ b/daemon/core/misc/xmlsession.py @@ -0,0 +1,33 @@ +# +# CORE +# Copyright (c)2011-2013 the Boeing Company. +# See the LICENSE file included in this distribution. +# +# author: Jeff Ahrenholz +# + +''' +Helpers for loading and saving XML files. savesessionxml(session, filename) is +the main public interface here. +''' + +import os.path +from core.netns import nodes +from xmlparser import CoreDocumentParser +from xmlwriter import CoreDocumentWriter + +def opensessionxml(session, filename, start=False, nodecls=nodes.CoreNode): + ''' Import a session from the EmulationScript XML format. + ''' + doc = CoreDocumentParser(session, filename, start, nodecls) + if start: + session.name = os.path.basename(filename) + session.filename = filename + session.node_count = str(session.getnodecount()) + session.instantiate() + +def savesessionxml(session, filename): + ''' Export a session to the EmulationScript XML format. + ''' + doc = CoreDocumentWriter(session) + doc.writexml(filename) diff --git a/daemon/core/misc/xmlutils.py b/daemon/core/misc/xmlutils.py index 4783b738..864b4e40 100644 --- a/daemon/core/misc/xmlutils.py +++ b/daemon/core/misc/xmlutils.py @@ -5,14 +5,9 @@ # # author: Jeff Ahrenholz # -''' -Helpers for loading and saving XML files. savesessionxml(session, filename) is -the main public interface here. -''' -import os, pwd -from xml.dom.minidom import parse, Document, Node + from core.netns import nodes -from core.api import coreapi +from xml.dom.minidom import Node def addelementsfromlist(dom, parent, iterable, name, attr_name): ''' XML helper to iterate through a list and add items to parent using tags @@ -168,791 +163,3 @@ def xmltypetonodeclass(session, type): return eval("nodes.%s" % type) else: return None - -class CoreDocumentParser(object): - def __init__(self, session, filename, start=False, - nodecls=nodes.CoreNode): - self.session = session - self.verbose = self.session.getcfgitembool('verbose', False) - self.filename = filename - self.dom = parse(filename) - self.start = start - self.nodecls = nodecls - - #self.scenario = getoneelement(self.dom, "Scenario") - self.np = getoneelement(self.dom, "NetworkPlan") - if self.np is None: - raise ValueError, "missing NetworkPlan!" - self.mp = getoneelement(self.dom, "MotionPlan") - self.sp = getoneelement(self.dom, "ServicePlan") - self.meta = getoneelement(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 warn(self, msg): - if self.session: - warnstr = "XML parsing '%s':" % (self.filename) - self.session.warn("%s %s" % (warnstr, msg)) - - 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. - ''' - id = int(obj.getAttribute("id")) - name = str(obj.getAttribute("name")) - type = str(obj.getAttribute("type")) - return(id, name, type) - - def parsenets(self): - linkednets = [] - for net in self.np.getElementsByTagName("NetworkDefinition"): - id, name, type = self.getcommonattributes(net) - nodecls = xmltypetonodeclass(self.session, type) - if not nodecls: - self.warn("skipping unknown network node '%s' type '%s'" % \ - (name, type)) - continue - n = self.session.addobj(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) - getparamssetattrs(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.objbyname(netid) - except KeyError: - n.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 = nodes.RJ45Node - else: - nodecls = self.nodecls - n = self.session.addobj(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 - getparamssetattrs(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.objbyname(netid) - except KeyError: - n.warn("skipping node %s interface %s: unknown net %s" % \ - (n.name, name, netid)) - return - for addr in ifc.getElementsByTagName("address"): - addrstr = gettextchild(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 = gettextelementstolist(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) - - elif name[:3] == "xen": - mgr = self.session.xen - # TODO: assign other config managers here - if mgr: - mgr.setconfig_keyvalues(nodenum, name, kvs) - - 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(lambda(k, v): (int(v)), kvs) - kvs = map(self.numericvalue, kvs) - except ValueError: - self.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 = getoneelement(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: - location.refscale = float(scale) - point = getoneelement(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.defaultservices[type] = services - self.session.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.objbyname(name) - if n is None: - self.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.obj(objid) - self.session.services.addservicestonode(node=n, nodetype=n.type, - services_str=svclists[objid], - verbose=self.verbose) - - 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 = self.session.services.getservicebyname(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 = gettextchild(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 = gettextchild(file) - typestr = "service:%s:%s" % (name, filename) - self.session.services.setservicefile(nodenum=n.objid, type=typestr, - filename=filename, - srcname=None, data=data) - if len(files): - values.append("files=%s" % files) - if not bool(service.getAttribute("custom")): - return True - self.session.services.setcustomservice(n.objid, svc, values) - 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 = gettextchild(hook) - if data is None: - data = "" # allow for empty file - type = "hook:%s" % state - self.session.sethook(type, filename=filename, - srcname=None, data=data) - - def parsemeta(self): - opt = getoneelement(self.meta, "SessionOptions") - if opt: - for param in opt.getElementsByTagName("param"): - k = str(param.getAttribute("name")) - v = str(param.getAttribute("value")) - if v == '': - v = gettextchild(param) # allow attribute/text for newlines - setattr(self.session.options, k, v) - hooks = getoneelement(self.meta, "Hooks") - if hooks: - self.parsehooks(hooks) - meta = getoneelement(self.meta, "MetaData") - if meta: - for param in meta.getElementsByTagName("param"): - k = str(param.getAttribute("name")) - v = str(param.getAttribute("value")) - if v == '': - v = gettextchild(param) - self.session.metadata.additem(k, v) - - -class CoreDocumentWriter(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): - self.session.emane.setup() # not during runtime? - self.addorigin() - self.adddefaultservices() - self.addnets() - self.addnodes() - self.addmetadata() - - def writexml(self, filename): - self.session.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._objslock: - for net in self.session.objs(): - if not isinstance(net, nodes.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.getmodels(net) - modelconfigs += net.session.emane.getmodels(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._type == coreapi.CORE_TLV_REG_MOBILITY: - type = "mobility" - model.setAttribute("type", type) - 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)) - model.appendChild(key) - - def addnodes(self): - ''' Add PyCoreNode objects as node XML elements. - ''' - with self.session._objslock: - for node in self.session.objs(): - if not isinstance(node, nodes.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.nodeid()) - if node.type: - n.setAttribute("type", node.type) - self.addinterfaces(n, node) - self.addposition(node) - addparamtoparent(self, n, "icon", node.icon) - addparamtoparent(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, netmodel._name, - None, ifc) - 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.defaultservices: - defaults = self.session.services.getdefaultservices(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.getdefaultservices(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)) - s.setAttribute("startup_idx", str(svc._startindex)) - if svc._starttime != "": - s.setAttribute("start_time", str(svc._starttime)) - # only record service names if not a customized service - if not svc._custom: - continue - s.setAttribute("custom", str(svc._custom)) - addelementsfromlist(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 = self.session.services.getservicefiledata(svc, 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) - - addtextelementsfromlist(self, s, svc._startup, "Command", - (("type","start"),)) - addtextelementsfromlist(self, s, svc._shutdown, "Command", - (("type","stop"),)) - addtextelementsfromlist(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.getdefaultvalues() - for i, (k, v) in enumerate(self.session.options.getkeyvaluelist()): - if str(v) != str(defaults[i]): - addtextparamtoparent(self, options, k, v) - #addparamtoparent(self, options, k, v) - if options.hasChildNodes(): - self.meta.appendChild(options) - # hook scripts - self.addhooks() - # meta - meta = self.createElement("MetaData") - self.meta.appendChild(meta) - for (k, v) in self.session.metadata.items(): - addtextparamtoparent(self, meta, k, v) - #addparamtoparent(self, meta, k, v) - -def opensessionxml(session, filename, start=False, nodecls=nodes.CoreNode): - ''' Import a session from the EmulationScript XML format. - ''' - doc = CoreDocumentParser(session, filename, start, nodecls) - if start: - session.name = os.path.basename(filename) - session.filename = filename - session.node_count = str(session.getnodecount()) - session.instantiate() - -def savesessionxml(session, filename): - ''' Export a session to the EmulationScript XML format. - ''' - doc = CoreDocumentWriter(session) - doc.writexml(filename) - diff --git a/daemon/core/misc/xmlwriter.py b/daemon/core/misc/xmlwriter.py new file mode 100644 index 00000000..3fc3ba4b --- /dev/null +++ b/daemon/core/misc/xmlwriter.py @@ -0,0 +1,377 @@ +# +# CORE +# Copyright (c)2011-2013 the Boeing Company. +# See the LICENSE file included in this distribution. +# +# author: Jeff Ahrenholz +# + +import os +import pwd +from core.netns import nodes +from core.api import coreapi +from xml.dom.minidom import Document +from xmlutils import * + +class CoreDocumentWriter(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): + self.session.emane.setup() # not during runtime? + self.addorigin() + self.adddefaultservices() + self.addnets() + self.addnodes() + self.addmetadata() + + def writexml(self, filename): + self.session.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._objslock: + for net in self.session.objs(): + if not isinstance(net, nodes.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.getmodels(net) + modelconfigs += net.session.emane.getmodels(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._type == coreapi.CORE_TLV_REG_MOBILITY: + type = "mobility" + model.setAttribute("type", type) + 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)) + model.appendChild(key) + + def addnodes(self): + ''' Add PyCoreNode objects as node XML elements. + ''' + with self.session._objslock: + for node in self.session.objs(): + if not isinstance(node, nodes.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.nodeid()) + if node.type: + n.setAttribute("type", node.type) + self.addinterfaces(n, node) + self.addposition(node) + addparamtoparent(self, n, "icon", node.icon) + addparamtoparent(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, netmodel._name, + None, ifc) + 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.defaultservices: + defaults = self.session.services.getdefaultservices(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.getdefaultservices(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)) + s.setAttribute("startup_idx", str(svc._startindex)) + if svc._starttime != "": + s.setAttribute("start_time", str(svc._starttime)) + # only record service names if not a customized service + if not svc._custom: + continue + s.setAttribute("custom", str(svc._custom)) + addelementsfromlist(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 = self.session.services.getservicefiledata(svc, 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) + + addtextelementsfromlist(self, s, svc._startup, "Command", + (("type","start"),)) + addtextelementsfromlist(self, s, svc._shutdown, "Command", + (("type","stop"),)) + addtextelementsfromlist(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.getdefaultvalues() + for i, (k, v) in enumerate(self.session.options.getkeyvaluelist()): + if str(v) != str(defaults[i]): + addtextparamtoparent(self, options, k, v) + #addparamtoparent(self, options, k, v) + if options.hasChildNodes(): + self.meta.appendChild(options) + # hook scripts + self.addhooks() + # meta + meta = self.createElement("MetaData") + self.meta.appendChild(meta) + for (k, v) in self.session.metadata.items(): + addtextparamtoparent(self, meta, k, v) + #addparamtoparent(self, meta, k, v) diff --git a/daemon/sbin/core-daemon b/daemon/sbin/core-daemon index 66b40c30..0a7cd7e2 100755 --- a/daemon/sbin/core-daemon +++ b/daemon/sbin/core-daemon @@ -35,7 +35,7 @@ from core.constants import * from core.api import coreapi from core.coreobj import PyCoreNet from core.misc.utils import hexdump, daemonize, cmdresult, mutedetach -from core.misc.xmlutils import opensessionxml, savesessionxml +from core.misc.xmlsession import opensessionxml, savesessionxml DEFAULT_MAXFD = 1024