Merge "Plug updated VIFs"

This commit is contained in:
Jenkins 2016-08-17 16:30:25 +00:00 committed by Gerrit Code Review
commit 9d246c9230
5 changed files with 155 additions and 40 deletions

View File

@ -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

View File

@ -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)

View File

@ -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'}

View File

@ -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)

View File

@ -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.
"""
# 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(
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)
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
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)[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.
"""
cna_w = super(PvmLBVifDriver, self).plug(vif, slot_num)
# 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.
"""
cna_w = super(PvmOvsVifDriver, self).plug(vif, slot_num)
# 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