Merge "XenAPI: OVS agent updates the wrong port with Neutron"
This commit is contained in:
@@ -135,6 +135,11 @@ class VirtualInterfacePlugException(NovaException):
|
|||||||
msg_fmt = _("Virtual interface plugin failed")
|
msg_fmt = _("Virtual interface plugin failed")
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualInterfaceUnplugException(NovaException):
|
||||||
|
msg_fmt = _("Virtual interface unplugin failed: "
|
||||||
|
"%(reason)s")
|
||||||
|
|
||||||
|
|
||||||
class GlanceConnectionFailed(NovaException):
|
class GlanceConnectionFailed(NovaException):
|
||||||
msg_fmt = _("Connection to glance host %(server)s failed: "
|
msg_fmt = _("Connection to glance host %(server)s failed: "
|
||||||
"%(reason)s")
|
"%(reason)s")
|
||||||
|
@@ -55,6 +55,11 @@ class ObjectsTestCase(stubs.XenAPITestBaseNoDB):
|
|||||||
vdi.get_X("ref")
|
vdi.get_X("ref")
|
||||||
self.session.call_xenapi.assert_called_once_with("VDI.get_X", "ref")
|
self.session.call_xenapi.assert_called_once_with("VDI.get_X", "ref")
|
||||||
|
|
||||||
|
def test_VIF(self):
|
||||||
|
vdi = objects.VIF(self.session)
|
||||||
|
vdi.get_X("ref")
|
||||||
|
self.session.call_xenapi.assert_called_once_with("VIF.get_X", "ref")
|
||||||
|
|
||||||
def test_VBD(self):
|
def test_VBD(self):
|
||||||
vbd = objects.VBD(self.session)
|
vbd = objects.VBD(self.session)
|
||||||
vbd.get_X("ref")
|
vbd.get_X("ref")
|
||||||
|
@@ -100,6 +100,10 @@ class ApplySessionHelpersTestCase(stubs.XenAPITestBaseNoDB):
|
|||||||
self.session.VDI.get_X("ref")
|
self.session.VDI.get_X("ref")
|
||||||
self.session.call_xenapi.assert_called_once_with("VDI.get_X", "ref")
|
self.session.call_xenapi.assert_called_once_with("VDI.get_X", "ref")
|
||||||
|
|
||||||
|
def test_apply_session_helpers_add_VIF(self):
|
||||||
|
self.session.VIF.get_X("ref")
|
||||||
|
self.session.call_xenapi.assert_called_once_with("VIF.get_X", "ref")
|
||||||
|
|
||||||
def test_apply_session_helpers_add_VBD(self):
|
def test_apply_session_helpers_add_VBD(self):
|
||||||
self.session.VBD.get_X("ref")
|
self.session.VBD.get_X("ref")
|
||||||
self.session.call_xenapi.assert_called_once_with("VBD.get_X", "ref")
|
self.session.call_xenapi.assert_called_once_with("VBD.get_X", "ref")
|
||||||
|
@@ -17,6 +17,7 @@ import mock
|
|||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.network import model
|
from nova.network import model
|
||||||
|
from nova import test
|
||||||
from nova.tests.unit.virt.xenapi import stubs
|
from nova.tests.unit.virt.xenapi import stubs
|
||||||
from nova.virt.xenapi import network_utils
|
from nova.virt.xenapi import network_utils
|
||||||
from nova.virt.xenapi import vif
|
from nova.virt.xenapi import vif
|
||||||
@@ -67,6 +68,22 @@ class XenVIFDriverTestBase(stubs.XenAPITestBaseNoDB):
|
|||||||
self._session = mock.Mock()
|
self._session = mock.Mock()
|
||||||
self._session.call_xenapi.side_effect = fake_call_xenapi
|
self._session.call_xenapi.side_effect = fake_call_xenapi
|
||||||
|
|
||||||
|
def mock_patch_object(self, target, attribute, return_val=None,
|
||||||
|
side_effect=None):
|
||||||
|
"""Utilility function to mock object's attribute at runtime:
|
||||||
|
Some methods are dynamic, so standard mocking does not work
|
||||||
|
and we need to mock them at runtime.
|
||||||
|
e.g. self._session.VIF.get_record which is dynamically
|
||||||
|
created via the override function of __getattr__.
|
||||||
|
"""
|
||||||
|
|
||||||
|
patcher = mock.patch.object(target, attribute,
|
||||||
|
return_value=return_val,
|
||||||
|
side_effect=side_effect)
|
||||||
|
mock_one = patcher.start()
|
||||||
|
self.addCleanup(patcher.stop)
|
||||||
|
return mock_one
|
||||||
|
|
||||||
|
|
||||||
class XenVIFDriverTestCase(XenVIFDriverTestBase):
|
class XenVIFDriverTestCase(XenVIFDriverTestBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -149,41 +166,126 @@ class XenAPIBridgeDriverTestCase(XenVIFDriverTestBase, object):
|
|||||||
ret_vif_ref = self.bridge_driver.plug(instance, vif, vm_ref, device)
|
ret_vif_ref = self.bridge_driver.plug(instance, vif, vm_ref, device)
|
||||||
self.assertEqual('fake_vif_ref', ret_vif_ref)
|
self.assertEqual('fake_vif_ref', ret_vif_ref)
|
||||||
|
|
||||||
@mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
|
||||||
return_value='fake_vif_ref')
|
|
||||||
def test_unplug(self, mock_get_vif_ref):
|
|
||||||
instance = {'name': "fake_instance"}
|
|
||||||
vm_ref = "fake_vm_ref"
|
|
||||||
self.bridge_driver.unplug(instance, fake_vif, vm_ref)
|
|
||||||
|
|
||||||
expected = [mock.call('VIF.destroy', 'fake_vif_ref')]
|
|
||||||
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
|
||||||
|
|
||||||
|
|
||||||
class XenAPIOpenVswitchDriverTestCase(XenVIFDriverTestBase):
|
class XenAPIOpenVswitchDriverTestCase(XenVIFDriverTestBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(XenAPIOpenVswitchDriverTestCase, self).setUp()
|
super(XenAPIOpenVswitchDriverTestCase, self).setUp()
|
||||||
self.ovs_driver = vif.XenAPIOpenVswitchDriver(self._session)
|
self.ovs_driver = vif.XenAPIOpenVswitchDriver(self._session)
|
||||||
|
|
||||||
@mock.patch.object(network_utils, 'find_network_with_bridge',
|
|
||||||
return_value='fake_network_ref')
|
|
||||||
@mock.patch.object(vif.XenVIFDriver, '_create_vif',
|
@mock.patch.object(vif.XenVIFDriver, '_create_vif',
|
||||||
return_value='fake_vif_ref')
|
return_value='fake_vif_ref')
|
||||||
|
@mock.patch.object(vif.XenAPIOpenVswitchDriver,
|
||||||
|
'create_vif_interim_network')
|
||||||
@mock.patch.object(vif.XenVIFDriver, '_get_vif_ref', return_value=None)
|
@mock.patch.object(vif.XenVIFDriver, '_get_vif_ref', return_value=None)
|
||||||
def test_plug(self, mock_get_vif_ref, mock_create_vif,
|
@mock.patch.object(vif.vm_utils, 'lookup', return_value='fake_vm_ref')
|
||||||
mock_find_network_with_bridge):
|
def test_plug(self, mock_lookup, mock_get_vif_ref,
|
||||||
|
mock_create_vif_interim_network,
|
||||||
|
mock_create_vif):
|
||||||
instance = {'name': "fake_instance_name"}
|
instance = {'name': "fake_instance_name"}
|
||||||
vm_ref = "fake_vm_ref"
|
ret_vif_ref = self.ovs_driver.plug(
|
||||||
device = 1
|
instance, fake_vif, vm_ref=None, device=1)
|
||||||
ret_vif_ref = self.ovs_driver.plug(instance, fake_vif, vm_ref, device)
|
self.assertTrue(mock_lookup.called)
|
||||||
|
self.assertTrue(mock_get_vif_ref.called)
|
||||||
|
self.assertTrue(mock_create_vif_interim_network.called)
|
||||||
|
self.assertTrue(mock_create_vif.called)
|
||||||
self.assertEqual('fake_vif_ref', ret_vif_ref)
|
self.assertEqual('fake_vif_ref', ret_vif_ref)
|
||||||
|
|
||||||
@mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
@mock.patch.object(vif.XenAPIOpenVswitchDriver, '_ovs_del_br')
|
||||||
return_value='fake_vif_ref')
|
@mock.patch.object(vif.XenAPIOpenVswitchDriver, '_ovs_del_port')
|
||||||
def test_unplug(self, mock_get_vif_ref):
|
@mock.patch.object(network_utils, 'find_network_with_name_label',
|
||||||
|
return_value='fake_network')
|
||||||
|
@mock.patch.object(vif.XenVIFDriver, 'unplug')
|
||||||
|
def test_unplug(self, mock_super_unplug,
|
||||||
|
mock_find_network_with_name_label,
|
||||||
|
mock_ovs_del_port,
|
||||||
|
mock_ovs_del_br):
|
||||||
instance = {'name': "fake_instance"}
|
instance = {'name': "fake_instance"}
|
||||||
vm_ref = "fake_vm_ref"
|
vm_ref = "fake_vm_ref"
|
||||||
|
|
||||||
|
mock_network_get_VIFs = self.mock_patch_object(
|
||||||
|
self._session.network, 'get_VIFs', return_val=None)
|
||||||
|
mock_network_get_bridge = self.mock_patch_object(
|
||||||
|
self._session.network, 'get_bridge', return_val='fake_bridge')
|
||||||
|
mock_network_destroy = self.mock_patch_object(
|
||||||
|
self._session.network, 'destroy')
|
||||||
self.ovs_driver.unplug(instance, fake_vif, vm_ref)
|
self.ovs_driver.unplug(instance, fake_vif, vm_ref)
|
||||||
|
|
||||||
expected = [mock.call('VIF.destroy', 'fake_vif_ref')]
|
self.assertTrue(mock_super_unplug.called)
|
||||||
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
self.assertTrue(mock_find_network_with_name_label.called)
|
||||||
|
self.assertTrue(mock_network_get_VIFs.called)
|
||||||
|
self.assertTrue(mock_network_get_bridge.called)
|
||||||
|
self.assertEqual(mock_ovs_del_port.call_count, 2)
|
||||||
|
self.assertTrue(mock_network_destroy.called)
|
||||||
|
|
||||||
|
@mock.patch.object(vif.XenAPIOpenVswitchDriver, '_ovs_del_br')
|
||||||
|
@mock.patch.object(vif.XenAPIOpenVswitchDriver, '_ovs_del_port')
|
||||||
|
@mock.patch.object(network_utils, 'find_network_with_name_label',
|
||||||
|
return_value='fake_network')
|
||||||
|
@mock.patch.object(vif.XenVIFDriver, 'unplug')
|
||||||
|
def test_unplug_exception(self, mock_super_unplug,
|
||||||
|
mock_find_network_with_name_label,
|
||||||
|
mock_ovs_del_port,
|
||||||
|
mock_ovs_del_br):
|
||||||
|
instance = {'name': "fake_instance"}
|
||||||
|
vm_ref = "fake_vm_ref"
|
||||||
|
|
||||||
|
self.mock_patch_object(
|
||||||
|
self._session.network, 'get_VIFs', return_val=None)
|
||||||
|
self.mock_patch_object(
|
||||||
|
self._session.network, 'get_bridge', return_val='fake_bridge')
|
||||||
|
self.mock_patch_object(
|
||||||
|
self._session.network, 'destroy',
|
||||||
|
side_effect=test.TestingException)
|
||||||
|
|
||||||
|
self.assertRaises(exception.VirtualInterfaceUnplugException,
|
||||||
|
self.ovs_driver.unplug, instance, fake_vif,
|
||||||
|
vm_ref)
|
||||||
|
|
||||||
|
@mock.patch.object(vif.XenAPIOpenVswitchDriver, '_ovs_add_patch_port')
|
||||||
|
@mock.patch.object(vif.XenAPIOpenVswitchDriver, '_ovs_map_external_ids')
|
||||||
|
def test_post_start_actions(self, mock_ovs_map_external_ids,
|
||||||
|
mock_ovs_add_patch_port):
|
||||||
|
vif_ref = "fake_vif_ref"
|
||||||
|
instance = {'name': 'fake_instance_name'}
|
||||||
|
fake_vif_rec = {'uuid': fake_vif['uuid'],
|
||||||
|
'MAC': fake_vif['address'],
|
||||||
|
'network': 'fake_network',
|
||||||
|
'other_config': {
|
||||||
|
'nicira-iface-id': 'fake-nicira-iface-id'}
|
||||||
|
}
|
||||||
|
mock_VIF_get_record = self.mock_patch_object(
|
||||||
|
self._session.VIF, 'get_record', return_val=fake_vif_rec)
|
||||||
|
mock_network_get_bridge = self.mock_patch_object(
|
||||||
|
self._session.network, 'get_bridge',
|
||||||
|
return_val='fake_bridge_name')
|
||||||
|
mock_network_get_uuid = self.mock_patch_object(
|
||||||
|
self._session.network, 'get_uuid',
|
||||||
|
return_val='fake_network_uuid')
|
||||||
|
|
||||||
|
self.ovs_driver.post_start_actions(instance, vif_ref)
|
||||||
|
|
||||||
|
self.assertTrue(mock_VIF_get_record.called)
|
||||||
|
self.assertTrue(mock_network_get_bridge.called)
|
||||||
|
self.assertTrue(mock_network_get_uuid.called)
|
||||||
|
self.assertEqual(mock_ovs_add_patch_port.call_count, 2)
|
||||||
|
self.assertTrue(mock_ovs_map_external_ids.called)
|
||||||
|
|
||||||
|
@mock.patch.object(network_utils, 'find_network_with_name_label',
|
||||||
|
return_value="exist_network_ref")
|
||||||
|
def test_create_vif_interim_network_exist(self,
|
||||||
|
mock_find_network_with_name_label):
|
||||||
|
mock_network_create = self.mock_patch_object(
|
||||||
|
self._session.network, 'create', return_val='new_network_ref')
|
||||||
|
network_ref = self.ovs_driver.create_vif_interim_network(fake_vif)
|
||||||
|
self.assertFalse(mock_network_create.called)
|
||||||
|
self.assertEqual(network_ref, 'exist_network_ref')
|
||||||
|
|
||||||
|
@mock.patch.object(network_utils, 'find_network_with_name_label',
|
||||||
|
return_value=None)
|
||||||
|
def test_create_vif_interim_network_new(self,
|
||||||
|
mock_find_network_with_name_label):
|
||||||
|
mock_network_create = self.mock_patch_object(
|
||||||
|
self._session.network, 'create', return_val='new_network_ref')
|
||||||
|
network_ref = self.ovs_driver.create_vif_interim_network(fake_vif)
|
||||||
|
self.assertTrue(mock_network_create.called)
|
||||||
|
self.assertEqual(network_ref, 'new_network_ref')
|
||||||
|
@@ -100,6 +100,12 @@ class VDI(XenAPISessionObject):
|
|||||||
super(VDI, self).__init__(session, "VDI")
|
super(VDI, self).__init__(session, "VDI")
|
||||||
|
|
||||||
|
|
||||||
|
class VIF(XenAPISessionObject):
|
||||||
|
"""Virtual Network Interface."""
|
||||||
|
def __init__(self, session):
|
||||||
|
super(VIF, self).__init__(session, "VIF")
|
||||||
|
|
||||||
|
|
||||||
class SR(XenAPISessionObject):
|
class SR(XenAPISessionObject):
|
||||||
"""Storage Repository."""
|
"""Storage Repository."""
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
|
@@ -53,6 +53,7 @@ def apply_session_helpers(session):
|
|||||||
session.VM = cli_objects.VM(session)
|
session.VM = cli_objects.VM(session)
|
||||||
session.SR = cli_objects.SR(session)
|
session.SR = cli_objects.SR(session)
|
||||||
session.VDI = cli_objects.VDI(session)
|
session.VDI = cli_objects.VDI(session)
|
||||||
|
session.VIF = cli_objects.VIF(session)
|
||||||
session.VBD = cli_objects.VBD(session)
|
session.VBD = cli_objects.VBD(session)
|
||||||
session.PBD = cli_objects.PBD(session)
|
session.PBD = cli_objects.PBD(session)
|
||||||
session.PIF = cli_objects.PIF(session)
|
session.PIF = cli_objects.PIF(session)
|
||||||
@@ -69,7 +70,7 @@ class XenAPISession(object):
|
|||||||
# changed in development environments.
|
# changed in development environments.
|
||||||
# MAJOR VERSION: Incompatible changes with the plugins
|
# MAJOR VERSION: Incompatible changes with the plugins
|
||||||
# MINOR VERSION: Compatible changes, new plguins, etc
|
# MINOR VERSION: Compatible changes, new plguins, etc
|
||||||
PLUGIN_REQUIRED_VERSION = '1.4'
|
PLUGIN_REQUIRED_VERSION = '1.5'
|
||||||
|
|
||||||
def __init__(self, url, user, pw):
|
def __init__(self, url, user, pw):
|
||||||
version_string = version.version_string_with_package()
|
version_string = version.version_string_with_package()
|
||||||
|
@@ -760,7 +760,7 @@ class SessionBase(object):
|
|||||||
return base64.b64encode(zlib.compress("dom_id: %s" % dom_id))
|
return base64.b64encode(zlib.compress("dom_id: %s" % dom_id))
|
||||||
|
|
||||||
def _plugin_nova_plugin_version_get_version(self, method, args):
|
def _plugin_nova_plugin_version_get_version(self, method, args):
|
||||||
return pickle.dumps("1.4")
|
return pickle.dumps("1.5")
|
||||||
|
|
||||||
def _plugin_xenhost_query_gc(self, method, args):
|
def _plugin_xenhost_query_gc(self, method, args):
|
||||||
return pickle.dumps("False")
|
return pickle.dumps("False")
|
||||||
|
@@ -23,6 +23,7 @@ import nova.conf
|
|||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova.i18n import _LW
|
from nova.i18n import _LW
|
||||||
|
from nova.network import model as network_model
|
||||||
from nova.virt.xenapi import network_utils
|
from nova.virt.xenapi import network_utils
|
||||||
from nova.virt.xenapi import vm_utils
|
from nova.virt.xenapi import vm_utils
|
||||||
|
|
||||||
@@ -180,11 +181,18 @@ class XenAPIBridgeDriver(XenVIFDriver):
|
|||||||
def unplug(self, instance, vif, vm_ref):
|
def unplug(self, instance, vif, vm_ref):
|
||||||
super(XenAPIBridgeDriver, self).unplug(instance, vif, vm_ref)
|
super(XenAPIBridgeDriver, self).unplug(instance, vif, vm_ref)
|
||||||
|
|
||||||
|
def post_start_actions(self, instance, vif_ref):
|
||||||
|
"""no further actions needed for this driver type"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class XenAPIOpenVswitchDriver(XenVIFDriver):
|
class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||||
"""VIF driver for Open vSwitch with XenAPI."""
|
"""VIF driver for Open vSwitch with XenAPI."""
|
||||||
|
|
||||||
def plug(self, instance, vif, vm_ref=None, device=None):
|
def plug(self, instance, vif, vm_ref=None, device=None):
|
||||||
|
"""create an interim network for this vif; and build
|
||||||
|
the vif_rec which will be used by xapi to create VM vif
|
||||||
|
"""
|
||||||
if not vm_ref:
|
if not vm_ref:
|
||||||
vm_ref = vm_utils.lookup(self._session, instance['name'])
|
vm_ref = vm_utils.lookup(self._session, instance['name'])
|
||||||
|
|
||||||
@@ -198,10 +206,10 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
|||||||
if not device:
|
if not device:
|
||||||
device = 0
|
device = 0
|
||||||
|
|
||||||
# with OVS model, always plug into an OVS integration bridge
|
# Create an interim network for each VIF, so dom0 has a single
|
||||||
# that is already created
|
# bridge for each device (the emulated and PV ethernet devices
|
||||||
network_ref = network_utils.find_network_with_bridge(
|
# will both be on this bridge.
|
||||||
self._session, CONF.xenserver.ovs_integration_bridge)
|
network_ref = self.create_vif_interim_network(vif)
|
||||||
vif_rec = {}
|
vif_rec = {}
|
||||||
vif_rec['device'] = str(device)
|
vif_rec['device'] = str(device)
|
||||||
vif_rec['network'] = network_ref
|
vif_rec['network'] = network_ref
|
||||||
@@ -216,4 +224,157 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
|||||||
return self._create_vif(vif, vif_rec, vm_ref)
|
return self._create_vif(vif, vif_rec, vm_ref)
|
||||||
|
|
||||||
def unplug(self, instance, vif, vm_ref):
|
def unplug(self, instance, vif, vm_ref):
|
||||||
|
"""unplug vif:
|
||||||
|
1. unplug and destroy vif.
|
||||||
|
2. delete the patch port pair between the integration bridge and
|
||||||
|
the interim network.
|
||||||
|
3. destroy the interim network
|
||||||
|
4. delete the OVS bridge service for the interim network
|
||||||
|
"""
|
||||||
super(XenAPIOpenVswitchDriver, self).unplug(instance, vif, vm_ref)
|
super(XenAPIOpenVswitchDriver, self).unplug(instance, vif, vm_ref)
|
||||||
|
|
||||||
|
net_name = self.get_vif_interim_net_name(vif)
|
||||||
|
network = network_utils.find_network_with_name_label(
|
||||||
|
self._session, net_name)
|
||||||
|
if network is None:
|
||||||
|
return
|
||||||
|
vifs = self._session.network.get_VIFs(network)
|
||||||
|
if vifs:
|
||||||
|
# only remove the interim network when it's empty.
|
||||||
|
# for resize/migrate on local host, vifs on both of the
|
||||||
|
# source and target VM will be connected to the same
|
||||||
|
# interim network.
|
||||||
|
return
|
||||||
|
LOG.debug('destroying patch port pair for vif: vif_id=%(vif_id)s',
|
||||||
|
{'vif_id': vif['id']})
|
||||||
|
bridge_name = self._session.network.get_bridge(network)
|
||||||
|
patch_port1, patch_port2 = self._get_patch_port_pair_names(vif['id'])
|
||||||
|
try:
|
||||||
|
# delete the patch port pair
|
||||||
|
self._ovs_del_port(bridge_name, patch_port1)
|
||||||
|
self._ovs_del_port(CONF.xenserver.ovs_integration_bridge,
|
||||||
|
patch_port2)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.warn(_LW("Failed to delete patch port pair for vif %(if)s,"
|
||||||
|
" exception:%(exception)s"),
|
||||||
|
{'if': vif, 'exception': e}, instance=instance)
|
||||||
|
raise exception.VirtualInterfaceUnplugException(
|
||||||
|
reason=_("Failed to delete patch port pair"))
|
||||||
|
|
||||||
|
LOG.debug('destroying network: network=%(network)s,'
|
||||||
|
'bridge=%(br)s',
|
||||||
|
{'network': network, 'br': bridge_name})
|
||||||
|
try:
|
||||||
|
self._session.network.destroy(network)
|
||||||
|
# delete bridge if it still exists.
|
||||||
|
# As there is patch port existing on this bridge when destroying
|
||||||
|
# the VM vif (which happens when shutdown the VM), the bridge
|
||||||
|
# won't be destroyed automatically by XAPI. So let's destroy it
|
||||||
|
# at here.
|
||||||
|
self._ovs_del_br(bridge_name)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.warn(_LW("Failed to delete bridge for vif %(if)s, "
|
||||||
|
"exception:%(exception)s"),
|
||||||
|
{'if': vif, 'exception': e}, instance=instance)
|
||||||
|
raise exception.VirtualInterfaceUnplugException(
|
||||||
|
reason=_("Failed to delete bridge"))
|
||||||
|
|
||||||
|
def post_start_actions(self, instance, vif_ref):
|
||||||
|
"""Do needed actions post vif start:
|
||||||
|
plug the interim ovs bridge to the integration bridge;
|
||||||
|
set external_ids to the int-br port which will service
|
||||||
|
for this vif.
|
||||||
|
"""
|
||||||
|
vif_rec = self._session.VIF.get_record(vif_ref)
|
||||||
|
network_ref = vif_rec['network']
|
||||||
|
bridge_name = self._session.network.get_bridge(network_ref)
|
||||||
|
network_uuid = self._session.network.get_uuid(network_ref)
|
||||||
|
iface_id = vif_rec['other_config']['nicira-iface-id']
|
||||||
|
patch_port1, patch_port2 = self._get_patch_port_pair_names(iface_id)
|
||||||
|
LOG.debug('plug_ovs_bridge: port1=%(port1)s, port2=%(port2)s,'
|
||||||
|
'network_uuid=%(uuid)s, bridge_name=%(bridge_name)s',
|
||||||
|
{'port1': patch_port1, 'port2': patch_port2,
|
||||||
|
'uuid': network_uuid, 'bridge_name': bridge_name})
|
||||||
|
if bridge_name is None:
|
||||||
|
raise exception.VirtualInterfacePlugException(
|
||||||
|
_("Failed to find bridge for vif"))
|
||||||
|
|
||||||
|
self._ovs_add_patch_port(bridge_name, patch_port1, patch_port2)
|
||||||
|
self._ovs_add_patch_port(CONF.xenserver.ovs_integration_bridge,
|
||||||
|
patch_port2, patch_port1)
|
||||||
|
self._ovs_map_external_ids(patch_port2, vif_rec)
|
||||||
|
|
||||||
|
def get_vif_interim_net_name(self, vif):
|
||||||
|
return ("net-" + vif['id'])[:network_model.NIC_NAME_LEN]
|
||||||
|
|
||||||
|
def create_vif_interim_network(self, vif):
|
||||||
|
net_name = self.get_vif_interim_net_name(vif)
|
||||||
|
network_rec = {'name_label': net_name,
|
||||||
|
'name_description': "interim network for vif",
|
||||||
|
'other_config': {}}
|
||||||
|
network_ref = network_utils.find_network_with_name_label(
|
||||||
|
self._session, net_name)
|
||||||
|
if network_ref:
|
||||||
|
# already exist, just return
|
||||||
|
# in some scenarios: e..g resize/migrate, it won't create new
|
||||||
|
# interim network.
|
||||||
|
return network_ref
|
||||||
|
try:
|
||||||
|
network_ref = self._session.network.create(network_rec)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.warn(_LW("Failed to create interim network for vif %(if)s, "
|
||||||
|
"exception:%(exception)s"),
|
||||||
|
{'if': vif, 'exception': e})
|
||||||
|
raise exception.VirtualInterfacePlugException(
|
||||||
|
_("Failed to create the interim network for vif"))
|
||||||
|
return network_ref
|
||||||
|
|
||||||
|
def _get_patch_port_pair_names(self, iface_id):
|
||||||
|
return (("pp1-%s" % iface_id)[:network_model.NIC_NAME_LEN],
|
||||||
|
("pp2-%s" % iface_id)[:network_model.NIC_NAME_LEN])
|
||||||
|
|
||||||
|
def _ovs_add_patch_port(self, bridge_name, port_name, peer_port_name):
|
||||||
|
cmd = 'ovs_add_patch_port'
|
||||||
|
args = {'bridge_name': bridge_name,
|
||||||
|
'port_name': port_name,
|
||||||
|
'peer_port_name': peer_port_name
|
||||||
|
}
|
||||||
|
self._exec_dom0_cmd(cmd, args)
|
||||||
|
|
||||||
|
def _ovs_del_port(self, bridge_name, port_name):
|
||||||
|
cmd = 'ovs_del_port'
|
||||||
|
args = {'bridge_name': bridge_name,
|
||||||
|
'port_name': port_name
|
||||||
|
}
|
||||||
|
self._exec_dom0_cmd(cmd, args)
|
||||||
|
|
||||||
|
def _ovs_del_br(self, bridge_name):
|
||||||
|
cmd = 'ovs_del_br'
|
||||||
|
args = {'bridge_name': bridge_name}
|
||||||
|
self._exec_dom0_cmd(cmd, args)
|
||||||
|
|
||||||
|
def _ovs_set_if_external_id(self, interface, extneral_id, value):
|
||||||
|
cmd = 'ovs_set_if_external_id'
|
||||||
|
args = {'interface': interface,
|
||||||
|
'extneral_id': extneral_id,
|
||||||
|
'value': value}
|
||||||
|
self._exec_dom0_cmd(cmd, args)
|
||||||
|
|
||||||
|
def _ovs_map_external_ids(self, interface, vif_rec):
|
||||||
|
'''set external ids on the integration bridge vif
|
||||||
|
'''
|
||||||
|
mac = vif_rec['MAC']
|
||||||
|
iface_id = vif_rec['other_config']['nicira-iface-id']
|
||||||
|
vif_uuid = vif_rec['uuid']
|
||||||
|
status = 'active'
|
||||||
|
|
||||||
|
self._ovs_set_if_external_id(interface, 'attached-mac', mac)
|
||||||
|
self._ovs_set_if_external_id(interface, 'iface-id', iface_id)
|
||||||
|
self._ovs_set_if_external_id(interface, 'xs-vif-uuid', vif_uuid)
|
||||||
|
self._ovs_set_if_external_id(interface, 'iface-status', status)
|
||||||
|
|
||||||
|
def _exec_dom0_cmd(self, cmd, cmd_args):
|
||||||
|
args = {'cmd': cmd,
|
||||||
|
'args': cmd_args
|
||||||
|
}
|
||||||
|
self._session.call_plugin_serialized('xenhost', 'network_config', args)
|
||||||
|
@@ -333,6 +333,18 @@ class VMOps(object):
|
|||||||
if bad_volumes_callback and bad_devices:
|
if bad_volumes_callback and bad_devices:
|
||||||
bad_volumes_callback(bad_devices)
|
bad_volumes_callback(bad_devices)
|
||||||
|
|
||||||
|
# Do some operations which have to be done after start:
|
||||||
|
# e.g. The vif's interim bridge won't be created until VM starts.
|
||||||
|
# So the operations on the interim bridge have be done after
|
||||||
|
# start.
|
||||||
|
self._post_start_actions(instance)
|
||||||
|
|
||||||
|
def _post_start_actions(self, instance):
|
||||||
|
vm_ref = vm_utils.lookup(self._session, instance['name'])
|
||||||
|
vif_refs = self._session.call_xenapi("VM.get_VIFs", vm_ref)
|
||||||
|
for vif_ref in vif_refs:
|
||||||
|
self.vif_driver.post_start_actions(instance, vif_ref)
|
||||||
|
|
||||||
def _get_vdis_for_instance(self, context, instance, name_label,
|
def _get_vdis_for_instance(self, context, instance, name_label,
|
||||||
image_meta, image_type, block_device_info):
|
image_meta, image_type, block_device_info):
|
||||||
"""Create or connect to all virtual disks for this instance."""
|
"""Create or connect to all virtual disks for this instance."""
|
||||||
@@ -1920,13 +1932,20 @@ class VMOps(object):
|
|||||||
"""Creates vifs for an instance."""
|
"""Creates vifs for an instance."""
|
||||||
|
|
||||||
LOG.debug("Creating vifs", instance=instance)
|
LOG.debug("Creating vifs", instance=instance)
|
||||||
|
vif_refs = []
|
||||||
|
|
||||||
# this function raises if vm_ref is not a vm_opaque_ref
|
# this function raises if vm_ref is not a vm_opaque_ref
|
||||||
self._session.call_xenapi("VM.get_domid", vm_ref)
|
self._session.call_xenapi("VM.get_domid", vm_ref)
|
||||||
|
|
||||||
for device, vif in enumerate(network_info):
|
for device, vif in enumerate(network_info):
|
||||||
LOG.debug('Create VIF %s', vif, instance=instance)
|
LOG.debug('Create VIF %s', vif, instance=instance)
|
||||||
self.vif_driver.plug(instance, vif, vm_ref=vm_ref, device=device)
|
vif_ref = self.vif_driver.plug(instance, vif,
|
||||||
|
vm_ref=vm_ref, device=device)
|
||||||
|
vif_refs.append(vif_ref)
|
||||||
|
|
||||||
|
LOG.debug('Created the vif_refs: %(vifs)s for VM name: %(name)s',
|
||||||
|
{'vifs': vif_refs, 'name': instance['name']},
|
||||||
|
instance=instance)
|
||||||
|
|
||||||
def plug_vifs(self, instance, network_info):
|
def plug_vifs(self, instance, network_info):
|
||||||
"""Set up VIF networking on the host."""
|
"""Set up VIF networking on the host."""
|
||||||
|
@@ -30,7 +30,8 @@ import utils
|
|||||||
# 1.2 - Added support for pci passthrough devices
|
# 1.2 - Added support for pci passthrough devices
|
||||||
# 1.3 - Add vhd2 functions for doing glance operations by url
|
# 1.3 - Add vhd2 functions for doing glance operations by url
|
||||||
# 1.4 - Add support of Glance v2 api
|
# 1.4 - Add support of Glance v2 api
|
||||||
PLUGIN_VERSION = "1.4"
|
# 1.5 - Added function for network configuration on ovs bridge
|
||||||
|
PLUGIN_VERSION = "1.5"
|
||||||
|
|
||||||
|
|
||||||
def get_version(session):
|
def get_version(session):
|
||||||
|
@@ -217,6 +217,65 @@ def iptables_config(session, args):
|
|||||||
raise pluginlib.PluginError(_("Invalid iptables command"))
|
raise pluginlib.PluginError(_("Invalid iptables command"))
|
||||||
|
|
||||||
|
|
||||||
|
def _ovs_add_patch_port(args):
|
||||||
|
bridge_name = pluginlib.exists(args, 'bridge_name')
|
||||||
|
port_name = pluginlib.exists(args, 'port_name')
|
||||||
|
peer_port_name = pluginlib.exists(args, 'peer_port_name')
|
||||||
|
cmd_args = ['ovs-vsctl', '--', '--if-exists', 'del-port',
|
||||||
|
port_name, '--', 'add-port', bridge_name, port_name,
|
||||||
|
'--', 'set', 'interface', port_name,
|
||||||
|
'type=patch', 'options:peer=%s' % peer_port_name]
|
||||||
|
return _run_command(cmd_args)
|
||||||
|
|
||||||
|
|
||||||
|
def _ovs_del_port(args):
|
||||||
|
bridge_name = pluginlib.exists(args, 'bridge_name')
|
||||||
|
port_name = pluginlib.exists(args, 'port_name')
|
||||||
|
cmd_args = ['ovs-vsctl', '--', '--if-exists', 'del-port',
|
||||||
|
bridge_name, port_name]
|
||||||
|
return _run_command(cmd_args)
|
||||||
|
|
||||||
|
|
||||||
|
def _ovs_del_br(args):
|
||||||
|
bridge_name = pluginlib.exists(args, 'bridge_name')
|
||||||
|
cmd_args = ['ovs-vsctl', '--', '--if-exists',
|
||||||
|
'del-br', bridge_name]
|
||||||
|
return _run_command(cmd_args)
|
||||||
|
|
||||||
|
|
||||||
|
def _ovs_set_if_external_id(args):
|
||||||
|
interface = pluginlib.exists(args, 'interface')
|
||||||
|
extneral_id = pluginlib.exists(args, 'extneral_id')
|
||||||
|
value = pluginlib.exists(args, 'value')
|
||||||
|
cmd_args = ['ovs-vsctl', 'set', 'Interface', interface,
|
||||||
|
'external-ids:%s=%s' % (extneral_id, value)]
|
||||||
|
return _run_command(cmd_args)
|
||||||
|
|
||||||
|
|
||||||
|
ALLOWED_NETWORK_CMDS = {
|
||||||
|
# allowed cmds to config OVS bridge
|
||||||
|
'ovs_add_patch_port': _ovs_add_patch_port,
|
||||||
|
'ovs_del_port': _ovs_del_port,
|
||||||
|
'ovs_del_br': _ovs_del_br,
|
||||||
|
'ovs_set_if_external_id': _ovs_set_if_external_id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def network_config(session, args):
|
||||||
|
"""network config functions"""
|
||||||
|
cmd = pluginlib.exists(args, 'cmd')
|
||||||
|
if not isinstance(cmd, basestring):
|
||||||
|
msg = _("invalid command '%s'") % str(cmd)
|
||||||
|
raise pluginlib.PluginError(msg)
|
||||||
|
return
|
||||||
|
if cmd not in ALLOWED_NETWORK_CMDS:
|
||||||
|
msg = _("Dom0 execution of '%s' is not permitted") % cmd
|
||||||
|
raise pluginlib.PluginError(msg)
|
||||||
|
return
|
||||||
|
cmd_args = pluginlib.exists(args, 'args')
|
||||||
|
return ALLOWED_NETWORK_CMDS[cmd](cmd_args)
|
||||||
|
|
||||||
|
|
||||||
def _power_action(action, arg_dict):
|
def _power_action(action, arg_dict):
|
||||||
# Host must be disabled first
|
# Host must be disabled first
|
||||||
host_uuid = arg_dict['host_uuid']
|
host_uuid = arg_dict['host_uuid']
|
||||||
@@ -453,10 +512,12 @@ def get_pci_type(session, pci_device):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Support both serialized and non-serialized plugin approaches
|
# Support both serialized and non-serialized plugin approaches
|
||||||
_, methodname = xmlrpclib.loads(sys.argv[1])
|
_, methodname = xmlrpclib.loads(sys.argv[1])
|
||||||
if methodname in ['query_gc', 'get_pci_device_details', 'get_pci_type']:
|
if methodname in ['query_gc', 'get_pci_device_details', 'get_pci_type',
|
||||||
|
'network_config']:
|
||||||
utils.register_plugin_calls(query_gc,
|
utils.register_plugin_calls(query_gc,
|
||||||
get_pci_device_details,
|
get_pci_device_details,
|
||||||
get_pci_type)
|
get_pci_type,
|
||||||
|
network_config)
|
||||||
|
|
||||||
XenAPIPlugin.dispatch(
|
XenAPIPlugin.dispatch(
|
||||||
{"host_data": host_data,
|
{"host_data": host_data,
|
||||||
|
Reference in New Issue
Block a user