예제 #1
0
    def test_golden(self,
                    steps,
                    local_class,
                    operating_system,
                    display_only_failed=None,
                    token=None,
                    number=None):
        """Test step that finds any output named with _output.txt, and compares to similar named .py file."""
        if token:
            folder_root = pathlib.Path(
                f"{operating_system}/{token}/{local_class.__name__}/cli/equal")
        else:
            folder_root = pathlib.Path(
                f"{operating_system}/{local_class.__name__}/cli/equal")

        # Get list of output files to parse and sort
        convert = lambda text: int(text) if text.isdigit() else text
        aph_key = lambda key: [convert(c) for c in re.split("([0-9]+)", key)]
        if number and not operating_system or not local_class:
            output_glob = sorted(
                glob.glob(f"{folder_root}/golden_output{number}_output.txt"),
                key=aph_key,
            )
        else:
            output_glob = sorted(glob.glob(f"{folder_root}/*_output.txt"),
                                 key=aph_key)

        all_txt_glob = sorted(glob.glob(f"{folder_root}/*.txt"), key=aph_key)

        unacceptable_filenames = [
            fil for fil in all_txt_glob if fil not in output_glob
        ]

        if len(output_glob) == 0:
            steps.failed(
                f"No files found in appropriate directory for {local_class}")

        # Look for any files ending with _output.txt, presume the user defined name from that (based
        # on truncating that _output.txt suffix) and obtaining expected results and potentially an arguments file

        for user_defined in output_glob:
            glo_values.parserTotal += 1
            user_test = os.path.basename(user_defined[:-len("_output.txt")])
            if token:
                msg = f"Gold -> {operating_system} -> Token {token} -> {local_class.__name__} -> {user_test}"
            else:
                msg = f"Gold -> {operating_system} -> {local_class.__name__} -> {user_test}"

            with steps.start(msg, continue_=True):
                golden_output_str = read_from_file(
                    f"{folder_root}/{user_test}_output.txt")
                golden_output = {
                    "execute.return_value": golden_output_str,
                    "expect.return_value": golden_output_str,
                }

                golden_parsed_output = read_python_file(
                    f"{folder_root}/{user_test}_expected.py")
                arguments = {}
                if os.path.exists(f"{folder_root}/{user_test}_arguments.json"):
                    arguments = read_json_file(
                        f"{folder_root}/{user_test}_arguments.json")

                device = Mock(**golden_output)
                obj = local_class(device=device)
                try:
                    parsed_output = obj.parse(**arguments)
                except Exception as e:
                    parsed_output = {}
                    self.add_logger()
                    log.error(traceback.format_exc(), extra={'colour': 'red'})
                    self.remove_logger()
                    glo_values.parserErrored += 1

                # Use Diff method to get the difference between
                # what is expected and the parsed output
                dd = Diff(parsed_output, golden_parsed_output)
                dd.findDiff()
                if parsed_output != golden_parsed_output:
                    glo_values.parserFailed += 1
                    # if -f flag provided, then add the screen handler back into
                    # the root.handlers to displayed failed tests. Decorator removes
                    # screen handler from root.handlers after failed tests are displayed
                    # to stdout
                    if display_only_failed:
                        self.add_logger()
                        log.info(banner(msg))
                    # Format expected and parsed output in a nice format
                    parsed_json_data = format_output(parsed_output)
                    golden_parsed_output_json_data = format_output(
                        golden_parsed_output)

                    # Display device output, parsed output, and golden_output of failed tests
                    log.info(
                        f"\nThe following is the device output before it is parsed:\n{golden_output['execute.return_value']}\n\n",
                        extra={'colour': 'yellow'})
                    log.info(
                        f"The following is your device's parsed output:\n{parsed_json_data}\n",
                        extra={'colour': 'yellow'})
                    log.info(
                        f"The following is your expected output:\n{golden_parsed_output_json_data}\n",
                        extra={'colour': 'yellow'})
                    log.info(
                        "The following is the difference between the two outputs:\n",
                        extra={'colour': 'yellow'})

                    # Display the diff between parsed output and golden_output
                    log.info(str(dd), extra={'colour': 'yellow'})
                    if display_only_failed:
                        self.remove_logger()
                    raise AssertionError(
                        "Device output and expected output do not match")
                else:
                    glo_values.parserPassed += 1
                    # If tests pass, display the device output in debug mode
                    # But first check if the screen handler is removed, if it is
                    # put it back into the root otherwise just display to stdout
                    if (self.temporary_screen_handler in log.root.handlers
                            or self.temporary_screen_handler is None):
                        logging.debug(banner(msg))
                        logging.debug(
                            "\nThe following is the device output for the passed parser:\n{}\n"
                            .format(golden_output['execute.return_value']),
                            extra={'colour': 'yellow'})

                    else:
                        self.add_logger()
                        logging.debug(banner(msg))
                        logging.debug(
                            "\nThe following is the device output for the passed parser:\n{}\n"
                            .format(golden_output['execute.return_value']),
                            extra={'colour': 'yellow'})
                        self.remove_logger()
        if unacceptable_filenames:
            for unacc_fil in unacceptable_filenames:
                unacc_fil_name = pathlib.Path(unacc_fil).name
                msg = f"{unacc_fil_name} does not follow the filename schema and will not be ran..."
                with steps.start(msg, continue_=True) as step:
                    if (self.temporary_screen_handler in log.root.handlers
                            or self.temporary_screen_handler is None):
                        log.info(
                            f"Filename should be `{unacc_fil_name.split('.')[0]}_expected.txt`",
                            extra={'colour': 'yellow'})
                    else:
                        self.add_logger()
                        log.info(msg, extra={'colour': 'yellow'})
                        log.info(
                            f"Filename should be `{unacc_fil_name.split('.')[0]}_expected.txt`",
                            extra={'colour': 'yellow'})
                        self.remove_logger()
                    step.failed()
예제 #2
0
    def _pre_post_processors(self,
                             testbed,
                             processor,
                             section,
                             data,
                             name,
                             devices_connected,
                             processor_targets,
                             processor_type,
                             pre_processor_result=Passed):
        """
        execute pre/post processors and return if pre-processor runs and processor result

        Arguments:
            testbed (`obj`): testbed object
            processor (`obj`): Aetest Processor object
            section (`obj`): Aetest Section object
            data (`list`) : data of section
            name (`str`) : name of section in health yaml
            devices_connected (`list`) : list of connected devices
            processor_targets (`list`) : list of `processor_flag which ones 
                                         will be run as pre/post processors
            processor_type (`str`) : processor type `pre` or `post`
            pre_processor_result (`ob`) : result object. Default to `Passed`

        Returns:
            pre_processor_run (`bool`) : if pre processor runs or not
            pre_processor_result (`obj`) : return processor result (Result obj)
        """
        new_data_dict = {}
        selected_options = 0
        list_of_args = []
        # flag if health args are given to pyats command
        args_flag = False
        # flag if health args are defined under action in health yaml
        args_in_yaml_flag = False
        log.debug(
            'data:\n{d}'.format(d=json.dumps(data, indent=2, sort_keys=True)))
        orig_data = copy.deepcopy(data)

        # check if health arguments are given to pyats command
        for arg_name in ['health_sections', 'health_uids', 'health_groups']:
            if getattr(runtime.args, arg_name):
                args_flag = True
            for item in self._get_actions(data):
                if Dq(item).contains(
                        'health_sections|health_uids|health_groups',
                        regex=True):
                    args_in_yaml_flag = True

        for arg_name in ['health_sections', 'health_uids', 'health_groups']:
            log.debug('Checking {an}'.format(an=arg_name))
            selected = None
            selected_options = 0
            for item in self._get_actions(data):
                # from argument

                arg_search_keyword = getattr(runtime.args, arg_name)
                if arg_search_keyword:
                    args_flag = True
                    selected = self._select_health(
                        section, item, arg_search_keyword.split(' '), arg_name)
                    selected_options += 1
                    list_of_args.append(arg_name)
                if selected:
                    new_data_dict.setdefault(arg_name, {}).setdefault(
                        selected_options, selected)

                if not args_flag:
                    # from datafile
                    search_keywords = []
                    search_keywords = getattr(
                        runtime.args,
                        arg_name) or Dq(item).get_values(arg_name)
                    if not isinstance(search_keywords, list):
                        search_keywords = [search_keywords]
                    if search_keywords == []:
                        # if args are given to one of actions, other actions
                        # will run to all sections by default. To do so,
                        # adding `.*` as search_keywords
                        # ex.)
                        # - api:               # only section1
                        #     function: func1
                        #     health_sections: section1
                        # - api:               # all sections
                        #     function: func2
                        if (args_in_yaml_flag and arg_name == 'health_sections'
                            ) and (not Dq(item).get_values('health_sections')
                                   and not Dq(item).get_values('health_uids')):
                            search_keywords = ['.*']
                        else:
                            search_keywords = None

                    log.debug(
                        "arg_name, search_keywords: {sel_name}, {sel}".format(
                            sel_name=arg_name, sel=search_keywords))
                    if search_keywords:
                        selected_options += 1
                        list_of_args.append(arg_name)
                        selected = self._select_health(section, item,
                                                       search_keywords,
                                                       arg_name)
                    if selected:
                        new_data_dict.setdefault(arg_name, {}).setdefault(
                            selected_options, selected)

        if args_flag:
            # check for the case which multiple `arg_name`s given and check the same
            # among the `arg_name`s. if same between `arg_name`s, data will be overwittern
            # by one of new_data_dict value to execute selected ones
            new_data_flag = False
            if new_data_dict:
                value = ''
                log.debug(
                    'num of health args: {n}'.format(n=len(set(list_of_args))))
                log.debug(
                    'num of new_data_dict: {n}'.format(n=len(new_data_dict)))
                if len(set(list_of_args)) == len(new_data_dict):
                    for key, value_ in new_data_dict.items():
                        if value == value_:
                            new_data_flag = True
                        else:
                            new_data_flag = False
                            if not value:
                                value = value_
                                if len(new_data_dict) == 1:
                                    new_data_flag = True
        else:
            new_data_flag = bool(new_data_dict)

        log.debug('new_data_flag: {f}'.format(f=new_data_flag))

        log.debug('new_data_dict: {ndd}'.format(
            ndd=json.dumps(new_data_dict, indent=2, sort_keys=True)))

        if new_data_flag:
            temp_data = []
            # override data because meeting criteria by `arg_name`s
            for key in new_data_dict:
                for idx in new_data_dict[key]:
                    temp_data.append(new_data_dict[key][idx].pop())
                data = temp_data
        else:
            if (not new_data_dict or len(set(list_of_args)) !=
                    len(new_data_dict)) and len(set(list_of_args)) != 0:
                data = []
        # processor start message
        log.debug('{type}-processor {name} started'.format(
            name=name, type=processor_type.capitalize()))
        pre_processor_run = True

        # check if `processor` tag matches processor_targets and
        # if device for action is connected
        # create temp_data with matched actions and override data by temp_data
        temp_data = []
        # list of checked devices. flag to ignore checked device
        device_checked = []
        # None if no device is defined in any actions
        all_devices_connected = None

        for each_data in self._get_actions(data):
            for key in each_data:
                log.debug(
                    'processor_targets: {pt}'.format(pt=processor_targets))
                log.debug('processor: {p}'.format(
                    p=each_data[key].get('processor', 'both')))
                if each_data[key].get('processor',
                                      'both') in processor_targets:
                    # check if device for action is connected
                    all_devices_connected = None
                    for uut in self._get_device_names(orig_data, each_data):
                        if uut not in device_checked:
                            device_checked.append(uut)
                            if isinstance(uut, str):
                                if (testbed.devices[uut].name
                                        in devices_connected) or (
                                            testbed.devices[uut].alias
                                            in devices_connected):
                                    all_devices_connected = True
                                else:
                                    all_devices_connected = False
                                    log.info(
                                        'Device {d} is not connected.'.format(
                                            d=testbed.devices[uut].name))
                                    break
                            else:
                                if (uut.name in devices_connected) or (
                                        uut.alias in devices_connected):
                                    all_devices_connected = True
                                else:
                                    all_devices_connected = False
                                    log.info(
                                        'Device {d} is not connected.'.format(
                                            d=testbed.devices[uut].name))
                                    break
                    if (all_devices_connected == True
                            or all_devices_connected is None):
                        temp_data.append(each_data)

        # until here, data contains only actions
        # for cases like `parallel`, `loop`, need to put the headers
        # from original data `orig_data`
        if 'actions' in orig_data:
            data = copy.deepcopy(orig_data)
            if temp_data:
                data['actions'] = temp_data
                data = [{'loop': data}]
            else:
                data = []
        elif isinstance(orig_data, list):
            if len(orig_data) > 0 and 'parallel' in orig_data[0]:
                data = copy.deepcopy(orig_data)[0]
                if temp_data:
                    data['parallel'] = temp_data
                    data = [data]
                else:
                    data = []
            else:
                data = temp_data
        else:
            data = temp_data
        # remove section if no data
        if not data:
            processor.reporter.remove_section(id_list=processor.uid.list)

        # if any device is not connected, processor will be skipped
        if devices_connected:
            # instantiate Steps() to reset step number
            steps = Steps()
            # execute dispatcher in Blitz
            result = self.dispatcher(steps, testbed, section, data, name)
            try:
                log.debug('Blitz section return:\n{result}'.format(
                    result=json.dumps(result, indent=2, sort_keys=True)))
            except TypeError:
                log.debug('Blitz section return:\n{result}'.format(
                    result=format_output(result)))
            # check section result
            log.debug('section result: {section_result}'.format(
                section_result=section.result.name))
            log.debug('steps result: {steps_result}'.format(
                steps_result=steps.result.name))
            if processor_type == 'pre' and steps.result != Passed and steps.result != Passx:
                log.info(
                    "Pre-processor pyATS Health {name} was failed, but continue section and Post-processor"
                    .format(name=name))
                # save pre-processor result
                pre_processor_result = steps.result
                return pre_processor_run, pre_processor_result
            elif processor_type == 'post':
                # refrect processor results to section
                processor.result += steps.result
                section.result = section.result + processor.result + self.pre_processor_result

                return pre_processor_run, pre_processor_result
        else:
            if processor_type == 'pre':
                pre_processor_run = False
                # processor is skipped. but call passed to move forward     for this case
                log.info(
                    "Pre-processor pyATS Health '{name}' is skipped because devices are not connected."
                    .format(name=name))
                return pre_processor_run, pre_processor_result
            elif processor_type == 'post':
                # for the case only pre-processors runs
                if section.result == pre_processor_result:
                    log.info('Only Pre-processor runs. Section result and '
                             'Pre-processor result are different.Reflecting '
                             'Post-processor result to Section.')
                    # reflect processor results to section
                    section.result = section.result + processor.result + self.pre_processor_result
                log.info(
                    "Post-processor pyATS Health '{name}' was skipped because devices are not connected."
                    .format(name=name))
                return pre_processor_run, pre_processor_result

        return pre_processor_run, pre_processor_result
예제 #3
0
파일: health.py 프로젝트: Eve320/genielibs
    def _pre_post_processors(self,
                             testbed,
                             processor,
                             section,
                             data,
                             name,
                             devices_connected,
                             processor_flag,
                             processor_targets,
                             processor_type,
                             pre_processor_result=Passed):
        """
        execute pre/post processors and return if pre-processor runs and processor result

        Arguments:
            testbed (`obj`): testbed object
            processor (`obj`): Aetest Processor object
            section (`obj`): Aetest Section object
            data (`list`) : data of section
            name (`str`) : name of section in health yaml
            devices_connected (`bool`) : if devices are connected, or not
            processor_flag (`str`) : `pre`, `post`, `both` and etc
            processor_targets (`list`) : list of `processor_flag which ones 
                                         will be run as pre/post processors
            processor_type (`str`) : processor type `pre` or `post`
            pre_processor_result (`ob`) : result object. Default to `Passed`

        Returns:
            pre_processor_run (`bool`) : if pre processor runs or not
            pre_processor_result (`obj`) : return processor result (Result obj)
        """
        new_data_dict = {}
        selected_options = 0
        list_of_args = []
        args_flag = False

        # check if health arguments are given to pyats command
        for arg_name in ['health_sections', 'health_uids', 'health_groups']:
            if getattr(runtime.args, arg_name):
                args_flag = True

        for arg_name in ['health_sections', 'health_uids', 'health_groups']:

            log.debug('Checking {an}'.format(an=arg_name))
            selected = None
            selected_options = 0
            for item in data:
                # from argument

                arg_search_keyword = getattr(runtime.args, arg_name)
                if arg_search_keyword:
                    args_flag = True
                    selected = self._select_health(
                        section, item, arg_search_keyword.split(' '), arg_name)
                    selected_options += 1
                    list_of_args.append(arg_name)
                if selected:
                    new_data_dict.setdefault(arg_name, {}).setdefault(
                        selected_options, selected)

                if not args_flag:
                    # from datafile
                    search_keywords = []
                    search_keywords = getattr(
                        runtime.args,
                        arg_name) or Dq(item).get_values(arg_name)
                    if not isinstance(search_keywords, list):
                        search_keywords = [search_keywords]
                    if search_keywords == []:
                        search_keywords = None

                    log.debug(
                        "arg_name, search_keywords: {sel_name}, {sel}".format(
                            sel_name=arg_name, sel=search_keywords))
                    if search_keywords:
                        selected_options += 1
                        list_of_args.append(arg_name)
                        selected = self._select_health(section, item,
                                                       search_keywords,
                                                       arg_name)
                    if selected:
                        new_data_dict.setdefault(arg_name, {}).setdefault(
                            selected_options, selected)

        # check for the case which multiple `arg_name`s given and check the same
        # among the `arg_name`s. if same between `arg_name`s, data will be overwittern
        # by one of new_data_dict value to execute selected ones
        new_data_flag = False
        if new_data_dict:
            value = ''
            log.debug(
                'num of health args: {n}'.format(n=len(set(list_of_args))))
            log.debug('num of new_data_dict: {n}'.format(n=len(new_data_dict)))
            if len(set(list_of_args)) == len(new_data_dict):
                for key, value_ in new_data_dict.items():
                    if value == value_:
                        new_data_flag = True
                    else:
                        new_data_flag = False
                        if not value:
                            value = new_data_dict[key]
                            if len(new_data_dict) == 1:
                                new_data_flag = True

        log.debug('new_data_flag: {f}'.format(f=new_data_flag))
        log.debug('new_data_dict: {ndd}'.format(ndd=new_data_dict))
        if new_data_flag:
            data2 = []
            # override data because meeting criteria by `arg_name`s
            for key in new_data_dict:
                for idx in new_data_dict[key]:
                    data2.append(new_data_dict[key][idx].pop())
                data = data2
        else:
            # remove report based on conditions
            # - no found data based on search
            # - devices are not connected
            # - number of given arguments and found data are not equal
            # - number of given arguments is not 0
            if (not new_data_dict or not devices_connected
                    or len(set(list_of_args)) != len(new_data_dict)) and len(
                        set(list_of_args)) != 0:
                processor.reporter.remove_section(id_list=processor.uid.list)
            if (not new_data_dict or len(set(list_of_args)) !=
                    len(new_data_dict)) and len(set(list_of_args)) != 0:
                data = []
            # if devices are not connected, delete processor from reporter
            if not devices_connected and data:
                processor.reporter.remove_section(id_list=processor.uid.list)

        # processor start message
        log.debug('{type}-processor {name} started'.format(
            name=name, type=processor_type.capitalize()))
        pre_processor_run = True

        # check `processor` to control
        if processor_flag in processor_targets:
            # if any device is not connected, processor will be skipped
            if devices_connected:
                # instantiate Steps() to reset step number
                steps = Steps()
                result = self.dispatcher(steps, testbed, section, data, name)

                log.debug('Blitz section return:\n{result}'.format(
                    result=format_output(result)))
                # check section result
                log.debug('section result: {section_result}'.format(
                    section_result=section.result.name))
                log.debug('steps result: {steps_result}'.format(
                    steps_result=steps.result.name))
                if processor_type == 'pre' and steps.result != Passed and steps.result != Passx:
                    log.info(
                        "Pre-processor pyATS Health {name} was failed, but continue section and Post-processor"
                        .format(name=name))
                    # save pre-processor result
                    pre_processor_result = steps.result
                    return pre_processor_run, pre_processor_result
                elif processor_type == 'post':
                    # refrect result to section
                    getattr(
                        section,
                        str(steps.result + steps.result +
                            self.pre_processor_result))()
                    return pre_processor_run, pre_processor_result

            else:
                if processor_type == 'pre':
                    pre_processor_run = False
                    # processor is skipped. but call passed to move forward     for this case
                    log.info(
                        "Pre-processor pyATS Health '{name}' is skipped because devices are not connected."
                        .format(name=name))
                    return pre_processor_run, pre_processor_result
                elif processor_type == 'post':
                    # for the case only pre-processors runs
                    if section.result == pre_processor_result:
                        log.info(
                            'Only Pre-processor runs. Section result and Pre-processor result are different. Reflecting Post-processor result to Section.'
                        )
                        getattr(section,
                                str(section.result + pre_processor_result))()
                    log.info(
                        "Post-processor pyATS Health '{name}' was skipped because devices are not connected."
                        .format(name=name))
                    return pre_processor_run, pre_processor_result
        else:
            log.info('Skipped because {name} is not {type}-processor'.format(
                name=name, type=processor_type.capitalize()))
            return pre_processor_run, pre_processor_result

        return pre_processor_run, pre_processor_result
예제 #4
0
    def _pre_post_processors(self,
                             testbed,
                             processor,
                             section,
                             data,
                             name,
                             reconnect,
                             processor_targets,
                             processor_type,
                             pre_processor_result=Passed,
                             health_settings=None):
        """
        execute pre/post processors and return if pre-processor runs and processor result

        Arguments:
            testbed (`obj`): testbed object
            processor (`obj`): Aetest Processor object
            section (`obj`): Aetest Section object
            data (`list`) : data of section
            name (`str`) : name of section in health yaml
            reconnect (`dict` or None) : parameters for reconnect
            processor_targets (`list`) : list of `processor_flag which ones 
                                         will be run as pre/post processors
            processor_type (`str`) : processor type `pre` or `post`
            pre_processor_result (`ob`) : result object. Default to `Passed`

        Returns:
            pre_processor_run (`bool`) : if pre processor runs or not
            pre_processor_result (`obj`) : return processor result (Result obj)
        """
        devices_connected = []
        new_data_dict = {}
        selected_options = 0
        list_of_args = []
        # store reasons why processor is skipped
        reasons = []
        # flag if health args are given to pyats command
        args_flag = False
        # flag if health args are defined under action in health yaml
        args_in_yaml_flag = False
        log.debug(
            'data:\n{d}'.format(d=json.dumps(data, indent=2, sort_keys=True)))
        orig_data = copy.deepcopy(data)

        # check if health arguments are given to pyats command
        for arg_name in [
                'health_tc_sections', 'health_tc_uids', 'health_tc_groups',
                'health_sections', 'health_uids', 'health_groups'
        ]:
            if getattr(runtime.args, arg_name):
                args_flag = True
            for item in self._get_actions(data, processor_targets):
                if Dq(item).contains(
                        'health_tc_sections|health_tc_uids|health_tc_groups|health_sections|health_uids|health_groups',
                        regex=True):
                    args_in_yaml_flag = True

        for arg_name in [
                'health_tc_sections', 'health_tc_uids', 'health_tc_groups',
                'health_sections', 'health_uids', 'health_groups'
        ]:
            log.debug('Checking {an}'.format(an=arg_name))
            selected = None
            selected_options = 0
            for item in self._get_actions(data, processor_targets):
                # from argument

                arg_search_keyword = getattr(runtime.args, arg_name)
                if arg_search_keyword:
                    args_flag = True
                    selected = self._select_health(
                        section, item, arg_search_keyword.split(' '), arg_name)
                    selected_options += 1
                    list_of_args.append(arg_name)
                if selected:
                    new_data_dict.setdefault(arg_name, {}).setdefault(
                        selected_options, selected)

                if not args_flag:
                    # from datafile
                    search_keywords = []
                    search_keywords = getattr(
                        runtime.args,
                        arg_name) or Dq(item).get_values(arg_name)
                    if not isinstance(search_keywords, list):
                        search_keywords = [search_keywords]
                    if search_keywords == []:
                        # if args are given to one of actions, other actions
                        # will run to all sections by default. To do so,
                        # adding `.*` as search_keywords
                        # ex.)
                        # - api:               # only section1
                        #     function: func1
                        #     health_tc_sections: section1
                        # - api:               # all sections
                        #     function: func2
                        if (args_in_yaml_flag and arg_name
                                in ['health_tc_sections', 'health_sections']
                                and
                            ((not Dq(item).get_values('health_tc_sections')
                              or not Dq(item).get_values('health_sections'))
                             and (not Dq(item).get_values('health_tc_uids')
                                  or not Dq(item).get_values('health_uids')))):
                            search_keywords = ['.*']
                        else:
                            search_keywords = None

                    log.debug(
                        "arg_name, search_keywords: {sel_name}, {sel}".format(
                            sel_name=arg_name, sel=search_keywords))
                    if search_keywords:
                        selected_options += 1
                        list_of_args.append(arg_name)
                        selected = self._select_health(section, item,
                                                       search_keywords,
                                                       arg_name)
                    if selected:
                        new_data_dict.setdefault(arg_name, {}).setdefault(
                            selected_options, selected)

        if args_flag:
            # check for the case which multiple `arg_name`s given and check the same
            # among the `arg_name`s. if same between `arg_name`s, data will be overwittern
            # by one of new_data_dict value to execute selected ones
            new_data_flag = False
            if new_data_dict:
                value = ''
                log.debug(
                    'num of health args: {n}'.format(n=len(set(list_of_args))))
                log.debug(
                    'num of new_data_dict: {n}'.format(n=len(new_data_dict)))
                if len(set(list_of_args)) == len(new_data_dict):
                    for key, value_ in new_data_dict.items():
                        if value == value_:
                            new_data_flag = True
                        else:
                            new_data_flag = False
                            if not value:
                                value = value_
                                if len(new_data_dict) == 1:
                                    new_data_flag = True
        else:
            new_data_flag = len(set(list_of_args)) == len(new_data_dict)

        log.debug('new_data_flag: {f}'.format(f=new_data_flag))

        log.debug('new_data_dict: {ndd}'.format(
            ndd=json.dumps(new_data_dict, indent=2, sort_keys=True)))

        if new_data_flag:
            temp_data = []
            # override data because meeting criteria by `arg_name`s
            for key, value__ in new_data_dict.items():
                for idx in value__:
                    # data from each health arg should be same
                    # so remove redundant data by overwriting
                    temp_data = [new_data_dict[key][idx].pop()]
                data = temp_data
        elif (not new_data_dict or len(set(list_of_args)) != len(new_data_dict)
              ) and len(set(list_of_args)) != 0:
            reasons.append(
                f"health arg {set(list_of_args)-set(new_data_dict.keys())} does not meet criteria"
            )
            data = []
        # processor start message
        log.debug('{type}-processor {name} started'.format(
            name=name, type=processor_type.capitalize()))
        pre_processor_run = True

        # check if `processor` tag matches processor_targets and
        # if device for action is connected
        # create temp_data with matched actions and override data by temp_data
        temp_data = []
        # list of checked devices. flag to ignore checked device
        device_checked = []
        # None if no device is defined in any actions
        all_devices_connected = None

        common_api = False

        if new_data_dict and new_data_flag:
            # get connected devices list
            devices_connected = self._check_all_devices_connected(
                testbed, data, reconnect)
            devices_connected = [dev for dev in devices_connected if dev != '']

        actions = self._get_actions(data, processor_targets)
        if not actions:
            # check processor in action and put in proc_in_action
            proc_in_action = []
            if isinstance(data, list):
                for each_data in data:
                    for each_proc in Dq(each_data).get_values('processor'):
                        proc_in_action.append(each_proc)
            else:
                for each_proc in Dq(data).get_values('processor'):
                    proc_in_action.append(each_proc)
            proc_in_action = set(proc_in_action)
            if proc_in_action:
                reasons.append(
                    f"processor {proc_in_action} does not meet criteria {processor_targets}"
                )
        for each_data in actions:
            for key in each_data:
                # get processor key from action. by default, `both`
                each_data_dq = Dq(each_data)
                processor_from_yaml = each_data_dq.contains(key).get_values(
                    'processor', 0)
                if not processor_from_yaml:
                    processor_from_yaml = 'both'

                log.debug(
                    'processor_targets: {pt}'.format(pt=processor_targets))
                log.debug('processor: {p}'.format(p=processor_from_yaml))

                # find `common_api` key and return True/False
                common_api = any(each_data_dq.get_values('common_api'))

                if processor_from_yaml in processor_targets:
                    # check if device for action is connected
                    all_devices_connected = None
                    devices_not_connected = []
                    for uut in self._get_device_names(orig_data, each_data):
                        if uut not in device_checked:
                            device_checked.append(uut)
                            if isinstance(uut, str):
                                if (testbed.devices[uut].name
                                        in devices_connected) or (
                                            testbed.devices[uut].alias
                                            in devices_connected):
                                    all_devices_connected = True
                                else:
                                    all_devices_connected = False
                                    devices_not_connected.append(uut)
                            elif (uut.name in devices_connected) or (
                                    uut.alias in devices_connected):
                                all_devices_connected = True
                            else:
                                all_devices_connected = False
                                devices_not_connected.append(uut)

                    if devices_not_connected:
                        log.warning("devices are not connected: {}".format(
                            devices_not_connected))

                    force_all_connected = health_settings.get(
                        'force_all_connected', True)
                    if device_checked and not force_all_connected and devices_connected:
                        log.warning(
                            "force_all_connected is False. Executing even though some of devices might not be connected."
                        )
                    # data will be created if all devices are connected or
                    # if force_all_connected == False and one of devices is connected
                    if (all_devices_connected == True or all_devices_connected
                            is None) or (force_all_connected == False
                                         and devices_connected):
                        temp_data.append(each_data)
                    else:
                        log.warning(
                            'health check is blocked due to force_all_connected is True.'
                        )

        # until here, data contains only actions
        # for cases like `parallel`, `loop`, need to put the headers
        # from original data `orig_data`
        if 'actions' in orig_data and data and temp_data:
            data = copy.deepcopy(orig_data)
            if temp_data:
                data['actions'] = temp_data
                data = [{'loop': data}]
            else:
                data = []
        elif isinstance(orig_data, list):
            if len(orig_data
                   ) > 0 and 'parallel' in orig_data[0] and data and temp_data:
                data = copy.deepcopy(orig_data)[0]
                if temp_data:
                    data['parallel'] = temp_data
                    data = [data]
                else:
                    data = []
            elif len(orig_data) > 0 and 'run_condition' in orig_data[
                    0] and data and temp_data:
                data = copy.deepcopy(orig_data)[0]
                data = [data]
            else:
                data = temp_data
        else:
            data = temp_data
        # remove section if no data
        removed_section = False
        # set reason in case device is not connected
        if (not devices_connected and not common_api) and not reasons:
            reasons.append('Device is not connected')
        if not data or reasons:
            processor.result = Skipped
            processor.reporter.remove_section(id_list=processor.uid.list)
            removed_section = True

        # if any device is not connected, processor will be skipped
        # if common_api is True, will execute
        if devices_connected or common_api:
            # instantiate Steps() to reset step number
            steps = Steps()
            # execute dispatcher in Blitz
            result = self.dispatcher(steps, testbed, section, data, name)

            if isinstance(data, list):
                hide_processor = any(
                    Dq(data[0]).get_values('hide_processor', 0) == True
                    for each_data in data)

            else:
                hide_processor = Dq(data[0]).get_values('hide_processor', 0)

            if hide_processor and not removed_section:
                removed_section = self._remove_section(processor)
            try:
                log.debug('Blitz section return:\n{result}'.format(
                    result=json.dumps(result, indent=2, sort_keys=True)))
            except TypeError:
                log.debug('Blitz section return:\n{result}'.format(
                    result=format_output(result)))
            # check section result
            log.debug('section result: {section_result}'.format(
                section_result=section.result.name))
            log.debug('steps result: {steps_result}'.format(
                steps_result=steps.result.name))

            # if section is skipped by run_condition, remove section
            if (isinstance(result, dict) and 'run_condition_skipped' in result
                    and not removed_section
                    and result['run_condition_skipped'] == True):
                processor.result = Skipped
                removed_section = self._remove_section(processor)
            if processor_type == 'pre' and steps.result != Passed and steps.result != Passx:
                log.info(
                    "Pre-processor pyATS Health {name} was failed, but continue section and Post-processor"
                    .format(name=name))
                # save pre-processor result
                pre_processor_result = steps.result
                return pre_processor_run, pre_processor_result
            elif processor_type == 'post':
                # refrect processor results to section
                processor.result += steps.result
                section.result = section.result + processor.result + self.pre_processor_result

                # return processor.result to raise the result
                # at end of context post processor
                return pre_processor_run, processor.result

        elif processor_type == 'pre':
            pre_processor_run = False
            # processor is skipped
            log.info(
                f"Pre-processor pyATS Health '{name}' is skipped due to: {reasons}"
            )
            if pre_processor_result == Passed:
                # processor.skipped()
                pre_processor_result = Skipped
            return pre_processor_run, pre_processor_result
        elif processor_type == 'post':
            # for the case only pre-processors runs
            if section.result == pre_processor_result:
                log.info('Only Pre-processor runs. Section result and '
                         'Pre-processor result are different.Reflecting '
                         'Post-processor result to Section.')
                # reflect processor results to section
                section.result = section.result + processor.result + self.pre_processor_result
            # processor is skipped
            log.info(
                f"Post-processor pyATS Health '{name}' was skipped due to: {reasons}"
            )
            if pre_processor_result == Passed:
                # processor.skipped()
                pre_processor_result = Skipped

            # return processor.result to raise the result
            # at end of context post processor
            return pre_processor_run, processor.result

        return pre_processor_run, pre_processor_result
예제 #5
0
    def _pre_post_processors(self,
                             testbed,
                             section,
                             data,
                             name,
                             devices_connected,
                             processor_flag,
                             processors,
                             processor_type,
                             pre_processor_result=Passed):
        # processor start message
        # import remote_pdb; remote_pdb.set_trace()
        log.debug('{type}-processor {name} started'.format(
            name=name, type=processor_type.capitalize()))
        pre_processor_run = True

        # check `processor` to control
        if processor_flag in processors:
            # if any device is not connected, processor will be skipped
            if devices_connected:
                # instantiate Steps() to reset step number
                steps = Steps()
                result = self.dispatcher(steps, testbed, section, data, name)

                log.debug('Blitz section return:\n{result}'.format(
                    result=format_output(result)))
                # check section result
                log.debug('section result: {section_result}'.format(
                    section_result=section.result.name))
                log.debug('steps result: {steps_result}'.format(
                    steps_result=steps.result.name))

                if processor_type == 'pre' and steps.result != Passed and steps.result != Passx:
                    log.info(
                        "Pre-processor pyATS Health {name} was failed, but continue section and Post-processor"
                        .format(name=name))
                    # save pre-processor result
                    pre_processor_result = steps.result
                    return pre_processor_run, pre_processor_result
                elif processor_type == 'post':
                    # refrect result to section
                    getattr(
                        section,
                        str(steps.result + steps.result +
                            self.pre_processor_result))()
                    return pre_processor_run, pre_processor_result

            else:
                if processor_type == 'pre':
                    pre_processor_run = False
                    # processor is skipped. but call passed to move forward     for this case
                    log.info(
                        "Pre-processor pyATS Health '{name}' is skipped  because devices are not connected."
                        .format(name=name))
                    return pre_processor_run, pre_processor_result
                elif processor_type == 'post':
                    # for the case only pre-processors runs
                    if section.result == pre_processor_result:
                        log.info(
                            'Only Pre-processor runs. Section result and Pre-processor result are different. Reflecting Post-processor result to Section.'
                        )
                        getattr(section,
                                str(section.result + pre_processor_result))()
                    log.info(
                        "Post-processor pyATS Health '{name}' was skipped because devices are not connected."
                        .format(name=name))
                    return pre_processor_run, pre_processor_result
        else:
            log.info('Skipped because {name} is not {type}-processor'.format(
                name=name, type=processor_type.capitalize()))
            return pre_processor_run, pre_processor_result

        return pre_processor_run, pre_processor_result