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)