[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.2 首先解析endpoint_type
ENDPOINT_TYPE_TO_INTERFACE = {
'publicURL': 'public',
'internalURL': 'internal',
'adminURL': 'admin',
}
3.3 然后解析--os-auth-token,如果有就将它替换成--os-token
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调用