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)