def test_not_found_in_empty_discovery_container(self):
     discovery_container = DiscoveryContainer(metadata_container=self.metadata_container)
     requirements = [
         Requirement("Chassis.Chassis", min=1, max=1)
     ]
     preconditions = Preconditions(self.metadata_container, requirements)
     self.assertEqual(ValidationStatus.FAILED, preconditions.validate(discovery_container))
Exemple #2
0
    def run(self):
        if self.metadata_container is None:
            return

        self.chosen_endpoint, self.chosen_zone, self.chosen_volume, self.chosen_volume_collection = (None,)*4
        self.initiator_endpoint, self.target_endpoint, self.zone_endpoint, self.created_volume = (None,)*4

        test_name = "Storage Services CRUD test"
        print "MESSAGE::%s starting" % test_name

        print "TEST_CASE::API crawling"
        api_explorer = ApiExplorer(self.metadata_container, self.configuration)
        self.discovery_container, status = api_explorer.discover(MetadataConstants.SERVICE_ROOT_URI,
                                                                 MetadataConstants.SERVICE_ROOT)
        print "STATUS::{status}".format(status=status)

        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1),
            Requirement(MetadataConstants.STORAGE_SERVICE, min=1),
            Requirement(MetadataConstants.VOLUME, min=1),
            Requirement(MetadataConstants.VOLUME_COLLECTION, min=1)
        ]
        preconditions = Preconditions(self.metadata_container, requirements)
        status = preconditions.validate(self.discovery_container)

        if status == ValidationStatus.FAILED:
            self.set_status_failed()
            return

        self.api_caller = ApiCaller(self.configuration)

        status = ValidationStatus.join_statuses(status, self.crud_fabrics_target())

        print "MESSAGE::%s overall status: %s" % (test_name, ColorPrinter.format_status(status))
Exemple #3
0
    def run(self):
        if self.metadata_container is None:
            return

        test_name = "Storage Services CRUD test"
        print "MESSAGE::%s starting" % test_name

        print "TEST_CASE::API crawling"
        api_explorer = ApiExplorer(self.metadata_container, self.configuration)
        self.discovery_container, status = api_explorer.discover(MetadataConstants.SERVICE_ROOT_URI,
                                                                 MetadataConstants.SERVICE_ROOT)
        print "STATUS::{status}".format(status=status)

        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1),
            Requirement(MetadataConstants.STORAGE_SERVICE, min=1),
            Requirement(MetadataConstants.LOGICAL_DRIVE_COLLECTION, min=1),
            Requirement(MetadataConstants.REMOTE_TARGET_COLLECTION, min=1),
            Requirement(MetadataConstants.LOGICAL_DRIVE, min=2)
        ]
        preconditions = Preconditions(self.metadata_container, requirements)
        status = preconditions.validate(self.discovery_container)

        if  status == ValidationStatus.FAILED:
            self.set_status_blocked()
            return

        self.api_caller = ApiCaller(self.configuration)

        status = ValidationStatus.join_statuses(status, self.crud_logical_drive())
        status = ValidationStatus.join_statuses(status, self.crud_remote_target())

        print "MESSAGE::%s overall status: %s" % (test_name, ColorPrinter.format_status(status))
    def run(self):
        print "TEST_CASE::API crawling"
        api_explorer = ApiExplorer(self.metadata_container, self.configuration)

        discovery_container, status = api_explorer.discover(
            MetadataConstants.SERVICE_ROOT_URI, MetadataConstants.SERVICE_ROOT)
        print "STATUS::{status}".format(status=status)

        print "TEST_CASE::Checking for mandatory entities"
        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1)
        ]
        preconditions = Preconditions(self.metadata_container, requirements)
        self.set_validation_status(preconditions.validate(discovery_container))

        validation_functions = [
            self.at_least_one_compute_module_in_pod,
            self.at_least_one_ethernet_switch_in_the_pod,
            self.ethernet_based_fabric_for_network_connectivity,
            self.secure_communication_channel, self.node_reset_support,
            self.manual_tests, self.validate_all_or_none_chassis_have_location,
            self.validate_path_using_contains_property,
            self.power_monitoring_support
        ]

        for validation_function in validation_functions:
            try:
                validation_function(discovery_container)
            except Exception as err:
                print "ERROR::Exception %s" % err
                self.set_validation_status(ValidationStatus.FAILED)

        print "SCREEN::Overall status: %s" % self.validation_status
 def test_not_found_in_non_empty_discovery_container(self):
     discovery_container = DiscoveryContainer(metadata_container=self.metadata_container)
     discovery_container.add_resource(ApiResource("id_1", 'netloc', {'@odata.id': '123',
                                                           'Oem':
                                                               {'Intel_RackScale':
                                                                    {'Location':
                                                                         {'Id': 1, 'ParentId': None}
                                                                     }
                                                                }
                                                 }, None))
     requirements = [
         Requirement("Chassis.Chassis", min=1, max=1)
     ]
     preconditions = Preconditions(self.metadata_container, requirements)
     self.assertEqual(ValidationStatus.FAILED, preconditions.validate(discovery_container))
 def test_fail_when_more_than_max(self):
     discovery_container = DiscoveryContainer(metadata_container=self.metadata_container)
     discovery_container.add_resource(ApiResource("id_1", 'netloc', {'@odata.id': '123',
                                                           '@odata.type' : '#Chassis.v1_0_0.Chassis',
                                                           'Oem':
                                                               {'Intel_RackScale':
                                                                    {'Location':
                                                                         {'Id': 1, 'ParentId': None}
                                                                     }
                                                                }
                                                 }, None))
     requirements = [
         Requirement("Chassis.Chassis", min=0, max=0)
     ]
     preconditions = Preconditions(self.metadata_container, requirements)
     self.assertEqual(ValidationStatus.FAILED, preconditions.validate(discovery_container))
    def run(self):
        if self.metadata_container is None:
            return

        print "TEST_CASE::API crawling"
        api_explorer = ApiExplorer(self.metadata_container, self.configuration)

        self.discovery_container, status = api_explorer.discover(
            MetadataConstants.SERVICE_ROOT_URI, MetadataConstants.SERVICE_ROOT)
        print "STATUS::{status}".format(status=status)

        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1),
        ]
        preconditions = Preconditions(self.metadata_container, requirements)
        status = preconditions.validate(self.discovery_container)

        self.api_caller = ApiCaller(self.configuration)
        self.crud_vlan()
Exemple #8
0
    def run(self):
        if self.metadata_container is None:
            return

        self.chosen_endpoint, self.chosen_zone, self.chosen_volume, self.chosen_volume_collection = (
            None, ) * 4
        self.initiator_endpoint, self.target_endpoint, self.zone_endpoint, self.created_volume = (
            None, ) * 4

        self.initiator_unique_name = self.generate_unique_names() if self.configuration.UniqueInitiatorName \
            != "generate" else self.configuration.UniqueInitiatorName
        self.target_unique_name = self.generate_unique_names() if self.configuration.UniqueTargetName \
            != "generate" else self.configuration.UniqueTargetName

        test_name = "Storage Services CRUD test with NVM Express (NVMe) Support"
        print "MESSAGE::%s starting" % test_name

        print "TEST_CASE::API crawling"
        api_explorer = ApiExplorer(self.metadata_container, self.configuration)
        self.discovery_container, status = api_explorer.discover(
            MetadataConstants.SERVICE_ROOT_URI, MetadataConstants.SERVICE_ROOT)
        print "STATUS::{status}".format(status=status)

        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1),
            Requirement(MetadataConstants.STORAGE_SERVICE, min=1),
            Requirement(MetadataConstants.VOLUME, min=1),
            Requirement(MetadataConstants.VOLUME_COLLECTION, min=1)
        ]
        preconditions = Preconditions(self.metadata_container, requirements)
        status = preconditions.validate(self.discovery_container)

        if status == ValidationStatus.FAILED:
            self.set_status_failed()
            return

        self.api_caller = ApiCaller(self.configuration)

        status = ValidationStatus.join_statuses(status, self.crud_nvme())

        print "MESSAGE::%s overall status: %s" % (
            test_name, ColorPrinter.format_status(status))
Exemple #9
0
    def run(self):
        if self.metadata_container is None:
            return

        print "TEST_CASE::API crawling"

        api_explorer = ApiExplorer(self.metadata_container, self.configuration)

        self.discovery_container, status = api_explorer.discover(MetadataConstants.SERVICE_ROOT_URI,
                                                                 MetadataConstants.SERVICE_ROOT)
        print "STATUS::{status}".format(status=status)

        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1),
        ]
        preconditions = Preconditions(self.metadata_container, requirements)
        status = preconditions.validate(self.discovery_container)

        self.api_caller = ApiCaller(self.configuration)
        self.crud_vlan()
Exemple #10
0
    def run(self):
        if self.metadata_container is None:
            return

        print "TEST_CASE::API crawling"
        api_explorer = ApiExplorer(self.metadata_container, self.configuration)

        discovery_container, status = api_explorer.discover(MetadataConstants.SERVICE_ROOT_URI,
                                                            MetadataConstants.SERVICE_ROOT)
        print "STATUS::{status}".format(status=status)

        print "TEST_CASE::Checking for mandatory entities"
        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1)
        ]
        preconditions = Preconditions(self.metadata_container, requirements)
        self.set_validation_status(preconditions.validate(discovery_container))

        validation_functions = [self.at_least_one_compute_module_in_pod,
                                self.at_least_one_ethernet_switch_in_the_pod,
                                self.ethernet_based_fabric_for_network_connectivity,
                                self.secure_communication_channel,
                                self.node_reset_support,
                                self.manual_tests,
                                self.validate_all_or_none_chassis_have_location,
                                self.validate_path_using_contains_property,
                                self.power_monitoring_support]

        for validation_function in validation_functions:
            try:
                validation_function(discovery_container)
            except Exception as err:
                cts_error("Exception {err:exception}", err=err)
                self.set_validation_status(ValidationStatus.FAILED)

        print "SCREEN::Overall status: %s" % self.validation_status
Exemple #11
0
class MetadataGetValidator:
    def __init__(self, metadata_container, requirements=None, ignore=None, skip_check_predicate=None):
        """
        :type metadata_container: cts_core.metadata.metadata_container.MetadataContainer
        :type requirements: list(Requirement)
        """
        self._metadata_container = metadata_container
        self._preconditions = Preconditions(metadata_container, requirements, skip_check_predicate=skip_check_predicate)
        self._ignore = ignore if ignore is not None else set()

    def validate(self, discovery_container):
        """
        :type discovery_container: cts_core.discovery.discovery_container.DiscoveryContainer
        :rtype: cts_core.validation.validation_status.ValidationStatus
        """
        print "TEST_CASE::Checking for mandatory entities"
        status = self._preconditions.validate(discovery_container)
        print "STATUS::%s" % status

        resources = discovery_container.itervalues()
        count = len(discovery_container)
        for idx, api_resource in enumerate(resources):
            print "TEST_CASE::%s" % api_resource.odata_id
            print "MESSAGE::[%5d/%5d] Checking %s : %s" % (
            idx + 1, count, api_resource.odata_id, api_resource.odata_type)

            api_resource_status = self.validate_single_entity(api_resource)
            print "STATUS::%s" % api_resource_status
            status = ValidationStatus.join_statuses(status, api_resource_status)

        print "SCREEN::Overall status: %s" % status
        return status

    def get_validated_information(self, discovery_container):
        resources = discovery_container.itervalues()

        ValidatedEntity = collections.namedtuple('ValidatedEntity', 'test_case count checking message status')
        list_of_validated_elements = []

        for idx, api_resource in enumerate(resources):
            api_resource_status = self.validate_single_entity(api_resource)

            list_of_validated_elements.append(ValidatedEntity(test_case=api_resource.odata_id,
                                                              count=idx + 1,
                                                              checking=api_resource.odata_id,
                                                              message=api_resource.odata_type,
                                                              status=api_resource_status))
        return list_of_validated_elements

    def validate_single_entity(self, api_resource):
        """
        :type api_resource: cts_core.discovery.api_resource.ApiResourceProxy
        :rtype: str
        """
        if not api_resource.body:
            cts_warning("{odata_id:id} can't be analyzed - body is empty", api_resource.odata_id)
            return ValidationStatus.FAILED

        try:
            entity_details = self._metadata_container.entities[api_resource.odata_type]
        except KeyError:
            cts_error("Unable to find definition of entity {expected_odata_type} for resource {odata_id:id}",
                      expected_odata_type=api_resource.odata_type, odata_id=api_resource.odata_id)
            return ValidationStatus.FAILED

        return entity_details.validate(resource=api_resource.body,
                                       resource_path=api_resource.odata_id,
                                       odata_type=api_resource.odata_type)
Exemple #12
0
    def crud_vlan(self):
        """
        Test is trying to perform CRUD (create, read, update, delete) operations on a VLAN Network Interface resource
        """
        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1),
            Requirement(MetadataConstants.VLAN_NETWORK_INTERFACE_COLLECTION,
                        min=1),
        ]
        preconditions = Preconditions(self.metadata_container, requirements)
        status = preconditions.validate(self.discovery_container)

        if status is ValidationStatus.PASSED_WITH_WARNINGS:
            cts_warning(
                "Without some components it is not possible to perform tests.")
            print "STATUS::{status}".format(status=status)
            return ValidationStatus.PASSED_WITH_WARNINGS

        if status is ValidationStatus.FAILED:
            return ValidationStatus.BLOCKED

        vlan_network_interface_collections = \
            dict(self.discovery_container.get_resources(MetadataConstants.VLAN_NETWORK_INTERFACE_COLLECTION,
                 any_child_version=True))

        chosen_vlan_network_interface_collection = None
        new_vlan_id = 0

        for vlan_network_interface_collection in vlan_network_interface_collections:
            present_vlan_ids = []
            vlan_network_interfaces = \
                dict(self.discovery_container.
                     get_resources(MetadataConstants.VLAN_NETWORK_INTERFACE,
                                   constraints=[from_collection(vlan_network_interface_collection)],
                                   any_child_version=True))

            if len(vlan_network_interfaces) > MAX_VLAN_ID - MIN_VLAN_ID:
                continue
            for resource_link, resource in vlan_network_interfaces.iteritems():
                try:
                    present_vlan_ids.append(resource.body["VLANId"])
                except:
                    cts_error("Inorrect resource {resource_link:id} structure",
                              **locals())

            for possible_id in range(MIN_VLAN_ID, MAX_VLAN_ID + 1):
                if possible_id not in present_vlan_ids:
                    chosen_vlan_network_interface_collection = vlan_network_interface_collection
                    new_vlan_id = possible_id
                    break

            if chosen_vlan_network_interface_collection:
                break

        if chosen_vlan_network_interface_collection:
            print "MESSAGE::CRUD test will be performed on vlan network interface collection %s" % \
                  chosen_vlan_network_interface_collection
        else:
            cts_warning('No free VLAN id available')
            return ValidationStatus.BLOCKED

        self.post_vlan_payload = dict(
            VLANId=new_vlan_id,
            VLANEnable=True,
            Oem=dict(Intel_RackScale=dict(Tagged=True)))

        if self._test_case_create_vlan(
                chosen_vlan_network_interface_collection):
            self._test_case_get_created_vlan()
            self._test_case_delete_created_vlan()
            self._test_case_get_deleted_vlan()
Exemple #13
0
    def crud_static_mac(self):
        """
            Test is trying to perform CRUD (create, read, update, delete) operations on a Static MAC resource
            """
        def _find_unique_mac_address_vlan_id_pair(present_pairs):
            MAX_ADDRESS = 281474976710656 - 1  # 16^12 - 1

            def address_to_string(address):
                parts = []
                for _ in range(6):
                    parts.append(address % 256)
                    address = address / 256
                return ":".join("{0:02x}".format(part)
                                for part in parts).upper()

            address = 0
            vlan_id = MIN_VLAN_ID
            while (address_to_string(address), vlan_id) in present_pairs:
                address = address + 1
                if address > MAX_ADDRESS:
                    address = 0
                    vlan_id = vlan_id + 1
                    if vlan_id > MAX_VLAN_ID:
                        return None
            return (address_to_string(address), vlan_id)

        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1),
            Requirement(MetadataConstants.ETHERNET_SWITCH, min=1),
            Requirement(MetadataConstants.ETHERNET_SWITCH_PORT, min=1),
            Requirement(MetadataConstants.STATIC_MAC_COLLECTION, min=1),
        ]
        preconditions = Preconditions(self.metadata_container, requirements)

        if preconditions.validate(
                self.discovery_container) == ValidationStatus.FAILED:
            return ValidationStatus.BLOCKED

        static_mac_collections = \
            dict(self.discovery_container.get_resources(MetadataConstants.STATIC_MAC_COLLECTION,
                 any_child_version=True))

        chosen_static_mac_collection = static_mac_collections.keys()[0]

        print "MESSAGE::\nMESSAGE::Static MAC CRUD test will be performed on collection: {}"\
            .format(chosen_static_mac_collection)

        present_vlan_id_mac_address_pairs = []

        for resource_link, resource in self.discovery_container.get_resources(
                MetadataConstants.STATIC_MAC,
                any_child_version=True,
                constraints=[from_collection(chosen_static_mac_collection)]):
            try:
                present_vlan_id_mac_address_pairs.append(
                    (resource.body["MACAddress"], resource.body["VLANId"]))
            except Exception as err:
                cts_warning(
                    "{link:id} Incorrect resource structure; err={err:exception}",
                    link=resource_link,
                    err=err)

        initial_values = _find_unique_mac_address_vlan_id_pair(
            present_vlan_id_mac_address_pairs)

        if not initial_values:
            cts_error("Cannot create a Static MAC on the chosen collection - "
                      "all MAC addresses and VLAN ids already occupied")
            return ValidationStatus.BLOCKED

        self.post_static_mac_payload = dict(MACAddress=initial_values[0],
                                            VLANId=initial_values[1])

        # PSME does not support patching Static MAC
        if self._test_case_create_static_mac(chosen_static_mac_collection):
            self._test_case_get_created_static_mac()
            self._test_case_delete_created_static_mac()
            self._test_case_get_deleted_static_mac()
Exemple #14
0
class MetadataPatchValidator(SkipListMixin, PatchNontrivialPropertyMixin):
    def __init__(self, metadata_container, configuration, strategy, requirements=None, skip_list=None):
        """
        :type metadata_container: cts_core.metadata.metadata_container.MetadataContainer
        :type configuration: cts_framework.configuration.configuration.Configurations
        :type strategy: cts_core.validation.patch.patching_strategy.PatchingStrategy
        :type requirements: list
        """
        self._metadata_container = metadata_container
        self._api_caller = ApiCaller(configuration)
        self._strategy = strategy
        self._preconditions = Preconditions(metadata_container, requirements)
        self._fast_mode = getenv('CTS_PATCH_ONE_PER_TYPE')
        self._types_patched = set()
        self._skip_list = skip_list

        if self._fast_mode:
            cts_warning("Test results may be UNRELIABLE - PATCHING ONLY ONE RESOURCE OF EACH TYPE!")

        self._endpoints_and_keys_to_ignore = []
        if self._skip_list:
            cts_warning("Test results may be UNRELIABLE - Some elements presented on REST API will be IGNORED!")
            self._endpoints_and_keys_to_ignore = self._load_ignore_list_file(skip_list)

    def validate(self, discovery_container):
        self.discovery_container = discovery_container
        status = self._check_mandatory_entities()

        for api_resource in self._enumerate_resources(discovery_container):
            context = Context(api_resource)
            resource_status = self._validate_resource(context)
            self._print_resource_footer(api_resource, context, resource_status)
            redfish_uri_compliance = discovery_container.validate_redfish_uris_consistency(api_resource, self._metadata_container)
            status = ValidationStatus.join_statuses(status, resource_status, redfish_uri_compliance)

        print "SCREEN::Overall status: %s" % status
        return status

    @staticmethod
    def _load_ignore_list_file(path_to_file=None):
        if not path_to_file:
            return []

        endpoints_to_ignore = {}
        with open(path_to_file, 'r') as f:
            for line in f.readlines():
                if '::' in line:
                    pre_formatted_value = line.split('::')
                else:
                    cts_warning("{line} is not proper format, add ::[*] to ignore entire endpoint")
                    continue
                list_of_endpoints_key = MetadataPatchValidator.__parse_into_a_list(pre_formatted_value[1])
                endpoints_to_ignore[pre_formatted_value[0]] = [value for value in list_of_endpoints_key.split(',')]
            print(endpoints_to_ignore)
        return endpoints_to_ignore

    @staticmethod
    def __parse_into_a_list(text):
        return text.replace('\n', '').replace('[', '').replace(']', '')

    def _enumerate_resources(self, discovery_container):
        count = len(discovery_container)
        resource_urls = sorted(self.discovery_container.keys())
        for idx, url in enumerate(resource_urls):
            api_resource = discovery_container[url]
            self._print_progress(api_resource, count, idx)
            if self.skip_resource(api_resource):
                continue
            if self._fast_mode:
                generalized_id = discovery_container._generalize_url(api_resource.url)
                if generalized_id in self._types_patched:
                    print "MESSAGE::skipping... other {} has already been tested". \
                        format(generalized_id)
                    continue
                else:
                    self._types_patched.add(generalized_id)

            yield api_resource

    @staticmethod
    def _print_progress(api_resource, count, idx):
        print "SCREEN::" + "-" * 120
        progress = "[%5d/%5d]" % (idx + 1, count)
        print "MESSAGE::%s - %s : %s" % \
              (progress, api_resource.odata_id, api_resource.odata_type)

    def _check_mandatory_entities(self):
        print "TEST_CASE::Checking for mandatory entities"
        status = self._preconditions.validate(self.discovery_container)
        print "STATUS::%s" % status
        return status

    def _validate_resource(self, context):
        """
        :type context: Context
        :rtype: str
        """
        api_resource = context.api_resource

        if self._metadata_container.to_be_ignored(api_resource.odata_type):
            return ValidationStatus.PASSED

        # Load Ignore Elements list and verify.
        properties_to_skip = []
        if api_resource.odata_id in self._endpoints_and_keys_to_ignore:
            properties_to_skip = self._endpoints_and_keys_to_ignore[api_resource.odata_id]

            if properties_to_skip[0] == '*':
                print('MESSAGE::Skipping patching {}. This odata_id is present on list of endpoints to ignore'.
                      format(api_resource.odata_id))
                return ValidationStatus.PASSED_WITH_WARNINGS
            else:
                print('MESSAGE::This properties from {} will be skipped from patching.'.
                      format(api_resource.odata_id))
                print('MESSAGE::Elements are on list to ignore:')
                for idp, property in enumerate(properties_to_skip, 1):
                    print('MESSAGE::\t{idp}. {property}'.format(idp=idp,
                                                                property=property))

        # do not attempt patch Virtual systems
        if "SystemType" in api_resource.body and api_resource.body["SystemType"] == "Virtual":
            print "MESSAGE::Skipping patching of a virtual system {}".format(api_resource.odata_id)
            return ValidationStatus.PASSED

        try:
            properties_list = [property for property in
                               self._metadata_container.entities[api_resource.odata_type].properties.values()
                               if str(property) not in properties_to_skip]

            try:
                if len(properties_to_skip):
                    return self._validate_property_list(context,
                                                        list(),
                                                        properties_list,
                                                        properties_to_skip)

                return self._validate_property_list(context,
                                                    list(),
                                                    properties_list)
            except SystemExit:
                raise
            except:
                cts_error("Unhandled exception {exception:stacktrace} "
                          "while handling resource {odata_id:id}",
                          exception=format_exc(),
                          odata_id=api_resource.odata_id)
                return ValidationStatus.FAILED
        except KeyError:
            cts_error("Unable to find definition of entity type {odata_type}",
                      odata_type=api_resource.odata_type)
            return ValidationStatus.FAILED

    def _validate_property_list(self, context, variable_path, property_list, ignore_list=[]):
        """
        :type context: Context
        :type variable_path: list [str or int]
        :type property_list: list[cts_core.metadata.model.property.Property]
        :rtype: str
        """
        api_resource = context.api_resource

        status = ValidationStatus.PASSED
        # use local copy - api resource will be modified while test is executed
        local_property_list = list(property_list)

        for property_description in local_property_list:

            if not len(ignore_list) or (len(ignore_list) and str(property_description) not in sum([ignored_property.split("->") for ignored_property in ignore_list], [])):
                status = ValidationStatus.join_statuses(self._validate_property(context,
                                                                                variable_path,
                                                                                property_description,
                                                                                ignore_list=[ignored_property for ignored_property in ignore_list]),
                                                        status)
                continue
            else:
                if len([ignored_subproperty for ignored_subproperty in ignored_property.split("->") for ignored_property in ignore_list]) > 1:
                   status = ValidationStatus.join_statuses(self._validate_property(context,
                                                                                   variable_path,
                                                                                   property_description,
                                                                                   ignore_list=[ignored_property.split("->")[1] for ignored_property in ignore_list]),
                                                           status)
                else:
                    status = ValidationStatus.join_statuses(self._validate_property(context,
                                                                                    variable_path,
                                                                                    property_description,
                                                                                    ignore_list=[ignored_property for ignored_property in ignore_list]),
                                                            status)

        # validate additional properties
        try:
            body = api_resource.get_value_from_path(variable_path)
        except KeyError as key:
            cts_warning("Unable to access {odataid:id}->{path}. Key={key}",
                        odataid=api_resource.odata_id, path="->".join(variable_path), key=key)
            body = None

        if body is not None:
            all_properties = set(body.keys())
            known_properties = set([property.name for property in local_property_list])
            additional_properties = all_properties - known_properties
            for additional_property_name in additional_properties:
                property_value = body[additional_property_name]
                if isinstance(property_value, dict) and "@odata.type" in property_value:
                    raw_soup = """
                    <Property Name="{name}" Type="{type}">
                        <Annotation Term="OData.Permissions"
                                    EnumMember="OData.Permission/ReadWrite"/>
                    </Property>
                    """
                    soup = BeautifulSoup(raw_soup.format(name=additional_property_name,
                                                         type=get_odata_type(property_value)),
                                         "lxml").find_all("property")[0]

                    adhoc_description = Property(self._metadata_container, None, soup)
                    status = ValidationStatus.join_statuses(
                        self._validate_property(context,
                                                variable_path,
                                                adhoc_description),
                        status)

        return status

    def _validate_property(self, context, variable_path, property_description,
                           skip_collection=False, ignore_list=[]):
        """
        :type context: Context
        :type variable_path: list [str or int]
        :type property_description: cts_core.metadata.model.property.Property
        :type skip_collection: bool
        :rtype: str
        """
        applicability = None
        if property_description.patch_status == PatchStatus.NONTRIVIAL:
            applicability, validation_result = self.handle_nontrivial(context, variable_path, property_description)
            if applicability == ApplicabilityTestResult.MATCHED:
                return validation_result
            if applicability in [ApplicabilityTestResult.NOT_MATCHED, ApplicabilityTestResult.SKIP_PROPERTY]:
                return validation_result

        api_resource = context.api_resource

        if not skip_collection:
            variable_path = variable_path + [property_description.name]

        try:
            property_body = api_resource.get_value_from_path(variable_path)
            if len(ignore_list) == 1 and ignore_list[0] in property_body:
                path_s = "->".join([str(segment) for segment in variable_path])
                cts_message("Patching %s->%s" % (api_resource.odata_id, path_s))
                cts_message("This property {ignore_element} is marked in IgnoredElement list as element to skip".format(
                    ignore_element=ignore_list[0]
                ))
        except KeyError as error:
            if property_description.is_required:
                path_s = "->".join([str(segment) for segment in variable_path])
                print "TEST_CASE::Patching %s->%s" % (api_resource.odata_id, path_s)
                cts_error("Unable to patch {error}", error=error)
                status = ValidationStatus.FAILED
                print "STATUS::%s" % status
            else:
                status = ValidationStatus.PASSED

            return status

        if property_description.is_collection and not skip_collection:
            status = ValidationStatus.PASSED

            for path_element, _ in enumerate(property_body):
                status = \
                    ValidationStatus.join_statuses(status,
                                                   self._validate_property(context,
                                                                           variable_path + [path_element],
                                                                           property_description,
                                                                           skip_collection=True,
                                                                           ignore_list=ignore_list))
            return status
        else:
            try:
                if self._metadata_container.types[property_description.type].type_category \
                        == MetadataTypeCategories.COMPLEX_TYPE:
                    return self._validate_property_list(context,
                                                        variable_path,
                                                        self._metadata_container.types[property_description.type].
                                                        properties.itervalues(),
                                                        ignore_list)
                else:
                    if str(property_description) in ignore_list:
                        return ValidationStatus.PASSED
                    return self._validate_leaf_property(context,
                                                        variable_path,
                                                        property_description)
            except KeyError as key:
                cts_error("Unable to find definition of type {type} referenced by {odata_id:id}",
                          type=key,
                          odata_id=api_resource.odata_id)
                return ValidationStatus.FAILED

    def _validate_leaf_property(self, context, variable_path, property_description):
        """
        :type context: Context
        :type variable_path: list [str or int]
        :type property_description: cts_core.metadata.model.property.Property
        :rtype: str
        """
        if property_description.patch_status in (PatchStatus.NOT_PATCHABLE, PatchStatus.NOT_DEFINED):
            return ValidationStatus.PASSED

        if property_description.patch_status == PatchStatus.NONTRIVIAL:
            applicability, validation_result = self.handle_nontrivial(context, variable_path, property_description)
            if applicability == ApplicabilityTestResult.MATCHED:
                return validation_result
            if applicability in [ApplicabilityTestResult.NOT_MATCHED, ApplicabilityTestResult.SKIP_PROPERTY]:
                return validation_result

        return self._validate_patchable_property(context, variable_path, property_description)

    def _validate_patchable_property(self, context, variable_path, property_description):
        """
        :type context: Context
        :type variable_path: list [str or int]
        :type property_description: cts_core.metadata.model.property.Property
        :rtype: str
        """
        api_resource = context.api_resource
        verify_only_response = property_description.patch_status == PatchStatus.WRITE_ONLY

        new_values_list = property_description.generate_values(property_description.annotations)
        allowed_values = api_resource.get_allowable_from_path(variable_path)
        if allowed_values:
            new_values_list = [value for value in new_values_list if value in allowed_values]

        collection = None
        if property_description.is_collection and len(variable_path) > 1:
            collection = api_resource.get_value_from_path(variable_path[:-1])

        try:
            value_pre = current_value = api_resource.get_value_from_path(variable_path)
        except KeyError:
            return ValidationStatus.PASSED

        total_status = ValidationStatus.PASSED

        for new_value in new_values_list:
            if new_value != current_value:
                status, current_value = \
                    self._patch_and_verify_property(context,
                                                    variable_path,
                                                    current_value,
                                                    new_value,
                                                    verify_only_response,
                                                    collection=collection,
                                                    property_description_type=property_description.name)

                total_status = ValidationStatus.join_statuses(total_status, status)

                if current_value is None:
                    try:
                        value_pre = current_value = api_resource.get_value_from_path(variable_path)
                    except KeyError:
                        return total_status

        if not allowed_values or value_pre in allowed_values:
            total_status = ValidationStatus.join_statuses(total_status,
                                                          self._restore_property(context,
                                                                                 value_pre,
                                                                                 variable_path,
                                                                                 collection=collection))
        else:
            print "MESSAGE::Original value '{value_pre}' is illegal. Will not _restore_property" \
                .format(value_pre=dumps(value_pre))

        return total_status

    def _patch_and_verify_property(self, context, variable_path, current_value, value_requested,
                                   verify_only_response=False, collection=None, property_description_type=None):
        """
        :type context: Context
        :type variable_path: list [str or int]
        :type current_value: *
        :type value_requested: *
        :type verify_only_response: bool
        """

        property = "%s[%s]" \
                   % (context.api_resource.odata_id, "->".join([str(path) for path in
                                                                variable_path]))

        patch_applied, status, validation_status = self._patch_property(context,
                                                                        property,
                                                                        current_value,
                                                                        value_requested,
                                                                        variable_path,
                                                                        collection=collection)
        # only _verify_property if the PATCH request succeeded and is not only writeable
        if status == RequestStatus.SUCCESS and patch_applied:
            if not verify_only_response:
                verify_status, current_value = self._verify_property(context,
                                                                     patch_applied,
                                                                     property,
                                                                     current_value,
                                                                     value_requested,
                                                                     variable_path,
                                                                     property_description_type=property_description_type)
                validation_status = ValidationStatus.join_statuses(validation_status, verify_status)
            else:
                validation_status = ValidationStatus.join_statuses(validation_status, ValidationStatus.PASSED)

        return validation_status, current_value

    def _patch_property(self, context, property_name, value_pre, value_requested, variable_path, collection=None):
        """
        Issues Patch request to update property to new requested value. Tries to determine
        if patch has been applied and sets validation status accordingly.

        :type context: Context
        :type property_name: str
        :type value_pre: *
        :type value_requested: *
        :type variable_path: list [str or int]
        :rtype: (bool, bool, str)
        """

        api_resource = context.api_resource

        print "TEST_CASE:: Patch %s := %s" % (property_name, str(value_requested))
        context.register_patch_attempt(property_name, str(value_requested))

        data = create_data(variable_path, value_requested, collection)

        status, status_code, response_body, headers = self._api_caller.patch_resource(
            api_resource.url,
            self.discovery_container,
            payload=data,
            acceptable_return_codes=self._strategy.allowable_return_codes)

        patch_applied = self._strategy.was_patch_applied(status_code)

        if status != RequestStatus.SUCCESS or not patch_applied:
            validation_status = ValidationStatus.FAILED
        else:
            validation_status = ValidationStatus.PASSED
        print "STATUS::%s" % validation_status

        return patch_applied, status, validation_status

    def _verify_property(self, context, patch_applied, property_name, value_pre,
                         value_requested, variable_path, property_description_type):
        """
        Verifies if property value after patch is as expected.
        Should be equal to original value if patch has not been applied.
        Should be equal to requested value if patch has been applied.

        :type context: Context
        :type patch_applied: bool
        :type property_name: str
        :type value_pre: *
        :type value_requested: *
        :type variable_path: list[str or int]
        :rtype: (str, *)
        """

        api_resource = self.discovery_container[context.api_resource.url] # refresh reference to the resource

        validation_status = ValidationStatus.PASSED
        print "TEST_CASE::Verify %s (expected: %s)" % (property_name, str(value_requested))

        try:
            value_post = api_resource.get_value_from_path(variable_path)

            if self._validate_corner_cases(property_description_type, value_post, value_requested) and patch_applied:
                print "STATUS::%s" % validation_status
                return validation_status, value_post

            if value_requested != value_post and patch_applied:
                cts_error("{odata_id:id} Unexpected value after patching {property} - " +
                          "IS : {post:ignore}, EXPECTED : {requested:ignore}",
                          odata_id=api_resource.odata_id,
                          property=property_name,
                          post=value_post,
                          requested=value_requested)
                validation_status = ValidationStatus.FAILED
            elif value_pre != value_post and not patch_applied:
                cts_error("{odata_id:id} Service reported that the patch has not been applied, "
                          "but '{property}' has changed unexpectedly "
                          "from {pre:ignore} to {post:ignore}",
                          odata_id=api_resource.odata_id,
                          property=property_name,
                          pre=value_pre,
                          post=value_post)
                validation_status = ValidationStatus.FAILED
        except ValueNotFound:
            validation_status = ValidationStatus.FAILED

        print "STATUS::%s" % validation_status
        return validation_status, value_post

    def _restore_property(self, context, value_pre, variable_path, collection=None):
        """
        Final patching that restores original value of the property.

        :type context: Context
        :type value_pre: *
        :type variable_path: list [int or str]
        :return: str
        """
        api_resource = context.api_resource

        validation_status = ValidationStatus.PASSED

        property = "%s" % ("->".join([str(path) for path in variable_path]))
        print "TEST_CASE::Restore %s := %s" % (property, dumps(value_pre))

        data = create_data(variable_path, value_pre, collection)
        status, status_code, response_body, headers = self._api_caller.patch_resource(
            api_resource.url,
            self.discovery_container,
            payload=data,
            acceptable_return_codes=self._strategy.allowable_return_codes)
        if status != RequestStatus.SUCCESS:
            cts_error("{odata_id:id} Restoring {property} failed. status code {code}",
                      odata_id=api_resource.odata_id,
                      property=property,
                      code=status_code)
            validation_status = ValidationStatus.FAILED
        print "STATUS::%s" % validation_status
        return validation_status

    def _print_resource_footer(self, api_resource, context, resource_status):
        if not context.attempted_patches:
            if resource_status not in [ValidationStatus.PASSED,
                                       ValidationStatus.PASSED_WITH_WARNINGS]:
                # 0 attempted patches means 0 test cases.
                # FAILED means error (visible in the log) happened during validation,
                # but not associated with any TEST_CASE
                # For the purpose of showing failing test case, we produce dummy TEST_CASE here
                print "TEST_CASE::Patching %s" % api_resource.odata_id
                print "STATUS::%s" % resource_status
            else:
                print "MESSAGE::[{resource} - 0 patchable properties found]". \
                    format(resource=api_resource.odata_id)

    def _validate_corner_cases(self, property_description_type, value_post, value_requested):
        if property_description_type == "MACAddress":
            return self._validate_canonical_form_of_mac_address(value_post, value_requested)
        return False

    def _validate_canonical_form_of_mac_address(self, value_post, value_requested):
        post = self._mac_address_standarizer(value_post)
        req = self._mac_address_standarizer(value_requested)

        return int(post, 16) == int(req, 16)

    @staticmethod
    def _mac_address_standarizer(mac_address):
        replace_dict = {":": "", "-": ""}
        for i, j in replace_dict.iteritems():
            mac_address = mac_address.replace(i, j)

        return mac_address
Exemple #15
0
    def crud_vlan(self):
        """
        Test is trying to perform CRUD (create, read, update, delete) operations on a VLAN Network Interface resource
        """
        requirements = [
            Requirement(MetadataConstants.SERVICE_ROOT, min=1, max=1),
            Requirement(MetadataConstants.VLAN_NETWORK_INTERFACE_COLLECTION, min=1),
        ]
        preconditions = Preconditions(self.metadata_container, requirements)
        status = preconditions.validate(self.discovery_container)

        if status is ValidationStatus.PASSED_WITH_WARNINGS:
            cts_warning("Without some components it is not possible to perform tests.")
            print "STATUS::{status}".format(status=status)
            return ValidationStatus.PASSED_WITH_WARNINGS

        if status is ValidationStatus.FAILED:
            return ValidationStatus.BLOCKED

        vlan_network_interface_collections = \
            dict(self.discovery_container.get_resources(MetadataConstants.VLAN_NETWORK_INTERFACE_COLLECTION,
                 any_child_version=True))

        chosen_vlan_network_interface_collection = None
        new_vlan_id = 0

        for vlan_network_interface_collection in vlan_network_interface_collections:
            try:
                parent_port = get_parent_odata_id(vlan_network_interface_collection)
                parent_port_id = self.discovery_container[parent_port].body["PortId"]
            except:
                continue

            # For arista setup, only ever try adding VLANs on the main /1 port
            if parent_port_id.endswith("/2") or parent_port_id.endswith("/3") or parent_port_id.endswith("/4"):
                continue

            present_vlan_ids = []
            vlan_network_interfaces = \
                dict(self.discovery_container.
                     get_resources(MetadataConstants.VLAN_NETWORK_INTERFACE,
                                   constraints=[from_collection(vlan_network_interface_collection)],
                                   any_child_version=True))

            if len(vlan_network_interfaces) > MAX_VLAN_ID - MIN_VLAN_ID:
                print "MESSAGE::No free VLAN id available on collection {}".format(vlan_network_interface_collection)
                continue

            for resource_link, resource in vlan_network_interfaces.iteritems():
                    try:
                        present_vlan_ids.append(resource.body["VLANId"])
                    except:
                        cts_error("Inorrect resource {resource_link:id} structure", **locals())

            for possible_id in range(MIN_VLAN_ID, MAX_VLAN_ID + 1):
                if possible_id not in present_vlan_ids:
                    chosen_vlan_network_interface_collection = vlan_network_interface_collection
                    new_vlan_id = possible_id
                    break

            if chosen_vlan_network_interface_collection:
                break

        if chosen_vlan_network_interface_collection:
            print "MESSAGE::CRUD test will be performed on vlan network interface collection %s" % \
                  chosen_vlan_network_interface_collection
        else:
            cts_warning('No free VLAN id available on any of the suitable ports (perhaps there are no suitable ports)')
            return ValidationStatus.BLOCKED

        self.post_vlan_payload = dict(VLANId=new_vlan_id,
                                      VLANEnable=True,
                                      Oem=dict(
                                        Intel_RackScale=dict(
                                            Tagged=True
                                        )))

        if self._test_case_create_vlan(chosen_vlan_network_interface_collection):
            self._test_case_get_created_vlan()
            self._test_case_delete_created_vlan()
            self._test_case_get_deleted_vlan()