Exemplo n.º 1
0
    def _discover(self):
        # filter decorated aetest methods from helper methods
        aetest_methods = {}
        for item in dir(self):
            if hasattr(getattr(self, item), '__testcls__'):
                aetest_methods.update({item: getattr(self, item)})

        used_uids = []
        sections = []
        for component in self.parameters.get('test_sections', {}):
            for action, data in component.items():

                # Attempt to find existing method with same name as action
                method = aetest_methods.get(action)
                if not method:
                    # The function doesn't exist
                    # Generate the test automatically
                    if 'setup' in data:
                        # Load the setup one
                        method = setup_section
                    elif 'cleanup' in data:
                        # Load the cleanup one
                        method = cleanup_section
                    else:
                        # Default is test
                        method = test_section

                func = copy_func(method)
                func.uid = action
                iteration = 1
                while func.uid in used_uids:
                    func.uid = '{}.{}'.format(func.uid, iteration)
                    iteration += 1

                func.parameters = ParameterDict()
                func.parameters['data'] = data
                func.source = Source(self, method.__class__)

                new_method = func.__get__(self, func.__testcls__)

                sections.append(new_method)
                used_uids.append(new_method.uid)
        return sections
Exemplo n.º 2
0
    def discover(self):
        self.history = OrderedDict()
        try:
            order = self.device.clean['order']
        except KeyError:
            raise Exception("Key 'order' is missing for device "
                            "'{d}'".format(d=self.device.name))

        # Insert the 'images' value into necessary clean sections
        if self.device.clean['images']:
            initialize_clean_sections(self.image_handler, order)
        # self.device.clean['change_boot_variable'] = {'images': []}

        all_data = {}
        all_schema = {}
        sections = []
        common_data = {}
        self.parameters['common_data'] = common_data
        for section in order:
            try:
                data = self.device.clean[section] or {}
            except KeyError:
                # Cannot find section - raise exception
                raise Exception("Cannot find '{section}' in the provided "
                                "sections even though it was provided in "
                                "the order list '{order}'".\
                                        format(section=section, order=order))

            # Load it up
            # If source isnt provided then check if it is inside the clean json
            if 'source' not in data:
                # Check if that one exists in the json
                task = _get_clean(section, clean_data, self.device)
            else:
                task = load_class(data, self.device)

            # Verify if schema exists for this section
            if hasattr(task, 'schema'):
                # if the stage has schema defined then build the bigger schema
                all_schema[task.__name__] = task.schema
                all_data[task.__name__] = data
                # unwrap to get original method, tmp fix need to handle in genie core infra
                task = unwrap(task)

            func = copy_func(task)
            func.uid = task.__name__
            func.parameters = ParameterDict()
            func.parameters['device'] = self.device
            func.parameters['common_data'] = common_data
            func.source = Source(self, objcls=func.__class__)

            for parameter, value in data.items():
                func.parameters[parameter] = value

            # Bind it and append to the section list
            new_section = func.__get__(self, func.__testcls__)
            self.history[new_section.uid] = new_section

            # Add processor, add parameters to it if any
            if self.device.clean.get('device_recovery'):
                processor = partial(recovery_processor,
                                    **self.device.clean.get('device_recovery'))
                processors.add(new_section,
                               pre=[block_section],
                               post=[processor],
                               exception=[])

            sections.append(new_section)

        recovery_data = self.device.clean.get('device_recovery')
        # if recovery info not provided, don't need to check schema
        if recovery_data:
            recovery_schema = recovery_processor.schema
            all_schema['device_recovery'] = recovery_schema
            all_data['device_recovery'] = recovery_data

        try:
            Schema(all_schema).validate(all_data)
        except SchemaMissingKeyError as e:
            # proto type
            raise ValueError(
                "Clean schema check failed. The following keys are missing from clean yaml file:\n\n{}"
                .format(self._format_missing_key_msg(
                    e.missing_list))) from None
        except SchemaTypeError as e:

            raise TypeError(
                "Clean schema check failed. Incorrect value type was provided for the "
                "following key:\n\n{}\n\nExpected type {} but got type {}".
                format(self._format_missing_key_msg([e.path]), str(e.type),
                       type(e.data))) from None
        except SchemaUnsupportedKeyError as e:
            raise ValueError(
                "Clean schema check failed. The following keys are not supported:\n\n{}"
                .format(self._format_missing_key_msg(
                    e.unsupported_keys))) from None

        return sections
Exemplo n.º 3
0
    def __iter__(self):
        '''Built-in function __iter__

        Generator function, yielding each testable item within this container
        in the order of appearance inside the test cases. This is the main
        mechanism that allows looping through CleanTestcase Section's child
        items.
        '''
        self.discover()
        used_uids = {}

        order = self.device.clean['order']
        while True:
            for stage in order:
                if stage not in self.stages:
                    raise Exception("Stage '{}' has no configuration"
                                    " in clean.yaml for device {}".format(
                                        stage, self.device.name))

                # Check if stage has hit execution limits to protect
                # against an infinite loop scenario
                count = len(used_uids.get(stage, []))
                limit = self.stages[stage]['stage_reuse_limit'] or \
                        self.global_stage_reuse_limit

                if count >= limit:
                    # Dont log this for every remaining stage
                    if not aetest.executer.goto:
                        log.error(
                            banner(
                                REUSE_LIMIT_MSG.format(stage=stage,
                                                       limit=limit)))

                    aetest.executer.goto_result = results.Blocked
                    aetest.executer.goto = [['Infinite loop scenario', str]]

                # If image handler has a method to update this section - execute it
                if self.image_handler:
                    self.image_handler.update_section(stage)

                cls = self.stages[stage]['func']

                # Get a unique ID for the section
                cls_name = stage.split('__')[0]
                if cls_name not in used_uids:
                    used_uids[cls_name] = []
                    cls.uid = cls.__name__
                else:
                    cls.uid = f"{cls.__name__}({len(used_uids[cls_name])+1})"

                used_uids[cls_name].append(cls.uid)

                cls.parameters = ParameterDict()
                cls.parameters['device'] = self.device

                args = self.stages[stage]['args']
                for parameter, value in args.items():
                    cls.parameters[parameter] = value

                if self.device_recovery_processor:
                    processors.affix(cls,
                                     pre=[block_section],
                                     post=[self.device_recovery_processor])

                cls = cls()
                cls.__name__ = cls.__uid__
                cls.history = self.history
                cls.history[cls.uid] = cls

                # Create a stage section
                new_section = StageSection(cls, parent=self)

                # For some unknown reason, this is required for internal arguments
                # like 'steps' and 'section' to be propagated. Do not remove.
                cls.parameters.internal = new_section.parameters.internal

                yield new_section

                pass_order = self.stages[stage]['change_order_if_pass']
                if pass_order and new_section.result in [Passed, Passx]:
                    msg = "Due to 'change_order_if_pass' the order of clean " \
                          "is changed to:\n- " + "\n- ".join(pass_order)
                    log.warning(msg)
                    order = pass_order
                    if self.image_handler:
                        self.image_handler.update_image_references(cls)
                    break

                fail_order = self.stages[stage]['change_order_if_fail']
                if fail_order and new_section.result in [Failed, Errored]:
                    msg = "Due to 'change_order_if_fail' the order of clean " \
                          "is changed to:\n- " + "\n- ".join(fail_order)
                    log.warning(msg)
                    order = fail_order

                    # In this case we do not want the overall clean result to
                    # be failed. Leave the section result alone but change the
                    # parents to Passed.
                    new_section.parent.parent.result = Passed
                    new_section.parent.result = Passed
                    new_section.result = Passed
                    break

                if not new_section.result:
                    # Dont log this for every remaining stage
                    if not aetest.executer.goto:
                        log.error(banner("*** Terminating Genie Clean ***"))

                    aetest.executer.goto_result = results.Blocked
                    msg = '{} has {}'.format(new_section.uid,
                                             new_section.result)
                    aetest.executer.goto = [[msg, str]]

                # image handler updates latest image
                if self.image_handler:
                    self.image_handler.update_image_references(cls)

            else:
                # Every stage in 'order' successfully ran
                # Break from while loop to finish clean
                break
Exemplo n.º 4
0
    def __iter__(self):
        '''Built-in function __iter__

        Generator function, yielding each testable item within this container
        in the order of appearance inside the test cases. This is the main
        mechanism that allows looping through CleanTestcase Section's child
        items.
        '''
        self.discover()
        used_uids = {}

        order = self.device.clean['order']
        while True:
            for stage in order:
                if stage not in self.stages:
                    self.failed("Stage '{}' has no configuration"
                                " in clean.yaml for device {}".format(
                                    stage, self.device.name))

                # Check if stage has hit execution limits to protect
                # against an infinite loop scenario
                count = len(used_uids.get(stage, []))
                limit = self.stages[stage]['stage_reuse_limit'] or \
                        self.global_stage_reuse_limit

                if count >= limit:
                    # Dont log this for every remaining stage
                    if not aetest.executer.goto:
                        log.error(
                            banner(
                                REUSE_LIMIT_MSG.format(stage=stage,
                                                       limit=limit)))

                    aetest.executer.goto_result = results.Blocked
                    aetest.executer.goto = [['Infinite loop scenario', str]]

                # If image handler has a method to update this section - execute it
                if self.image_handler:
                    self.image_handler.update_section(stage)

                func = self.stages[stage]['func']

                # Get a unique ID for the section
                if stage not in used_uids:
                    used_uids[stage] = []
                    func.uid = stage
                else:
                    func.uid = "{}({})".format(stage,
                                               len(used_uids[stage]) + 1)

                used_uids[stage].append(func.uid)

                # Setup stage function
                func.source = Source(self, objcls=func.__class__)
                func.parameters = ParameterDict()
                func.parameters['device'] = self.device

                args = self.stages[stage]['args']
                for parameter, value in args.items():
                    func.parameters[parameter] = value

                # Bind function
                section = func.__get__(self, func.__testcls__)
                self.history[section.uid] = section

                if self.device_recovery_processor:
                    processors.affix(section,
                                     pre=[block_section],
                                     post=[self.device_recovery_processor],
                                     exception=[])

                new_section = section.__testcls__(section, parent=self)
                yield new_section

                pass_order = self.stages[stage]['change_order_if_pass']
                if pass_order and new_section.result in [Passed, Passx]:
                    msg = "Due to 'change_order_if_pass' the order of clean " \
                          "is changed to:\n- " + "\n- ".join(pass_order)
                    log.warning(msg)
                    order = pass_order
                    break

                fail_order = self.stages[stage]['change_order_if_fail']
                if fail_order and new_section.result in [Failed, Errored]:
                    msg = "Due to 'change_order_if_fail' the order of clean " \
                          "is changed to:\n- " + "\n- ".join(fail_order)
                    log.warning(msg)
                    order = fail_order

                    # In this case we do not want the overall clean result to
                    # be failed. Leave the section result alone but change the
                    # parents to Passed.
                    new_section.parent.parent.result = Passed
                    new_section.parent.result = Passed
                    new_section.result = Passed
                    break

                if not new_section.result:
                    # Dont log this for every remaining stage
                    if not aetest.executer.goto:
                        log.error(banner("*** Terminating Genie Clean ***"))

                    aetest.executer.goto_result = results.Blocked
                    msg = '{} has {}'.format(new_section.uid,
                                             new_section.result)
                    aetest.executer.goto = [[msg, str]]

                # image handler updates latest image
                if self.image_handler:
                    self.image_handler.update_image_references(section)

            else:
                # Every stage in 'order' successfully ran
                # Break from while loop to finish clean
                break