云祺Logo 云祺Logo
关于我们

技术分享

技术分享 OpenStack基于Libvirt的虚拟化平台调度实现----Nova虚拟机启动源码实现(3)

OpenStack基于Libvirt的虚拟化平台调度实现----Nova虚拟机启动源码实现(3)

2018-02-06

接续上篇博文,我们继续来解析方法_create_image中的第二部分和第三部分,即驱动配置和文件注入部分的代码。

2.驱动配置部分代码解析

先来看方法_create_image中实现驱动配置部分的代码:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def _create_image(self, context, instance, libvirt_xml,  

  2.                       disk_mapping, suffix='',  

  3.                       disk_images=None, network_info=None,  

  4.                       block_device_info=None, files=None, admin_pass=None):  

  5.         """ 

  6.         调用之一传进来的参数: 

  7.         # context:上下文信息; 

  8.         # instance:实例信息; 

  9.         # libvirt_xml:为新建立的实例参数获取配置数据conf,并把获取的数据conf转换为xml格式; 

  10.         # disk_mapping=disk_info['mapping']:来宾系统磁盘的映射信息; 

  11.         # network_info=network_info:转换为传统格式的网络资源信息; 

  12.         # block_device_info=block_device_info:实例错误记录的块设备; 

  13.         # files=injected_files:编码后的注入文件; 

  14.         # admin_pass=admin_password:admin密码; 

  15.         # suffix=''; 

  16.         # disk_images=None; 

  17.         """  

  18.   

  19.         ......  

  20.   

  21.         # 驱动配置;  

  22.         if configdrive.required_by(instance):  

  23.             LOG.info(_('Using config drive'), instance=instance)  

  24.             extra_md = {}  

  25.             if admin_pass:  

  26.                 extra_md['admin_pass'] = admin_pass  

  27.   

  28.             # InstanceMetadata:虚拟机实例元数据类;  

  29.             # 获取虚拟机实例元数据操作类的实例化对象;  

  30.             # instance:虚拟机实例信息;  

  31.             # content=files:注入文件;  

  32.             # extra_md=extra_md:密码;  

  33.             inst_md = instance_metadata.InstanceMetadata(instance, content=files, extra_md=extra_md)  

  34.               

  35.             # ConfigDriveBuilder:获取配置驱动文件类的实例化对象;  

  36.             # 在类的实例化过程中,主要完成了以下工作:  

  37.             # 建立存储配置驱动临时文件的目录文件;  

  38.             # 根据instance_md获取实例元数据,并根据版本version更新完善不同版本的EC2类型的实例元数据;  

  39.             # 并把实例元数据写入到配置驱动的临时存储文件;  

  40.               

  41.             # 根据完善后的不同版本的实例元数据,生成ISO格式或vfat格式的镜像文件,默认是ISO格式的;  

  42.             with configdrive.ConfigDriveBuilder(instance_md=inst_md) as cdb:  

  43.                 # 生成镜像文件以后放置的路径;  

  44.                 configdrive_path = basepath(fname='disk.config')  

  45.                 LOG.info(_('Creating config drive at %(path)s'),  

  46.                          {'path': configdrive_path}, instance=instance)  

  47.   

  48.                 # 根据配置参数选择,生成ISO格式或vfat格式的镜像文件,默认是ISO格式的;  

  49.                 try:  

  50.                     cdb.make_drive(configdrive_path)  

  51.                 except exception.ProcessExecutionError, e:  

  52.                     with excutils.save_and_reraise_exception():  

  53.                         LOG.error(_('Creating config drive failed '  

  54.                                   'with error: %s'),  

  55.                                   e, instance=instance)  


首先经语句if configdrive.required_by(instance)判断,如果系统规定总是创建config drive,则进行后面的驱动配置操作。

接着调用语句inst_md = instance_metadata.InstanceMetadata(instance, content=files, extra_md=extra_md)实现类InstanceMetadata的初始化,获取虚拟机实例元数据操作类的实例化对象;

先来看看类InstanceMetadata的初始化方法:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. class InstanceMetadata():  

  2.     """ 

  3.     虚拟机实例元数据; 

  4.     """  

  5.   

  6.     def __init__(self, instance, address=None, content=[], extra_md=None,  

  7.                  conductor_api=None):  

  8.         # 实例信息;  

  9.         self.instance = instance  

  10.         # 密码信息;  

  11.         self.extra_md = extra_md  

  12.   

  13.         # 根据配置参数“use_local”具体的选择初始化类conductor_api.LocalAPI或者是conductor_api.API;  

  14.         # 获取并返回类的实例对象;  

  15.         # LocalAPI类:conductor API 的本地版本,这个类处理了本地数据库的更新等操作,而不是通过RPC;  

  16.         # API类:这个类通过RPC处理了数据库的更新等操作;  

  17.         # 这里简单解释一下nova conductor服务,在Grizzly版的Nova中,nova-conductor是在nova-compute之上的新的服务层;  

  18.         # 它使得nova-compute不再直接访问数据库;  

  19.         if conductor_api:  

  20.             self.conductor_api = conductor_api  

  21.         else:  

  22.             self.conductor_api = conductor.API()  

  23.   

  24.         # 获取admin的上下文信息;  

  25.         ctxt = context.get_admin_context()  

  26.   

  27.         # 类conductor_api.LocalAPI或conductor_api.API的实例化对象;  

  28.         capi = self.conductor_api  

  29.         # 根据host获取可用的域(zone);  

  30.         self.availability_zone = ec2utils.get_availability_zone_by_host(instance['host'], capi)  

  31.         # 为实例返回包含IP信息的字典;  

  32.         self.ip_info = ec2utils.get_ip_info_for_instance(ctxt, instance)  

  33.         # 获取实例的安全组信息;  

  34.         self.security_groups = capi.security_group_get_by_instance(ctxt, instance)  

  35.         # 通过实例instance获取所有的块设备映射;  

  36.         self.mappings = _format_instance_mapping(capi, ctxt, instance)  

  37.   

  38.         if instance.get('user_data'Noneis not None:  

  39.             self.userdata_raw = base64.b64decode(instance['user_data'])  

  40.         else:  

  41.             self.userdata_raw = None  

  42.   

  43.         self.ec2_ids = capi.get_ec2_ids(ctxt, instance)  

  44.   

  45.         self.address = address  

  46.   

  47.         # expose instance metadata.  

  48.         self.launch_metadata = {}  

  49.         for item in instance.get('metadata', []):  

  50.             self.launch_metadata[item['key']] = item['value']  

  51.   

  52.         self.password = password.extract_password(instance)  

  53.   

  54.         self.uuid = instance.get('uuid')  

  55.   

  56.         self.content = {}  

  57.         self.files = []  

  58.   

  59.         # get network info, and the rendered network template  

  60.         # 获取admin的上下文信息;  

  61.         ctxt = context.get_admin_context()  

  62.         # 返回实例相关的所有网络信息;  

  63.         # 并根据instance中新的实例信息更新表示一个实例信息缓存记录的数据表;  

  64.         network_info = network.API().get_instance_nw_info(ctxt, instance, conductor_api=capi)  

  65.   

  66.         self.network_config = None  

  67.         # 根据给定的网络信息返回一个渲染好的网络模板;  

  68.         cfg = netutils.get_injected_network_template(network_info)  

  69.   

  70.         if cfg:  

  71.             key = "%04i" % len(self.content)  

  72.             self.content[key] = cfg  

  73.             self.network_config = {"name""network_config",  

  74.                 'content_path'"/%s/%s" % (CONTENT_DIR, key)}  

  75.   

  76.         for (path, contents) in content:  

  77.             key = "%04i" % len(self.content)  

  78.             self.files.append({'path': path,  

  79.                 'content_path'"/%s/%s" % (CONTENT_DIR, key)})  

  80.             self.content[key] = contents  

类的初始化方法完成了一些变量和参数的初始化过程,我们对其中比较重要的一些语句进行解析。


首先来看下面的语句:

if conductor_api:
    self.conductor_api = conductor_api
else:
    self.conductor_api = conductor.API()

由传入的参数知道conductor_api的值为none,所以直接执行self.conductor_api = conductor.API()。这条语句是对类API进行了初始化,并获取了实例化对象。具体来看代码:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def API(*args, **kwargs):  

  2.     """ 

  3.     根据配置参数“use_local”具体的选择初始化类conductor_api.LocalAPI或者是conductor_api.API; 

  4.     获取并返回类的实例对象; 

  5.     LocalAPI类:conductor API 的本地版本,这个类处理了本地数据库的更新等操作,而不是通过RPC; 

  6.     API类:这个类通过RPC处理了数据库的更新等操作; 

  7.     这里简单解释一下nova conductor服务,在Grizzly版的Nova中,nova-conductor是在nova-compute之上的新的服务层; 

  8.     它使得nova-compute不再直接访问数据库; 

  9.     """  

  10.     use_local = kwargs.pop('use_local'False)  

  11.     if oslo.config.cfg.CONF.conductor.use_local or use_local:  

  12.         api = conductor_api.LocalAPI  

  13.     else:  

  14.         api = conductor_api.API  

  15.     return api(*args, **kwargs)  

就像注释中写的那样,这里根据配置参数“use_local”具体的选择初始化类conductor_api.LocalAPI或者是conductor_api.API,获取并返回类的实例对象。


其中,LocalAPI类是conductor API 的本地版本,这个类处理了本地数据库的更新等操作,而不是通过RPC;而API类则通过RPC处理了数据库的更新等操作。

这里需要简单解释一下nova conductor服务,在Grizzly版的Nova中,nova-conductor是在nova-compute之上的新的服务层,它使得nova-compute不再直接访问数据库。

我们回到类InstanceMetadata的初始化方法中,我们可以看到很多变量和参数的初始化赋值过程中,尤其是涉及到数据库查询的变量赋值中,都是通过nova-conductor这个服务层进行实现的,这个服务层实际上增强了代码的可扩展性和安全性。

我们再回到方法_create_image中,来看语句configdrive.ConfigDriveBuilder(instance_md=inst_md),这条鱼据实现的是对ConfigDriveBuilder这个构建配置驱动器的类进行实例的初始化,并获取类的初始化实例对象。具体来看这个类的初始化方法的代码:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. class ConfigDriveBuilder(object):  

  2.     """ 

  3.     构建配置驱动器; 

  4.     """  

  5.   

  6.     def __init__(self, instance_md=None):  

  7.         """ 

  8.         建立存储配置驱动临时文件的目录文件; 

  9.         根据instance_md获取实例元数据,并根据版本version更新完善不同版本的EC2类型的实例元数据; 

  10.         并把实例元数据写入到配置驱动的临时存储文件; 

  11.         """  

  12.         self.imagefile = None  

  13.           

  14.         # 建立配置驱动临时文件所放置的目录文件;  

  15.         # config_drive_tempdir:这个参数定义了要建立的配置驱动临时文件所放置的目录;  

  16.         # 参数的默认值为tempfile.tempdir,这里定义为NONE;  

  17.         self.tempdir = tempfile.mkdtemp(dir=CONF.config_drive_tempdir,  

  18.                                         prefix='cd_gen_')  

  19.   

  20.         # 把instance_md中的元数据写入到目录文件tempdir中;  

  21.           

  22.         # 根据instance_md获取实例元数据,并根据版本version更新完善不同版本的EC2类型的实例元数据;  

  23.         # 并把实例元数据写入到配置驱动的临时存储文件;  

  24.         if instance_md is not None:  

  25.             self.add_instance_metadata(instance_md)  

这个初始化过程中,最重要的语句就是self.add_instance_metadata(instance_md),它通过调用方法add_instance_metadata来实现把instance_md中的元数据写入到驱动配置的临时目录文件tempdir中。我们进一步来看方法add_instance_metadata的实现:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def add_instance_metadata(self, instance_md):  

  2.         """ 

  3.         获取实例元数据,并根据版本version更新不同版本的EC2类型的实例元数据; 

  4.         并把实例元数据写入到配置驱动的临时存储文件; 

  5.         """  

  6.         # metadata_for_config_drive:获取实例元数据,并根据版本version更新不同版本的EC2类型的实例元数据;  

  7.         # 不同版本的元数据都转换为json格式,并对应到相应的路径,以字典的形式(path,value)返回;  

  8.           

  9.         # 把data(实例元数据)写入到配置驱动的临时存储文件;  

  10.         for (path, value) in instance_md.metadata_for_config_drive():  

  11.             self._add_file(path, value)  

  12.             LOG.debug(_('Added %(filepath)s to config drive'),  

  13.                       {'filepath': path})  

再来看方法metadata_for_config_drive:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def metadata_for_config_drive(self):  

  2.         """ 

  3.         Yields (path, value) tuples for metadata elements. 

  4.         获取实例元数据,并根据版本version更新不同版本的EC2类型的实例元数据; 

  5.         不同版本的元数据都转换为json格式,并对应到相应的路径,以字典的形式(path,value)返回; 

  6.         """  

  7.           

  8.         # EC2 style metadata  

  9.         # EC2类型的实例元数据;  

  10.         # 从第一个版本开始,遍历所有的时间版本,循环更新实例的元数据(其中有定义跳过的版本);  

  11.         # 每一个版本的实例元数据都转化成json格式,并对应到相应的路径;  

  12.         for version in VERSIONS + ["latest"]:  

  13.             if version in CONF.config_drive_skip_versions.split(' '):  

  14.                 continue  

  15.   

  16.             # 获取实例的元数据,并根据version来完善实例的元数据;  

  17.             data = self.get_ec2_metadata(version)  

  18.             if 'user-data' in data:  

  19.                 filepath = os.path.join('ec2', version, 'user-data')  

  20.                 yield (filepath, data['user-data'])  

  21.                 del data['user-data']  

  22.   

  23.             try:  

  24.                 del data['public-keys']['0']['_name']  

  25.             except KeyError:  

  26.                 pass  

  27.   

  28.             filepath = os.path.join('ec2', version, 'meta-data.json')  

  29.             yield (filepath, json.dumps(data['meta-data']))  

  30.   

  31.         for version in OPENSTACK_VERSIONS + ["latest"]:  

  32.             path = 'openstack/%s/%s' % (version, MD_JSON_NAME)  

  33.             yield (path, self.lookup(path))  

  34.   

  35.             path = 'openstack/%s/%s' % (version, UD_NAME)  

  36.             if self.userdata_raw is not None:  

  37.                 yield (path, self.lookup(path))  

  38.   

  39.         for (cid, content) in self.content.iteritems():  

  40.             yield ('%s/%s/%s' % ("openstack", CONTENT_DIR, cid), content)  

以上的两个方法add_instance_metadata和metadata_for_config_drive最终实现了获取实例元数据,并根据版本version更新不同版本的EC2类型的实例元数据,并把实例元数据写入到配置驱动的临时存储文件当中。这里不再对这两个方法的具体实现进行代码解析,可以直接看我的代码注释即可。

至此,语句configdrive.ConfigDriveBuilder(instance_md=inst_md)解析完成。

我们再回到方法_create_image之中,继续看驱动配置的执行代码。之后最重要的一条语句就是cdb.make_drive(configdrive_path)。这条语句实现了根据配置参数选择,生成ISO格式或vfat格式的镜像文件,默认是ISO格式的。

我们来具体看方法make_drive的代码:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def make_drive(self, path):  

  2.         """ 

  3.         path:生成镜像文件以后放置的路径; 

  4.         根据配置参数选择,生成ISO格式或vfat格式的镜像文件,默认是ISO格式的; 

  5.         """  

  6.           

  7.         # 这个参数定义了配置驱动的格式,iso9660或者是vfat;  

  8.         # 参数的默认值为iso9660;  

  9.         # _make_iso9660:生成ISO格式的镜像文件;  

  10.         if CONF.config_drive_format == 'iso9660':  

  11.             self._make_iso9660(path)  

  12.         elif CONF.config_drive_format == 'vfat':  

  13.             self._make_vfat(path)  

  14.         else:  

  15.             raise exception.ConfigDriveUnknownFormat(  

  16.                 format=CONF.config_drive_format)  

配置参数config_drive_format定义了配置驱动的格式,iso9660或者是vfat。由于参数的默认值为iso9660,所以这里默认调用方法_make_iso9660来生成ISO格式的镜像文件。进一步来看方法_make_iso9660的代码:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def _make_iso9660(self, path):  

  2.         """ 

  3.         生成ISO格式的镜像文件; 

  4.         """  

  5.         publisher = "%(product)s %(version)s" % {  

  6.             'product': version.product_string(),  

  7.             'version': version.version_string_with_package()  

  8.             }  

  9.   

  10.         utils.execute(CONF.mkisofs_cmd,  

  11.                       '-o', path,  

  12.                       '-ldots',  

  13.                       '-allow-lowercase',  

  14.                       '-allow-multidot',  

  15.                       '-l',  

  16.                       '-publisher',  

  17.                       publisher,  

  18.                       '-quiet',  

  19.                       '-J',  

  20.                       '-r',  

  21.                       '-V''config-2',  

  22.                       self.tempdir,  

  23.                       attempts=1,  

  24.                       run_as_root=False)  

可见这里通过配置参数CONF.mkisofs_cmd调用命令genisoimage,来实现ISO格式的镜像文件的建立。

至此,方法_create_image中的第二部分,驱动配置的实现解析完成。(但是理解上还不到位)

3.文件注入部分代码解析

我们先来看方法_create_image中实现文件注入部分的代码:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def _create_image(self, context, instance, libvirt_xml,  

  2.                       disk_mapping, suffix='',  

  3.                       disk_images=None, network_info=None,  

  4.                       block_device_info=None, files=None, admin_pass=None):  

  5.         """ 

  6.         调用之一传进来的参数: 

  7.         # context:上下文信息; 

  8.         # instance:实例信息; 

  9.         # libvirt_xml:为新建立的实例参数获取配置数据conf,并把获取的数据conf转换为xml格式; 

  10.         # disk_mapping=disk_info['mapping']:来宾系统磁盘的映射信息; 

  11.         # network_info=network_info:转换为传统格式的网络资源信息; 

  12.         # block_device_info=block_device_info:实例错误记录的块设备; 

  13.         # files=injected_files:编码后的注入文件; 

  14.         # admin_pass=admin_password:admin密码; 

  15.         # suffix=''; 

  16.         # disk_images=None; 

  17.         """  

  18.   

  19.         ......  

  20.   

  21.         # 文件注入;  

  22.         elif CONF.libvirt_inject_partition != -2:  

  23.             # 要注入文件的目标分区号;  

  24.             target_partition = None  

  25.             if not instance['kernel_id']:  

  26.                 # 如果不是kernel_id镜像;  

  27.                 target_partition = CONF.libvirt_inject_partition  

  28.                 if target_partition == 0:  

  29.                     target_partition = None  

  30.             # 如果虚拟机实例类型为lxc,则目标分区号设置为None;  

  31.             if CONF.libvirt_type == 'lxc':  

  32.                 target_partition = None  

  33.   

  34.             # 如果定义了开机时注入ssh公钥,而且实例中具有'key_data'数据,则获取这个'key_data'数据;  

  35.             # libvirt_inject_key:这个参数定义了在开机时,是否注入ssh公钥;  

  36.             # 参数的默认值为True;   

  37.             if CONF.libvirt_inject_key and instance['key_data']:  

  38.                 key = str(instance['key_data'])  

  39.             else:  

  40.                 key = None  

  41.   

  42.             # get_injected_network_template:根据给定的网络信息返回一个渲染好的网络模板;  

  43.             net = netutils.get_injected_network_template(network_info)  

  44.   

  45.             # 获取虚拟机实例的元数据metadata;  

  46.             metadata = instance.get('metadata')  

  47.   

  48.             # libvirt_inject_password:这个参数定义了在开机时,是否注入管理员密码;  

  49.             # 参数的默认值为False;  

  50.             if not CONF.libvirt_inject_password:  

  51.                 admin_pass = None  

  52.   

  53.             # 如果key, net, metadata, admin_pass, files有一项不为none,就执行下面的代码;  

  54.             if any((key, net, metadata, admin_pass, files)):  

  55.                 # If we're not using config_drive, inject into root fs  

  56.                 injection_path = image('disk').path  

  57.                 img_id = instance['image_ref']  

  58.   

  59.                 for inj in ('key''net''metadata''admin_pass''files'):  

  60.                     if locals()[inj]:  

  61.                         LOG.info(_('Injecting %(inj)s into image '  

  62.                                    '%(img_id)s'), locals(), instance=instance)  

  63.                 # inject_data:注入指定的项目到指定的磁盘镜像;  

  64.                 # injection_path:要注入磁盘镜像的存储路径;  

  65.                 # key, net, metadata, admin_pass, files:要注入的项目;  

  66.                 # partition=target_partition:要注入磁盘的分区号;  

  67.                 # use_cow=CONF.use_cow_images:这个参数定义了是否使用cow格式的镜像文件,默认值为True;  

  68.                 try:  

  69.                     disk.inject_data(injection_path,  

  70.                                      key, net, metadata, admin_pass, files,  

  71.                                      partition=target_partition,  

  72.                                      use_cow=CONF.use_cow_images,  

  73.                                      mandatory=('files',))  

  74.                 except Exception as e:  

  75.                     with excutils.save_and_reraise_exception():  

  76.                         LOG.error(_('Error injecting data into image '  

  77.                                     '%(img_id)s (%(e)s)') % locals(),  

  78.                                   instance=instance)  

实际上,文件注入部分实现的就是把key(ssh公钥),net(渲染好的网络模板),metadata(虚拟机实例的元数据),admin_pass(用户密码)和files(传进来的已编码的文件)等注入到建立好的磁盘镜像之中。而实现这个过程的最重要的语句就是:

disk.inject_data(injection_path,
                 key, net, metadata, admin_pass, files,
                 partition=target_partition,
                 use_cow=CONF.use_cow_images,
                 mandatory=('files',))

我们来进一步看看方法inject_data的具体代码实现:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def inject_data(image, key=None, net=None, metadata=None, admin_password=None,  

  2.                 files=None, partition=None, use_cow=False, mandatory=()):  

  3.     """ 

  4.     注入指定的项目到磁盘镜像image; 

  5.      

  6.     # image:要注入磁盘镜像的存储路径; 

  7.     # key, net, metadata, admin_pass, files:要注入的项目; 

  8.     # partition:要注入磁盘的分区号; 

  9.     # use_cow:这个参数定义了是否使用cow格式的镜像文件,默认值为True; 

  10.     """  

  11.     LOG.debug(_("Inject data image=%(image)s key=%(key)s net=%(net)s "  

  12.                 "metadata=%(metadata)s admin_password=ha-ha-not-telling-you "  

  13.                 "files=%(files)s partition=%(partition)s use_cow=%(use_cow)s")  

  14.               % locals())  

  15.     fmt = "raw"  

  16.     if use_cow:  

  17.         fmt = "qcow2"  

  18.     try:  

  19.         fs = vfs.VFS.instance_for_image(image, fmt, partition)  

  20.         fs.setup()  

  21.     except Exception as e:  

  22.         for inject in mandatory:  

  23.             inject_val = locals()[inject]  

  24.             if inject_val:  

  25.                 raise  

  26.         LOG.warn(_('Ignoring error injecting data into image '  

  27.                    '(%(e)s)') % locals())  

  28.         return False  

  29.   

  30.     try:  

  31.         return inject_data_into_fs(fs, key, net, metadata, admin_password, files, mandatory)  

  32.     finally:  

  33.         fs.teardown()  

在这个方法中首先调用方法instance_for_image实现为挂载建立好的镜像准备磁盘,并调用方法setup实现磁盘的挂载。

再调用方法inject_data_into_fs实现相关文件的文件注入。我们来看方法inject_data_into_fs的代码:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def inject_data_into_fs(fs, key, net, metadata, admin_password, files, mandatory=()):  

  2.     """ 

  3.     注入指定数据到已经挂载的文件系统; 

  4.     """  

  5.     status = True  

  6.     for inject in ('key''net''metadata''admin_password''files'):  

  7.         inject_val = locals()[inject]  

  8.         inject_func = globals()['_inject_%s_into_fs' % inject]  

  9.         if inject_val:  

  10.             try:  

  11.                 inject_func(inject_val, fs)  

  12.             except Exception as e:  

  13.                 if inject in mandatory:  

  14.                     raise  

  15.                 LOG.warn(_('Ignoring error injecting %(inject)s into image '  

  16.                            '(%(e)s)') % locals())  

  17.                 status = False  

  18.     return status  

在这个方法中主要是通过不同字符串的匹配,来调用不同的方法,从而实现向磁盘镜像注入不同的文件信息。

例如这个方法中可以具体实现调用方法_inject_key_into_fs、_inject_net_into_fs、_inject_metadata_into_fs、_inject_admin_password_into_fs和_inject_files_into_fs,具体实现key、net、metadata、admin_password和files等文件的注入过程。

我们以方法_inject_net_into_fs为例,来解析向磁盘镜像注入文件的实现过程。来看方法_inject_net_into_fs的代码实现:

[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def _inject_net_into_fs(net, fs):  

  2.     """ 

  3.     Inject /etc/network/interfaces into the filesystem rooted at fs. 

  4.     net is the contents of /etc/network/interfaces. 

  5.     注入文件/etc/network/interfaces(即net)到文件系统fs的根目录; 

  6.     """  

  7.   

  8.     LOG.debug(_("Inject key fs=%(fs)s net=%(net)s") %  

  9.               locals())  

  10.     netdir = os.path.join('etc''network')  

  11.     fs.make_path(netdir)  

  12.     fs.set_ownership(netdir, "root""root")  

  13.     fs.set_permissions(netdir, 0744)  

  14.   

  15.     netfile = os.path.join('etc''network''interfaces')  

  16.     # 注入指定的文件到指定的文件系统;  

  17.     _inject_file_into_fs(fs, netfile, net)  

这个方法实现了注入文件/etc/network/interfaces(即net)到文件系统fs的根目录,在这个方法中,具体调用了方法_inject_file_into_fs来实现了向磁盘镜像注入指定的文件信息。


再来看方法_inject_file_into_fs的代码实现:


[python]    view plain   copy   print   ?   在CODE上查看代码片   派生到我的代码片  
  1. def _inject_file_into_fs(fs, path, contents, append=False):  

  2.     """ 

  3.     注入指定的文件到指定的文件系统; 

  4.     """  

  5.     LOG.debug(_("Inject file fs=%(fs)s path=%(path)s append=%(append)s") %  

  6.               locals())  

  7.     # 追加contents到path指定文件的结尾;  

  8.     if append:  

  9.         fs.append_file(path, contents)  

  10.     # 用contents替换path指定文件的内容;  

  11.     else:  

  12.         fs.replace_file(path, contents)  

这个方法的实现比较好理解,直接看我的代码注释即可。


至此,方法_create_image中的第三部分,即文件注入已完全解析完成。

从而,方法_create_image也已经解析完成。

在下篇博文 OpenStack基于Libvirt的虚拟化平台调度实现----Nova虚拟机启动源码实现(4)当中,我们将回到方法spawn中,继续对Nova虚拟机启动源码实现进行解析

云祺备份软件,云祺容灾备份系统,虚拟机备份,数据库备份,文件备份,实时备份,勒索软件,美国,图书馆
  • 标签:
  • 其他

您可能感兴趣的新闻 换一批

现在下载,可享30天免费试用

立即下载

请添加好友为您提供支持
jia7jia_7

微信售后服务二维码

请拨打电话
为您提供支持

400-9955-698