diff --git a/daemon/core/misc/xmlsession.py b/daemon/core/misc/xmlsession.py index ad0efde5..f74388dc 100644 --- a/daemon/core/misc/xmlsession.py +++ b/daemon/core/misc/xmlsession.py @@ -14,7 +14,7 @@ the main public interface here. import os.path from core.netns import nodes from xmlparser import core_document_parser -from xmlwriter import CoreDocumentWriter +from xmlwriter import core_document_writer def opensessionxml(session, filename, start=False, nodecls=nodes.CoreNode): ''' Import a session from the EmulationScript XML format. @@ -27,8 +27,8 @@ def opensessionxml(session, filename, start=False, nodecls=nodes.CoreNode): session.node_count = str(session.getnodecount()) session.instantiate() -def savesessionxml(session, filename): +def savesessionxml(session, filename, version = 0.0): ''' Export a session to the EmulationScript XML format. ''' - doc = CoreDocumentWriter(session) + doc = core_document_writer(session, version) doc.writexml(filename) diff --git a/daemon/core/misc/xmlwriter.py b/daemon/core/misc/xmlwriter.py index 3fc3ba4b..37818cbe 100644 --- a/daemon/core/misc/xmlwriter.py +++ b/daemon/core/misc/xmlwriter.py @@ -1,377 +1,12 @@ -# # CORE -# Copyright (c)2011-2013 the Boeing Company. +# Copyright (c) 2015 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 * +from xmlwriter0 import CoreDocumentWriter0 -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 core_document_writer(session, version): + if version == 0.0: + doc = CoreDocumentWriter0(session) + else: + raise ValueError, 'unsupported document version: %s' % version + return doc diff --git a/daemon/core/misc/xmlwriter0.py b/daemon/core/misc/xmlwriter0.py new file mode 100644 index 00000000..ce025477 --- /dev/null +++ b/daemon/core/misc/xmlwriter0.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 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): + 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)