def wait_for_job(self, operation, status_code, job): """Check if call is async, wait for it to complete. :param operation: the operation being performed :param status_code: the status code :param job: the job :returns: task -- list of dicts detailing tasks in the job :raises: VolumeBackendAPIException """ task = None if status_code == STATUS_202: rc, result, status, task = self.wait_for_job_complete(job) if rc != 0: exception_message = ( "Error %(operation)s. Status code: %(sc)s. " "Error: %(error)s. Status: %(status)s." % ({ 'operation': operation, 'sc': rc, 'error': six.text_type(result), 'status': status })) raise exception.VolumeBackendAPIException( data=exception_message) return task
def get_next_free_srdf_group(self): """Helper function to get RDFG on arrays that is free for use. :returns: next RDF group number on local and remote array """ local_in_use_rdfg_list = self.replication.get_rdf_group_list( array_id=self.conn.array_id) remote_in_use_rdfg_list = self.replication.get_rdf_group_list( array_id=self.conn.remote_array) new_rdfg = 100 local_list, remote_list = set(), set() rdfg_match = False for group in local_in_use_rdfg_list: local_list.add(group['rdfgNumber']) for group in remote_in_use_rdfg_list: remote_list.add(group['rdfgNumber']) while not rdfg_match and new_rdfg <= 250: if (new_rdfg not in local_list) and (new_rdfg not in remote_list): return new_rdfg new_rdfg += 1 if not rdfg_match: raise exception.VolumeBackendAPIException( 'There are no free RDFGs available on {arr1} and {arr2} from ' '100 or higher that can both have the same number.'.format( arr1=self.conn.array_id, arr2=self.conn.remote_array))
def rest_request(self, target_url, method, params=None, request_object=None, timeout=None): """Sends a request (GET, POST, PUT, DELETE) to the target api. :param target_url: target url (string) :param method: The method (GET, POST, PUT, or DELETE) :param params: Additional URL parameters :param request_object: request payload (dict) :param timeout: optional timeout override :return: server response object (dict), status code """ if timeout: timeout_val = timeout else: timeout_val = self.timeout if not self.session: self.establish_rest_session() url = ("{}{}".format(self.base_url, target_url)) try: if request_object: response = self.session.request(method=method, url=url, timeout=timeout_val, data=json.dumps(request_object, sort_keys=True, indent=4)) elif params: response = self.session.request(method=method, url=url, params=params, timeout=timeout_val) else: response = self.session.request(method=method, url=url, timeout=timeout_val) status_code = response.status_code try: response = response.json() except ValueError: LOG.debug("No response received from API. Status code " "received is: {}".format(status_code)) response = None LOG.debug("{} request to {} has returned with a status " "code of: {}".format(method, url, status_code)) return response, status_code except (requests.Timeout, requests.ConnectionError) as e: LOG.error("The {} request to URL {} timed-out, but may have been " "successful. Please check the array. Exception " "received: {}.".format(method, url, e)) return None, None except Exception as e: exp_message = ("The {} request to URL: {} failed with " "exception {}".format(method, url, e)) raise exception.VolumeBackendAPIException(data=exp_message)
def check_status_code_success(operation, status_code, message): """Check if a status code indicates success. :param operation: operation being performed -- str :param status_code: status code -- int :param message: server response -- str :raises: VolumeBackendAPIException """ if status_code not in [STATUS_200, STATUS_201, STATUS_202, STATUS_204]: exception_message = ( 'Error {op}. The status code received is {sc} and the message ' 'is {msg}.'.format(op=operation, sc=status_code, msg=message)) if status_code == STATUS_404: raise exception.ResourceNotFoundException( data=exception_message) if status_code == STATUS_401: raise exception.UnauthorizedRequestException() raise exception.VolumeBackendAPIException(data=exception_message)
def _wait_for_job_complete(): # Called at an interval until the job is finished. retries = kwargs['retries'] try: kwargs['retries'] = retries + 1 if not kwargs['wait_for_job_called']: is_complete, result, rc, status, task = ( self._is_job_finished(job_id)) if is_complete is True: kwargs['wait_for_job_called'] = True kwargs['rc'], kwargs['status'] = rc, status kwargs['result'], kwargs['task'] = result, task except Exception: exception_message = "Issue encountered waiting for job." LOG.exception(exception_message) raise exception.VolumeBackendAPIException( data=exception_message) return kwargs
def upload_file(self, **kwargs): """Upload a file. :key version: Unisphere version -- int :key no_version: if versionless uri -- bool :key category: resource category e.g. sloprovisioning -- str :key resource_level: resource level e.g. storagegroup -- str :key resource_level_id: resource level id -- str :key resource_type: optional name of resource -- str :key resource_type_id: optional name of resource -- str :key resource: optional name of resource -- str :key resource_id: optional name of resource -- str :key object_type: optional name of resource -- str :key object_type_id: optional name of resource -- str :key form_data: multipart form data -- dict :returns: response success details -- dict """ response_content = dict() target_uri = self._build_uri(**kwargs) response, status_code = self.rest_client.file_transfer_request( method=POST, upload=True, uri=target_uri, form_data=kwargs.get('form_data')) try: response_content = json.loads(response.text) msg = response_content.get('message') operation = ('upload {resource_type} resource'.format( resource_type=kwargs.get('resource_level'))) self.check_status_code_success(operation, status_code, msg) # Workaround until failed responses do not return 200 if not response_content.get('success', False): LOG.error(msg) raise exception.VolumeBackendAPIException(msg) LOG.info('The settings upload request was successful.') except ValueError: LOG.error('There request to upload to {uri} has failed and no ' 'message has been returned from Unisphere. Please check ' 'Unisphere REST logs for further details.'.format( uri=target_uri)) return response_content
def check_status_code_success(operation, status_code, message): """Check if a status code indicates success. :param operation: the operation :param status_code: the status code :param message: the server response :raises: VolumeBackendAPIException """ if status_code not in [STATUS_200, STATUS_201, STATUS_202, STATUS_204]: exception_message = ( 'Error {operation}. The status code received ' 'is {sc} and the message is {message}.'.format( operation=operation, sc=status_code, message=message)) if status_code == STATUS_404: raise exception.ResourceNotFoundException( data=exception_message) if status_code == STATUS_401: raise exception.UnauthorizedRequestException() else: raise exception.VolumeBackendAPIException( data=exception_message)
def wait_for_job(self, operation, status_code, job): """Check if call is async, wait for it to complete. :param operation: operation being performed -- str :param status_code: status code -- int :param job: job id -- str :returns: task details -- list :raises: VolumeBackendAPIException """ task = None if status_code == STATUS_202: rc, result, status, task = self.wait_for_job_complete(job) if rc != 0: exception_message = ( 'Error {op}. Status code: {sc}. Error: {err}. ' 'Status: {st}.'.format( op=operation, sc=rc, err=six.text_type(result), st=status)) LOG.error(exception_message) raise exception.VolumeBackendAPIException( data=exception_message) return task
def wait_for_job(self, operation, status_code, job): """Check if call is async, wait for it to complete. :param operation: the operation being performed :param status_code: the status code :param job: the job :returns: task -- list of dicts detailing tasks in the job :raises: VolumeBackendAPIException """ task = None if status_code == STATUS_202: rc, result, status, task = self.wait_for_job_complete(job) if rc != 0: exception_message = ( "Error {operation}. Status code: {sc}. " "Error: {error}. Status: {status}.".format( operation=operation, sc=rc, error=six.text_type(result), status=status)) LOG.error(exception_message) raise exception.VolumeBackendAPIException( data=exception_message) return task
def check_status_code_success(operation, status_code, message): """Check if a status code indicates success. :param operation: the operation :param status_code: the status code :param message: the server response :raises: VolumeBackendAPIException """ if status_code not in [STATUS_200, STATUS_201, STATUS_202, STATUS_204]: exception_message = ("Error %(operation)s. The status code " "received is %(sc)s and the message is " "%(message)s." % ({ 'operation': operation, 'sc': status_code, 'message': message })) if status_code == STATUS_404: raise exception.ResourceNotFoundException( data=exception_message) if status_code == STATUS_401: raise exception.UnauthorizedRequestException() else: raise exception.VolumeBackendAPIException( data=exception_message)
class PyU4VCommonTest(testtools.TestCase): """Test common.""" def setUp(self): """setUp.""" super(PyU4VCommonTest, self).setUp() self.data = pcd.CommonData() self.conf_file, self.conf_dir = ( pf.FakeConfigFile.create_fake_config_file()) univmax_conn.file_path = self.conf_file with mock.patch.object(rest_requests.RestRequests, 'establish_rest_session', return_value=pf.FakeRequestsSession()): self.conn = univmax_conn.U4VConn() self.common = self.conn.common self.common.interval = 1 self.common.retries = 1 def tearDown(self): """tearDown.""" super(PyU4VCommonTest, self).tearDown() pf.FakeConfigFile.delete_fake_config_file(self.conf_file, self.conf_dir) def test_wait_for_job_complete(self): """Test wait_for_job_complete.""" _, _, status, _ = self.common.wait_for_job_complete( self.data.job_list[0]) self.assertEqual('SUCCEEDED', status) @mock.patch.object(common.CommonFunctions, '_is_job_finished', return_value=(True, '', 0, 'SUCCEEDED', '')) def test_wait_for_job_complete_running(self, mock_job): """Test wait_for_job_complete running.""" _, _, status, _ = self.common.wait_for_job_complete( self.data.job_list[1]) self.assertEqual('SUCCEEDED', status) @mock.patch.object( common.CommonFunctions, '_is_job_finished', side_effect=[exception.VolumeBackendAPIException('random exception')]) def test_wait_for_job_complete_exception(self, mock_job): """Test wait_for_job_complete exception.""" self.assertRaises(exception.VolumeBackendAPIException, self.common.wait_for_job_complete, self.data.job_list[1]) @mock.patch.object(common.CommonFunctions, '_is_job_finished', return_value=(False, '', 0, 'RUNNING', '')) def test_wait_for_job_complete_timeout(self, mock_job): """Test wait_for_job_complete timeout.""" self.common.retries = 0 rc, result, status, _ = self.common.wait_for_job_complete( self.data.job_list[1]) self.assertEqual('RUNNING', status) self.assertEqual(-1, rc) self.assertIsNone(result) def test_get_job_by_id(self): """Test get_job_by_id.""" job = self.common.get_job_by_id(self.data.job_list[0]['jobId']) self.assertEqual('SUCCEEDED', job['status']) self.assertEqual('12345', job['jobId']) @mock.patch.object(common.CommonFunctions, 'get_job_by_id', return_value=pcd.CommonData.job_list[0]) def test_is_job_finished_success(self, mock_job): job = self.common._is_job_finished(self.data.job_list[0]['jobId']) self.assertEqual((True, None, 0, 'SUCCEEDED', None), job) @mock.patch.object(common.CommonFunctions, 'get_job_by_id', return_value=pcd.CommonData.job_list[2]) def test_is_job_finished_failure(self, mock_job): job = self.common._is_job_finished(self.data.job_list[2]['jobId']) self.assertEqual((True, None, -1, 'FAILED', None), job) @mock.patch.object(common.CommonFunctions, 'get_job_by_id', return_value=pcd.CommonData.job_list[1]) def test_is_job_finished_incomplete(self, mock_job): job = self.common._is_job_finished(self.data.job_list[1]['jobId']) self.assertEqual((False, None, 0, 'RUNNING', None), job) def test_check_status_code_success(self): """Test check_status_code_success.""" self.common.check_status_code_success('test-success', 201, '') self.assertRaises(exception.ResourceNotFoundException, self.common.check_status_code_success, 'test-404', 404, '') self.assertRaises(exception.UnauthorizedRequestException, self.common.check_status_code_success, 'test-401', 401, '') self.assertRaises(exception.VolumeBackendAPIException, self.common.check_status_code_success, 'test-500', 500, '') @mock.patch.object(common.CommonFunctions, 'wait_for_job_complete', side_effect=[(0, '', '', ''), (1, '', '', '')]) def test_wait_for_job(self, mock_complete): """Test wait_for_job.""" # Not an async job self.common.wait_for_job('sync-job', 200, {}) mock_complete.assert_not_called() # Async, completes successfully self.common.wait_for_job('sync-job', 202, {}) mock_complete.assert_called_once() # Async, job fails self.assertRaises(exception.VolumeBackendAPIException, self.common.wait_for_job, 'sync-job', 202, {}) def test_build_uri_version_control(self): """Test _build_uri.""" # No version supplied, use self.U4V_VERSION built_uri_1 = self.common._build_uri(category=SLOPROVISIONING, resource_level=SYMMETRIX, resource_level_id=self.data.array, resource_type=VOLUME) uri_1 = ('/{ver}/sloprovisioning/symmetrix/{array}/volume'.format( ver=UNISPHERE_VERSION, array=self.data.array)) self.assertEqual(uri_1, built_uri_1) # version supplied as keyword argument resource_name = self.data.device_id version_2 = self.data.U4P_VERSION built_uri_2 = self.common._build_uri(category=SLOPROVISIONING, resource_level=SYMMETRIX, resource_level_id=self.data.array, resource_type=VOLUME, resource_type_id=resource_name, version=version_2) uri_2 = ( '/{ver}/sloprovisioning/symmetrix/{array}/volume/{res}'.format( ver=version_2, array=self.data.array, res=resource_name)) self.assertEqual(uri_2, built_uri_2) # version and no_version keywords supplied, no_version overruled built_uri_3 = self.common._build_uri(category=SLOPROVISIONING, resource_level=SYMMETRIX, resource_level_id=self.data.array, resource_type=VOLUME, resource_type_id=resource_name, version=UNISPHERE_VERSION, no_version=True) uri_3 = ( '/{ver}/sloprovisioning/symmetrix/{array}/volume/{res}'.format( ver=UNISPHERE_VERSION, array=self.data.array, res=resource_name)) self.assertEqual(uri_3, built_uri_3) # no_version flag passed, no version required for URI built_uri_4 = self.common._build_uri(category=SLOPROVISIONING, resource_level=SYMMETRIX, resource_level_id=self.data.array, resource_type=VOLUME, resource_type_id=resource_name, no_version=True) uri_4 = ('/sloprovisioning/symmetrix/{array}/volume/{res}'.format( array=self.data.array, res=resource_name)) self.assertEqual(uri_4, built_uri_4) def test_traditional_build_uri(self): """Test _build_uri.""" # Only default args arrayID, category, resource_type passed built_uri = self.common._build_uri(self.data.array, 'sloprovisioning', 'volume') temp_uri = ('/{}/sloprovisioning/symmetrix/{array}/volume'.format( self.data.U4P_VERSION, array=self.data.array)) self.assertEqual(temp_uri, built_uri) # Default args passed along with resource_name and version kwarg built_uri_2 = self.common._build_uri(self.data.array, 'sloprovisioning', 'volume', version=self.data.U4P_VERSION, resource_name=self.data.device_id) temp_uri_2 = ( '/{}/sloprovisioning/symmetrix/{array}/volume/{res}'.format( self.data.U4P_VERSION, array=self.data.array, res=self.data.device_id)) self.assertEqual(temp_uri_2, built_uri_2) def test_new_build_uri_minimum(self): """Test _build_uri.""" # Pass in only minimum required kwargs - version is optional built_uri_1 = self.common._build_uri(version=self.data.U4P_VERSION, category='sloprovisioning', resource_level='symmetrix') temp_uri_1 = '/{}/sloprovisioning/symmetrix'.format( self.data.U4P_VERSION) self.assertEqual(temp_uri_1, built_uri_1) def test_new_build_uri_resource_level_id(self): """Test _build_uri.""" # Pass in minimum kwargs with specified resource_level_id built_uri_2 = self.common._build_uri(version=self.data.U4P_VERSION, category='sloprovisioning', resource_level='symmetrix', resource_level_id=self.data.array) temp_uri_2 = ('/{}/sloprovisioning/symmetrix/{}'.format( self.data.U4P_VERSION, self.data.array)) self.assertEqual(temp_uri_2, built_uri_2) def test_new_build_uri_resource_type(self): # Pass in minimum kwargs with specified resource_type built_uri_3 = self.common._build_uri(version=self.data.U4P_VERSION, category='sloprovisioning', resource_level='symmetrix', resource_level_id=self.data.array, resource_type='storagegroup') temp_uri_3 = ('/{}/sloprovisioning/symmetrix/{}/{}'.format( self.data.U4P_VERSION, self.data.array, 'storagegroup')) self.assertEqual(temp_uri_3, built_uri_3) def test_new_build_uri_resource_type_id(self): # Pass in minimum kwargs with specified resource_type_id built_uri_4 = self.common._build_uri( version=self.data.U4P_VERSION, category='sloprovisioning', resource_level='symmetrix', resource_level_id=self.data.array, resource_type='storagegroup', resource_type_id=self.data.storagegroup_name_1) temp_uri_4 = ('/{}/sloprovisioning/symmetrix/{}/{}/{}'.format( self.data.U4P_VERSION, self.data.array, 'storagegroup', self.data.storagegroup_name_1)) self.assertEqual(temp_uri_4, built_uri_4) def test_new_build_uri_resource(self): # Pass in minimum kwargs with specified resource built_uri_5 = self.common._build_uri( version=self.data.U4P_VERSION, category='sloprovisioning', resource_level='symmetrix', resource_level_id=self.data.array, resource_type='storagegroup', resource_type_id=self.data.storagegroup_name_1, resource='snap') temp_uri_5 = ('/{}/sloprovisioning/symmetrix/{}/{}/{}/{}'.format( self.data.U4P_VERSION, self.data.array, 'storagegroup', self.data.storagegroup_name_1, 'snap')) self.assertEqual(temp_uri_5, built_uri_5) def test_new_build_uri_resource_id(self): # Pass in minimum kwargs with specified resource_id built_uri_6 = self.common._build_uri( version=self.data.U4P_VERSION, category='sloprovisioning', resource_level='symmetrix', resource_level_id=self.data.array, resource_type='storagegroup', resource_type_id=self.data.storagegroup_name_1, resource='snap', resource_id=self.data.snapshot_name) temp_uri_6 = ('/{}/sloprovisioning/symmetrix/{}/{}/{}/{}/{}'.format( self.data.U4P_VERSION, self.data.array, 'storagegroup', self.data.storagegroup_name_1, 'snap', self.data.snapshot_name)) self.assertEqual(temp_uri_6, built_uri_6) def test_new_build_uri_object_type(self): # Pass in minimum kwargs with specified object_type built_uri_7 = self.common._build_uri( version=self.data.U4P_VERSION, category='sloprovisioning', resource_level='symmetrix', resource_level_id=self.data.array, resource_type='storagegroup', resource_type_id=self.data.storagegroup_name_1, resource='snap', resource_id=self.data.snapshot_name, object_type='generation') temp_uri_7 = ('/{}/sloprovisioning/symmetrix/{}/{}/{}/{}/{}/{}'.format( self.data.U4P_VERSION, self.data.array, 'storagegroup', self.data.storagegroup_name_1, 'snap', self.data.snapshot_name, 'generation')) self.assertEqual(temp_uri_7, built_uri_7) def test_new_build_uri_object_type_id(self): # Pass in minimum kwargs with specified object_type_id built_uri_8 = self.common._build_uri( version=self.data.U4P_VERSION, category='sloprovisioning', resource_level='symmetrix', resource_level_id=self.data.array, resource_type='storagegroup', resource_type_id=self.data.storagegroup_name_1, resource='snap', resource_id=self.data.snapshot_name, object_type='generation', object_type_id='1') temp_uri_8 = ( '/{}/sloprovisioning/symmetrix/{}/{}/{}/{}/{}/{}/{}'.format( self.data.U4P_VERSION, self.data.array, 'storagegroup', self.data.storagegroup_name_1, 'snap', self.data.snapshot_name, 'generation', '1')) self.assertEqual(temp_uri_8, built_uri_8) def test_new_build_uri_performance(self): # Category is performance so no use of version in URI built_uri_9 = self.common._build_uri(category='performance', resource_level='Array', resource_type='keys') temp_uri_9 = '/performance/Array/keys' self.assertEqual(temp_uri_9, built_uri_9) def test_get_request(self): """Test get_request.""" message = self.common.get_request('/version', resource_type='version') self.assertEqual(self.data.server_version, message) def test_get_resource(self): """Test get_resource.""" # Traditional Method message = self.common.get_resource(self.data.array, 'sloprovisioning', 'volume', resource_name=None, params=None) self.assertEqual(self.data.volume_list[2], message) # New Method message_1 = self.common.get_resource(category='sloprovisioning', resource_level='symmetrix', resource_level_id=self.data.array, resource_type='volume') self.assertEqual(self.data.volume_list[2], message_1) def test_create_resource(self): """Test create_resource.""" # Traditional Method message = self.common.create_resource(self.data.array, 'sloprovisioning', 'storagegroup', {}) self.assertEqual(self.data.job_list[0], message) # New Method message_1 = self.common.create_resource( category='sloprovisioning', resource_level='storagegroup', resource_level_id=self.data.array) self.assertEqual(self.data.job_list[0], message_1) def test_modify_resource(self): """Test modify_resource.""" # Traditional Method message = self.common.modify_resource(self.data.array, 'sloprovisioning', 'storagegroup', {}) self.assertEqual(self.data.job_list[0], message) # New Method message_1 = self.common.modify_resource( category='sloprovisioning', resource_level='storagegroup', resource_level_id=self.data.array) self.assertEqual(self.data.job_list[0], message_1) def test_delete_resource(self): """Test delete_resource.""" # Traditional Method self.common.delete_resource(self.data.array, 'sloprovisioning', 'storagegroup', self.data.storagegroup_name) # New Method self.common.delete_resource( category='sloprovisioning', resource_level='storagegroup', resource_level_id=self.data.array, resource_type_id=self.data.storagegroup_name) def test_create_list_from_file(self): """Test create_list_from_file.""" example_file = """Item1\nItem2\nItem3""" with mock.patch('builtins.open', mock.mock_open(read_data=example_file), create=True): list_from_file = self.common.create_list_from_file(example_file) self.assertTrue(isinstance(list_from_file, list)) self.assertIn('Item1', list_from_file) @mock.patch('builtins.open', new_callable=mock.mock_open) def test_read_csv_values(self, mck_open): """Test read_csv_values.""" csv_response = [{ 'kpi_a': 'perf_data_1', 'kpi_b': 'perf_data_2' }, { 'kpi_a': 'perf_data_3', 'kpi_b': 'perf_data_4' }, { 'kpi_a': 'perf_data_5', 'kpi_b': 'perf_data_6' }] with mock.patch.object(csv, 'DictReader', return_value=csv_response): csv_data = self.common.read_csv_values(file_name='mock_csv_file') reference_csv_response = { 'kpi_a': ['perf_data_1', 'perf_data_3', 'perf_data_5'], 'kpi_b': ['perf_data_2', 'perf_data_4', 'perf_data_6'] } self.assertTrue(isinstance(csv_data, dict)) self.assertEqual(reference_csv_response, csv_data) def test_get_uni_version(self): """Test get_uni_version.""" version, major_version = self.common.get_uni_version() self.assertEqual(self.data.server_version['version'], version) self.assertEqual(self.data.u4v_version, major_version) def test_get_array_list(self): """Test get_array_list.""" array_list = self.common.get_array_list() self.assertEqual(self.data.symm_list['symmetrixId'], array_list) def test_get_v3_or_newer_array_list(self): """Test get_v3_or_newer_array_list.""" array_list = self.common.get_v3_or_newer_array_list() self.assertEqual(self.data.symm_list['symmetrixId'], array_list) def test_get_array(self): """Test get_array.""" array_details = self.common.get_array(self.data.array) self.assertEqual(self.data.symmetrix[0], array_details) def test_get_wlp_info_success(self): """Test get_wlp_information success.""" with mock.patch.object( self.common, 'get_resource', return_value=self.data.wlp_info) as mck_wlp_info: wlp_info = self.common.get_wlp_information(self.data.array) self.assertEqual(self.data.wlp_info, wlp_info) mck_wlp_info.assert_called_once_with( category='wlp', resource_level='symmetrix', resource_level_id=self.data.array) def test_get_wlp_info_fail(self): """Test get_wlp_information fail.""" with mock.patch.object(self.common, 'get_resource', return_value=None): wlp_info = self.common.get_wlp_information(self.data.array) self.assertFalse(wlp_info) self.assertIsInstance(wlp_info, dict) def test_get_headroom_success(self): """Test get_headroom success.""" with mock.patch.object( self.common, 'get_resource', return_value=self.data.headroom_array) as mck_head: headroom = self.common.get_headroom(self.data.array, self.data.workload, 'SRP_TEST', 'Gold') self.assertEqual(self.data.headroom_array['gbHeadroom'], headroom) params = { 'srp': 'SRP_TEST', 'slo': 'Gold', 'workloadtype': self.data.workload } mck_head.assert_called_once_with(category='wlp', resource_level='symmetrix', resource_level_id=self.data.array, resource_type='headroom', params=params) def test_get_headroom_fail(self): """Test get_headroom fail.""" with mock.patch.object(self.common, 'get_resource', return_value=None): headroom = self.common.get_headroom(self.data.array, self.data.workload) self.assertFalse(headroom) self.assertIsInstance(headroom, list) def test_check_ipv4(self): """Test check_ipv4.""" self.assertTrue(self.common.check_ipv4(self.data.ip)) def test_check_ipv4_fail(self): """Test check_ipv4.""" self.assertFalse(self.common.check_ipv4('invalid')) def test_check_ipv6(self): """Test check_ipv6.""" self.assertTrue(self.common.check_ipv6(self.data.ipv6)) def test_check_ipv6_fail(self): """Test check_ipv6.""" self.assertFalse(self.common.check_ipv6('invalid')) def test_get_iterator_page_list(self): """Test get_iterator_page_list.""" iterator_page = self.common.get_iterator_page_list('123', 1, 1000) self.assertEqual(self.data.iterator_page['result'], iterator_page) def test_get_iterator_results(self): rest_response_in = self.data.vol_with_pages ref_response = [{'volumeId': '00001'}, {'volumeId': '00002'}] response = self.common.get_iterator_results(rest_response_in) self.assertEqual(response, ref_response) def test_convert_to_snake_case(self): """Test convert_to_snake_case variations.""" string_1 = 'CamelCase' string_2 = 'camelCase' string_3 = 'Camel_Case' string_4 = 'snake_case' self.assertEqual(self.common.convert_to_snake_case(string_1), 'camel_case') self.assertEqual(self.common.convert_to_snake_case(string_2), 'camel_case') self.assertEqual(self.common.convert_to_snake_case(string_3), 'camel_case') self.assertEqual(self.common.convert_to_snake_case(string_4), 'snake_case')
def modify_metrodr_environment(self, environment_name, action, metro=False, dr=False, keep_r2=False, force=False, symforce=False, _async=False, dr_replication_mode=None): """Performs Functions to modify state of MetroDR environment. :param environment_name: name of Metro Dr Environment up to 16 characters-- str :param action: action to be performed on Environment, Establish, Failover, Failback, Restore, SetMode, Split, UpdateR1 --str :param metro: directs action towards R11--R21 Metro Device leg of Metro DR environment -- bool :param dr: directs action towards Device Pairs on Disaster Recovery leg of Metro DR environment -- bool :param keep_r2: Used with Suspend Option, Ensures that the R2 data on Metro remains available to host -- bool :param force: forces operation to complete, used with caution, not recommended as part of fully automated workflow --bool :param symforce: forces operation to complete, used with caution, requires ALLOW_SRDF_SYMFORCE parameter to be set in solutions enabler options file, default is not enabled,not recommended as part of fully automated workflow -- bool :param _async: if call should be executed asynchronously or synchronously -- bool :param dr_replication_mode: set mode of DR link, AdaptiveCopyDisk or Asynchronous -- str :returns: details of metro dr environment and state -- dict """ metro_dr_action = constants.METRO_DR_ACTIONS.get(action.upper()) if metro_dr_action: payload = {'action': metro_dr_action} else: msg = ('SRDF Action must be one of [Establish, Split, Suspend, ' 'Recover, Restore, Resume, Failover, Failback, Update_R1, ' 'SetMode]') LOG.exception(msg) raise exception.VolumeBackendAPIException(data=msg) action_params = constants.METRO_DR_ACTION_PARAMS.get( metro_dr_action.upper()) if metro_dr_action == 'Suspend': payload.update({ action_params: { 'metro': metro, 'force': force, 'keep_r2': keep_r2, 'dr': dr, 'symforce': symforce } }) elif metro_dr_action in ['Failover', 'Failback', 'Split', 'UpdateR1']: payload.update( {action_params: { 'force': force, 'symforce': symforce }}) elif metro_dr_action in ['Establish', 'Restore']: if (metro and dr) and metro_dr_action == 'Restore': msg = ('Restore Operation can only be performed on a single ' 'SRDF leg, please choice either Metro or DR not both') LOG.exception(msg) raise exception.InvalidInputException(message=msg) payload.update({ action_params: { 'metro': metro, 'force': force, 'dr': dr, 'symforce': symforce } }) elif metro_dr_action == 'SetMode': payload.update({ action_params: { "mode": dr_replication_mode, "force": force, "symforce": symforce } }) if _async: payload.update(ASYNC_UPDATE) return self.modify_resource(category=REPLICATION, resource_level=SYMMETRIX, resource_level_id=self.array_id, resource_type=METRO_DR, resource_type_id=environment_name, payload=payload)
def rest_request(self, target_url, method, params=None, request_object=None, timeout=None): """Send a request to the target api. Valid methods are 'GET', 'POST', 'PUT', 'DELETE'. :param target_url: target url --str :param method: method -- str :param params: Additional URL parameters -- dict :param request_object: request payload -- dict :param timeout: optional timeout override -- int :returns: server response, status code -- dict, int """ if timeout: timeout_val = timeout else: timeout_val = self.timeout if not self.session: self.session = self.establish_rest_session() url = '{base_url}{target_url}'.format(base_url=self.base_url, target_url=target_url) try: if request_object: response = self.session.request(method=method, url=url, timeout=timeout_val, data=json.dumps(request_object, sort_keys=True, indent=4)) elif params: response = self.session.request(method=method, url=url, params=params, timeout=timeout_val) else: response = self.session.request(method=method, url=url, timeout=timeout_val) status_code = response.status_code try: response = response.json() except ValueError: response = None if not status_code: status_code = None LOG.debug('No response received from API. Status code ' 'received is: {sc}.'.format(sc=status_code)) LOG.debug('{method} request to {url} has returned with a status ' 'code of: {sc}.'.format(method=method, url=url, sc=status_code)) return response, status_code except requests.Timeout as error: LOG.error( 'The {method} request to URL {url} timed-out, but may have ' 'been successful. Please check the array. Exception received: ' '{exc}.'.format(method=method, url=url, exc=error)) return None, None except r_exc.SSLError as error: msg = ( 'The connection to {base} has encountered an SSL error. ' 'Please check your SSL config or supplied SSL cert in Cinder ' 'configuration. SSL Exception message: {m}'.format( base=self.base_url, m=error)) raise r_exc.SSLError(msg) from error except (r_exc.ConnectionError, r_exc.HTTPError) as error: exc_class, __, __ = sys.exc_info() msg = ( 'The {met} to Unisphere server {base} has experienced a {exc} ' 'error. Please check your Unisphere server connection and ' 'availability. Exception message: {msg}'.format( met=method, base=self.base_url, exc=error.__class__.__name__, msg=error)) raise exc_class(msg) from error except Exception as error: exp_message = ( 'The {method} request to URL {url} failed with exception: ' '{e}.'.format(method=method, url=url, e=error)) raise exception.VolumeBackendAPIException( data=exp_message) from error
def file_transfer_request(self, method, uri, timeout=None, download=False, r_obj=None, upload=False, form_data=None): """Send a file transfer request via REST to the target API. Valid methods are 'POST' and 'PUT'. :param method: request method -- str :param uri: target uri -- str :param timeout: optional timeout override -- int :param download: if download request -- bool :param r_obj: download request payload -- dict :param upload: if upload request -- bool :param form_data: upload multipart form data -- dict :returns: server response, status code -- dict, int :raises: InvalidInputException, VolumeBackendAPIException, Timeout, SSLError, ConnectionError, HTTPError """ if download and not upload: headers = { CONTENT_TYPE: APP_JSON, ACCEPT: APP_OCT, USER_AGENT: ua_details, APP_TYPE: self.headers.get('application-type') } elif upload and not download: headers = { ACCEPT_ENC: APP_MPART, USER_AGENT: ua_details, APP_TYPE: self.headers.get('application-type') } else: msg = ('You must select one of upload/download for ' 'file_transfer_request method.') LOG.error(msg) raise exception.InvalidInputException(msg) timeout_val = self.timeout if not timeout else timeout data = json.dumps(r_obj, sort_keys=True, indent=4) if r_obj else None url = '{base_url}{uri}'.format(base_url=self.base_url, uri=uri) try: ft_session = self.establish_rest_session(headers=headers) response = ft_session.request(method=method, url=url, timeout=timeout_val, stream=download, data=data, files=form_data) ft_session.close() status_code = response.status_code LOG.debug('{method} request to {url} has returned with a status ' 'code of: {sc}.'.format(method=method, url=url, sc=status_code)) return response, status_code except requests.Timeout as error: LOG.error( 'The {method} request to URL {url} timed-out, but may have ' 'been successful. Please check the array. Exception received: ' '{exc}.'.format(method=method, url=url, exc=error)) return None, None except r_exc.SSLError as error: msg = ( 'The connection to {base} has encountered an SSL error. ' 'Please check your SSL config or supplied SSL cert in Cinder ' 'configuration. SSL Exception message: {m}'.format( base=self.base_url, m=error)) raise r_exc.SSLError(msg) from error except (r_exc.ConnectionError, r_exc.HTTPError) as error: exc_class, __, __ = sys.exc_info() msg = ( 'The {met} to Unisphere server {base} has experienced a {exc} ' 'error. Please check your Unisphere server connection and ' 'availability. Exception message: {msg}'.format( met=method, base=self.base_url, exc=error.__class__.__name__, msg=error)) raise exc_class(msg) from error except Exception as error: exp_message = ( 'The {method} request to URL {url} failed with exception: ' '{e}.'.format(method=method, url=url, e=error)) raise exception.VolumeBackendAPIException(data=exp_message)
def rest_request(self, target_url, method, params=None, request_object=None, timeout=None): """Send a request (GET, POST, PUT, DELETE) to the target api. :param target_url: target url (string) :param method: The method (GET, POST, PUT, or DELETE) :param params: Additional URL parameters :param request_object: request payload (dict) :param timeout: optional timeout override :return: server response object (dict), status code """ if timeout: timeout_val = timeout else: timeout_val = self.timeout if not self.session: self.establish_rest_session() url = "%s%s" % (self.base_url, target_url) try: if request_object: response = self.session.request(method=method, url=url, timeout=timeout_val, data=json.dumps(request_object, sort_keys=True, indent=4)) elif params: response = self.session.request(method=method, url=url, params=params, timeout=timeout_val) else: response = self.session.request(method=method, url=url, timeout=timeout_val) status_code = response.status_code try: response = response.json() except ValueError: LOG.debug( "No response received from API. Status code " "received is %(status_code)s.", {'status_code': status_code}) response = None LOG.debug( "%(method)s request to %(url)s has returned " "with a status code of: %(status_code)s.", { 'method': method, 'url': url, 'status_code': status_code }) return response, status_code except (requests.Timeout, requests.ConnectionError) as e: LOG.error( "The %(method)s request to URL %(url)s " "timed-out, but may have been successful. Please check " "the array. Exception received: %(e)s.", { 'method': method, 'url': url, 'e': e }) return None, None except Exception as e: exp_message = ("The %(method)s request to URL %(url)s failed " "with exception %(e)s.", { 'method': method, 'url': url, 'e': e }) raise exception.VolumeBackendAPIException(data=exp_message)