Python发送请求代token

匡晟
2023-12-01

在请求neutronserver时,需要先创建neutronclient,再通过neutronclient访问neutron server。例如:dash board访问neutron server 时,需要在horirzon/openstack_dashboard/api/neutron.py文件中创建neutronclient.

例如在list router时,函数如下:

def router_list(request, **params):
    routers = neutronclient(request).list_routers(**params).get('routers')
    return [Router(r) for r in routers]

函数调用neutronclient的list_routers函数。

neutronclient定义如下:

def neutronclient(request):
    insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
    cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
    c = neutron_client.Client(token=request.user.token.id,
                              auth_url=base.url_for(request, 'identity'),
                              endpoint_url=base.url_for(request, 'network'),
                              insecure=insecure, ca_cert=cacert)
    return c

from neutronclient.v2_0 import client as neutron_client
因为当前用的是2.0版本,所以在v2_0中创建的neutronclient。创建时带入了token id。

在neutronclient/v2_0/client.py中 client定义如下

class Client(ClientBase):
    def __init__(self, **kwargs):
        """Initialize a new client for the Neutron v2.0 API."""
        super(Client, self).__init__(**kwargs)
        self._register_extensions(self.version)
client继承ClientBase,在初始化时首先调用父类的构造函数。
class ClientBase(object):
    def __init__(self, **kwargs):
        """Initialize a new client for the Neutron v2.0 API."""
        super(ClientBase, self).__init__()
        self.retries = kwargs.pop('retries', 0)
        self.raise_errors = kwargs.pop('raise_errors', True)
        self.httpclient = client.construct_http_client(**kwargs)
        self.version = '2.0'
        self.format = 'json'
        self.action_prefix = "/v%s" % (self.version)
        self.retry_interval = 1
在父类的构造函数中,要创建httpclient,然后通过httpclient去访问api.
def construct_http_client(username=None,
                          user_id=None,
                          tenant_name=None,
                          tenant_id=None,
                          password=None,
                          auth_url=None,
                          token=None,
                          region_name=None,
                          timeout=None,
                          endpoint_url=None,
                          insecure=False,
                          endpoint_type='publicURL',
                          log_credentials=None,
                          auth_strategy='keystone',
                          ca_cert=None,
                          service_type='network',
                          session=None,
                          **kwargs):

    if session:
        kwargs.setdefault('user_agent', 'python-neutronclient')
        kwargs.setdefault('interface', endpoint_type)
        return SessionClient(session=session,
                             service_type=service_type,
                             region_name=region_name,
                             **kwargs)
    else:
        # FIXME(bklei): username and password are now optional. Need
        # to test that they were provided in this mode.  Should also
        # refactor to use kwargs.
        return HTTPClient(username=username,
                          password=password,
                          tenant_id=tenant_id,
                          tenant_name=tenant_name,
                          user_id=user_id,
                          auth_url=auth_url,
                          token=token,
                          endpoint_url=endpoint_url,
                          insecure=insecure,
                          timeout=timeout,
                          region_name=region_name,
                          endpoint_type=endpoint_type,
                          service_type=service_type,
                          ca_cert=ca_cert,
                          log_credentials=log_credentials,
                          auth_strategy=auth_strategy)

construct_http_client函数中,通过是否有session来决定创建SessionClient还是HTTPClient.
所以创建的neutronclient的httpclient实际是SessionClient或者HTTPClient。
通过日志查看都是SessionClient,大概是因为neutron服务一直在运行中吧,session已经存在。第一次启动是HTTPClient。
neutronclient的list_routers函数如下:
def list_routers(self, retrieve_all=True, **_params):
    """Fetches a list of all routers for a tenant."""
    # Pass filters in "params" argument to do_request
    return self.list('routers', self.routers_path, retrieve_all,
                     **_params)
list函数如下:
def list(self, collection, path, retrieve_all=True, **params):
    if retrieve_all:
        res = []
        for r in self._pagination(collection, path, **params):
            res.extend(r[collection])
        return {collection: res}
    else:
        return self._pagination(collection, path, **params)
_pagination函数如下:
def _pagination(self, collection, path, **params):
    if params.get('page_reverse', False):
        linkrel = 'previous'
    else:
        linkrel = 'next'
    next = True
    while next:
        res = self.get(path, params=params)
        yield res
        next = False
        try:
            for link in res['%s_links' % collection]:
                if link['rel'] == linkrel:
                    query_str = urlparse.urlparse(link['href']).query
                    params = urlparse.parse_qs(query_str)
                    next = True
                    break
        except KeyError:
            break

self.get函数如下:

def get(self, action, body=None, headers=None, params=None):
    _logger.info("headers is %s,body is %s"%(headers,body))
    return self.retry_request("GET", action, body=body,
                              headers=headers, params=params)

retry_request函数如下:

def retry_request(self, method, action, body=None,
                  headers=None, params=None):

    max_attempts = self.retries + 1
    for i in range(max_attempts):
        try:
            return self.do_request(method, action, body=body,
                                   headers=headers, params=params)
        except exceptions.ConnectionFailed:
            # Exception has already been logged by do_request()
            if i < self.retries:
                _logger.debug('Retrying connection to Neutron service')
                time.sleep(self.retry_interval)
            elif self.raise_errors:
                raise

    if self.retries:
        msg = (_("Failed to connect to Neutron server after %d attempts")
               % max_attempts)
    else:
        msg = _("Failed to connect Neutron server")

    raise exceptions.ConnectionFailed(reason=msg)

do_request函数如下:

def do_request(self, method, action, body=None, headers=None, params=None):
    # Add format and tenant_id
    action += ".%s" % self.format
    action = self.action_prefix + action
    if type(params) is dict and params:
        params = utils.safe_encode_dict(params)
        action += '?' + urlparse.urlencode(params, doseq=1)

    if body:
        body = self.serialize(body)

    resp, replybody = self.httpclient.do_request(
        action, method, body=body,
        content_type=self.content_type())

    status_code = resp.status_code
    if status_code in (requests.codes.ok,
                       requests.codes.created,
                       requests.codes.accepted,
                       requests.codes.no_content):
        return self.deserialize(replybody, status_code)
    else:
        if not replybody:
            replybody = resp.reason
        self._handle_fault_response(status_code, replybody)

在do_request里,调用了httpclient.do_request函数。在BaseClient中构造了httpclient,do_request函数如下:

def do_request(self, url, method, **kwargs):
    # Ensure client always has correct uri - do not guesstimate anything
    self.authenticate_and_fetch_endpoint_url()
    self._check_uri_length(url)

    try:
        kwargs.setdefault('headers', {})
        if self.auth_token is None:
            self.auth_token = ""
        kwargs['headers']['X-Auth-Token'] = self.auth_token
        resp, body = self._cs_request(self.endpoint_url + url, method,
                                      **kwargs)
        return resp, body
    except exceptions.Unauthorized:
        self.authenticate()
        kwargs.setdefault('headers', {})
        kwargs['headers']['X-Auth-Token'] = self.auth_token
        resp, body = self._cs_request(
            self.endpoint_url + url, method, **kwargs)
        return resp, body

在这个函数中,首先进行token和endpoint_url确认。然后坚持请求url的长度,最大不得超过8192.http默认的请求最大是255,不知道这里怎么是8192.最后将token信息放入header中。然后keystonemiddleware从header中获取token进行验证。


下面说明在第一次请求时没有token,怎么去获取token。

authenticate_and_fetch_endpoint_url函数如下:
def authenticate_and_fetch_endpoint_url(self):
    if not self.auth_token:
        self.authenticate()
    elif not self.endpoint_url:
        self.endpoint_url = self._get_endpoint_url()
authenticate函数如下:
def authenticate(self):
    if self.auth_strategy == 'keystone':
        self._authenticate_keystone()
    elif self.auth_strategy == 'noauth':
        self._authenticate_noauth()
    else:
        err_msg = _('Unknown auth strategy: %s') % self.auth_strategy
        raise exceptions.Unauthorized(message=err_msg)
_authenticate_keystone函数如下:
def _authenticate_keystone(self):
    if self.user_id:
        creds = {'userId': self.user_id,
                 'password': self.password}
    else:
        creds = {'username': self.username,
                 'password': self.password}

    if self.tenant_id:
        body = {'auth': {'passwordCredentials': creds,
                         'tenantId': self.tenant_id, }, }
    else:
        body = {'auth': {'passwordCredentials': creds,
                         'tenantName': self.tenant_name, }, }

    if self.auth_url is None:
        raise exceptions.NoAuthURLProvided()

    token_url = self.auth_url + "/tokens"
    resp, resp_body = self._cs_request(token_url, "POST",
                                       body=json.dumps(body),
                                       content_type="application/json",
                                       allow_redirects=True)
    if resp.status_code != 200:
        raise exceptions.Unauthorized(message=resp_body)
    if resp_body:
        try:
            resp_body = json.loads(resp_body)
        except ValueError:
            pass
    else:
        resp_body = None
    self._extract_service_catalog(resp_body)

向keystone获取token。


 类似资料: