问题导读
1、/nova/virt/disk/的源码文件,分别有什么内涵?
2、在什么确定了的情况下,导入相应的挂载方式实现类?
3、VFS模块使用什么API来访问磁盘镜像文件?
概要
openstack在创建实例的时候可以将文件和相关的参数注入到实例的镜像中,来完成密码的设定、密钥的添加等动作,而这些动作的完成都是在实例启动之前。这在有些情况下会非常有用,比如修改root密码,注入ssh密钥,对实例的个性化定制等等。
1.相关源码的源码结构
文件和元数据注入是/nova/virt/disk/下的源码文件实现的,我们先来看一下/nova/virt/disk/的源码结构。
/nova/virt/disk/api.py
提供了调整、重新分区和修改磁盘镜像的方法;
包括文件和元数据注入等方法;
磁盘上的实践包括:重定义大小,文件系统建立和文件注入等;
def get_disk_size(path):
获取虚拟磁盘镜像大小;
def extend(image, size):
根据定义的size镜像大小,改变当前镜像的大小;
def can_resize_fs(image, size,use_cow=False):
检测是否可以对文件系统的大小进行重新调整;
class _DiskImage(object):
磁盘文件镜像的操作类;
def _device_for_path(path):
从path中获取表示device的路径信息;
def _reset(self, device):
为之前挂载的目录进行内部状态的重新设置;
def errors(self):
返回所有操作的错误集合;
def mount(self):
应用对象属性,实现磁盘镜像的挂载;
def umount(self):
从文件系统上卸载挂载点;
def teardown(self):
从文件系统上删除磁盘镜像;
- def inject_data(image, key=None, net=None, metadata=None, admin_password=None, files=None, partition=None, use_cow=False, mandatory=()):
复制代码
注入指定的项目到磁盘镜像image;
def setup_container(image,container_dir, use_cow=False):
安装LXC容器;
将会挂载loopback镜像到容器路径,来为容器建立根文件系统;
def teardown_container(container_dir):
这个方法实现了删除容器在根文件系统的挂载;
它将会卸载已经挂载的容器,并删除所有的连接设备;
def clean_lxc_namespace(container_dir):
这个方法实现了清理容器命名空间的根文件系统的挂载;
它将会卸载已经挂载的名称,但是只会留下相关的连接设备;
- def inject_data_into_fs(fs,key, net, metadata, admin_password, files, mandatory=()):
复制代码
注入指定数据到已经挂载的文件系统;
def _inject_files_into_fs(files,fs):
注入文件到文件系统;
def _inject_file_into_fs(fs,path, contents, append=False):
注入指定的文件到指定的文件系统;
def_inject_metadata_into_fs(metadata,fs):
注入元数据到文件系统;
def _setup_selinux_for_keys(fs,sshdir):
获取selinux来宾系统来确认注入key的内容的正确性;
def _inject_key_into_fs(key,fs):
添加指定的公开ssh密钥到root目录下的authorized_keys文件中;
def _inject_net_into_fs(net,fs):
注入文件/etc/network/interfaces(即net)到文件系统fs的根目录;
def _inject_admin_password_into_fs(admin_passwd,fs):
为admin_passwd设置超级用户密码;
def_set_passwd(username,admin_passwd, passwd_data, shadow_data):
为用户admin_passwd设置密码;
/nova/virt/disk/vfs/api.py
虚拟文件系统实践;
虚拟文件系统API;
class VFS(object):
VFS类定义了一个接口,用来为虚拟磁盘镜像文件系统操作文件;
defsetup(self):
执行任务的方法;
defteardown(self):
释放在setup方法中初始化的所有的资源;
defmake_path(self, path):
建立路径;
defappend_file(self, path,content):
添加内容(@content)到@path所标志文件的结尾,
如果@path所标志文件不存在,则新建立文件;
def replace_file(self, path,content):
用@content替换@path所指定文件的内容,
如果文件不存在,则新建立文件;
def read_file(self, path):
返回@path所指定文件的全部内容;
defhas_file(self, path):
如果@path指定的文件存在,则返回true值;
def set_permissions(self, path,mode):
设置文件权限;
def set_ownership(self, path,user, group):
设置文件的用户及用户组;
/nova/virt/disk/vfs/guestfs.py
来宾文件系统访问磁盘镜像文件;
class VFSGuestFS(vfs.VFS):
类 VFSGuestFS继承自类VFS;
类VFSGuestFS实现了VFS模块,这个模块使用了libguestfs API来访问磁盘镜像文件;
- def __init__(self, imgfile,imgfmt='raw', partition=None):
- def setup_os(self):
- def setup_os_static(self):
- def setup_os_inspect(self):
- def setup_os_root(self, root):
- def setup(self):
- def teardown(self):
- def _canonicalize_path(path):
- def make_path(self, path):
- def append_file(self, path,content):
- def replace_file(self, path,content):
- def read_file(self, path):
- def has_file(self, path):
- def set_permissions(self, path,mode):
- def set_ownership(self, path,user, group):
复制代码
/nova/virt/disk/vfs/localfs.py
本地文件系统访问磁盘镜像文件操作;
classVFSLocalFS(vfs.VFS):
类VFSLocalFS继承自类VFS,这个类实现了VFS模块,
它映射到一个挂载在主机(本地)文件系统上的现有的虚拟根目录;
- def__init__(self, imgfile,imgfmt="raw", partition=None, imgdir=None):
- def setup(self):
- def teardown(self):
- defmake_path(self, path):
- def append_file(self, path,content):
- def replace_file(self, path,content):
- def read_file(self, path):
- def has_file(self, path):
- def set_permissions(self, path,mode):
- def set_ownership(self, path,user, group):
复制代码
/nova/virt/disk/mount/api.py
支持挂载磁盘镜像到主机文件系统;
支持挂载虚拟镜像文件;
classMount(object):
这个类是标准的挂载操作类,可以被子类重写;
基本的设备操作包括get、map和mount等;
def instance_for_format(imgfile,mountdir, partition, imgfmt):
根据镜像类型来判断导入代表不同挂载方式的挂载类;
如果镜像类型是raw,则导入nova.virt.disk.mount.loop.LoopMount;
如果镜像是其他类型,则导入nova.virt.disk.mount.nbd.NbdMount;
def instance_for_device(imgfile,mountdir, partition, device):
根据设备属性来判断导入代表不同挂载方式的挂载类;
如果设备属性中包括"loop",则导入nova.virt.disk.mount.loop.LoopMount;
如果设备属性中不包括"loop",则导入nova.virt.disk.mount.nbd.NbdMount;
defreset_dev(self):
重新设置设备路径允许进行卸载操作;
def get_dev(self):
设置可用的镜像文件作为块设备在文件系统中;
def unget_dev(self):
从文件系统的命名空间释放块设备;
def map_dev(self):
映射设备的分区到文件系统的命名空间;
defunmap_dev(self):
从文件系统的命名空间删除设备的分区;
def mnt_dev(self):
挂载设备到文件系统;
def unmnt_dev(self):
从文件系统卸载设备;
def do_mount(self):
调用get、map和mnt等操作实现设备的挂载操作;
def do_umount(self):
调用unmnt操作实现设备的卸载操作;
def do_teardown(self):
调用umnt、unmap和unget操作实现相关资源的卸载及清理工作;
def _get_dev_retry_helper(self):
def_inner_get_dev(self):
/nova/virt/disk/mount/loop.py
支持回环设备挂载镜像;
针对raw格式的镜像的回环挂载支持类;
class LoopMount(api.Mount):
针对raw格式的镜像的回环挂载支持类;
类LoopMount继承自类Mount;
- def _inner_get_dev(self):
- def get_dev(self):
- def unget_dev(self):
复制代码
/nova/virt/disk/mount/nbd.py
磁盘镜像的qemu-nbd挂载支持类;
class NbdMount(api.Mount):
磁盘镜像的qemu-nbd挂载支持类;
类NbdMount继承自类Mount;
- def_detect_nbd_devices(self):
复制代码
检测nbd设备文件;
- def_find_unused(self,devices):
- def _allocate_nbd(self):
- def _read_pid_file(self,pidfile):
- def _inner_get_dev(self):
- defget_dev(self):
- def unget_dev(self):
复制代码
2.OpenStack Nova文件和元数据注入
openstack在创建实例的时候,可以对实例进行文件注入,注入的内容可为:
key ssh公钥;
network 网络参数;
admin_password 注入超级用户 root 的密码;
metadata 注入元数据meta.js;
在下一篇博客中将会具体来看相关代码的实现;
首先来看各种文件注入的命令行实现:
注入ssh密钥
创建密钥(ssh-keygen),添加密钥对;
- nova keypair-add --pub_key.ssh/id_rsa.pub mykey
复制代码
创建实例的时候注入密钥;
- nova boot --key_name mykey --imageid_of_image --flavor 1 name_of_instance
复制代码
注:id_of_image可以通过nova image-list取得;
注入文件
假如我要注入.vimrc这个文件到新创建的实例中,可以:
- nova boot --file /root/.vimrc=/root/.vimrc--image id_of_image --flavor 1 name_of_instance
复制代码
注:可以注入多个文件(最多5个),只要写多个--file;
注入元数据
可以通过--meta给实例中传入键值对,注入后会写在/meta.js文件里,以类似python字典的方式存储在虚拟机的/meta.js文件中;
- nova boot --meta key2=value2 --metakey1=value1 --image id_of_image --flavor 1 name_of_instance
- cat /meta.js
- {"key2″: “value2″,“key1″: “value1″}
复制代码
3.文件注入过程中涉及的镜像挂载方式
OpenStack grizzly版本中支持的文件注入执行中镜像的挂载方式有两种:loop和nbd。每种方式对应的实现代码位于nova/nova/virt/disk/mount/目录下,文件名分别为:loop.py和nbd.py。这里以在CENTOS系统上的应用操作,来对这两种文件挂载方式进行一个简单的介绍。
loop方式
loop方式下,镜像以loopdevice的方式挂载到宿主机的某个设备名下,例如:
- #losetup–find –show centos-6.2-x86_64.raw
- #/dev/loop0
复制代码
以上命令表示,centos-6.2-x86_64.raw已经被挂载为设备/dev/loop0,可以通过该loop设备来操作镜像文件。
之后,通过kpartx命令,找到loop设备上的可用分区,并分别在宿主机上进行挂载。
注:linux loop device介绍
在Linux中,有一种特殊的块设备叫loop device,这种loop device设备是通过影射操作系统上的正常的文件而形成的虚拟块设备。因为这种设备的存在,就为我们提供了一种创建一个存在于其他文件中的虚拟文件系统的机制.
回环设备( 'loopback device')允许用户以一个普通磁盘文件虚拟一个块设备。设想一个磁盘设备,对它的所有读写操作都将被重定向到读写 一个名为 disk-image 的普通文件而非操作实际磁盘或分区的轨道和扇区。(当然,disk-image 必须存在于一个实际的磁盘上,而这个磁 盘必须比虚拟的磁盘容量更大。)回环设备允许你这样使用一个普通文件。
回环设备的使用与其它任何块设备相同。特别是,你可以在这个设备上创建文件系统并像普通的磁盘一样将它挂载在系统中。这样的一个将全部内容保存在一个普通文件中的文件系统,被称为虚拟文件系统(virtual file system)。
nbd方式
nbd方式下是通过qemu-nbd命令来实现虚拟机镜像的挂载。
首先确保nbd内核模块已经加载:
复制代码
此时在/dev目录下应该可以看到nbd的设备:
- /dev/nbd0 /dev/nbd10 /dev/nbd12 /dev/nbd14 /dev/nbd2 /dev/nbd4/dev/nbd6 /dev/nbd8
- /dev/nbd1/dev/nbd11 /dev/nbd13 /dev/nbd15 /dev/nbd3 /dev/nbd5 /dev/nbd7 /dev/nbd9
-
- #qemu-nbd–connect=/dev/nbd2 /root/temp/centos-6.2-x86_64.raw
- #fdisk-l /dev/nbd2
- Disk/dev/nbd2: 21.5 GB, 21474836480 bytes
- 255heads, 63 sectors/track, 2610 cylinders, total 41943040 sectors
- Units= sectors of 1 * 512 = 512 bytes
- Sectorsize (logical/physical): 512 bytes / 512 bytes
- I/Osize (minimum/optimal): 512 bytes / 512 bytes
- Diskidentifier: 0x00046def
-
- DeviceBoot Start End Blocks Id System
- /dev/nbd2p1* 2048 41943039 20970496 83 Linux
复制代码
此时已经可以看到镜像文件的分区了,通过将/dev/nbd2p1设备挂载在宿主机上就可以对镜像中的文件进行操作了。
注:NBD简介
NBD(Network Block Device)让你可以将一个远程主机的磁盘空间,当作一个块设备来使用。就像一块硬盘一样.使用它,你可以很方便的将另一台服务器的硬盘空间,增加到本地服务器上。NBD与NFS有所不同.NFS只是提供一个挂载点供客户端使用,客户端无法改变这个挂载点的分区格式。而NBD提供的是一个块设备,客户端可以把这个块设备格式化成各种类型的分区.更便于用户的使用. NBD是一个内核模块,大部分Linux发行版都已包含它。
在/nova/virt/disk/mount/api.py代码文件中,类Mount定义了标准的挂载操作类,在这个类中有两个方法,instance_for_format和instance_for_device,前者实现了通过镜像文件的格式来判断采用哪种挂载方式,后者实现了通过设备属性中是否包括“loop”来判断采用哪种挂载方式,具体来看代码:
- @staticmethod
- def instance_for_format(imgfile, mountdir, partition, imgfmt):
- LOG.debug(_("Instance for format imgfile=%(imgfile)s "
- "mountdir=%(mountdir)s partition=%(partition)s "
- "imgfmt=%(imgfmt)s") % locals())
- if imgfmt == "raw":
- LOG.debug(_("Using LoopMount"))
- return importutils.import_object(
- "nova.virt.disk.mount.loop.LoopMount",
- imgfile, mountdir, partition)
- else:
- LOG.debug(_("Using NbdMount"))
- return importutils.import_object(
- "nova.virt.disk.mount.nbd.NbdMount",
- imgfile, mountdir, partition)
复制代码
- @staticmethod
- def instance_for_device(imgfile, mountdir, partition, device):
- LOG.debug(_("Instance for device imgfile=%(imgfile)s "
- "mountdir=%(mountdir)s partition=%(partition)s "
- "device=%(device)s") % locals())
- if "loop" in device:
- LOG.debug(_("Using LoopMount"))
- return importutils.import_object(
- "nova.virt.disk.mount.loop.LoopMount",
- imgfile, mountdir, partition, device)
- else:
- LOG.debug(_("Using NbdMount"))
- return importutils.import_object(
- "nova.virt.disk.mount.nbd.NbdMount",
- imgfile, mountdir, partition, device)
复制代码
可见,确定了所应用的挂载方式后,就导入相应的挂载方式实现类。
|