问题导读
1、OpenStack的H版比早前版本,稳定在哪些地方?
2、如何根据原实例的domain重新define一个实例 ?
3、创建虚拟机的时候出现了异常,怎么处理?
本文主要是对比OpenStack的E版本和H版本中实例reboot的代码实现过程,从中可以看出OpenStack在各个版本迭代过程中,变得越来越稳定!同时也希望能给还在被Essex的各种bug折磨的童鞋们一点参考,做了注释的地方就是H比E处理得好的方法,主要贴H版的代码。
Havana中VM的reboot
- def reboot(self, context, instance, network_info, reboot_type='SOFT',
- block_device_info=None, bad_volumes_callback=None):
- """Reboot a virtual machine, given an instance reference."""
- if reboot_type == 'SOFT':
- # NOTE(vish): This will attempt to do a graceful shutdown/restart.
- ###此处处理_create_domain()运行时产生的异常
- #当有异常产生时,设置soft_reboot_success为False,表示soft reboot失败,用hard reboot 实例,这一点在Essex中是没有的。
- try:
- soft_reboot_success = self._soft_reboot(instance)
- except libvirt.libvirtError as e:
- LOG.debug(_("Instance soft reboot failed: %s"), e)
- soft_reboot_success = False
- #soft reboot失败后用hard reboot
- if soft_reboot_success:
- LOG.info(_("Instance soft rebooted successfully."),
- instance=instance)
- return
- else:
- LOG.warn(_("Failed to soft reboot instance. "
- "Trying hard reboot."),
- instance=instance)
- return self._hard_reboot(context, instance, network_info,
- block_device_info)
复制代码
- def _soft_reboot(self, instance):
- """Attempt to shutdown and restart the instance gracefully.
-
- We use shutdown and create here so we can return if the guest
- responded and actually rebooted. Note that this method only
- succeeds if the guest responds to acpi. Therefore we return
- success or failure so we can fall back to a hard reboot if
- necessary.
-
- :returns: True if the reboot succeeded
- """
- dom = self._lookup_by_name(instance["name"])
- (state, _max_mem, _mem, _cpus, _t) = dom.info()
- state = LIBVIRT_POWER_STATE[state]
- old_domid = dom.ID()
- # NOTE(vish): This check allows us to reboot an instance that
- # is already shutdown.
- if state == power_state.RUNNING:
- dom.shutdown()#shutdown正常关闭虚拟机
- # NOTE(vish): This actually could take slighty longer than the
- # FLAG defines depending on how long the get_info
- # call takes to return.
- self._prepare_pci_devices_for_use(
- pci_manager.get_instance_pci_devs(instance))
- for x in xrange(CONF.libvirt_wait_soft_reboot_seconds):
- dom = self._lookup_by_name(instance["name"])
- (state, _max_mem, _mem, _cpus, _t) = dom.info()
- state = LIBVIRT_POWER_STATE[state]
- new_domid = dom.ID()
-
- # NOTE(ivoks): By checking domain IDs, we make sure we are
- # not recreating domain that's already running.
- if old_domid != new_domid:
- if state in [power_state.SHUTDOWN,
- power_state.CRASHED]:
- LOG.info(_("Instance shutdown successfully."),
- instance=instance)
- self._create_domain(domain=dom) #根据原实例的domain重新define一个实例
- timer = loopingcall.FixedIntervalLoopingCall(
- self._wait_for_running, instance)
- timer.start(interval=0.5).wait()
- return True
- else:
- LOG.info(_("Instance may have been rebooted during soft "
- "reboot, so return now."), instance=instance)
- return True
- greenthread.sleep(1)
- return False
复制代码
- def _hard_reboot(self, context, instance, network_info,
- block_device_info=None):
- """Reboot a virtual machine, given an instance reference.
-
- Performs a Libvirt reset (if supported) on the domain.
-
- If Libvirt reset is unavailable this method actually destroys and
- re-creates the domain to ensure the reboot happens, as the guest
- OS cannot ignore this action.
-
- If xml is set, it uses the passed in xml in place of the xml from the
- existing domain.
- """
-
- self._destroy(instance)#libvirt删除原实例
- disk_info = blockinfo.get_disk_info(CONF.libvirt_type,
- instance,
- block_device_info)
- # NOTE(vish): This could generate the wrong device_format if we are
- # using the raw backend and the images don't exist yet.
- # The create_images_and_backing below doesn't properly
- # regenerate raw backend images, however, so when it
- # does we need to (re)generate the xml after the images
- # are in place.
- #根据数据库的信息,使用to_xml()拼装出一个虚拟机的XML描述文件,避免异常导致原虚拟机的xml文件缺失部分信息。
- xml = self.to_xml(context, instance, network_info, disk_info,
- block_device_info=block_device_info,
- write_to_disk=True)
-
- # NOTE (rmk): Re-populate any missing backing files.
- disk_info_json = self.get_instance_disk_info(instance['name'], xml,
- block_device_info)
- instance_dir = libvirt_utils.get_instance_path(instance)
- self._create_images_and_backing(context, instance, instance_dir,
- disk_info_json)
-
- # Initialize all the necessary networking, block devices and
- # start the instance.
- self._create_domain_and_network(xml, instance, network_info,
- block_device_info, context=context,
- reboot=True)##创建新的虚拟机
- self._prepare_pci_devices_for_use(
- pci_manager.get_instance_pci_devs(instance))
-
- def _wait_for_reboot():
- """Called at an interval until the VM is running again."""
- state = self.get_info(instance)['state']
-
- if state == power_state.RUNNING:
- LOG.info(_("Instance rebooted successfully."),
- instance=instance)
- raise loopingcall.LoopingCallDone()
-
- timer = loopingcall.FixedIntervalLoopingCall(_wait_for_reboot)
- timer.start(interval=0.5).wait()
复制代码
- def _create_domain_and_network(self, xml, instance, network_info,
- block_device_info=None, power_on=True):
- ###hard reboot创建虚拟机的函数,实际上也是调用的soft reboot 的创建函数_create_domain()
- """Do required network setup and create domain."""
- block_device_mapping = driver.block_device_info_get_mapping(
- block_device_info)
- #创建虚拟机之前连接卷,避免找不到卷的异常
- for vol in block_device_mapping:
- connection_info = vol['connection_info']
- disk_dev = vol['mount_device'].rpartition("/")[2]
- disk_info = {
- 'dev': disk_dev,
- 'bus': blockinfo.get_disk_bus_for_disk_dev(CONF.libvirt_type,
- disk_dev),
- 'type': 'disk',
- }
- self.volume_driver_method('connect_volume',
- connection_info,
- disk_info)
-
- self.plug_vifs(instance, network_info)
- self.firewall_driver.setup_basic_filtering(instance, network_info)
- self.firewall_driver.prepare_instance_filter(instance, network_info)
- domain = self._create_domain(xml, instance=instance, power_on=power_on)
-
- self.firewall_driver.apply_instance_filter(instance, network_info)
- return domain
复制代码
- #soft reboot使用的函数
- def _create_domain(self, xml=None, domain=None,
- instance=None, launch_flags=0, power_on=True):
- """Create a domain.
-
- Either domain or xml must be passed in. If both are passed, then
- the domain definition is overwritten from the xml.
- """
- inst_path = None
- if instance:
- inst_path = libvirt_utils.get_instance_path(instance)
-
- if CONF.libvirt_type == 'lxc':
- if not inst_path:
- inst_path = None
-
- container_dir = os.path.join(inst_path, 'rootfs')
- fileutils.ensure_tree(container_dir)
- image = self.image_backend.image(instance, 'disk')
- disk.setup_container(image.path,
- container_dir=container_dir,
- use_cow=CONF.use_cow_images)
- ### 创建虚拟机的时候进行了异常处理,define虚拟机时是会产生异常,例如已挂载的卷找不到了等,向上抛出异常,程序会继续执行,而不会在这里就停止执行,导致soft reboot不成功,也不会执行hard reboot,这样就避免了僵尸实例的产生。
- if xml:
- try:
- domain = self._conn.defineXML(xml)##libvirt创建一个虚拟机
- except Exception as e:
- LOG.error(_("An error occurred while trying to define a domain"
- " with xml: %s") % xml)
- raise e
-
- if power_on:
- try:
- domain.createWithFlags(launch_flags)
- except Exception as e:
- with excutils.save_and_reraise_exception():
- LOG.error(_("An error occurred while trying to launch a "
- "defined domain with xml: %s") %
- domain.XMLDesc(0))
-
- try:
- self._enable_hairpin(domain.XMLDesc(0))
- except Exception:
- with excutils.save_and_reraise_exception():
- LOG.error(_("An error occurred while enabling hairpin mode on "
- "domain with xml: %s") % domain.XMLDesc(0))
-
- # NOTE(uni): Now the container is running with its own private mount
- # namespace and so there is no need to keep the container rootfs
- # mounted in the host namespace
- if CONF.libvirt_type == 'lxc':
- state = self.get_info(instance)['state']
- container_dir = os.path.join(inst_path, 'rootfs')
- if state == power_state.RUNNING:
- disk.clean_lxc_namespace(container_dir=container_dir)
- else:
- disk.teardown_container(container_dir=container_dir)
-
- return domain
复制代码
|