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:
parent
89fd32394f
commit
0e3372ac2e
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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."""
|
||||
|
|
Loading…
Reference in New Issue