Beispiel #1
0
    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)
Beispiel #2
0
    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 = {}
Beispiel #3
0
 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)
Beispiel #4
0
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)
Beispiel #5
0
    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
Beispiel #6
0
    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
Beispiel #7
0
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
Beispiel #8
0
    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))
Beispiel #9
0
    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))
Beispiel #10
0
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
Beispiel #11
0
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