Merge "Add vif driver framework"

This commit is contained in:
Jenkins 2016-02-15 13:31:27 +00:00 committed by Gerrit Code Review
commit 57e2eb13d0
7 changed files with 433 additions and 172 deletions

View File

@ -1,4 +1,4 @@
# Copyright 2015 IBM Corp.
# Copyright 2015, 2016 IBM Corp.
#
# All Rights Reserved.
#
@ -43,13 +43,13 @@ class TestNetwork(test.TestCase):
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):
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 response. One should already exist, the other
# should not.
# Mock up the CNA responses.
cnas = [cna('AABBCCDDEEFF'), cna('AABBCCDDEE11'), cna('AABBCCDDEE22')]
mock_vm_get.return_value = cnas
@ -60,15 +60,24 @@ class TestNetwork(test.TestCase):
{'address': 'aa:bb:cc:dd:ee:33'}
]
# Mock out the vif driver
def validate_unplug(adapter, host_uuid, instance, vif,
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(cna_w_list, cnas)
mock_unplug.side_effect = validate_unplug
# Run method
p_vifs = tf_net.UnplugVifs(self.apt, inst, net_info, 'host_uuid')
p_vifs.execute(self.mock_lpar_wrap)
# The delete should have only been called once. The second CNA didn't
# have a matching mac...so it should be skipped.
self.assertEqual(1, cnas[0].delete.call_count)
self.assertEqual(0, cnas[1].delete.call_count)
self.assertEqual(1, cnas[2].delete.call_count)
# 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."""
@ -82,9 +91,9 @@ class TestNetwork(test.TestCase):
self.assertRaises(tf_net.VirtualInterfaceUnplugException,
p_vifs.execute, self.mock_lpar_wrap)
@mock.patch('nova_powervm.virt.powervm.vm.crt_vif')
@mock.patch('nova_powervm.virt.powervm.vif.plug')
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
def test_plug_vifs_rmc(self, mock_vm_get, mock_vm_crt):
def test_plug_vifs_rmc(self, mock_vm_get, mock_plug):
"""Tests that a crt vif can be done with secure RMC."""
inst = objects.Instance(**powervm.TEST_INSTANCE)
@ -105,11 +114,11 @@ class TestNetwork(test.TestCase):
p_vifs.execute(self.mock_lpar_wrap)
# The create should have only been called once.
self.assertEqual(2, mock_vm_crt.call_count)
self.assertEqual(2, mock_plug.call_count)
@mock.patch('nova_powervm.virt.powervm.vm.crt_vif')
@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_vm_crt):
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)
@ -129,16 +138,16 @@ class TestNetwork(test.TestCase):
# The create should not have been called. The response should have
# been empty.
self.assertEqual(0, mock_vm_crt.call_count)
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)
@mock.patch('nova_powervm.virt.powervm.vm.crt_vif')
@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_vm_crt):
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)
@ -156,11 +165,11 @@ class TestNetwork(test.TestCase):
p_vifs.execute, self.mock_lpar_wrap)
# The create should not have been invoked
self.assertEqual(0, mock_vm_crt.call_count)
self.assertEqual(0, mock_plug.call_count)
@mock.patch('nova_powervm.virt.powervm.vm.crt_vif')
@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_vm_crt):
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)
@ -171,7 +180,7 @@ class TestNetwork(test.TestCase):
net_info = [{'address': 'aa:bb:cc:dd:ee:ff'}]
# Ensure that an exception is raised by a timeout.
mock_vm_crt.side_effect = eventlet.timeout.Timeout()
mock_plug.side_effect = eventlet.timeout.Timeout()
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
@ -180,13 +189,14 @@ class TestNetwork(test.TestCase):
p_vifs.execute, self.mock_lpar_wrap)
# The create should have only been called once.
self.assertEqual(1, mock_vm_crt.call_count)
self.assertEqual(1, mock_plug.call_count)
@mock.patch('nova_powervm.virt.powervm.vm.crt_vif')
@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_vm_crt):
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')
@ -203,20 +213,21 @@ class TestNetwork(test.TestCase):
p_vifs.execute(self.mock_lpar_wrap)
# The create should have only been called once.
self.assertEqual(1, mock_vm_crt.call_count)
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.vm.crt_vif')
@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_vm_crt):
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')
@ -227,7 +238,7 @@ class TestNetwork(test.TestCase):
net_info = [{'address': 'aa:bb:cc:dd:ee:ff'}]
# Ensure that an exception is raised by a timeout.
mock_vm_crt.side_effect = eventlet.timeout.Timeout()
mock_plug.side_effect = eventlet.timeout.Timeout()
# Run method
p_vifs = tf_net.PlugVifs(mock.MagicMock(), self.apt, inst, net_info,
@ -237,17 +248,17 @@ class TestNetwork(test.TestCase):
p_vifs.execute, self.mock_lpar_wrap)
# The create should have only been called once.
self.assertEqual(1, mock_vm_crt.call_count)
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.vm.crt_secure_rmc_vif')
@mock.patch('nova_powervm.virt.powervm.vm.get_secure_rmc_vswitch')
@mock.patch('nova_powervm.virt.powervm.vm.crt_vif')
@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_vm_crt,
mock_get_rmc_vswitch, mock_crt_rmc_vif):
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)
@ -261,7 +272,7 @@ class TestNetwork(test.TestCase):
p_vifs.execute([])
# The create should have only been called once.
self.assertEqual(1, mock_crt_rmc_vif.call_count)
self.assertEqual(1, mock_plug_rmc_vif.call_count)
@mock.patch('nova.utils.is_neutron')
def test_get_vif_events(self, mock_is_neutron):

View File

@ -1197,13 +1197,13 @@ class TestPowerVMDriver(test.TestCase):
self.assertIsNotNone(value)
@mock.patch('pypowervm.wrappers.logical_partition.LPAR.can_modify_io')
@mock.patch('nova_powervm.virt.powervm.vm.crt_secure_rmc_vif')
@mock.patch('nova_powervm.virt.powervm.vm.get_secure_rmc_vswitch')
@mock.patch('nova_powervm.virt.powervm.vm.crt_vif')
@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_vifs(
self, mock_vm_get, mock_vm_crt, mock_get_rmc_vswitch, mock_crt_rmc_vif,
mock_can_modify_io):
self, mock_vm_get, mock_plug_vif, mock_get_rmc_vswitch,
mock_plug_rmc_vif, mock_can_modify_io):
# Mock up the CNA response
cnas = [mock.MagicMock(), mock.MagicMock()]
cnas[0].mac = 'AABBCCDDEEFF'
@ -1230,8 +1230,8 @@ class TestPowerVMDriver(test.TestCase):
# The create should have only been called once. The other was already
# existing.
self.assertEqual(1, mock_vm_crt.call_count)
self.assertEqual(0, mock_crt_rmc_vif.call_count)
self.assertEqual(1, mock_plug_vif.call_count)
self.assertEqual(0, mock_plug_rmc_vif.call_count)
@mock.patch('nova_powervm.virt.powervm.tasks.vm.Get')
def test_plug_vif_failures(self, mock_vm):

View File

@ -0,0 +1,144 @@
# Copyright 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 mock
from nova import exception
from nova import test
from pypowervm.tests import test_fixtures as pvm_fx
from pypowervm.wrappers import managed_system as pvm_ms
from pypowervm.wrappers import network as pvm_net
from nova_powervm.virt.powervm import vif
def cna(mac):
"""Builds a mock Client Network Adapter for unit tests."""
nic = mock.MagicMock()
nic.mac = mac
nic.vswitch_uri = 'fake_href'
return nic
class TestVifFunctions(test.TestCase):
def setUp(self):
super(TestVifFunctions, self).setUp()
self.adpt = self.useFixture(pvm_fx.AdapterFx(
traits=pvm_fx.LocalPVMTraits)).adpt
@mock.patch('pypowervm.wrappers.network.VSwitch.search')
def test_get_secure_rmc_vswitch(self, mock_search):
# Test no data coming back gets none
mock_search.return_value = []
resp = vif.get_secure_rmc_vswitch(self.adpt, 'host_uuid')
self.assertIsNone(resp)
# Mock that a couple vswitches get returned, but only the correct
# MGMT Switch gets returned
mock_vs = mock.MagicMock()
mock_vs.name = 'MGMTSWITCH'
mock_search.return_value = [mock_vs]
self.assertEqual(mock_vs,
vif.get_secure_rmc_vswitch(self.adpt, 'host_uuid'))
mock_search.assert_called_with(
self.adpt, parent_type=pvm_ms.System.schema_type,
parent_uuid='host_uuid', name=vif.SECURE_RMC_VSWITCH)
@mock.patch('pypowervm.tasks.cna.crt_cna')
@mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid')
def test_plug_secure_rmc_vif(self, mock_pvm_uuid, mock_crt):
mock_pvm_uuid.return_value = 'lpar_uuid'
vif.plug_secure_rmc_vif(self.adpt, 'instance', 'host_uuid')
mock_crt.assert_called_once_with(
self.adpt, 'host_uuid', 'lpar_uuid', 4094, vswitch='MGMTSWITCH',
crt_vswitch=True)
def test_build_vif_driver(self):
# Test the Shared Ethernet Adapter type VIF
mock_inst = mock.MagicMock()
mock_inst.name = 'instance'
self.assertIsInstance(
vif._build_vif_driver(self.adpt, 'host_uuid', mock_inst,
{'type': 'pvm_sea'}),
vif.PvmSeaVifDriver)
# Test raises exception for no type
self.assertRaises(exception.VirtualInterfacePlugException,
vif._build_vif_driver, self.adpt, 'host_uuid',
mock_inst, {})
# Test an invalid vif type
self.assertRaises(exception.VirtualInterfacePlugException,
vif._build_vif_driver, self.adpt, 'host_uuid',
mock_inst, {'type': 'ovs'})
class TestVifSeaDriver(test.TestCase):
def setUp(self):
super(TestVifSeaDriver, self).setUp()
self.adpt = self.useFixture(pvm_fx.AdapterFx(
traits=pvm_fx.LocalPVMTraits)).adpt
self.inst = mock.MagicMock()
self.drv = vif.PvmSeaVifDriver(self.adpt, 'host_uuid', self.inst)
@mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid')
@mock.patch('pypowervm.tasks.cna.crt_cna')
def test_plug(self, mock_crt_cna, mock_pvm_uuid):
"""Tests that a VIF can be created."""
# Set up the mocks
fake_vif = {'network': {'meta': {'vlan': 5}},
'address': 'aabbccddeeff'}
def validate_crt(adpt, host_uuid, lpar_uuid, vlan, mac_addr=None):
self.assertEqual('host_uuid', host_uuid)
self.assertEqual(5, vlan)
self.assertEqual('aabbccddeeff', mac_addr)
return pvm_net.CNA.bld(self.adpt, 5, host_uuid)
mock_crt_cna.side_effect = validate_crt
# Invoke
resp = self.drv.plug(fake_vif)
# Validate (along with validate method above)
self.assertEqual(1, mock_crt_cna.call_count)
self.assertIsNotNone(resp)
self.assertIsInstance(resp, pvm_net.CNA)
@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."""
# Mock up the CNA response. Two should already exist, the other
# should not.
cnas = [cna('AABBCCDDEEFF'), cna('AABBCCDDEE11'), cna('AABBCCDDEE22')]
mock_vm_get.return_value = cnas
# Run method. The AABBCCDDEE11 wont' be unplugged (wasn't invoked
# below) and the last unplug will also just no-op because its not on
# the VM.
self.drv.unplug({'address': 'aa:bb:cc:dd:ee:ff'})
self.drv.unplug({'address': 'aa:bb:cc:dd:ee:22'})
self.drv.unplug({'address': 'aa:bb:cc:dd:ee:33'})
# The delete should have only been called once. The second CNA didn't
# have a matching mac...so it should be skipped.
self.assertEqual(1, cnas[0].delete.call_count)
self.assertEqual(0, cnas[1].delete.call_count)
self.assertEqual(1, cnas[2].delete.call_count)

View File

@ -1,4 +1,4 @@
# Copyright 2014, 2015, 2016 IBM Corp.
# Copyright 2014, 2016 IBM Corp.
#
# All Rights Reserved.
#
@ -13,7 +13,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from __future__ import absolute_import
@ -32,7 +31,6 @@ from pypowervm.tests import test_fixtures as pvm_fx
from pypowervm.tests.test_utils import pvmhttp
from pypowervm.wrappers import base_partition as pvm_bp
from pypowervm.wrappers import logical_partition as pvm_lpar
from pypowervm.wrappers import network as pvm_net
from nova_powervm.tests.virt import powervm
from nova_powervm.virt.powervm import vm
@ -450,30 +448,6 @@ class TestVM(test.TestCase):
vm.power_off, None, None, 'host_uuid',
mock.Mock(state=pvm_bp.LPARState.RUNNING))
@mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid')
@mock.patch('pypowervm.tasks.cna.crt_cna')
def test_crt_vif(self, mock_crt_cna, mock_pvm_uuid):
"""Tests that a VIF can be created."""
# Set up the mocks
fake_vif = {'network': {'meta': {'vlan': 5}},
'address': 'aabbccddeeff'}
def validate_of_crt(*kargs, **kwargs):
self.assertEqual('fake_host', kargs[1])
self.assertEqual(5, kargs[3])
self.assertEqual('aabbccddeeff', kwargs['mac_addr'])
return pvm_net.CNA.bld(self.apt, 5, 'fake_host')
mock_crt_cna.side_effect = validate_of_crt
# Invoke
resp = vm.crt_vif(self.apt, mock.MagicMock(), 'fake_host', fake_vif)
# Validate (along with validate method above)
self.assertEqual(1, mock_crt_cna.call_count)
self.assertIsNotNone(resp)
self.assertIsInstance(resp, pvm_net.CNA)
@mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid')
@mock.patch('nova_powervm.virt.powervm.vm.get_vm_qp')
def test_instance_exists(self, mock_getvmqp, mock_getuuid):

View File

@ -27,6 +27,7 @@ from nova_powervm.virt.powervm.i18n import _
from nova_powervm.virt.powervm.i18n import _LE
from nova_powervm.virt.powervm.i18n import _LI
from nova_powervm.virt.powervm.i18n import _LW
from nova_powervm.virt.powervm import vif
from nova_powervm.virt.powervm import vm
LOG = logging.getLogger(__name__)
@ -45,18 +46,18 @@ class UnplugVifs(task.Task):
"""The task to unplug Virtual Network Interfaces from a VM."""
def __init__(self, adapter, instance, network_info, host_uuid):
def __init__(self, adapter, instance, network_infos, host_uuid):
"""Create the task.
:param adapter: The pypowervm adapter.
:param instance: The nova instance.
:param network_info: The network information containing the nova
:param network_infos: The network information containing the nova
VIFs to create.
:param host_uuid: The host system's PowerVM UUID.
"""
self.adapter = adapter
self.instance = instance
self.network_info = network_info
self.network_infos = network_infos
self.host_uuid = host_uuid
super(UnplugVifs, self).__init__(name='unplug_vifs',
@ -81,35 +82,10 @@ class UnplugVifs(task.Task):
cna_w_list = vm.get_cnas(self.adapter, self.instance, self.host_uuid)
# Walk through the VIFs and delete the corresponding CNA on the VM.
for vif in self.network_info:
for cna_w in cna_w_list:
# If the MAC address matched, attempt the delete.
if vm.norm_mac(cna_w.mac) == vif['address']:
LOG.info(_LI('Deleting VIF with mac %(mac)s for instance '
'%(inst)s.'), {'mac': vif['address'],
'inst': self.instance.name},
instance=self.instance)
try:
cna_w.delete()
except Exception as e:
LOG.error(_LE('Unable to unplug VIF with mac %(mac)s '
'for instance %(inst)s.'),
{'mac': vif['address'],
'inst': self.instance.name},
instance=self.instance)
LOG.error(e)
raise VirtualInterfaceUnplugException()
for network_info in self.network_infos:
vif.unplug(self.adapter, self.host_uuid, self.instance,
network_info, cna_w_list=cna_w_list)
# Break from the loop as we had a successful unplug.
# This prevents from going to 'else' loop.
break
else:
LOG.warning(_LW('Unable to unplug VIF with mac %(mac)s for '
'instance %(inst)s. The VIF was not found on '
'the instance.'),
{'mac': vif['address'],
'inst': self.instance.name},
instance=self.instance)
return cna_w_list
@ -117,7 +93,7 @@ class PlugVifs(task.Task):
"""The task to plug the Virtual Network Interfaces to a VM."""
def __init__(self, virt_api, adapter, instance, network_info, host_uuid):
def __init__(self, virt_api, adapter, instance, network_infos, host_uuid):
"""Create the task.
Provides the 'vm_cnas' - the Virtual Machine's Client Network Adapters.
@ -125,14 +101,14 @@ class PlugVifs(task.Task):
:param virt_api: The VirtAPI for the operation.
:param adapter: The pypowervm adapter.
:param instance: The nova instance.
:param network_info: The network information containing the nova
:param network_infos: The network information containing the nova
VIFs to create.
:param host_uuid: The host system's PowerVM UUID.
"""
self.virt_api = virt_api
self.adapter = adapter
self.instance = instance
self.network_info = network_info
self.network_infos = network_infos
self.host_uuid = host_uuid
super(PlugVifs, self).__init__(name='plug_vifs', provides='vm_cnas',
@ -146,16 +122,16 @@ class PlugVifs(task.Task):
cna_w_list = vm.get_cnas(self.adapter, self.instance, self.host_uuid)
# Trim the VIFs down to the ones that haven't yet been created.
crt_vifs = []
for vif in self.network_info:
crt_network_infos = []
for network_info in self.network_infos:
for cna_w in cna_w_list:
if vm.norm_mac(cna_w.mac) == vif['address']:
if vm.norm_mac(cna_w.mac) == network_info['address']:
break
else:
crt_vifs.append(vif)
crt_network_infos.append(network_info)
# If there are no vifs to create, then just exit immediately.
if len(crt_vifs) == 0:
if len(crt_network_infos) == 0:
return []
# Check to see if the LPAR is OK to add VIFs to.
@ -191,14 +167,14 @@ class PlugVifs(task.Task):
self.instance, self._get_vif_events(),
deadline=CONF.vif_plugging_timeout,
error_callback=self._vif_callback_failed):
for vif in crt_vifs:
for network_info in crt_network_infos:
LOG.info(_LI('Creating VIF with mac %(mac)s for instance '
'%(sys)s'),
{'mac': vif['address'],
{'mac': network_info['address'],
'sys': self.instance.name},
instance=self.instance)
vm.crt_vif(self.adapter, self.instance, self.host_uuid,
vif)
vif.plug(self.adapter, self.host_uuid, self.instance,
network_info)
except eventlet.timeout.Timeout:
LOG.error(_LE('Error waiting for VIF to be created for instance '
'%(sys)s'), {'sys': self.instance.name},
@ -234,9 +210,9 @@ class PlugVifs(task.Task):
# more information.
if (utils.is_neutron() and CONF.vif_plugging_is_fatal and
CONF.vif_plugging_timeout):
return [('network-vif-plugged', vif['id'])
for vif in self.network_info
if not vif.get('active', True)]
return [('network-vif-plugged', network_info['id'])
for network_info in self.network_infos
if not network_info.get('active', True)]
else:
return []
@ -269,7 +245,7 @@ class PlugMgmtVif(task.Task):
'%s'), self.instance.name, instance=self.instance)
# Determine if we need to create the secure RMC VIF. This should only
# be needed if there is not a VIF on the secure RMC vSwitch
vswitch_w = vm.get_secure_rmc_vswitch(self.adapter, self.host_uuid)
vswitch_w = vif.get_secure_rmc_vswitch(self.adapter, self.host_uuid)
if vswitch_w is None:
LOG.debug('No management VIF created for instance %s due to '
'lack of Management Virtual Switch',
@ -285,5 +261,5 @@ class PlugMgmtVif(task.Task):
return None
# Return the created management CNA
return vm.crt_secure_rmc_vif(self.adapter, self.instance,
return vif.plug_secure_rmc_vif(self.adapter, self.instance,
self.host_uuid)

View File

@ -0,0 +1,208 @@
# Copyright 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 abc
import logging
import six
from nova import exception
from oslo_utils import importutils
from pypowervm.tasks import cna as pvm_cna
from pypowervm.wrappers import managed_system as pvm_ms
from pypowervm.wrappers import network as pvm_net
from nova_powervm.virt.powervm.i18n import _
from nova_powervm.virt.powervm.i18n import _LE
from nova_powervm.virt.powervm.i18n import _LI
from nova_powervm.virt.powervm.i18n import _LW
from nova_powervm.virt.powervm import vm
LOG = logging.getLogger(__name__)
SECURE_RMC_VSWITCH = 'MGMTSWITCH'
SECURE_RMC_VLAN = 4094
VIF_MAPPING = {'pvm_sea': 'nova_powervm.virt.powervm.vif.PvmSeaVifDriver'}
class VirtualInterfaceUnplugException(exception.NovaException):
"""Indicates that a VIF unplug failed."""
# TODO(thorst) symmetrical to the exception in base Nova. Evaluate
# moving to Nova core.
msg_fmt = _("Virtual interface unplug failed")
def _build_vif_driver(adapter, host_uuid, instance, vif):
"""Returns the appropriate VIF Driver for the given VIF.
:param adapter: The pypowervm adapter API interface.
:param host_uuid: The host system UUID.
:param instance: The nova instance.
:param vif: The virtual interface to from Nova.
:return: The appropriate PvmVifDriver for the VIF.
"""
if vif.get('type') is None:
raise exception.VirtualInterfacePlugException(
_("vif_type parameter must be present "
"for this vif_driver implementation"))
# Check the type to the implementations
if VIF_MAPPING.get(vif['type']):
return importutils.import_object(
VIF_MAPPING.get(vif['type']), adapter, host_uuid, instance)
# No matching implementation, raise error.
raise exception.VirtualInterfacePlugException(
_("Unable to find appropriate PowerVM VIF Driver for VIF type "
"%(vif_type)s on instance %(instance)s") %
{'vif_type': vif['type'], 'instance': instance.name})
def plug(adapter, host_uuid, instance, vif):
"""Plugs a virtual interface (network) into a VM.
:param adapter: The pypowervm adapter.
:param host_uuid: The host UUID for the PowerVM API.
:param instance: The nova instance object.
:param vif: The virtual interface to plug into the instance.
"""
vif_drv = _build_vif_driver(adapter, host_uuid, instance, vif)
vif_drv.plug(vif)
def unplug(adapter, host_uuid, instance, vif, cna_w_list=None):
"""Unplugs a virtual interface (network) from a VM.
:param adapter: The pypowervm adapter.
:param host_uuid: The host UUID for the PowerVM API.
:param instance: The nova instance object.
:param vif: The virtual interface to plug into the instance.
:param cna_w_list: (Optional, Default: None) The list of Client Network
Adapters from pypowervm. Providing this input
allows for an improvement in operation speed.
"""
vif_drv = _build_vif_driver(adapter, host_uuid, instance, vif)
vif_drv.unplug(vif, cna_w_list=cna_w_list)
def get_secure_rmc_vswitch(adapter, host_uuid):
"""Returns the vSwitch that is used for secure RMC.
:param adapter: The pypowervm adapter API interface.
:param host_uuid: The host system UUID.
:return: The wrapper for the secure RMC vSwitch. If it does not exist
on the system, None is returned.
"""
vswitches = pvm_net.VSwitch.search(
adapter, parent_type=pvm_ms.System.schema_type,
parent_uuid=host_uuid, name=SECURE_RMC_VSWITCH)
if len(vswitches) == 1:
return vswitches[0]
return None
def plug_secure_rmc_vif(adapter, instance, host_uuid):
"""Creates the Secure RMC Network Adapter on the VM.
:param adapter: The pypowervm adapter API interface.
:param instance: The nova instance to create the VIF against.
:param host_uuid: The host system UUID.
:return: The created network adapter wrapper.
"""
lpar_uuid = vm.get_pvm_uuid(instance)
return pvm_cna.crt_cna(adapter, host_uuid, lpar_uuid, SECURE_RMC_VLAN,
vswitch=SECURE_RMC_VSWITCH, crt_vswitch=True)
@six.add_metaclass(abc.ABCMeta)
class PvmVifDriver(object):
"""Represents an abstract class for a PowerVM Vif Driver.
A VIF Driver understands a given virtual interface type (network). It
understands how to plug and unplug a given VIF for a virtual machine.
"""
def __init__(self, adapter, host_uuid, instance):
"""Initializes a VIF Driver.
:param adapter: The pypowervm adapter API interface.
:param host_uuid: The host system UUID.
:param instance: The nova instance that the vif action will be run
against.
"""
self.adapter = adapter
self.host_uuid = host_uuid
self.instance = instance
@abc.abstractmethod
def plug(self, vif):
"""Plugs a virtual interface (network) into a VM.
:param vif: The virtual interface to plug into the instance.
"""
pass
def unplug(self, vif, cna_w_list=None):
"""Unplugs a virtual interface (network) from a VM.
:param vif: The virtual interface to plug into the instance.
:param cna_w_list: (Optional, Default: None) The list of Client Network
Adapters from pypowervm. Providing this input
allows for an improvement in operation speed.
"""
# This is a default implementation that most implementations will
# require.
# Need to find the adapters if they were not provided
if not cna_w_list:
cna_w_list = vm.get_cnas(self.adapter, self.instance,
self.host_uuid)
for cna_w in cna_w_list:
# If the MAC address matched, attempt the delete.
if vm.norm_mac(cna_w.mac) == vif['address']:
LOG.info(_LI('Deleting VIF with mac %(mac)s for instance '
'%(inst)s.'),
{'mac': vif['address'], 'inst': self.instance.name})
try:
cna_w.delete()
except Exception as e:
LOG.error(_LE('Unable to unplug VIF with mac %(mac)s '
'for instance %(inst)s.'),
{'mac': vif['address'],
'inst': self.instance.name})
LOG.exception(e)
raise VirtualInterfaceUnplugException()
# Break from the loop as we had a successful unplug.
# This prevents from going to 'else' loop.
break
else:
LOG.warning(_LW('Unable to unplug VIF with mac %(mac)s for '
'instance %(inst)s. The VIF was not found on '
'the instance.'),
{'mac': vif['address'], 'inst': self.instance.name})
class PvmSeaVifDriver(PvmVifDriver):
"""The PowerVM Shared Ethernet Adapter VIF Driver."""
def plug(self, vif):
lpar_uuid = vm.get_pvm_uuid(self.instance)
# CNA's require a VLAN. If the network doesn't provide, default to 1
vlan = vif['network']['meta'].get('vlan', 1)
return pvm_cna.crt_cna(self.adapter, self.host_uuid, lpar_uuid, vlan,
mac_addr=vif['address'])

View File

@ -26,7 +26,6 @@ from nova.virt import event
from nova.virt import hardware
from pypowervm import exceptions as pvm_exc
from pypowervm.helpers import log_helper as pvm_log
from pypowervm.tasks import cna
from pypowervm.tasks import ibmi
from pypowervm.tasks import power
from pypowervm.tasks import vterm
@ -96,11 +95,6 @@ POWERVM_STOPABLE_STATE = (pvm_bp.LPARState.RUNNING, pvm_bp.LPARState.STARTING,
pvm_bp.LPARState.OPEN_FIRMWARE,
pvm_bp.LPARState.ERROR, pvm_bp.LPARState.RESUMING)
# Attributes for secure RMC
# TODO(thorst) The name of the secure RMC vswitch will change.
SECURE_RMC_VSWITCH = 'MGMTSWITCH'
SECURE_RMC_VLAN = 4094
def translate_event(pvm_state, pwr_state):
"""Translate the PowerVM state and see if it has changed.
@ -711,52 +705,6 @@ def get_cnas(adapter, instance, host_uuid):
return pvm_net.CNA.wrap(cna_resp)
def crt_vif(adapter, instance, host_uuid, vif):
"""Will create a Client Network Adapter on the system.
:param adapter: The pypowervm adapter API interface.
:param instance: The nova instance to create the VIF against.
:param host_uuid: The host system UUID.
:param vif: The nova VIF that describes the ethernet interface.
:return: The created network adapter wrapper.
"""
lpar_uuid = get_pvm_uuid(instance)
# CNA's require a VLAN. If the network doesn't provide, default to 1
vlan = vif['network']['meta'].get('vlan', 1)
return cna.crt_cna(adapter, host_uuid, lpar_uuid, vlan,
mac_addr=vif['address'])
def crt_secure_rmc_vif(adapter, instance, host_uuid):
"""Creates the Secure RMC Network Adapter on the VM.
:param adapter: The pypowervm adapter API interface.
:param instance: The nova instance to create the VIF against.
:param host_uuid: The host system UUID.
:return: The created network adapter wrapper.
"""
lpar_uuid = get_pvm_uuid(instance)
return cna.crt_cna(adapter, host_uuid, lpar_uuid, SECURE_RMC_VLAN,
vswitch=SECURE_RMC_VSWITCH, crt_vswitch=True)
def get_secure_rmc_vswitch(adapter, host_uuid):
"""Returns the vSwitch that is used for secure RMC.
:param adapter: The pypowervm adapter API interface.
:param host_uuid: The host system UUID.
:return: The wrapper for the secure RMC vSwitch. If it does not exist
on the system, None is returned.
"""
resp = adapter.read(pvm_ms.System.schema_type, root_id=host_uuid,
child_type=pvm_net.VSwitch.schema_type)
vswitches = pvm_net.VSwitch.wrap(resp)
for vswitch in vswitches:
if vswitch.name == SECURE_RMC_VSWITCH:
return vswitch
return None
def norm_mac(mac):
"""Normalizes a MAC address from pypowervm format to OpenStack.