XenAPI: OVS agent updates the wrong port with Neutron
With xen hypervisor, it will create two sets of port/interface for each VM vif: One is named as tapx.y which is qemu-emulated NIC and is used when no PV drivers installed with the VM; The other one is named as vifx.0 which is the xen network frontend NIC and is used when VM has PV drivers installed. But neutron only handle one port. If the finally used port is not the one handled by neutron, the guest VM will have no network access. The solution is to change nova xenapi to add a bridge, which we call the 'interim bridge', between the VM and the OVS integration bridge. In this way it will only expose to neutron with the integration bridge's port which connected to the interim bridge. So it will fix the issue mentioned above. Closes-Bug: #1268955 Change-Id: I0cfc0284e1fcd1a6169d31a7ad410716037e5cc2
This commit is contained in:
parent
f9f46ee3af
commit
97745f2bee
@ -181,6 +181,11 @@ class VirtualInterfacePlugException(NovaException):
|
||||
msg_fmt = _("Virtual interface plugin failed")
|
||||
|
||||
|
||||
class VirtualInterfaceUnplugException(NovaException):
|
||||
msg_fmt = _("Virtual interface unplugin failed: "
|
||||
"%(reason)s")
|
||||
|
||||
|
||||
class GlanceConnectionFailed(NovaException):
|
||||
msg_fmt = _("Connection to glance host %(server)s failed: "
|
||||
"%(reason)s")
|
||||
|
@ -55,6 +55,11 @@ class ObjectsTestCase(stubs.XenAPITestBaseNoDB):
|
||||
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):
|
||||
vbd = objects.VBD(self.session)
|
||||
vbd.get_X("ref")
|
||||
|
@ -100,6 +100,10 @@ class ApplySessionHelpersTestCase(stubs.XenAPITestBaseNoDB):
|
||||
self.session.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):
|
||||
self.session.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.network import model
|
||||
from nova import test
|
||||
from nova.tests.unit.virt.xenapi import stubs
|
||||
from nova.virt.xenapi import network_utils
|
||||
from nova.virt.xenapi import vif
|
||||
@ -67,6 +68,22 @@ class XenVIFDriverTestBase(stubs.XenAPITestBaseNoDB):
|
||||
self._session = mock.Mock()
|
||||
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):
|
||||
def setUp(self):
|
||||
@ -149,41 +166,126 @@ class XenAPIBridgeDriverTestCase(XenVIFDriverTestBase, object):
|
||||
ret_vif_ref = self.bridge_driver.plug(instance, vif, vm_ref, device)
|
||||
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):
|
||||
def setUp(self):
|
||||
super(XenAPIOpenVswitchDriverTestCase, self).setUp()
|
||||
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',
|
||||
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)
|
||||
def test_plug(self, mock_get_vif_ref, mock_create_vif,
|
||||
mock_find_network_with_bridge):
|
||||
@mock.patch.object(vif.vm_utils, 'lookup', return_value='fake_vm_ref')
|
||||
def test_plug(self, mock_lookup, mock_get_vif_ref,
|
||||
mock_create_vif_interim_network,
|
||||
mock_create_vif):
|
||||
instance = {'name': "fake_instance_name"}
|
||||
vm_ref = "fake_vm_ref"
|
||||
device = 1
|
||||
ret_vif_ref = self.ovs_driver.plug(instance, fake_vif, vm_ref, device)
|
||||
ret_vif_ref = self.ovs_driver.plug(
|
||||
instance, fake_vif, vm_ref=None, device=1)
|
||||
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)
|
||||
|
||||
@mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
|
||||
return_value='fake_vif_ref')
|
||||
def test_unplug(self, mock_get_vif_ref):
|
||||
@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(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"
|
||||
|
||||
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)
|
||||
|
||||
expected = [mock.call('VIF.destroy', 'fake_vif_ref')]
|
||||
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
|
||||
self.assertTrue(mock_super_unplug.called)
|
||||
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")
|
||||
|
||||
|
||||
class VIF(XenAPISessionObject):
|
||||
"""Virtual Network Interface."""
|
||||
def __init__(self, session):
|
||||
super(VIF, self).__init__(session, "VIF")
|
||||
|
||||
|
||||
class SR(XenAPISessionObject):
|
||||
"""Storage Repository."""
|
||||
def __init__(self, session):
|
||||
|
@ -53,6 +53,7 @@ def apply_session_helpers(session):
|
||||
session.VM = cli_objects.VM(session)
|
||||
session.SR = cli_objects.SR(session)
|
||||
session.VDI = cli_objects.VDI(session)
|
||||
session.VIF = cli_objects.VIF(session)
|
||||
session.VBD = cli_objects.VBD(session)
|
||||
session.PBD = cli_objects.PBD(session)
|
||||
session.PIF = cli_objects.PIF(session)
|
||||
@ -69,7 +70,7 @@ class XenAPISession(object):
|
||||
# changed in development environments.
|
||||
# MAJOR VERSION: Incompatible changes with the plugins
|
||||
# MINOR VERSION: Compatible changes, new plguins, etc
|
||||
PLUGIN_REQUIRED_VERSION = '1.4'
|
||||
PLUGIN_REQUIRED_VERSION = '1.5'
|
||||
|
||||
def __init__(self, url, user, pw):
|
||||
version_string = version.version_string_with_package()
|
||||
|
@ -760,7 +760,7 @@ class SessionBase(object):
|
||||
return base64.b64encode(zlib.compress("dom_id: %s" % dom_id))
|
||||
|
||||
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):
|
||||
return pickle.dumps("False")
|
||||
|
@ -23,6 +23,7 @@ import nova.conf
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
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 vm_utils
|
||||
|
||||
@ -180,11 +181,18 @@ class XenAPIBridgeDriver(XenVIFDriver):
|
||||
def unplug(self, 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):
|
||||
"""VIF driver for Open vSwitch with XenAPI."""
|
||||
|
||||
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:
|
||||
vm_ref = vm_utils.lookup(self._session, instance['name'])
|
||||
|
||||
@ -198,10 +206,10 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
if not device:
|
||||
device = 0
|
||||
|
||||
# with OVS model, always plug into an OVS integration bridge
|
||||
# that is already created
|
||||
network_ref = network_utils.find_network_with_bridge(
|
||||
self._session, CONF.xenserver.ovs_integration_bridge)
|
||||
# Create an interim network for each VIF, so dom0 has a single
|
||||
# bridge for each device (the emulated and PV ethernet devices
|
||||
# will both be on this bridge.
|
||||
network_ref = self.create_vif_interim_network(vif)
|
||||
vif_rec = {}
|
||||
vif_rec['device'] = str(device)
|
||||
vif_rec['network'] = network_ref
|
||||
@ -216,4 +224,157 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
|
||||
return self._create_vif(vif, vif_rec, 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)
|
||||
|
||||
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:
|
||||
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,
|
||||
image_meta, image_type, block_device_info):
|
||||
"""Create or connect to all virtual disks for this instance."""
|
||||
@ -1920,13 +1932,20 @@ class VMOps(object):
|
||||
"""Creates vifs for an instance."""
|
||||
|
||||
LOG.debug("Creating vifs", instance=instance)
|
||||
vif_refs = []
|
||||
|
||||
# this function raises if vm_ref is not a vm_opaque_ref
|
||||
self._session.call_xenapi("VM.get_domid", vm_ref)
|
||||
|
||||
for device, vif in enumerate(network_info):
|
||||
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):
|
||||
"""Set up VIF networking on the host."""
|
||||
|
@ -30,7 +30,8 @@ import utils
|
||||
# 1.2 - Added support for pci passthrough devices
|
||||
# 1.3 - Add vhd2 functions for doing glance operations by url
|
||||
# 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):
|
||||
|
@ -217,6 +217,65 @@ def iptables_config(session, args):
|
||||
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):
|
||||
# Host must be disabled first
|
||||
host_uuid = arg_dict['host_uuid']
|
||||
@ -453,10 +512,12 @@ def get_pci_type(session, pci_device):
|
||||
if __name__ == "__main__":
|
||||
# Support both serialized and non-serialized plugin approaches
|
||||
_, 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,
|
||||
get_pci_device_details,
|
||||
get_pci_type)
|
||||
get_pci_type,
|
||||
network_config)
|
||||
|
||||
XenAPIPlugin.dispatch(
|
||||
{"host_data": host_data,
|
||||
|
Loading…
Reference in New Issue
Block a user