Example #1
0
    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
Example #2
0
    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))
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
        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
Example #6
0
    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
Example #7
0
    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)
Example #8
0
    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
Example #9
0
    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
Example #10
0
    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)
Example #11
0
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')
Example #12
0
    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)
Example #13
0
    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
Example #14
0
    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)
Example #15
0
    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)