Files
nova-powervm/nova_powervm/tests/virt/powervm/tasks/test_network.py
esberglu c1a17ca319 Replace TestCase with NoDBTestCase
It was suggested that we replace TestCase with NoDBTestCase [1]. This
does two things. First it prevents the test case setup from running the
database schema migrations unnecessarily which should improve
performance. Second it will cause the test to fail if the code does
touch the DB when the developer is claiming that it shouldn't.

[1] https://review.openstack.org/#/c/409404/46/nova/tests/unit/virt/powervm/test_media.py

Change-Id: I8bcc9cbdcadd9fe89a4450ca405387899baa9fc3
2017-11-13 10:08:31 -06:00

403 lines
17 KiB
Python

# Copyright 2015, 2016 IBM Corp.
#
# 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 copy
import eventlet
import mock
from nova import exception
from nova import objects
from nova import test
from pypowervm.tests import test_fixtures as pvm_fx
from pypowervm.wrappers import iocard as pvm_card
from pypowervm.wrappers import network as pvm_net
from nova_powervm.tests.virt import powervm
from nova_powervm.virt.powervm.tasks import network as tf_net
def cna(mac):
"""Builds a mock Client Network Adapter (or VNIC) for unit tests."""
nic = mock.MagicMock()
nic.mac = mac
nic.vswitch_uri = 'fake_href'
return nic
class TestNetwork(test.NoDBTestCase):
def setUp(self):
super(TestNetwork, self).setUp()
self.flags(host='host1')
self.apt = self.useFixture(pvm_fx.AdapterFx()).adpt
self.mock_lpar_wrap = mock.MagicMock()
self.mock_lpar_wrap.can_modify_io.return_value = True, None
@mock.patch('nova_powervm.virt.powervm.vif.unplug')
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
def test_unplug_vifs(self, mock_vm_get, mock_unplug):
"""Tests that a delete of the vif can be done."""
inst = objects.Instance(**powervm.TEST_INSTANCE)
# Mock up the CNA responses.
cnas = [cna('AABBCCDDEEFF'), cna('AABBCCDDEE11'), cna('AABBCCDDEE22')]
mock_vm_get.return_value = cnas
# Mock up the network info. This also validates that they will be
# sanitized to upper case.
net_info = [
{'address': 'aa:bb:cc:dd:ee:ff'}, {'address': 'aa:bb:cc:dd:ee:22'},
{'address': 'aa:bb:cc:dd:ee:33'}
]
# Mock out the vif driver
def validate_unplug(adapter, host_uuid, instance, vif,
slot_mgr, cna_w_list=None):
self.assertEqual(adapter, self.apt)
self.assertEqual('host_uuid', host_uuid)
self.assertEqual(instance, inst)
self.assertIn(vif, net_info)
self.assertEqual('slot_mgr', slot_mgr)
self.assertEqual(cna_w_list, cnas)
mock_unplug.side_effect = validate_unplug
# Run method
p_vifs = tf_net.UnplugVifs(self.apt, inst, net_info, 'host_uuid',
'slot_mgr')
p_vifs.execute(self.mock_lpar_wrap)
# Make sure the unplug was invoked, so that we know that the validation
# code was called
self.assertEqual(3, mock_unplug.call_count)
def test_unplug_vifs_invalid_state(self):
"""Tests that the delete raises an exception if bad VM state."""
inst = objects.Instance(**powervm.TEST_INSTANCE)
# Mock that the state is incorrect
self.mock_lpar_wrap.can_modify_io.return_value = False, 'bad'
# Run method
p_vifs = tf_net.UnplugVifs(self.apt, inst, mock.Mock(), 'host_uuid',
'slot_mgr')
self.assertRaises(exception.VirtualInterfaceUnplugException,
p_vifs.execute, self.mock_lpar_wrap)
@mock.patch('nova_powervm.virt.powervm.vif.plug')
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
@mock.patch('nova_powervm.virt.powervm.vm.get_vnics')
def test_plug_vifs_rmc(self, mock_vnic_get, mock_cna_get, mock_plug):
"""Tests that a crt vif can be done with secure RMC."""
inst = objects.Instance(**powervm.TEST_INSTANCE)
# Mock up the CNA response. One should already exist, the other
# should not.
pre_cnas = [cna('AABBCCDDEEFF'), cna('AABBCCDDEE11')]
mock_cna_get.return_value = copy.deepcopy(pre_cnas)
# Ditto VNIC response.
mock_vnic_get.return_value = [cna('AABBCCDDEE33'), cna('AABBCCDDEE44')]
# Mock up the network info. This also validates that they will be
# sanitized to upper case.
net_info = [
{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'},
{'address': 'aa:bb:cc:dd:ee:22', 'vnic_type': 'normal'},
{'address': 'aa:bb:cc:dd:ee:33', 'vnic_type': 'direct'},
{'address': 'aa:bb:cc:dd:ee:55', 'vnic_type': 'direct'}
]
# Both updates run first (one CNA, one VNIC); then the CNA create, then
# the VNIC create.
mock_new_cna = mock.Mock(spec=pvm_net.CNA)
mock_new_vnic = mock.Mock(spec=pvm_card.VNIC)
mock_plug.side_effect = ['upd_cna', 'upd_vnic',
mock_new_cna, mock_new_vnic]
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
'host_uuid', 'slot_mgr')
all_cnas = p_vifs.execute(self.mock_lpar_wrap)
# 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=False)
mock_plug.assert_any_call(self.apt, 'host_uuid', inst, net_info[3],
'slot_mgr', new_vif=True)
# The Task provides the list of original CNAs plus only CNAs that were
# created.
self.assertEqual(pre_cnas + [mock_new_cna], all_cnas)
@mock.patch('nova_powervm.virt.powervm.vif.plug')
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
def test_plug_vifs_rmc_no_create(self, mock_vm_get, mock_plug):
"""Verifies if no creates are needed, none are done."""
inst = objects.Instance(**powervm.TEST_INSTANCE)
# Mock up the CNA response. Both should already exist.
mock_vm_get.return_value = [cna('AABBCCDDEEFF'), cna('AABBCCDDEE11')]
# Mock up the network info. This also validates that they will be
# sanitized to upper case. This also validates that we don't call
# get_vnics if no nets have vnic_type 'direct'.
net_info = [
{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'},
{'address': 'aa:bb:cc:dd:ee:11', 'vnic_type': 'normal'}
]
# 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 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')
def test_plug_vifs_invalid_state(self, mock_vm_get, mock_plug):
"""Tests that a crt_vif fails when the LPAR state is bad."""
inst = objects.Instance(**powervm.TEST_INSTANCE)
# Mock up the CNA response. Only doing one for simplicity
mock_vm_get.return_value = []
net_info = [{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'}]
# Mock that the state is incorrect
self.mock_lpar_wrap.can_modify_io.return_value = False, 'bad'
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
'host_uuid', 'slot_mgr')
self.assertRaises(exception.VirtualInterfaceCreateException,
p_vifs.execute, self.mock_lpar_wrap)
# The create should not have been invoked
self.assertEqual(0, mock_plug.call_count)
@mock.patch('nova_powervm.virt.powervm.vif.plug')
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
def test_plug_vifs_timeout(self, mock_vm_get, mock_plug):
"""Tests that crt vif failure via loss of neutron callback."""
inst = objects.Instance(**powervm.TEST_INSTANCE)
# Mock up the CNA response. Only doing one for simplicity
mock_vm_get.return_value = [cna('AABBCCDDEE11')]
# Mock up the network info.
net_info = [{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'}]
# Ensure that an exception is raised by a timeout.
mock_plug.side_effect = eventlet.timeout.Timeout()
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
'host_uuid', 'slot_mgr')
self.assertRaises(exception.VirtualInterfaceCreateException,
p_vifs.execute, self.mock_lpar_wrap)
# The create should have only been called once.
self.assertEqual(1, mock_plug.call_count)
@mock.patch('nova_powervm.virt.powervm.vif.plug')
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
def test_plug_vifs_diff_host(self, mock_vm_get, mock_plug):
"""Tests that crt vif handles bad inst.host value."""
inst = powervm.TEST_INST1
# Set this up as a different host from the inst.host
self.flags(host='host2')
# Mock up the CNA response. Only doing one for simplicity
mock_vm_get.return_value = [cna('AABBCCDDEE11')]
# Mock up the network info.
net_info = [{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'}]
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
'host_uuid', 'slot_mgr')
with mock.patch.object(inst, 'save') as mock_inst_save:
p_vifs.execute(self.mock_lpar_wrap)
# The create should have only been called once.
self.assertEqual(1, mock_plug.call_count)
# Should have called save to save the new host and then changed it back
self.assertEqual(2, mock_inst_save.call_count)
self.assertEqual('host1', inst.host)
@mock.patch('nova_powervm.virt.powervm.vif.plug')
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
def test_plug_vifs_diff_host_except(self, mock_vm_get, mock_plug):
"""Tests that crt vif handles bad inst.host value.
This test ensures that if we get a timeout exception we still reset
the inst.host value back to the original value
"""
inst = powervm.TEST_INST1
# Set this up as a different host from the inst.host
self.flags(host='host2')
# Mock up the CNA response. Only doing one for simplicity
mock_vm_get.return_value = [cna('AABBCCDDEE11')]
# Mock up the network info.
net_info = [{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'}]
# Ensure that an exception is raised by a timeout.
mock_plug.side_effect = eventlet.timeout.Timeout()
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
'host_uuid', 'slot_mgr')
with mock.patch.object(inst, 'save') as mock_inst_save:
self.assertRaises(exception.VirtualInterfaceCreateException,
p_vifs.execute, self.mock_lpar_wrap)
# The create should have only been called once.
self.assertEqual(1, mock_plug.call_count)
# Should have called save to save the new host and then changed it back
self.assertEqual(2, mock_inst_save.call_count)
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_plug, mock_unplug):
"""Tests that the revert flow works properly."""
inst = objects.Instance(**powervm.TEST_INSTANCE)
# Fake CNA list. The one pre-existing VIF should *not* get reverted.
cna_list = [cna('AABBCCDDEEFF'), cna('FFEEDDCCBBAA')]
mock_vm_get.return_value = cna_list
# Mock up the network info. Three roll backs.
net_info = [
{'address': 'aa:bb:cc:dd:ee:ff', 'vnic_type': 'normal'},
{'address': 'aa:bb:cc:dd:ee:22', 'vnic_type': 'normal'},
{'address': 'aa:bb:cc:dd:ee:33', 'vnic_type': 'normal'}
]
# Make sure we test raising an exception
mock_unplug.side_effect = [exception.NovaException(), None]
# 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 twice. The exception shouldn't stop the
# second call.
self.assertEqual(2, mock_unplug.call_count)
# Make sure each call is invoked correctly. The first plug was not a
# new vif, so it should not be reverted.
c2 = mock.call(self.apt, 'host_uuid', inst, net_info[1],
'slot_mgr', cna_w_list=cna_list)
c3 = mock.call(self.apt, 'host_uuid', inst, net_info[2],
'slot_mgr', cna_w_list=cna_list)
mock_unplug.assert_has_calls([c2, c3])
@mock.patch('nova_powervm.virt.powervm.vif.plug_secure_rmc_vif')
@mock.patch('nova_powervm.virt.powervm.vif.get_secure_rmc_vswitch')
@mock.patch('nova_powervm.virt.powervm.vif.plug')
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
def test_plug_mgmt_vif(self, mock_vm_get, mock_plug,
mock_get_rmc_vswitch, mock_plug_rmc_vif):
"""Tests that a mgmt vif can be created."""
inst = objects.Instance(**powervm.TEST_INSTANCE)
# Mock up the rmc vswitch
vswitch_w = mock.MagicMock()
vswitch_w.href = 'fake_mgmt_uri'
mock_get_rmc_vswitch.return_value = vswitch_w
# Run method such that it triggers a fresh CNA search
p_vifs = tf_net.PlugMgmtVif(self.apt, inst, 'host_uuid', 'slot_mgr')
p_vifs.execute(None)
# With the default get_cnas mock (which returns a Mock()), we think we
# found an existing management CNA.
mock_plug_rmc_vif.assert_not_called()
mock_vm_get.assert_called_once_with(
self.apt, inst, vswitch_uri='fake_mgmt_uri')
# Now mock get_cnas to return no hits
mock_vm_get.reset_mock()
mock_vm_get.return_value = []
p_vifs.execute(None)
# Get was called; and since it didn't have the mgmt CNA, so was plug.
self.assertEqual(1, mock_plug_rmc_vif.call_count)
mock_vm_get.assert_called_once_with(
self.apt, inst, vswitch_uri='fake_mgmt_uri')
# Now pass CNAs, but not the mgmt vif, "from PlugVifs"
cnas = [mock.Mock(vswitch_uri='uri1'), mock.Mock(vswitch_uri='uri2')]
mock_plug_rmc_vif.reset_mock()
mock_vm_get.reset_mock()
p_vifs.execute(cnas)
# Get wasn't called, since the CNAs were passed "from PlugVifs"; but
# since the mgmt vif wasn't included, plug was called.
mock_vm_get.assert_not_called()
mock_plug_rmc_vif.assert_called()
# Finally, pass CNAs including the mgmt.
cnas.append(mock.Mock(vswitch_uri='fake_mgmt_uri'))
mock_plug_rmc_vif.reset_mock()
p_vifs.execute(cnas)
# Neither get nor plug was called.
mock_vm_get.assert_not_called()
mock_plug_rmc_vif.assert_not_called()
@mock.patch('nova.utils.is_neutron')
def test_get_vif_events(self, mock_is_neutron):
# Set up common mocks.
inst = objects.Instance(**powervm.TEST_INSTANCE)
net_info = [mock.MagicMock(), mock.MagicMock()]
net_info[0]['id'] = 'a'
net_info[0].get.return_value = False
net_info[1]['id'] = 'b'
net_info[1].get.return_value = True
# Set up the runner.
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
'host_uuid', 'slot_mgr')
p_vifs.crt_network_infos = net_info
# Mock that neutron is off.
mock_is_neutron.return_value = False
self.assertEqual([], p_vifs._get_vif_events())
# Turn neutron on.
mock_is_neutron.return_value = True
resp = p_vifs._get_vif_events()
# Only one should be returned since only one was active.
self.assertEqual(1, len(resp))