summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.utils.p.gzbin291 -> 291 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.virt.configdrive.p.gzbin0 -> 618 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gzbin735 -> 734 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gzbin313 -> 313 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gzbin433 -> 430 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gzbin645 -> 725 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gzbin17580 -> 21340 bytes
-rw-r--r--nova/tests/test_hypervapi.py8
-rw-r--r--nova/virt/hyperv/vif.py133
-rw-r--r--nova/virt/hyperv/vmops.py94
-rw-r--r--nova/virt/hyperv/vmutils.py16
11 files changed, 186 insertions, 65 deletions
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.utils.p.gz
index 861c1ee8e..df40b08c0 100644
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.utils.p.gz
+++ b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.utils.p.gz
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.virt.configdrive.p.gz
new file mode 100644
index 000000000..b51766f75
--- /dev/null
+++ b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.virt.configdrive.p.gz
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gz
index acf47a4f6..092a1f933 100644
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gz
+++ b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gz
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gz
index af57ccc47..77f333c00 100644
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gz
+++ b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gz
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gz
index b67b1a894..8ab166a60 100644
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gz
+++ b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gz
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gz
index 24fb6e539..97e96be17 100644
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gz
+++ b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gz
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gz
index 0634adcba..728464ca9 100644
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gz
+++ b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gz
Binary files differ
diff --git a/nova/tests/test_hypervapi.py b/nova/tests/test_hypervapi.py
index f5713c457..9fec9d151 100644
--- a/nova/tests/test_hypervapi.py
+++ b/nova/tests/test_hypervapi.py
@@ -68,7 +68,8 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
self._setup_stubs()
self.flags(instances_path=r'C:\Hyper-V\test\instances',
- vswitch_name='external')
+ vswitch_name='external',
+ network_api_class='nova.network.quantumv2.api.API')
self._hypervutils = hypervutils.HyperVUtils()
self._conn = driver_hyperv.HyperVDriver(None)
@@ -119,6 +120,7 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
from nova.virt.hyperv import hostops
from nova.virt.hyperv import livemigrationops
from nova.virt.hyperv import snapshotops
+ from nova.virt.hyperv import vif
from nova.virt.hyperv import vmops
from nova.virt.hyperv import volumeops
from nova.virt.hyperv import volumeutils
@@ -129,6 +131,7 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
basevolumeutils,
baseops,
hostops,
+ vif,
vmops,
vmutils,
volumeops,
@@ -240,6 +243,9 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
self.assertEquals(len(dvd_paths), 0)
def test_spawn_no_vswitch_exception(self):
+ self.flags(network_api_class='nova.network.api.API')
+ # Reinstantiate driver, as the VIF plugin is loaded during __init__
+ self._conn = driver_hyperv.HyperVDriver(None)
# Set flag to a non existing vswitch
self.flags(vswitch_name=str(uuid.uuid4()))
self.assertRaises(vmutils.HyperVException, self._spawn_instance, True)
diff --git a/nova/virt/hyperv/vif.py b/nova/virt/hyperv/vif.py
new file mode 100644
index 000000000..a898d3ac2
--- /dev/null
+++ b/nova/virt/hyperv/vif.py
@@ -0,0 +1,133 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 Cloudbase Solutions Srl
+# Copyright 2013 Pedro Navarro Perez
+# All Rights Reserved.
+#
+# 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.
+import sys
+import uuid
+
+# Check needed for unit testing on Unix
+if sys.platform == 'win32':
+ import wmi
+
+from abc import abstractmethod
+from nova.openstack.common import cfg
+from nova.openstack.common import log as logging
+from nova.virt.hyperv import vmutils
+
+hyperv_opts = [
+ cfg.StrOpt('vswitch_name',
+ default=None,
+ help='External virtual switch Name, '
+ 'if not provided, the first external virtual '
+ 'switch is used'),
+]
+
+CONF = cfg.CONF
+CONF.register_opts(hyperv_opts)
+
+LOG = logging.getLogger(__name__)
+
+
+class HyperVBaseVIFDriver(object):
+ @abstractmethod
+ def plug(self, instance, vif):
+ pass
+
+ @abstractmethod
+ def unplug(self, instance, vif):
+ pass
+
+
+class HyperVQuantumVIFDriver(HyperVBaseVIFDriver):
+ """Quantum VIF driver."""
+
+ def plug(self, instance, vif):
+ # Quantum takes care of plugging the port
+ pass
+
+ def unplug(self, instance, vif):
+ # Quantum takes care of unplugging the port
+ pass
+
+
+class HyperVNovaNetworkVIFDriver(HyperVBaseVIFDriver):
+ """Nova network VIF driver."""
+
+ def __init__(self):
+ self._vmutils = vmutils.VMUtils()
+ self._conn = wmi.WMI(moniker='//./root/virtualization')
+
+ def _find_external_network(self):
+ """Find the vswitch that is connected to the physical nic.
+ Assumes only one physical nic on the host
+ """
+ #If there are no physical nics connected to networks, return.
+ LOG.debug(_("Attempting to bind NIC to %s ")
+ % CONF.vswitch_name)
+ if CONF.vswitch_name:
+ LOG.debug(_("Attempting to bind NIC to %s ")
+ % CONF.vswitch_name)
+ bound = self._conn.Msvm_VirtualSwitch(
+ ElementName=CONF.vswitch_name)
+ else:
+ LOG.debug(_("No vSwitch specified, attaching to default"))
+ self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')
+ if len(bound) == 0:
+ return None
+ if CONF.vswitch_name:
+ return self._conn.Msvm_VirtualSwitch(
+ ElementName=CONF.vswitch_name)[0]\
+ .associators(wmi_result_class='Msvm_SwitchPort')[0]\
+ .associators(wmi_result_class='Msvm_VirtualSwitch')[0]
+ else:
+ return self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')\
+ .associators(wmi_result_class='Msvm_SwitchPort')[0]\
+ .associators(wmi_result_class='Msvm_VirtualSwitch')[0]
+
+ def plug(self, instance, vif):
+ extswitch = self._find_external_network()
+ if extswitch is None:
+ raise vmutils.HyperVException(_('Cannot find vSwitch'))
+
+ vm_name = instance['name']
+
+ nic_data = self._conn.Msvm_SyntheticEthernetPortSettingData(
+ ElementName=vif['id'])[0]
+
+ switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0]
+ #Create a port on the vswitch.
+ (new_port, ret_val) = switch_svc.CreateSwitchPort(
+ Name=str(uuid.uuid4()),
+ FriendlyName=vm_name,
+ ScopeOfResidence="",
+ VirtualSwitch=extswitch.path_())
+ if ret_val != 0:
+ LOG.error(_('Failed creating a port on the external vswitch'))
+ raise vmutils.HyperVException(_('Failed creating port for %s') %
+ vm_name)
+ ext_path = extswitch.path_()
+ LOG.debug(_("Created switch port %(vm_name)s on switch %(ext_path)s")
+ % locals())
+
+ vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name)
+ vm = vms[0]
+
+ nic_data.Connection = [new_port]
+ self._vmutils.modify_virt_resource(self._conn, nic_data, vm)
+
+ def unplug(self, instance, vif):
+ #TODO(alepilotti) Not implemented
+ pass
diff --git a/nova/virt/hyperv/vmops.py b/nova/virt/hyperv/vmops.py
index 1fba15506..3d8958266 100644
--- a/nova/virt/hyperv/vmops.py
+++ b/nova/virt/hyperv/vmops.py
@@ -24,6 +24,7 @@ import uuid
from nova.api.metadata import base as instance_metadata
from nova import exception
from nova.openstack.common import cfg
+from nova.openstack.common import importutils
from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova import utils
@@ -35,10 +36,6 @@ from nova.virt.hyperv import vmutils
LOG = logging.getLogger(__name__)
hyperv_opts = [
- cfg.StrOpt('vswitch_name',
- default=None,
- help='Default vSwitch Name, '
- 'if none provided first external is used'),
cfg.BoolOpt('limit_cpu_features',
default=False,
help='Required for live migration among '
@@ -59,14 +56,32 @@ hyperv_opts = [
CONF = cfg.CONF
CONF.register_opts(hyperv_opts)
CONF.import_opt('use_cow_images', 'nova.virt.driver')
+CONF.import_opt('network_api_class', 'nova.network')
class VMOps(baseops.BaseOps):
+ _vif_driver_class_map = {
+ 'nova.network.quantumv2.api.API':
+ 'nova.virt.hyperv.vif.HyperVQuantumVIFDriver',
+ 'nova.network.api.API':
+ 'nova.virt.hyperv.vif.HyperVNovaNetworkVIFDriver',
+ }
+
def __init__(self, volumeops):
super(VMOps, self).__init__()
self._vmutils = vmutils.VMUtils()
self._volumeops = volumeops
+ self._load_vif_driver_class()
+
+ def _load_vif_driver_class(self):
+ try:
+ class_name = self._vif_driver_class_map[CONF.network_api_class]
+ self._vif_driver = importutils.import_object(class_name)
+ except KeyError:
+ raise TypeError(_("VIF driver not found for "
+ "network_api_class: %s") %
+ CONF.network_api_class)
def list_instances(self):
"""Return the names of all the instances known to Hyper-V."""
@@ -158,8 +173,8 @@ class VMOps(baseops.BaseOps):
self._create_scsi_controller(instance['name'])
for vif in network_info:
- mac_address = vif['address'].replace(':', '')
- self._create_nic(instance['name'], mac_address)
+ self._create_nic(instance['name'], vif)
+ self._vif_driver.plug(instance, vif)
if configdrive.required_by(instance):
self._create_config_drive(instance, injected_files,
@@ -367,46 +382,28 @@ class VMOps(baseops.BaseOps):
LOG.info(_('Created drive type %(drive_type)s for %(vm_name)s') %
locals())
- def _create_nic(self, vm_name, mac):
+ def _create_nic(self, vm_name, vif):
"""Create a (synthetic) nic and attach it to the vm."""
LOG.debug(_('Creating nic for %s '), vm_name)
- #Find the vswitch that is connected to the physical nic.
- vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name)
- extswitch = self._find_external_network()
- if extswitch is None:
- raise vmutils.HyperVException(_('Cannot find vSwitch'))
- vm = vms[0]
- switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0]
- #Find the default nic and clone it to create a new nic for the vm.
- #Use Msvm_SyntheticEthernetPortSettingData for Windows or Linux with
- #Linux Integration Components installed.
+ #Create a new nic
syntheticnics_data = self._conn.Msvm_SyntheticEthernetPortSettingData()
default_nic_data = [n for n in syntheticnics_data
if n.InstanceID.rfind('Default') > 0]
new_nic_data = self._vmutils.clone_wmi_obj(self._conn,
'Msvm_SyntheticEthernetPortSettingData',
default_nic_data[0])
- #Create a port on the vswitch.
- (new_port, ret_val) = switch_svc.CreateSwitchPort(
- Name=str(uuid.uuid4()),
- FriendlyName=vm_name,
- ScopeOfResidence="",
- VirtualSwitch=extswitch.path_())
- if ret_val != 0:
- LOG.error(_('Failed creating a port on the external vswitch'))
- raise vmutils.HyperVException(_('Failed creating port for %s') %
- vm_name)
- ext_path = extswitch.path_()
- LOG.debug(_("Created switch port %(vm_name)s on switch %(ext_path)s")
- % locals())
- #Connect the new nic to the new port.
- new_nic_data.Connection = [new_port]
- new_nic_data.ElementName = vm_name + ' nic'
- new_nic_data.Address = mac
+
+ #Configure the nic
+ new_nic_data.ElementName = vif['id']
+ new_nic_data.Address = vif['address'].replace(':', '')
new_nic_data.StaticMacAddress = 'True'
new_nic_data.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}']
- #Add the new nic to the vm.
+
+ #Add the new nic to the vm
+ vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name)
+ vm = vms[0]
+
new_resources = self._vmutils.add_virt_resource(self._conn,
new_nic_data, vm)
if new_resources is None:
@@ -414,33 +411,6 @@ class VMOps(baseops.BaseOps):
vm_name)
LOG.info(_("Created nic for %s "), vm_name)
- def _find_external_network(self):
- """Find the vswitch that is connected to the physical nic.
- Assumes only one physical nic on the host
- """
- #If there are no physical nics connected to networks, return.
- LOG.debug(_("Attempting to bind NIC to %s ")
- % CONF.vswitch_name)
- if CONF.vswitch_name:
- LOG.debug(_("Attempting to bind NIC to %s ")
- % CONF.vswitch_name)
- bound = self._conn.Msvm_VirtualSwitch(
- ElementName=CONF.vswitch_name)
- else:
- LOG.debug(_("No vSwitch specified, attaching to default"))
- self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')
- if len(bound) == 0:
- return None
- if CONF.vswitch_name:
- return self._conn.Msvm_VirtualSwitch(
- ElementName=CONF.vswitch_name)[0]\
- .associators(wmi_result_class='Msvm_SwitchPort')[0]\
- .associators(wmi_result_class='Msvm_VirtualSwitch')[0]
- else:
- return self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')\
- .associators(wmi_result_class='Msvm_SwitchPort')[0]\
- .associators(wmi_result_class='Msvm_VirtualSwitch')[0]
-
def reboot(self, instance, network_info, reboot_type):
"""Reboot the specified instance."""
vm = self._vmutils.lookup(self._conn, instance['name'])
diff --git a/nova/virt/hyperv/vmutils.py b/nova/virt/hyperv/vmutils.py
index bae8a1f1a..d899f977d 100644
--- a/nova/virt/hyperv/vmutils.py
+++ b/nova/virt/hyperv/vmutils.py
@@ -130,7 +130,7 @@ class VMUtils(object):
return newinst
def add_virt_resource(self, conn, res_setting_data, target_vm):
- """Add a new resource (disk/nic) to the VM."""
+ """Adds a new resource to the VM."""
vs_man_svc = conn.Msvm_VirtualSystemManagementService()[0]
(job, new_resources, ret_val) = vs_man_svc.\
AddVirtualSystemResources([res_setting_data.GetText_(1)],
@@ -145,8 +145,20 @@ class VMUtils(object):
else:
return None
+ def modify_virt_resource(self, conn, res_setting_data, target_vm):
+ """Updates a VM resource."""
+ vs_man_svc = conn.Msvm_VirtualSystemManagementService()[0]
+ (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources(
+ ResourceSettingData=[res_setting_data.GetText_(1)],
+ ComputerSystem=target_vm.path_())
+ if ret_val == constants.WMI_JOB_STATUS_STARTED:
+ success = self.check_job_status(job)
+ else:
+ success = (ret_val == 0)
+ return success
+
def remove_virt_resource(self, conn, res_setting_data, target_vm):
- """Add a new resource (disk/nic) to the VM."""
+ """Removes a VM resource."""
vs_man_svc = conn.Msvm_VirtualSystemManagementService()[0]
(job, ret_val) = vs_man_svc.\
RemoveVirtualSystemResources([res_setting_data.path_()],