python-novaclient源码分析-M版本

一 代码结构

[root@Alljun-Lee python-novaclient]# ls
babel.cfg         doc          LICENSE     other-requirements.txt  releasenotes      run_tests.sh  setup.py  test-requirements.txt  tox.ini
CONTRIBUTING.rst  HACKING.rst  novaclient  README.rst              requirements.txt  setup.cf      tools

二 代码分析

1. setup.py会自动的解析setup.cfg文件,其中的[entry_points]就是程序的初始化入口

  1.1 例如我们在执行命令'# nova list'时,我们先看nova命令的源代码,'# which nova'

  1.2 nova命令对应的脚本在目录'/usr/bin/nova'

      # cat /usr/bin/nova
      #!/usr/bin/python
      # PBR Generated from 'console_scripts'

      import sys

      from novaclient.shell import main


      if __name__ == "__main__":
          sys.exit(main())
  1.3 我们可以很快的知道它的入口函数'novaclient.shell----def main()'

2. # vim novaclient/shell.py 主要是def main()方法

def main():
    try: 
        argv = [encodeutils.safe_decode(a) for a in sys.argv[1:]]
        OpenStackComputeShell().main(argv)
    except Exception as exc: 
        logger.debug(exc, exc_info=1)
        print(_("ERROR (%(type)s): %(msg)s") % {
              'type': exc.__class__.__name__,
              'msg': encodeutils.exception_to_unicode(exc)},
              file=sys.stderr)
        sys.exit(1)
    except KeyboardInterrupt:
        print(_("... terminating nova client"), file=sys.stderr)
        sys.exit(130)


if __name__ == "__main__":
    main()
3. 进入OpenStackComputeShell().main(argv)

    def main(self, argv):
        # Parse args once to find version and debug settings
        parser = self.get_base_parser(argv)

        # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse
        #                thinking usage-list --end is ambiguous; but it
        #                works fine with only --endpoint-type present
        #                Go figure.
        if '--endpoint_type' in argv:
            spot = argv.index('--endpoint_type')
            argv[spot] = '--endpoint-type'
            # NOTE(Vek): Not emitting a warning here, as that will
            #            occur when "--endpoint-type" is processed

        # For backwards compat with old os-auth-token parameter
        if '--os-auth-token' in argv:
            spot = argv.index('--os-auth-token')
            argv[spot] = '--os-token'
            print(_('WARNING: Option "%(option)s" is deprecated; %(use)s') % {
                'option': '--os-auth-token',
                'use': _('use "%s"; this option will be removed in '
                         'novaclient 3.3.0.') % '--os-token',
            }, file=sys.stderr)

        (args, args_list) = parser.parse_known_args(argv)

        self.setup_debugging(args.debug)
        self.extensions = []
        do_help = ('help' in argv) or (
            '--help' in argv) or ('-h' in argv) or not argv
        # bash-completion should not require authentification
        skip_auth = do_help or (
            'bash-completion' in argv)

        # Discover available auth plugins
        novaclient.auth_plugin.discover_auth_systems()

        if not args.os_compute_api_version:
            api_version = api_versions.get_api_version(
                DEFAULT_MAJOR_OS_COMPUTE_API_VERSION)
        else:
            api_version = api_versions.get_api_version(
                args.os_compute_api_version)

        os_username = args.os_username
        os_user_id = args.os_user_id
        os_password = None  # Fetched and set later as needed
        os_project_name = getattr(
            args, 'os_project_name', getattr(args, 'os_tenant_name', None))
        os_project_id = getattr(
            args, 'os_project_id', getattr(args, 'os_tenant_id', None))
        os_auth_url = args.os_auth_url
        os_region_name = args.os_region_name
        os_auth_system = args.os_auth_system
        endpoint_type = args.endpoint_type
        insecure = args.insecure
        service_type = args.service_type
        service_name = args.service_name
        volume_service_name = args.volume_service_name
        bypass_url = args.bypass_url
        os_cache = args.os_cache
        cacert = args.os_cacert
        timeout = args.timeout
        keystone_session = None
        keystone_auth = None

        # We may have either, both or none of these.
        # If we have both, we don't need USERNAME, PASSWORD etc.
        # Fill in the blanks from the SecretsHelper if possible.
        # Finally, authenticate unless we have both.
        # Note if we don't auth we probably don't have a tenant ID so we can't
        # cache the token.
        auth_token = getattr(args, 'os_token', None)
        management_url = bypass_url if bypass_url else None

        if os_auth_system and os_auth_system != "keystone":
            warnings.warn(_(
                'novaclient auth plugins that are not keystone are deprecated.'
                ' Auth plugins should now be done as plugins to keystoneauth'
                ' and selected with --os-auth-type or OS_AUTH_TYPE'))
            auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_system)
        else:
            auth_plugin = None

        if not endpoint_type:
            endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE

        # This allow users to use endpoint_type as (internal, public or admin)
        # just like other openstack clients (glance, cinder etc)
        if endpoint_type in ['internal', 'public', 'admin']:
            endpoint_type += 'URL'
        if not service_type:
            # Note(alex_xu): We need discover version first, so if there isn't
            # service type specified, we use default nova service type.
            service_type = DEFAULT_NOVA_SERVICE_TYPE

        # If we have an auth token but no management_url, we must auth anyway.
        # Expired tokens are handled by client.py:_cs_request
        must_auth = not (auth_token and management_url)

        # Do not use Keystone session for cases with no session support. The
        # presence of auth_plugin means os_auth_system is present and is not
        # keystone.
        use_session = True
        if auth_plugin or bypass_url or os_cache or volume_service_name:
            use_session = False

        # FIXME(usrleon): Here should be restrict for project id same as
        # for os_username or os_password but for compatibility it is not.
        if must_auth and not skip_auth:
            if auth_plugin:
                auth_plugin.parse_opts(args)

            if not auth_plugin or not auth_plugin.opts:
                if not os_username and not os_user_id:
                    raise exc.CommandError(
                        _("You must provide a username "
                          "or user ID via --os-username, --os-user-id, "
                          "env[OS_USERNAME] or env[OS_USER_ID]"))
            if not any([os_project_name, os_project_id]):
                raise exc.CommandError(_("You must provide a project name or"
                                         " project ID via --os-project-name,"
                                         " --os-project-id, env[OS_PROJECT_ID]"
                                         " or env[OS_PROJECT_NAME]. You may"
                                         " use os-project and os-tenant"
                                         " interchangeably."))

            if not os_auth_url:
                if os_auth_system and os_auth_system != 'keystone':
                    os_auth_url = auth_plugin.get_auth_url()

            if not os_auth_url:
                    raise exc.CommandError(
                        _("You must provide an auth url "
                          "via either --os-auth-url or env[OS_AUTH_URL] "
                          "or specify an auth_system which defines a "
                          "default url with --os-auth-system "
                          "or env[OS_AUTH_SYSTEM]"))

            if use_session:
                # Not using Nova auth plugin, so use keystone
                with utils.record_time(self.times, args.timings,
                                       'auth_url', args.os_auth_url):
                    keystone_session = (
                        loading.load_session_from_argparse_arguments(args))
                    keystone_auth = (
                        loading.load_auth_from_argparse_arguments(args))
            else:
                # set password for auth plugins
                os_password = args.os_password
        if (not skip_auth and
                not any([os_project_name, os_project_id])):
            raise exc.CommandError(_("You must provide a project name or"
                                     " project id via --os-project-name,"
                                     " --os-project-id, env[OS_PROJECT_ID]"
                                     " or env[OS_PROJECT_NAME]. You may"
                                     " use os-project and os-tenant"
                                     " interchangeably."))

        if not os_auth_url and not skip_auth:
            raise exc.CommandError(
                _("You must provide an auth url "
                  "via either --os-auth-url or env[OS_AUTH_URL]"))

        # This client is just used to discover api version. Version API needn't
        # microversion, so we just pass version 2 at here.
        self.cs = client.Client(
            api_versions.APIVersion("2.0"),
            os_username, os_password, os_project_name,
            tenant_id=os_project_id, user_id=os_user_id,
            auth_url=os_auth_url, insecure=insecure,
            region_name=os_region_name, endpoint_type=endpoint_type,
            extensions=self.extensions, service_type=service_type,
            service_name=service_name, auth_system=os_auth_system,
            auth_plugin=auth_plugin, auth_token=auth_token,
            volume_service_name=volume_service_name,
            timings=args.timings, bypass_url=bypass_url,
            os_cache=os_cache, http_log_debug=args.debug,
            cacert=cacert, timeout=timeout,
            session=keystone_session, auth=keystone_auth)

        if not skip_auth:
            if not api_version.is_latest():
                if api_version > api_versions.APIVersion("2.0"):
                    if not api_version.matches(novaclient.API_MIN_VERSION,
                                               novaclient.API_MAX_VERSION):
                        raise exc.CommandError(
                            _("The specified version isn't supported by "
                              "client. The valid version range is '%(min)s' "
                              "to '%(max)s'") % {
                                "min": novaclient.API_MIN_VERSION.get_string(),
                                "max": novaclient.API_MAX_VERSION.get_string()}
                        )
            api_version = api_versions.discover_version(self.cs, api_version)

        # build available subcommands based on version
        self.extensions = client.discover_extensions(api_version)
        self._run_extension_hooks('__pre_parse_args__')

        subcommand_parser = self.get_subcommand_parser(
            api_version, do_help=do_help, argv=argv)
        self.parser = subcommand_parser

        if args.help or not argv:
            subcommand_parser.print_help()
            return 0

        args = subcommand_parser.parse_args(argv)
        self._run_extension_hooks('__post_parse_args__', args)

        # Short-circuit and deal with help right away.
        if args.func == self.do_help:
            self.do_help(args)
            return 0
        elif args.func == self.do_bash_completion:
            self.do_bash_completion(args)
            return 0

        if not args.service_type:
            service_type = (utils.get_service_type(args.func) or
                            DEFAULT_NOVA_SERVICE_TYPE)

        if utils.isunauthenticated(args.func):
            # NOTE(alex_xu): We need authentication for discover microversion.
            # But the subcommands may needn't it. If the subcommand needn't,
            # we clear the session arguements.
            keystone_session = None
            keystone_auth = None

        # Recreate client object with discovered version.
        self.cs = client.Client(
            api_version,
            os_username, os_password, os_project_name,
            tenant_id=os_project_id, user_id=os_user_id,
            auth_url=os_auth_url, insecure=insecure,
            region_name=os_region_name, endpoint_type=endpoint_type,
            extensions=self.extensions, service_type=service_type,
            service_name=service_name, auth_system=os_auth_system,
            auth_plugin=auth_plugin, auth_token=auth_token,
            volume_service_name=volume_service_name,
            timings=args.timings, bypass_url=bypass_url,
            os_cache=os_cache, http_log_debug=args.debug,
            cacert=cacert, timeout=timeout,
            session=keystone_session, auth=keystone_auth)

        # Now check for the password/token of which pieces of the
        # identifying keyring key can come from the underlying client
        if must_auth:
            helper = SecretsHelper(args, self.cs.client)
            self.cs.client.keyring_saver = helper
            if (auth_plugin and auth_plugin.opts and
                    "os_password" not in auth_plugin.opts):
                use_pw = False
            else:
                use_pw = True

            tenant_id = helper.tenant_id
            # Allow commandline to override cache
            if not auth_token:
                auth_token = helper.auth_token
            if not management_url:
                management_url = helper.management_url
            if tenant_id and auth_token and management_url:
                self.cs.client.tenant_id = tenant_id
                self.cs.client.auth_token = auth_token
                self.cs.client.management_url = management_url
                self.cs.client.password_func = lambda: helper.password
            elif use_pw:
                # We're missing something, so auth with user/pass and save
                # the result in our helper.
                self.cs.client.password = helper.password

        try:
            # This does a couple of bits which are useful even if we've
            # got the token + service URL already. It exits fast in that case.
            if not utils.isunauthenticated(args.func):
                if not use_session:
                    # Only call authenticate() if Nova auth plugin is used.
                    # If keystone is used, authentication is handled as part
                    # of session.
                    self.cs.authenticate()
        except exc.Unauthorized:
            raise exc.CommandError(_("Invalid OpenStack Nova credentials."))
        except exc.AuthorizationFailure:
            raise exc.CommandError(_("Unable to authorize user"))

        args.func(self.cs, args)

        if args.timings:
            self._dump_timings(self.times + self.cs.get_timings())

3.1 解析传过来的参数parser = self.get_base_parser(argv)

3.2 首先解析endpoint_type

ENDPOINT_TYPE_TO_INTERFACE = {
    'publicURL': 'public',
    'internalURL': 'internal',
    'adminURL': 'admin',
}
3.3 然后解析--os-auth-token,如果有就将它替换成--os-token
3.4 self.setup_debugging(args.debug)--设置日志的debug级别
3.5 如果有help/--help/-h/bash-completion,则跳过认证
3.6 接下来就是一系列的环境变量的判断,默认的文件在/root/keystone_admin/demo
3.7 self.cs = client.Client(),这个是最重要的,因为openstack里面的api有不同的版本区别,这一个就是用来判断你接下来要调用的哪个版本的api
    换个角度来理解就是它接下来调用的那个目录下的api--'python-novaclient/novaclient/v2/'--'python-novaclient/novaclient/v1_1/'--'python-novaclient/novaclient/v3/'
3.8 然后进入到'novaclient/v2/client.py'里面的类--class Client(object):
    def __init__()这个方法主要是将各个api做初始化,当然包括扩展的api--注意根据不同的api版本会进行两次api初始化

    def __init__(self, username=None, api_key=None, project_id=None,
                 auth_url=None, insecure=False, timeout=None,
                 proxy_tenant_id=None, proxy_token=None, region_name=None,
                 endpoint_type='publicURL', extensions=None,
                 service_type='compute', service_name=None,
                 volume_service_name=None, timings=False, bypass_url=None,
                 os_cache=False, no_cache=True, http_log_debug=False,
                 auth_system='keystone', auth_plugin=None, auth_token=None,
                 cacert=None, tenant_id=None, user_id=None,
                 connection_pool=False, session=None, auth=None,
                 api_version=None, direct_use=True, **kwargs):
        """Initialization of Client object.

        :param str username: Username
        :param str api_key: API Key
        :param str project_id: Project ID
        :param str auth_url: Auth URL
        :param bool insecure: Allow insecure
        :param float timeout: API timeout, None or 0 disables
        :param str proxy_tenant_id: Tenant ID
        :param str proxy_token: Proxy Token
        :param str region_name: Region Name
        :param str endpoint_type: Endpoint Type
        :param str extensions: Exensions
        :param str service_type: Service Type
        :param str service_name: Service Name
        :param str volume_service_name: Volume Service Name
        :param bool timings: Timings
        :param str bypass_url: Bypass URL
        :param bool os_cache: OS cache
        :param bool no_cache: No cache
        :param bool http_log_debug: Enable debugging for HTTP connections
        :param str auth_system: Auth system
        :param str auth_plugin: Auth plugin
        :param str auth_token: Auth token
        :param str cacert: cacert
        :param str tenant_id: Tenant ID
        :param str user_id: User ID
        :param bool connection_pool: Use a connection pool
        :param str session: Session
        :param str auth: Auth
        :param api_version: Compute API version
        :type api_version: novaclient.api_versions.APIVersion
        """
        if direct_use:
            import warnings

            warnings.warn(
                _LW("'novaclient.v2.client.Client' is not designed to be "
                    "initialized directly. It is inner class of novaclient. "
                    "Please, use 'novaclient.client.Client' instead. "
                    "Related lp bug-report: 1493576"))

        # FIXME(comstud): Rename the api_key argument above when we
        # know it's not being used as keyword argument

        # NOTE(cyeoh): In the novaclient context (unlike Nova) the
        # project_id is not the same as the tenant_id. Here project_id
        # is a name (what the Nova API often refers to as a project or
        # tenant name) and tenant_id is a UUID (what the Nova API
        # often refers to as a project_id or tenant_id).

        password = api_key
        self.projectid = project_id
        self.tenant_id = tenant_id
        self.user_id = user_id
        self.flavors = flavors.FlavorManager(self)
        self.flavor_access = flavor_access.FlavorAccessManager(self)
        self.images = images.ImageManager(self)
        self.limits = limits.LimitsManager(self)
        self.servers = servers.ServerManager(self)
        self.versions = versions.VersionManager(self)
        self.api_version = api_version or api_versions.APIVersion("2.0")

        # extensions
        self.agents = agents.AgentsManager(self)
        self.dns_domains = floating_ip_dns.FloatingIPDNSDomainManager(self)
        self.dns_entries = floating_ip_dns.FloatingIPDNSEntryManager(self)
        self.cloudpipe = cloudpipe.CloudpipeManager(self)
        self.certs = certs.CertificateManager(self)
        self.floating_ips = floating_ips.FloatingIPManager(self)
        self.floating_ip_pools = floating_ip_pools.FloatingIPPoolManager(self)
        self.fping = fping.FpingManager(self)
        self.volumes = volumes.VolumeManager(self)
        self.volume_snapshots = volume_snapshots.SnapshotManager(self)
        self.volume_types = volume_types.VolumeTypeManager(self)
        self.keypairs = keypairs.KeypairManager(self)
        self.networks = networks.NetworkManager(self)
        self.quota_classes = quota_classes.QuotaClassSetManager(self)
        self.quotas = quotas.QuotaSetManager(self)
        self.security_groups = security_groups.SecurityGroupManager(self)
        self.security_group_rules = \
            security_group_rules.SecurityGroupRuleManager(self)
        self.security_group_default_rules = \
            security_group_default_rules.SecurityGroupDefaultRuleManager(self)
        self.usage = usage.UsageManager(self)
        self.virtual_interfaces = \
            virtual_interfaces.VirtualInterfaceManager(self)
        self.aggregates = aggregates.AggregateManager(self)
        self.hosts = hosts.HostManager(self)
        self.hypervisors = hypervisors.HypervisorManager(self)
        self.hypervisor_stats = hypervisors.HypervisorStatsManager(self)
        self.services = services.ServiceManager(self)
        self.fixed_ips = fixed_ips.FixedIPsManager(self)
        self.floating_ips_bulk = floating_ips_bulk.FloatingIPBulkManager(self)
        self.os_cache = os_cache or not no_cache
        self.availability_zones = \
            availability_zones.AvailabilityZoneManager(self)
        self.server_groups = server_groups.ServerGroupsManager(self)
        self.server_migrations = \
            server_migrations.ServerMigrationsManager(self)

        # Add in any extensions...
        if extensions:
            for extension in extensions:
                if extension.manager_class:
                    setattr(self, extension.name,
                            extension.manager_class(self))

        self.client = client._construct_http_client(
            username=username,
            password=password,
            user_id=user_id,
            project_id=project_id,
            tenant_id=tenant_id,
            auth_url=auth_url,
            auth_token=auth_token,
            insecure=insecure,
            timeout=timeout,
            auth_system=auth_system,
            auth_plugin=auth_plugin,
            proxy_token=proxy_token,
            proxy_tenant_id=proxy_tenant_id,
            region_name=region_name,
            endpoint_type=endpoint_type,
            service_type=service_type,
            service_name=service_name,
            volume_service_name=volume_service_name,
            timings=timings,
            bypass_url=bypass_url,
            os_cache=self.os_cache,
            http_log_debug=http_log_debug,
            cacert=cacert,
            connection_pool=connection_pool,
            session=session,
            auth=auth,
            api_version=api_version,
            **kwargs)

3.8.1 加载扩展api的--self.extensions = client.discover_extensions(api_version)

def discover_extensions(version, only_contrib=False):
    """Returns the list of extensions, which can be discovered by python path,
    contrib path and by entry-point 'novaclient.extension'.

    :param version: api version
    :type version: str or novaclient.api_versions.APIVersion
    :param only_contrib: search only in contrib directory or not
    :type only_contrib: bool
    """
    # 获取api的版本,其实就是用来得到api的目录,前面有提到过
    if not isinstance(version, api_versions.APIVersion):
        version = api_versions.get_api_version(version)
    # 接下来就是根据参数only_contrib来决定用那些方法来得到扩展的api    
    if only_contrib:
        # 获取对应的版本目录下的contrib文件夹下的api,例如‘# ls novaclient/v2/contrib/’
        chain = _discover_via_contrib_path(version)
    else:
        chain = itertools.chain(_discover_via_python_path(),
                                _discover_via_contrib_path(version),
                                _discover_via_entry_points())
    return [ext.Extension(name, module) for name, module in chain]
3.8.2 subcommand_parser = self.get_subcommand_parser--解析命令,将它们都转换成以'do_'开头进行匹配

          主要是和文件‘novaclient/v2/shell.py’内的方法匹配

3.8.3 def get_subcommand_parser(self, version, do_help=False, argv=None):
      导入文件'novaclient.v%s.shell',在这个文件内找出相关的action;
      在扩展模块内找出相关的action。
3.9 self.cs.authenticate()--进行授权认证
3.9.1 进入文件'novaclient/client.py'    
      def authenticate(self):
4. 最后执行'args.func(self.cs, args)'就是真正的执行命令的接口了

NOTE: 上面讲的主要是通过终端命令行的方式进行的api调用过程               
                接下来分析horizon那边过来的api调用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: appium-python-client安装包可以通过pip命令进行安装,具体步骤如下: 1. 打开命令行工具(如Windows下的cmd或者Linux下的终端),输入以下命令安装pip: ``` sudo apt-get install python-pip ``` 2. 安装appium-python-client,输入以下命令: ``` pip install Appium-Python-Client ``` 3. 等待安装完成即可使用appium-python-client。 ### 回答2: appium-python-client是一个用于进行移动应用测试的Python库。它作为Appium测试框架的一个客户端,提供了许多功能和方法来编写和执行测试脚本。 要安装appium-python-client,需要先确保已经安装了Python环境。以下是安装appium-python-client的步骤: 1. 打开终端或命令提示符,并进入到所需的Python项目目录。 2. 使用pip命令安装appium-python-client。在终端或命令提示符中输入以下命令: pip install Appium-Python-Client 在执行命令后,pip会从Python Package Index(PyPI)上下载并安装appium-python-client。 安装完成后,您可以在Python脚本中导入appium-python-client并开始编写测试代码。 例如,您可以使用以下命令导入appium-python-client库: ```python from appium import webdriver ``` 然后,您可以使用appium-python-client提供的方法来启动Appium服务器、初始化移动设备连接、定位元素,并执行各种移动应用测试操作。 总结: appium-python-client是一个用于进行移动应用测试的Python库,可以通过pip命令进行安装。安装完成后,您可以在Python脚本中导入该库并使用其提供的方法来编写和执行移动应用测试脚本。 ### 回答3: appium-python-client是一个用于与Appium服务器进行通信的Python库。要安装appium-python-client,您可以按照以下步骤进行操作: 1. 确保您的电脑已经安装了Python解释器。您可以通过在命令行中输入“python --version”来验证Python是否已经安装。 2. 打开命令行或终端,并输入以下命令来安装appium-python-client: ``` pip install Appium-Python-Client ``` 注意:您需要确保您的电脑已经安装了pip,它是一个用于安装Python库的包管理工具。如果您的电脑上没有安装pip,您可以通过以下命令来安装它: ``` python -m ensurepip --upgrade ``` 3. 安装完成后,您可以在Python脚本中导入appium-python-client模块并使用它来与Appium服务器进行通信。例如,您可以使用以下代码示例来连接到Appium服务器: ```python from appium import webdriver desired_caps = { 'platformName': 'Android', 'platformVersion': '9', 'deviceName': 'Android Emulator', 'app': 'path/to/your/app.apk' } driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) ``` 这将创建一个WebDriver实例,用于控制连接到Appium服务器的设备。您可以使用WebDriver实例来执行各种测试操作,例如查找元素、发送输入等。 通过按照以上步骤安装appium-python-client,您就可以在Python中使用它来创建和执行Appium测试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值