fuel-plugin-xenserver/plugin_source/deployment_scripts/patchset/ovs-interim-bridge.patch

295 lines
12 KiB
Diff

diff --git a/nova/exception.py b/nova/exception.py
index 40b82bf..f9cd12f 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -187,6 +187,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/virt/xenapi/client/objects.py b/nova/virt/xenapi/client/objects.py
index 5cc91eb..7f7215c 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 8f277ff..70e9bec 100644
--- a/nova/virt/xenapi/client/session.py
+++ b/nova/virt/xenapi/client/session.py
@@ -65,6 +65,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)
diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py
index 5c7a350..b9660be 100644
--- a/nova/virt/xenapi/vif.py
+++ b/nova/virt/xenapi/vif.py
@@ -23,6 +23,7 @@ from oslo_log import log as logging
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
@@ -185,11 +186,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'])
@@ -203,10 +211,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
@@ -221,4 +229,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 ab9368c..51d9627 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -348,6 +348,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."""
@@ -1935,13 +1947,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."""