예제 #1
0
파일: client.py 프로젝트: jordant/heat
    def _do_request(self, method, url, body, headers):
        """
        Connects to the server and issues a request.  Handles converting
        any returned HTTP error status codes to OpenStack/heat exceptions
        and closing the server connection. Returns the result data, or
        raises an appropriate exception.

        :param method: HTTP method ("GET", "POST", "PUT", etc...)
        :param url: urlparse.ParsedResult object with URL information
        :param body: data to send (as string, filelike or iterable),
                     or None (default)
        :param headers: mapping of key/value pairs to add as headers

        :note

        If the body param has a read attribute, and method is either
        POST or PUT, this method will automatically conduct a chunked-transfer
        encoding and use the body as a file object or iterable, transferring
        chunks of data using the connection's send() method. This allows large
        objects to be transferred efficiently without buffering the entire
        body in memory.
        """
        if url.query:
            path = url.path + "?" + url.query
        else:
            path = url.path

        try:
            connection_type = self.get_connection_type()
            headers = headers or {}

            if 'x-auth-token' not in headers and self.auth_tok:
                headers['x-auth-token'] = self.auth_tok

            c = connection_type(url.hostname, url.port, **self.connect_kwargs)

            def _pushing(method):
                return method.lower() in ('post', 'put')

            def _simple(body):
                return body is None or isinstance(body, basestring)

            def _filelike(body):
                return hasattr(body, 'read')

            def _sendbody(connection, iter):
                connection.endheaders()
                for sent in iter:
                    # iterator has done the heavy lifting
                    pass

            def _chunkbody(connection, iter):
                connection.putheader('Transfer-Encoding', 'chunked')
                connection.endheaders()
                for chunk in iter:
                    connection.send('%x\r\n%s\r\n' % (len(chunk), chunk))
                connection.send('0\r\n\r\n')

            # Do a simple request or a chunked request, depending
            # on whether the body param is file-like or iterable and
            # the method is PUT or POST
            #
            if not _pushing(method) or _simple(body):
                # Simple request...
                c.request(method, path, body, headers)
            elif _filelike(body) or self._iterable(body):
                c.putrequest(method, path)

                for header, value in headers.items():
                    c.putheader(header, value)

                iter = self.image_iterator(c, headers, body)

                _chunkbody(c, iter)
            else:
                raise TypeError('Unsupported image type: %s' % body.__class__)

            res = c.getresponse()

            def _retry(res):
                return res.getheader('Retry-After')

            status_code = self.get_status_code(res)
            if status_code in self.OK_RESPONSE_CODES:
                return res
            elif status_code in self.REDIRECT_RESPONSE_CODES:
                raise exception.RedirectException(res.getheader('Location'))
            elif status_code == httplib.UNAUTHORIZED:
                raise exception.NotAuthorized()
            elif status_code == httplib.FORBIDDEN:
                raise exception.NotAuthorized()
            elif status_code == httplib.NOT_FOUND:
                raise exception.NotFound(res.read())
            elif status_code == httplib.CONFLICT:
                raise exception.Duplicate(res.read())
            elif status_code == httplib.BAD_REQUEST:
                raise exception.Invalid(reason=res.read())
            elif status_code == httplib.MULTIPLE_CHOICES:
                raise exception.MultipleChoices(body=res.read())
            elif status_code == httplib.REQUEST_ENTITY_TOO_LARGE:
                raise exception.LimitExceeded(retry=_retry(res),
                                              body=res.read())
            elif status_code == httplib.INTERNAL_SERVER_ERROR:
                raise Exception("Internal Server error: %s" % res.read())
            elif status_code == httplib.SERVICE_UNAVAILABLE:
                raise exception.ServiceUnavailable(retry=_retry(res))
            elif status_code == httplib.REQUEST_URI_TOO_LONG:
                raise exception.RequestUriTooLong(body=res.read())
            else:
                raise Exception("Unknown error occurred! %s" % res.read())

        except (socket.error, IOError), e:
            raise exception.ClientConnectionError(e)
예제 #2
0
    def _v2_auth(self, token_url):
        def get_endpoint(service_catalog):
            """
            Select an endpoint from the service catalog

            We search the full service catalog for services
            matching both type and region. If the client
            supplied no region then any endpoint for the service
            is considered a match. There must be one -- and
            only one -- successful match in the catalog,
            otherwise we will raise an exception.
            """
            region = self.creds.get('region')

            service_type_matches = lambda s: s.get('type') == self.service_type
            region_matches = lambda e: region is None or e['region'] == region

            endpoints = [
                ep for s in service_catalog if service_type_matches(s)
                for ep in s['endpoints'] if region_matches(ep)
            ]

            if len(endpoints) > 1:
                raise exception.RegionAmbiguity(region=region)
            elif not endpoints:
                raise exception.NoServiceEndpoint()
            else:
                # FIXME(sirp): for now just use the public url.
                return endpoints[0]['publicURL']

        creds = self.creds

        creds = {
            "auth": {
                "tenantName": creds['tenant'],
                "passwordCredentials": {
                    "username": creds['username'],
                    "password": creds['password']
                }
            }
        }

        headers = {}
        headers['Content-Type'] = 'application/json'
        req_body = json.dumps(creds)

        resp, resp_body = self._do_request(token_url,
                                           'POST',
                                           headers=headers,
                                           body=req_body)

        if resp.status == 200:
            resp_auth = json.loads(resp_body)['access']
            self.management_url = get_endpoint(resp_auth['serviceCatalog'])
            self.auth_token = resp_auth['token']['id']
        elif resp.status == 305:
            raise exception.RedirectException(resp['location'])
        elif resp.status == 400:
            raise exception.AuthBadRequest(url=token_url)
        elif resp.status == 401:
            raise exception.NotAuthorized()
        elif resp.status == 404:
            raise exception.AuthUrlNotFound(url=token_url)
        else:
            try:
                body = json.loads(resp_body)
                msg = body['error']['message']
            except:
                msg = resp_body
            raise exception.KeystoneError(resp.status, msg)