XenAPI: Fix VIF plug and unplug problem

When vif.plug is called, XenAPI's vif plug does not plug a device,
but returns a dictionary of the vif record. This is not vif.plug is
expected to do. This fix will create a new VIF when vif_driver.plug
is called and destroy this VIF when vif_driver.unplug is called

Change-Id: I1eb4cc3b1c44662fb3063a854880ebf79fcf6942
Closes-Bug: #1521977
This commit is contained in:
Huan Xie 2015-12-07 06:12:31 +00:00
parent 89fd32394f
commit 0e3372ac2e
3 changed files with 264 additions and 20 deletions

View File

@ -0,0 +1,189 @@
# Copyright 2013 OpenStack Foundation
# 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 mock
from nova import exception
from nova.network import model
from nova.tests.unit.virt.xenapi import stubs
from nova.virt.xenapi import network_utils
from nova.virt.xenapi import vif
fake_vif = {
'created_at': None,
'updated_at': None,
'deleted_at': None,
'deleted': 0,
'id': '123456789123',
'address': '00:00:00:00:00:00',
'network_id': 123,
'instance_uuid': 'fake-uuid',
'uuid': 'fake-uuid-2',
}
def fake_call_xenapi(method, *args):
if method == "VM.get_VIFs":
return ["fake_vif_ref", "fake_vif_ref_A2"]
if method == "VIF.get_record":
if args[0] == "fake_vif_ref":
return {'uuid': fake_vif['uuid'],
'MAC': fake_vif['address'],
'network': 'fake_network',
'other_config': {'nicira-iface-id': fake_vif['id']}
}
else:
raise exception.Exception("Failed get vif record")
if method == "VIF.unplug":
return
if method == "VIF.destroy":
if args[0] == "fake_vif_ref":
return
else:
raise exception.Exception("unplug vif failed")
if method == "VIF.create":
if args[0] == "fake_vif_rec":
return "fake_vif_ref"
else:
raise exception.Exception("VIF existed")
return "Unexpected call_xenapi: %s.%s" % (method, args)
class XenVIFDriverTestBase(stubs.XenAPITestBaseNoDB):
def setUp(self):
super(XenVIFDriverTestBase, self).setUp()
self._session = mock.Mock()
self._session.call_xenapi.side_effect = fake_call_xenapi
class XenVIFDriverTestCase(XenVIFDriverTestBase):
def setUp(self):
super(XenVIFDriverTestCase, self).setUp()
self.base_driver = vif.XenVIFDriver(self._session)
def test_get_vif_ref(self):
vm_ref = "fake_vm_ref"
vif_ref = 'fake_vif_ref'
ret_vif_ref = self.base_driver._get_vif_ref(fake_vif, vm_ref)
self.assertEqual(vif_ref, ret_vif_ref)
expected = [mock.call('VM.get_VIFs', vm_ref),
mock.call('VIF.get_record', vif_ref)]
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
def test_get_vif_ref_none_and_exception(self):
vm_ref = "fake_vm_ref"
vif = {'address': "no_match_vif_address"}
ret_vif_ref = self.base_driver._get_vif_ref(vif, vm_ref)
self.assertIsNone(ret_vif_ref)
expected = [mock.call('VM.get_VIFs', vm_ref),
mock.call('VIF.get_record', 'fake_vif_ref'),
mock.call('VIF.get_record', 'fake_vif_ref_A2')]
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
def test_create_vif(self):
vif_rec = "fake_vif_rec"
vm_ref = "fake_vm_ref"
ret_vif_ref = self.base_driver._create_vif(fake_vif, vif_rec, vm_ref)
self.assertEqual("fake_vif_ref", ret_vif_ref)
expected = [mock.call('VIF.create', vif_rec)]
self.assertEqual(expected, self._session.call_xenapi.call_args_list)
def test_create_vif_exception(self):
self.assertRaises(exception.NovaException,
self.base_driver._create_vif,
"fake_vif", "missing_vif_rec", "fake_vm_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.base_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)
@mock.patch.object(vif.XenVIFDriver, '_get_vif_ref',
return_value='missing_vif_ref')
def test_unplug_exception(self, mock_get_vif_ref):
instance = "fake_instance"
vm_ref = "fake_vm_ref"
self.assertRaises(exception.NovaException,
self.base_driver.unplug,
instance, fake_vif, vm_ref)
class XenAPIBridgeDriverTestCase(XenVIFDriverTestBase, object):
def setUp(self):
super(XenAPIBridgeDriverTestCase, self).setUp()
self.bridge_driver = vif.XenAPIBridgeDriver(self._session)
@mock.patch.object(vif.XenAPIBridgeDriver, '_ensure_vlan_bridge',
return_value='fake_network_ref')
@mock.patch.object(vif.XenVIFDriver, '_create_vif',
return_value='fake_vif_ref')
def test_plug_create_vlan(self, mock_create_vif, mock_ensure_vlan_bridge):
instance = {'name': "fake_instance_name"}
network = model.Network()
network._set_meta({'should_create_vlan': True})
vif = model.VIF()
vif._set_meta({'rxtx_cap': 1})
vif['network'] = network
vif['address'] = "fake_address"
vm_ref = "fake_vm_ref"
device = 1
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.XenVIFDriver, '_get_vif_ref', return_value=None)
def test_plug(self, mock_get_vif_ref, mock_create_vif,
mock_find_network_with_bridge):
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)
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.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)

View File

@ -18,8 +18,11 @@
"""VIF drivers for XenAPI."""
from oslo_config import cfg
from oslo_log import log as logging
from nova import exception
from nova.i18n import _
from nova.i18n import _LW
from nova.virt.xenapi import network_utils
from nova.virt.xenapi import vm_utils
@ -31,11 +34,56 @@ xenapi_ovs_integration_bridge_opt = cfg.StrOpt('ovs_integration_bridge',
CONF = cfg.CONF
CONF.register_opt(xenapi_ovs_integration_bridge_opt, 'xenserver')
LOG = logging.getLogger(__name__)
class XenVIFDriver(object):
def __init__(self, xenapi_session):
self._session = xenapi_session
def _get_vif_ref(self, vif, vm_ref):
vif_refs = self._session.call_xenapi("VM.get_VIFs", vm_ref)
for vif_ref in vif_refs:
try:
vif_rec = self._session.call_xenapi('VIF.get_record', vif_ref)
if vif_rec['MAC'] == vif['address']:
return vif_ref
except Exception:
# When got exception here, maybe the vif is removed during the
# loop, ignore this vif and continue
continue
return None
def _create_vif(self, vif, vif_rec, vm_ref):
try:
vif_ref = self._session.call_xenapi('VIF.create', vif_rec)
except Exception as e:
LOG.warn(_LW("Failed to create vif, exception:%(exception)s, "
"vif:%(vif)s"), {'exception': e, 'vif': vif})
raise exception.NovaException(
reason=_("Failed to create vif %s") % vif)
LOG.debug("create vif %(vif)s for vm %(vm_ref)s successfully",
{'vif': vif, 'vm_ref': vm_ref})
return vif_ref
def unplug(self, instance, vif, vm_ref):
try:
LOG.debug("unplug vif, vif:%(vif)s, vm_ref:%(vm_ref)s",
{'vif': vif, 'vm_ref': vm_ref}, instance=instance)
vif_ref = self._get_vif_ref(vif, vm_ref)
if not vif_ref:
LOG.debug("vif didn't exist, no need to unplug vif %s",
vif, instance=instance)
return
self._session.call_xenapi('VIF.destroy', vif_ref)
except Exception as e:
LOG.warn(
_LW("Fail to unplug vif:%(vif)s, exception:%(exception)s"),
{'vif': vif, 'exception': e}, instance=instance)
raise exception.NovaException(
reason=_("Failed to unplug vif %s") % vif)
class XenAPIBridgeDriver(XenVIFDriver):
"""VIF Driver for XenAPI that uses XenAPI to create Networks."""
@ -43,6 +91,14 @@ class XenAPIBridgeDriver(XenVIFDriver):
def plug(self, instance, vif, vm_ref=None, device=None):
if not vm_ref:
vm_ref = vm_utils.lookup(self._session, instance['name'])
# if VIF already exists, return this vif_ref directly
vif_ref = self._get_vif_ref(vif, vm_ref)
if vif_ref:
LOG.debug("VIF %s already exists when plug vif",
vif_ref, instance=instance)
return vif_ref
if not device:
device = 0
@ -65,7 +121,7 @@ class XenAPIBridgeDriver(XenVIFDriver):
else:
vif_rec['qos_algorithm_type'] = ''
vif_rec['qos_algorithm_params'] = {}
return vif_rec
return self._create_vif(vif, vif_rec, vm_ref)
def _ensure_vlan_bridge(self, network):
"""Ensure that a VLAN bridge exists."""
@ -126,8 +182,8 @@ class XenAPIBridgeDriver(XenVIFDriver):
return network_ref
def unplug(self, instance, vif):
pass
def unplug(self, instance, vif, vm_ref):
super(XenAPIBridgeDriver, self).unplug(instance, vif, vm_ref)
class XenAPIOpenVswitchDriver(XenVIFDriver):
@ -137,6 +193,13 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
if not vm_ref:
vm_ref = vm_utils.lookup(self._session, instance['name'])
# if VIF already exists, return this vif_ref directly
vif_ref = self._get_vif_ref(vif, vm_ref)
if vif_ref:
LOG.debug("VIF %s already exists when plug vif",
vif_ref, instance=instance)
return vif_ref
if not device:
device = 0
@ -155,7 +218,7 @@ class XenAPIOpenVswitchDriver(XenVIFDriver):
# OVS on the hypervisor monitors this key and uses it to
# set the iface-id attribute
vif_rec['other_config'] = {'nicira-iface-id': vif['id']}
return vif_rec
return self._create_vif(vif, vif_rec, vm_ref)
def unplug(self, instance, vif):
pass
def unplug(self, instance, vif, vm_ref):
super(XenAPIOpenVswitchDriver, self).unplug(instance, vif, vm_ref)

View File

@ -1576,11 +1576,10 @@ class VMOps(object):
self._destroy_vdis(instance, vm_ref)
self._destroy_kernel_ramdisk(instance, vm_ref)
vm_utils.destroy_vm(self._session, instance, vm_ref)
self.unplug_vifs(instance, network_info)
self.unplug_vifs(instance, network_info, vm_ref)
self.firewall_driver.unfilter_instance(
instance, network_info=network_info)
vm_utils.destroy_vm(self._session, instance, vm_ref)
def pause(self, instance):
"""Pause VM instance."""
@ -1896,25 +1895,18 @@ class VMOps(object):
self._session.call_xenapi("VM.get_domid", vm_ref)
for device, vif in enumerate(network_info):
vif_rec = self.vif_driver.plug(instance, vif,
vm_ref=vm_ref, device=device)
network_ref = vif_rec['network']
LOG.debug('Creating VIF for network %s',
network_ref, instance=instance)
vif_ref = self._session.call_xenapi('VIF.create', vif_rec)
LOG.debug('Created VIF %(vif_ref)s, network %(network_ref)s',
{'vif_ref': vif_ref, 'network_ref': network_ref},
instance=instance)
LOG.debug('Create VIF %s', vif, instance=instance)
self.vif_driver.plug(instance, vif, vm_ref=vm_ref, device=device)
def plug_vifs(self, instance, network_info):
"""Set up VIF networking on the host."""
for device, vif in enumerate(network_info):
self.vif_driver.plug(instance, vif, device=device)
def unplug_vifs(self, instance, network_info):
def unplug_vifs(self, instance, network_info, vm_ref):
if network_info:
for vif in network_info:
self.vif_driver.unplug(instance, vif)
self.vif_driver.unplug(instance, vif, vm_ref)
def reset_network(self, instance, rescue=False):
"""Calls resetnetwork method in agent."""