def run(self): api_explorer = ApiExplorer(self.metadata_container, self.configuration) discovery_container, status = api_explorer.discover( MetadataConstants.SERVICE_ROOT_URI, MetadataConstants.SERVICE_ROOT) status = ValidationStatus.PASSED catalog = expanduser( "~" ) + "/.cts/tests/Rack_Scale_2_5_Storage_Services/required/tests_archeo_ss/" for test in listdir(catalog): with open(catalog + test) as f: data = json.load(f) test_name = data["name"] print "TEST_CASE::Validating section " + ColorPrinter.format_text(test_name, color=ColorPrinter.YELLOW_FORE) + \ " of architecture specification" status_of_test = ArchSpecTests().parse_json( discovery_container, data) if _skip_pnc_tests_if_resource_is_not_available(test) and \ (status_of_test == ValidationStatus.FAILED): status = ValidationStatus.join_statuses( status, ValidationStatus.PASSED_WITH_WARNINGS) print "MESSAGE:: CTS can not detect a PNC. Skipping this tests" else: status = ValidationStatus.join_statuses( status, status_of_test) print "STATUS::{status}".format(status=status_of_test) print "MESSAGE::All tests finished" print "STATUS::{status}".format(status=status)
def action_runner(self, specific_action): actions_status = ValidationStatus.RUNNING for action_step, action in enumerate(specific_action['actions'], 1): step_counter = "%d/%d" % (action_step, len(specific_action['actions'])) action_type = action['type'] if action_type == 'timeout': try: self.perform_timeout(step_counter, action['value']) except IndexError: self.perform_timeout(step_counter) continue retry_if_fail, retry_timeout = self.__get_retry_information(action) action_args = (specific_action['name'], action['name'], action['action'], step_counter, int(retry_if_fail), int(retry_timeout)) if action_type == 'external': actions_status = ValidationStatus.join_statuses(self.perform_external_command(*action_args)) continue elif action_type == 'usecase': actions_status = ValidationStatus.join_statuses(self.perform_action(*action_args)) return actions_status
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 _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 action_runner(self, specific_action): actions_status = ValidationStatus.RUNNING for action_step, action in enumerate(specific_action['actions'], 1): step_counter = "%d/%d" % (action_step, len(specific_action['actions'])) action_type = action['type'] if action_type == 'timeout': try: self.perform_timeout(step_counter, action['value']) except IndexError: self.perform_timeout(step_counter) continue retry_if_fail, retry_timeout = self.__get_retry_information(action) action_args = (specific_action['name'], action['name'], action['action'], step_counter, int(retry_if_fail), int(retry_timeout)) if action_type == 'external': actions_status = ValidationStatus.join_statuses( self.perform_external_command(*action_args)) continue elif action_type == 'usecase': actions_status = ValidationStatus.join_statuses( self.perform_action(*action_args)) return actions_status
def min_count_search(api, key): """ Verify the minimal presents of the properties :param api: :param key: :return: """ resource_tree = objectpath.Tree(json.loads(json.dumps(api))) phase_status = ValidationStatus.PASSED for element_key, element_count in key.iteritems(): result_tuple = tuple( resource_tree.execute('$..{key}'.format(key=element_key))) if len(result_tuple) < 1: cts_color_message( "\t{status} Minimal count of {element_key} not achieved. Minimum values <= {count}" .format(status=get_fail(), element_key=element_key, count=element_count)) phase_status = ValidationStatus.join_statuses( ValidationStatus.FAILED) else: cts_color_message( "\t{status} Minimal count of {element_key} was achieved". format(status=get_ok(), element_key=element_key)) phase_status = ValidationStatus.join_statuses( ValidationStatus.PASSED) return phase_status
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 _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
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 redfish_uri_compliance = discovery_container.validate_redfish_uris_consistency( api_resource, self._metadata_container) status = ValidationStatus.join_statuses(status, api_resource_status, redfish_uri_compliance) print "SCREEN::Overall status: %s" % status return status
def set_validation_status(self, status): print "STATUS::%s" % status if hasattr(self, "validation_status"): self.validation_status = ValidationStatus.join_statuses( self.validation_status, status) else: self.validation_status = status
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))
def validate(self, resource, resource_path, annotations=None): """ :type annotations: dict :type resource: dict :type resource_path: str :rtype: str """ try: odata_type = self.metadata_container.map_type(get_odata_type(resource)) 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 except KeyError: pass #this is legal that complex type does not have odata.type specified types_consistency_validation_status, complex_type_name = self.validate_types_consistency(resource, resource_path) if self.metadata_container.to_be_ignored(complex_type_name): return ValidationStatus.PASSED if complex_type_name == self.name: resource_validation_status = self.validate_properties(resource, resource_path) else: try: resource_validation_status = self.metadata_container.types[complex_type_name].validate(resource, resource_path) except KeyError: resource_validation_status = ValidationStatus.FAILED except Exception as err: import traceback cts_error("{path} - exception {err:exceptions}: {stack:stacktrace}", path=resource_path, err=err, stack=traceback.format_exc()) resource_validation_status = ValidationStatus.FAILED return ValidationStatus.join_statuses(types_consistency_validation_status, resource_validation_status)
def handle_nontrivial_property(self): try: value_pre = self.get_property_value() except KeyError: return ValidationStatus.FAILED for payload in self.payload(): property_str = "->".join( map(str, self.variable_path + [self.property_description.name])) print "TEST_CASE:: Patch %s[%s]" % (self.api_resource.odata_id, property_str) patch_status = self.do_patch(payload) print "STATUS::%s" % patch_status if patch_status == ValidationStatus.FAILED: return ValidationStatus.FAILED print "TEST_CASE:: Verify %s[%s]" % (self.api_resource.odata_id, property_str) verify_status = self.verify(self.api_resource, payload) print "STATUS::%s" % verify_status print "TEST_CASE:: Restore %s[%s]" % (self.api_resource.odata_id, property_str) restore_status = self.do_restore(value_pre) print "STATUS::%s" % restore_status return ValidationStatus.join_statuses(patch_status, verify_status, restore_status)
def validate_path_using_contains_property(self): res = ValidationStatus.PASSED remained_to_be_visited = [r.odata_id for r in self.discover_container.physical_location_aware_resources()] root_resources = self.discover_container.root_resources() if root_resources: if len(root_resources) > 1: fmt = ', '.join(["{resources[%d].odata_id:id}" % idx for idx, _ in enumerate(root_resources)]) cts_error("More than one root resource (with NULL ParentId). " + "Expected only one. Root resources found: %s" % fmt, resources=root_resources) res = ValidationStatus.FAILED for root in root_resources: res = ValidationStatus.join_statuses( self._validate_from(root, remained_to_be_visited, parent_location=None, parent_odata_id=None), res) else: if self.discover_container.physical_location_aware_resources(): cts_error("Resources with Location in Oem property has been found, but none has NULL ParentId") res = ValidationStatus.FAILED if remained_to_be_visited: fmt = ', '.join(['{remained_to_be_visited[%d]:id}' % i for i, _ in enumerate(remained_to_be_visited)]) cts_error("Not all location-aware resources visited while traversing tree using 'Link->Contains'. " + "Unreachable are: %s" % fmt, remained_to_be_visited=remained_to_be_visited) res = ValidationStatus.FAILED return res
def _test_delete_all_created_endpoints_and_zones(self): status = ValidationStatus.PASSED for resource, link in OrderedDict([("volume", self.created_volume), ("zone", self.zone_endpoint), ("initiator endpoint", self.initiator_endpoint), ("target endpoint", self.target_endpoint)]).iteritems(): print "TEST_CASE::Delete {resource}".format(resource=resource) if link: if not self.__delete_location(link): self.set_status_failed() status = ValidationStatus.join_statuses(status, ValidationStatus.FAILED) else: self.set_status_passed() status = ValidationStatus.join_statuses(status, ValidationStatus.PASSED) else: self.set_status_blocked() status = ValidationStatus.join_statuses(status, ValidationStatus.BLOCKED) return status
def test_roulette(self, api, value_element): test_phases = { self.tree_deep_search: [ "last_property", "Test phase: Verify that the \"PropertyRequirements\" fields exists" ], self.min_count_search: [ "min_count", "Test phase: Verify the number of elements meets the minimum criterion " "(\"MinCount\")" ], self.read_requirement: [ "read_requirement", "Test phase: Verify that the \"ReadRequirement\" fields exist and are readable" ], self.write_requirement: [ "write_requirement", "Test phase: ONLY Verify that the \"WriteRequirement\" elements are exist" ] } test_case_status = ValidationStatus.PASSED for test_to_do, test_description in test_phases.iteritems(): phase_status = ValidationStatus.PASSED each_value_element = value_element[test_description[0]] try: if len(each_value_element) > 0: cts_message(test_description[1]) each_value_element = set( value_element[test_description[0]]) else: continue except TypeError: pass for each in each_value_element: phase_status = ValidationStatus.join_statuses( test_to_do(api.body, each)) print("STATUS::{status}".format(status=phase_status)) test_case_status = ValidationStatus.join_statuses(phase_status) cts_color_message("Tested element {odata_type} ({odata_id})".format( odata_type=api.odata_type, odata_id=api.odata_id), font_color="LIGHT_BLUE") print("STATUS::{status}".format(status=test_case_status))
def read_requirement(api, key): """ Verify that the resource exists in the list and that it can be read with GET :param api: :param key: :return: """ resource_tree = objectpath.Tree(json.loads(json.dumps(api))) phase_status = ValidationStatus.PASSED for element_key, element_property in key.iteritems(): result_tuple = tuple( resource_tree.execute('$..{key}'.format(key=element_key))) if not result_tuple: if element_property == "Mandatory": cts_error( "\t{status} Element {element_key} is {element_property}" .format(status=get_fail(), element_key=element_key, element_property=element_property)) phase_status = ValidationStatus.join_statuses( ValidationStatus.FAILED) elif element_property == "IfImplemented": continue else: cts_color_message( "\t{status} Element {element_key} is {element_property}" .format(status=get_info(), element_key=element_key, element_property=element_property)) phase_status = ValidationStatus.join_statuses( ValidationStatus.PASSED_WITH_WARNINGS) else: cts_message( "\t{status} Element {element_key} is {element_property}". format(status=get_ok(), element_key=element_key, element_property=element_property)) phase_status = ValidationStatus.join_statuses( ValidationStatus.PASSED) return phase_status
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) status = ValidationStatus.join_statuses(status, resource_status) print "SCREEN::Overall status: %s" % status return status
def crud_fabrics_target(self): status = ValidationStatus.PASSED cts_message("CRUD FABRICS TARGET") # test pre check self.__find_volumes_without_any_links(optional_pre_check=True) try: status = ValidationStatus.join_statuses(status, self._test_create_volume()) except: self._test_delete_all_created_endpoints_and_zones() status = ValidationStatus.FAILED self.__find_and_choose_endpoint_collection() self.__find_and_choose_volume() if not (self.chosen_volume and self.chosen_endpoint and self.chosen_zone): self.set_status_blocked() return ValidationStatus.BLOCKED cts_message("Chosen Volume for test: {volume_url}".format(volume_url=self.chosen_volume)) try: status = ValidationStatus.join_statuses(status, self._test_create_initiator_endpoint()) status = ValidationStatus.join_statuses(status, self._test_create_target_endpoint()) status = ValidationStatus.join_statuses(status, self._test_create_zone_with_only_one_endpoint()) status = ValidationStatus.join_statuses(status, self._test_patch_zone_with_target_endpoint()) except: status = ValidationStatus.FAILED finally: status = ValidationStatus.join_statuses(status, self._test_delete_all_created_endpoints_and_zones()) return status
def crud_nvme(self): status = ValidationStatus.PASSED cts_message("CRUD NVME") # test pre check self.__find_volumes_without_any_links(optional_pre_check=True) try: status = ValidationStatus.join_statuses(status, self._test_create_volume()) except: self._test_delete_all_created_endpoints_and_zones() status = ValidationStatus.FAILED self.__find_and_choose_endpoint_collection() self.__find_and_choose_volume() if not (self.chosen_volume and self.chosen_endpoint and self.chosen_zone): self.set_status_blocked() return ValidationStatus.BLOCKED cts_message("Chosen Volume for test: {volume_url}".format(volume_url=self.chosen_volume)) try: status = ValidationStatus.join_statuses(status, self._test_create_initiator_endpoint()) status = ValidationStatus.join_statuses(status, self._test_create_target_endpoint()) status = ValidationStatus.join_statuses(status, self._test_create_zone_with_only_one_endpoint()) status = ValidationStatus.join_statuses(status, self._test_patch_zone_with_target_endpoint()) except: status = ValidationStatus.FAILED finally: status = ValidationStatus.join_statuses(status, self._test_delete_all_created_endpoints_and_zones()) return status
def validate_properties(self, container, path): """ :type container: dict :type path: str :rtype: str """ additional_items_validation = self._validate_additional_properties(container, path) fields_validation = list() for resource_property in self.properties.itervalues(): fields_validation.append(resource_property.validate(container, path)) return ValidationStatus.join_statuses(additional_items_validation, *fields_validation)
def _validate_container(self, container, path): """ :type container: dict :type path: str :rtype: str """ additional_items_validation = self._validate_additional_properties(container, path) fields_validation = list() for resource_property in self.properties.itervalues(): fields_validation.append(resource_property.validate(container, path)) return ValidationStatus.join_statuses(additional_items_validation, *fields_validation)
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)
def detect(self, profile_name, purpose, profile=None): cts_color_message( "Testing a profile: {name}".format(name=profile_name), "HEADER") cts_color_message("Purpose: {purpose}".format(purpose=purpose)) status = ValidationStatus.PASSED self.main_resource_validation(profile) self.show_results(profile_name) if self.missing_main_elements: status = ValidationStatus.join_statuses(ValidationStatus.BLOCKED) cts_warning("Some elements are missing for all tests") print("STATUS::{status}".format(status=status)) else: status = ValidationStatus.join_statuses(ValidationStatus.PASSED) cts_message("All main elements are available") print("STATUS::{status}".format(status=status)) self.run(self.profile_container) cts_color_message("Deeper verification", font_color="HEADER") self.validate_profile_container() return status
def _validate_additional_properties(self, container, path): """ :type container: dict :type path: str :rtype: str """ status = ValidationStatus.PASSED expected_properties = self.properties.keys() + self.navigation_properties.keys() present_properties = container.keys() unused_properties = set(expected_properties) - set(present_properties) for property_name in container.keys(): if property_name not in expected_properties and not is_special_property(property_name): status = ValidationStatus.join_statuses(status, self._validate_additional_property(property_name, container[property_name], path, unused_properties)) return status
def run(self): api_explorer = ApiExplorer(self.metadata_container, self.configuration) discovery_container, status = api_explorer.discover(MetadataConstants.SERVICE_ROOT_URI, MetadataConstants.SERVICE_ROOT) status = ValidationStatus.PASSED catalog = expanduser("~") + "/.cts/tests/Rack_Scale_2_5_PSME/required/tests_archeo_psme/" for test in listdir(catalog): with open(catalog + test) as f: data = json.load(f) test_name = data["name"] print "TEST_CASE::Validating section " + ColorPrinter.format_text(test_name, color=ColorPrinter.YELLOW_FORE) + \ " of architecture specification" status_of_test = ArchSpecTests().parse_json(discovery_container, data) print "STATUS::{status}".format(status=status_of_test) status = ValidationStatus.join_statuses(status, status_of_test) print "MESSAGE::All tests finished" print "STATUS::{status}".format(status=status)
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))
def _validate_additional_properties(self, container, path): """ :type container: dict :type path: str :rtype: str """ status = ValidationStatus.PASSED expected_properties = self.properties.keys( ) + self.navigation_properties.keys() present_properties = container.keys() unused_properties = set(expected_properties) - set(present_properties) for property_name in container.keys(): if property_name not in expected_properties and not is_special_property( property_name): status = ValidationStatus.join_statuses( status, self._validate_additional_property( property_name, container[property_name], path, unused_properties)) return status
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)
def _validate_collection(self, property_value, resource_path): value_is_collection = True status = ValidationStatus.PASSED if isinstance(property_value, list): try: for path_element, resource_element in enumerate( property_value): status = ValidationStatus.join_statuses( self.metadata_container.types[self.type].validate( resource_element, "%s->%s" % (resource_path, path_element), self.annotations), status) except TypeError: value_is_collection = False else: value_is_collection = False if not value_is_collection: cts_error( "Property {resource_path} is expected to be collection, but it is not", resource_path=resource_path) status = ValidationStatus.FAILED return status
def handle_nontrivial_property(self): try: value_pre = self.get_property_value() except KeyError: return ValidationStatus.FAILED for payload in self.payload(): property_str = "->".join(map(str, self.variable_path + [self.property_description.name])) print "TEST_CASE:: Patch %s[%s]" % (self.api_resource.odata_id, property_str) patch_status = self.do_patch(payload) print "STATUS::%s" % patch_status if patch_status == ValidationStatus.FAILED: return ValidationStatus.FAILED print "TEST_CASE:: Verify %s[%s]" % (self.api_resource.odata_id, property_str) verify_status = self.verify(self.api_resource, payload) print "STATUS::%s" % verify_status print "TEST_CASE:: Restore %s[%s]" % (self.api_resource.odata_id, property_str) restore_status = self.do_restore(value_pre) print "STATUS::%s" % restore_status return ValidationStatus.join_statuses(patch_status, verify_status, restore_status)
def crud_logical_drive(self, create_logical_drive_for_target_crud=None): """ Test is trying to perform CRUD (create, read, update, delete) operations on a logical drive :type create_logical_drive_for_target_crud: Boolean """ status = ValidationStatus.BLOCKED lvm_found = False chosen_logical_drive_type, chosen_logical_drive_mode = None, None chosen_logical_drive_collection, chosen_logical_drive, chosen_logical_drive_group = None, None, None inherited_bootable = None # to speed things up, always look for the lowest capacity logical drive LVM_LV_SPECIFIC_CONSTRAINTS = [equal("Mode", "LV"), greater("CapacityGiB", 0), not_equal("Snapshot", True), order_by("CapacityGiB")] CEPH_RBD_SPECIFIC_CONSTRAINTS = [equal("Mode", "RBD"), greater("CapacityGiB", 0), order_by("CapacityGiB")] logical_drive_collections = dict(self.discovery_container.get_resources( MetadataConstants.LOGICAL_DRIVE_COLLECTION, any_child_version=True)) for collection in logical_drive_collections.keys(): chosen_logical_drive_collection = collection logical_drive_groups = dict(self.discovery_container.get_resources(MetadataConstants.LOGICAL_DRIVE, any_child_version=True, constraints=[equal("Mode", "LVG"), from_collection(collection)])) if logical_drive_groups: lvm_found = True else: logical_drive_groups = dict(self.discovery_container.get_resources(MetadataConstants.LOGICAL_DRIVE, any_child_version=True, constraints=[equal("Mode", "Pool"), from_collection( collection)])) if logical_drive_groups: specific_constraints = LVM_LV_SPECIFIC_CONSTRAINTS if lvm_found else CEPH_RBD_SPECIFIC_CONSTRAINTS for lvg_link, lvg in logical_drive_groups.iteritems(): chosen_logical_drive_group = lvg.odata_id try: child_lvs = [lv["@odata.id"] for lv in lvg.body["Links"]["LogicalDrives"]] chosen_logical_drive, _ = self.discovery_container.get_resources( MetadataConstants.LOGICAL_DRIVE, any_child_version=True, constraints=specific_constraints + [odata_ids(child_lvs)])[0] lv = self.discovery_container[chosen_logical_drive] inherited_bootable = lv.body["Bootable"] chosen_logical_drive_type = lv.body["Type"] chosen_logical_drive_mode = lv.body["Mode"] except (KeyError,IndexError) as error: cts_error("Exception while parsing {id:id}: {error:exception}", id=chosen_logical_drive, error=error) chosen_logical_drive = None pass else: break if not chosen_logical_drive_group: resource = "logical drive group" if lvm_found else "Pool" cts_warning("Could not find a {resource} resource in collection {collection}", **locals()) if lvm_found and not chosen_logical_drive: cts_warning("Could not find a logical volume in collection {collection}", **locals()) if lvm_found and not chosen_logical_drive: cts_error("Could not find any logical volume") return status if not chosen_logical_drive_group: cts_error("Could not find any logical drive group, aborting") return status self.post_logical_drive_payload = dict( Name="LVM Logical Drive created by CTS", Type=chosen_logical_drive_type, Mode=chosen_logical_drive_mode, Protected=False, CapacityGiB=self.discovery_container[chosen_logical_drive].body["CapacityGiB"], Image="CTS test image", Bootable=inherited_bootable if inherited_bootable else False, Snapshot=True, Links=dict( LogicalDrives=[{"@odata.id": chosen_logical_drive_group}], MasterDrive={"@odata.id": urlparse(chosen_logical_drive).path} ) ) # Creating the first RBD if no other was found if not chosen_logical_drive: if lvm_found: print "ERROR::Could not find a logical drive suitable for snapshot" return status cts_warning("Could not find any logical drive suitable for snapshot. Since CEPH was detected, CTS will "\ "try to create the first RBD") self.post_logical_drive_payload = dict( Name="CEPH Logical Drive created by CTS", Type="CEPH", Mode="RBD", Protected=False, CapacityGiB=self.discovery_container[chosen_logical_drive_group].body["CapacityGiB"]/2, Bootable=False, Snapshot=False, Links=dict( LogicalDrives=[{"@odata.id": urlparse(chosen_logical_drive_group).path}], MasterDrive=None ) ) print "MESSAGE::Logical drive CRUD test will be performed on logical drive collection %s, creating logical " \ "drive based on %s on group %s" % (chosen_logical_drive_collection, chosen_logical_drive, chosen_logical_drive_group) self.expected_created_logical_drive_body = deepcopy(self.post_logical_drive_payload) # The logical drive in LogicalDrives link is really the volume group and is going to be seen under # UsedBy on REST self.expected_created_logical_drive_body["Links"]["UsedBy"] = \ self.expected_created_logical_drive_body["Links"]["LogicalDrives"] self.expected_created_logical_drive_body["Links"]["LogicalDrives"] = [] status = self._test_case_create_logical_drive(chosen_logical_drive_collection) if status == ValidationStatus.PASSED: # only perform other tests if creation was successful ld = self.created_logical_drive status = ValidationStatus.join_statuses(status, self._test_case_get_created_logical_drive(ld)) if create_logical_drive_for_target_crud: return status if status == ValidationStatus.PASSED: status = ValidationStatus.join_statuses(status, self._test_case_update_created_logical_drive()) status = ValidationStatus.join_statuses(status, self._test_case_delete_created_logical_drive(ld)) status = ValidationStatus.join_statuses(status, self._test_case_get_deleted_logical_drive(ld)) return status
def _validate_from(self, resource, remained_to_be_visited, parent_location=None, parent_odata_id=None): """ :type resource: ApiResourceProxy :rtype: str """ res = ValidationStatus.PASSED if resource.odata_id in remained_to_be_visited: remained_to_be_visited.remove(resource.odata_id) location = resource.location if location and location.parent_id: if not parent_location: cts_error("Parent resource {parent_odata_id:id} has no location specified, " + "but child resource {odata_id:id} has Location->ParentId={parent_id:ignore}", parent_odata_id=parent_odata_id, odata_id=resource.odata_id, parent_id=location.parent_id) res = ValidationStatus.FAILED if parent_location: if location.parent_id != parent_location.id: cts_error("Parent resource {parent_odata_id:id} contains resource {odata_id:id}, " + "but child resource has invalid Location->ParentId. " + "Is: {id:ignore}. Expected: {expected:ignore}", parent_odata_id=parent_odata_id, odata_id=resource.odata_id, id=location.parent_id, expected=parent_location.id) res = ValidationStatus.FAILED if parent_odata_id and resource.contained_by: if parent_odata_id != resource.contained_by: cts_error("Resource {odata_id:id} has ContainedBy={contained_by:id}, " + "but it's parent is {parent_odata_id:id}", odata_id=resource.odata_id, contained_by=resource.contained_by, parent_odata_id=parent_odata_id) res = ValidationStatus.FAILED if res != ValidationStatus.FAILED and resource.contains: locations_within_scope = dict() for sub_resource_id in resource.contains: try: sub_resource = self.discover_container.odataid_lookup(sub_resource_id)[0] # validate that resources have unique location id within scope of their parent if sub_resource.location.id in locations_within_scope: fmt = ", ".join(['{locations[%d]:id}' % i for i, _ in enumerate(locations_within_scope[sub_resource.location.id])]) cts_error("Resource {odata_id:id} has location id {location_id:ignore} " + "that conflicts with resources: %s" % fmt, odata_id=sub_resource.odata_id, location_id=sub_resource.location.id, locations=locations_within_scope[sub_resource.location.id]) res = ValidationStatus.FAILED locations_within_scope.setdefault(sub_resource.location.id, []).append(sub_resource.odata_id) res = ValidationStatus.join_statuses(res, self._validate_from(sub_resource, remained_to_be_visited, location, resource.odata_id)) except IndexError: cts_error("Resource {odata_id:id} contains reference to nonexistent resource {subresource:id}", odata_id=resource.odata_id, subresource=sub_resource_id) res = ValidationStatus.FAILED return res
def crud_logical_drive(self, called_from_crud_remote_target=None): """ Test is trying to perform CRUD (create, read, update, delete) operations on a logical volume :type discovery_container: cts_core.discovery.discovery_container.DiscoveryContainer :type self.metadata_container: cts_core.metadata.self.metadata_container.MetadataContainer """ status = ValidationStatus.BLOCKED lvm_found = False logical_volumes = dict() logical_volume_groups = dict() logical_drive_collections = dict() for resource_link, resource in self.discovery_container.iteritems(): ok = True if self.metadata_container.entities[MetadataConstants.LOGICAL_DRIVE].compare_entities(resource.odata_type): print "DEBUG::found entity of type %s in %s" % (MetadataConstants.LOGICAL_DRIVE, resource_link) try: if resource.body["Type"] == "LVM": lvm_found = True if resource.body["Mode"] == "LVG": logical_volume_groups[resource_link] = resource elif resource.body["Mode"] == "LV": if "CapacityGiB" not in resource.body.keys(): print "DEBUG::%s drive does not have specified capacity" % resource_link ok = False if "Snapshot" in resource.body.keys() and resource.body["Snapshot"] == True: print "DEBUG::%s cannot be used - drive is a snapshot volume" % resource_link ok = False if ok: logical_volumes[resource_link] = resource elif resource.body["Type"] == "CEPH": if resource.body["Mode"] == "Pool": logical_volume_groups[resource_link] = resource elif resource.body["Mode"] == "RBD": if "CapacityGiB" not in resource.body.keys(): print "DEBUG::%s drive does not have specified capacity" % resource_link ok = False if ok: logical_volumes[resource_link] = resource except: print "WARNING::Incorrect resource %s structure" % resource_link elif self.metadata_container.entities[MetadataConstants.LOGICAL_DRIVE_COLLECTION]\ .compare_entities(resource.odata_type): logical_drive_collections[resource_link] = resource if lvm_found and (not logical_volume_groups or not logical_volumes): print "ERROR::Insufficient resources available on API for test. Found %s logical volumes and %s logical " \ " volume groups" % (len(logical_volumes), len(logical_volume_groups)) return ValidationStatus.BLOCKED # to speed things up, always look for the lowest capacity logical volume lowest_capacity = float('inf') chosen_logical_drive_collection, chosen_logical_volume, chosen_logical_volume_group = None, None, None inherited_bootable = None if not logical_volume_groups: if lvm_found: print "ERROR::No LVM volume group found, aborting" else: print "ERROR::No CEPH Pool device found, aborting" return ValidationStatus.BLOCKED for lvg_link, lvg in logical_volume_groups.iteritems(): # finding a collection matching this volume group possible_collections = [collection for collection in logical_drive_collections.keys() if collection in lvg_link] if not possible_collections: continue try: for lv in lvg.body["Links"]["LogicalDrives"]: # iterating LVs that have this LVG as a parent try: lv_link = lv["@odata.id"] lv_type = logical_volumes[lv_link].body["Type"] lv_mode = logical_volumes[lv_link].body["Mode"] lv_capacity = logical_volumes[lv_link].body["CapacityGiB"] lv_bootable = logical_volumes[lv_link].body["Bootable"] if lv_capacity < lowest_capacity: lowest_capacity = lv_capacity inherited_bootable = lv_bootable chosen_logical_volume = lv_link chosen_logical_volume_type = lv_type chosen_logical_volume_mode = lv_mode chosen_logical_volume_group = lvg_link chosen_logical_drive_collection = possible_collections[0] except KeyError: pass except KeyError: pass self.post_logical_drive_payload = dict( Name="LVM Logical Drive created by CTS", Type=chosen_logical_volume_type, Mode=chosen_logical_volume_mode, Protected=False, CapacityGiB=self.discovery_container[chosen_logical_volume].body["CapacityGiB"], Bootable=inherited_bootable if inherited_bootable else False, Snapshot=True, Links=dict( LogicalDrives=[{"@odata.id": chosen_logical_volume_group}], MasterDrive={"@odata.id": chosen_logical_volume} ) ) # Creating the first RBD if no other was found if not chosen_logical_volume: if lvm_found: print "ERROR::Could not find a (logical volume, logical volume group) pair suitable for the test" return status else: self.post_logical_drive_payload = dict( Name="CEPH Logical Drive created by CTS", Type="CEPH", Mode="RBD", Protected=False, CapacityGiB=self.discovery_container[chosen_logical_volume_group].body["CapacityGiB"]/2, Bootable=False, Snapshot=False, Links=dict( LogicalDrives=[{"@odata.id": chosen_logical_volume_group}], MasterDrive=None ) ) print "MESSAGE::Logical drive CRUD test will be performed on logical drive collection %s, creating logical volume " \ "based on %s on group %s" % (chosen_logical_drive_collection, chosen_logical_volume, chosen_logical_volume_group) self.expected_created_logical_drive_body = deepcopy(self.post_logical_drive_payload) # The logical drive in LogicalDrives link is really the volume group and is going to be seen under # UsedBy on REST self.expected_created_logical_drive_body["Links"]["UsedBy"] = \ self.expected_created_logical_drive_body["Links"]["LogicalDrives"] self.expected_created_logical_drive_body["Links"]["LogicalDrives"] = [] status = self._test_case_create_logical_drive(chosen_logical_drive_collection) if status == ValidationStatus.PASSED: # only perform other tests if creation was successful status = ValidationStatus.join_statuses(status, self._test_case_get_created_logical_drive()) if called_from_crud_remote_target: status = ValidationStatus.join_statuses(status, self.crud_remote_target(called_from_crud_logical_drive=True)) else: status = ValidationStatus.join_statuses(status, self._test_case_update_created_logical_drive()) status = ValidationStatus.join_statuses(status, self._test_case_delete_created_logical_drive()) status = ValidationStatus.join_statuses(status, self._test_case_get_deleted_logical_drive()) 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 crud_remote_target(self, called_from_crud_logical_drive=None): """ Test is checking for rsa compute blade presence :type discovery_container: cts_core.discovery.discovery_container.DiscoveryContainer :type self.metadata_container: cts_core.metadata.self.metadata_container.MetadataContainer """ target_iqn = "cts.target:cts_test_target" target_iqns = set() logical_volumes = dict() remote_target_collections = dict() for resource_link, resource in self.discovery_container.iteritems(): if self.metadata_container.entities[MetadataConstants.LOGICAL_DRIVE].compare_entities(resource.odata_type): print "DEBUG::found entity of type %s in %s" % (MetadataConstants.LOGICAL_DRIVE, resource_link) try: # looking for unprotected LVs with no targets associated with it if ((resource.body["Mode"] == "LV" or resource.body["Mode"] == "RBD") and not len(resource.body["Links"]["Targets"]) and not resource.body["Protected"]): logical_volumes[resource_link] = resource except: print "WARNING::Incorrect resource %s structure" % resource_link elif self.metadata_container.entities[MetadataConstants.REMOTE_TARGET_COLLECTION].compare_entities(resource.odata_type): remote_target_collections[resource_link] = resource elif self.metadata_container.entities[MetadataConstants.REMOTE_TARGET].compare_entities( resource.odata_type): try: for address in resource.body["Addresses"]: target_iqns.add(address["iSCSI"]["TargetIQN"]) except KeyError: pass if target_iqn in target_iqns: iqn_number = 0 while target_iqn + str(iqn_number) in target_iqns: iqn_number += 1 target_iqn = target_iqn + str(iqn_number) if not logical_volumes: print "WARNING::Insufficient resources available on API for test. Could not find an unprotected logical volume with"\ " no targets attached" if called_from_crud_logical_drive: print "ERROR::Creating a new logical volume for the target failed, skipping remote target tests" return ValidationStatus.BLOCKED else: print "MESSAGE::Trying to create a new logical volume for the target, then proceeding with remote target CRUD test" return self.crud_logical_drive(called_from_crud_remote_target=True) chosen_logical_volume = logical_volumes.keys()[0] chosen_remote_target_collection = remote_target_collections.keys()[0] print "MESSAGE::Remote Target CRUD test will be performed on remote target collection %s, creating remote target " \ "based on %s" % (chosen_remote_target_collection, chosen_logical_volume) self.post_remote_target_payload = dict( Addresses=[dict( iSCSI=dict( TargetIQN=target_iqn, TargetLUN=[dict( LUN=1, LogicalDrive={"@odata.id": chosen_logical_volume} )], ) )], Initiator=[dict( iSCSI=dict( InitiatorIQN="cts.initiator:initiator_cts_test" ) )] ) status = self._test_case_create_remote_target(chosen_remote_target_collection) if status == ValidationStatus.PASSED: # only perform other tests if creation was successful status = ValidationStatus.join_statuses(status, self._test_case_get_created_remote_target()) status = ValidationStatus.join_statuses(status, self._test_case_logical_volume_has_link_to_created_target( chosen_logical_volume)) status = ValidationStatus.join_statuses(status, self._test_case_update_created_remote_target()) status = ValidationStatus.join_statuses(status, self._test_case_delete_created_remote_target()) status = ValidationStatus.join_statuses(status, self._test_case_get_deleted_remote_target()) return status
def set_validation_status(self, status): print "STATUS::%s" % status if hasattr(self, "validation_status"): self.validation_status = ValidationStatus.join_statuses(self.validation_status, status) else: self.validation_status = status
def crud_remote_target(self): """ Test is checking for rsa compute blade presence """ ld_created_for_crud = False target_iqn = "cts.target:cts_test_target" target_iqns = set() chosen_logical_drive, chosen_remote_target_collection = None, None if not self.discovery_container.count_resources(MetadataConstants.LOGICAL_DRIVE, any_child_version=True): cts_error("Insufficient resources for API test. No logical drives found.") return ValidationStatus.BLOCKED logical_drives = dict(self.discovery_container.get_resources(MetadataConstants.LOGICAL_DRIVE, any_child_version=True, constraints=[equal("Mode", "LV"), equal("Protected", False)])) if not logical_drives: logical_drives = dict(self.discovery_container.get_resources(MetadataConstants.LOGICAL_DRIVE, any_child_version=True, constraints=[equal("Mode", "RBD"), equal("Protected", False)])) try: chosen_remote_target_collection = self.discovery_container.get_resources( MetadataConstants.REMOTE_TARGET_COLLECTION, any_child_version=True)[0][0] chosen_logical_drive = [link for link, resource in logical_drives.iteritems() if not len(resource.body["Links"]["Targets"])][0] except (KeyError, IndexError): pass if not chosen_remote_target_collection: cts_error("Insufficient resources available on API for test. Could not find any remote target collection") return ValidationStatus.BLOCKED if not chosen_logical_drive: cts_warning("Insufficient resources available on API for test. Could not find an unprotected logical drive " "with no targets attached") print "MESSAGE::Trying to create a new logical volume for the target, then proceeding with remote target "\ "CRUD test" if self.crud_logical_drive(create_logical_drive_for_target_crud=True) == ValidationStatus.PASSED: chosen_logical_drive = self.created_logical_drive ld_created_for_crud = True else: cts_error("Creating a new logical volume for the target failed, skipping remote target tests") return ValidationStatus.BLOCKED targets = [link for link, resource in self.discovery_container.get_resources(MetadataConstants.REMOTE_TARGET, any_child_version=True, constraints=[ from_collection(chosen_remote_target_collection) ])] for target in targets: addresses = self.discovery_container[target].body["Addresses"] for address in addresses: try: target_iqns.add(address["iSCSI"]["TargetIQN"]) except KeyError: pass if target_iqn in target_iqns: iqn_number = 0 while target_iqn + str(iqn_number) in target_iqns: iqn_number += 1 target_iqn = target_iqn + str(iqn_number) print "MESSAGE::Remote Target CRUD test will be performed on remote target collection %s, creating remote "\ "target based on %s with first lowest consecutive \'cts.target:cts_test_target<number>\' target IQN "\ "available: %s" % (chosen_remote_target_collection, chosen_logical_drive, target_iqn) self.post_remote_target_payload = dict( Addresses=[dict( iSCSI=dict( TargetIQN=target_iqn, TargetLUN=[dict( LUN=1, LogicalDrive={"@odata.id": urlparse(chosen_logical_drive).path} )], ) )], Initiator=[dict( iSCSI=dict( InitiatorIQN="cts.initiator:initiator_cts_test" ) )] ) status = self._test_case_create_remote_target(chosen_remote_target_collection) if status == ValidationStatus.PASSED: # only perform other tests if creation was successful status = ValidationStatus.join_statuses(status, self._test_case_get_created_remote_target()) status = ValidationStatus.join_statuses(status, self._test_case_logical_drive_has_link_to_created_target( chosen_logical_drive)) status = ValidationStatus.join_statuses(status, self._test_case_update_created_remote_target()) status = ValidationStatus.join_statuses(status, self._test_case_delete_created_remote_target()) status = ValidationStatus.join_statuses(status, self._test_case_get_deleted_remote_target()) if ld_created_for_crud: ld = self.created_logical_drive del_status = self._test_case_delete_created_logical_drive(ld) del_status = ValidationStatus.join_statuses(del_status, self._test_case_get_deleted_logical_drive(ld)) if status == ValidationStatus.PASSED and del_status != status: cts_error("Remote target CRUD test passed, but the logical drive it created failed to delete properly") status = ValidationStatus.PASSED_WITH_WARNINGS return status