nova-api是nova对外提供Restful API的服务,Horizon、novaclient等均通过该api与nova进行通信。

nova其实对外提供了多个api服务,包括下面这些服务:

nova-api
nova-api-ec2
nova-api-metadata
nova-api-os-compute
其中,nova-api用于启动其他三个服务。下面逐个分析下。

nova-api

入口在 nova.cmd.api:main ,主要是基于WSGI、PasteDeploy、Webob、Routes等框架实现Restful API:

    launcher = service.process_launcher()
for api in CONF.enabled_apis:
should_use_ssl = api in CONF.enabled_ssl_apis
if api == 'ec2':
server = service.WSGIService(api, use_ssl=should_use_ssl,
max_url_len=16384)
else:
server = service.WSGIService(api, use_ssl=should_use_ssl)
launcher.launch_service(server, workers=server.workers or 1)
launcher.wait()

对每一个enabled_api,都会创建一个WSGIService,然后调用launch_service:

def _start_child(self, wrap):
if len(wrap.forktimes) > wrap.workers:
# Limit ourselves to one process a second (over the period of
# number of workers * 1 second). This will allow workers to
# start up quickly but ensure we don't fork off children that
# die instantly too quickly.
if time.time() - wrap.forktimes[0] < wrap.workers:
LOG.info(_LI('Forking too fast, sleeping'))
time.sleep(1) wrap.forktimes.pop(0) wrap.forktimes.append(time.time()) pid = os.fork()
if pid == 0:
# create a green thread to run child
# 实际上最终调用的service的start/wait方法
launcher = self._child_process(wrap.service)
while True:
self._child_process_handle_signal()
status, signo = self._child_wait_for_exit_or_signal(launcher)
if not _is_sighup_and_daemon(signo):
break
launcher.restart() os._exit(status) LOG.info(_LI('Started child %d'), pid) wrap.children.add(pid)
self.children[pid] = wrap return pid def launch_service(self, service, workers=1):
wrap = ServiceWrapper(service, workers) LOG.info(_LI('Starting %d workers'), wrap.workers)
while self.running and len(wrap.children) < wrap.workers:
self._start_child(wrap)

既然每个服务都是 service.WSGIService ,那么WSGIService是如何初始化的呢

class WSGIService(object):
"""Provides ability to launch API from a 'paste' configuration.""" def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
"""Initialize, but do not start the WSGI server. :param name: The name of the WSGI server given to the loader.
:param loader: Loads the WSGI application using the given name.
:returns: None """
self.name = name
# 根据self.name从配置文件中获取manager,若有则实例化对应的manager
self.manager = self._get_manager()
self.loader = loader or wsgi.Loader()
# 通过paste.deploy加载app,对应配置文件为api-paste.ini
self.app = self.loader.load_app(name)
self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
self.port = getattr(CONF, '%s_listen_port' % name, 0)
self.workers = (getattr(CONF, '%s_workers' % name, None) or
processutils.get_worker_count())
if self.workers and self.workers < 1:
worker_name = '%s_workers' % name
msg = (_("%(worker_name)s value of %(workers)s is invalid, "
"must be greater than 0") %
{'worker_name': worker_name,
'workers': str(self.workers)})
raise exception.InvalidInput(msg)
self.use_ssl = use_ssl
self.server = wsgi.Server(name,
self.app,
host=self.host,
port=self.port,
use_ssl=self.use_ssl,
max_url_len=max_url_len)
# Pull back actual port used
self.port = self.server.port
self.backdoor_port = None

即从api-paste.ini加载app后,创建wsgi.Server

Paste Deployment是用于发现和配置WSGI appliaction和server的系统。对于WSGI application,用户提供一个单独的函数(loadapp),用于从配置文件或者python egg中加载WSGI application。因为WSGI application提供了唯一的单独的简单的访问入口,所以application不需要暴露application的内部的实现细节。首先了解几个基本的概念:

  • application: 应用,符合WSGI规范的可调用对象,接受参数(environ,start_response), 调用start_response返回状态和消息头,返回结果作为消息体。

  • filter:过滤器,可调用对象,类型python中的装饰器,接受一个application对象作为参数,返回一个封装后的application。

  • app_factory:可调用对象,接受参数(global,**local_conf),返回application对象

  • composite_factory: 可调用对象,接受参数(loader,global_config,**local_conf), loader有几个方法, get_app用于获取wsgi_app, get_filter用于加载filter, 返回application对象。

  • filter_factory: 可调用对象,接受参数(global_config, **local_conf),返回filter对象

以nova-api-os-compute为例看看这个配置文件是咋回事

# 这儿是整个api的入口,将不同版本转发到compiste处理
[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v1.1: openstack_compute_api_v2
/v2: openstack_compute_api_v2
/v3: openstack_compute_api_v3 # 比如v3版本api在这儿处理,在使用keystone配置时,要经过request_id faultwrap sizelimit authtoken keystonecontext这些filter的处理最后再交给osapi_compute_app_v3这个app来处理
# 注意这些filter的顺序:先把前n-1个filter逆序,然后逐个应用到app上
[composite:openstack_compute_api_v3]
use = call:nova.api.auth:pipeline_factory_v3
noauth = request_id faultwrap sizelimit noauth_v3 osapi_compute_app_v3
keystone = request_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v3 # filter部分定义了这个filter由哪个类来处理
[filter:request_id]
paste.filter_factory = nova.openstack.common.middleware.request_id:RequestIdMiddleware.factory [filter:compute_req_id]
paste.filter_factory = nova.api.compute_req_id:ComputeReqIdMiddleware.factory [filter:faultwrap]
paste.filter_factory = nova.api.openstack:FaultWrapper.factory [filter:noauth]
paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory [filter:noauth_v3]
paste.filter_factory = nova.api.openstack.auth:NoAuthMiddlewareV3.factory [filter:ratelimit]
paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory [filter:sizelimit]
paste.filter_factory = nova.api.sizelimit:RequestBodySizeLimiter.factory # 这儿是v3的处理app,即APIRouterV3.factory
[app:osapi_compute_app_v3]
paste.app_factory = nova.api.openstack.compute:APIRouterV3.factory

APIRouterV3会加载setup.cfg中配置的extensions:

        # 初始化api_extension_manager并加载所有的API_EXTENSION_NAMESPACE对应的extensions。
# 这儿加载的就是setup.cfg中配置的nova.api.v3.extensions部分,比如:
# nova.api.v3.extensions =
# access_ips = nova.api.openstack.compute.plugins.v3.access_ips:AccessIPs
# admin_actions = nova.api.openstack.compute.plugins.v3.admin_actions:AdminActions
# admin_password = nova.api.openstack.compute.plugins.v3.admin_password:AdminPassword
# agents = nova.api.openstack.compute.plugins.v3.agents:Agents
# ...
# 当然如果有第三方的应用也声明了此namespace的extension,这里也会一并load进来。
# 其中的check_func为check_load_extension,其所作的工作主要包括:
# a) 检测是否为相V3APIExtensionBase类型
# b) extension的黑白名单过滤和验证
self.api_extension_manager = stevedore.enabled.EnabledExtensionManager(
namespace=self.API_EXTENSION_NAMESPACE, # 'nova.api.v3.extensions'
check_func=_check_load_extension,
invoke_on_load=True,
invoke_kwds={"extension_info": self.loaded_extension_info}) mapper = PlainMapper()
self.resources = {} # NOTE(cyeoh) Core API support is rewritten as extensions
# but conceptually still have core
if list(self.api_extension_manager):
# NOTE(cyeoh): Stevedore raises an exception if there are
# no plugins detected. I wonder if this is a bug.
# Extensions define what resources they want to add through a get_resources function
self.api_extension_manager.map(self._register_resources,
mapper=mapper)
# Extensions define what resources they want to add through
# a get_controller_extensions function
self.api_extension_manager.map(self._register_controllers) # 检测core extensions是否全部加载成功
missing_core_extensions = self.get_missing_core_extensions(
self.loaded_extension_info.get_extensions().keys())
if not self.init_only and missing_core_extensions:
LOG.critical(_("Missing core API extensions: %s"),
missing_core_extensions)
raise exception.CoreAPIMissing(
missing_apis=missing_core_extensions)

以 servers = nova.api.openstack.compute.plugins.v3.servers:Servers 这个extension为例来看看加载过程是怎么样的。从前面的代码可以看到对每个extension,会调用get_resources和get_controller_extensions两个函数,其所作的工作分别为新增资源和扩展现有资源以添加action。

class Servers(extensions.V3APIExtensionBase):
"""Servers.""" name = "Servers"
alias = "servers"
version = 1 def get_resources(self):
member_actions = {'action': 'POST'}
collection_actions = {'detail': 'GET'}
resources = [
extensions.ResourceExtension(
'servers',
ServersController(extension_info=self.extension_info),
member_name='server', collection_actions=collection_actions,
member_actions=member_actions)] return resources def get_controller_extensions(self):
return []

这里需要注意的是,V2 API中会区分Core API和Extension API。在V3 API中,所有的API均作为extension的形式提供。如V2 API中的servers是Core API,在V3 API中也是一个普通的extension,此extension会在get_resources方法中返回servers资源对象,而get_controller_extensions方法会返回空list。

而当disk_config等需要对servers资源扩展action时,其所需做的就是在get_controller_extensions中返回对servers资源的扩展:

class Disk_config(extensions.ExtensionDescriptor):
"""Disk Management Extension.""" name = "DiskConfig"
alias = ALIAS
namespace = XMLNS_DCF
updated = "2011-09-27T00:00:00Z" def get_controller_extensions(self):
servers_extension = extensions.ControllerExtension(
self, 'servers', ServerDiskConfigController()) images_extension = extensions.ControllerExtension(
self, 'images', ImageDiskConfigController()) return [servers_extension, images_extension]

最后再来看看V3版本API是如何避免各Extension之间代码耦合的

V3中,主要是利用了Stevedore避免了各Extension之间的代码耦合,从而避免了上述问题。

还是以当前对核心资源“虚拟机”的create方法扩展来看,在servers资源对应的Controller nova.api.openstack.compute.pulgins.v3.servers:ServersController中,将create作为了一个extension的namespace,并会在初始化时就load所有的extensions:

# Look for implementation of extension point of server creation
self.create_extension_manager = \
stevedore.enabled.EnabledExtensionManager(
namespace=self.EXTENSION_CREATE_NAMESPACE, #nova.api.v3.extensions.server.create
check_func=_check_load_extension('server_create'),
invoke_on_load=True,
invoke_kwds={"extension_info": self.extension_info},
propagate_map_exceptions=True)
if not list(self.create_extension_manager):
LOG.debug("Did not find any server create extensions")

然后在create方法中,会通过map方法依次调用各个Extensions的server_create方法:

        # Query extensions which want to manipulate the keyword
# arguments.
# NOTE(cyeoh): This is the hook that extensions use
# to replace the extension specific code below.
# When the extensions are ported this will also result
# in some convenience function from this class being
# moved to the extension
if list(self.create_extension_manager):
self.create_extension_manager.map(self._create_extension_point,
server_dict, create_kwargs) def _create_extension_point(self, ext, server_dict, create_kwargs):
handler = ext.obj
LOG.debug("Running _create_extension_point for %s", ext.obj) handler.server_create(server_dict, create_kwargs)

在nova的setup.cfg中也配置了此namespace对应的所有的extensions,比如 nova.api.openstack.compute.plugins.v3.config_drive:ConfigDrive,其server_create方法如下所示,会更新ServersController:create方法中传入的create_kwargs字典:

    def server_create(self, server_dict, create_kwargs):
create_kwargs['config_drive'] = server_dict.get(ATTRIBUTE_NAME)

最终,ServersController:create方法会以如下方式调用内部API来创建VM,即直接使用被各个extensions更新过的create_kwargs,而不用感知其实际的内容。

            (instances, resv_id) = self.compute_api.create(context,
inst_type,
image_uuid,
display_name=name,
display_description=name,
metadata=server_dict.get('metadata', {}),
admin_password=password,
requested_networks=requested_networks,
**create_kwargs)

由上面的分析可知,如上所有的所谓的Extension,最终均依赖的还是一个内部的API,如果此API本身不具有扩展性,那么如上所有的Extension,均只能在此内部API支持的功能的基础上进行发挥。 比如文中提到的创建VM的内部create API,此API的参数是固定的:

    def create(self, context, instance_type,
image_href, kernel_id=None, ramdisk_id=None,
min_count=None, max_count=None,
display_name=None, display_description=None,
key_name=None, key_data=None, security_group=None,
availability_zone=None, user_data=None, metadata=None,
injected_files=None, admin_password=None,
block_device_mapping=None, access_ip_v4=None,
access_ip_v6=None, requested_networks=None, config_drive=None,
auto_disk_config=None, scheduler_hints=None, legacy_bdm=True):
"""Provision instances, sending instance information to the
scheduler. The scheduler will determine where the instance(s)
go and will handle creating the DB entries. Returns a tuple of (instances, reservation_id)
"""

参考文档:

http://blog.csdn.net/cloudresearch/article/details/19050595

http://www.choudan.net/2013/07/30/OpenStack-API分析%28一%29.html

http://www.choudan.net/2013/07/31/OpenStack-API分析%28二%29.html

nova分析(3)—— nova-api的更多相关文章

  1. nova分析(10)—— nova-rootwrap

    一.nova-rootwrap的作用 部署玩过openstack的都应该知道,它会生成一个nova用户来管理所有服务.nova身份在linux中属于普通用户级别,避免了一些需要root身份运行的操作, ...

  2. 在Openstack H版部署Nova Cell 时 ,终端输入nova service-list 和 nova host-list 命令将报错

    关于Cell的基本介绍,可以参考贤哥的一篇文章: [OpenStack]G版中关于Nova的Cell  http://blog.csdn.net/lynn_kong/article/details/8 ...

  3. KVM 介绍(8):使用 libvirt 迁移 QEMU/KVM 虚机和 Nova 虚机 [Nova Libvirt QEMU/KVM Live Migration]

    学习 KVM 的系列文章: (1)介绍和安装 (2)CPU 和 内存虚拟化 (3)I/O QEMU 全虚拟化和准虚拟化(Para-virtulizaiton) (4)I/O PCI/PCIe设备直接分 ...

  4. MediaInfo源代码分析 2:API函数

    本文主要分析MediaInfo的API函数.它的API函数位于MediaInfo.h文件中的一个叫做MediaInfo的类中. 该类如下所示,部分重要的方法已经加上了注释: //MediaInfo类 ...

  5. openstack私有云布署实践【11.3 计算nova - compute节点-nova用户免密登录(用于云主机冷迁移+扩展云主机大小)】

    云主机迁移+扩展云主机大小 ,官方说它依赖nova用户之间的免密登录.确保每个resion区域的compute节点服务器他们可以相互SSH免密   compute1-7     他们相互SSH免密 k ...

  6. nova创建虚拟机源码分析系列之六 api入口create方法

    openstack 版本:Newton 注:博文图片采用了很多大牛博客图片,仅作为总结学习,非商用.该图全面的说明了nova创建虚机的过程,从逻辑的角度清晰的描述了前端请求创建虚拟机之后发生的一系列反 ...

  7. nova分析(6)—— nova service启动过程

    Nova project下面具有多个service,api,compute,sceduler等等,他们的启动过程都几乎类似,这一篇博客就详细记录nova-sceduler的启动过程.文章中贴出的源码都 ...

  8. nova分析(7)—— nova-scheduler

    Nova-Scheduler主要完成虚拟机实例的调度分配任务,创建虚拟机时,虚拟机该调度到哪台物理机上,迁移时若没有指定主机,也需要经过scheduler.资源调度是云平台中的一个很关键问题,如何做到 ...

  9. nova分析(8)—— nova-compute

    nova-compute是管理和配置虚拟机的入口,在所有compute机器上都需要该服务来创建和管理虚拟机. nova-compute服务的入口在 nova.cmd.compute:main ,其启动 ...

随机推荐

  1. java CAS

    在Doug Lea提供的cucurenct包 (J.U.C)中,CAS理论是实现整个java包的基石.   Compare and Swap 在这里,CAS 指的是现代 CPU 广泛支持的一种对内存中 ...

  2. linux/windows 下kill某个pid的进程

    [linux环境] 方法1:截取进程pid,再kill ps -ef | grep java.endorsed.dirs | grep -v grep | cut -c10-15 | xargs ki ...

  3. SQL SERVER 2012使用sequence

    从之前Oracle转过来,现在看sql server中对于id的实现竟然用guid这种方式.为啥不像在Oracle中一样使用Sequence并行获取序列号呢?今天看MSDN才发现在sql server ...

  4. Sprint第二个冲刺(第四天)

    一.Sprint 计划会议: 第四次会议总结情况如下展示:昨天完成了美化按钮.增添图片的功能,今天在我们的努力下又完成了查看用户资料和底栏显示功能,由此可见我们团队的小伙伴都很厉害,也很勤奋.从燃尽图 ...

  5. 浅谈jQuery中setInterval()方法

    定义和用法: setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式. setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口 ...

  6. ES6-函数扩展

  7. html--整站制作

    1.样式初置 body,div,ul,ol,h1,h2,h3,h4,h5,p,form,input,textarea,select{margin:0;padding:0;} li{list-style ...

  8. c#部分---输入班级人数,输入语文数学英语成绩,打印语文前两名,数学后两名,英语平均分

    1.开始收集输入项 2.用冒泡排序,统计语文成绩,并附带把语数英三门课全排列 3.数学成绩排序,附带把三门课全排序‘ 4.最后算英语的平均分:

  9. Unity Shaders

    推荐!! Unity技术人员的博客: http://aras-p.info/blog/ Unity 中的 Shader 按照使用方式来分的话,分为3种: Surface Shader 受光照影响 Cg ...

  10. Unity学习资源

    NGUI文档及视频: http://www.tasharen.com/forum/index.php?topic=6754 动态更新的解决方案: http://game.ceeger.com/foru ...