novaclient代码解析之---通过nova show <server-id>命令解析Nova client与Nova API之间的调用关系

卫焕
2023-12-01

#nova–debug show <server-id>, (这里不讨论novaclient的认证过程)

可以client端得到的debug信息,调用过程如下:

1. 从服务器获取token
2. 调用servers.Controller.index()
3. 调用servers.Controller.show()
4. 调用flavors.Controller.show()
5. 调用images.Controller.show()

<pre name="code" class="python"><span style="font-size:14px;">REQ: curl -g -i --cacert "/opt/stack/data/ca-bundle.pem" -X GET http://9.115.122.54:35357/v3/auth/tokens -H "X-
Subject-Token: {SHA1}6b17daa3f28a43f6ce18863dfaa4374fbe59f6e0" -H "User-Agent: python-keystoneclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}6bbfbc9a37c9
ab53696017541de0df5107e9ad35"

nova.api.openstack.wsgi: Calling method '<bound method Controller.index of <nova.api
.openstack.compute.servers.Controller object at 0x7fbd39a7b7d0>>' 

nova.compute.api: Searching by: {'deleted': False, 'project_id': u'ecb571eb456242f1a
f7e37c5190382bd', u'name': u'vm1'} 
nova.osapi_compute.wsgi.server: localhost "GET /v2/ecb571eb456242f1af7e37c5190382b
d/servers?name=vm1 HTTP/1.1" status: 200 len: 553 time: 0.1614599

nova.api.openstack.wsgi: Calling method '<bound method Controller.show of <nova.api.
openstack.compute.servers.Controller object at 0x7fbd39a7b7d0>>'
nova.osapi_compute.wsgi.server: localhost "GET /v2/ecb571eb456242f1af7e37c5190382bd/servers/33b937f9-2785-4fe0-8f88-6474ff50f7bb HTTP/1.1" status: 200 len: 1892 time: 0.0848639

nova.api.openstack.wsgi: Calling method '<bound method Controller.show of <nova.api.openstack.compute.flavors.Controller object at 0x7fbd39a0b910>>' 
nova.osapi_compute.wsgi.server:localhost "GET /v2/ecb571eb456242f1af7e37c5190382bd/flavors/42 HTTP/1.1" status: 200 len: 621 time: 0.0289028

nova.api.openstack.wsgi:Calling method '<bound method Controller.show of <nova.api.openstack.compute.images.Controller object at 0x7fbd399f0bd0>>' 
nova.osapi_compute.wsgi.server: localhost "GET /v2/ecb571eb456242f1af7e37c5190382bd/images/16f62e95-57b0-4ddd-88ee-2b063ce5f1e3 HTTP/1.1" status: 200 len: 1001 time: 0.1902299</span>

 


为什么有这些调用呢?上篇博客提到novaclient最后运行了

args.func(self.cs, args)
由于当下是v2.client,所以回到v2.shell.py,有新的发现,这里有各种具体命令的执行,以及参数的检查等等。我们用

# nova show <server-id>

所以检查这个函数,这里就是具体执行show操作代码。

@cliutils.arg(
    '--minimal',
    dest='minimal',
    action="store_true",
    default=False,
    help=_('Skips flavor/image lookups when showing servers'))
@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_show(cs, args):
    """Show details about the given server."""
    _print_server(cs, args)
直接看_print_server

def _print_server(cs, args, server=None):
    # By default when searching via name we will do a
    # findall(name=blah) and due a REST /details which is not the same
    # as a .get() and doesn't get the information about flavors and
    # images. This fix it as we redo the call with the id which does a
    # .get() to get all informations.
    if not server:
        server = _find_server(cs, args.server)

    minimal = getattr(args, "minimal", False)

    networks = server.networks
    info = server._info.copy()
    for network_label, address_list in networks.items():
        info['%s network' % network_label] = ', '.join(address_list)

    flavor = info.get('flavor', {})
    flavor_id = flavor.get('id', '')
    if minimal:
        info['flavor'] = flavor_id
    else:
        try:
            info['flavor'] = '%s (%s)' % (_find_flavor(cs, flavor_id).name,
                                          flavor_id)
        except Exception:
            info['flavor'] = '%s (%s)' % (_("Flavor not found"), flavor_id)

    if 'security_groups' in info:
        # when we have multiple nics the info will include the
        # security groups N times where N == number of nics. Be nice
        # and only display it once.
        info['security_groups'] = ', '.join(
            sorted(set(group['name'] for group in info['security_groups'])))

    image = info.get('image', {})
    if image:
        image_id = image.get('id', '')
        if minimal:
            info['image'] = image_id
        else:
            try:
                info['image'] = '%s (%s)' % (_find_image(cs, image_id).name,
                                             image_id)
            except Exception:
                info['image'] = '%s (%s)' % (_("Image not found"), image_id)
    else:  # Booted from volume
        info['image'] = _("Attempt to boot from volume - no image supplied")

    info.pop('links', None)
    info.pop('addresses', None)

    utils.print_dict(info)
关键在_find_server这里,_find_server直接挂接到utils.find_resource

def find_resource(manager, name_or_id, **find_args):
    """Helper for the _find_* methods."""
    # for str id which is not uuid (for Flavor and Keypair search currently)
    if getattr(manager, 'is_alphanum_id_allowed', False):
        try:
            return manager.get(name_or_id)
        except exceptions.NotFound:
            pass

    # try to get entity as integer id
    try:
        return manager.get(int(name_or_id))
    except (TypeError, ValueError, exceptions.NotFound):
        pass
这里可见各种manager.get(),没错,这里的manager就是上篇博客提到的在novaclient中注册的manager。

上篇博客提到novaclient是怎么运行起来的,在novaclient创建过程中,有许多extension的注册

$ vim novaclient/v2/client.py

        # 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)
命令为nova show <server-id>,  这里我们关注serverManager. 

utils.manager.get()调用了novaclient.v2.servers.ServerManager.get()

这里啰嗦一句ServerManager创建过程,

class ServerManager(base.BootingManagerWithFind):
    resource_class = Server

    def _boot(self, resource_url, response_key, name, image, flavor,
              meta=None, files=None, userdata=None,
              reservation_id=None, return_raw=False, min_count=None,
              max_count=None, security_groups=None, key_name=None,
注意 resource_class定义,这里后面会用到。

    def get(self, server):
        """
        Get a server.

        :param server: ID of the :class:`Server` to get.
        :rtype: :class:`Server`
        """
        return self._get("/servers/%s" % base.getid(server), "server")
这里的base.getid(server)对应了API中, servers.Controller.index()
    def _get(self, url, response_key):
        _resp, body = self.api.client.get(url)
        return self.resource_class(self, body[response_key], loaded=True)
在通过server name找到server uuid之后,这里self.api.client.get(url)对应了API中 servers.Controller.show()

注意最后一句的self,resource_class(...),这里就是在ServerManager类中指明的

resource_class = Server

至此Client通过REST API交互server信息貌似应该结束了, 实则不尽然, 没有说明flavor和image信息。

回到_print_server函数,会发现在没有指定 --minimal参数时候,继续寻找image和flavor信息。

至此nova show <server-id>调用完全结束。


这里还有一个问题,在nova show <server-id>时候,API内部extension机制是如何被触发的呢?最直接印象是,我并没有调用security-groups信息,为什么在返回的server info里面会有呢?下篇分解。



 类似资料: