class Session(object): # sessions that get automatically shutdown when the process # terminates normally __sessions = set() verbose = False ''' CORE session manager. ''' def __init__(self, sessionid = None, cfg = {}, server = None, persistent = False, mkdir = True): if sessionid is None: # try to keep this short since it's used to construct # network interface names pid = os.getpid() sessionid = ((pid >> 16) ^ (pid & ((1 << 16) - 1))) sessionid ^= ((id(self) >> 16) ^ (id(self) & ((1 << 16) - 1))) sessionid &= 0xffff self.sessionid = sessionid self.sessiondir = os.path.join(tempfile.gettempdir(), "pycore.%s" % self.sessionid) if mkdir: os.mkdir(self.sessiondir) self.name = None self.filename = None self.thumbnail = None self.user = None self.node_count = None self._time = time.time() self.evq = EventLoop() # dict of objects: all nodes and nets self._objs = {} self._objslock = threading.Lock() # dict of configurable objects self._confobjs = {} self._confobjslock = threading.Lock() self._handlers = set() self._handlerslock = threading.Lock() self._hooks = {} self.setstate(state=coreapi.CORE_EVENT_DEFINITION_STATE, info=False, sendevent=False) # dict of configuration items from /etc/core/core.conf config file self.cfg = cfg self.server = server if not persistent: self.addsession(self) self.master = False self.broker = CoreBroker(session=self, verbose=self.verbose) self.location = CoreLocation(self) self.mobility = MobilityManager(self) self.netidmanager = NetIDSubnetMapManager(self) self.services = CoreServices(self) self.emane = emane.Emane(self) self.xen = xenconfig.XenConfigManager(self) self.sdt = Sdt(self) # future parameters set by the GUI may go here self.options = SessionConfig(self) self.metadata = SessionMetaData(self) @classmethod def addsession(cls, session): cls.__sessions.add(session) @classmethod def delsession(cls, session): try: cls.__sessions.remove(session) except KeyError: pass @classmethod def atexit(cls): while cls.__sessions: s = cls.__sessions.pop() if cls.verbose: print(("WARNING: automatically shutting down " \ "non-persistent session %s" % s.sessionid), file = sys.stderr) s.shutdown() def __del__(self): # note: there is no guarantee this will ever run self.shutdown() def shutdown(self): ''' Shut down all emulation objects and remove the session directory. ''' self.emane.shutdown() self.broker.shutdown() self.sdt.shutdown() self.delobjs() self.netidmanager.clear() preserve = False if hasattr(self.options, 'preservedir'): if self.options.preservedir == '1': preserve = True if not preserve: shutil.rmtree(self.sessiondir, ignore_errors = True) if self.server: self.server.delsession(self) self.delsession(self) def isconnected(self): ''' Returns true if this session has a request handler. ''' with self._handlerslock: if len(self._handlers) == 0: return False else: return True def connect(self, handler): ''' Set the request handler for this session, making it connected. ''' # the master flag will only be set after a GUI has connected with the # handler, e.g. not during normal startup if handler.master is True: self.master = True with self._handlerslock: self._handlers.add(handler) def disconnect(self, handler): ''' Disconnect a request handler from this session. Shutdown this session if there is no running emulation. ''' with self._handlerslock: try: self._handlers.remove(handler) except KeyError: raise ValueError("Handler %s not associated with this session" % handler) num_handlers = len(self._handlers) if num_handlers == 0: # shut down this session unless we are instantiating, running, # or collecting final data if self.getstate() < coreapi.CORE_EVENT_INSTANTIATION_STATE or \ self.getstate() > coreapi.CORE_EVENT_DATACOLLECT_STATE: self.shutdown() def broadcast(self, src, msg): ''' Send Node and Link CORE API messages to all handlers connected to this session. ''' self._handlerslock.acquire() for handler in self._handlers: if handler == src: continue if isinstance(msg, coreapi.CoreNodeMessage) or \ isinstance(msg, coreapi.CoreLinkMessage): try: handler.sendall(msg.rawmsg) except Exception as e: self.warn("sendall() error: %s" % e) self._handlerslock.release() def broadcastraw(self, src, data): ''' Broadcast raw data to all handlers except src. ''' self._handlerslock.acquire() for handler in self._handlers: if handler == src: continue try: handler.sendall(data) except Exception as e: self.warn("sendall() error: %s" % e) self._handlerslock.release() def gethandler(self): ''' Get one of the connected handlers, preferrably the master. ''' with self._handlerslock: if len(self._handlers) == 0: return None for handler in self._handlers: if handler.master: return handler for handler in self._handlers: return handler def setstate(self, state, info = False, sendevent = False, returnevent = False): ''' Set the session state. When info is true, log the state change event using the session handler's info method. When sendevent is true, generate a CORE API Event Message and send to the connected entity. ''' self._time = time.time() self._state = state replies = [] if self.isconnected() and info: statename = coreapi.state_name(state) with self._handlerslock: for handler in self._handlers: handler.info("SESSION %s STATE %d: %s at %s" % \ (self.sessionid, state, statename, time.ctime())) self.writestate(state) self.runhook(state) if self.isconnected() and sendevent: tlvdata = b"" tlvdata += coreapi.CoreEventTlv.pack(coreapi.CORE_TLV_EVENT_TYPE, state) msg = coreapi.CoreEventMessage.pack(0, tlvdata) # send Event Message to connected handlers (e.g. GUI) try: if returnevent: replies.append(msg) else: self.broadcastraw(None, msg) except Exception as e: self.warn("Error sending Event Message: %s" % e) # also inform slave servers tmp = self.broker.handlerawmsg(msg) return replies def getstate(self): ''' Retrieve the current state of the session. ''' return self._state def writestate(self, state): ''' Write the current state to a state file in the session dir. ''' try: f = open(os.path.join(self.sessiondir, "state"), "w") f.write("%d %s\n" % (state, coreapi.state_name(state))) f.close() except Exception as e: self.warn("Error writing state file: %s" % e) def runhook(self, state, hooks=None): ''' Run hook scripts upon changing states. If hooks is not specified, run all hooks in the given state. ''' if state not in self._hooks: return if hooks is None: hooks = self._hooks[state] for (filename, data) in hooks: try: f = open(os.path.join(self.sessiondir, filename), "w") f.write(data) f.close() except Exception as e: self.warn("Error writing hook '%s': %s" % (filename, e)) self.info("Running hook %s for state %s" % (filename, state)) try: check_call(["/bin/sh", filename], cwd=self.sessiondir, env=self.getenviron()) except Exception as e: self.warn("Error running hook '%s' for state %s: %s" % (filename, state, e)) def sethook(self, type, filename, srcname, data): ''' Store a hook from a received File Message. ''' if srcname is not None: raise NotImplementedError (hookid, state) = type.split(':')[:2] if not state.isdigit(): self.warn("Error setting hook having state '%s'" % state) return state = int(state) hook = (filename, data) if state not in self._hooks: self._hooks[state] = [hook,] else: self._hooks[state] += hook # immediately run a hook if it is in the current state # (this allows hooks in the definition and configuration states) if self.getstate() == state: self.runhook(state, hooks = [hook,]) def delhooks(self): ''' Clear the hook scripts dict. ''' self._hooks = {} def getenviron(self, state=True): ''' Get an environment suitable for a subprocess.Popen call. This is the current process environment with some session-specific variables. ''' env = os.environ.copy() env['SESSION'] = "%s" % self.sessionid env['SESSION_DIR'] = "%s" % self.sessiondir env['SESSION_NAME'] = "%s" % self.name env['SESSION_FILENAME'] = "%s" % self.filename env['SESSION_USER'] = "******" % self.user env['SESSION_NODE_COUNT'] = "%s" % self.node_count if state: env['SESSION_STATE'] = "%s" % self.getstate() try: readfileintodict(os.path.join(CORE_CONF_DIR, "environment"), env) except IOError: pass if self.user: try: readfileintodict(os.path.join('/home', self.user, ".core", "environment"), env) except IOError: pass return env def setthumbnail(self, thumbfile): ''' Set the thumbnail filename. Move files from /tmp to session dir. ''' if not os.path.exists(thumbfile): self.thumbnail = None return dstfile = os.path.join(self.sessiondir, os.path.basename(thumbfile)) shutil.move(thumbfile, dstfile) #print "thumbnail: %s -> %s" % (thumbfile, dstfile) self.thumbnail = dstfile def setuser(self, user): ''' Set the username for this session. Update the permissions of the session dir to allow the user write access. ''' if user is not None: try: uid = pwd.getpwnam(user).pw_uid gid = os.stat(self.sessiondir).st_gid os.chown(self.sessiondir, uid, gid) except Exception as e: self.warn("Failed to set permission on %s: %s" % (self.sessiondir, e)) self.user = user def objs(self): ''' Return iterator over the emulation object dictionary. ''' return iter(list(self._objs.values())) def getobjid(self): ''' Return a unique, random object id. ''' self._objslock.acquire() while True: id = random.randint(1, 0xFFFF) if id not in self._objs: break self._objslock.release() return id def addobj(self, cls, *clsargs, **clskwds): ''' Add an emulation object. ''' obj = cls(self, *clsargs, **clskwds) self._objslock.acquire() if obj.objid in self._objs: self._objslock.release() obj.shutdown() raise KeyError("non-unique object id %s for %s" % (obj.objid, obj)) self._objs[obj.objid] = obj self._objslock.release() return obj def obj(self, objid): ''' Get an emulation object. ''' if objid not in self._objs: raise KeyError("unknown object id %s" % (objid)) return self._objs[objid] def objbyname(self, name): ''' Get an emulation object using its name attribute. ''' with self._objslock: for obj in self.objs(): if hasattr(obj, "name") and obj.name == name: return obj raise KeyError("unknown object with name %s" % (name)) def delobj(self, objid): ''' Remove an emulation object. ''' self._objslock.acquire() try: o = self._objs.pop(objid) except KeyError: o = None self._objslock.release() if o: o.shutdown() del o gc.collect() # print "gc count:", gc.get_count() # for o in gc.get_objects(): # if isinstance(o, PyCoreObj): # print "XXX XXX XXX PyCoreObj:", o # for r in gc.get_referrers(o): # print "XXX XXX XXX referrer:", gc.get_referrers(o) def delobjs(self): ''' Clear the _objs dictionary, and call each obj.shutdown() routine. ''' self._objslock.acquire() while self._objs: k, o = self._objs.popitem() o.shutdown() self._objslock.release() def writeobjs(self): ''' Write objects to a 'nodes' file in the session dir. The 'nodes' file lists: number, name, api-type, class-type ''' try: f = open(os.path.join(self.sessiondir, "nodes"), "w") with self._objslock: for objid in sorted(self._objs.keys()): o = self._objs[objid] f.write("%s %s %s %s\n" % (objid, o.name, o.apitype, type(o))) f.close() except Exception as e: self.warn("Error writing nodes file: %s" % e) def addconfobj(self, objname, type, callback): ''' Objects can register configuration objects that are included in the Register Message and may be configured via the Configure Message. The callback is invoked when receiving a Configure Message. ''' if type not in coreapi.reg_tlvs: raise Exception("invalid configuration object type") self._confobjslock.acquire() self._confobjs[objname] = (type, callback) self._confobjslock.release() def confobj(self, objname, session, msg): ''' Invoke the callback for an object upon receipt of a Configure Message for that object. A no-op if the object doesn't exist. ''' replies = [] self._confobjslock.acquire() if objname == "all": for objname in self._confobjs: (type, callback) = self._confobjs[objname] reply = callback(session, msg) if reply is not None: replies.append(reply) self._confobjslock.release() return replies if objname in self._confobjs: (type, callback) = self._confobjs[objname] self._confobjslock.release() reply = callback(session, msg) if reply is not None: replies.append(reply) return replies else: self.info("session object doesn't own model '%s', ignoring" % \ objname) self._confobjslock.release() return replies def confobjs_to_tlvs(self): ''' Turn the configuration objects into a list of Register Message TLVs. ''' tlvdata = b"" self._confobjslock.acquire() for objname in self._confobjs: (type, callback) = self._confobjs[objname] # type must be in coreapi.reg_tlvs tlvdata += coreapi.CoreRegTlv.pack(type, objname) self._confobjslock.release() return tlvdata def info(self, msg): ''' Utility method for writing output to stdout. ''' if hasattr(self.options, 'clientlogfile'): fname = self.options.clientlogfile with open(fname, 'a') as logfile: print(msg, file = logfile, flush = True) else: print(msg, file = sys.stdout, flush = True) sys.stdout.flush() def warn(self, msg): ''' Utility method for writing output to stderr. ''' if hasattr(self.options, 'clientlogfile'): fname = self.options.clientlogfile with open(fname, 'a') as logfile: print(msg, file = logfile, flush = True) else: print(msg, file = sys.stderr, flush = True) sys.stderr.flush() def dumpsession(self): ''' Debug print this session. ''' self.info("session id=%s name=%s state=%s connected=%s" % \ (self.sessionid, self.name, self._state, self.isconnected())) num = len(self._objs) self.info(" file=%s thumb=%s nc=%s/%s" % \ (self.filename, self.thumbnail, self.node_count, num)) def exception(self, level, source, objid, text): ''' Generate an Exception Message ''' vals = (objid, str(self.sessionid), level, source, time.ctime(), text) types = ("NODE", "SESSION", "LEVEL", "SOURCE", "DATE", "TEXT") tlvdata = b'' for (t,v) in zip(types, vals): if v is not None: tlvdata += coreapi.CoreExceptionTlv.pack( eval("coreapi.CORE_TLV_EXCP_%s" % t), v) msg = coreapi.CoreExceptionMessage.pack(0, tlvdata) self.warn("exception: %s (%s) %s" % (source, objid, text)) # send Exception Message to connected handlers (e.g. GUI) self.broadcastraw(None, msg) def getcfgitem(self, cfgname): ''' Return an entry from the configuration dictionary that comes from command-line arguments and/or the core.conf config file. ''' if cfgname not in self.cfg: return None else: return self.cfg[cfgname] def getcfgitembool(self, cfgname, defaultifnone = None): ''' Return a boolean entry from the configuration dictionary, may return None if undefined. ''' item = self.getcfgitem(cfgname) if item is None: return defaultifnone return bool(item.lower() == "true") def getcfgitemint(self, cfgname, defaultifnone = None): ''' Return an integer entry from the configuration dictionary, may return None if undefined. ''' item = self.getcfgitem(cfgname) if item is None: return defaultifnone return int(item) def instantiate(self, handler=None): ''' We have entered the instantiation state, invoke startup methods of various managers and boot the nodes. Validate nodes and check for transition to the runtime state. ''' self.writeobjs() # controlnet may be needed by some EMANE models self.addremovectrlif(node=None, remove=False) if self.emane.startup() == self.emane.NOT_READY: return # instantiate() will be invoked again upon Emane.configure() self.broker.startup() self.mobility.startup() # boot the services on each node self.bootnodes(handler) # allow time for processes to start time.sleep(0.125) self.validatenodes() self.emane.poststartup() # assume either all nodes have booted already, or there are some # nodes on slave servers that will be booted and those servers will # send a node status response message self.checkruntime() def getnodecount(self): ''' Returns the number of CoreNodes and CoreNets, except for those that are not considered in the GUI's node count. ''' with self._objslock: count = len([x for x in self.objs() if not isinstance(x, (nodes.PtpNet, nodes.CtrlNet))]) # on Linux, GreTapBridges are auto-created, not part of # GUI's node count if 'GreTapBridge' in globals(): count -= len([x for x in self.objs() if isinstance(x, GreTapBridge) and not \ isinstance(x, nodes.TunnelNode)]) return count def checkruntime(self): ''' Check if we have entered the runtime state, that all nodes have been started and the emulation is running. Start the event loop once we have entered runtime (time=0). ''' # this is called from instantiate() after receiving an event message # for the instantiation state, and from the broker when distributed # nodes have been started if self.node_count is None: return if self.getstate() == coreapi.CORE_EVENT_RUNTIME_STATE: return session_node_count = int(self.node_count) nc = self.getnodecount() # count booted nodes not emulated on this server # TODO: let slave server determine RUNTIME and wait for Event Message # broker.getbootocunt() counts all CoreNodes from status reponse # messages, plus any remote WLANs; remote EMANE, hub, switch, etc. # are already counted in self._objs nc += self.broker.getbootcount() self.info("Checking for runtime with %d of %d session nodes" % \ (nc, session_node_count)) if nc < session_node_count: return # do not have information on all nodes yet # information on all nodes has been received and they have been started # enter the runtime state # TODO: more sophisticated checks to verify that all nodes and networks # are running state = coreapi.CORE_EVENT_RUNTIME_STATE self.evq.run() self.setstate(state, info=True, sendevent=True) def datacollect(self): ''' Tear down a running session. Stop the event loop and any running nodes, and perform clean-up. ''' self.evq.stop() with self._objslock: for obj in self.objs(): if isinstance(obj, nodes.PyCoreNode): self.services.stopnodeservices(obj) self.emane.shutdown() self.updatectrlifhosts(remove=True) self.addremovectrlif(node=None, remove=True) # self.checkshutdown() is currently invoked from node delete handler def checkshutdown(self): ''' Check if we have entered the shutdown state, when no running nodes and links remain. ''' with self._objslock: nc = len(self._objs) # TODO: this doesn't consider slave server node counts # wait for slave servers to enter SHUTDOWN state, then master session # can enter SHUTDOWN replies = () if nc == 0: replies = self.setstate(state=coreapi.CORE_EVENT_SHUTDOWN_STATE, info=True, sendevent=True, returnevent=True) self.sdt.shutdown() return replies def setmaster(self, handler): ''' Look for the specified handler and set our master flag appropriately. Returns True if we are connected to the given handler. ''' with self._handlerslock: for h in self._handlers: if h != handler: continue self.master = h.master return True return False def shortsessionid(self): ''' Return a shorter version of the session ID, appropriate for interface names, where length may be limited. ''' return (self.sessionid >> 8) ^ (self.sessionid & ((1 << 8) - 1)) def bootnodes(self, handler): ''' Invoke the boot() procedure for all nodes and send back node messages to the GUI for node messages that had the status request flag. ''' #self.addremovectrlif(node=None, remove=False) with self._objslock: for n in self.objs(): if not isinstance(n, nodes.PyCoreNode): continue if isinstance(n, nodes.RJ45Node): continue # add a control interface if configured self.addremovectrlif(node=n, remove=False) n.boot() nodenum = n.objid if handler is None: continue if nodenum in handler.nodestatusreq: tlvdata = b"" tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_NUMBER, nodenum) tlvdata += coreapi.CoreNodeTlv.pack(coreapi.CORE_TLV_NODE_EMUID, n.objid) reply = coreapi.CoreNodeMessage.pack(coreapi.CORE_API_ADD_FLAG \ | coreapi.CORE_API_LOC_FLAG, tlvdata) try: handler.request.sendall(reply) except Exception as e: self.warn("sendall() error: %s" % e) del handler.nodestatusreq[nodenum] self.updatectrlifhosts() def validatenodes(self): with self._objslock: for n in self.objs(): # TODO: this can be extended to validate everything # such as vnoded process, bridges, etc. if not isinstance(n, nodes.PyCoreNode): continue if isinstance(n, nodes.RJ45Node): continue n.validate() def addremovectrlnet(self, remove=False): ''' Create a control network bridge as necessary. When the remove flag is True, remove the bridge that connects control interfaces. ''' prefix = None try: if self.cfg['controlnet']: prefix = self.cfg['controlnet'] except KeyError: pass if hasattr(self.options, 'controlnet'): prefix = self.options.controlnet if not prefix: return None # no controlnet needed # return any existing controlnet bridge id = "ctrlnet" try: ctrlnet = self.obj(id) if remove: self.delobj(ctrlnet.objid) return None return ctrlnet except KeyError: if remove: return None # build a new controlnet bridge updown_script = None try: if self.cfg['controlnet_updown_script']: updown_script = self.cfg['controlnet_updown_script'] except KeyError: pass prefixes = prefix.split() if len(prefixes) > 1: assign_address = True if self.master: try: prefix = prefixes[0].split(':', 1)[1] except IndexError: prefix = prefixes[0] # possibly only one server else: # slave servers have their name and localhost in the serverlist servers = self.broker.getserverlist() servers.remove('localhost') prefix = None for server_prefix in prefixes: server, p = server_prefix.split(':') if server == servers[0]: prefix = p break if not prefix: msg = "Control network prefix not found for server '%s'" % \ servers[0] self.exception(coreapi.CORE_EXCP_LEVEL_ERROR, "Session.addremovectrlnet()", None, msg) prefix = prefixes[0].split(':', 1)[1] assign_address = False else: # with one prefix, only master gets a ctrlnet address assign_address = self.master ctrlnet = self.addobj(cls=nodes.CtrlNet, objid=id, prefix=prefix, assign_address=assign_address, updown_script=updown_script) # tunnels between controlnets will be built with Broker.addnettunnels() self.broker.addnet(id) for server in self.broker.getserverlist(): self.broker.addnodemap(server, id) return ctrlnet def addremovectrlif(self, node, remove=False): ''' Add a control interface to a node when a 'controlnet' prefix is listed in the config file or session options. Uses addremovectrlnet() to build or remove the control bridge. ''' ctrlnet = self.addremovectrlnet(remove) if ctrlnet is None: return if node is None: return ctrlip = node.objid try: addrlist = ["%s/%s" % (ctrlnet.prefix.addr(ctrlip), ctrlnet.prefix.prefixlen)] except ValueError: msg = "Control interface not added to node %s. " % node.objid msg += "Invalid control network prefix (%s). " % ctrlnet.prefix msg += "A longer prefix length may be required for this many nodes." node.exception(coreapi.CORE_EXCP_LEVEL_ERROR, "Session.addremovectrlif()", msg) return ifi = node.newnetif(net = ctrlnet, ifindex = ctrlnet.CTRLIF_IDX_BASE, ifname = "ctrl0", hwaddr = MacAddr.random(), addrlist = addrlist) node.netif(ifi).control = True def updatectrlifhosts(self, remove=False): ''' Add the IP addresses of control interfaces to the /etc/hosts file. ''' if not self.getcfgitembool('update_etc_hosts', False): return id = "ctrlnet" try: ctrlnet = self.obj(id) except KeyError: return header = "CORE session %s host entries" % self.sessionid if remove: if self.getcfgitembool('verbose', False): self.info("Removing /etc/hosts file entries.") filedemunge('/etc/hosts', header) return entries = [] for ifc in ctrlnet.netifs(): name = ifc.node.name for addr in ifc.addrlist: entries.append("%s %s" % (addr.split('/')[0], ifc.node.name)) if self.getcfgitembool('verbose', False): self.info("Adding %d /etc/hosts file entries." % len(entries)) filemunge('/etc/hosts', header, '\n'.join(entries) + '\n') def runtime(self): ''' Return the current time we have been in the runtime state, or zero if not in runtime. ''' if self.getstate() == coreapi.CORE_EVENT_RUNTIME_STATE: return time.time() - self._time else: return 0.0 def addevent(self, etime, node=None, name=None, data=None): ''' Add an event to the event queue, with a start time relative to the start of the runtime state. ''' etime = float(etime) runtime = self.runtime() if runtime > 0.0: if time <= runtime: self.warn("Could not schedule past event for time %s " \ "(run time is now %s)" % (time, runtime)) return etime = etime - runtime func = self.runevent self.evq.add_event(etime, func, node=node, name=name, data=data) if name is None: name = "" self.info("scheduled event %s at time %s data=%s" % \ (name, etime + runtime, data)) def runevent(self, node=None, name=None, data=None): ''' Run a scheduled event, executing commands in the data string. ''' now = self.runtime() if name is None: name = "" self.info("running event %s at time %s cmd=%s" % (name, now, data)) if node is None: mutedetach(shlex.split(data)) else: n = self.obj(node) n.cmd(shlex.split(data), wait=False) def sendobjs(self): ''' Return API messages that describe the current session. ''' replies = [] nn = 0 ni = 0 # send NetIDSubnetMap msgs = self.netidmanager.toconfmsgs(flags=0, nodenum=-1, typeflags=coreapi.CONF_TYPE_FLAGS_UPDATE) replies.extend(msgs) # send node messages for node and network objects with self._objslock: for obj in self.objs(): created_nodemsg = False msg = obj.tonodemsg(flags = coreapi.CORE_API_ADD_FLAG) if msg is not None: created_nodemsg = True replies.append(msg) nn += 1 # send interface messages from interface objects # if obj has tonodemsg(), the it's a node and thus contains # interfaces. we will now iterate over those interface and push # one API message each. if created_nodemsg: for ifindex, interface in list(obj._netif.items()): msg = interface.tointerfacemsg(flags = coreapi.CORE_API_ADD_FLAG) if msg is not None: replies.append(msg) ni += 1 nl = 0 # send link messages from net objects with self._objslock: for obj in self.objs(): linkmsgs = obj.tolinkmsgs(flags = coreapi.CORE_API_ADD_FLAG) for msg in linkmsgs: replies.append(msg) nl += 1 # send model info configs = self.mobility.getallconfigs() configs += self.emane.getallconfigs() for (nodenum, cls, values) in configs: #cls = self.mobility._modelclsmap[conftype] msg = cls.toconfmsg(flags=0, nodenum=nodenum, typeflags=coreapi.CONF_TYPE_FLAGS_UPDATE, values=values) replies.append(msg) # service customizations svc_configs = self.services.getallconfigs() for (nodenum, svc) in svc_configs: opaque = "service:%s" % svc._name tlvdata = b"" tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_NODE, nodenum) tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OPAQUE, opaque) tmp = coreapi.CoreConfMessage(flags=0, hdr=b"", data=tlvdata) replies.append(self.services.configure_request(tmp)) for (filename, data) in self.services.getallfiles(svc): flags = coreapi.CORE_API_ADD_FLAG tlvdata = coreapi.CoreFileTlv.pack(coreapi.CORE_TLV_FILE_NODE, nodenum) tlvdata += coreapi.CoreFileTlv.pack(coreapi.CORE_TLV_FILE_NAME, str(filename)) tlvdata += coreapi.CoreFileTlv.pack(coreapi.CORE_TLV_FILE_TYPE, opaque) tlvdata += coreapi.CoreFileTlv.pack(coreapi.CORE_TLV_FILE_DATA, str(data)) replies.append(coreapi.CoreFileMessage.pack(flags, tlvdata)) # TODO: send location info # replies.append(self.location.toconfmsg()) # send hook scripts for state in sorted(self._hooks.keys()): for (filename, data) in self._hooks[state]: flags = coreapi.CORE_API_ADD_FLAG tlvdata = coreapi.CoreFileTlv.pack(coreapi.CORE_TLV_FILE_NAME, str(filename)) tlvdata += coreapi.CoreFileTlv.pack(coreapi.CORE_TLV_FILE_TYPE, "hook:%s" % state) tlvdata += coreapi.CoreFileTlv.pack(coreapi.CORE_TLV_FILE_DATA, str(data)) replies.append(coreapi.CoreFileMessage.pack(flags, tlvdata)) # send meta data tmp = coreapi.CoreConfMessage(flags=0, hdr=b"", data=b"") opts = self.options.configure_request(tmp, typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE) if opts: replies.append(opts) meta = self.metadata.configure_request(tmp, typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE) if meta: replies.append(meta) self.info("informing GUI about %d nodes, %d interfaces and %d links" % (nn, ni, nl)) return replies