예제 #1
0
 def _create_write_connection(self, method, url,
                              file_size=None,
                              cookies=None,
                              overwrite=None,
                              content_type=None,
                              cacerts=False,
                              ssl_thumbprint=None):
     """Create HTTP connection to write to VMDK file."""
     LOG.debug("Creating HTTP connection to write to file with "
               "size = %(file_size)d and URL = %(url)s.",
               {'file_size': file_size,
                'url': url})
     try:
         conn = self._create_connection(url, method, cacerts,
                                        ssl_thumbprint)
         headers = {'User-Agent': USER_AGENT}
         if file_size:
             headers.update({'Content-Length': str(file_size)})
         if overwrite:
             headers.update({'Overwrite': overwrite})
         if cookies:
             headers.update({'Cookie':
                            self._build_vim_cookie_header(cookies)})
         if content_type:
             headers.update({'Content-Type': content_type})
         for key, value in headers.items():
             conn.putheader(key, value)
         conn.endheaders()
         return conn
     except requests.RequestException as excep:
         excep_msg = _("Error occurred while creating HTTP connection "
                       "to write to VMDK file with URL = %s.") % url
         LOG.exception(excep_msg)
         raise exceptions.VimConnectionException(excep_msg, excep)
예제 #2
0
 def connect(self, method, content_length, cookie):
     try:
         if self._scheme == 'http':
             conn = httplib.HTTPConnection(self._server)
         elif self._scheme == 'https':
             # TODO(browne): This needs to be changed to use python requests
             conn = httplib.HTTPSConnection(self._server)  # nosec
         else:
             excep_msg = _("Invalid scheme: %s.") % self._scheme
             LOG.error(excep_msg)
             raise ValueError(excep_msg)
         conn.putrequest(method, '/folder/%s?%s' % (self.path, self._query))
         conn.putheader('User-Agent', constants.USER_AGENT)
         conn.putheader('Content-Length', content_length)
         conn.putheader('Cookie', cookie)
         conn.endheaders()
         LOG.debug("Created HTTP connection to transfer the file with "
                   "URL = %s.", str(self))
         return conn
     except (httplib.InvalidURL, httplib.CannotSendRequest,
             httplib.CannotSendHeader) as excep:
         excep_msg = _("Error occurred while creating HTTP connection "
                       "to write to file with URL = %s.") % str(self)
     LOG.exception(excep_msg)
     raise exceptions.VimConnectionException(excep_msg, excep)
예제 #3
0
        def _invoke_api(module, method, *args, **kwargs):
            try:
                api_method = getattr(module, method)
                return api_method(*args, **kwargs)
            except exceptions.VimFaultException as excep:
                # If this is due to an inactive session, we should re-create
                # the session and retry.
                if exceptions.NOT_AUTHENTICATED in excep.fault_list:
                    # The NotAuthenticated fault is set by the fault checker
                    # due to an empty response. An empty response could be a
                    # valid response; for e.g., response for the query to
                    # return the VMs in an ESX server which has no VMs in it.
                    # Also, the server responds with an empty response in the
                    # case of an inactive session. Therefore, we need a way to
                    # differentiate between these two cases.
                    if self.is_current_session_active():
                        LOG.debug("Returning empty response for "
                                  "%(module)s.%(method)s invocation.",
                                  {'module': module,
                                   'method': method})
                        return []
                    else:
                        # empty response is due to an inactive session
                        excep_msg = (
                            _("Current session: %(session)s is inactive; "
                              "re-creating the session while invoking "
                              "method %(module)s.%(method)s.") %
                            {'session': _trunc_id(self._session_id),
                             'module': module,
                             'method': method})
                        LOG.debug(excep_msg)
                        self._create_session()
                        raise exceptions.VimConnectionException(excep_msg,
                                                                excep)
                else:
                    # no need to retry for other VIM faults like
                    # InvalidArgument
                    # Raise specific exceptions here if possible
                    if excep.fault_list:
                        LOG.debug("Fault list: %s", excep.fault_list)
                        fault = excep.fault_list[0]
                        clazz = exceptions.get_fault_class(fault)
                        if clazz:
                            raise clazz(six.text_type(excep),
                                        details=excep.details)
                    raise

            except exceptions.VimConnectionException:
                with excutils.save_and_reraise_exception():
                    # Re-create the session during connection exception only
                    # if the session has expired. Otherwise, it could be
                    # a transient issue.
                    if not self.is_current_session_active():
                        LOG.debug("Re-creating session due to connection "
                                  "problems while invoking method "
                                  "%(module)s.%(method)s.",
                                  {'module': module,
                                   'method': method})
                        self._create_session()
예제 #4
0
    def test_invoke_api_not_recreate_session(self):
        api_session = self._create_api_session(True)
        api_session._create_session = mock.Mock()
        vim_obj = api_session.vim
        vim_obj.SessionIsActive.return_value = True
        ret = mock.Mock()
        responses = [exceptions.VimConnectionException(None), ret]

        def api(*args, **kwargs):
            response = responses.pop(0)
            if isinstance(response, Exception):
                raise response
            return response

        module = mock.Mock()
        module.api = api
        with mock.patch.object(greenthread, 'sleep'):
            self.assertEqual(ret, api_session.invoke_api(module, 'api'))
        self.assertFalse(api_session._create_session.called)
예제 #5
0
    def write(self, data):
        """Write data to the file.

        :param data: data to be written
        :raises: VimConnectionException, VimException
        """
        try:
            self._file_handle.send(data)
        except requests.RequestException as excep:
            excep_msg = _("Connection error occurred while writing data to"
                          " %s.") % self._url
            LOG.exception(excep_msg)
            raise exceptions.VimConnectionException(excep_msg, excep)
        except Exception as excep:
            # TODO(vbala) We need to catch and raise specific exceptions
            # related to connection problems, invalid request and invalid
            # arguments.
            excep_msg = _("Error occurred while writing data to"
                          " %s.") % self._url
            LOG.exception(excep_msg)
            raise exceptions.VimException(excep_msg, excep)
예제 #6
0
        def request_handler(managed_object, **kwargs):
            """Handler for vSphere API calls.

            Invokes the API and parses the response for fault checking and
            other errors.

            :param managed_object: managed object reference argument of the
                                   API call
            :param kwargs: keyword arguments of the API call
            :returns: response of the API call
            :raises: VimException, VimFaultException, VimAttributeException,
                     VimSessionOverLoadException, VimConnectionException
            """
            try:
                if isinstance(managed_object, str):
                    # For strings, use string value for value and type
                    # of the managed object.
                    managed_object = vim_util.get_moref(managed_object,
                                                        managed_object)
                if managed_object is None:
                    return
                request = getattr(self.client.service, attr_name)
                response = request(managed_object, **kwargs)
                if (attr_name.lower() == 'retrievepropertiesex'):
                    Service._retrieve_properties_ex_fault_checker(response)
                return response
            except exceptions.VimFaultException:
                # Catch the VimFaultException that is raised by the fault
                # check of the SOAP response.
                raise

            except suds.WebFault as excep:
                fault_string = None
                if excep.fault:
                    fault_string = excep.fault.faultstring

                doc = excep.document
                detail = None
                if doc is not None:
                    detail = doc.childAtPath('/detail')
                    if not detail:
                        # NOTE(arnaud): this is needed with VC 5.1
                        detail = doc.childAtPath('/Envelope/Body/Fault/detail')
                fault_list = []
                details = {}
                if detail:
                    for fault in detail.getChildren():
                        fault_type = fault.get('type')
                        if fault_type.endswith(exceptions.SECURITY_ERROR):
                            fault_type = exceptions.NOT_AUTHENTICATED
                        fault_list.append(fault_type)
                        for child in fault.getChildren():
                            details[child.name] = child.getText()
                raise exceptions.VimFaultException(fault_list, fault_string,
                                                   excep, details)

            except AttributeError as excep:
                raise exceptions.VimAttributeException(
                    _("No such SOAP method %s.") % attr_name, excep)

            except (httplib.CannotSendRequest,
                    httplib.ResponseNotReady,
                    httplib.CannotSendHeader) as excep:
                raise exceptions.VimSessionOverLoadException(
                    _("httplib error in %s.") % attr_name, excep)

            except requests.RequestException as excep:
                raise exceptions.VimConnectionException(
                    _("requests error in %s.") % attr_name, excep)

            except Exception as excep:
                # TODO(vbala) should catch specific exceptions and raise
                # appropriate VimExceptions.

                # Socket errors which need special handling; some of these
                # might be caused by server API call overload.
                if (six.text_type(excep).find(ADDRESS_IN_USE_ERROR) != -1 or
                        six.text_type(excep).find(CONN_ABORT_ERROR)) != -1:
                    raise exceptions.VimSessionOverLoadException(
                        _("Socket error in %s.") % attr_name, excep)
                # Type error which needs special handling; it might be caused
                # by server API call overload.
                elif six.text_type(excep).find(RESP_NOT_XML_ERROR) != -1:
                    raise exceptions.VimSessionOverLoadException(
                        _("Type error in %s.") % attr_name, excep)
                else:
                    raise exceptions.VimException(
                        _("Exception in %s.") % attr_name, excep)
예제 #7
0
def fake_temp_session_exception():
    raise vexc.VimConnectionException("it's a fake!", "Session Exception")
예제 #8
0
    def _create_write_connection(self,
                                 url,
                                 file_size=None,
                                 cookies=None,
                                 overwrite=None,
                                 content_type=None,
                                 cacerts=False):
        """Create HTTP connection to write to VMDK file."""
        LOG.debug(
            "Creating HTTP connection to write to file with "
            "size = %(file_size)d and URL = %(url)s.", {
                'file_size': file_size,
                'url': url
            })
        _urlparse = urlparse.urlparse(url)
        scheme, netloc, path, params, query, fragment = _urlparse

        try:
            if scheme == 'http':
                conn = httplib.HTTPConnection(netloc)
            elif scheme == 'https':
                conn = httplib.HTTPSConnection(netloc)
                cert_reqs = None

                # cacerts can be either True or False or contain
                # actual certificates. If it is a boolean, then
                # we need to set cert_reqs and clear the cacerts
                if isinstance(cacerts, bool):
                    if cacerts:
                        cert_reqs = ssl.CERT_REQUIRED
                    else:
                        cert_reqs = ssl.CERT_NONE
                    cacerts = None

                conn.set_cert(ca_certs=cacerts, cert_reqs=cert_reqs)
            else:
                excep_msg = _("Invalid scheme: %s.") % scheme
                LOG.error(excep_msg)
                raise ValueError(excep_msg)

            if query:
                path = path + '?' + query

            headers = {'User-Agent': USER_AGENT}
            if file_size:
                headers.update({'Content-Length': str(file_size)})
            if overwrite:
                headers.update({'Overwrite': overwrite})
            if cookies:
                headers.update(
                    {'Cookie': self._build_vim_cookie_header(cookies)})
            if content_type:
                headers.update({'Content-Type': content_type})

            conn.putrequest('PUT', path)
            for key, value in six.iteritems(headers):
                conn.putheader(key, value)
            conn.endheaders()
            return conn
        except requests.RequestException as excep:
            excep_msg = _("Error occurred while creating HTTP connection "
                          "to write to VMDK file with URL = %s.") % url
            LOG.exception(excep_msg)
            raise exceptions.VimConnectionException(excep_msg, excep)
예제 #9
0
    def test_select_datastore(self, filter_datastores, get_profile_id):
        # Test with no hosts.
        size_bytes = units.Ki
        req = {self._ds_sel.SIZE_BYTES: size_bytes}
        self._vops.get_hosts.return_value = mock.Mock(objects=[])

        self.assertEqual((), self._ds_sel.select_datastore(req))
        self._vops.get_hosts.assert_called_once_with()

        # Test with single host with no valid datastores.
        host_1 = self._create_host('host-1')
        self._vops.get_hosts.return_value = mock.Mock(
            objects=[mock.Mock(obj=host_1)])
        self._vops.continue_retrieval.return_value = None
        self._vops.get_dss_rp.side_effect = exceptions.VimException('error')

        self.assertEqual((), self._ds_sel.select_datastore(req))
        self._vops.get_dss_rp.assert_called_once_with(host_1)

        # Test with three hosts and vCenter connection problem while fetching
        # datastores for the second host.
        self._vops.get_dss_rp.reset_mock()
        host_2 = self._create_host('host-2')
        host_3 = self._create_host('host-3')
        self._vops.get_hosts.return_value = mock.Mock(
            objects=[mock.Mock(obj=host_1),
                     mock.Mock(obj=host_2),
                     mock.Mock(obj=host_3)])
        self._vops.get_dss_rp.side_effect = [
            exceptions.VimException('no valid datastores'),
            exceptions.VimConnectionException('connection error')]

        self.assertRaises(exceptions.VimConnectionException,
                          self._ds_sel.select_datastore,
                          req)
        get_dss_rp_exp_calls = [mock.call(host_1), mock.call(host_2)]
        self.assertEqual(get_dss_rp_exp_calls,
                         self._vops.get_dss_rp.call_args_list)

        # Modify previous case to return datastores for second and third host,
        # where none of them meet the requirements which include a storage
        # profile and affinity requirements.
        aff_ds_types = [ds_sel.DatastoreType.VMFS]
        req[ds_sel.DatastoreSelector.HARD_AFFINITY_DS_TYPE] = aff_ds_types

        ds_1a = mock.sentinel.ds_1a
        anti_affinity_ds = [ds_1a]
        req[ds_sel.DatastoreSelector.HARD_ANTI_AFFINITY_DS] = anti_affinity_ds

        profile_name = mock.sentinel.profile_name
        req[ds_sel.DatastoreSelector.PROFILE_NAME] = profile_name

        profile_id = mock.sentinel.profile_id
        get_profile_id.return_value = profile_id

        ds_2a = mock.sentinel.ds_2a
        ds_2b = mock.sentinel.ds_2b
        ds_3a = mock.sentinel.ds_3a

        self._vops.get_dss_rp.reset_mock()
        rp_2 = mock.sentinel.rp_2
        rp_3 = mock.sentinel.rp_3
        self._vops.get_dss_rp.side_effect = [
            exceptions.VimException('no valid datastores'),
            ([ds_2a, ds_2b], rp_2),
            ([ds_3a], rp_3)]

        filter_datastores.return_value = []

        self.assertEqual((), self._ds_sel.select_datastore(req))
        get_profile_id.assert_called_once_with(profile_name)
        get_dss_rp_exp_calls.append(mock.call(host_3))
        self.assertEqual(get_dss_rp_exp_calls,
                         self._vops.get_dss_rp.call_args_list)
        filter_datastores_exp_calls = [
            mock.call([ds_2a, ds_2b], size_bytes, profile_id, anti_affinity_ds,
                      aff_ds_types),
            mock.call([ds_3a], size_bytes, profile_id, anti_affinity_ds,
                      aff_ds_types)]
        self.assertEqual(filter_datastores_exp_calls,
                         filter_datastores.call_args_list)

        # Modify previous case to have a non-empty summary list after filtering
        # with preferred utilization threshold unset.
        self._vops.get_dss_rp.side_effect = [
            exceptions.VimException('no valid datastores'),
            ([ds_2a, ds_2b], rp_2),
            ([ds_3a], rp_3)]

        summary_2b = self._create_summary(ds_2b, free_space=0.5 * units.Mi,
                                          capacity=units.Mi)
        filter_datastores.side_effect = [[summary_2b]]
        self._vops.get_connected_hosts.return_value = [host_1]

        self.assertEqual((host_2, rp_2, summary_2b),
                         self._ds_sel.select_datastore(req))

        # Modify previous case to have a preferred utilization threshold
        # satsified by one datastore.
        self._vops.get_dss_rp.side_effect = [
            exceptions.VimException('no valid datastores'),
            ([ds_2a, ds_2b], rp_2),
            ([ds_3a], rp_3)]

        req[ds_sel.DatastoreSelector.PREF_UTIL_THRESH] = 0.4
        summary_3a = self._create_summary(ds_3a, free_space=0.7 * units.Mi,
                                          capacity=units.Mi)
        filter_datastores.side_effect = [[summary_2b], [summary_3a]]

        self.assertEqual((host_3, rp_3, summary_3a),
                         self._ds_sel.select_datastore(req))

        # Modify previous case to have a preferred utilization threshold
        # which cannot be satisfied.
        self._vops.get_dss_rp.side_effect = [
            exceptions.VimException('no valid datastores'),
            ([ds_2a, ds_2b], rp_2),
            ([ds_3a], rp_3)]
        filter_datastores.side_effect = [[summary_2b], [summary_3a]]

        req[ds_sel.DatastoreSelector.PREF_UTIL_THRESH] = 0.2
        summary_2b.freeSpace = 0.75 * units.Mi

        self.assertEqual((host_2, rp_2, summary_2b),
                         self._ds_sel.select_datastore(req))

        # Clear side effects.
        self._vops.get_dss_rp.side_effect = None