Openstack Cell的API与RPC流程

接上一篇Openstack Cell简要说明,稍微详细的描述下Openstack cell中的API与RPC流程。

API选择

当一个请求进入到nova的api中,api会处理这个请求并找到对应的compute_api上的函数。Nova的compute模块中提供了两个api类,一个是默认场景下的compute api,在nova.compute.api.py中定义的API类;另一个是在nova.compute.cells_api.py中定义的ComputeCellsAPI类。

nova.api.openstack.compute.servers.py中,请求被传递到了self.compute_api,其值为compute.API()

1
2
3
4
5
6
7
8
from nova import compute

...

def __init__(self, ext_mgr=None, **kwargs):
super(Controller, self).__init__(**kwargs)
self.compute_api = compute.API()
self.ext_mgr = ext_mgr

跳转到nova.compute.__init__.py

1
2
3
4
5
6
7
8
9
10
11
12
13
CELL_TYPE_TO_CLS_NAME = {'api': 'nova.compute.cells_api.ComputeCellsAPI',
'compute': 'nova.compute.api.API',
None: 'nova.compute.api.API',
}

def _get_compute_api_class_name():
"""Returns the name of compute API class."""
cell_type = nova.cells.opts.get_cell_type()
return CELL_TYPE_TO_CLS_NAME[cell_type]

def API(*args, **kwargs):
class_name = _get_compute_api_class_name() # 判断是否是cell场景及是api cell还是compute cell
return importutils.import_object(class_name, *args, **kwargs)

以及nova.cells.opts.py中的get_cell_type函数如下。由此可以见当nova的配置文件中设置了cell_type为api时,请求才会被分配到cell场景的compute api(nova.compute.cells_api.ComputeCellsAPI),否则都会分配到默认场景的compute api(nova.compute.api.API)。

1
2
3
4
5
6
def get_cell_type():
"""Return the cell type, 'api', 'compute', or None (if cells is disabled).
"""
if not CONF.cells.enable:
return
return CONF.cells.cell_type

Cell的API

cell的compute_api提供了rpcapi以及task_rpcapi的重定向,因为和默认场景的compute_api有些函数的名字相同,以避免混淆。

同时ComputeCellsAPI提供了_cast_to_cells_call_to_cells函数,用于在某个功能不需要经过cell的rpc流程进行处理时,可以直接调用子cell节点API类中的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ComputeCellsAPI(compute_api.API):
def __init__(self, *args, **kwargs):
super(ComputeCellsAPI, self).__init__(*args, **kwargs)
self.cells_rpcapi = cells_rpcapi.CellsAPI()
# Avoid casts/calls directly to compute
self.compute_rpcapi = ComputeRPCAPIRedirect(self.cells_rpcapi)
# Redirect conductor build_instances to cells
self._compute_task_api = ConductorTaskRPCAPIRedirect(self.cells_rpcapi)
self._cell_type = 'api'

def _cast_to_cells(self, context, instance, method, *args, **kwargs):
instance_uuid = instance.uuid
cell_name = instance.cell_name
if not cell_name:
raise exception.InstanceUnknownCell(instance_uuid=instance_uuid)
self.cells_rpcapi.cast_compute_api_method(context, cell_name,
method, instance_uuid, *args, **kwargs)

def _call_to_cells(self, context, instance, method, *args, **kwargs):
instance_uuid = instance.uuid
cell_name = instance.cell_name
if not cell_name:
raise exception.InstanceUnknownCell(instance_uuid=instance_uuid)
return self.cells_rpcapi.call_compute_api_method(context, cell_name,
method, instance_uuid, *args, **kwargs)

_cast_to_cells函数主要用于单向的消息传输,不需要等待响应,如停止虚拟机、删除虚拟机等;_call_to_cells函数是基于请求并需要得到相应的,如各种获取类的功能。

这两个函数在编写cell场景接口的时候提供了很大的便利,如果在nova.compute.api.API类中已经有了完善的自定义接口,那么可以直接通过这两个函数将cell的api转接到子cell上的这个自定义接口。

举个例子,如果我在nova.compute.api.API下面有这么一个名叫test的函数,在ComputeCellAPI中我只需要写一个这样的函数即可:

1
2
3
4
@wrap_check_policy
@check_instance_cell
def test(self, context, instance):
self._cast_to_cells(context, instance, 'test') # 或者是return self._call_to_cells(context, instance, 'test')

Cell的RPC流程

nova.cells.rpcapi.py中定义了cell的rpc api。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CellsAPI(object):

...

def __init__(self):
super(CellsAPI, self).__init__()
target = messaging.Target(topic=CONF.cells.topic, version='1.0')
version_cap = self.VERSION_ALIASES.get(CONF.upgrade_levels.cells,
CONF.upgrade_levels.cells)
# NOTE(sbauza): Yes, this is ugly but cells_utils is calling cells.db
# which itself calls cells.rpcapi... You meant import cycling ? Gah.
from nova.cells import utils as cells_utils
serializer = cells_utils.ProxyObjectSerializer()
self.client = rpc.get_client(target,
version_cap=version_cap,
serializer=serializer)

nova-cells进程负责接收从CellsAPI发出的消息,并转入nova.cells.manager.py中的CellsManager类,在初始函数中定义了cell功能的具体执行者self.msg_runner,指向了nova.cells.messaging.py中的MessageRunner类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class CellsManager(manager.Manager):

...

def __init__(self, *args, **kwargs):
LOG.warning(_LW('The cells feature of Nova is considered experimental '
'by the OpenStack project because it receives much '
'less testing than the rest of Nova. This may change '
'in the future, but current deployers should be aware '
'that the use of it in production right now may be '
'risky.'))
# Mostly for tests.
cell_state_manager = kwargs.pop('cell_state_manager', None)
super(CellsManager, self).__init__(service_name='cells',
*args, **kwargs)
if cell_state_manager is None:
cell_state_manager = cells_state.CellStateManager
self.state_manager = cell_state_manager()
self.msg_runner = messaging.MessageRunner(self.state_manager) # cell功能的具体执行者
cells_driver_cls = importutils.import_class(
CONF.cells.driver)
self.driver = cells_driver_cls()
self.instances_to_heal = iter([])

MessageRunner是cell的rpc流程中最重要的一个环节,主要的任务就是负责消息请求的具体执行。在nova.cells.messaging.py中还定义了_TargetedMessage_BroadcastMessage_ResponseMessage这三个继承了_BaseMessage类的消息处理类。

_TargetedMessage用于发送给指定的cell。

_BroadcastMessage用于广播给所有cell。

_ResponseMessage可以看作_TargetedMessage的一个特例,用于返回调用结果给发送者。

当请求进入到MessageRunner中的具体函数时,根据所给定的消息处理类生成一个实例,然后调用改对象的执行函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  def build_instances(self, ctxt, target_cell, build_inst_kwargs):
"""Called by the cell scheduler to tell a child cell to build
instance(s).
"""
method_kwargs = dict(build_inst_kwargs=build_inst_kwargs)
# 创建虚拟机是一个具体到某个cell中的功能,所以使用了指定cell的消息处理类生成一个实例
message = _TargetedMessage(self, ctxt, 'build_instances',
method_kwargs, 'down', target_cell)
# 在指定的cell上执行'build_instances'
message.process()

def instance_delete_everywhere(self, ctxt, instance, delete_type):
"""This is used by API cell when it didn't know what cell
an instance was in, but the instance was requested to be
deleted or soft_deleted. So, we'll broadcast this everywhere.
"""
method_kwargs = dict(instance=instance, delete_type=delete_type)
# 删除所有cell中的指定虚拟机,所以使用了广播的消息处理类生成一个实例
message = _BroadcastMessage(self, ctxt,
'instance_delete_everywhere',
method_kwargs, 'down',
run_locally=False)
# 在所有cell上执行'instance_delete_everywhere'
message.process()