keystone 身份验证流程(2)

本文地址:http://blog.csdn.net/spch2008/article/details/9380231

QuantumClient端的身份验证

第一部分:

QuantumClent\shell.py QuantumShell类中,有一个run方法,负责接收用户命令并执行。

#命令 quantum port-show c5975e89-c3cb-4cce-a751-5931258c42d1
def run(self, argv):
   
    #argv  ==》  ['port-show', 'c5975e89-c3cb-4cce-a751-5931258c42d1']

    #调用参数解释器解析参数	
    self.options, remainder = self.parser.parse_known_args(argv)
	
    #self.options
    '''Namespace(debug=False, help=<quantumclient.shell.QuantumShell object at 0x18f5fd0>, 
       os_auth_strategy='keystone', os_auth_url='http://172.16.4.1:5000/v2.0/', os_password='admin', 
       os_region_name='', os_tenant_name='admin', os_token='', os_url='', os_username='admin')
    '''
    #remainder
    #['port-show', 'c5975e89-c3cb-4cce-a751-5931258c42d1']
            
    self.initialize_app(remainder)
       
    result = self.run_subcommand(remainder)
	
    return result

类中有一个参数解释器,负责提取linux中的环境变量。

def build_option_parser(self, description, version):     
        # Global arguments
        parser.add_argument(
            '--os-auth-strategy', metavar='<auth-strategy>',
            default=env('OS_AUTH_STRATEGY', default='keystone'))
   
        parser.add_argument(
            '--os-auth-url', metavar='<auth-url>',
            default=env('OS_AUTH_URL'),
            help='Authentication URL (Env: OS_AUTH_URL)')

        parser.add_argument(
            '--os-tenant-name', metavar='<auth-tenant-name>',
            default=env('OS_TENANT_NAME'),
            help='Authentication tenant name (Env: OS_TENANT_NAME)')

        parser.add_argument(
            '--os-username', metavar='<auth-username>',
            default=utils.env('OS_USERNAME'),
            help='Authentication username (Env: OS_USERNAME)')

        parser.add_argument(
            '--os-password', metavar='<auth-password>',
            default=utils.env('OS_PASSWORD'),
            help='Authentication password (Env: OS_PASSWORD)')

        parser.add_argument(
            '--os-token', metavar='<token>',
            default=env('OS_TOKEN'),
            help='Defaults to env[OS_TOKEN]')

        return parser
我得环境变量如下:
declare -x OS_AUTH_URL="http://172.16.4.1:5000/v2.0/"
declare -x OS_PASSWORD="admin"
declare -x OS_TENANT_NAME="admin"
declare -x OS_USERNAME="admin"
由于环境变量中没有配置验证方式,即OS_AUTH_STRATEGY,从上述代码中可以看出,将默认采用keystone进行身份验证。

initialize_app函数中调用authenticate_user,进行初步的身份验证。
def authenticate_user(self):
        
    if self.options.os_auth_strategy == 'keystone':     
         
        # Validate password flow auth
        if not self.options.os_username:
            raise exc.CommandError(
                "You must provide a username via"
                " either --os-username or env[OS_USERNAME]")

        if not self.options.os_password:
            raise exc.CommandError(
                "You must provide a password via"
                " either --os-password or env[OS_PASSWORD]") 

    self.client_manager = clientmanager.ClientManager(
        token=self.options.os_token,
        url=self.options.os_url,
        auth_url=self.options.os_auth_url,
        tenant_name=self.options.os_tenant_name,
        username=self.options.os_username,
        password=self.options.os_password,
        region_name=self.options.os_region_name,
        api_version=self.api_version,
        auth_strategy=self.options.os_auth_strategy, )
    return

验证是否指定了相应的变量,只有这些变量有值,才能进行下一步的验证,否则提示用户输入。将解析所得的参数传入ClientManager中,也

就是说有了ClientManager对象,就有了目前解析所得的这些参数。这里比较重要的一个变量就是client_manager,后面它将用于向keystone进行身份验证,取得token。

记住这个client_manager。

 def run_subcommand(self, argv):     
    subcommand = self.command_manager.find_command(argv)
    cmd_factory, cmd_name, sub_argv = subcommand

    cmd = cmd_factory(self, self.options)

    return run_command(cmd, cmd_parser, sub_argv)


def run_command(cmd, cmd_parser, sub_argv):
   
    return cmd.run(known_args)

这两段代码有点无关紧要了,只是取得命令对应的类(ShowPort),创建对象实例。注意:cmd_factory将self(即QuantumShell对象)与self.options传入,

初始化ShowPort。

'port-show': utils.import_class(
        'quantumclient.quantum.v2_0.port.ShowPort'),

下面将视野转向quantumclient.quantum.v2_0.port.ShowPort类啦

第二部分:

ShowPort最顶层的基类为cliff.command

class Command(object):
    
    __metaclass__ = abc.ABCMeta

    def __init__(self, app, app_args):
        self.app = app
        self.app_args = app_args
        return
其中的app为QuantumShell对象,app_args为QuantumShell对象中的self.options。

分析第一部分的cmd.run,cmd即ShowPort对象。调用的是父类ShowCommand的get_data方法。

def get_data(self, parsed_args):
        self.log.debug('get_data(%s)' % parsed_args)
        quantum_client = self.get_client()

        obj_shower = getattr(quantum_client, "show_%s" % self.resource)
        data = obj_shower(_id, **params)

而其中的get_client为调用ShowCommand父类QuantumCommand的get_client()方法

class QuantumCommand(command.OpenStackCommand):
    api = 'network'
    log = logging.getLogger(__name__ + '.QuantumCommand')

    def get_client(self):
        return self.app.client_manager.quantum
好了,到这里终于又回到QuantumShell对象的client_manager变量了。

找到quantumclient.common.clientmanager.py,此文件中有两个类

class ClientCache(object):
  
    def __init__(self, factory):
        self.factory = factory
        self._handle = None

    def __get__(self, instance, owner):
        # Tell the ClientManager to login to keystone
        if self._handle is None:
            self._handle = self.factory(instance)
        return self._handle


class ClientManager(object):
    
    quantum = ClientCache(quantum_client.make_client)

    def initialize(self):
        if not self._url:
            httpclient = HTTPClient(username=self._username,
                                    tenant_name=self._tenant_name,
                                    password=self._password,
                                    region_name=self._region_name,
                                    auth_url=self._auth_url)
            httpclient.authenticate()
            # Populate other password flow attributes
            self._token = httpclient.auth_token
            self._url = httpclient.endpoint_url
这里用到了一个 python描述符的技术。当get_client方法中调用client_manager.quantum时,会触发ClientCache中的__get__方法。

而__get__中会触发quantum_client的make_client方法。


API_VERSIONS = {
    '2.0': 'quantumclient.v2_0.client.Client',
}


def make_client(instance):

    quantum_client = utils.get_client_class(
        API_NAME,
        instance._api_version[API_NAME],
        API_VERSIONS,
    )

    instance.initialize()

    client = quantum_client(token=instance._token)
    return client
instance即ClientManager对象,initialize负责向keystone索取token,后面通信将采用此token。而quantum_client为quantumclient.v2_0.client.Client类。


client为quantumclient\v2_0\client.py中的Client类,其构造函数__init__

def __init__(self, **kwargs):
    self.httpclient = HTTPClient(**kwargs)
    self.version = '2.0'
    self.format = 'json'
    self.action_prefix = "/v%s" % (self.version)
    self.retries = 0
    self.retry_interval = 1
其中,将token传入HTTPClient对象中,用于后续身份验证。

port-showt对应于quantumclient\v2_0\client.py中的show-port, 在ShowCommand中的get_data方法中通过getattr(quantum_client, "show_%s" % self.resource)

取得该方法。

@APIParamsCall
def show_port(self, port, **_params):

    return self.get(self.port_path % (port), params=_params)
    
def get(self, action, body=None, headers=None, params=None):
    return self.retry_request("GET", action, body=body,
                              headers=headers, params=params)    
		
		
def retry_request(self, method, action, body=None,
       
    return self.do_request(method, action, body=body,
                           headers=headers, params=params)

						   
def do_request(self, method, action, body=None, headers=None, params=None):
     
    resp, replybody = self.httpclient.do_request(action, method, body=body)
通过上述追踪,最终调用的是httpclient的do_request方法。


def do_request(self, url, method, **kwargs):
       
    kwargs['headers']['X-Auth-Token'] = self.auth_token

最终在do_request中,将token加入请求信息头部,用于后续身份验证。



你可能感兴趣的:(keystone 身份验证流程(2))