本文地址: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 clientinstance即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