分享

cinderclient源码解析之一

shihailong123 发表于 2014-11-22 19:22:06 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 20346
问题导读

1、一个简单的cinder命令使用什么方法实现的?
2、什么方法,实现了根据解析的命令行参数调用具体的方法?
3、如果所要调用的方法没有标志为unauthenticated,则调用什么方法?





我们知道对应openstack的各个模块中,都有相应的客户端模块实现,其作用是为用户访问具体模块提供了接口,并且也作为模块之间相互访问的途径。这里将对cinder的客户端实现cinderclient进行简单的源码解析。

这里先以一个简单的cinder命令(cinder list)为例,来逐步解析代码的实现过程。

首先会调用/python-cinderclient/cinderclient/shell.py中的方法main,具体来看这个方法的实现:
  1. def main(self, argv):
  2.         # Parse args once to find version and debug settings
  3.         
  4.         # get_base_parser:获取基本的命令行解析器;
  5.         # 调用add_argument方法实现添加具体命令行参数;
  6.         # 构造参数解析类ArgumentParser的实例parser,然后通过实例调用方法parser.add_argument增加一些固有的参数;
  7.         parser = self.get_base_parser()
  8.         # parse_known_args函数是将解析的参数按属性的方式存储到Namespace对象;
  9.         (options, args) = parser.parse_known_args(argv)
  10.         self.setup_debugging(options.debug)
  11.         api_version_input = True
  12.         # 确定使用API的版本,默认版本为V1;
  13.         if not options.os_volume_api_version:
  14.             # Environment variable OS_VOLUME_API_VERSION was
  15.             # not set and '--os-volume-api-version' option doesn't
  16.             # specify a value.  Fall back to default.
  17.             options.os_volume_api_version = DEFAULT_OS_VOLUME_API_VERSION
  18.             api_version_input = False
  19.         # build available subcommands based on version
  20.         # 根据所使用的API版本,进一步确定API扩展的版本;
  21.         self.extensions = self._discover_extensions(options.os_volume_api_version)
  22.         self._run_extension_hooks('__pre_parse_args__')
  23.         # 创建可利用的subcommands,基于版本;
  24.         subcommand_parser = self.get_subcommand_parser(
  25.             options.os_volume_api_version)
  26.         self.parser = subcommand_parser
  27.         if options.help or not argv:
  28.             subcommand_parser.print_help()
  29.             return 0
  30.         # 命令行参数的解析;
  31.         args = subcommand_parser.parse_args(argv)
  32.         self._run_extension_hooks('__post_parse_args__', args)
  33.         # Short-circuit and deal with help right away.
  34.         if args.func == self.do_help:
  35.             self.do_help(args)
  36.             return 0
  37.         elif args.func == self.do_bash_completion:
  38.             self.do_bash_completion(args)
  39.             return 0
  40.         (os_username, os_password, os_tenant_name, os_auth_url,
  41.          os_region_name, os_tenant_id, endpoint_type, insecure,
  42.          service_type, service_name, volume_service_name,
  43.          username, apikey, projectid, url, region_name, cacert) = (
  44.              args.os_username, args.os_password,
  45.              args.os_tenant_name, args.os_auth_url,
  46.              args.os_region_name, args.os_tenant_id,
  47.              args.endpoint_type, args.insecure,
  48.              args.service_type, args.service_name,
  49.              args.volume_service_name, args.username,
  50.              args.apikey, args.projectid,
  51.              args.url, args.region_name, args.os_cacert)
  52.         if not endpoint_type:
  53.             endpoint_type = DEFAULT_CINDER_ENDPOINT_TYPE
  54.         if not service_type:
  55.             service_type = DEFAULT_CINDER_SERVICE_TYPE
  56.             service_type = utils.get_service_type(args.func) or service_type
  57.         #FIXME(usrleon): Here should be restrict for project id same as
  58.         # for os_username or os_password but for compatibility it is not.
  59.         # 若干参数的相关验证和处理;
  60.         if not utils.isunauthenticated(args.func):
  61.             if not os_username:
  62.                 if not username:
  63.                     raise exc.CommandError(
  64.                         "You must provide a username "
  65.                         "via either --os-username or env[OS_USERNAME]")
  66.                 else:
  67.                     os_username = username
  68.             if not os_password:
  69.                 if not apikey:
  70.                     raise exc.CommandError("You must provide a password "
  71.                                            "via either --os-password or via "
  72.                                            "env[OS_PASSWORD]")
  73.                 else:
  74.                     os_password = apikey
  75.             if not (os_tenant_name or os_tenant_id):
  76.                 if not projectid:
  77.                     raise exc.CommandError("You must provide a tenant_id "
  78.                                            "via either --os-tenant-id or "
  79.                                            "env[OS_TENANT_ID]")
  80.                 else:
  81.                     os_tenant_name = projectid
  82.             if not os_auth_url:
  83.                 if not url:
  84.                     raise exc.CommandError(
  85.                         "You must provide an auth url "
  86.                         "via either --os-auth-url or env[OS_AUTH_URL]")
  87.                 else:
  88.                     os_auth_url = url
  89.             if not os_region_name and region_name:
  90.                 os_region_name = region_name
  91.         if not (os_tenant_name or os_tenant_id):
  92.             raise exc.CommandError(
  93.                 "You must provide a tenant_id "
  94.                 "via either --os-tenant-id or env[OS_TENANT_ID]")
  95.         if not os_auth_url:
  96.             raise exc.CommandError(
  97.                 "You must provide an auth url "
  98.                 "via either --os-auth-url or env[OS_AUTH_URL]")
  99.         # 类Client的初始化;
  100.         self.cs = client.Client(options.os_volume_api_version, os_username,
  101.                                 os_password, os_tenant_name, os_auth_url,
  102.                                 insecure, region_name=os_region_name,
  103.                                 tenant_id=os_tenant_id,
  104.                                 endpoint_type=endpoint_type,
  105.                                 extensions=self.extensions,
  106.                                 service_type=service_type,
  107.                                 service_name=service_name,
  108.                                 volume_service_name=volume_service_name,
  109.                                 retries=options.retries,
  110.                                 http_log_debug=args.debug,
  111.                                 cacert=cacert)
  112.         # 如果所要调用的方法没有标志为unauthenticated,则需要进行身份验证操作;
  113.         try:
  114.             if not utils.isunauthenticated(args.func):
  115.                 self.cs.authenticate()
  116.         except exc.Unauthorized:
  117.             raise exc.CommandError("Invalid OpenStack Cinder credentials.")
  118.         except exc.AuthorizationFailure:
  119.             raise exc.CommandError("Unable to authorize user")
  120.         # 实现调用相关方法对Volume API的版本进行验证操作;
  121.         endpoint_api_version = None
  122.         # Try to get the API version from the endpoint URL.  If that fails fall
  123.         # back to trying to use what the user specified via
  124.         # --os-volume-api-version or with the OS_VOLUME_API_VERSION environment
  125.         # variable.  Fail safe is to use the default API setting.
  126.         try:
  127.             endpoint_api_version = \
  128.                 self.cs.get_volume_api_version_from_endpoint()
  129.             if endpoint_api_version != options.os_volume_api_version:
  130.                 msg = (("Volume API version is set to %s "
  131.                         "but you are accessing a %s endpoint. "
  132.                         "Change its value via either --os-volume-api-version "
  133.                         "or env[OS_VOLUME_API_VERSION]")
  134.                        % (options.os_volume_api_version, endpoint_api_version))
  135.                 raise exc.InvalidAPIVersion(msg)
  136.         except exc.UnsupportedVersion:
  137.             endpoint_api_version = options.os_volume_api_version
  138.             if api_version_input:
  139.                 logger.warning("Unable to determine the API version via "
  140.                                "endpoint URL.  Falling back to user "
  141.                                "specified version: %s" %
  142.                                endpoint_api_version)
  143.             else:
  144.                 logger.warning("Unable to determine the API version from "
  145.                                "endpoint URL or user input.  Falling back to "
  146.                                "default API version: %s" %
  147.                                endpoint_api_version)
  148.         args.func(self.cs, args)
复制代码


来看这个方法的实现,首先,是命令行参数的解析部分:
parser = self.get_base_parser(),获取基本的命令行解析器,调用add_argument方法实现添加具体命令行参数,构造参数解析类ArgumentParser的实例parser,然后通过实例调用方法parser.add_argument增加一些固有的参数;
(options, args) = parser.parse_known_args(argv),parse_known_args方法是将解析的参数按属性的方式存储到Namespace对象;
输出示例:
  1. options = Namespace(apikey='', debug=False, endpoint_type='publicURL', help=False, insecure=False, os_auth_url='http://172.21.5.164:35357/v2.0/', os_cacert=None, os_password='453757e2e8314b0a', os_region_name='',
  2. os_tenant_id='', os_tenant_name='admin', os_username='admin', os_volume_api_version=None, projectid='', region_name=None, retries=0, service_name='', service_type=None, url='', username=None, volume_service_name='')
  3. args = list
  4.         # 确定使用API的版本,默认版本为V1;
  5.         if not options.os_volume_api_version:
  6.             # Environment variable OS_VOLUME_API_VERSION was
  7.             # not set and '--os-volume-api-version' option doesn't
  8.             # specify a value.  Fall back to default.
  9.             options.os_volume_api_version = DEFAULT_OS_VOLUME_API_VERSION
  10.             api_version_input = False
  11.         # build available subcommands based on version
  12.         # 根据所使用的API版本,进一步确定API扩展的版本;
  13.         self.extensions = self._discover_extensions(options.os_volume_api_version)
  14.         self._run_extension_hooks('__pre_parse_args__')
  15.         # 创建可利用的subcommands,基于版本;
  16.         subcommand_parser = self.get_subcommand_parser(
  17.             options.os_volume_api_version)
  18.         self.parser = subcommand_parser
  19.         if options.help or not argv:
  20.             subcommand_parser.print_help()
  21.             return 0
  22.         # 命令行参数的解析;
  23.         args = subcommand_parser.parse_args(argv)
  24.         self._run_extension_hooks('__post_parse_args__', args)
复制代码

这部分代码实现的功能是确定所使用的API版本,并且根据具体的API版本实现解析子命令的操作;
  1.   (os_username, os_password, os_tenant_name, os_auth_url,
  2.          os_region_name, os_tenant_id, endpoint_type, insecure,
  3.          service_type, service_name, volume_service_name,
  4.          username, apikey, projectid, url, region_name, cacert) = (
  5.              args.os_username, args.os_password,
  6.              args.os_tenant_name, args.os_auth_url,
  7.              args.os_region_name, args.os_tenant_id,
  8.              args.endpoint_type, args.insecure,
  9.              args.service_type, args.service_name,
  10.              args.volume_service_name, args.username,
  11.              args.apikey, args.projectid,
  12.              args.url, args.region_name, args.os_cacert)
复制代码

这部分代码实现了根据解析的具体参数对相关变量进行赋值操作,为后面方法的调用做具体参数的准备;
输出示例:
  1. os_username = admin
  2. os_password = 453757e2e8314b0a
  3. os_tenant_name = admin
  4. os_auth_url = http://172.21.5.164:35357/v2.0/
  5. os_region_name =
  6. os_tenant_id =
  7. endpoint_type = publicURL
  8. insecure = False
  9. service_type = None
  10. service_name =
  11. volume_service_name =
  12. username = None
  13. apikey =
  14. projectid =
  15. url =
  16. region_name = None
  17. cacert = None
  18.         # 若干参数的相关验证和处理;
  19.         # 如果所要调用的方法没有标志为unauthenticated,则需要进行参数(权限)的验证操作;
  20.         if not utils.isunauthenticated(args.func):
  21.             if not os_username:
  22.                 if not username:
  23.                     raise exc.CommandError(
  24.                         "You must provide a username "
  25.                         "via either --os-username or env[OS_USERNAME]")
  26.                 else:
  27.                     os_username = username
  28.             if not os_password:
  29.                 if not apikey:
  30.                     raise exc.CommandError("You must provide a password "
  31.                                            "via either --os-password or via "
  32.                                            "env[OS_PASSWORD]")
  33.                 else:
  34.                     os_password = apikey
  35.             if not (os_tenant_name or os_tenant_id):
  36.                 if not projectid:
  37.                     raise exc.CommandError("You must provide a tenant_id "
  38.                                            "via either --os-tenant-id or "
  39.                                            "env[OS_TENANT_ID]")
  40.                 else:
  41.                     os_tenant_name = projectid
  42.             if not os_auth_url:
  43.                 if not url:
  44.                     raise exc.CommandError(
  45.                         "You must provide an auth url "
  46.                         "via either --os-auth-url or env[OS_AUTH_URL]")
  47.                 else:
  48.                     os_auth_url = url
  49.             if not os_region_name and region_name:
  50.                 os_region_name = region_name
  51.         if not (os_tenant_name or os_tenant_id):
  52.             raise exc.CommandError(
  53.                 "You must provide a tenant_id "
  54.                 "via either --os-tenant-id or env[OS_TENANT_ID]")
  55.         if not os_auth_url:
  56.             raise exc.CommandError(
  57.                 "You must provide an auth url "
  58.                 "via either --os-auth-url or env[OS_AUTH_URL]")
复制代码


上述代码实现的功能是,如果所要调用的方法没有标志为unauthenticated,则需要对方法的执行进行权限相关参数的验证操作;  
  1.   # 类Client的初始化;
  2.         self.cs = client.Client(options.os_volume_api_version, os_username,
  3.                                 os_password, os_tenant_name, os_auth_url,
  4.                                 insecure, region_name=os_region_name,
  5.                                 tenant_id=os_tenant_id,
  6.                                 endpoint_type=endpoint_type,
  7.                                 extensions=self.extensions,
  8.                                 service_type=service_type,
  9.                                 service_name=service_name,
  10.                                 volume_service_name=volume_service_name,
  11.                                 retries=options.retries,
  12.                                 http_log_debug=args.debug,
  13.                                 cacert=cacert)
  14.         # 如果所要调用的方法没有标志为unauthenticated,则需要进行身份验证操作;
  15.         try:
  16.             if not utils.isunauthenticated(args.func):
  17.                 self.cs.authenticate()
  18.         except exc.Unauthorized:
  19.             raise exc.CommandError("Invalid OpenStack Cinder credentials.")
  20.         except exc.AuthorizationFailure:
  21.             raise exc.CommandError("Unable to authorize user")
  22.         # 实现调用相关方法对Volume API的版本进行验证操作;
  23.         endpoint_api_version = None
  24.         # Try to get the API version from the endpoint URL.  If that fails fall
  25.         # back to trying to use what the user specified via
  26.         # --os-volume-api-version or with the OS_VOLUME_API_VERSION environment
  27.         # variable.  Fail safe is to use the default API setting.
  28.         try:
  29.             endpoint_api_version = \
  30.                 self.cs.get_volume_api_version_from_endpoint()
  31.             if endpoint_api_version != options.os_volume_api_version:
  32.                 msg = (("Volume API version is set to %s "
  33.                         "but you are accessing a %s endpoint. "
  34.                         "Change its value via either --os-volume-api-version "
  35.                         "or env[OS_VOLUME_API_VERSION]")
  36.                        % (options.os_volume_api_version, endpoint_api_version))
  37.                 raise exc.InvalidAPIVersion(msg)
  38.         except exc.UnsupportedVersion:
  39.             endpoint_api_version = options.os_volume_api_version
  40.             if api_version_input:
  41.                 logger.warning("Unable to determine the API version via "
  42.                                "endpoint URL.  Falling back to user "
  43.                                "specified version: %s" %
  44.                                endpoint_api_version)
  45.             else:
  46.                 logger.warning("Unable to determine the API version from "
  47.                                "endpoint URL or user input.  Falling back to "
  48.                                "default API version: %s" %
  49.                                endpoint_api_version)
复制代码

上述代码实现的功能是进行类Client的初始化操作,如果所要调用的方法没有标志为unauthenticated,则调用方法authenticate实现在进行了类Client的初始化操作后,对所要调用方法进行执行权限的验证操作;


在方法mian的最后,语句args.func(self.cs, args)实现根据解析的命令行参数调用具体的方法,输出示例为args.func = do_list,说明这里调用的具体方法是do_list;


上一篇
cinderclient源码解析之二



作者:溜溜小哥
本文转载自:http://blog.csdn.net/gaoxingnengjisuan







没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条