summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Erdfelt <johannes.erdfelt@rackspace.com>2011-09-14 16:19:00 +0000
committerJohannes Erdfelt <johannes.erdfelt@rackspace.com>2011-09-14 16:19:00 +0000
commit9614c5690118f239652e7ebccdd4ce6ecffbe4ee (patch)
treeff71837cc793c42fd3f98aba8639976cca64f2a1
parent147290d01389d72d3754bbaa088660f38a6871d8 (diff)
parent89736bf13562811cebb42cd6e3377d7f9e0a0b9c (diff)
downloadnova-9614c5690118f239652e7ebccdd4ce6ecffbe4ee.tar.gz
nova-9614c5690118f239652e7ebccdd4ce6ecffbe4ee.tar.xz
nova-9614c5690118f239652e7ebccdd4ce6ecffbe4ee.zip
Merge with trunk
-rw-r--r--nova/api/ec2/cloud.py28
-rw-r--r--nova/compute/api.py2
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/046_add_instance_swap.py48
-rw-r--r--nova/db/sqlalchemy/models.py2
-rw-r--r--nova/tests/api/ec2/test_cloud.py4
-rw-r--r--nova/tests/fake_network.py30
-rw-r--r--nova/tests/test_libvirt.py133
-rw-r--r--nova/tests/test_xenapi.py3
-rw-r--r--nova/tests/vmwareapi/stubs.py2
-rw-r--r--nova/virt/disk.py41
-rw-r--r--nova/virt/libvirt/connection.py23
-rw-r--r--nova/virt/vmwareapi/fake.py2
-rw-r--r--nova/virt/vmwareapi/vif.py27
-rw-r--r--nova/virt/vmwareapi/vm_util.py26
-rw-r--r--nova/virt/vmwareapi/vmops.py106
-rw-r--r--nova/virt/xenapi/vmops.py8
-rw-r--r--nova/volume/driver.py9
-rw-r--r--tools/esx/guest_tool.py70
18 files changed, 388 insertions, 176 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index eacfdc0df..0efb90d6e 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -272,11 +272,23 @@ class CloudController(object):
mappings = {}
mappings['ami'] = block_device.strip_dev(root_device_name)
mappings['root'] = root_device_name
-
- # 'ephemeralN' and 'swap'
+ 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
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 +298,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/compute/api.py b/nova/compute/api.py
index d674224e5..4220f47ae 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1050,7 +1050,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/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/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py
index 7fe353b3d..7bdae0552 100644
--- a/nova/tests/api/ec2/test_cloud.py
+++ b/nova/tests/api/ec2/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),
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..b7c1ef1ab 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -35,6 +35,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
@@ -51,6 +52,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 = """
+ <domain type='kvm'>
+ <devices>
+ <disk type='file'>
+ <source file='filename'/>
+ </disk>
+ </devices>
+ </domain>
+ """
+
+ 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 +179,24 @@ 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')
+ 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')
connection.LibvirtConnection._conn = fake
def fake_lookup(self, instance_name):
-
- class FakeVirtDomain(object):
-
- def snapshotCreateXML(self, *args):
- return None
-
- def XMLDesc(self, *args):
- return """
- <domain type='kvm'>
- <devices>
- <disk type='file'>
- <source file='filename'/>
- </disk>
- </devices>
- </domain>
- """
-
return FakeVirtDomain()
def fake_execute(self, *args):
@@ -797,8 +778,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()
@@ -840,6 +819,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):
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/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
@@ -47,7 +47,5 @@ def set_stubs(stubs):
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",
fake_is_vim_object)
diff --git a/nova/virt/disk.py b/nova/virt/disk.py
index cd3422829..9fe164cfb 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'
+ '<os_type>=<mkfs command>')
+
+
+_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 f591ce02c..18e643ea8 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
@@ -466,7 +467,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
@@ -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'])
@@ -1102,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(
@@ -1110,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'):
diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py
index 0dea13aba..ae00bca0f 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(context, 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 6bdc2f23a..063b84a62 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
@@ -718,39 +728,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)
@@ -758,7 +774,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/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))
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
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'])