def __init__(self, machine, **kwargs): super(Host, self).__init__(machine) self.params = Parameters() self.params._from_dict(self._machine._pool_params) self.params._from_dict(self._machine._slave_desc) self._machine.set_initns(self)
def __init__(self, host, **kwargs): self._host = host self.params = Parameters() self.params._from_dict(self._host._slave_desc) self.devices = Devices(self._host) self._device_mapping = {}
def __init__(self, **kwargs): self.params = Parameters() for name, val in kwargs.items(): if name == "params": raise RequirementError("'params' is a reserved keyword.") p = Param() p.val = val setattr(self.params, name, p)
class Host(Namespace): """Namespace derived class for the init namespace Should not be created by the tester, instead it's automatically created by the LNST Controller before the 'test' method of a recipe is called. In addition to the base Namespace class it allows for assignment of a NetNamespace instance to create a new network namespace on the host.""" def __init__(self, machine, **kwargs): super(Host, self).__init__(machine) self.params = Parameters() self.params._from_dict(self._machine._pool_params) self.params._from_dict(self._machine._slave_desc) self._machine.set_initns(self) @property def namespaces(self): """List of network namespaces available on the machine Does not include the init namespace (self).""" ret = [] for x in list(self._objects.values()): if isinstance(x, NetNamespace): ret.append(x) return ret def map_device(self, dev_id, how): #TODO if this is supposed to be public it should be better than dict["hwaddr"]!!!! hwaddr = how["hwaddr"] dev = self._machine.get_dev_by_hwaddr(hwaddr) if dev: self._objects[dev_id] = dev dev._id = dev_id dev._enable() else: raise ControllerError( "Device with macaddr {} not found on {}.".format( hwaddr, self.hostid)) def _custom_setattr(self, name, value): if not super(Host, self)._custom_setattr(name, value): if isinstance(value, NetNamespace): self._machine.add_netns(value) self._objects[name] = value value._machine = self._machine return True else: return False else: return True def init_class(self, cls, *args, **kwargs): self._machine.send_class(cls) return self._machine.init_remote_class(cls, *args, **kwargs)
def __init__(self, **kwargs): """ Args: kwargs -- dictionary of arbitrary named arguments that correspond to class attributes (Param type). Values will be parsed and set to Param instances under the self.params object. """ self._orig_kwargs = kwargs.copy() #by defaults loads the params into self.params - no checks pseudocode: self.params = Parameters() for name in dir(self): param = getattr(self, name) if isinstance(param, Param): if name in kwargs: val = kwargs.pop(name) val = param.type_check(val) setattr(self.params, name, val) else: try: val = copy.deepcopy(param.default) setattr(self.params, name, val) except AttributeError: if param.mandatory: raise TestModuleError( "Parameter {} is mandatory".format(name)) if len(kwargs): for name in list(kwargs.keys()): raise TestModuleError("Unknown parameter {}".format(name)) self._res_data = None
def __init__(self, **kwargs): """ Args: kwargs -- dictionary of arbitrary named arguments that correspond to class attributes (Param type). Values will be parsed and set to Param instances under the self.params object. """ #by defaults loads the params into self.params - no checks pseudocode: self.params = Parameters() for x in dir(self): val = getattr(self, x) if isinstance(val, Param): setattr(self.params, x, copy.deepcopy(val)) for name, val in kwargs.items(): try: param = getattr(self.params, name) except: raise TestModuleError("Unknown parameter {}".format(name)) param.val = val for name, param in self.params: if param.mandatory and not param.set: raise TestModuleError("Parameter {} is mandatory".format(name)) self._res_data = None
class DeviceReq(object): """Specifies an Ethernet Device requirement To define a Device requirement you assign a DeviceReq instance to a HostReq instance in a BaseRecipe derived class. Example: class MyRecipe(BaseRecipe): m1 = HostReq() m1.eth0 = DeviceReq(label="net1") Args: label -- string value indicating the network the Device is connected to kwargs -- any other arguments will be treated as arbitrary string parameters that will be matched to parameters of Slave machines which can define their parameter values based on the implementation of the SlaveMachineParser """ def __init__(self, label, **kwargs): self.label = label self.params = Parameters() for name, val in kwargs.items(): if name == "params": raise RequirementError("'params' is a reserved keyword.") p = Param() p.val = val setattr(self.params, name, p) def _to_dict(self): res = {'network': self.label, 'params': self.params._to_dict()} return res
def __init__(self, **kwargs): """ The __init__ method does 2 things: * copies Requirements -- since Requirements are defined as class attributes, we need to copy the objects to avoid conflicts with multiple instances of the same class etc... The copied objects are stored under a Requirements object available through the 'req' attribute. This way you can optionally change the Requirements of an instantiated Recipe. * copies and instantiates Parameters -- Parameters are also class attributes so they need to be copied into a Parameters() object (accessible in the 'params' attribute). Next, the copied objects are loaded with values from kwargs and checked if mandatory Parameters have values. """ self.matched = None self.req = _Requirements() self.params = Parameters() for attr in dir(self): val = getattr(self, attr) if isinstance(val, HostReq): setattr(self.req, attr, copy.deepcopy(val)) elif isinstance(val, Param): setattr(self.params, attr, copy.deepcopy(val)) for name, val in kwargs.items(): try: param = getattr(self.params, name) param.val = val except: raise RecipeError("Unknown parameter {}".format(name)) for name, param in self.params: if param.mandatory and not param.set: raise RecipeError("Parameter {} is mandatory".format(name))
def __init__(self, **kwargs): """ The __init__ method does 2 things: * copies Requirements -- since Requirements are defined as class attributes, we need to copy the objects to avoid conflicts with multiple instances of the same class etc... The copied objects are stored under a Requirements object available through the 'req' attribute. This way you can optionally change the Requirements of an instantiated Recipe. * copies and instantiates Parameters -- Parameters are also class attributes so they need to be copied into a Parameters() object (accessible in the 'params' attribute). Next, the copied objects are loaded with values from kwargs and checked if mandatory Parameters have values. """ self._ctl = None self.runs = [] self.req = _Requirements() self.params = Parameters() attrs = {name: getattr(type(self), name) for name in dir(type(self))} params = ((name, val) for name, val in attrs.items() if isinstance(val, Param)) for name, val in params: if name in kwargs: param_val = kwargs.pop(name) param_val = val.type_check(param_val) setattr(self.params, name, param_val) else: try: param_val = copy.deepcopy(val.default) setattr(self.params, name, param_val) except AttributeError: if val.mandatory: raise RecipeError( "Parameter {} is mandatory".format(name)) reqs = ((name, val) for name, val in attrs.items() if isinstance(val, HostReq)) for name, val in reqs: new_val = copy.deepcopy(val) new_val.reinit_with_params(self.params) setattr(self.req, name, new_val) if len(kwargs): for key in list(kwargs.keys()): raise RecipeError("Unknown parameter {}".format(key))
class HostReq(object): """Specifies a Slave machine requirement To define a Host requirement you assign a HostReq instance to a class attribute of a BaseRecipe derived class. Example: class MyRecipe(BaseRecipe): m1 = HostReq() Args: kwargs -- any other arguments will be treated as arbitrary string parameters that will be matched to parameters of Slave machines which can define their parameter values based on the implementation of the SlaveMachineParser """ def __init__(self, **kwargs): self.params = Parameters() for name, val in kwargs.items(): if name == "params": raise RequirementError("'params' is a reserved keyword.") p = Param() p.val = val setattr(self.params, name, p) def __iter__(self): for x in dir(self): val = getattr(self, x) if isinstance(val, DeviceReq): yield (x, val) def _to_dict(self): res = {'interfaces': {}, 'params': {}} for dev_id, dev in self: res['interfaces'][dev_id] = dev._to_dict() res['params'] = self.params._to_dict() return res
class Host(object): """Tester facing slave Host API Objects of this class are created by the Controller and provided to the Recipe object to use from it's 'test()' method. This tester facing API allows the tester to create new Devices and run Jobs on the remote Host. Example: m1.bond0 = Bond() # to create a new bond device m1.run("ip a") # to run a shell command """ #TODO add packet capture options def __init__(self, host, **kwargs): self._host = host self.params = Parameters() self.params._from_dict(self._host._slave_desc) self.devices = Devices(self._host) self._device_mapping = {} def __getattr__(self, name): """direct access to Device objects All mapped devices of a Host are directly accessible as attributes of the Host objects. This is implemented by this __getattr__ override""" try: return self._device_mapping[name] except: raise AttributeError("%s object has no attribute named %r" % (self.__class__.__name__, name)) def __setattr__(self, name, value): """allows for dynamic creation of devices During execution of the recipes 'test' method, a tester can create new soft devices by assigning a Device object to the Host object instance, this is implemented by overriding this __setattr__ method. It also handles VirtualDevice creation before recipe execution (virtual match). """ if isinstance(value, VirtualDevice): # TODO creation of VirtualDevices should be disabled during test # execution, it's commented out right now because I haven't found # a good solution yet... # msg = "Creating VirtualDevices in recipe execution is "\ # "not supported right now." # raise HostError(msg) if name in self._device_mapping: raise HostError("Device with name '%s' already assigned." % name) value.host = self._host self._host.add_tmp_device(value) value.create() self._host.wait_for_tmp_devices(DEFAULT_TIMEOUT) self._device_mapping[name] = value elif isinstance(value, RemoteDevice): if name in self._device_mapping: raise HostError("Device with name '%s' already assigned." % name) if value.if_index is None: value.host = self._host self._host.create_remote_device(value) self._device_mapping[name] = value else: super(Host, self).__setattr__(name, value) def _map_device(self, dev_id, how): hwaddr = how["hwaddr"] dev = self._host.get_dev_by_hwaddr(hwaddr) self._device_mapping[dev_id] = dev def run(self, what, bg=False, fail=False, timeout=DEFAULT_TIMEOUT, json=False, netns=None, desc=None): """ Args: what (mandatory) -- what should be run on the host. Can be either a string, that will be executed on the Host as a shell command, or a TestModule object. bg -- run in background flag. Default 'False'. When True, the method will return immediately after the Job request is sent to the Slave Host. fail -- default 'False'. If True, a Failure will be reported as PASS timeout: time limit in seconds. Default is 60. Only respected for jobs running in foreground (background Jobs don't have a time limit) json: Process JSON output into dictionary. Default 'False'. netns: Run in the specified network namespace. Currently not functional. desc: Decription printed in logs. Accepts a string value. Returns: a Job object that acts as a handle to access the remote Job. If the Job was ran on foreground, the returned Job object will be filled with result data. If the Job was ran in background, the immediately returned Job object can be used to manipulate the running Job remotely and when the result data arrives from the Slave the Job object will be automatically updated. """ #TODO support network namespaces if netns is not None: raise HostError("netns parameter not supported yet.") job = Job(self._host, what, expect=not fail, json=json, netns=netns, desc=desc) try: self._host.run_job(job) if not bg: if not job.wait(timeout): logging.debug("Killing timed-out job") job.kill() except: raise finally: pass #TODO check expect result here # if bg=True: # add "job started" result # else: # add job result return job