本文主要分析Nova的一个event机制,目前主要用于VIF plugin是的notification,可以实现Nova 和 Neutron直接VIF 状态信息的交互。
1. nova部分
vif_plugging_timeout配置参数的解释,用于定义创建VM时等待VIF准备好的时间
cfg.BoolOpt('vif_plugging_is_fatal', default=True, help="Fail instance boot if vif plugging fails"), cfg.IntOpt('vif_plugging_timeout', default=300, help='Number of seconds to wait for neutron vif plugging ' 'events to arrive before continuing or failing (see ' 'vif_plugging_is_fatal). If this is set to zero and ' 'vif_plugging_is_fatal is False, events should not ' 'be expected to arrive at all.'),
_create_domain_and_network():
timeout = CONF.vif_plugging_timeout if (self._conn_supports_start_paused and #判断是不是支持开机是暂停,主要是virt_type是否是kvm or qemu, 此处进行判断的原因是vm需要暂停一下等待linux bridge创建好并把vm连接到br-int上去 utils.is_neutron() and not vifs_already_plugged and power_on and timeout): events = self._get_neutron_events(network_info)#返回所有vif的active属性为False的项 else: events = [] launch_flags = events and libvirt.VIR_DOMAIN_START_PAUSED or 0 domain = None try: with self.virtapi.wait_for_instance_event(#上下文管理 instance, events, deadline=timeout, error_callback=self._neutron_failed_callback): self.plug_vifs(instance, network_info) self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) with self._lxc_disk_handler(instance, image_meta, block_device_info, disk_info): domain = self._create_domain( xml, instance=instance, launch_flags=launch_flags, power_on=power_on) self.firewall_driver.apply_instance_filter(instance, network_info)
contextlib.contextmanager和yield实现pyhton的上下文管理器,另一种实现方法可以参考with的实现原理,在我的另外一篇文章中有介绍。
任何在yield之前的内容都可以看做在代码块执行前的操作,而任何yield之后的操作都可以放在exit函数中。wait_for_instance_event ()就是
先准备计划等待一些event, 然后运行_create_domain_and_network()中提到的代码块,同时开始等待,等待超时后调用error_callback,
详细介绍在代码的注释中说的很清楚,在次不做翻译了。主要实现的功能就是等待vif准备好,等待时间超过300ms,就算本次vm创建失败。
@contextlib.contextmanager def wait_for_instance_event(self, instance, event_names, deadline=300, error_callback=None): """Plan to wait for some events, run some code, then wait. This context manager will first create plans to wait for the provided event_names, yield, and then wait for all the scheduled events to complete. Note that this uses an eventlet.timeout.Timeout to bound the operation, so callers should be prepared to catch that failure and handle that situation appropriately. If the event is not received by the specified timeout deadline, eventlet.timeout.Timeout is raised. If the event is received but did not have a 'completed' status, a NovaException is raised. If an error_callback is provided, instead of raising an exception as detailed above for the failure case, the callback will be called with the event_name and instance, and can return True to continue waiting for the rest of the events, False to stop processing, or raise an exception which will bubble up to the waiter. :param instance: The instance for which an event is expected :param event_names: A list of event names. Each element can be a string event name or tuple of strings to indicate (name, tag). :param deadline: Maximum number of seconds we should wait for all of the specified events to arrive. :param error_callback: A function to be called if an event arrives """ if error_callback is None: error_callback = self._default_error_callback events = {} for event_name in event_names: if isinstance(event_name, tuple):#event_name是一个元组,如('network-vif-plugged', vif['id']) name, tag = event_name event_name = objects.InstanceExternalEvent.make_key( #nova/objects/external_event.py, 在该文件中定义event的几种类型 name, tag) events[event_name] = ( self._compute.instance_events.prepare_for_instance_event( instance, event_name)) yield with eventlet.timeout.Timeout(deadline): for event_name, event in events.items(): actual_event = event.wait() if actual_event.status == 'completed': continue decision = error_callback(event_name, instance) if decision is False: break
该函数返回一个event给上面的函数中的events中的event,再执行event.wait()
def prepare_for_instance_event(self, instance, event_name): """Prepare to receive an event for an instance. This will register an event for the given instance that we will wait on later. This should be called before initiating whatever action will trigger the event. The resulting eventlet.event.Event object should be wait()'d on to ensure completion. :param instance: the instance for which the event will be generated :param event_name: the name of the event we're expecting :returns: an event object that should be wait()'d on """ @utils.synchronized(self._lock_name(instance)) def _create_or_get_event(): if instance.uuid not in self._events: self._events.setdefault(instance.uuid, {}) return self._events[instance.uuid].setdefault( event_name, eventlet.event.Event()) LOG.debug('Preparing to wait for external event %(event)s', {'event': event_name}, instance=instance) return _create_or_get_event()
2. neturon部分
OK,分析到这里不免产生疑惑,上文中判断event完成的语句是:
if actua_event.status == ‘completed‘
那么问题来了,是谁在改变event的status呢?plug vif 是由neutron完成的,故猜想应该是由neutron在修改这个status, 看nova api v2 和 v3
都有server_external_events.py 这个文件,显然,Nova为修改event的状态提供了Rest API, 在Neutron的代码下面,可以在notifiers目录下
的nova.py文件中找到调用rest API 的代码,故猜想成功了。
在neutron/db/db_base_plugin_v2.py文件中有:
if cfg.CONF.notify_nova_on_port_status_changes: from neutron.notifiers import nova # NOTE(arosen) These event listeners are here to hook into when # port status changes and notify nova about their change. self.nova_notifier = nova.Notifier() event.listen(models_v2.Port, 'after_insert', self.nova_notifier.send_port_status) event.listen(models_v2.Port, 'after_update', self.nova_notifier.send_port_status) event.listen(models_v2.Port.status, 'set', self.nova_notifier.record_port_status_changed)
当port的状态改变时,通知nova.