nova 创建虚拟机流程

来源:互联网 发布:梅雨知时节 作家 编辑:程序博客网 时间:2024/05/21 12:45

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.5libvirt 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

注:macip写配置文件/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_serverip

          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 = []

                     #显示eth0route,并删除

           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)

            #eth0ip删除,并复制给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作为该宿主机上vmdhcpserver来分配ip地址。同时也作为vmdefault gateway,与外界通信。

5.  给虚机分配网络时,自动生成mac地址,从数据库选择一个没有用过的fixedip(10.1.0.2),将mac:ip写入配置文件/opt/stack/data/nova/networks/nova-br100.confdhcpserver使用。

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.3fetch_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.3copy_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:

#准备basekernelramdisk镜像,确保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.3create_cow_image方法

#准备basedisk镜像,确保base目录下的文件存在

prepare_template(target=base, *args, **kwargs)

withutils.remove_path_on_error(self.path):

# 利用qemu-img命令创建disk文件,backing_filebase文件

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

函数功能:利用cprsync命令拷贝镜像

”””

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)

0 0
原创粉丝点击