Exemplo n.º 1
0
 def _parse_ignore_types(self):
     if len(self.configuration.IgnoreTypes):
         self._ignore_types = set([type.strip() for type in self.configuration.IgnoreTypes.split(",")])
         print "WARNING::CTS is configured to skip properties and entities of the following " \
               "types:"
         for ignore_type in self._ignore_types:
             cts_warning("\t- {ignore_type}", ignore_type=ignore_type)
Exemplo n.º 2
0
    def __init__(self, url, netloc, json_body, expected_odata_type):
        """
        :param url: URL of api resource to add
        :type url: str
        :param netloc: address and port of the API service that owns the resource
        :type netloc: str
        :param json_body: Python representation of API resource
        :type json_body: OrderedDict
        :param expected_odata_type: When CTS issues GET request, it usually expects a resource with a specific
        @odata.type
        :type expected_odata_type: str
        """
        self.url = url
        self.netloc = netloc
        self.odata_id = json_body.get(".".join([ODATA, ID]))
        self.odata_type = sanitize_odata_type(
            json_body.get(".".join([ODATA, TYPE])))
        if self.odata_type is None:
            self.odata_type = sanitize_odata_type(expected_odata_type)
        self.expected_odata_type = sanitize_odata_type(expected_odata_type)
        self.body = json_body
        self.location = ApiResource._get_location(json_body)
        self.contains = ApiResource._get_contains(json_body)
        self.contained_by = ApiResource._get_contained_by(json_body)
        self.parent_url = self._get_parent_url()

        if not self.odata_id:
            cts_warning(
                "@odata.id property not present in {body:response_body}. Setting 'None'.",
                body=json_body)

        if not self.odata_type:
            cts_warning(
                "@odata.type property not present in {body:response_body}. Setting 'None'.",
                body=json_body)
Exemplo n.º 3
0
    def _load_metadata(self):
        qualifiers, services = self.get_services_and_qualifiers()

        metadata_manager = MetadataManager(qualifiers,
                                           ignore_types=self.ignore_types,
                                           map_types=self.map_types)
        if self.configuration.MetadataDirectory:
            cts_warning("CTS is configured to use custom metadata from: {dir}",
                        dir=self.configuration.MetadataDirectory)
            metadata_container = self._load_custom_metadata(
                metadata_manager, self.configuration.MetadataDirectory)
        elif self.configuration.RedfishMetadata:
            cts_warning(
                "CTS is configured to use Redfish metadata: {metadata_version}",
                metadata_version=self.configuration.RedfishMetadata)
            metadata_container = \
                self._load_custom_metadata(metadata_manager, '/'.join((Constants.METADATA_REDFISH_DIR,
                                                                      str(self.configuration.RedfishMetadata))))
        else:
            metadata_container = self._load_built_in_metadata(
                metadata_manager, services)

        if metadata_container is not None:
            metadata_container.print_types()
        return metadata_container
Exemplo n.º 4
0
    def _process_namespace(self, schema):
        """
        :type schema: bs4.element.Tag
        """
        try:
            namespace_name = schema[NAMESPACE]
        except KeyError:
            cts_warning(
                "Incorrect schema definition {schema_definition},\n missing of namespace name",
                schema_definition=str(schema))

        for entity_soup in schema.find_all(ENTITY_TYPE):
            entity = Entity(self._metadata_container, namespace_name, entity_soup, self.qualifiers)
            self._metadata_container.entities[entity.name] = entity

        for type_soup in schema.find_all(ENUM_TYPE):
            enum_type = EnumType(self._metadata_container, namespace_name, type_soup,
                                 self.qualifiers)
            self._metadata_container.types[enum_type.name] = enum_type

        for type_soup in schema.find_all(COMPLEX_TYPE):
            complex_type = ComplexType(self._metadata_container, namespace_name, type_soup,
                                       self.qualifiers)
            self._metadata_container.types[complex_type.name] = complex_type

        for type_soup in schema.find_all(TYPE_DEFINITION):
            type_definition = TypeDefinition(self._metadata_container, namespace_name, type_soup,
                                             self.qualifiers)
            self._metadata_container.types[type_definition.name] = type_definition
Exemplo n.º 5
0
    def request(cls, http_method, url, **kwargs):
        if not cls.replay_mode_on():
            return None
        if cls._cursor < len(cls._http_request_ids):
            _method, _url, _request, _response, _status_code = HttpRequestDAO.retrieve(cls._http_request_ids[cls._cursor])
            if url != _url:
                cts_error("{url:id} requested url={url} is different than url={_url} " +
                          "that is stored in the database. Quitting Replay mode.",
                          **locals())
                sys.exit("Replay Error")

            try:
                request_obj_from_db = json.loads(_request)
            except (JSONDecodeError, ValueError) as err:
                request_obj_from_db = {}

            if not dictionaries_equal(kwargs, request_obj_from_db):
                request = json.dumps(sanitize_json(kwargs))
                cts_warning("{url:id} request {request} is different than request {_request} from database. ",
                            url=url, request=sanitize_json(kwargs), _request=sanitize_json(request_obj_from_db))
                sys.exit("Replay Error")

            cls._cursor += 1
            return _response

        else:
            cts_error("Reached end of recording. Quitting Replay mode.")
            cls._replay_mode_on = False
            return None
Exemplo n.º 6
0
    def __init__(self, url, netloc, json_body, expected_odata_type):
        """
        :param url: URL of api resource to add
        :type url: str
        :param netloc: address and port of the API service that owns the resource
        :type netloc: str
        :param json_body: Python representation of API resource
        :type json_body: OrderedDict
        :param expected_odata_type: When CTS issues GET request, it usually expects a resource with a specific
        @odata.type
        :type expected_odata_type: str
        """
        self.url = url
        self.netloc = netloc
        self.odata_id = json_body.get(".".join([ODATA, ID]))
        self.odata_type = sanitize_odata_type(json_body.get(".".join([ODATA, TYPE])))
        if self.odata_type is None:
            self.odata_type = sanitize_odata_type(expected_odata_type)
        self.expected_odata_type = sanitize_odata_type(expected_odata_type)
        self.body = json_body
        self.location = ApiResource._get_location(json_body)
        self.contains = ApiResource._get_contains(json_body)
        self.contained_by = ApiResource._get_contained_by(json_body)
        self.parent_url = self._get_parent_url()

        if not self.odata_id:
            cts_warning("@odata.id property not present in {body:response_body}. Setting 'None'.", body=json_body)

        if not self.odata_type:
            cts_warning("@odata.type property not present in {body:response_body}. Setting 'None'.", body=json_body)
Exemplo n.º 7
0
 def map_type(self, type):
     if type in self._map_types:
         mapped_type = self._map_types[type]
         cts_warning("User declared to use {mapped} to validate original",
                     mapped=mapped_type, original=type)
         return mapped_type
     return type
Exemplo n.º 8
0
    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)
Exemplo n.º 9
0
    def _validate_types_consistency(self,
                                    resource,
                                    resource_path,
                                    odata_type=None):
        """
        :type resource: dict
        :type resource_path: str
        :type odata_type: str
        :return: validation_status, type that will be validated
        :rtype: str, str
        """
        if not odata_type:
            try:
                odata_type = get_odata_type(resource)
            except KeyError:
                odata_type = None

        if not odata_type:
            cts_warning("Missing odata.type property in {odata_id:id}",
                        odata_id=resource_path)
            return ValidationStatus.PASSED_WITH_WARNINGS, self.name
        else:
            reported_odata_type = self.metadata_container.map_type(odata_type)
            if self.metadata_container.to_be_ignored(odata_type,
                                                     reported_odata_type):
                return ValidationStatus.PASSED, reported_odata_type

        try:
            if self.is_compatible(reported_odata_type):
                msg = (
                    "DEBUG::Resource {odata_id} reported entity type {reported_odata_type}, "
                    +
                    "expected type {expected_entity_type}, types are consistent"
                ).format(odata_id=resource_path,
                         reported_odata_type=reported_odata_type,
                         expected_entity_type=self.name)
                print msg

                return ValidationStatus.PASSED, reported_odata_type
            else:
                cts_error(
                    "Resource {odata_id:id} reported entity type {reported_odata_type}, "
                    +
                    "expected type {expected_entity_type}, types are not consistent",
                    odata_id=resource_path,
                    reported_odata_type=reported_odata_type,
                    expected_entity_type=self.name)

                return ValidationStatus.FAILED, reported_odata_type
        except KeyError:
            cts_error(
                "Resource {odata_id:id} reported unknown entity type {reported_odata_type},"
                +
                "will process validation with expected entity type {expected_entity_type}",
                odata_id=resource_path,
                reported_odata_type=reported_odata_type,
                expected_entity_type=self.name)

            return ValidationStatus.FAILED, reported_odata_type
Exemplo n.º 10
0
 def map_type(self, type):
     if type in self._map_types:
         mapped_type = self._map_types[type]
         cts_warning("User declared to use {mapped} to validate original",
                     mapped=mapped_type,
                     original=type)
         return mapped_type
     return type
Exemplo n.º 11
0
    def post_resource(self, url, discovery_container, payload=None, acceptable_return_codes=None,
                      wait_if_async=True, expect_location=None, api_endpoint_override=None):
        """
        Sends http POST request to remote endpoint.
        Throws AsyncOperation if service created task in response to this request.

        :type url: str
        :type discovery_container: DiscoveryContainer
        :type payload: dict
        :type acceptable_return_codes: list(int)
        :type wait_if_async: bool
        """
        from cts_core.discovery.api_explorer import ApiExplorer

        if expect_location is None:
            expect_location = True

        _, status, status_code, response_body, headers = \
            self._perform_call(url,
                               http_method=HttpMethods.POST,
                               payload=payload,
                               acceptable_return_codes=acceptable_return_codes,
                               api_endpoint_override=api_endpoint_override)
        # determine if the operation was completed synchronously or asynchronously
        if status_code == ReturnCodes.ACCEPTED:
            if not wait_if_async:
                raise AsyncOperation()
            status, status_code, response_body, headers = self._wait_for_task(headers,
                                                                              discovery_container,
                                                                              acceptable_ret_codes=acceptable_return_codes)

        if expect_location and status and not self._skip_refresh_after_request(url):
            try:
                # add the created element and discover all its children
                new_url = self.links_factory.get_resource_link(headers["Location"],
                                                               api_endpoint_override=api_endpoint_override).link
                print "MESSAGE::Refreshing {} info".format(new_url)
                # refresh the collection info
                _, get_status, get_status_code, _, _ = self.get_resource(url, discovery_container,
                                                                         api_endpoint_override=api_endpoint_override,
                                                                         check_resource_against_metadata=True)
                _, rediscovery_status = ApiExplorer(discovery_container.metadata_container,
                                                    self._config_property_reader).discover(new_url,
                                                                                           discovery_container.get_expected_odata_type_for_url(new_url),
                                                                                           discovery_container)
                if get_status and rediscovery_status:
                    print "MESSAGE::Refreshed %s and its children info" % new_url
                else:
                    cts_warning("Refreshing {odata_id:id} after POST generated errors. Get status code: {code}",
                                odata_id=new_url, code=get_status_code)

            except (KeyError, TypeError) as err:
                cts_warning(
                    "POST {odata_id:id} Response has no 'Location' header; Error: {err:exception}",
                    odata_id=url, err=err)

        return status, status_code, response_body, headers
Exemplo n.º 12
0
    def post_resource(self, url, discovery_container, payload=None, acceptable_return_codes=None,
                      wait_if_async=True, expect_location=None, api_endpoint_override=None):
        """
        Sends http POST request to remote endpoint.
        Throws AsyncOperation if service created task in response to this request.

        :type url: str
        :type discovery_container: DiscoveryContainer
        :type payload: dict
        :type acceptable_return_codes: list(int)
        :type wait_if_async: bool
        """
        from cts_core.discovery.api_explorer import ApiExplorer

        if expect_location is None:
            expect_location = True

        _, status, status_code, response_body, headers = \
            self._perform_call(url,
                               http_method=HttpMethods.POST,
                               payload=payload,
                               acceptable_return_codes=acceptable_return_codes,
                               api_endpoint_override=api_endpoint_override)
        # determine if the operation was completed synchronously or asynchronously
        if status_code == ReturnCodes.ACCEPTED:
            if not wait_if_async:
                raise AsyncOperation()
            status, status_code, response_body, headers = self._wait_for_task(headers,
                                                                              discovery_container,
                                                                              acceptable_ret_codes=acceptable_return_codes)

        if expect_location and status and not self._skip_refresh_after_request(url):
            try:
                # add the created element and discover all its children
                new_url = self.links_factory.get_resource_link(headers["Location"],
                                                               api_endpoint_override=api_endpoint_override).link
                print "MESSAGE::Refreshing {} info".format(new_url)
                # refresh the collection info
                _, get_status, get_status_code, _, _ = self.get_resource(url, discovery_container,
                                                                         api_endpoint_override=api_endpoint_override,
                                                                         check_resource_against_metadata=True)
                _, rediscovery_status = ApiExplorer(discovery_container.metadata_container,
                                                    self._config_property_reader).discover(new_url,
                                                                                           discovery_container.get_expected_odata_type_for_url(new_url),
                                                                                           discovery_container)
                if get_status and rediscovery_status:
                    print "MESSAGE::Refreshed %s and its children info" % new_url
                else:
                    cts_warning("Refreshing {odata_id:id} after POST generated errors. Get status code: {code}",
                                odata_id=new_url, code=get_status_code)

            except (KeyError, TypeError) as err:
                cts_warning(
                    "POST {odata_id:id} Response has no 'Location' header; Error: {err:exception}",
                    odata_id=url, err=err)

        return status, status_code, response_body, headers
Exemplo n.º 13
0
 def _parse_ignore_types(self):
     self._ignore_types = set([
         type.strip() for type in self.configuration.IgnoreTypes.split(",")
     ])
     if len(self._ignore_types) > 0:
         print "WARNING::CTS is configured to skip properties and entities of the following " \
               "types:"
         for ignore_type in self._ignore_types:
             cts_warning("\t- {ignore_type}", ignore_type=ignore_type)
Exemplo n.º 14
0
 def to_be_ignored(self, *types):
     ignored_types = self.get_ignored_types
     for type in set(types):
         if self._wide_types(type, ignored_types):
             return True
         elif type in ignored_types:
             cts_warning("User declared to skip validation of type {type}", type=type)
             return True
     return False
Exemplo n.º 15
0
    def _precondition_check(self, discovery_container):
        """
        :type discovery_container: cts_core.discovery.discovery_container.DiscoveryContainer
        :rtype: cts_core.validation.validation_status.ValidationStatus
        """
        status = ValidationStatus.PASSED

        if self.requirements is None:
            return status

        for requirement in self.requirements:
            if requirement.base not in self._metadata_container.entities:
                cts_error("Internal Error. Type {base} not found in metadata",
                          base=requirement.base)
                status = ValidationStatus.FAILED

        discovered = dict()
        for requirement in self.requirements:
            discovered[requirement.base] = discovery_container.count_resources(
                requirement.base, any_child_version=True)

        for requirement in self.requirements:
            if self._skip_check_predicate and self._skip_check_predicate(
                    discovery_container, requirement):
                continue

            if (requirement.optional is True and discovered.setdefault(
                    requirement.base, 0) < requirement.min):
                cts_warning(
                    "Minimum number of {type} is {min} to make this tests. Current number: {current}. "
                    "Tests will be skipped",
                    type=requirement.base,
                    min=requirement.min,
                    current=discovered[requirement.base])
                status = ValidationStatus.PASSED_WITH_WARNINGS
                continue

            if (requirement.min is not None and discovered.setdefault(
                    requirement.base, 0) < requirement.min):
                cts_error(
                    "Minimum number of {type} is {min}. Current number: {current}",
                    type=requirement.base,
                    min=requirement.min,
                    current=discovered[requirement.base])
                status = ValidationStatus.FAILED

            if (requirement.max is not None and discovered.setdefault(
                    requirement.base, 0) > requirement.max):
                cts_error(
                    "Maximum number of {type} is {max}. Current number: {current}",
                    type=requirement.base,
                    max=requirement.max,
                    current=discovered[requirement.base])
                status = ValidationStatus.FAILED

        return status
Exemplo n.º 16
0
 def _wide_types(type, ignored_types):
     import re
     for i in ignored_types:
         prog = re.compile(i)
         if prog.match(type):
             cts_warning("User declared to skip validation of type {type} using this pattern {pattern}",
                         type=type,
                         pattern=prog.pattern)
             return True
     return False
Exemplo n.º 17
0
 def to_be_ignored(self, *types):
     ignored_types = self.get_ignored_types
     for type in set(types):
         if self._wide_types(type, ignored_types):
             return True
         elif type in ignored_types:
             cts_warning("User declared to skip validation of type {type}",
                         type=type)
             return True
     return False
Exemplo n.º 18
0
    def _validate_property_list(self, context, variable_path, property_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:
            status = ValidationStatus.join_statuses(
                self._validate_property(context, variable_path,
                                        property_description), 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
Exemplo n.º 19
0
 def _wide_types(type, ignored_types):
     import re
     for i in ignored_types:
         prog = re.compile(i)
         if prog.match(type):
             cts_warning(
                 "User declared to skip validation of type {type} using this pattern {pattern}",
                 type=type,
                 pattern=prog.pattern)
             return True
     return False
Exemplo n.º 20
0
    def patch_resource(self,
                       url,
                       discovery_container,
                       payload=None,
                       acceptable_return_codes=None,
                       wait_if_async=True,
                       api_endpoint_override=None):
        """
        Sends http PATCH request to remote endpoint.
        Throws AsyncOperation if service created task in response to this request.

        :type url: str
        :type discovery_container: DiscoveryContainer
        :type payload: dict
        :type acceptable_return_codes: list(int)
        :type wait_if_async: bool
        """

        _, patch_status, patch_status_code, response_body, headers = \
            self._perform_call(url,
                               http_method=HttpMethods.PATCH,
                               payload=payload,
                               acceptable_return_codes=acceptable_return_codes,
                               api_endpoint_override=api_endpoint_override)

        # determine if the operation was completed synchronously or asynchronously
        if patch_status_code == ReturnCodes.ACCEPTED:
            if not wait_if_async:
                raise AsyncOperation()
            patch_status, patch_status_code, response_body, headers = self._wait_for_task(
                headers,
                discovery_container,
                acceptable_ret_codes=acceptable_return_codes)

        if patch_status != RequestStatus.SUCCESS:
            cts_error("{url:id} Patch failed. Status code: {code}",
                      url=url,
                      code=patch_status_code)

        print "MESSAGE::Refreshing {} info".format(url)

        _, get_status, get_status_code, _, _ = self.get_resource(
            url,
            discovery_container,
            api_endpoint_override=api_endpoint_override,
            check_resource_against_metadata=True)
        if not get_status:
            cts_warning(
                "Refreshing {url:id} after PATCH generated errors. Get status code: {code}",
                url=url,
                code=get_status_code)

        return patch_status, patch_status_code, response_body, headers
Exemplo n.º 21
0
    def add_resource(self, resource, check_resource_against_metadata=None):
        """
        :type resource: cts_core.discovery.api_resource.ApiResource
        :return: cts_framework.commons.enums.RequestStatus
        """

        if resource is None:
            return
        if resource.odata_id is None:
            return
        self[resource.url] = resource

        odata_type = resource.odata_type

        if odata_type:  # do not add the resource to any table if it is of an unknown type
            self.register_url_pattern(resource.url, odata_type)
            resource.expected_odata_type = odata_type
            new_row = json_normalize(resource.body)
            new_row["url"] = resource.url

            if odata_type in self._dataframes:
                # delete the deprecated definition of the resource if it exists
                if len(self._dataframes[odata_type]
                       [self._dataframes[odata_type]["url"] ==
                        resource.url].index):
                    self._dataframes[odata_type] = \
                        self._dataframes[odata_type][self._dataframes[odata_type]["url"] != resource.url]
                self._dataframes[odata_type] = \
                    concat([self._dataframes[odata_type], new_row], axis=0, ignore_index=True)
            else:
                self._dataframes[odata_type] = new_row

        if not check_resource_against_metadata:
            return RequestStatus.SUCCESS

        # this method is called in ApiCaller.get_resource() method, hence RequestStatus as a return type
        if self.metadata_container:
            status = MetadataGetValidator(
                self.metadata_container).validate_single_entity(resource)
            if status == ValidationStatus.PASSED:
                print "DEBUG::Metadata validation for resource {} passed".format(
                    resource.url)
                return RequestStatus.SUCCESS
            else:
                cts_error(
                    "Resource {odata_id:id} did not pass metadata validation",
                    odata_id=resource.url)
                return RequestStatus.FAILED
        else:
            cts_warning("Metadata not known, skipping resource validation")
            return RequestStatus.SUCCESS
Exemplo n.º 22
0
    def delete_resource(self,
                        url,
                        discovery_container,
                        acceptable_return_codes=None,
                        wait_if_async=True,
                        api_endpoint_override=None):
        """
        Sends http DELETE request to remote endpoint.
        Throws AsyncOperation if service created task in response to this request.

        :type url: str
        :type discovery_container: DiscoveryContainer
        :type acceptable_return_codes: list(int)
        :type wait_if_async: bool
        """
        link, status, status_code, response_body, headers = \
            self._perform_call(url,
                               http_method=HttpMethods.DELETE,
                               acceptable_return_codes=acceptable_return_codes,
                               api_endpoint_override=api_endpoint_override)

        # determine if the operation was completed synchronously or asynchronously
        if status_code == ReturnCodes.ACCEPTED:
            if not wait_if_async:
                raise AsyncOperation()
            status, status_code, response_body, headers = self._wait_for_task(
                headers,
                discovery_container,
                acceptable_ret_codes=acceptable_return_codes)

        # In UNPICKLE mode, CTS can't find a parent resource, so return mocked success
        if getenv('CTS_UNPICKLE', None):
            return MockConstants.delete_resource()

        if status == RequestStatus.SUCCESS:
            if discovery_container[link.link].parent_url:
                self.get_resource(discovery_container[link.link].parent_url,
                                  discovery_container,
                                  api_endpoint_override=api_endpoint_override)
            else:
                cts_warning(
                    "Unable to update the parent of {url:id} - parent unknown",
                    url=link.link)
            discovery_container.delete_resource(link.link)
        else:
            # not deleted. refresh
            self.get_resource(url,
                              discovery_container,
                              api_endpoint_override=api_endpoint_override)

        return status, status_code, response_body, headers
Exemplo n.º 23
0
    def _wait_for_task(self,
                       headers,
                       discovery_container,
                       api_endpoint_override=None,
                       acceptable_ret_codes=None):
        if acceptable_ret_codes is None:
            acceptable_ret_codes = [
                ReturnCodes.OK, ReturnCodes.CREATED, ReturnCodes.NO_CONTENT
            ]
        task_monitor = headers.get('Location')
        print "MESSAGE::Waiting for task completion. Monitor = %s" % task_monitor
        if task_monitor:
            for _ in range(0, TASK_TIMEOUT):
                _, status, status_code, task, headers = \
                    self.get_resource(task_monitor,
                                      discovery_container,
                                      acceptable_ret_codes +
                                      [
                                          ReturnCodes.ACCEPTED
                                      ],
                                      api_endpoint_override)

                # The client may also cancel the operation by performing a DELETE on the Task resource. Deleting the
                # Task resource object may invalidate the associated Task Monitor and subsequent GET on the Task
                # Monitor URL returns either 410 (Gone) or 404 (Not Found)
                if status_code in [ReturnCodes.NOT_FOUND, ReturnCodes.GONE]:
                    cts_warning("Task was cancelled unexpectedly")
                    return RequestStatus.FAILED, None, None, None

                # Once the operation has completed, the Task Monitor shall return a status code of OK (200) or
                # CREATED (201) for POST and include the headers and response body of the initial operation, as if it
                #  had completed synchronously
                if status_code in acceptable_ret_codes:
                    return status, status_code, task, headers

                # As long as the operation is in process, the service shall continue to return a status code of
                # 202 (Accepted) when querying the Task Monitor returned in the location header
                if status_code not in [ReturnCodes.ACCEPTED]:
                    cts_error(
                        "{odata_id:id} Task monitor returned unexpected status code: {code}",
                        odata_id=task_monitor,
                        code=status_code)
                    return RequestStatus.FAILED, None, None, None

                print "MESSAGE::Task in progress. Waiting for completion"
                time.sleep(1)
        else:
            cts_error(
                "{odata_id:id} Task has been created but Location header not found",
                odata_id=task_monitor)
            return RequestStatus.FAILED, None, None, None
Exemplo n.º 24
0
    def validate(self,
                 resource,
                 resource_path,
                 annotations=None,
                 odata_type=None):
        """
        :type annotations: dict
        :type resource: dict
        :type resource_path: str
        :type odata_type: str
        :rtype: str
        """
        if not odata_type:
            try:
                odata_type = get_odata_type(resource)
            except KeyError:
                odata_type = None

        if not odata_type:
            cts_warning(
                "Resource {odata_id} did not report odata.type property",
                odata_id=resource_path)
            return ValidationStatus.PASSED_WITH_WARNINGS
        else:
            mapped_odata_type = self.metadata_container.map_type(odata_type)
            if self.metadata_container.to_be_ignored(odata_type,
                                                     mapped_odata_type):
                return ValidationStatus.PASSED

        types_consistency_validation_status, entity_type = self._validate_types_consistency(
            resource, resource_path, odata_type)

        if entity_type == self.name:
            resource_validation_status = self._validate_container(
                resource, resource_path)
        else:
            try:
                resource_validation_status = self.metadata_container.entities[
                    entity_type]._validate_container(resource, resource_path)
            except KeyError:
                resource_validation_status = ValidationStatus.FAILED
            except Exception as error:
                cts_error("{id:id} Unexpected error: {error:exception}",
                          id == resource_path,
                          error=error)
                resource_validation_status = ValidationStatus.FAILED

        return ValidationStatus.join_statuses(
            types_consistency_validation_status, resource_validation_status)
Exemplo n.º 25
0
    def tree_deep_search(body, key):
        """
        Use this method for looking a last property on the tree
        :return ValidationStatus
        """
        resource_tree = objectpath.Tree(json.loads(json.dumps(body)))

        result_tuple = tuple(resource_tree.execute('$..{}'.format(key)))
        if not result_tuple:
            cts_warning("\t{status} Key {key} was not found".format(
                status=get_warning(), key=key))
            return ValidationStatus.PASSED_WITH_WARNINGS
        cts_message("\t{status} Key {key} was found".format(status=get_ok(),
                                                            key=key))
        return ValidationStatus.PASSED
Exemplo n.º 26
0
    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
Exemplo n.º 27
0
    def _parse_service_types_override(self):
        self._service_types_override = {service for service in
                                        (type.strip() for type in self.configuration.ServiceTypeOverride.split(","))
                                        if len(service) > 0
                                        }

        illegal = self._service_types_override - set(ServiceTypes.all())
        if len(illegal) > 0:
            cts_error("Invalid service types specified in ServiceTypeOverride configuration parameter: {illegal}",
                      illegal=", ".join(illegal))
        self._service_types_override -= illegal

        if len(self._service_types_override) > 0:
            cts_warning("Test was designed to be run against metadata for: {registered}, " +
                        "but user declared that service supports: {override}",
                        registered=self.registered_services_s(), override=", ".join(self._service_types_override))
Exemplo n.º 28
0
    def add_resource(self, resource, check_resource_against_metadata=None):
        """
        :type resource: cts_core.discovery.api_resource.ApiResource
        :return: cts_framework.commons.enums.RequestStatus
        """

        if resource is None:
            return
        if resource.odata_id is None:
            return
        self[resource.url] = resource

        odata_type = resource.odata_type

        if odata_type:  # do not add the resource to any table if it is of an unknown type
            self.register_url_pattern(resource.url, odata_type)
            resource.expected_odata_type = odata_type
            new_row = json_normalize(resource.body)
            new_row["url"] = resource.url

            if odata_type in self._dataframes:
                # delete the deprecated definition of the resource if it exists
                if len(self._dataframes[odata_type][self._dataframes[odata_type]["url"] ==
                                                    resource.url].index):
                    self._dataframes[odata_type] = \
                        self._dataframes[odata_type][self._dataframes[odata_type]["url"] != resource.url]
                self._dataframes[odata_type] = \
                    concat([self._dataframes[odata_type], new_row], axis=0, ignore_index=True)
            else:
                self._dataframes[odata_type] = new_row

        if not check_resource_against_metadata:
            return RequestStatus.SUCCESS

        # this method is called in ApiCaller.get_resource() method, hence RequestStatus as a return type
        if self.metadata_container:
            status = MetadataGetValidator(self.metadata_container).validate_single_entity(resource)
            if status == ValidationStatus.PASSED:
                print "DEBUG::Metadata validation for resource {} passed".format(resource.url)
                return RequestStatus.SUCCESS
            else:
                cts_error("Resource {odata_id:id} did not pass metadata validation", odata_id=resource.url)
                return RequestStatus.FAILED
        else:
            cts_warning("Metadata not known, skipping resource validation")
            return RequestStatus.SUCCESS
Exemplo n.º 29
0
    def _validate_types_consistency(self, resource, resource_path, odata_type=None):
        """
        :type resource: dict
        :type resource_path: str
        :type odata_type: str
        :return: validation_status, type that will be validated
        :rtype: str, str
        """
        if not odata_type:
            try:
                odata_type = get_odata_type(resource)
            except KeyError:
                odata_type = None

        if not odata_type:
            cts_warning("Missing odata.type property in {odata_id:id}", odata_id=resource_path)
            return ValidationStatus.PASSED_WITH_WARNINGS, self.name
        else:
            reported_odata_type = self.metadata_container.map_type(odata_type)
            if self.metadata_container.to_be_ignored(odata_type, reported_odata_type):
                return ValidationStatus.PASSED, reported_odata_type

        try:
            if self.is_compatible(reported_odata_type):
                msg = ("DEBUG::Resource {odata_id} reported entity type {reported_odata_type}, " +
                       "expected type {expected_entity_type}, types are consistent").format(
                    odata_id=resource_path, reported_odata_type=reported_odata_type, expected_entity_type=self.name)
                print msg

                return ValidationStatus.PASSED, reported_odata_type
            else:
                cts_error("Resource {odata_id:id} reported entity type {reported_odata_type}, " +
                          "expected type {expected_entity_type}, types are not consistent",
                          odata_id=resource_path,
                          reported_odata_type=reported_odata_type,
                          expected_entity_type=self.name)

                return ValidationStatus.FAILED, reported_odata_type
        except KeyError:
            cts_error("Resource {odata_id:id} reported unknown entity type {reported_odata_type}," +
                      "will process validation with expected entity type {expected_entity_type}",
                      odata_id=resource_path,
                      reported_odata_type=reported_odata_type,
                      expected_entity_type=self.name)

            return ValidationStatus.FAILED, reported_odata_type
Exemplo n.º 30
0
    def _check_power(self, chassis, discovery_container, error):
        """Check if power monitoring is possible via 'Power' property """
        print "MESSAGE::  Checking Power property"
        power_control_ok = False

        try:
            power_id = chassis.body["Power"]["@odata.id"]
            power = discovery_container.odataid_lookup(power_id)
            if not power:
                cts_error("  Unknown Power referenced: {id:id}", id=power_id)
                return True, False

            power_controls_ids = filter(None, [
                m.get("@odata.id", None)
                for m in power[0].body.get("PowerControl", list())
            ])
            power_controls = discovery_container.odataid_lookup(
                *power_controls_ids)
            for power_control in power_controls:
                power_control_health = ""
                try:
                    for power_control_body in power_control.body[
                            "PowerControl"]:
                        power_control_health = power_control_body["Status"][
                            "Health"]
                        power_consumed_watts = float(
                            power_control_body['PowerConsumedWatts'])
                        if power_consumed_watts > 0:
                            print "MESSAGE::  Found PowerConsumedWatts > 0 in %s.Power [...]" % chassis.odata_id
                            power_control_ok = True  # this is it!  can monitor power consumption
                            return False, power_control_ok
                        else:
                            cts_warning(
                                "  power_consumed_watts = {power_consumed_watts}. Looking for value > 0",
                                **locals())
                except KeyError:
                    print "MESSAGE::  PowerConsumedWatts not found in PowerControl: %s" % power_control.odata_id
                except TypeError:
                    print "ERROR::    Can't get data for {1} Health Status: {0}".format(
                        power_control_health, power_control.odata_id)
                    return True, False
        except KeyError:
            print "MESSAGE::  Power property not found in chassis %s" % chassis.odata_id

        return error, power_control_ok
Exemplo n.º 31
0
    def _check_power_zones(self, chassis, discovery_container, error):
        """Check if power monitoring is possible via 'PowerZones' property """
        print "MESSAGE::  Checking power zones"
        power_control_ok = False

        try:
            power_zones_id = chassis.body["PowerZones"]["@odata.id"]
            power_zones = discovery_container.odataid_lookup(power_zones_id)
            if not power_zones:
                cts_error("  Unknown PowerZones referenced: {id:id}",
                          id=power_zones_id)
                return True, False

            member_ids = filter(None, [
                m.get("@odata.id", None)
                for m in power_zones[0].body.get("Members", list())
            ])
            power_zones = discovery_container.odataid_lookup(*member_ids)

            if not power_zones:
                print "MESSAGE::    No power zone found"

            for power_zone in power_zones:
                print "MESSAGE::    Checking power zone %s" % power_zone.odata_id
                try:
                    power_consumed_watts = float(
                        power_zone.body['PowerConsumedWatts'])
                    if power_consumed_watts > 0:  # this is it!  can monitor power consumption
                        print "MESSAGE::   Found PowerConsumedWatts > 0 in %s.PowerZones [...]" % chassis.odata_id
                        power_control_ok = True
                    else:
                        cts_warning(
                            "   power_consumed_watts = {power_consumed_watts}. Looking for value > 0",
                            **locals())
                except KeyError:
                    print "MESSAGE::   PowerConsumedWatts not found in PowerZone: %s" % power_zone.odata_id
                except TypeError:
                    print "ERROR::     Can't get data for %s. Please check Health Status" % power_zone.odata_id
                    return True, False

        except KeyError:
            print "MESSAGE::  PowerZones attribute not found in chassis %s" % (
                chassis.odata_id)

        return error, power_control_ok
Exemplo n.º 32
0
    def power_monitoring_support(self, discovery_container):
        """
        Test is basing on fact, that without PSME there's no healthy drawer on PODm API.
        :type discovery_container: cts_core.discovery.discovery_container.DiscoveryContainer
        :type self.metadata_container: cts_core.metadata.self.metadata_container.MetadataContainer
        """
        print "TEST_CASE::Power monitoring support at Rack level"

        all_chassis = [
            r for r in discovery_container.iter_all(MetadataConstants.CHASSIS)
            if (r.body["ChassisType"] == "Zone") or (
                r.body["ChassisType"] == "Rack")
        ]

        if len(all_chassis) == 0:
            cts_error("Chassis Type: Zone not found")
            self.set_validation_status(ValidationStatus.FAILED)
            return

        overall_error = False
        overall_result = True

        for chassis in all_chassis:
            error = False
            print "MESSAGE::Checking Rack %s" % chassis.odata_id
            error, monitoring_supported_via_power_property = self._check_power(
                chassis, discovery_container, error)
            error, monitoring_supported_via_power_zones = self._check_power_zones(
                chassis, discovery_container, error)

            result = monitoring_supported_via_power_property or monitoring_supported_via_power_zones
            if result and not error:
                print "MESSAGE::Rack %s is OK" % chassis.odata_id
            else:
                cts_warning("Rack {id:id} does not have power monitoring",
                            id=chassis.odata_id)

            overall_error = overall_error or error
            overall_result = overall_result or result

        if overall_result and not overall_error:
            self.set_validation_status(ValidationStatus.PASSED)
            return
        else:
            self.set_validation_status(ValidationStatus.FAILED)
Exemplo n.º 33
0
    def _wait_for_task(self, headers, discovery_container, api_endpoint_override=None, acceptable_ret_codes=None):
        if acceptable_ret_codes is None:
            acceptable_ret_codes = [ReturnCodes.OK,
                                    ReturnCodes.CREATED,
                                    ReturnCodes.NO_CONTENT
                                    ]
        task_monitor = headers.get('Location')
        print "MESSAGE::Waiting for task completion. Monitor = %s" % task_monitor
        if task_monitor:
            for _ in range(0, TASK_TIMEOUT):
                _, status, status_code, task, headers = \
                    self.get_resource(task_monitor,
                                      discovery_container,
                                      acceptable_ret_codes +
                                      [
                                          ReturnCodes.ACCEPTED
                                      ],
                                      api_endpoint_override)

                # The client may also cancel the operation by performing a DELETE on the Task resource. Deleting the
                # Task resource object may invalidate the associated Task Monitor and subsequent GET on the Task
                # Monitor URL returns either 410 (Gone) or 404 (Not Found)
                if status_code in [ReturnCodes.NOT_FOUND, ReturnCodes.GONE]:
                    cts_warning("Task was cancelled unexpectedly")
                    return RequestStatus.FAILED, None, None, None

                # Once the operation has completed, the Task Monitor shall return a status code of OK (200) or
                # CREATED (201) for POST and include the headers and response body of the initial operation, as if it
                #  had completed synchronously
                if status_code in acceptable_ret_codes:
                    return status, status_code, task, headers

                # As long as the operation is in process, the service shall continue to return a status code of
                # 202 (Accepted) when querying the Task Monitor returned in the location header
                if status_code not in [ReturnCodes.ACCEPTED]:
                    cts_error("{odata_id:id} Task monitor returned unexpected status code: {code}",
                              odata_id=task_monitor, code=status_code)
                    return RequestStatus.FAILED, None, None, None

                print "MESSAGE::Task in progress. Waiting for completion"
                time.sleep(1)
        else:
            cts_error("{odata_id:id} Task has been created but Location header not found",
                      odata_id=task_monitor)
            return RequestStatus.FAILED, None, None, None
Exemplo n.º 34
0
    def _load_metadata(self):
        qualifiers, services = self.get_services_and_qualifiers()

        metadata_manager = MetadataManager(qualifiers,
                                           ignore_types=self.ignore_types,
                                           map_types=self.map_types)
        if self.configuration.MetadataDirectory:
            cts_warning("CTS is configured to use custom metadata from: {dir}",
                        dir=self.configuration.MetadataDirectory)
            metadata_container = self._load_custom_metadata(
                metadata_manager, self.configuration.MetadataDirectory)
        else:
            metadata_container = self._load_built_in_metadata(
                metadata_manager, services)

        if metadata_container is not None:
            metadata_container.print_types()
        return metadata_container
Exemplo n.º 35
0
 def _check_resource_list(response_body, pointer, url):
     cts_warning(
         "Could not find requestes resource {path} from {url:id} by JSON pointer dereferencing. "
         "Trying to use alternative, non-RSD based method using requested @odata.id",
         path=pointer,
         url=url)
     dissected_pointer = pointer.rsplit("/", 1)
     resource_list = resolve_pointer(response_body, dissected_pointer[0])
     if isinstance(resource_list, list):
         requested_resource_odata = url[url.find("/redfish"):]
         for resource in resource_list:
             if resource["@odata.id"] == requested_resource_odata:
                 return resource
     cts_error(
         "Could not find {path} from {url:id} with search by @odata.id",
         path=pointer,
         url=url)
     return None
Exemplo n.º 36
0
    def delete_resource(self, url, discovery_container, acceptable_return_codes=None,
                        wait_if_async=True, api_endpoint_override=None):
        """
        Sends http DELETE request to remote endpoint.
        Throws AsyncOperation if service created task in response to this request.

        :type url: str
        :type discovery_container: DiscoveryContainer
        :type acceptable_return_codes: list(int)
        :type wait_if_async: bool
        """
        link, status, status_code, response_body, headers = \
            self._perform_call(url,
                               http_method=HttpMethods.DELETE,
                               acceptable_return_codes=acceptable_return_codes,
                               api_endpoint_override=api_endpoint_override)

        # determine if the operation was completed synchronously or asynchronously
        if status_code == ReturnCodes.ACCEPTED:
            if not wait_if_async:
                raise AsyncOperation()
            status, status_code, response_body, headers = self._wait_for_task(headers,
                                                                              discovery_container,
                                                                              acceptable_ret_codes=acceptable_return_codes)

        # In UNPICKLE mode, CTS can't find a parent resource, so return mocked success
        if getenv('CTS_UNPICKLE', None):
            return MockConstants.delete_resource()

        if status == RequestStatus.SUCCESS:
            if discovery_container[link.link].parent_url:
                self.get_resource(discovery_container[link.link].parent_url,
                                  discovery_container,
                                  api_endpoint_override=api_endpoint_override)
            else:
                cts_warning("Unable to update the parent of {url:id} - parent unknown",
                            url=link.link)
            discovery_container.delete_resource(link.link)
        else:
            # not deleted. refresh
            self.get_resource(url, discovery_container,
                              api_endpoint_override=api_endpoint_override)

        return status, status_code, response_body, headers
Exemplo n.º 37
0
    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)
Exemplo n.º 38
0
    def _precondition_check(self, discovery_container):
        """
        :type discovery_container: cts_core.discovery.discovery_container.DiscoveryContainer
        :rtype: cts_core.validation.validation_status.ValidationStatus
        """
        status = ValidationStatus.PASSED

        if self.requirements is None:
            return status

        for requirement in self.requirements:
            if requirement.base not in self._metadata_container.entities:
                cts_error("Internal Error. Type {base} not found in metadata", base=requirement.base)
                status = ValidationStatus.FAILED

        discovered = dict()
        for requirement in self.requirements:
            discovered[requirement.base] = discovery_container.count_resources(requirement.base, any_child_version=True)

        for requirement in self.requirements:
            if self._skip_check_predicate and self._skip_check_predicate(discovery_container, requirement):
                continue

            if (requirement.optional is True and discovered.setdefault(requirement.base, 0) < requirement.min):
                cts_warning("Minimum number of {type} is {min} to make this tests. Current number: {current}. "
                            "Tests will be skipped",
                            type=requirement.base, min=requirement.min, current=discovered[requirement.base])
                status = ValidationStatus.PASSED_WITH_WARNINGS
                continue

            if (requirement.min is not None and discovered.setdefault(requirement.base, 0) < requirement.min):
                cts_error("Minimum number of {type} is {min}. Current number: {current}",
                          type=requirement.base, min=requirement.min, current=discovered[requirement.base])
                status = ValidationStatus.FAILED

            if (requirement.max is not None and discovered.setdefault(requirement.base, 0) > requirement.max):
                cts_error("Maximum number of {type} is {max}. Current number: {current}",
                          type=requirement.base, max=requirement.max, current=discovered[requirement.base])
                status = ValidationStatus.FAILED

        return status
Exemplo n.º 39
0
    def patch_resource(self, url, discovery_container, payload=None,
                       acceptable_return_codes=None, wait_if_async=True, api_endpoint_override=None):
        """
        Sends http PATCH request to remote endpoint.
        Throws AsyncOperation if service created task in response to this request.

        :type url: str
        :type discovery_container: DiscoveryContainer
        :type payload: dict
        :type acceptable_return_codes: list(int)
        :type wait_if_async: bool
        """

        _, patch_status, patch_status_code, response_body, headers = \
            self._perform_call(url,
                               http_method=HttpMethods.PATCH,
                               payload=payload,
                               acceptable_return_codes=acceptable_return_codes,
                               api_endpoint_override=api_endpoint_override)

        # determine if the operation was completed synchronously or asynchronously
        if patch_status_code == ReturnCodes.ACCEPTED:
            if not wait_if_async:
                raise AsyncOperation()
            patch_status, patch_status_code, response_body, headers = self._wait_for_task(headers,
                                                                                          discovery_container,
                                                                                          acceptable_ret_codes=acceptable_return_codes)

        if patch_status != RequestStatus.SUCCESS:
            cts_error("{url:id} Patch failed. Status code: {code}", url=url, code=patch_status_code)

        print "MESSAGE::Refreshing {} info".format(url)

        _, get_status, get_status_code, _, _ = self.get_resource(url, discovery_container,
                                                                 api_endpoint_override=api_endpoint_override,
                                                                 check_resource_against_metadata=True)
        if not get_status:
            cts_warning("Refreshing {url:id} after PATCH generated errors. Get status code: {code}",
                        url=url, code=get_status_code)

        return patch_status, patch_status_code, response_body, headers
Exemplo n.º 40
0
 def _parse_map_types(self):
     if self.configuration.MapTypes:
         try:
             self._map_types = {from_type: to_type for (from_type, to_type) in \
                                ((pair[0].strip(), pair[1].strip()) for pair in \
                                 (pair.split(':') for pair in self.configuration.MapTypes.split(',')) \
                                 )
                                if len(from_type) > 0 and len(to_type) > 0
                                }
             if len(self._map_types) > 0:
                 print "WARNING::CTS is configured to use the following type mappings to " \
                       "validate instances of unknown types:"
                 for from_type in self._map_types:
                     cts_warning("\t- {from_type}:{to_type}",
                                 from_type=from_type,
                                 to_type=self._map_types[from_type])
         except Exception as error:
             cts_error("Error while parsing 'MapTypes' configuration parameter. error = {error}",
                       error=error)
     else:
         self._map_types = dict()
Exemplo n.º 41
0
 def __init__(self,
              metadata_container,
              configuration,
              strategy,
              requirements=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()
     if self._fast_mode:
         cts_warning(
             "Test results may be UNRELIABLE - PATCHING ONLY ONE RESOURCE OF EACH TYPE!"
         )
    def handle_nontrivial(self, context, variable_path, property_description):
        """
        :param context:
        :type context:
        :param variable_path:
        :type variable_path:
        :param property_description:
        :type property_description:
        :return:
        :rtype:
        """
        applicability, validation_result = self.patch_nontrivial(context.api_resource,
                                                                 variable_path,
                                                                 property_description)

        if applicability in [ApplicabilityTestResult.SKIP_PROPERTY, ApplicabilityTestResult.NOT_MATCHED]:
            cts_warning("Skipping non-trivial property {name}", name=property_description)
            return ApplicabilityTestResult.SKIP_PROPERTY, ValidationStatus.PASSED_WITH_WARNINGS

        context.register_patch_attempt(self, property_description.name)
        return applicability, validation_result
Exemplo n.º 43
0
 def _parse_map_types(self):
     if self.configuration.MapTypes:
         try:
             self._map_types = {from_type: to_type for (from_type, to_type) in \
                                ((pair[0].strip(), pair[1].strip()) for pair in \
                                 (pair.split(':') for pair in self.configuration.MapTypes.split(',')) \
                                 )
                                if len(from_type) > 0 and len(to_type) > 0
                                }
             if len(self._map_types) > 0:
                 print "WARNING::CTS is configured to use the following type mappings to " \
                       "validate instances of unknown types:"
                 for from_type in self._map_types:
                     cts_warning("\t- {from_type}:{to_type}",
                                 from_type=from_type,
                                 to_type=self._map_types[from_type])
         except Exception as error:
             cts_error(
                 "Error while parsing 'MapTypes' configuration parameter. error = {error}",
                 error=error)
     else:
         self._map_types = dict()
Exemplo n.º 44
0
    def _parse_service_types_override(self):
        self._service_types_override = {
            service
            for service in (
                type.strip()
                for type in self.configuration.ServiceTypeOverride.split(","))
            if len(service) > 0
        }

        illegal = self._service_types_override - set(ServiceTypes.all())
        if len(illegal) > 0:
            cts_error(
                "Invalid service types specified in ServiceTypeOverride configuration parameter: {illegal}",
                illegal=", ".join(illegal))
        self._service_types_override -= illegal

        if len(self._service_types_override) > 0:
            cts_warning(
                "Test was designed to be run against metadata for: {registered}, "
                + "but user declared that service supports: {override}",
                registered=self.registered_services_s(),
                override=", ".join(self._service_types_override))
Exemplo n.º 45
0
    def _check_power(self, chassis, discovery_container, error):
        """Check if power monitoring is possible via 'Power' property """
        print "MESSAGE::  Checking Power property"
        power_control_ok = False

        try:
            power_id = chassis.body["Power"]["@odata.id"]
            power = discovery_container.odataid_lookup(power_id)
            if not power:
                cts_error("  Unknown Power referenced: {id:id}", id=power_id)
                return True, False

            power_controls_ids = filter(None, [m.get("@odata.id", None) for m in power[0].body.get("PowerControl",
                                                                                                   list())])
            power_controls = discovery_container.odataid_lookup(*power_controls_ids)
            for power_control in power_controls:
                power_control_health = ""
                try:
                    for power_control_body in power_control.body["PowerControl"]:
                        power_control_health = power_control_body["Status"]["Health"]
                        power_consumed_watts = float(power_control_body['PowerConsumedWatts'])
                        if power_consumed_watts > 0:
                            print "MESSAGE::  Found PowerConsumedWatts > 0 in %s.Power [...]" % chassis.odata_id
                            power_control_ok = True  # this is it!  can monitor power consumption
                            return False, power_control_ok
                        else:
                            cts_warning("  power_consumed_watts = {power_consumed_watts}. Looking for value > 0",
                                        **locals())
                except KeyError:
                    print "MESSAGE::  PowerConsumedWatts not found in PowerControl: %s" % power_control.odata_id
                except TypeError:
                    print "ERROR::    Can't get data for {1} Health Status: {0}".format(
                        power_control_health,
                        power_control.odata_id)
                    return True, False
        except KeyError:
            print "MESSAGE::  Power property not found in chassis %s" % chassis.odata_id

        return error, power_control_ok
Exemplo n.º 46
0
    def _load_metadata(self):
        qualifiers, services = self.get_services_and_qualifiers()

        metadata_manager = MetadataManager(qualifiers,
                                           ignore_types=self.ignore_types,
                                           map_types=self.map_types)
        if self.configuration.MetadataDirectory:
            cts_warning("CTS is configured to use custom metadata from: {dir}",
                        dir=self.configuration.MetadataDirectory)
            metadata_container = self._load_custom_metadata(metadata_manager, self.configuration.MetadataDirectory)
        elif self.configuration.RedfishMetadata:
            cts_warning("CTS is configured to use Redfish metadata: {metadata_version}",
                        metadata_version=self.configuration.RedfishMetadata)
            metadata_container = \
                self._load_custom_metadata(metadata_manager, '/'.join((Constants.METADATA_REDFISH_DIR,
                                                                      str(self.configuration.RedfishMetadata))))
        else:
            metadata_container = self._load_built_in_metadata(metadata_manager, services)

        if metadata_container is not None:
            metadata_container.print_types()
        return metadata_container
Exemplo n.º 47
0
    def power_monitoring_support(self, discovery_container):
        """
        Test is basing on fact, that without PSME there's no healthy drawer on PODm API.
        :type discovery_container: cts_core.discovery.discovery_container.DiscoveryContainer
        :type self.metadata_container: cts_core.metadata.self.metadata_container.MetadataContainer
        """
        print "TEST_CASE::Power monitoring support at Rack level"

        all_chassis = [r for r in discovery_container.iter_all(MetadataConstants.CHASSIS)
                        if (r.body["ChassisType"] == "Zone") or (r.body["ChassisType"] == "Rack")]

        if len(all_chassis) == 0:
            cts_error("Chassis Type: Zone not found")
            self.set_validation_status(ValidationStatus.FAILED)
            return

        overall_error = False
        overall_result = True

        for chassis in all_chassis:
            error = False
            print "MESSAGE::Checking Rack %s" % chassis.odata_id
            error, monitoring_supported_via_power_property = self._check_power(chassis, discovery_container, error)
            error, monitoring_supported_via_power_zones = self._check_power_zones(chassis, discovery_container, error)

            result = monitoring_supported_via_power_property or monitoring_supported_via_power_zones
            if result and not error:
                print "MESSAGE::Rack %s is OK" % chassis.odata_id
            else:
                cts_warning("Rack {id:id} does not have power monitoring", id=chassis.odata_id)

            overall_error = overall_error or error
            overall_result = overall_result or result

        if overall_result and not overall_error:
            self.set_validation_status(ValidationStatus.PASSED)
            return
        else:
            self.set_validation_status(ValidationStatus.FAILED)
Exemplo n.º 48
0
    def validate(self, resource, resource_path, annotations=None, odata_type=None):
        """
        :type annotations: dict
        :type resource: dict
        :type resource_path: str
        :type odata_type: str
        :rtype: str
        """
        if not odata_type:
            try:
                odata_type = get_odata_type(resource)
            except KeyError:
                odata_type = None

        if not odata_type:
            cts_warning("Resource {odata_id} did not report odata.type property", odata_id=resource_path)
            return ValidationStatus.PASSED_WITH_WARNINGS
        else:
            mapped_odata_type = self.metadata_container.map_type(odata_type)
            if self.metadata_container.to_be_ignored(odata_type, mapped_odata_type):
                return ValidationStatus.PASSED

        types_consistency_validation_status, entity_type = self._validate_types_consistency(resource,
                                                                                            resource_path,
                                                                                            odata_type)

        if entity_type == self.name:
            resource_validation_status = self._validate_container(resource, resource_path)
        else:
            try:
                resource_validation_status = self.metadata_container.entities[entity_type]._validate_container(resource,
                                                                                                               resource_path)
            except KeyError:
                resource_validation_status = ValidationStatus.FAILED
            except Exception as error:
                cts_error("{id:id} Unexpected error: {error:exception}", id==resource_path, error=error)
                resource_validation_status = ValidationStatus.FAILED

        return ValidationStatus.join_statuses(types_consistency_validation_status, resource_validation_status)
Exemplo n.º 49
0
    def _check_power_zones(self, chassis, discovery_container, error):
        """Check if power monitoring is possible via 'PowerZones' property """
        print "MESSAGE::  Checking power zones"
        power_control_ok = False

        try:
            power_zones_id = chassis.body["PowerZones"]["@odata.id"]
            power_zones = discovery_container.odataid_lookup(power_zones_id)
            if not power_zones:
                cts_error("  Unknown PowerZones referenced: {id:id}", id=power_zones_id)
                return True, False

            member_ids = filter(None, [m.get("@odata.id", None) for m in power_zones[0].body.get("Members", list())])
            power_zones = discovery_container.odataid_lookup(*member_ids)

            if not power_zones:
                print "MESSAGE::    No power zone found"

            for power_zone in power_zones:
                print "MESSAGE::    Checking power zone %s" % power_zone.odata_id
                try:
                    power_consumed_watts = float(power_zone.body['PowerConsumedWatts'])
                    if power_consumed_watts > 0:  # this is it!  can monitor power consumption
                        print "MESSAGE::   Found PowerConsumedWatts > 0 in %s.PowerZones [...]" % chassis.odata_id
                        power_control_ok = True
                    else:
                        cts_warning("   power_consumed_watts = {power_consumed_watts}. Looking for value > 0",
                                    **locals())
                except KeyError:
                    print "MESSAGE::   PowerConsumedWatts not found in PowerZone: %s" % power_zone.odata_id
                except TypeError:
                    print "ERROR::     Can't get data for %s. Please check Health Status" % power_zone.odata_id
                    return True, False

        except KeyError:
            print "MESSAGE::  PowerZones attribute not found in chassis %s" % (chassis.odata_id)

        return error, power_control_ok
Exemplo n.º 50
0
    def handle_nontrivial(self, context, variable_path, property_description):
        """
        :param context:
        :type context:
        :param variable_path:
        :type variable_path:
        :param property_description:
        :type property_description:
        :return:
        :rtype:
        """
        applicability, validation_result = self.patch_nontrivial(
            context.api_resource, variable_path, property_description)

        if applicability in [
                ApplicabilityTestResult.SKIP_PROPERTY,
                ApplicabilityTestResult.NOT_MATCHED
        ]:
            cts_warning("Skipping non-trivial property {name}",
                        name=property_description)
            return ApplicabilityTestResult.SKIP_PROPERTY, ValidationStatus.PASSED_WITH_WARNINGS

        context.register_patch_attempt(self, property_description.name)
        return applicability, validation_result
Exemplo n.º 51
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()