nova-compute服务启动时调用manager中的host初始化函数
self.manager.init_host()
在host初始化函数中完成如下操作:
#初始化libvirt的事件处理
self.driver.init_host(host=self.host)
#注册生命周期事件的处理函数
self.init_virt_events()
#处理evacuated的虚拟机
通过libvirt接口获取本节点上所有的虚拟机,再查询这些虚拟机在数据库中的host信息。如果host与当前节点不一致,说明是已经撤离的虚拟机,直接destroy。
self._destroy_evacuated_instances(context)
#虚拟机状态同步
for instance in instances:
wait_ticks = self._init_instance(context, instance,
wait_ticks=wait_ticks)
_init_instance
完成了虚拟机状态的同步,同步规则如下:
finish_revert_migration
resume_state_on_host_boot
启动虚拟机(实际上是hard_reboot操作,但是不更新数据库状态)。
init_host中对事件处理的初始化:
#注册异常处理函数,这里的libvirt_error_handler是空的,也就是异常不做处理
libvirt.registerErrorHandler(libvirt_error_handler, None)
#向libvirt注册一个事件
libvirt.virEventRegisterDefaultImpl()
#
self._init_events()
_init_events
中:
#创建一个队列,用于存储事件消息
#创建一对管道,用于事件消息的通知
self._init_events_pipe()
#启动一个系统原生线程,线程内用循环监听上面注册的libvirt事件。
_native_thread
libvirt.virEventRunDefaultImpl()
#启动一个绿色线程,线程内用一个循环分发监听到的libvirt事件。
eventlet.spawn(self._dispatch_thread)
事件分发流程_dispatch_thread
#读取上面建立的管道内容,如果读出数据,说明队列中有消息待处理。没有消息则退出此次循环。
_c = self._event_notify_recv.read(1)
#尝试读取事件队列
event = self._event_queue.get(block=False)
#如果是生命周期事件,则进入生命周期事件处理函数
self.emit_event(event)
#处理连接断开事件(告警日志打印,重置nova与libvirt的连接conn)
conn = last_close_event['conn']
生命周期事件处理函数emit_event(self, event)
#调用注册的事件处理函数
self._compute_event_callback(event)
注册事件处理函数init_virt_events
#此处注册了handle_events作为生命周期事件的处理函数
self.driver.register_event_listener(self.handle_events)
handle_events
-->>handle_lifecycle_event
#按照如下的关系同步虚拟机在openstack层的电源状态
#EVENT_LIFECYCLE_STOPPED -> SHUTDOWN
#EVENT_LIFECYCLE_STARTED -> RUNNING
#EVENT_LIFECYCLE_PAUSED -> PAUSED
#EVENT_LIFECYCLE_RESUMED -> RUNNING
self._sync_instance_power_state(context,
instance,
vm_power_state)
_sync_instance_power_state
#如果虚拟机的宿主机不是当前节点,说明虚拟机做了迁移,这种虚拟机直接跳过,不做同步。
if self.host != db_instance.host
#虚拟机的任务状态不为空,说明当前事件只是一个任务的中间状态,也直接跳过不做处理
elif db_instance.task_state is not None
#事件上报的虚拟机电源状态与数据库电源状态不一致的情况下,更新数据库中的虚拟机电源状态。
if vm_power_state != db_power_state:
db_instance.power_state = vm_power_state
db_instance.save()
#数据库中的虚拟机状态为ACTIVE
#接收到SHUTDOWN/CRASHED -> call stop api
#接收到SUSPENDED -> call stop api
#接收到PAUSED -> 虚拟机异常pause,ignore
#接收到NOSTATE -> 虚拟机丢失,忽略
#数据库中的虚拟机状态为STOPPED,而上报的生命周期事件不是NOSTATE/SHUTDOWN/CRASHED其中之一,则强制关闭虚拟机。
self.compute_api.force_stop(context, db_instance)
#数据库中的虚拟机状态为PAUSED,上报的生命周期事件为SHUTDOWN/CRASHED,则认为一个暂停状态的虚拟机被关机了,强制关闭虚拟机。
self.compute_api.force_stop(context, db_instance)
#数据库中虚拟机状态为SOFT_DELETED或者DELETED,而上报的事件不是NOSTATE或者SHUTDOWN,则发出日志告警。
nova-compute服务启动时,libvirt driver会同步加载,并与libvirt建立一个长连接。通过这个连接注册了libvirt的生命周期事件的回调函数
#注册生命周期事件,只有这些事件发生时,后面virEventRunDefaultImpl才会被触发。
wrapped_conn.domainEventRegisterAny(
None,
libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
self._event_lifecycle_callback,
self)
当libvirt监听到事件发生时,会调用注册的回调函数
#将事件添加到队列中
self._queue_event(virtevent.LifecycleEvent(uuid, transition))
_queue_event
#加入队列
self._event_queue.put(event)
#通过管道通知给dispatch绿色线程
c = ' '.encode()
self._event_notify_send.write(c)
self._event_notify_send.flush()
nova-compute在服务启动的最后阶段启动了一个定时任务_sync_power_states
。这个定时任务的主要功能是同步节点上的虚拟机电源状态与数据库记录保持一致。最终也是通过与事件同步一样的_sync_instance_power_state
同步电源状态。
nova中的状态同步有以下几种情况:
1.服务启动时
数据库状态 | 节点状态 | 任务状态 | 处理 |
---|---|---|---|
SOFT_DELETED | - | 非RESIZE_MIGRATING | - |
ERROR | - | 非RESIZE_MIGRATING | - |
DELETED | - | - | 清理资源 |
- | - | RESIZE_MIGRATING | 回滚迁移操作 |
RUNNING | 非RUNNING | - | 启动 |
2.事件通知及定时任务的状态同步
数据库状态 | 上报状态 | 处理 |
---|---|---|
ACTIVE | SHUTDOWN | stop |
ACTIVE | CRASHED | stop |
ACTIVE | SUSPENDED | stop |
ACTIVE | PAUSED | ignore |
ACTIVE | NOSTATE | ignore |
STOPPED | 非NOSTATE/SHUTDOWN/CRASHED | destroy |
PAUSED | SHUTDOWN/CRASHED | destroy |
SOFT_DELETED | 非NOSTATE/SHUTDOWN | 日志告警 |
DELETED | 非NOSTATE/SHUTDOWN | 日志告警 |