Merge "Plug updated VIFs"
This commit is contained in:
commit
9d246c9230
@ -114,10 +114,16 @@ class TestNetwork(test.TestCase):
|
||||
# Run method
|
||||
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
|
||||
'host_uuid', 'slot_mgr')
|
||||
|
||||
p_vifs.execute(self.mock_lpar_wrap)
|
||||
|
||||
# The create should have only been called once.
|
||||
self.assertEqual(2, mock_plug.call_count)
|
||||
# new vif should be created twice.
|
||||
mock_plug.assert_any_call(self.apt, 'host_uuid', inst, net_info[0],
|
||||
'slot_mgr', new_vif=False)
|
||||
mock_plug.assert_any_call(self.apt, 'host_uuid', inst, net_info[1],
|
||||
'slot_mgr', new_vif=True)
|
||||
mock_plug.assert_any_call(self.apt, 'host_uuid', inst, net_info[2],
|
||||
'slot_mgr', new_vif=True)
|
||||
|
||||
@mock.patch('nova_powervm.virt.powervm.vif.plug')
|
||||
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
|
||||
@ -137,16 +143,12 @@ class TestNetwork(test.TestCase):
|
||||
# Run method
|
||||
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
|
||||
'host_uuid', 'slot_mgr')
|
||||
resp = p_vifs.execute(self.mock_lpar_wrap)
|
||||
p_vifs.execute(self.mock_lpar_wrap)
|
||||
|
||||
# The create should not have been called. The response should have
|
||||
# been empty.
|
||||
self.assertEqual(0, mock_plug.call_count)
|
||||
self.assertEqual([], resp)
|
||||
|
||||
# State check shouldn't have even been invoked as no creates were
|
||||
# required
|
||||
self.assertEqual(0, self.mock_lpar_wrap.can_modify_io.call_count)
|
||||
# The create should have been called with new_vif as False.
|
||||
mock_plug.assert_called_with(
|
||||
self.apt, 'host_uuid', inst, net_info[1],
|
||||
'slot_mgr', new_vif=False)
|
||||
|
||||
@mock.patch('nova_powervm.virt.powervm.vif.plug')
|
||||
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
|
||||
@ -257,8 +259,9 @@ class TestNetwork(test.TestCase):
|
||||
self.assertEqual('host1', inst.host)
|
||||
|
||||
@mock.patch('nova_powervm.virt.powervm.vif.unplug')
|
||||
@mock.patch('nova_powervm.virt.powervm.vif.plug')
|
||||
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
|
||||
def test_plug_vifs_revert(self, mock_vm_get, mock_unplug):
|
||||
def test_plug_vifs_revert(self, mock_vm_get, mock_plug, mock_unplug):
|
||||
"""Tests that the revert flow works properly."""
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
|
||||
@ -279,6 +282,7 @@ class TestNetwork(test.TestCase):
|
||||
# Run method
|
||||
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
|
||||
'host_uuid', 'slot_mgr')
|
||||
p_vifs.execute(self.mock_lpar_wrap)
|
||||
p_vifs.revert(self.mock_lpar_wrap, mock.Mock(), mock.Mock())
|
||||
|
||||
# The unplug should be called three times. The exception shouldn't
|
||||
|
@ -1556,9 +1556,9 @@ class TestPowerVMDriver(test.TestCase):
|
||||
|
||||
# The create should have only been called once. The other was already
|
||||
# existing.
|
||||
mock_plug_vif.assert_called_once_with(
|
||||
mock_plug_vif.assert_called_with(
|
||||
self.drv.adapter, self.drv.host_uuid, self.inst, net_info[1],
|
||||
mock_bld_slot_mgr.return_value)
|
||||
mock_bld_slot_mgr.return_value, new_vif=True)
|
||||
mock_bld_slot_mgr.return_value.save.assert_called_once_with()
|
||||
self.assertEqual(0, mock_plug_rmc_vif.call_count)
|
||||
|
||||
|
@ -160,6 +160,19 @@ class TestVifSeaDriver(test.TestCase):
|
||||
self.assertIsNotNone(resp)
|
||||
self.assertIsInstance(resp, pvm_net.CNA)
|
||||
|
||||
def test_plug_existing_vif(self):
|
||||
"""Tests that a VIF need not be created."""
|
||||
|
||||
# Set up the mocks
|
||||
fake_vif = {'network': {'meta': {'vlan': 5}},
|
||||
'address': 'aabbccddeeff'}
|
||||
fake_slot_num = 5
|
||||
|
||||
# Invoke
|
||||
resp = self.drv.plug(fake_vif, fake_slot_num, new_vif=False)
|
||||
|
||||
self.assertIsNone(resp)
|
||||
|
||||
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
|
||||
def test_unplug_vifs(self, mock_vm_get):
|
||||
"""Tests that a delete of the vif can be done."""
|
||||
@ -235,6 +248,19 @@ class TestVifLBDriver(test.TestCase):
|
||||
run_as_root=True)
|
||||
mock_ensure_bridge.assert_called_once_with('br0', 'tap_dev')
|
||||
|
||||
@mock.patch('nova.network.linux_net.LinuxBridgeInterfaceDriver.'
|
||||
'ensure_bridge')
|
||||
def test_plug_existing_vif(
|
||||
self, mock_ensure_bridge):
|
||||
|
||||
# Run the plug
|
||||
vif = {'network': {'bridge': 'br0'}, 'address': 'aa:bb:cc:dd:ee:ff',
|
||||
'id': 'vif_id', 'devname': 'tap_dev'}
|
||||
resp = self.drv.plug(vif, 6, new_vif=False)
|
||||
|
||||
mock_ensure_bridge.assert_called_once_with('br0', 'tap_dev')
|
||||
self.assertIsNone(resp)
|
||||
|
||||
@mock.patch('nova.utils.execute')
|
||||
@mock.patch('pypowervm.tasks.cna.find_trunks')
|
||||
@mock.patch('nova_powervm.virt.powervm.vif.PvmLBVifDriver.'
|
||||
@ -331,6 +357,27 @@ class TestVifOvsDriver(test.TestCase):
|
||||
mock_exec.assert_called_with('ip', 'link', 'set', 'device', 'up',
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch('nova.utils.execute')
|
||||
@mock.patch('nova.network.linux_net.create_ovs_vif_port')
|
||||
@mock.patch('nova_powervm.virt.powervm.vif.PvmOvsVifDriver.'
|
||||
'get_trunk_dev_name')
|
||||
def test_plug_existing_vif(self, mock_trunk_dev_name,
|
||||
mock_crt_ovs_vif_port, mock_exec):
|
||||
# Mock the data
|
||||
mock_trunk_dev_name.return_value = 'device'
|
||||
# Run the plug
|
||||
mock_vif = {'network': {'bridge': 'br0'},
|
||||
'address': 'aa:bb:cc:dd:ee:ff', 'id': 'vif_id'}
|
||||
slot_num = 5
|
||||
resp = self.drv.plug(mock_vif, slot_num, new_vif=False)
|
||||
|
||||
# Validate the calls
|
||||
mock_crt_ovs_vif_port.assert_called_once_with(
|
||||
'br0', 'device', 'vif_id', 'aa:bb:cc:dd:ee:ff', 'inst_uuid')
|
||||
mock_exec.assert_called_with('ip', 'link', 'set', 'device', 'up',
|
||||
run_as_root=True)
|
||||
self.assertIsNone(resp)
|
||||
|
||||
def test_get_trunk_dev_name(self):
|
||||
mock_vif = {'devname': 'tap_test', 'id': '1234567890123456'}
|
||||
|
||||
|
@ -113,6 +113,7 @@ class PlugVifs(pvm_task.PowerVMTask):
|
||||
self.network_infos = network_infos
|
||||
self.host_uuid = host_uuid
|
||||
self.slot_mgr = slot_mgr
|
||||
self.crt_network_infos, self.update_network_infos = [], []
|
||||
|
||||
super(PlugVifs, self).__init__(
|
||||
instance, 'plug_vifs', provides='vm_cnas', requires=['lpar_wrap'])
|
||||
@ -121,22 +122,23 @@ class PlugVifs(pvm_task.PowerVMTask):
|
||||
# Get the current adapters on the system
|
||||
cna_w_list = vm.get_cnas(self.adapter, self.instance)
|
||||
|
||||
# Trim the VIFs down to the ones that haven't yet been created.
|
||||
crt_network_infos = []
|
||||
# We will have two types of network infos. One is for newly created
|
||||
# vifs. The others are those that exist, but should be re-'treated'
|
||||
for network_info in self.network_infos:
|
||||
for cna_w in cna_w_list:
|
||||
if vm.norm_mac(cna_w.mac) == network_info['address']:
|
||||
self.update_network_infos.append(network_info)
|
||||
break
|
||||
else:
|
||||
crt_network_infos.append(network_info)
|
||||
self.crt_network_infos.append(network_info)
|
||||
|
||||
# If there are no vifs to create, then just exit immediately.
|
||||
if len(crt_network_infos) == 0:
|
||||
# If there are no vifs to create or update, then just exit immediately.
|
||||
if not self.crt_network_infos and not self.update_network_infos:
|
||||
return []
|
||||
|
||||
# Check to see if the LPAR is OK to add VIFs to.
|
||||
modifiable, reason = lpar_wrap.can_modify_io()
|
||||
if not modifiable:
|
||||
if not modifiable and self.crt_network_infos:
|
||||
LOG.error(_LE('Unable to create VIF(s) for instance %(sys)s. The '
|
||||
'VM was in a state where VIF plugging is not '
|
||||
'acceptable. The reason from the system is: '
|
||||
@ -161,20 +163,30 @@ class PlugVifs(pvm_task.PowerVMTask):
|
||||
self.instance.save()
|
||||
undo_host_change = True
|
||||
|
||||
# For existing VIFs that we just need to update, run the plug but do
|
||||
# not wait for the neutron event as that likely won't be sent (it was
|
||||
# already done).
|
||||
for network_info in self.update_network_infos:
|
||||
LOG.info(_LI("Updating VIF with mac %(mac)s for instance %(sys)s"),
|
||||
{'mac': network_info['address'],
|
||||
'sys': self.instance.name}, instance=self.instance)
|
||||
vif.plug(self.adapter, self.host_uuid, self.instance,
|
||||
network_info, self.slot_mgr, new_vif=False)
|
||||
|
||||
# For the VIFs, run the creates (and wait for the events back)
|
||||
try:
|
||||
with self.virt_api.wait_for_instance_event(
|
||||
self.instance, self._get_vif_events(),
|
||||
deadline=CONF.vif_plugging_timeout,
|
||||
error_callback=self._vif_callback_failed):
|
||||
for network_info in crt_network_infos:
|
||||
for network_info in self.crt_network_infos:
|
||||
LOG.info(_LI('Creating VIF with mac %(mac)s for instance '
|
||||
'%(sys)s'),
|
||||
{'mac': network_info['address'],
|
||||
'sys': self.instance.name},
|
||||
instance=self.instance)
|
||||
vif.plug(self.adapter, self.host_uuid, self.instance,
|
||||
network_info, self.slot_mgr)
|
||||
network_info, self.slot_mgr, new_vif=True)
|
||||
except eventlet.timeout.Timeout:
|
||||
LOG.error(_LE('Error waiting for VIF to be created for instance '
|
||||
'%(sys)s'), {'sys': self.instance.name},
|
||||
@ -228,7 +240,7 @@ class PlugVifs(pvm_task.PowerVMTask):
|
||||
|
||||
# Get the current adapters on the system
|
||||
cna_w_list = vm.get_cnas(self.adapter, self.instance)
|
||||
for network_info in self.network_infos:
|
||||
for network_info in self.crt_network_infos:
|
||||
try:
|
||||
vif.unplug(self.adapter, self.host_uuid, self.instance,
|
||||
network_info, self.slot_mgr, cna_w_list=cna_w_list)
|
||||
|
@ -83,7 +83,7 @@ def _build_vif_driver(adapter, host_uuid, instance, vif):
|
||||
{'vif_type': vif['type'], 'instance': instance.name})
|
||||
|
||||
|
||||
def plug(adapter, host_uuid, instance, vif, slot_mgr):
|
||||
def plug(adapter, host_uuid, instance, vif, slot_mgr, new_vif=True):
|
||||
"""Plugs a virtual interface (network) into a VM.
|
||||
|
||||
:param adapter: The pypowervm adapter.
|
||||
@ -92,6 +92,10 @@ def plug(adapter, host_uuid, instance, vif, slot_mgr):
|
||||
:param vif: The virtual interface to plug into the instance.
|
||||
:param slot_mgr: A NovaSlotManager. Used to store/retrieve the client
|
||||
slots used when a VIF is attached to the VM.
|
||||
:param new_vif: (Optional, Default: True) If set, indicates that it is
|
||||
a brand new VIF. If False, it indicates that the VIF
|
||||
is already on the client but should be treated on the
|
||||
bridge.
|
||||
"""
|
||||
vif_drv = _build_vif_driver(adapter, host_uuid, instance, vif)
|
||||
|
||||
@ -100,11 +104,11 @@ def plug(adapter, host_uuid, instance, vif, slot_mgr):
|
||||
slot_num = slot_mgr.build_map.get_vea_slot(vif['address'])
|
||||
|
||||
# Invoke the plug
|
||||
cna_w = vif_drv.plug(vif, slot_num)
|
||||
cna_w = vif_drv.plug(vif, slot_num, new_vif=new_vif)
|
||||
|
||||
# If the slot number hadn't been provided initially, save it for the
|
||||
# next rebuild
|
||||
if not slot_num:
|
||||
if not slot_num and new_vif:
|
||||
slot_mgr.register_cna(cna_w)
|
||||
|
||||
|
||||
@ -216,11 +220,17 @@ class PvmVifDriver(object):
|
||||
self.instance = instance
|
||||
|
||||
@abc.abstractmethod
|
||||
def plug(self, vif, slot_num):
|
||||
def plug(self, vif, slot_num, new_vif=True):
|
||||
"""Plugs a virtual interface (network) into a VM.
|
||||
|
||||
:param vif: The virtual interface to plug into the instance.
|
||||
:param slot_num: Which slot number to plug the VIF into. May be None.
|
||||
:param new_vif: (Optional, Default: True) If set, indicates that it is
|
||||
a brand new VIF. If False, it indicates that the VIF
|
||||
is already on the client but should be treated on the
|
||||
bridge.
|
||||
:return: The new vif that was created. Only returned if new_vif is
|
||||
set to True. Otherwise None is expected.
|
||||
"""
|
||||
pass
|
||||
|
||||
@ -297,14 +307,24 @@ class PvmVifDriver(object):
|
||||
class PvmSeaVifDriver(PvmVifDriver):
|
||||
"""The PowerVM Shared Ethernet Adapter VIF Driver."""
|
||||
|
||||
def plug(self, vif, slot_num):
|
||||
def plug(self, vif, slot_num, new_vif=True):
|
||||
"""Plugs a virtual interface (network) into a VM.
|
||||
|
||||
This method simply creates the client network adapter into the VM.
|
||||
|
||||
:param vif: The virtual interface to plug into the instance.
|
||||
:param slot_num: Which slot number to plug the VIF into. May be None.
|
||||
:param new_vif: (Optional, Default: True) If set, indicates that it is
|
||||
a brand new VIF. If False, it indicates that the VIF
|
||||
is already on the client but should be treated on the
|
||||
bridge.
|
||||
:return: The new vif that was created. Only returned if new_vif is
|
||||
set to True. Otherwise None is expected.
|
||||
"""
|
||||
# Do nothing if not a new VIF
|
||||
if not new_vif:
|
||||
return None
|
||||
|
||||
lpar_uuid = vm.get_pvm_uuid(self.instance)
|
||||
|
||||
# CNA's require a VLAN. If the network doesn't provide, default to 1
|
||||
@ -333,7 +353,7 @@ class PvmLioVifDriver(PvmVifDriver):
|
||||
return vif['devname']
|
||||
return ("nic" + vif['id'])[:network_model.NIC_NAME_LEN]
|
||||
|
||||
def plug(self, vif, slot_num):
|
||||
def plug(self, vif, slot_num, new_vif=True):
|
||||
"""Plugs a virtual interface (network) into a VM.
|
||||
|
||||
Creates a 'peer to peer' connection between the Management partition
|
||||
@ -344,16 +364,28 @@ class PvmLioVifDriver(PvmVifDriver):
|
||||
|
||||
:param vif: The virtual interface to plug into the instance.
|
||||
:param slot_num: Which slot number to plug the VIF into. May be None.
|
||||
:param new_vif: (Optional, Default: True) If set, indicates that it is
|
||||
a brand new VIF. If False, it indicates that the VIF
|
||||
is already on the client but should be treated on the
|
||||
bridge.
|
||||
:return: The new vif that was created. Only returned if new_vif is
|
||||
set to True. Otherwise None is expected.
|
||||
"""
|
||||
dev_name = self.get_trunk_dev_name(vif)
|
||||
|
||||
if new_vif:
|
||||
# Create the trunk and client adapter.
|
||||
lpar_uuid = vm.get_pvm_uuid(self.instance)
|
||||
mgmt_uuid = pvm_par.get_this_partition(self.adapter).uuid
|
||||
dev_name = self.get_trunk_dev_name(vif)
|
||||
cna_w, trunk_wraps = pvm_cna.crt_p2p_cna(
|
||||
cna_w = pvm_cna.crt_p2p_cna(
|
||||
self.adapter, self.host_uuid, lpar_uuid, [mgmt_uuid],
|
||||
CONF.powervm.pvm_vswitch_for_novalink_io, crt_vswitch=True,
|
||||
mac_addr=vif['address'], dev_name=dev_name, slot_num=slot_num)
|
||||
mac_addr=vif['address'], dev_name=dev_name,
|
||||
slot_num=slot_num)[0]
|
||||
else:
|
||||
cna_w = None
|
||||
|
||||
# Make sure to just run the up just in case.
|
||||
utils.execute('ip', 'link', 'set', dev_name, 'up', run_as_root=True)
|
||||
|
||||
return cna_w
|
||||
@ -362,7 +394,7 @@ class PvmLioVifDriver(PvmVifDriver):
|
||||
class PvmLBVifDriver(PvmLioVifDriver):
|
||||
"""The Linux Bridge VIF driver for PowerVM."""
|
||||
|
||||
def plug(self, vif, slot_num):
|
||||
def plug(self, vif, slot_num, new_vif=True):
|
||||
"""Plugs a virtual interface (network) into a VM.
|
||||
|
||||
Extends the base Lio implementation. Will make sure that the bridge
|
||||
@ -370,8 +402,18 @@ class PvmLBVifDriver(PvmLioVifDriver):
|
||||
|
||||
:param vif: The virtual interface to plug into the instance.
|
||||
:param slot_num: Which slot number to plug the VIF into. May be None.
|
||||
:param new_vif: (Optional, Default: True) If set, indicates that it is
|
||||
a brand new VIF. If False, it indicates that the VIF
|
||||
is already on the client but should be treated on the
|
||||
bridge.
|
||||
:return: The new vif that was created. Only returned if new_vif is
|
||||
set to True. Otherwise None is expected.
|
||||
"""
|
||||
# Only call the parent if it is truly a new VIF
|
||||
if new_vif:
|
||||
cna_w = super(PvmLBVifDriver, self).plug(vif, slot_num)
|
||||
else:
|
||||
cna_w = None
|
||||
|
||||
# Similar to libvirt's vif.py plug_bridge. Need to attach the
|
||||
# interface to the bridge.
|
||||
@ -430,7 +472,7 @@ class PvmLBVifDriver(PvmLioVifDriver):
|
||||
class PvmOvsVifDriver(PvmLioVifDriver):
|
||||
"""The Open vSwitch VIF driver for PowerVM."""
|
||||
|
||||
def plug(self, vif, slot_num):
|
||||
def plug(self, vif, slot_num, new_vif=True):
|
||||
"""Plugs a virtual interface (network) into a VM.
|
||||
|
||||
Extends the Lio implementation. Will make sure that the trunk device
|
||||
@ -439,8 +481,18 @@ class PvmOvsVifDriver(PvmLioVifDriver):
|
||||
|
||||
:param vif: The virtual interface to plug into the instance.
|
||||
:param slot_num: Which slot number to plug the VIF into. May be None.
|
||||
:param new_vif: (Optional, Default: True) If set, indicates that it is
|
||||
a brand new VIF. If False, it indicates that the VIF
|
||||
is already on the client but should be treated on the
|
||||
bridge.
|
||||
:return: The new vif that was created. Only returned if new_vif is
|
||||
set to True. Otherwise None is expected.
|
||||
"""
|
||||
# Only call the parent if it is truly a new VIF
|
||||
if new_vif:
|
||||
cna_w = super(PvmOvsVifDriver, self).plug(vif, slot_num)
|
||||
else:
|
||||
cna_w = None
|
||||
|
||||
# There will only be one trunk wrap, as we have created with just the
|
||||
# mgmt lpar. Next step is to set the device up and connect to the OVS
|
||||
|
Loading…
Reference in New Issue
Block a user