file injection代码

file injection原理来讲是比较简单的,在nova boot命令中,有参数--file,是将文件inject到image中

nova boot --flavor 2 --image d96b0e41-8264-41de-8dbb-6b31ce9bfbfc --key-name openstack --security-groups default --file /home/ubuntu/bootfromvolume1.xml=/home/cliu8/images/bootfromvolume.xml testinject19

这里容易混淆的是还有一个参数是user_data,这不是file injection,而是cloud-init使用这里面的东西对系统进行初始化。

在虚拟机启动的时候,会调用到/usr/lib/python2.7/dist-packages/nova/virt/libvirt/driver.py中的spawn函数

def spawn(self, context, instance, image_meta, injected_files,          admin_password, network_info=None, block_device_info=None):    disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,                                        instance,                                        block_device_info,                                        image_meta)    self._create_image(context, instance,                       disk_info['mapping'],                       network_info=network_info,                       block_device_info=block_device_info,                       files=injected_files,                       admin_pass=admin_password)    xml = self.to_xml(context, instance, network_info,                      disk_info, image_meta,                      block_device_info=block_device_info,                      write_to_disk=True)

    self._create_domain_and_network(context, xml, instance, network_info,                                    block_device_info)    LOG.debug(_("Instance is running"), instance=instance)

其中一步是create image,在_create_image函数中,有下面的逻辑

elif inject_files and CONF.libvirt.inject_partition != -2:    if booted_from_volume:        LOG.warn(_('File injection into a boot from volume '                   'instance is not supported'), instance=instance)    self._inject_data(        instance, network_info, admin_pass, files, suffix)

当有文件要inject的时候,并且参数inject_partition不设为-2,则执行file injection过程。

从这里我们可以看出,在参数中应该添加

# cat /etc/nova/nova-compute.conf [DEFAULT]compute_driver=libvirt.LibvirtDriver[libvirt]virt_type=kvminject_partition=-1

在代码中,我们可以看到这个参数是这样解释的:

cfg.IntOpt('inject_partition',            default=-2,            help='The partition to inject to : '                 '-2 => disable, -1 => inspect (libguestfs only), '                 '0 => not partitioned, >0 => partition number',           deprecated_name='libvirt_inject_partition',           deprecated_group='DEFAULT'),

在最新的代码中,我们往往选择用ligguestfs来进行文件注入,因而设为-1

在_inject_data函数中,

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

(Pdb) p injection_path'/var/lib/nova/instances/fedc59a9-6ff5-4c23-9595-d877a6796c84/disk'

(Pdb) p target_partition-1

这里key, net, metadata, admin_pass我们都不注入,所以都为空,只有files有值

这里就调用到/usr/lib/python2.7/dist-packages/nova/virt/disk/api.py的

def inject_data(image, key=None, net=None, metadata=None, admin_password=None,                files=None, partition=None, use_cow=False, mandatory=()):

在这个函数中首先生成一个对象来操作这个image

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

如果能够找到guestfs,则fs为nova.virt.disk.vfs.guestfs.VFSGuestFS

接着调用来初始化环境,该run起来的guestfs的applicance要运行起来,该mount的image要mount上去

fs.setup()

最后进行注入操作

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

在/usr/lib/python2.7/dist-packages/nova/virt/disk/vfs/guestfs.py的setup函数中,

并不是调用guestfish命令,而是直接通过python调用guestfsd的API,可以参考http://libguestfs.org/guestfs.3.html

先要有个handle

self.handle = tpool.Proxy(guestfs.GuestFS(close_on_exit=False))

然后将image作为一个drive添加到appliance

self.handle.add_drive_opts(self.imgfile, format=self.imgfmt)

运行appliance

self.handle.launch()

self.setup_os()

self.handle.aug_init("/", 0)

setup_os的实现很简单,由于我们的参数是-1,则调用setup_os_inspect

def setup_os(self):    if self.partition == -1:        self.setup_os_inspect()    else:        self.setup_os_static()

在函数setup_os_inspect中

先看有多少个root disk

roots = self.handle.inspect_os()

(Pdb) p roots['/dev/sda1']

发现只有一个然后调用

self.setup_os_root(roots[0])

在setup_os_root中,先是得到mount point

mounts = self.handle.inspect_get_mountpoints(root)

(Pdb) p mounts[('/', '/dev/sda1')]

最后self.handle.mount_options("", mount[1], mount[0])

(Pdb) p mount[1]'/dev/sda1'(Pdb) p mount[0]'/'

将/dev/sda1挂载到/下面

至此appliance起来了,image也挂载了, fs.setup()成功,接下来是调用inject_data_into_fs

inject_data_into_fs对不同的注入对象调用不同的函数,对于文件注入调用def _inject_files_into_fs(files, fs):

这里面是一个循环,每个文件都调用def _inject_file_into_fs(fs, path, contents, append=False):

这里面调用了fs.replace_file(path, contents)

其实是写一个文件到一个path

def replace_file(self, path, content):    LOG.debug(_("Replace file path=%s"), path)    path = self._canonicalize_path(path)    self.handle.write(path, content)

(Pdb) p pathu'/home/ubuntu/bootfromvolume1.xml'(Pdb) p content"\n  bootfromvolume\n  0f0806ab-531d-6134-5def-c5b495529289\n  2097152\n  2097152\n  1\n  \n    hvm\n    \n  \n  \n    \n    \n    \n  \n  \n  destroy\n  restart\n  restart\n  \n    /usr/bin/kvm-spice\n    \n      \n      \n      \n      640a10f7-3965-4a47-9641-002a94526445\n      \n     

\n    \n    \n     
\n    \n    \n    \n     
\n    \n    \n      \n      \n      \n     
\n        \n    \n      \n    \n    \n      \n    \n    \n    \n    \n      \n    \n