diff --git a/nova/exception.py b/nova/exception.py index b591cff19f3b..ec4621967c0b 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -135,6 +135,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") diff --git a/nova/tests/unit/virt/xenapi/client/test_objects.py b/nova/tests/unit/virt/xenapi/client/test_objects.py index efaf17a9c7db..6730745df92c 100644 --- a/nova/tests/unit/virt/xenapi/client/test_objects.py +++ b/nova/tests/unit/virt/xenapi/client/test_objects.py @@ -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") diff --git a/nova/tests/unit/virt/xenapi/client/test_session.py b/nova/tests/unit/virt/xenapi/client/test_session.py index 16c352feacb3..f9b490eca974 100644 --- a/nova/tests/unit/virt/xenapi/client/test_session.py +++ b/nova/tests/unit/virt/xenapi/client/test_session.py @@ -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") diff --git a/nova/tests/unit/virt/xenapi/test_vif.py b/nova/tests/unit/virt/xenapi/test_vif.py index a41e50645164..ebd3a9a85cc1 100644 --- a/nova/tests/unit/virt/xenapi/test_vif.py +++ b/nova/tests/unit/virt/xenapi/test_vif.py @@ -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') diff --git a/nova/virt/xenapi/client/objects.py b/nova/virt/xenapi/client/objects.py index 5cc91eb4c72d..7f7215c047c7 100644 --- a/nova/virt/xenapi/client/objects.py +++ b/nova/virt/xenapi/client/objects.py @@ -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): diff --git a/nova/virt/xenapi/client/session.py b/nova/virt/xenapi/client/session.py index cc5dd3924867..ada1b7b927cb 100644 --- a/nova/virt/xenapi/client/session.py +++ b/nova/virt/xenapi/client/session.py @@ -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() diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 03c4ee06d0ec..69726eedcd8a 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -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") diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py index f6717360265a..79d27fcb56a7 100644 --- a/nova/virt/xenapi/vif.py +++ b/nova/virt/xenapi/vif.py @@ -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) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 18765e1f262e..3d3fd4ce7b48 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -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.""" diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version b/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version index bec3a96f88a5..e82a41396a7a 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version @@ -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): diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost index 7417a3cd46d0..ed0ff0fd13de 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost @@ -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,