def xmlrpc___setitem__(self, *a, **k): return sanitize(self.xmlrpc_set)(*a, **k)
def xmlrpc_getFlags(self, *a, **k): return sanitize(self.getFlags)(*a, **k)
def xmlrpc_getAttributes(self, *a, **k): return sanitize(self.desc.getAttributes)(*a, **k)
def xmlrpc_h_get_time(self, *a, **k): return sanitize(self.desc.h_get_time)(*a, **k)
def xmlrpc_getkid(self, *a, **k): return sanitize(self.desc.getkid)(*a, **k)
class ConfigurationInterface(xmlrpc.XMLRPC, object): """Public interface to Conf objects (XMLRPC and get/set mechanics)""" conf_class = 'Conf' """Conf class towards which this class acts as a ConfigurationInterface.""" main_confdir = params.confdir """Base server configuration directory""" conf_def = [ { "handle": 'mro', "name": 'Class hierarchy', "type": 'List', "attr": ['ReadOnly', 'Hidden'] }, { "handle": 'log', "name": 'Log', "type": 'Log', "attr": ['History'] }, ] """Default configuration list""" server = None allow_none = True _Method__name = 'undefined' def __init__(self, desc): """Create an interface for configuration object `desc`. """ xmlrpc.XMLRPC.__init__(self, allowNone=True) object.__init__(self) self.separator = '/' self.devices = [] self.controls = {} self.desc = desc self.log = logger.SubLogger(self) def keys(self, *a, **k): return self.desc.keys(*a, **k) def get_preset(self, *a, **k): return self.desc.get_preset(*a, **k) def sete(self, *a, **k): return self.desc.sete(*a, **k) def hasattr(self, *a, **k): return self.desc.hasattr(*a, **k) def getattr(self, *a, **k): return self.desc.getattr(*a, **k) def setattr(self, *a, **k): return self.desc.setattr(*a, **k) def getkid(self, *a, **k): return self.desc.getkid(*a, **k) def gettype(self, *a, **k): return self.desc.gettype(*a, **k) def has_key(self, *a, **k): return self.desc.has_key(*a, **k) def listPresets(self, *a, **k): return self.desc.listPresets(*a, **k) def updateCurrent(self, *a, **k): return self.desc.updateCurrent(*a, **k) def update(self, *a, **k): return self.desc.update(*a, **k) def fp(self, *a, **k): return self.desc.fp(*a, **k) def iolist(self, *a, **k): return self.desc.iolist(*a, **k) def get_from_preset(self, *a, **k): return self.desc.get_from_preset(*a, **k) def compare_presets(self, *a, **k): return self.desc.compare_presets(*a, **k) def set_to_preset(self, *a, **k): return self.desc.set_to_preset(*a, **k) def xmlrpc_has_key(self, *a, **k): return self.desc.has_key(*a, **k) def xmlrpc_keys(self, *a, **k): return self.desc.keys(*a, **k) def xmlrpc_hasattr(self, *a, **k): return self.hasattr(*a, **k) def xmlrpc_getattr(self, *a, **k): return self.getattr(*a, **k) def xmlrpc_setattr(self, *a, **k): return self.setattr(*a, **k) def xmlrpc_getkid(self, *a, **k): return sanitize(self.desc.getkid)(*a, **k) def xmlrpc_applyDesc(self, *a, **k): return self.applyDesc(*a, **k) def xmlrpc_listPresets(self, *a, **k): return self.desc.listPresets(*a, **k) def save(self, *a, **k): return self.desc.save(*a, **k) def xmlrpc_save(self, *a, **k): return self.desc.save(*a, **k) def remove(self, *a, **k): return self.desc.remove(*a, **k) def xmlrpc_remove(self, *a, **k): return self.desc.remove # config. file(*a, **k) def rename(self, *a, **k): return self.desc.rename(*a, **k) def xmlrpc_rename(self, *a, **k): return self.desc.rename(*a, **k) def delete(self, *a, **k): return self.desc.delete(*a, **k) def xmlrpc_delete(self, *a, **k): return self.desc.delete # key(*a, **k) def xmlrpc_h_get(self, *a, **k): return sanitize(self.desc.h_get)(*a, **k) def xmlrpc_h_get_time(self, *a, **k): return sanitize(self.desc.h_get_time)(*a, **k) def xmlrpc_getAttributes(self, *a, **k): return sanitize(self.desc.getAttributes)(*a, **k) def xmlrpc_iolist(self, *a, **k): return self.iolist(*a, **k) def xmlrpc_get_from_preset(self, *a, **k): return self.get_from_preset(*a, **k) def xmlrpc_compare_presets(self, *a, **k): return self.compare_presets(*a, **k) def xmlrpc_set_to_preset(self, *a, **k): return self.set_to_preset(*a, **k) def h_get(self, *a, **k): return self.desc.h_get(*a, **k) def h_get_history(self, *a, **k): return self.desc.h_get_history(*a, **k) def h_get_time(self, *a, **k): return self.desc.h_get_time(*a, **k) def h_clear(self, *a, **k): return self.desc.h_clear(*a, **k) def h_time_at(self, *a, **k): return self.desc.h_time_at(*a, **k) def xmlrpc_h_time_at(self, *a, **k): return self.h_time_at(*a, **k) def xmlrpc___getitem__(self, *a, **k): return sanitize(self.xmlrpc_get)(*a, **k) def xmlrpc___setitem__(self, *a, **k): return sanitize(self.xmlrpc_set)(*a, **k) def xmlrpc_setFlags(self, *a, **k): return sanitize(self.setFlags)(*a, **k) def xmlrpc_getFlags(self, *a, **k): return sanitize(self.getFlags)(*a, **k) def xmlrpc___contains__(self, *a, **k): return self.desc.has_key(*a, **k) def xmlrpc___hash__(self, *a, **k): return self.__hash__(*a, **k) def xmlrpc___eq__(self, *a, **k): return self.__eq__(*a, **k) def xmlrpc___repr__(self, *a, **k): return self.desc.__repr__(*a, **k) def xmlrpc___str__(self, *a, **k): return self.__str__(*a, **k) # Must explicitly define these functions. # Cannot assign them to self during __init__ def __getitem__(self, *args, **kwargs): return self.get(*args, **kwargs) def __setitem__(self, *args, **kwargs): return self.set(*args, **kwargs) def __contains__(self, *args, **kwargs): return self.desc.has_key(*args, **kwargs) def getcontrols(self): """Returns currently defined controls""" return self.controls.keys() xmlrpc_getcontrols = getcontrols def close(self): if self.desc is False: return False print 'ConfigurationInterface.close', type(self), id(self) self.desc.close() self.desc = False return True @property def class_name(self): return self.__class__.__name__ def classname(self): return self.class_name xmlrpc_classname = classname def mro(self): mro = inspect.getmro(self.__class__) r = [] for cl in mro: r.append(cl.__name__) return r[:-3] xmlrpc_mro = mro def describe(self, *a, **kw): self['mro'] = self.mro() return self.desc.describe(*a, **kw) def get_mro(self): return self.mro() def iteritems(self): pass def iterkeys(self): pass def __iter__(self): pass def __hash__(self): """L'hash viene calcolato sulla base dell'oggetto self.log onde evitare ricorsioni infinite.""" return hash(self.log) def __eq__(self, other): if not getattr(other, '_Method__name', False): return False if self._Method__name != other._Method__name: return False return True def __str__(self): if self.desc is False: return 'Closed {}: {}, {}, {}'.format(self.__class__.__name__, repr(self), type(self), id(self)) r = self.__class__.__name__ + ' for ' + self.desc.__str__() r += '\nconf_dir: %s \nconf_obj: %s' % (self.conf_dir, self.conf_obj) return r def xmlrpc_describe(self, readLevel=0): """Sanitize description dictionary and filter depending on user's readLevel.""" r = self.desc.describe() for key, val in r.items(): if val['readLevel'] > readLevel: del r[key] continue val['current'] = xmlrpcSanitize(val['current'], attr=val['attr'], otype=val['type']) # if 'Binary' in val['attr'] or val['type']=='Profile': # val['current']=xmlrpc.Binary(dumps(val['current'])) r[key] = val l = [v.keys() for v in r.values()] print set([type(item) for sublist in l for item in sublist]) return r _rmodel = False def rmodel(self): """Dictionary model recursively listing all subdevices' paths. {'self':name, 'sub1':{'self':name, 'sub1sub1':{'self':name,...}, 'sub2':{'self':name} ...} """ if self._rmodel is not False: return self._rmodel out = {'self': self['name']} for name, path in self.list(): d = self.getSubHandler(path) if d is self: print 'Skipping myself', name continue if d is None: print 'skipping NONE', name, path continue out[path] = d.rmodel() return out xmlrpc_rmodel = rmodel @classmethod def _pget(cls, key, self): """Helper function for getting a class-defined property""" # print 'PGET', type(self), key # this works! return self.get(key) @classmethod def _pset(cls, key, self, val): """Helper function for setting a class-defined property""" # FIXME: pset does not work! # print 'PSET', type(self), key, val return self.set(key, val) @classmethod def setProperties(cls, *keys): """Contructs class properties corresponding to Conf options.""" for key in keys: if hasattr(cls, key): v = getattr(cls, key) print 'Property {} is overwriting previous attribute {}'.format( key, repr(v)) del v pget = functools.partial(cls._pget, key) pset = functools.partial(cls._pset, key) p = property(pget, pset) setattr(cls, key, p) def set_preset(self, *args, **kwargs): """Calls applyDesc after setting the preset""" r = self.desc.set_preset(*args, **kwargs) if r: self.applyDesc() return r def applyDesc(self, *a, **k): """To be reimplemented.""" return True def validate_preset_name(self, name): lst = self.listPresets() ret = select_preset_for_name(name, self.listPresets()) self.log.debug('validate_preset_name', name, ret, lst) return ret def setAttributes(self, name, attrlist, writeLevel=5): return self.desc.setAttributes(name, attrlist) xmlrpc_setAttributes = setAttributes def add_attr(self, opt, attr_name, writeLevel=5): return self.desc.add_attr(opt, attr_name) xmlrpc_add_attr = add_attr def del_attr(self, opt, attr_name, writeLevel=5): return self.desc.del_attr(opt, attr_name) xmplrpc_del_attr = del_attr def getFlags(self, opt): """Returns option flags for `opt`""" if self.controls.has_key(opt): out = {} pre = self.desc.getFlags(opt) for key, val in pre.iteritems(): r = self.controls[opt].getFlag(key) print 'control getflag', key, r if r: out[key] = val else: out[key] = pre[key] self.desc.setFlags(opt, out) return self.desc.getFlags(opt) def file_list(self, opt, ext=''): """Update a FileList options attribute""" odir = self.desc.getConf_dir() + opt + '/' r = listDirExt(odir, ext, create=True) self.setattr(opt, 'options', r) return r def get(self, name, *opt): """Get routing. First searches for a get_`name` method to call, then for a special control, finally directly retrieve the value from memory""" # Check get_name existance if not self.desc.has_key(name): if len(opt) > 0: return opt[0] self.log.warning('No property: ', name) raise NoProperty('NoProperty: ' + name) # Call getter functions func = getattr(self, 'get_' + name, False) val = None if func: val = func() self.desc.set(name, val) # Call special controls elif self.controls.has_key(name): val = self.controls[name].get() # print 'Getting control',name,val self.desc.set(name, val) # Get a SubDict if isinstance(val, dict): return SubDict(val, self, name) # If intercepted by any special function, return here # They should take care about managing special types (Dict, FileList, # RoleIO) if val is not None: return val prop = self.desc.gete(name) val = prop['current'] typ = prop.get('type', '') # Manage Dict-type options if isinstance(prop, bool) or isinstance(prop, str): self.log.error('Wrong type for handle', name, type(prop), repr(prop)) if typ == 'Meta': val = SubDict(val, self, name) # Update file listings on get() elif typ == 'FileList': self.file_list(name) # Resolve RoleIO elif typ == 'RoleIO': val = self.get_role_io(name, val) return val def get_role_io(self, name, val=None): """Retrieve `name` RoleIO option from the referred option value""" obj = self.roledev.get(name, False) # Try to remap if not obj: obj = self.map_role_dev(name) if obj: obj, pre, io = obj if io and obj: val = obj[io.handle] self.desc.set(name, val) else: val = self.desc.get(name) return val def gete(self, opt, *a, **k): r = self.desc.gete(opt, *a, **k) # Refresh file listing if r.get('type', '') == 'FileList': self.file_list(opt) r = self.desc.gete(opt, *a, **k) return r @property def root_obj(self): """Dummy root obj""" return self def xmlrpc_get(self, name, readLevel=0): """Client frontend for the `get()` method. Security check with `readLevel`. Only read values from memory if no acquisition isRunning. Pickle or otherwise xmlrpc-sanitize values for network transmission""" r = self.desc.getattr(name, 'readLevel') if readLevel < r: self.log.critical('Not authorized get', name) raise ReadNotAuthorized('Option: %s Required: %i Level: %i' % (name, r, readLevel)) p = self.desc.gete(name) if 'Hot' in p['attr']: if self.root_obj.get('isRunning'): r = self.desc.get(name) else: r = self.get(name) else: r = self.get(name) if p['type'] in ['Binary', 'Profile', 'Image']: return xmlrpc.Binary(dumps(r)) if p['type'] == 'Meta': return r.copy() return csutil.xmlrpcSanitize(r) def multiget(self, opts, readLevel=0): """Performs get operation on a list of options, returning a {opt:val} mapping""" r = {} for opt in opts: r[opt] = self.xmlrpc_get(opt, readLevel=readLevel) return r xmlrpc_multiget = sanitize(multiget) @sanitize def xmlrpc_geth(self, name, readLevel=0): dn = self.desc.gete(name) rl = dn['readLevel'] if rl > readLevel: self.log.critical('Not authorized geth', name) raise ReadNotAuthorized('Option: %s, Required: %i, Level: %i' % (name, rl, readLevel)) attr = dn.get('attr', []) if 'History' in attr and getattr(self.desc, 'history', False): return self.desc.h_get_history(name) return 'No history for property: ' + name @sanitize def xmlrpc_set(self, name, val, kwopt={'writeLevel': 5}): writeLevel = kwopt['writeLevel'] w = self.desc.getattr(name, 'writeLevel') if writeLevel < w: self.log.critical('Not authorized set', name) raise WriteNotAuthorized('Option: %s, Required: %i, Level: %i' % (name, w, writeLevel)) return self.set(name, val, kwopt=kwopt) def setFlags(self, opt, flags): """Set flags for option `opt`""" if self.controls.has_key(opt): out = {} pre = self.desc.getFlags(opt) for key, val in flags.iteritems(): r = self.controls[opt].setFlag(key, val) if r: out[key] = val else: out[key] = pre[key] flags = out return self.desc.setFlags(opt, flags) def map_role_dev(self, new=None): return True # FIXME: SET SHOULD RETURN NVAL, not (name,nval)!!! # Change client-side where needed def set(self, name, val, t=-1, kwopt={}): """Set routing. First searches for a set_`name` method to call. Then searches for a control `name`. Finally directly set the value on memory (self.desc). Returns the actual value set.""" if not self.desc: print 'No desc interface object', self, self.desc return None if self.desc.getEmpty() and not self.desc.has_key(name): self.desc.set(name, val, t) return name, val dn = self.desc.gete(name) oval = self.desc.get(name) typ = dn['type'] # Dict-like management if typ == 'Meta': # Obtain a pure-dict object which must be picklable val = val.copy() # Role management elif typ == 'Role': if isinstance(val, str): val = val.split(',') if len(val) == 1: val.append('default') if not (isinstance(val, list) or isinstance(val, tuple)): # Convert object to role list: [fullpath,preset] val = [val['fullpath'], val['preset']] # Resolve RoleIO # FIXME: security breach: could write to a protected opt as access # levels are not checked here elif typ == 'RoleIO': obj = self.roledev.get(name, False) # Try to remap if not obj: obj = self.map_role_dev(name) if obj: obj, pre, io = obj if io: obj[io.handle] = val ### HOOKS ### # Setter hook # Search for a setter function and call it func = getattr(self, 'set_' + name, False) if func and type(func) != types.MethodType: func = False # Controls hook # Search if a control object has been defined in the self.controls # dict. if (not func) and self.controls.has_key(name): func = self.controls[name].set # If a valid function was found, fill implicit arguments if present if func: r = fill_implicit_args(func, (val, ), kwopt) if not r: raise FunctionRoutingError() # Pass also filled kwargs to func val = func(val, **r[-1]) ############ if val == None: self.log.debug('Failed setting', name) return name, oval # SET IN MEMORY self.desc.set(name, val, t) # At the end, intercept Role mapping requests if typ == 'Role': r = self.map_role_dev(name, val) # Restoring old value if r is False: self.desc.set(name, oval) return name, val @sanitize def xmlrpc_gete(self, name, readLevel=0): r = self.desc.gete(name).entry.copy() if readLevel < r['readLevel']: self.log.critical('Not authorized gete', name) raise ReadNotAuthorized('Option: %s Required: %i Level: %i' % (name, r['readLevel'], readLevel)) r['current'] = self.xmlrpc_get(name, readLevel=readLevel) return r def xmlrpc_sete(self, name, opt, writeLevel=0): if writeLevel < 4: self.log.critical('Not authorized sete', name) raise WriteNotAuthorized('Option: %s Required: %i Level: %i' % (name, 4, writeLevel)) return self.sete(name, opt) def setConf_dir(self, cd): """Set the folder where the configuration should be saved.""" self.desc.setConf_dir(cd) def getConf_dir(self): """Return the folder where the configuration should be saved.""" return self.desc.getConf_dir() conf_dir = property(getConf_dir, setConf_dir) def setConf_obj(self, obj): """Set the output full path for current configuration.""" self.desc.setConf_obj(obj) def getConf_obj(self): """Return the output full path for current configuration.""" return self.desc.getConf_obj() conf_obj = property(getConf_obj, setConf_obj) @sanitize def echo(self, s='none', readLevel=0, writeLevel=0, userName=''): """Login demo function.""" l = ['guest', 'analyst', 'user', 'tech', 'maint', 'admin'] r = 'Welcome {}. \nYour role is: read={},{} / write={},{}.\nHere is your echo: {}'.format( userName, readLevel, l[readLevel], writeLevel, l[writeLevel], s) return r xmlrpc_echo = echo def check_read(self, opt, readLevel=0): """Check if option `opt` can be red by current user""" return self.getattr(opt, 'readLevel') <= readLevel xmlrpc_check_read = check_read def check_write(self, opt, writeLevel=0): """Check if option `opt` can be written by current user""" return self.getattr(opt, 'writeLevel') <= writeLevel xmlrpc_check_write = check_write
class BaseServer(device.Device): """Basic server object functions. Useful for testing purposes""" allowNone = True naturalName = 'server' restart = False scanningPorts = 0 _Method__name = 'MAINSERVER' name = 'server' conf_def = deepcopy(device.Device.conf_def) conf_def += server_conf.conf def __str__(self): return 'BASESERVER ' + repr(self) def __init__(self, manager=share.manager, confdir=params.confdir, datadir=params.datadir): self.manager = manager self._server = self self._root = self device.Device.__init__(self, parent=None, node='MAINSERVER') self.separator = '/' self.storage, self.beholder, self.balance, self.smaug, self.morla = [ False ] * 5 self.hsm, self.horizontal, self.vertical, self.flex, self.post, self.drop, self.kiln = [ False ] * 7 self.instruments = [] self.deviceservers = [] self.main_confdir = confdir self.main_datadir = datadir def pause_check(self): self.shutting_down = True def get_instruments(self): """Returns the list of available Instrument names""" lst = [(obj['comment'] or obj['name'], obj.naturalName) for obj in self.instruments] self.desc.set('instruments', lst) return lst def get_deviceservers(self): """Returns the list of available DeviceServer names""" lst = [obj['name'] for obj in self.deviceservers] self.desc.set('deviceservers', lst) return lst @property def runningInstrument(self): ins = self['runningInstrument'] if ins in ['', 'None', None, False]: return False obj = self.child(ins) if obj is None: return False return obj @property def lastInstrument(self): """Configured instrument, ready for test start""" ins = self['lastInstrument'] if ins in ['', 'None', None, False]: return False obj = self.child(ins) if obj is None: return False return obj def get_initTest(self): """Retrieve initialization status from active instrument""" obj = self.runningInstrument if obj is False: return False return obj['initTest'] def get_closingTest(self): """Retrieve closing status from active instrument""" obj = self.runningInstrument if obj is False: return False return obj['closingTest'] def get_progress(self): """Remove finished Progress before returning the list of active tasks.""" p = list(set(self.desc.get('progress'))) # Clean finished progresses for e0 in p[:]: e = e0.split('/') # Option name opt = e.pop(-1) # Retrive the pointed object obj = self.child(e) if not obj: self.log.error('Operation object no longer exists!', e0) p.remove(e0) # Operation ended if not obj[opt]: self.log.debug('Operation ended', e0) p.remove(e0) # List contains still running options return p def set_progress(self, kid): """Append kid to the list instead of substituting""" p = self.desc.get('progress') p.append(kid) return p def check(self): """Check for delayed start""" ins = self.lastInstrument # there must be a defined running instrument test_in_progress = ins and (ins['running'] + ins['initTest'] + ins['closingTest'] + ins['initializing']) delay = self['delay'] delayT = self['delayT'] T = self.kiln['T'] last_client_access_time = self['lastClientAccessTime'] auto_shutdown_interval = self['autoShutdownInterval'] client_inactive = (self.time() - last_client_access_time > auto_shutdown_interval) \ and auto_shutdown_interval >= 300 \ and last_client_access_time > 0 \ and not test_in_progress \ and not self['delayStart'] \ and not self.time_delta if client_inactive: self.log.debug('Client inactive: halting server.') self.support.get_halt() if not ins: self['delayStart'] = False elif self['delayStart'] and delay > 0: d = delay - self.time() if -120 < d < 0 or (T < delayT and delayT > 0): if test_in_progress == 0: self.log.info('Delayed start:', self['lastInstrument'], self['delay'], self['delayT']) ins.start_acquisition(userName=ins.measure['operator']) self['delay'] = 0 self['delayT'] = -1 self['delayStart'] = False return True else: self.log.warning( 'Delayed start cannot be applied: {}, {}, {}, {}'. format(ins['running'], ins['initTest'], ins['closingTest'], ins['initializing'])) elif d > 0: self.log.debug( 'Waiting for delayed start of {}. Remaining {}min. Target T: {}, current: {}' .format(self['lastInstrument'], int(d / 60), delayT, T)) else: self.log.error('Delayed start timed out.', d) self['delay'] = 0 self['delayStart'] = False return device.Device.check(self) def set_delayStart(self, val, userName='******'): """Forbit if no instrument is configured or set operator name.""" if not val: return False ins = self.lastInstrument if not ins: self.log.error( 'Cannot set delayed start if no instrument is configured.') return False if ins is False: self.log.error('Unknown instrument for delayed start: {}'.format( repr(ins))) return False if self['isRunning'] + ins['running'] + ins['initTest'] + ins[ 'closingTest'] + ins['initializing'] != 0: self.log.error( 'Cannot set delayed start. Instrument is already running/closing.' ) return False if self['delay'] < self.time() + 10: self.log.error( 'Delayed start require `delay` option to be set in the future.' ) return False self.log.debug('set_delayStart', val, userName) ins.measure['operator'] = userName return True def stop_acquisition(self, save=True, writeLevel=1): ins = self['runningInstrument'] obj = getattr(self, ins, False) if obj is False: if ins != '': self.log.error( 'Cannot stop acquisition for running instrument:', ins) return False if not obj['analysis']: self.log.warning('Acquisition is not running for instrument', ins) return False return obj.stop_acquisition(save=True, writeLevel=1) xmlrpc_stop_acquisition = stop_acquisition def time(self): """Returns the server's time""" return csutil.time() xmlrpc_time = time def mapdate(self, kid_times, readLevel=0): """Receives a list of KID names and times to check for updates. Replies with two lists. The first (idx) is the list of updated positional indexes referring to the relative position of the option in kid_times. The second (rep) contains new current values. Is time is positive, the value is red from memory (.desc.get()). If negative, reading is forced through a standard get() call.""" idx = [] rep = [] for i, (k, t0) in enumerate(kid_times): obj, n = self.read_kid(k) if not obj: continue rl = obj.getattr(n, 'readLevel') if rl > readLevel: self.log.error('Permission denied for option:', n, rl, readLevel) continue # Detect forced updates if t0 < 0: rep.append(obj.get(n)) idx.append(i) continue nt = obj.h_time_at(n) if nt > t0: # Retrieve the memory value, # in order not to trigger on-get updates rep.append(obj.desc.get(n)) idx.append(i) return [idx, rep] xmlrpc_mapdate = csutil.sanitize(mapdate) def shutdown(self, *a, **k): try: self.close() share.stop() except: print_exc() from twisted.internet import reactor reactor.stop() def get_eq_mac(self): all_mac = go("ifconfig | grep 'HW' | awk '{ print $5}'") return all_mac