nova 创建虚拟机流程
1 Nova创建虚机流程
Openstack创建虚拟机的整个流程如图1所示。前端horizon发送创建虚机的请求之后,novaapi接收请求,并作处理,详见1.1节。注:Nova scheduler在另外章节介绍。
图 1 nova创建虚拟机流程
1.1 Nova api
Nova api接受到前端(horizon)发来的创建虚机的请求后,调用create(self, req, body)函数(/opt/stack/nova/nova/api/openstack/compute/servers.pyà1088)。该函数主要获取前端发来的body数据并解析,将相应参数传递给compute api(1.2节)。
1.2 Compute api
Compute api接受到请求之后,调用create函数。
def create(self, context, instance_type,image_href, kernel_id=None, ramdisk_id=None, min_count=None, max_count=None, display_name=None,
display_description=None, key_name=None, key_data=None, security_group=None,availability_zone=None, user_data=None,metadata=None,
injected_files=None, admin_password=None, block_device_mapping=None, access_ip_v4=None,access_ip_v6=None, requested_networks=None, config_drive=None,auto_disk_config=None, scheduler_hints=None, iolimit=None, hypervisor=None):
“””
代码位置:/opt/stack/nova/nova/compute/api.pyà749
部分参数如下:
display_name=test
admin_password=123456
image_href=66c50770-21a4-46ce-85ee-392f29b05081
metadata={}
injected_files=[]
requested_networks={}
min_count=1
max_count=1
instance_type ={'memory_mb': 512L, 'root_gb': 0L, 'deleted_at': None, 'name':u'm1.tiny' , 'deleted': False,'created_at': None, 'ephemeral_gb': 0L, 'updated_at': None, 'disabled': False,'vcpus': 1L, 'extra_specs': {}, 'swap': 0L, 'rxtx_factor': 1.0, 'is_public':True, 'flavorid': u'1', 'vcpu_weight': None, 'id': 4L}
”””
- 检查各种参数及配额
_check_num_instances_quota(context, instance_type, min_count, max_count,hypervisor=hypervisor)
_check_metadata_properties_quota(context, metadata)
_check_injected_file_quota(context, injected_files)
_check_requested_networks(context, requested_networks)
- 获取镜像信息,检查flavor的ram,disk是否超出镜像规定的最小限额
(image_service, image_id) = lance.get_remote_image_service(image_href)
image = image_service.show(context, image_id)
if instance_type['memory_mb'] < int(image.get('min_ram') or 0):
raise exception
if instance_type['root_gb'] < int(image.get('min_disk') or 0):
raise exception - 将参数放入字典base_options={
'vm_state': 'building',
'ephemeral_gb': 0L,
'access_ip_v6': None,
'access_ip_v4': None,
'kernel_id': 'adaf51fe-c235-40b6-8aa7-80216d42303c',
'key_name': None,
'ramdisk_id': 'f9216b02-8e27-44da-b159-8d64401a16f2',
'instance_type_id': 4L,
'user_data':u'IyEvYmluL2Jhc2gKdXNlcmFkZCAtbSByb290CnBhc3N3ZCByb290IDw8IEVPRgpyb290CnJvb3QKRU9GCnBhc3N3ZCByb290IDw8IEVPRgpyb290CnJvb3QKRU9GCg==',
'vm_mode': '',
'display_name': u'test',
'config_drive_id': '',
'reservation_id': 'r-yl00q2af','architecture': 'Unknown',
'key_data': None, 'root_gb': 0L,
'user_id': u'c94cf849c7a94174b18f516c8fe497ee', 'hypervisor':u'libvirt',
'availability_zone': None,
'launch_time': '2014-10-30T05:28:15Z',
'metadata': {},
'display_description': u'test',
'memory_mb': 512L,
'vcpus': 1L,
'locked': False,
'image_ref': u'66c50770-21a4-46ce-85ee-392f29b05081',
'root_device_name': None,
'power_state': 0,
'progress': 0,
'cdrom_active': False,
'project_id': u'1ec9c7671c824b798fb9a028623aaccc',
'config_drive': ''}
- 利用base_options在数据库创建instance
instance = self.create_db_entry_for_new_instance(context, instance_type,image, base_options, security_group, block_device_mapping)
- rpc调用启动虚机(发请求到compute manger)
self.scheduler_rpcapi.run_instance(context,request_spec=request_spec, admin_password=admin_password,injected_files=injected_files, requested_networks=requested_networks,is_first_time=True, filter_properties=filter_properties)
1.3 Compute manager
Computemanager收到run_instance的请求后,运行函数run_instance。该函数内部会调用_run_instance函数如下:
def _run_instance(self,context, request_spec, filter_properties, requested_networks, injected_files,admin_password, is_first_time, instance):
“””
代码位置:/opt/stack/nova/nova/compute/manager.pyà510
参数实际值:
request_spec:={
'block_device_mapping':[],
'security_group': [u'default'],
'instance_uuids': ['8383fb3d-7dcf-4203-b639- 14c617b55259']'instance_uuids': ['8383fb3d-7dcf-4203-b639- 14c617b55259']
'instance_uuids': ['8383fb3d-7dcf-4203-b639- 14c617b55259']
'image': {'status': 'active',
'name':'cirros-test-image',
'deleted':False,
'container_format':'ami',
'created_at':'2014-10-30T04:21:05.000000',
'disk_format': 'ami',
'updated_at':'2014-10-30T04:21:09.000000',
'properties': {'kernel_id':'adaf51fe-c235-40b6-8aa7-80216d42303c',
'source': 'nova',
'ramdisk_id':'f9216b02-8e27-44da-b159-8d64401a16f2'},
'min_disk': 0,
'min_ram': 0,
'checksum':'2f81976cae15c16ef0010c51e3a6c163',
'owner':'1ec9c7671c824b798fb9a028623aaccc',
'is_public': True,
'deleted_at': None,
'id':'66c50770-21a4-46ce-85ee-392f29b05081', 'size': 25165824},
'instance_type': {'memory_mb': 512L,
'root_gb': 0L,
'deleted_at': None,
'name': u'm1.tiny',
'deleted': False,
'created_at': None,
'ephemeral_gb': 0L,
'updated_at': None,
'disabled': False,
'vcpus': 1L,
'extra_specs': {},
'swap': 0L,
'rxtx_factor': 1.0,
'is_public': True,
'flavorid': u'1',
'vcpu_weight': None,
'id': 4L},
'instance_properties': {'vm_state': 'building',
'ephemeral_gb': 0L,
'access_ip_v6': None,
'access_ip_v4': None,
'kernel_id': 'adaf51fe-c235-40b6-8aa7-80216d42303c',
'key_name': None,
'ramdisk_id':'f9216b02-8e27-44da-b159-8d64401a16f2', 'instance_type_id': 4L,
'user_data':u'IyEvYmluL2Jhc2gKdXNlcmFkZCAtbSByb290CnBhc3N3ZCByb290IDw8IEVPRgpyb290CnJvb3QKRU9GCnBhc3N3ZCByb290IDw8IEVPRgpyb290CnJvb3QKRU9GCg==',
'vm_mode': None,
'display_name':u'test',
'config_drive_id':'',
'reservation_id':'r-yl00q2af',
'os_type': None,
'architecture': None,
'key_data': None,
'iolimit': None,
'root_gb': 0L,
'user_id':u'c94cf849c7a94174b18f516c8fe497ee',
'hypervisor':u'libvirt',
'availability_zone':None,
'launch_time':'2014-10-30T05:28:15Z',
'metadata': {},
'display_description':u'test',
'memory_mb': 512L,
'vcpus': 1L,
'locked': False,
'image_ref':u'66c50770-21a4-46ce-85ee-392f29b05081', 'root_device_name': None,
'power_state': 0,
'auto_disk_config':None,
'progress': 0,
'cdrom_active':False,
'project_id': u'1ec9c7671c824b798fb9a028623aaccc',
'config_drive': ''}
}
admin_password=123456
injected_files=[]
requested_networks={}
”””
- 查看虚机是否存在
self._check_instance_not_already_created(context, instance)
- 查看镜像大小超过flavor规定最大值
self._check_image_size(context, instance)
- 更新虚机的状态(db)
self._start_building(context, instance)
- 分配网络,见1.4节 network
self._allocate_network
network_info=self.network_api.allocate_for_instance(context,instance,vpn=is_vpn, requested_networks=requested_networks)
- 孵化虚拟机,见1.5节libvirt driver
self.driver.spawn(context, instance, image_meta, network_info, block_device_info, injected_files,admin_password)
1.4 network
1.4.1 network api
networkapi收到compute manager发送的请求之后调用allocate_for_instance,增加一些参数,远程rpc调用将请求发送给network manager。
def allocate_for_instance(self,context, instance, **kwargs):
"""代码位置:/opt/stack/nova/nova/network/api.pyà277
Allocates all network structures for an instance.
:returns: network info as fromget_instance_nw_info() below
"""
args = kwargs
args['instance_id'] = instance['id']
args['instance_uuid'] = instance['uuid']
args['project_id'] = instance['project_id']
args['host'] = instance['host']
args['rxtx_factor'] = instance['instance_type']['rxtx_factor']
nw_info = rpc.call(context, FLAGS.network_topic,
{'method':'allocate_for_instance',
'args': args})
return network_model.NetworkInfo.hydrate(nw_info)
1.4.2 network manager
networkmanager收到请求后,调用函数allocate_for_instance。
def allocate_for_instance(self, context, **kwargs):
"""
代码位置:/opt/stack/nova/nova/network/manager.pyàclass NetworkManagerà1063
"""
- 从数据库获取所有网络networks
注:该网络是在安装时,写入数据库的
networks = self._get_networks_for_instance(admin_context, instance_id,project_id, requested_networks=requested_networks)à 1078
本环境只有一个network
{'bridge': u'br100',
'vpn_public_port': None,
'dhcp_start': u'10.1.0.2',
'bridge_interface': u'eth0',
'updated_at': None,
'id': 1L,
'cidr_v6': None,
'deleted_at': None,
'gateway':u'10.1.0.1',
'rxtx_base': None,
'uuid':u'e011b388-163b-4080-a083-bb63ebed7f6a',
'label':u'private',
'priority': None,
'project_id': None,
'netmask_v6': None,
'_sa_instance_state': <sqlalchemy.orm.state.InstanceStateobject at 0x42c4790>,
'deleted': False,
'vlan': None,
'broadcast':u'10.1.255.255',
'netmask':u'255.255.0.0',
'injected': False,
'cidr':u'10.1.0.0/16',
'vpn_public_address': None,
'multi_host': True,
'dns2': None,
'dns1':u'8.8.4.4',
'host': None,
'gateway_v6': None,
'vpn_private_address': None,
'created_at':datetime.datetime(2014, 10, 30, 4, 22, 44)}
- 分配mac地址
self._allocate_mac_addresses(context, instance_uuid, networks)->1083
mac为自动生成(address=utils.generate_mac_address),返回结果为vif
{'uuid':'7344166b-e3ef-44be-80e3-ecfa5696cce8',
'instance_uuid':u'90844617-d71f-474f-9cdf-35e244c846b0',
'network_id':1L,
'address':'fa:16:3e:76:b4:2f‘}
- 分配fixed ip
self._allocate_fixed_ips(admin_context, instance_id, host, networks,vpn=vpn, requested_networks=requested_networks)->1084
allocate_fixed_ip(self, context, instance_id, network, **kwargs)à2124
1) 从数据库(fixedip池)获取address,本例为10.1.0.2
self.db.fixed_ip_associate_pool(context.elevated(),network['id'],instance_ref['uuid'])->2142)
2) 从数据库获取网卡vif 信息
self.db.virtual_interface_get_by_instance_and_network(context,instance_ref['uuid'],network['id'])->2148)
3) 更新数据库,标示该fixed_ip被分配,对应网卡为vif
注:mac:ip写配置文件/opt/stack/data/nova/networks/nova-br100.conf
values = {'allocated': True,'virtual_interface_id': vif['id']}
self.db.fixed_ip_update(context,address, values)->2152
4) 宿主机建立网络self._setup_network_on_host(context, network)à2153
def_setup_network_on_host(self, context, network):à2055
"""
功能:1.设置dhcp_server的ip
2.创建网桥,并绑定特定网卡
3.设置iptables规则,并应用
"""
#network['dhcp_server']= self._get_dhcp_ip(context, network)从数据库获取,若不存在则从fixedip池中获取。本例为10.1.0.3。
try:
fip = self.db.fixed_ip_get_by_network_host(context,network_id, host)
return fip['address']
except exception.FixedIpNotFoundForNetworkHost:
elevated = context.elevated()
return self.db.fixed_ip_associate_pool(elevated,network_id, host=host)
#self.l3driver.initialize_gateway(network)
/opt/stack/nova/nova/network/l3.py(101)initialize_gateway()
def initialize_gateway(self,network_ref):
mac_address = utils.generate_mac_address()#生成mac地址
#network_ref['gateway']=10.1.0.1àgateway=True
dev = linux_net.plug(network_ref, mac_address,
gateway=(network_ref['gateway'] is not None))
linux_net.initialize_gateway_device(dev, network_ref)
/opt/stack/nova/nova/network/linux_net.py->1106
def plug(self, network, mac_address, gateway=True):
iface = FLAGS.flat_interface ornetwork['bridge_interface'] #iface=eth0
LinuxBridgeInterfaceDriver.ensure_bridge(network['bridge'],
iface, network, gateway)
iptables_manager.apply()
/opt/stack/nova/nova/network/linux_net.py->1168
def ensure_bridge(_self, bridge, interface, net_attrs=None,gateway=True):
# bridge=br100; interface=eth0, gateway=True
if not _device_exists(bridge):#br100若不存在,则创建
_execute('brctl', 'addbr', bridge, run_as_root=True)
_execute('brctl', 'setfd', bridge, 0, run_as_root=True)
_execute('brctl', 'stp', bridge, 'off', run_as_root=True)
_execute('ip', 'link', 'set', bridge, 'up', run_as_root=True)
if interface:
out, err = _execute('brctl', 'addif', bridge, interface, )#绑定网卡
old_routes = []
#显示eth0的route,并删除
out, err = _execute('ip', 'route', 'show', 'dev', interface)
for line in out.split('\n'):
fields = line.split()
if fields and 'via' in fields:
old_routes.append(fields)
_execute('ip', 'route', 'del', *fields, run_as_root=True)
#将eth0的ip删除,并复制给br100
out, err = _execute('ip', 'addr', 'show', 'dev', interface,)
for line in out.split('\n'):
fields = line.split()
if fields and fields[0] == 'inet':
params = fields[1:-1]
_execute(*_ip_bridge_cmd('del', params, fields[-1]),
_execute(*_ip_bridge_cmd('add', params, bridge),
for fields in old_routes:
_execute('ip', 'route', 'add', *fields, run_as_root=True)
#添加规则
ipv4_filter =iptables_manager.ipv4['filter']
if gateway:
ipv4_filter.add_rule('FORWARD',
'--in-interface %s -j ACCEPT' % bridge)
ipv4_filter.add_rule('FORWARD',
'--out-interface %s -j ACCEPT' % bridge)
#self.driver.update_dhcp(context, dev, network)重启dhcpserver
/usr/sbin/dnsmasq--strict-order --bind-interfaces
--conf-file=/opt/stack/data/dnsmasq.conf--domain=novalocal
--pid-file=/opt/stack/data/nova/networks/nova-br100.pid--listen-address=10.1.0.3 --except-interface=lo --dhcp-range=set:'private',10.1.0.2,static,120s--dhcp-lease-max=65536 --dhcp-hostsfile=/opt/stack/data/nova/networks/nova-br100.conf
--dhcp-script=/opt/stack/nova/bin/nova-dhcpbridge --leasefile-ro
- 获取分配给instance的networkinfo
self.get_instance_nw_info(context, instance_id, instance_uuid,rxtx_factor, host)
获得的network_info如下:
[VIF(
{'network':
Network(
{'bridge': u'br100',
'subnets': [
Subnet({‘ips’:[FixedIP({‘meta’: {},
‘version’: 4,
‘type’: u‘fixed’,
‘floating_ips': [],
'address':u'10.1.0.2'})],
'version': 4,
'meta': {u'dhcp_server':u'10.1.0.3'},
'dns': [IP({'meta': {},'version': 4,
'type': u'dns','address': u'8.8.4.4'})],
'routes': [],
'cidr': u'10.1.0.0/16',
gateway': IP({'meta': {}, 'version': 4,
'type': u'gateway',
'address': u'10.1.0.1'})}),
Subnet({'ips': [],
'version': None,
'meta': {u'dhcp_server': u'10.1.0.3'},
'dns': [],
'routes': [],
'cidr': None,
'gateway': IP({'meta': {}, 'version': None,
'type': u'gateway',
'address': None})})],
'meta': {u'tenant_id': None,
u'multi_host': True,
u'should_create_bridge': True,
u'bridge_interface': u'eth0'},
'id':u'e011b388-163b-4080-a083-bb63ebed7f6a',
'label': u'private'}),
'meta': {},
'id':u'99a3c937-1a0f-48f7-8015-166d7644e9a0',
'address': u'fa:16:3e:5a:f4:23'})]
1.4.3 nova network 总结
1. 宿主机配置br100(eth0)ip地址为192.168.96.80,
auto br100
iface br100 inet static
address 192.168.96.80
netmask 255.255.255.0
gateway 192.168.96.254
dns-nameservers114.114.114.114
bridge-ports eth0
2. 配置文件
FIXED_RANGE=10.1.0.0/16
FIXED_NETWORK_SIZE=65535
3. nova network 安装时,会执行以下命令,创建networks写入数据库。本例中,只有一个网络。
if [ !"$FIXED_RANGE" = "" ]; then
nova-manage network create private$FIXED_RANGE 1 $FIXED_NETWORK_SIZE
{ 'bridge': u'br100',
'vpn_public_port': None,
'dhcp_start': u'10.1.0.2',
'bridge_interface': u'eth0',
'updated_at': None,
'id': 1L,
'cidr_v6': None,
'deleted_at': None,
'gateway':u'10.1.0.1',
'rxtx_base': None,
'uuid':u'e011b388-163b-4080-a083-bb63ebed7f6a',
'label':u'private',
'priority': None,
'project_id': None,
'netmask_v6': None,
'_sa_instance_state': <sqlalchemy.orm.state.InstanceStateobject at 0x42c4790>,
'deleted': False,
'vlan': None,
'broadcast':u'10.1.255.255',
'netmask':u'255.255.0.0',
'injected': False,
'cidr':u'10.1.0.0/16',
'vpn_public_address': None,
'multi_host': True,
'dns2': None,
'dns1':u'8.8.4.4',
'host': None,
'gateway_v6': None,
'vpn_private_address': None,
'created_at': datetime.datetime(2014, 10, 30,4, 22, 44)}
4. 在创建第一个虚机时,会给宿主机br100分配一个在FIXED_RANGE之内的ip(10.1.0.3),该ip作为该宿主机上vm的dhcpserver来分配ip地址。同时也作为vm的default gateway,与外界通信。
5. 给虚机分配网络时,自动生成mac地址,从数据库选择一个没有用过的fixedip(10.1.0.2),将mac:ip写入配置文件/opt/stack/data/nova/networks/nova-br100.conf供dhcpserver使用。
6. 虚机向dhcp服务器发送请求时携带mac地址,dnsmasq收到请求后,将配置文件中对应mac地址的ip返回
1.5 libvirt driver
至此,新建的vm的所有数据信息(基本信息+网络)都已准备完毕。接下来就要调用libvirt的driver启动虚机了。调用的函数为spawn。
def spawn(self, context, instance, image_meta, injected_files,admin_password, network_info=None, block_device_info=None):
“””
代码位置:/opt/stack/nova/nova/virt/libvirt/driver.pyà1245
“””
1. 转换成libvirt所需的xml
xml = self.to_xml(instance, network_info, image_meta,block_device_info=block_device_info)
生成的xml为
<domain type="qemu">
<uuid>b110ed3b-9cc3-4d30-b433-a71090166b2c</uuid>
<name>instance-00000002</name>
<memory>524288</memory>
<vcpu>1</vcpu>
<os>
<type>hvm</type>
<kernel>/opt/stack/data/nova/instances/instance-00000002/kernel</kernel>
<initrd>/opt/stack/data/nova/instances/instance-00000002/ramdisk</initrd>
<cmdline>root=/dev/vdaconsole=ttyS0</cmdline>
</os>
<features>
<acpi/>
</features>
<clockoffset="utc"/>
<devices>
<disk type="file"device="cdrom">
<drivername="qemu"/>
<targetbus="ide" dev="hdb"/>
</disk>
<disk type="file"device="disk">
<drivername="qemu" type="qcow2" cache="writeback"/>
<source file="/opt/stack/data/nova/instances/instance-00000002/disk"/>
<targetbus="virtio" dev="hda"/>
</disk>
<interfacetype="bridge">
<macaddress="fa:16:3e:47:ef:94"/>
<modeltype="virtio"/>
<sourcebridge="br100"/>
<filterreffilter="nova-instance-instance-00000002-fa163e47ef94">
<parametername="IP" value="10.1.0.4"/>
<parametername="DHCPSERVER" value="10.1.0.3"/>
<parametername="PROJNET" value="10.1.0.0"/>
<parametername="PROJMASK" value="255.255.0.0"/>
</filterref>
</interface>
<serialtype="file">
<source path="/opt/stack/data/nova/instances/instance-00000002/console.log"/>
</serial>
<serialtype="pty"/>
<inputtype="tablet" bus="usb"/>
<graphicstype="vnc" autoport="yes" keymap="en-us"listen="0.0.0.0"/>
<video>
<modeltype="vga"/>
</video>
</devices>
</domain>
2. 准备镜像(将镜像拷贝到宿主机)
self._create_image(context, instance, xml, network_info=network_info, block_device_info=block_device_info,files=injected_files, admin_pass=admin_password)
详细请见1.6 节
3. 利用libvirt api创建domain,并将iptalbes规则应用到vm。
self._create_domain_and_network(xml, instance, network_info,block_device_info)
1) self.plug_vifs(instance,network_info)
2) self.firewall_driver.setup_basic_filtering(instance,network_info)
Basic_filter=[nova_base]
3) self.firewall_driver.prepare_instance_filter(instance,network_info)
² ipv4_rules
² ['-m state --state INVALID -jDROP', '-m state --state ESTABLISHED,RELATED -j ACCEPT', '-j $provider', u'-s10.1.0.3 -p udp --sport 67 --dport 68 -j ACCEPT', u'-s 10.1.0.0/16 -j ACCEPT',u'-j ACCEPT -p icmp -s 0.0.0.0/0', u'-j ACCEPT -p tcp --dport 80 -s 0.0.0.0/0',u'-j ACCEPT -p tcp --dport 22 -s 0.0.0.0/0', u'-j ACCEPT -p tcp --dport 3389 -s0.0.0.0/0', u'-j ACCEPT -p tcp --dport 50070 -s 0.0.0.0/0', u'-j ACCEPT -p tcp--dport 50030 -s 0.0.0.0/0', '-j $sg-fallback']
4) domain =self._create_domain(xml)
5) self.firewall_driver.apply_instance_filter(instance,network_info)
6) vm建成!!!!
1.6 Image
1.6.1 create_image
libvirtdriver把libvirt_xml创建好之后,我们发现对应的kernel,ramdisk和disk文件还没有。这时候就要开始创建镜像了。具体流程如下:
def _create_image(self, context, instance, libvirt_xml, suffix='',disk_images=None, network_info=None, block_device_info=None, files=None,admin_pass=None):
“””
代码位置:/opt/stack/nova/nova/virt/libvirt/driver.py 1428
”””
1. 定义basepath函数
def basepath(fname='', suffix=suffix):
returnos.path.join(FLAGS.instances_path,
instance['name'],
fname + suffix)
returnos.path.join(FLAGS.instances_path,
instance['name'],
fname + suffix)
FLAGS.instance_path=/opt/stack/data/nova/instances
2. 确保instance_path存在
utils.ensure_tree(basepath(suffix=''))
3. 将生成的libvirt_xml写入文件
libvirt_utils.write_to_file(basepath('libvirt.xml'),libvirt_xml)
/opt/stack/data/nova/instances/instance-0000000a/libvirt.xml
4. 修改console.log权限和写文件(若没有则创建)
/opt/stack/data/nova/instances/instance-0000000a/console.log
5. 获取ramdisk,kernel和disk文件(详见1.6.2节相应方法)
disk_images = {'image_id': instance['image_ref'],
'kernel_id': instance['kernel_id'],
'ramdisk_id': instance['ramdisk_id']}
- 获取kernel(调用imagebackend.py文件中Raw类相应的方法)
注:libvirt_utils.fetch_image是从glance下载镜像
raw('kernel').cache(fetch_func=libvirt_utils.fetch_image,
context=context,
filename=fname,
image_id=disk_images['kernel_id'],
user_id=instance['user_id'],
project_id=instance['project_id'])
fname =disk_images['kernel_id']->adaf51fe-c235-40b6-8aa7-80216d42303c
- 获取ramdisk(调用imagebackend.py文件中Raw类相应的方法)
raw('ramdisk').cache(fetch_func=libvirt_utils.fetch_image,
context=context,
filename=fname,
image_id=disk_images['ramdisk_id'],
user_id=instance['user_id'],
project_id=instance['project_id'])
fname = disk_images['ramdisk_id']->f9216b02-8e27-44da-b159-8d64401a16f2
- 获取disk(调用imagebackend.py文件中Qcow2类相应的方法)
image('disk').cache(fetch_func=libvirt_utils.fetch_image,
context=context,
filename=root_fname,
size=size,
image_id=disk_images['image_id'],
user_id=instance['user_id'],
project_id=instance['project_id'])
root_fname = hashlib.sha1(str(disk_images['image_id'])).hexdigest()
1.6.2 virt/libvirt/imagebackend
该文件定义了基类Image,Raw和Qcow2为子类。基类Image定义cache方法,主要功能是生成base路径(如:/opt/stack/data/nova/instances/_base/adaf51fe-c235…7-0216d42303c),调用子类create_image方法创建镜像文件(kernel,ramdisk,disk)。
注:子类的create_image方法第一个参数prepare_template被设置为基类Image中 cache的内部方法call_if_not_exists,该方法判断target文件是否存在,若不存在则调用libvirt_utils.fetch_image(详见1.6.3的fetch_image方法) 下载镜像文件。
class Image(object):
def cache(self, fetch_func, filename,size=None, *args, **kwargs):
“””
代码位置:/opt/stack/nova/nova/virt/libvirt/imagebackend.pyà105
self.path=/opt/stack/data/nova/instances/instance-0000000c/kernel
base=/opt/stack/data/nova/instances/_base/adaf51fe-c235-40b6-8aa7-0216d42303c
”””
@utils.synchronized(filename,external=True, lock_path=self.lock_path)
defcall_if_not_exists(target, *args, **kwargs):
“””
该方法作为子类中create_image方法的参数prepare_template
作用是若target文件不存在,则调用fetch_fuc获取文件
本例中fetch_fuc= libvirt_utils.fetch_image,即从glance下载镜像
”””
if notos.path.exists(target):
fetch_func(target=target,*args, **kwargs)
if not os.path.exists(self.path):
base_dir= os.path.join(FLAGS.instances_path, '_base')
if not os.path.exists(base_dir):
utils.ensure_tree(base_dir)
base = os.path.join(base_dir, filename)
#调用子类的create_image方法,prepare_template=call_if_not_exists
self.create_image(call_if_not_exists, base, size, *args, **kwargs)
class Raw(Image):
def __init__(self,instance, name):
super(Raw, self).__init__("file", "raw",is_block_dev=False)
self.path=os.path.join(FLAGS.instances_path, instance, name)
def create_image(self, prepare_template, base, size, *args, **kwargs):
“””
代码位置:/opt/stack/nova/nova/virt/libvirt/imagebackend.pyà139
prepare_template = call_if_not_exists
base=/opt/stack/data/nova/instances/_base/adaf51fe-c235-…6-8aa7-0216d42303c
self.path=/opt/stack/data/nova/instances/instance-0000000c/kernel
”””
@utils.synchronized(base,external=True, lock_path=self.lock_path)
def copy_raw_image(base,target, size):
“””
该方法利用cp,将base目录下的文件拷贝到target
”””
libvirt_utils.copy_image(base,target)à详见1.6.3节copy_image方法
if size:
disk.extend(target, size)
generating = 'image_id' not in kwargs
if generating:
#Generating image in place
prepare_template(target=self.path, *args, **kwargs)
else:
#准备base的kernel或ramdisk镜像,确保base目录下的文件存在
prepare_template(target=base,*args, **kwargs)# call_if_not_exists
with utils.remove_path_on_error(self.path):
# 将base文件拷贝到instance目录下
copy_raw_image(base,self.path, size)
class Qcow2(Image):
def __init__(self,instance, name):
super(Qcow2, self).__init__("file","qcow2", is_block_dev=False)
self.path=os.path.join(FLAGS.instances_path, instance, name)
def create_image(self, prepare_template, base, size, *args, **kwargs):
“””
代码位置:/opt/stack/nova/nova/virt/libvirt/imagebackend.pyà163
self.path=/opt/stack/data/nova/instances/instance-0000000c/disk
base=/opt/stack/data/nova/instances/_base/dd0ce6ec1af…eef7867f1063c9b5b028db
”””
@utils.synchronized(base, external=True,lock_path=self.lock_path)
def copy_qcow2_image(base, target, size):
“””
该方法利用qemu-img命令创建镜像,并设置backing_file
qemu-img create –f disk_format –o preallocation=metadata –obacking_file=base target
”””
qcow2_base = base
if size:
size_gb = size / (1024 * 1024 *1024)
qcow2_base += '_%d' % size_gb
if notos.path.exists(qcow2_base):
withutils.remove_path_on_error(qcow2_base):
libvirt_utils.copy_image(base, qcow2_base)
disk.extend(qcow2_base,size)
libvirt_utils.create_cow_image(qcow2_base,target)
->详见1.6.3节create_cow_image方法
# 准备 base 的 disk 镜像,确保 base 目录下的文件存在
prepare_template(target=base, *args, **kwargs)
withutils.remove_path_on_error(self.path):
# 利用qemu-img命令创建disk文件,backing_file为base文件
copy_qcow2_image(base,self.path, size)
1.6.3 virt/libvirt/utils.py
def fetch_image(context,target, image_id, user_id, project_id):
“””
代码位置:/opt/stack/nova/nova/virt/libvirt/utils.pyà397
”””
#该函数调用/opt/stack/nova/nova/virt/images.py中的fetch_to_raw方法(见1.6.4节)
images.fetch_to_raw(context, image_id, target, user_id, project_id)
….
def copy_image(src, dest,host=None):
“””
代码位置:/opt/stack/nova/nova/virt/libvirt/utils.pyà215
函数功能:利用cp或rsync命令拷贝镜像
”””
if not host:
execute('cp', src, dest)
else:
try:
execute('rsync', '--sparse', '--compress', '--dry-run', src,dest)
except exception.ProcessExecutionError:
execute('scp',src, dest)
else:
execute('rsync', '--sparse', '--compress', src, dest)
def create_cow_image(backing_file,path):
“””
代码位置:/opt/stack/nova/nova/virt/libvirt/utils.pyà84
函数功能:调用qemu-img命令创建镜像文件
”””
execute('qemu-img','create', '-f', 'qcow2', '-o preallocation=metadata', '-o',
'backing_file=%s'% backing_file, path)
1.6.4 virt/images.py
def fetch_to_raw(context,image_href, path, user_id, project_id):
“””
代码位置:/opt/stack/nova/nova/virt/images.pyà75
函数功能:调用fetch函数
”””
path_tmp = "%s.part" % path
fetch(context, image_href, path_tmp, user_id, project_id)
def fetch(context,image_href, path, _user_id, _project_id):
“””
代码位置:/opt/stack/nova/nova/virt/images.pyà63
函数功能:从glance下载镜像文件
”””
(image_service, image_id) =glance.get_remote_image_service(context, image_href)
with utils.remove_path_on_error(path):
with open(path, "wb") as image_file:
image_service.download(context, image_id, image_file)
nova 创建虚拟机流程相关推荐
- Nova创建虚拟机流程解读
一 介绍 创建一个虚拟机至少需要指定的参数有3个:虚拟机名字,镜像,Flavor.执行"nova image-list"命令可以看到目前可用的虚拟机镜像. 命令执行结果如下: [r ...
- PVE虚拟化平台之创建虚拟机流程
PVE虚拟化平台之创建虚拟机流程 一.PVE介绍今天,2022 年 11 月 17 日,有236篇文章可用. 二.登录PVE平台 三.登录PVE系统检查环境 1.进入PVE底层系统的shell命令终端 ...
- Nova 启动虚拟机流程解析
目录 文章目录 目录 前言 从请求说起 nova-api service 阶段 前言 Nova 启动虚拟机的东西太多,持续更新- 从请求说起 无论是通过 Dashboard 还是 CLI 启动一个虚拟 ...
- nova创建虚拟机源码分析系列之六 api入口create方法
openstack 版本:Newton 注:博文图片采用了很多大牛博客图片,仅作为总结学习,非商用. 该图全面的说明了nova创建虚机的过程,从逻辑的角度清晰的描述了前端请求创建虚拟机之后发生的一系列 ...
- nova创建instance流程
################################################################### # Start: Jeffrey 的话 ############ ...
- OpenStack---T版-nova组件部署流程
OpenStack---T版-nova组件部署流程 nova组件部署位置 计算节点Nova服务配置 nova组件部署位置 [控制节点ct] nova-api(nova主服务) nova-schedul ...
- 【JVM】Java对象创建的流程步骤
· 本文摘要 · 罗列Java创建对象的各种方式: · 讲解Java对象创建的流程步骤: 一.Java创建对象的各种方式 · 1. 用关键字new,老少皆知的方法:StringBuffer sb = ...
- 使用QEMU创建虚拟机
解决办法: 执行:yum upgrade device-mapper-libs yum -y install avahi /etc/init.d/messagebus restart /etc/ini ...
- VMWare 环境下devstack创建虚拟机报错及修改nova-api返回数据得条目
1.在生产环境中, 由于某个tenant下创建了有1300+条得security-group通过查询nova得数据库可以看出确实有1300+条得存在,但是通过curl调用的时候发现返回得数目只有100 ...
最新文章
- double,float,BigDecimal类型数值的操作
- 取两个数较小值c语言_编程代码:用C语言来实现下雪效果,这个冬天,雪花很美...
- python3精要(45)-exit
- TypeScript 3.7稳定版发布
- jdk中的设计模式_JDK中的设计模式
- webpack2 实践系列(二)— entry 和 output
- mahout推荐15-在hadoop上运行MapReduce
- ubuntu18.04播放mp4提示需要安装MPEG-4 AAC解码器和H.264解码器的解决办法
- matlab报错找不到icuuc54.dll解决办法:
- win10无法安装.net framework 3.5 解决方案/无法安装NetFx3解决方案
- windowsxp系统桌面卡住了解决
- 使用 GitHub Pages 和 Hexo 以及 Aurora 主题搭建静态个人博客
- 大疆2019校招提前批机器学习算法工程师在线笔试题目回忆版
- iOS模拟器运行报错Unable to install /xxx/build/ios/iphonesimulator/Runner.app
- 如何利用SUM函数合并单元格求和
- 以太网数据连接器行业现状调研及趋势分析报告
- 24点游戏 计算机编程,关于24点游戏的编程思路与基本算法
- javaweb项目JS文件报错解决办法
- 个人学习宋红康老师java入门记录的笔记,严禁商用.
- 自学python之路
热门文章
- pyinstaller打包技巧
- PDF怎么拆成一页一张?教你轻松实现拆分
- gpg秘钥的--keyserver
- 国产蓝牙耳机哪些牌子性价比高?不容错过的国产蓝牙耳机推荐
- Redis源码详解 - Replication(主备)流程
- PythonC++相互混合调用编程全面实战-05ctypes给c函数传递char字符串和wchar_t宽)
- E4A第三期-内网聊天软件
- “新智认知”杯上海高校程序设计竞赛暨第十七届上海大学程序设计春季联赛 B.CSL 的英语考试
- SpringBoot integrates the Karate Test framework and run.
- 如何用3dmax建成人的模型