From 6930c62a02a39f64506a7b2d2ec5b04dbff5fe3a Mon Sep 17 00:00:00 2001 From: sateesh Date: Mon, 5 Sep 2011 12:51:07 +0530 Subject: Multi-NIC support for vmwareapi virt driver in nova. Does injection of Multi-NIC information to instances with Operating system flavors Ubuntu, Windows and RHEL. vmwareapi virt driver now relies on calls to network manager instead of nova db calls for network configuration information of instance. Ensure if port group is properly associated with vlan_interface specified in case of VLAN networking for instances. Re-oranized VMWareVlanBridgeDriver and added session parmeter to methods to use existing session. Also removed session creation code as session comes as argument. Added check for flat_inject flag before attempting an inject operation. Removed stale code from vmwareapi stubs. Also updated some comments to be more meaningful. Did pep8 and pylint checks. Tried to improve pylint score for newly added lines of code. --- nova/tests/vmwareapi/stubs.py | 2 - nova/virt/vmwareapi/fake.py | 2 +- nova/virt/vmwareapi/vif.py | 27 +++++------ nova/virt/vmwareapi/vm_util.py | 26 +++++----- nova/virt/vmwareapi/vmops.py | 106 ++++++++++++++++++++++++----------------- tools/esx/guest_tool.py | 70 +++++++++++++++------------ 6 files changed, 124 insertions(+), 109 deletions(-) diff --git a/nova/tests/vmwareapi/stubs.py b/nova/tests/vmwareapi/stubs.py index 0ed5e9b68..7de10e612 100644 --- a/nova/tests/vmwareapi/stubs.py +++ b/nova/tests/vmwareapi/stubs.py @@ -45,8 +45,6 @@ def set_stubs(stubs): stubs.Set(vmware_images, 'get_vmdk_size_and_properties', fake.fake_get_vmdk_size_and_properties) stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image) - stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object", - fake_get_vim_object) stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object", fake_get_vim_object) stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object", diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index 4c62d18bb..f47ffdf1c 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -409,7 +409,7 @@ def fake_plug_vifs(*args, **kwargs): def fake_get_network(*args, **kwargs): """Fake get network.""" - return [{'type': 'fake'}] + return {'type': 'fake'} def fake_fetch_image(image, instance, **kwargs): diff --git a/nova/virt/vmwareapi/vif.py b/nova/virt/vmwareapi/vif.py index fb6548b34..9906b89e1 100644 --- a/nova/virt/vmwareapi/vif.py +++ b/nova/virt/vmwareapi/vif.py @@ -17,42 +17,35 @@ """VIF drivers for VMWare.""" -from nova import db from nova import exception from nova import flags from nova import log as logging -from nova import utils from nova.virt.vif import VIFDriver -from nova.virt.vmwareapi_conn import VMWareAPISession from nova.virt.vmwareapi import network_utils LOG = logging.getLogger("nova.virt.vmwareapi.vif") FLAGS = flags.FLAGS +FLAGS['vmwareapi_vlan_interface'].SetDefault('vmnic0') class VMWareVlanBridgeDriver(VIFDriver): """VIF Driver to setup bridge/VLAN networking using VMWare API.""" def plug(self, instance, network, mapping): + """Plug the VIF to specified instance using information passed. + Currently we are plugging the VIF(s) during instance creation itself. + We can use this method when we add support to add additional NIC to + an existing instance.""" + pass + + def ensure_vlan_bridge(self, session, network): """Create a vlan and bridge unless they already exist.""" vlan_num = network['vlan'] bridge = network['bridge'] - bridge_interface = network['bridge_interface'] + vlan_interface = FLAGS.vmwareapi_vlan_interface - # Open vmwareapi session - host_ip = FLAGS.vmwareapi_host_ip - host_username = FLAGS.vmwareapi_host_username - host_password = FLAGS.vmwareapi_host_password - if not host_ip or host_username is None or host_password is None: - raise Exception(_('Must specify vmwareapi_host_ip, ' - 'vmwareapi_host_username ' - 'and vmwareapi_host_password to use ' - 'connection_type=vmwareapi')) - session = VMWareAPISession(host_ip, host_username, host_password, - FLAGS.vmwareapi_api_retry_count) - vlan_interface = bridge_interface # Check if the vlan_interface physical network adapter exists on the # host. if not network_utils.check_if_vlan_interface_exists(session, @@ -92,4 +85,6 @@ class VMWareVlanBridgeDriver(VIFDriver): pgroup=pg_vlanid) def unplug(self, instance, network, mapping): + """Cleanup operations like deleting port group if no instance + is associated with it.""" pass diff --git a/nova/virt/vmwareapi/vm_util.py b/nova/virt/vmwareapi/vm_util.py index 82b5f7214..dd1c81196 100644 --- a/nova/virt/vmwareapi/vm_util.py +++ b/nova/virt/vmwareapi/vm_util.py @@ -39,8 +39,7 @@ def split_datastore_path(datastore_path): def get_vm_create_spec(client_factory, instance, data_store_name, - network_name="vmnet0", - os_type="otherGuest", network_ref=None): + vif_infos, os_type="otherGuest"): """Builds the VM Create spec.""" config_spec = client_factory.create('ns0:VirtualMachineConfigSpec') config_spec.name = instance.name @@ -61,14 +60,12 @@ def get_vm_create_spec(client_factory, instance, data_store_name, config_spec.numCPUs = int(instance.vcpus) config_spec.memoryMB = int(instance.memory_mb) - mac_address = None - if instance['mac_addresses']: - mac_address = instance['mac_addresses'][0]['address'] + vif_spec_list = [] + for vif_info in vif_infos: + vif_spec = create_network_spec(client_factory, vif_info) + vif_spec_list.append(vif_spec) - nic_spec = create_network_spec(client_factory, - network_name, mac_address) - - device_config_spec = [nic_spec] + device_config_spec = vif_spec_list config_spec.deviceChange = device_config_spec return config_spec @@ -93,8 +90,7 @@ def create_controller_spec(client_factory, key): return virtual_device_config -def create_network_spec(client_factory, network_name, mac_address, - network_ref=None): +def create_network_spec(client_factory, vif_info): """ Builds a config spec for the addition of a new network adapter to the VM. @@ -109,6 +105,9 @@ def create_network_spec(client_factory, network_name, mac_address, # NOTE(asomya): Only works on ESXi if the portgroup binding is set to # ephemeral. Invalid configuration if set to static and the NIC does # not come up on boot if set to dynamic. + network_ref = vif_info['network_ref'] + network_name = vif_info['network_name'] + mac_address = vif_info['mac_address'] backing = None if (network_ref and network_ref['type'] == "DistributedVirtualPortgroup"): @@ -295,11 +294,8 @@ def get_dummy_vm_create_spec(client_factory, name, data_store_name): return config_spec -def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask, - gateway, broadcast, dns): +def get_machine_id_change_spec(client_factory, machine_id_str): """Builds the machine id change config spec.""" - machine_id_str = "%s;%s;%s;%s;%s;%s" % (mac, ip_addr, netmask, - gateway, broadcast, dns) virtual_machine_config_spec = \ client_factory.create('ns0:VirtualMachineConfigSpec') diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 07a6ba6ab..3ac99f923 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -27,7 +27,6 @@ import urllib2 import uuid from nova import context as nova_context -from nova import db from nova import exception from nova import flags from nova import log as logging @@ -111,22 +110,6 @@ class VMWareVMOps(object): client_factory = self._session._get_vim().client.factory service_content = self._session._get_vim().get_service_content() - network = db.network_get_by_instance(nova_context.get_admin_context(), - instance['id']) - - net_name = network['bridge'] - - def _check_if_network_bridge_exists(): - network_ref = \ - network_utils.get_network_with_the_name(self._session, - net_name) - if network_ref is None: - raise exception.NetworkNotFoundForBridge(bridge=net_name) - return network_ref - - self.plug_vifs(instance, network_info) - network_obj = _check_if_network_bridge_exists() - def _get_datastore_ref(): """Get the datastore list and choose the first local storage.""" data_stores = self._session._call_method(vim_util, "get_objects", @@ -182,11 +165,36 @@ class VMWareVMOps(object): vm_folder_mor, res_pool_mor = _get_vmfolder_and_res_pool_mors() + def _check_if_network_bridge_exists(network_name): + network_ref = \ + network_utils.get_network_with_the_name(self._session, + network_name) + if network_ref is None: + raise exception.NetworkNotFoundForBridge(bridge=network_name) + return network_ref + + def _get_vif_infos(): + vif_infos = [] + for (network, mapping) in network_info: + mac_address = mapping['mac'] + network_name = network['bridge'] + if mapping.get('should_create_vlan'): + network_ref = self._vif_driver.ensure_vlan_bridge( + self._session, network) + else: + network_ref = _check_if_network_bridge_exists(network_name) + vif_infos.append({'network_name': network_name, + 'mac_address': mac_address, + 'network_ref': network_ref, + }) + return vif_infos + + vif_infos = _get_vif_infos() + # Get the create vm config spec config_spec = vm_util.get_vm_create_spec( client_factory, instance, - data_store_name, net_name, os_type, - network_obj) + data_store_name, vif_infos, os_type) def _execute_create_vm(): """Create VM on ESX host.""" @@ -204,8 +212,10 @@ class VMWareVMOps(object): _execute_create_vm() - # Set the machine id for the VM for setting the IP - self._set_machine_id(client_factory, instance) + # Set the machine.id parameter of the instance to inject + # the NIC configuration inside the VM + if FLAGS.flat_injected: + self._set_machine_id(client_factory, instance, network_info) # Naming the VM files in correspondence with the VM instance name # The flat vmdk file name @@ -716,39 +726,45 @@ class VMWareVMOps(object): """Return link to instance's ajax console.""" return 'http://fakeajaxconsole/fake_url' - def _set_machine_id(self, client_factory, instance): + def _set_machine_id(self, client_factory, instance, network_info): """ - Set the machine id of the VM for guest tools to pick up and change - the IP. + Set the machine id of the VM for guest tools to pick up and reconfigure + the network interfaces. """ - admin_context = nova_context.get_admin_context() vm_ref = self._get_vm_ref_from_the_name(instance.name) if vm_ref is None: raise exception.InstanceNotFound(instance_id=instance.id) - network = db.network_get_by_instance(nova_context.get_admin_context(), - instance['id']) - mac_address = None - if instance['mac_addresses']: - mac_address = instance['mac_addresses'][0]['address'] - - net_mask = network["netmask"] - gateway = network["gateway"] - broadcast = network["broadcast"] - # TODO(vish): add support for dns2 - dns = network["dns1"] - - addresses = db.instance_get_fixed_addresses(admin_context, - instance['id']) - ip_addr = addresses[0] if addresses else None + + machine_id_str = '' + for (network, info) in network_info: + # TODO(vish): add support for dns2 + # TODO(sateesh): add support for injection of ipv6 configuration + ip_v4 = ip_v6 = None + if 'ips' in info and len(info['ips']) > 0: + ip_v4 = info['ips'][0] + if 'ip6s' in info and len(info['ip6s']) > 0: + ip_v6 = info['ip6s'][0] + if len(info['dns']) > 0: + dns = info['dns'][0] + else: + dns = '' + + interface_str = "%s;%s;%s;%s;%s;%s" % \ + (info['mac'], + ip_v4 and ip_v4['ip'] or '', + ip_v4 and ip_v4['netmask'] or '', + info['gateway'], + info['broadcast'], + dns) + machine_id_str = machine_id_str + interface_str + '#' machine_id_change_spec = \ - vm_util.get_machine_id_change_spec(client_factory, mac_address, - ip_addr, net_mask, gateway, - broadcast, dns) + vm_util.get_machine_id_change_spec(client_factory, machine_id_str) + LOG.debug(_("Reconfiguring VM instance %(name)s to set the machine id " "with ip - %(ip_addr)s") % ({'name': instance.name, - 'ip_addr': ip_addr})) + 'ip_addr': ip_v4['ip']})) reconfig_task = self._session._call_method(self._session._get_vim(), "ReconfigVM_Task", vm_ref, spec=machine_id_change_spec) @@ -756,7 +772,7 @@ class VMWareVMOps(object): LOG.debug(_("Reconfigured VM instance %(name)s to set the machine id " "with ip - %(ip_addr)s") % ({'name': instance.name, - 'ip_addr': ip_addr})) + 'ip_addr': ip_v4['ip']})) def _get_datacenter_name_and_ref(self): """Get the datacenter name and the reference.""" diff --git a/tools/esx/guest_tool.py b/tools/esx/guest_tool.py index 97b5302ba..5158d883a 100644 --- a/tools/esx/guest_tool.py +++ b/tools/esx/guest_tool.py @@ -81,28 +81,34 @@ def _bytes2int(bytes): def _parse_network_details(machine_id): """ - Parse the machine.id field to get MAC, IP, Netmask and Gateway fields - machine.id is of the form MAC;IP;Netmask;Gateway;Broadcast;DNS1,DNS2 - where ';' is the separator. + Parse the machine_id to get MAC, IP, Netmask and Gateway fields per NIC. + machine_id is of the form ('NIC_record#NIC_record#', '') + Each of the NIC will have record NIC_record in the form + 'MAC;IP;Netmask;Gateway;Broadcast;DNS' where ';' is field separator. + Each record is separated by '#' from next record. """ + logging.debug(_("Received machine_id from vmtools : %s") % machine_id[0]) network_details = [] if machine_id[1].strip() == "1": pass else: - network_info_list = machine_id[0].split(';') - assert len(network_info_list) % 6 == 0 - no_grps = len(network_info_list) / 6 - i = 0 - while i < no_grps: - k = i * 6 - network_details.append(( - network_info_list[k].strip().lower(), - network_info_list[k + 1].strip(), - network_info_list[k + 2].strip(), - network_info_list[k + 3].strip(), - network_info_list[k + 4].strip(), - network_info_list[k + 5].strip().split(','))) - i += 1 + for machine_id_str in machine_id[0].split('#'): + network_info_list = machine_id_str.split(';') + if len(network_info_list) % 6 != 0: + break + no_grps = len(network_info_list) / 6 + i = 0 + while i < no_grps: + k = i * 6 + network_details.append(( + network_info_list[k].strip().lower(), + network_info_list[k + 1].strip(), + network_info_list[k + 2].strip(), + network_info_list[k + 3].strip(), + network_info_list[k + 4].strip(), + network_info_list[k + 5].strip().split(','))) + i += 1 + logging.debug(_("NIC information from vmtools : %s") % network_details) return network_details @@ -279,6 +285,7 @@ def _filter_duplicates(all_entries): def _set_rhel_networking(network_details=None): + """Set IPv4 network settings for RHEL distros.""" network_details = network_details or [] all_dns_servers = [] for network_detail in network_details: @@ -320,31 +327,33 @@ def _set_rhel_networking(network_details=None): def _set_ubuntu_networking(network_details=None): + """Set IPv4 network settings for Ubuntu.""" network_details = network_details or [] - """ Set IPv4 network settings for Ubuntu """ all_dns_servers = [] - for network_detail in network_details: + interface_file_name = '/etc/network/interfaces' + # Remove file + os.remove(interface_file_name) + # Touch file + _execute(['touch', interface_file_name]) + interface_file = open(interface_file_name, 'w') + for device, network_detail in enumerate(network_details): mac_address, ip_address, subnet_mask, gateway, broadcast,\ dns_servers = network_detail all_dns_servers.extend(dns_servers) adapter_name, current_ip_address = \ _get_linux_adapter_name_and_ip_address(mac_address) - if adapter_name and not ip_address == current_ip_address: - interface_file_name = \ - '/etc/network/interfaces' - # Remove file - os.remove(interface_file_name) - # Touch file - _execute(['touch', interface_file_name]) - interface_file = open(interface_file_name, 'w') + if adapter_name: interface_file.write('\nauto %s' % adapter_name) interface_file.write('\niface %s inet static' % adapter_name) interface_file.write('\nbroadcast %s' % broadcast) interface_file.write('\ngateway %s' % gateway) interface_file.write('\nnetmask %s' % subnet_mask) - interface_file.write('\naddress %s' % ip_address) - interface_file.close() + interface_file.write('\naddress %s\n' % ip_address) + logging.debug(_("Successfully configured NIC %d with " + "NIC info %s") % (device, network_detail)) + interface_file.close() + if all_dns_servers: dns_file_name = "/etc/resolv.conf" os.remove(dns_file_name) @@ -355,7 +364,8 @@ def _set_ubuntu_networking(network_details=None): for dns_server in unique_entries: dns_file.write("\nnameserver %s" % dns_server) dns_file.close() - print "\nRestarting networking....\n" + + logging.debug(_("Restarting networking....\n")) _execute(['/etc/init.d/networking', 'restart']) -- cgit From 248787462473195ab35591946ed6e3f0e2a818b0 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Sat, 10 Sep 2011 17:08:43 +0900 Subject: virt/libvirt: format ephemeral device and add fs label when formating ext3 fs his patch fixes the but reported by ttps://bugs.launchpad.net/bugs/827590 ttps://bugs.launchpad.net/nova/+bug/828357 The ephemeral device is formated as ext3 on Amazon ec2. The new options, vir_mkfs, is introduced. virt_mkfs use the format of = --- nova/virt/disk.py | 41 +++++++++++++++++++++++++++++++++++++++++ nova/virt/libvirt/connection.py | 13 +++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 52b2881e8..2c994626c 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -52,6 +52,47 @@ flags.DEFINE_integer('timeout_nbd', 10, flags.DEFINE_integer('max_nbd_devices', 16, 'maximum number of possible nbd devices') +# NOTE(yamahata): DEFINE_list() doesn't work because the command may +# include ','. For example, +# mkfs.ext3 -O dir_index,extent -E stride=8,stripe-width=16 +# --label %(fs_label)s %(target)s +# +# DEFINE_list() parses its argument by +# [s.strip() for s in argument.split(self._token)] +# where self._token = ',' +# No escape nor exceptional handling for ','. +# DEFINE_list() doesn't give us what we need. +flags.DEFINE_multistring('virt_mkfs', + ['windows=mkfs.ntfs --fast --label %(fs_label)s ' + '%(target)s', + # NOTE(yamahata): vfat case + #'windows=mkfs.vfat -n %(fs_label)s %(target)s', + 'linux=mkfs.ext3 -L %(fs_label)s -F %(target)s', + 'default=mkfs.ext3 -L %(fs_label)s -F %(target)s'], + 'mkfs commands for ephemeral device. The format is' + '=') + + +_MKFS_COMMAND = {} +_DEFAULT_MKFS_COMMAND = None + + +for s in FLAGS.virt_mkfs: + # NOTE(yamahata): mkfs command may includes '=' for its options. + # So item.partition('=') doesn't work here + os_type, mkfs_command = s.split('=', 1) + if os_type: + _MKFS_COMMAND[os_type] = mkfs_command + if os_type == 'default': + _DEFAULT_MKFS_COMMAND = mkfs_command + + +def mkfs(os_type, fs_label, target): + mkfs_command = (_MKFS_COMMAND.get(os_type, _DEFAULT_MKFS_COMMAND) or + '') % locals() + if mkfs_command: + utils.execute(*mkfs_command.split()) + def extend(image, size): """Increase image to size""" diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 19cef5ad7..6b740d995 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -38,6 +38,7 @@ Supports KVM, LXC, QEMU, UML, and XEN. """ import hashlib +import functools import multiprocessing import netaddr import os @@ -778,6 +779,10 @@ class LibvirtConnection(driver.ComputeDriver): if fs_format: utils.execute('mkfs', '-t', fs_format, target) + def _create_ephemeral(self, target, local_size, fs_label, os_type): + self._create_local(target, local_size) + disk.mkfs(os_type, fs_label, target) + def _create_swap(self, target, swap_gb): """Create a swap file of specified size""" self._create_local(target, swap_gb) @@ -866,9 +871,13 @@ class LibvirtConnection(driver.ComputeDriver): local_size=local_gb) for eph in driver.block_device_info_get_ephemerals(block_device_info): - self._cache_image(fn=self._create_local, + fn = functools.partial(self._create_ephemeral, + fs_label='ephemeral%d' % eph['num'], + os_type=inst.os_type) + self._cache_image(fn=fn, target=basepath(_get_eph_disk(eph)), - fname="local_%s" % eph['size'], + fname="ephemeral_%s_%s_%s" % + (eph['num'], eph['size'], inst.os_type), cow=FLAGS.use_cow_images, local_size=eph['size']) -- cgit From c890890c7ccbc7df1060d59747089b5e39c5510a Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Sat, 10 Sep 2011 17:11:21 +0900 Subject: api/ec2: make get_metadata() return correct mappings The entries corresponding to volumes are in the form of ebs': --- nova/api/ec2/cloud.py | 20 ++++++++++++++++++-- nova/tests/test_cloud.py | 4 +++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4f7030a5a..50c551f86 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -272,11 +272,17 @@ class CloudController(object): mappings = {} mappings['ami'] = block_device.strip_dev(root_device_name) mappings['root'] = root_device_name + ebs_devices = [] - # 'ephemeralN' and 'swap' + # 'ephemeralN', 'swap' and ebs for bdm in db.block_device_mapping_get_all_by_instance( ctxt, instance_ref['id']): - if (bdm['volume_id'] or bdm['snapshot_id'] or bdm['no_device']): + if bdm['no_device']: + continue + + # ebs volume case + if (bdm['volume_id'] or bdm['snapshot_id']): + ebs_devices.append(bdm['device_name']) continue virtual_name = bdm['virtual_name'] @@ -286,6 +292,16 @@ class CloudController(object): if block_device.is_swap_or_ephemeral(virtual_name): mappings[virtual_name] = bdm['device_name'] + # NOTE(yamahata): I'm not sure how ebs device should be numbered. + # Right now sort by device name for deterministic + # result. + if ebs_devices: + nebs = 0 + ebs_devices.sort() + for ebs in ebs_devices: + mappings['ebs%d' % nebs] = ebs + nebs += 1 + return mappings def get_metadata(self, address): diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 7fe353b3d..7bdae0552 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -1540,7 +1540,9 @@ class CloudTestCase(test.TestCase): 'ephemeral0': '/dev/sdb', 'swap': '/dev/sdc', 'ephemeral1': '/dev/sdd', - 'ephemeral2': '/dev/sd3'} + 'ephemeral2': '/dev/sd3', + 'ebs0': '/dev/sdh', + 'ebs1': '/dev/sdi'} self.assertEqual(self.cloud._format_instance_mapping(ctxt, instance_ref0), -- cgit From d8abe79da8dde2667936ee97d88d30d5cf0e6d7f Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Sat, 10 Sep 2011 17:11:31 +0900 Subject: api/ec2/ebs: make metadata returns correct swap and ephemeral0 --- nova/api/ec2/cloud.py | 6 +++ .../migrate_repo/versions/046_add_instance_swap.py | 48 ++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 2 + nova/virt/libvirt/connection.py | 8 ++++ 4 files changed, 64 insertions(+) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/046_add_instance_swap.py diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 50c551f86..0ad2d94f3 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -272,6 +272,12 @@ class CloudController(object): mappings = {} mappings['ami'] = block_device.strip_dev(root_device_name) mappings['root'] = root_device_name + default_local_device = instance_ref.get('default_local_device') + if default_local_device: + mappings['ephemeral0'] = default_local_device + default_swap_device = instance_ref.get('default_swap_device') + if default_swap_device: + mappings['swap'] = default_swap_device ebs_devices = [] # 'ephemeralN', 'swap' and ebs diff --git a/nova/db/sqlalchemy/migrate_repo/versions/046_add_instance_swap.py b/nova/db/sqlalchemy/migrate_repo/versions/046_add_instance_swap.py new file mode 100644 index 000000000..63e7bc4f9 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/046_add_instance_swap.py @@ -0,0 +1,48 @@ +# Copyright 2011 Isaku Yamahata +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import Column, Integer, MetaData, Table, String + +meta = MetaData() + +default_local_device = Column( + 'default_local_device', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + nullable=True) + +default_swap_device = Column( + 'default_swap_device', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + nullable=True) + +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + instances.create_column(default_local_device) + instances.create_column(default_swap_device) + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + meta.bind = migrate_engine + instances.drop_column('default_swap_device') + instances.drop_column('default_local_device') diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 211049112..b5f30a1e3 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -232,6 +232,8 @@ class Instance(BASE, NovaBase): uuid = Column(String(36)) root_device_name = Column(String(255)) + default_local_device = Column(String(255), nullable=True) + default_swap_device = Column(String(255), nullable=True) config_drive = Column(String(255)) # User editable field meant to represent what ip should be used diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 6b740d995..2a6f75d35 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -1111,6 +1111,11 @@ class LibvirtConnection(driver.ComputeDriver): nova_context.get_admin_context(), instance['id'], {'root_device_name': '/dev/' + self.default_root_device}) + if local_device: + db.instance_update( + nova_context.get_admin_context(), instance['id'], + {'default_local_device': '/dev/' + self.default_local_device}) + swap = driver.block_device_info_get_swap(block_device_info) if driver.swap_is_usable(swap): xml_info['swap_device'] = block_device.strip_dev( @@ -1119,6 +1124,9 @@ class LibvirtConnection(driver.ComputeDriver): not self._volume_in_mapping(self.default_swap_device, block_device_info)): xml_info['swap_device'] = self.default_swap_device + db.instance_update( + nova_context.get_admin_context(), instance['id'], + {'default_swap_device': '/dev/' + self.default_swap_device}) config_drive = False if instance.get('config_drive') or instance.get('config_drive_id'): -- cgit From 7ac94ccf7dbb5838ef877b9d954ea96bf1412b4b Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Mon, 12 Sep 2011 00:16:49 -0700 Subject: fix for lp847604 to unbreak instance rebooting --- nova/compute/api.py | 2 +- nova/virt/libvirt/connection.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index b0ea044c5..48bb266b7 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1049,7 +1049,7 @@ class API(base.Base): vm_state=vm_states.ACTIVE, task_state=task_states.REBOOTING) self._cast_compute_message('reboot_instance', context, instance_id, - reboot_type) + params={'reboot_type': reboot_type}) @scheduler_api.reroute_compute("rebuild") def rebuild(self, context, instance_id, image_href, admin_password, diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 19cef5ad7..fb2aed651 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -466,7 +466,7 @@ class LibvirtConnection(driver.ComputeDriver): shutil.rmtree(temp_dir) @exception.wrap_exception() - def reboot(self, instance, network_info): + def reboot(self, instance, network_info, reboot_type): """Reboot a virtual machine, given an instance reference. This method actually destroys and re-creates the domain to ensure the -- cgit From f40955d419c886be29213f73f5ffdf2f38e00057 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Mon, 12 Sep 2011 08:00:30 -0700 Subject: add test for method sig --- nova/tests/test_libvirt.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 93967ceec..d776a386b 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -16,6 +16,7 @@ import copy import eventlet +import inspect import mox import os import re @@ -35,6 +36,7 @@ from nova import utils from nova.api.ec2 import cloud from nova.compute import power_state from nova.compute import vm_states +from nova.virt import driver from nova.virt.libvirt import connection from nova.virt.libvirt import firewall from nova.tests import fake_network @@ -840,6 +842,50 @@ class LibvirtConnTestCase(test.TestCase): _assert_volume_in_mapping('sdg', False) _assert_volume_in_mapping('sdh1', False) + def test_reboot_signature(self): + """Test that libvirt driver method sig matches interface""" + def fake_reboot_with_correct_sig(ignore, instance, + network_info, reboot_type): + pass + + def fake_destroy(instance, network_info, cleanup=False): + pass + + def fake_plug_vifs(instance, network_info): + pass + + def fake_create_new_domain(xml): + return + + def fake_none(self, instance): + return + + instance = db.instance_create(self.context, self.test_instance) + network_info = _fake_network_info(self.stubs, 1) + + self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn') + connection.LibvirtConnection._conn.lookupByName = self.fake_lookup + + conn = connection.LibvirtConnection(False) + self.stubs.Set(conn, 'destroy', fake_destroy) + self.stubs.Set(conn, 'plug_vifs', fake_plug_vifs) + self.stubs.Set(conn.firewall_driver, + 'setup_basic_filtering', + fake_none) + self.stubs.Set(conn.firewall_driver, + 'prepare_instance_filter', + fake_none) + self.stubs.Set(conn, '_create_new_domain', fake_create_new_domain) + self.stubs.Set(conn.firewall_driver, + 'apply_instance_filter', + fake_none) + + args = [instance, network_info, 'SOFT'] + conn.reboot(*args) + + compute_driver = driver.ComputeDriver() + self.assertRaises(NotImplementedError, compute_driver.reboot, *args) + class NWFilterFakes: def __init__(self): -- cgit From 4cbfc60b225d0386b6719e49fc9797fd72dc219b Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Mon, 12 Sep 2011 09:15:31 -0700 Subject: remove unused dep --- nova/tests/test_libvirt.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index d776a386b..5346e089b 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -16,7 +16,6 @@ import copy import eventlet -import inspect import mox import os import re -- cgit From b05121e1e1b2a8276d1dd21583c379dc4755b3dd Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 13 Sep 2011 15:48:10 -0400 Subject: Update test_libvirt so that flags and fakes are used instead of mocks for utils.import_class and utils.import_object. Fixes #lp849329. --- nova/tests/fake_network.py | 30 ++++++++++++++++ nova/tests/test_libvirt.py | 87 +++++++++++++++++----------------------------- 2 files changed, 62 insertions(+), 55 deletions(-) diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 1ecb99b31..142206755 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -25,6 +25,36 @@ HOST = "testhost" FLAGS = flags.FLAGS +class FakeIptablesFirewallDriver(object): + def __init__(self, **kwargs): + pass + + def setattr(self, key, val): + self.__setattr__(key, val) + + def apply_instance_filter(self, instance, network_info): + pass + + +class FakeVIFDriver(object): + + def __init__(self, **kwargs): + pass + + def setattr(self, key, val): + self.__setattr__(key, val) + + def plug(self, instance, network, mapping): + return { + 'id': 'fake', + 'bridge_name': 'fake', + 'mac_address': 'fake', + 'ip_address': 'fake', + 'dhcp_server': 'fake', + 'extra_params': 'fake', + } + + class FakeModel(dict): """Represent a model from the db""" def __init__(self, *args, **kwargs): diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 233ee14de..8193d6ec2 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -51,6 +51,32 @@ def _concurrency(wait, done, target): done.send() +class FakeVirtDomain(object): + + def __init__(self, fake_xml=None): + if fake_xml: + self._fake_dom_xml = fake_xml + else: + self._fake_dom_xml=""" + + + + + + + + """ + + def snapshotCreateXML(self, *args): + return None + + def createWithFlags(self, launch_flags): + pass + + def XMLDesc(self, *args): + return self._fake_dom_xml + + class CacheConcurrencyTestCase(test.TestCase): def setUp(self): super(CacheConcurrencyTestCase, self).setUp() @@ -152,70 +178,23 @@ class LibvirtConnTestCase(test.TestCase): # A fake libvirt.virConnect class FakeLibvirtConnection(object): - pass - - # A fake connection.IptablesFirewallDriver - class FakeIptablesFirewallDriver(object): - - def __init__(self, **kwargs): - pass - - def setattr(self, key, val): - self.__setattr__(key, val) - - # A fake VIF driver - class FakeVIFDriver(object): - - def __init__(self, **kwargs): - pass - - def setattr(self, key, val): - self.__setattr__(key, val) - - def plug(self, instance, network, mapping): - return { - 'id': 'fake', - 'bridge_name': 'fake', - 'mac_address': 'fake', - 'ip_address': 'fake', - 'dhcp_server': 'fake', - 'extra_params': 'fake', - } + def defineXML(self, xml): + return FakeVirtDomain() # Creating mocks fake = FakeLibvirtConnection() - fakeip = FakeIptablesFirewallDriver - fakevif = FakeVIFDriver() # Customizing above fake if necessary for key, val in kwargs.items(): fake.__setattr__(key, val) - # Inevitable mocks for connection.LibvirtConnection - self.mox.StubOutWithMock(connection.utils, 'import_class') - connection.utils.import_class(mox.IgnoreArg()).AndReturn(fakeip) - self.mox.StubOutWithMock(connection.utils, 'import_object') - connection.utils.import_object(mox.IgnoreArg()).AndReturn(fakevif) + self.flags(image_service='nova.image.fake.FakeImageService') + self.flags(firewall_driver="nova.tests.fake_network.FakeIptablesFirewallDriver") + self.flags(libvirt_vif_driver="nova.tests.fake_network.FakeVIFDriver") + self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn') connection.LibvirtConnection._conn = fake def fake_lookup(self, instance_name): - - class FakeVirtDomain(object): - - def snapshotCreateXML(self, *args): - return None - - def XMLDesc(self, *args): - return """ - - - - - - - - """ - return FakeVirtDomain() def fake_execute(self, *args): @@ -797,8 +776,6 @@ class LibvirtConnTestCase(test.TestCase): shutil.rmtree(os.path.join(FLAGS.instances_path, instance.name)) shutil.rmtree(os.path.join(FLAGS.instances_path, '_base')) - self.assertTrue(count) - def test_get_host_ip_addr(self): conn = connection.LibvirtConnection(False) ip = conn.get_host_ip_addr() -- cgit From 98e2fd764b33fa5a3af6ca982a171717a12ee206 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 13 Sep 2011 15:33:34 -0500 Subject: Only allow up to 15 chars for a Windows hostname. --- nova/tests/test_xenapi.py | 3 ++- nova/virt/xenapi/vmops.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 4a83d139e..47c6a3c95 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -364,7 +364,7 @@ class XenAPIVMTestCase(test.TestCase): def _test_spawn(self, image_ref, kernel_id, ramdisk_id, instance_type_id="3", os_type="linux", - architecture="x86-64", instance_id=1, + hostname="test", architecture="x86-64", instance_id=1, check_injection=False, create_record=True, empty_dns=False): stubs.stubout_loopingcall_start(self.stubs) @@ -377,6 +377,7 @@ class XenAPIVMTestCase(test.TestCase): 'ramdisk_id': ramdisk_id, 'instance_type_id': instance_type_id, 'os_type': os_type, + 'hostname': hostname, 'architecture': architecture} instance = db.instance_create(self.context, values) else: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 15b942a82..6b56d668e 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -253,7 +253,7 @@ class VMOps(object): self.create_vifs(vm_ref, instance, network_info) self.inject_network_info(instance, network_info, vm_ref) - self.inject_hostname(vm_ref, instance['hostname']) + self.inject_hostname(instance, vm_ref, instance['hostname']) return vm_ref @@ -1160,8 +1160,12 @@ class VMOps(object): resp = self._make_plugin_call('agent', 'resetnetwork', instance, '', args, vm_ref) - def inject_hostname(self, vm_ref, hostname): + def inject_hostname(self, instance, vm_ref, hostname): """Inject the hostname of the instance into the xenstore.""" + if instance.os_type == "windows": + # NOTE(jk0): Windows hostnames can only be <= 15 chars. + hostname = hostname[:15] + logging.debug(_("injecting hostname to xs for vm: |%s|"), vm_ref) self._session.call_xenapi_request("VM.add_to_xenstore_data", (vm_ref, "vm-data/hostname", hostname)) -- cgit From de94312a584a25bd70e0410e69aef34bf7c275d4 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 13 Sep 2011 14:32:24 -0700 Subject: deprecate aoe --- nova/volume/driver.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 35e3ea8d0..e5bb498ed 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -221,7 +221,14 @@ class VolumeDriver(object): class AOEDriver(VolumeDriver): - """Implements AOE specific volume commands.""" + """WARNING! Deprecated. This driver will be removed in Essex. Its use + is not recommended. + + Implements AOE specific volume commands.""" + + def __init__(self, *args, **kwargs): + LOG.warn(_("AOEDriver is deprecated and will be removed in Essex")) + super(AOEDriver, self).__init__(*args, **kwargs) def ensure_export(self, context, volume): # NOTE(vish): we depend on vblade-persist for recreating exports -- cgit From a85a2c2e82fa8820b04f669c92a3500c7c6cebe2 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 13 Sep 2011 20:38:26 -0400 Subject: pep8 fixes. --- nova/tests/test_libvirt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 8193d6ec2..a30b00dbe 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -57,7 +57,7 @@ class FakeVirtDomain(object): if fake_xml: self._fake_dom_xml = fake_xml else: - self._fake_dom_xml=""" + self._fake_dom_xml = """ @@ -188,7 +188,8 @@ class LibvirtConnTestCase(test.TestCase): fake.__setattr__(key, val) self.flags(image_service='nova.image.fake.FakeImageService') - self.flags(firewall_driver="nova.tests.fake_network.FakeIptablesFirewallDriver") + fw_driver = "nova.tests.fake_network.FakeIptablesFirewallDriver" + self.flags(firewall_driver=fw_driver) self.flags(libvirt_vif_driver="nova.tests.fake_network.FakeVIFDriver") self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn') -- cgit