Plug tenant networks once provision is completed

Change-Id: I34f56f6e42b2b8e70dcaa7576b8d0106e6dffae8
This commit is contained in:
Sam Betts 2015-10-21 12:58:36 +01:00
parent 8a28fc7881
commit 774898ed84
5 changed files with 55 additions and 128 deletions

View File

@ -73,6 +73,25 @@ class PXEBoot(pxe.PXEBoot):
new_port.create()
return port['port']['fixed_ips'][0]['ip_address']
def _plug_tenant_networks(self, task, **kwargs):
ports = objects.Port.list_by_node_id(task.context, task.node.id)
for port in ports:
pargs = port['extra']
if pargs.get('type') == "tenant" and pargs['state'] == "DOWN":
try:
common.add_vnic(
task, pargs['vif_port_id'], port['address'],
pargs['seg_id'], pargs['pxe'])
except imcsdk.ImcException:
port.extra = {x: pargs[x] for x in pargs}
port.extra['state'] = "ERROR"
LOG.error("ADDING VNIC FAILED")
else:
port.extra = {x: pargs[x] for x in pargs}
port.extra['state'] = "UP"
LOG.info("ADDING VNIC SUCCESSFUL")
port.save()
def _unplug_provisioning(self, task, **kwargs):
LOG.debug("Unplugging the provisioning!")
if task.node.power_state != states.POWER_ON:
@ -87,6 +106,17 @@ class PXEBoot(pxe.PXEBoot):
client.delete_port(port['extra']['vif_port_id'])
port.destroy()
def _unplug_tenant_networks(self, task, **kwargs):
ports = objects.Port.list_by_node_id(task.context, task.node.id)
for port in ports:
pargs = port['extra']
if pargs.get('type') == "tenant" and pargs['state'] == "UP":
common.delete_vnic(task, port['extra']['vif_port_id'])
port.extra = {x: pargs[x] for x in pargs}
port.extra['state'] = "DOWN"
port.save()
LOG.info("DELETEING VNIC SUCCESSFUL")
def validate(self, task):
pass
@ -133,8 +163,14 @@ class PXEBoot(pxe.PXEBoot):
super(PXEBoot, self).prepare_instance(task)
if deploy_utils.get_boot_option(task.node) == "local":
self._unplug_provisioning(task)
self._plug_tenant_networks(task)
def clean_up_ramdisk(self, task):
super(PXEBoot, self).clean_up_ramdisk(task)
self._unplug_provisioning(task)
task.ports = objects.Port.list_by_node_id(task.context, task.node.id)
def clean_up_instance(self, task):
super(PXEBoot, self).clean_up_instance(task)
self._unplug_tenant_networks(task)
task.ports = objects.Port.list_by_node_id(task.context, task.node.id)

View File

@ -17,15 +17,11 @@ from oslo_log import log as logging
from oslo_utils import importutils
from ironic.common import exception
from ironic.common import states
from ironic.conductor import task_manager
from ironic.conductor import utils as manager_utils
from ironic.drivers import base
from ironic.drivers.modules import iscsi_deploy
from ironic import objects
from cisco_ironic_contrib.ironic.cimc import common
imcsdk = importutils.try_import('ImcSdk')
CONF = cfg.CONF
@ -37,53 +33,28 @@ class CIMCPXEVendorPassthru(iscsi_deploy.VendorPassthru):
@base.passthru(['POST'], async=True)
@task_manager.require_exclusive_lock
def add_vnic(self, task, **kwargs):
LOG.info("ENSURING NODE ON FOR VNIC ADDITION!")
if task.node.power_state != states.POWER_ON:
manager_utils.node_power_action(task, states.REBOOT)
LOG.info("ADDING PORT TO IRONIC DB")
new_port = objects.Port(
task.context, node_id=task.node.id, address=kwargs['mac'],
extra={"vif_port_id": kwargs['uuid'],
"type": "tenant", "state": "DOWN"})
extra={"vif_port_id": kwargs['uuid'], "seg_id": kwargs['vlan'],
"pxe": kwargs['pxe'], "type": "tenant", "state": "DOWN"})
new_port.create()
try:
LOG.info("ADDING VNIC TO CIMC")
common.add_vnic(
task, kwargs['uuid'], kwargs['mac'],
kwargs['vlan'], kwargs['pxe'])
except imcsdk.ImcException:
new_port.extra = {"vif_port_id": kwargs['uuid'], "type": "tenant",
"state": "ERROR"}
LOG.error("ADDING VNIC FAILED")
else:
new_port.extra = {"vif_port_id": kwargs['uuid'], "type": "tenant",
"state": "UP"}
LOG.info("ADDING VNIC SUCCESSFUL")
new_port.save()
@base.passthru(['POST'], async=True)
@task_manager.require_exclusive_lock
def delete_vnic(self, task, **kwargs):
# Ensure Node is powered on before changing VNIC settings
if task.node.power_state != states.POWER_ON:
manager_utils.node_power_action(task, states.REBOOT)
# Use neutron UUID to get port from ironic DB
ports = objects.Port.list_by_node_id(task.context, task.node.id)
todelete = None
for port in ports:
if port['extra']['vif_port_id'] == kwargs['uuid']:
if (port['extra']['vif_port_id'] == kwargs['uuid'] and
port['extra']['state'] == "DOWN"):
todelete = port
break
if todelete is None:
raise exception.NotFound("No port matched uuid provided")
# Delete vnic from server
common.delete_vnic(task, kwargs['uuid'])
# Delete port from ironic port DB
# Delete from DB
todelete.destroy()

View File

@ -15,7 +15,6 @@
from oslo_config import cfg
from oslo_log import log as logging
from oslo_service import loopingcall
from nova.network.neutronv2 import api as neutron
from nova.virt.ironic import driver as ironic_driver
@ -28,11 +27,6 @@ CONF = cfg.CONF
class CiscoIronicDriver(ironic_driver.IronicDriver):
"""Hypervisor driver for Ironic - bare metal provisioning."""
def _check_for_vnic_creation(self, ironicclient, address):
port = self.ironicclient.call("port.get_by_address", address)
if port.extra['state'] == "UP":
raise loopingcall.LoopingCallDone()
def macs_for_instance(self, instance):
return None
@ -50,11 +44,6 @@ class CiscoIronicDriver(ironic_driver.IronicDriver):
}
self.ironicclient.call("node.vendor_passthru", node_uuid,
"add_vnic", args=net_info)
timer = loopingcall.FixedIntervalLoopingCall(
self._check_for_vnic_creation,
self.ironicclient, vif['address'])
timer.start(interval=5).wait()
LOG.debug('Plug VIFs successful for instance', instance=instance)
def _unplug_vifs(self, node, instance, network_info):

View File

@ -17,14 +17,10 @@ import mock
from oslo_utils import importutils
from ironic.common import exception
from ironic.common import states
from ironic.conductor import task_manager
from ironic.conductor import utils as manager_utils
from ironic import objects
from ironic.tests.unit.drivers.modules.cimc import test_common
from cisco_ironic_contrib.ironic.cimc import common
imcsdk = importutils.try_import('ImcSdk')
TEST_DATA = {
@ -37,105 +33,47 @@ TEST_DATA = {
class CIMCPXEVendorPassthruTestCase(test_common.CIMCBaseTestCase):
@mock.patch.object(common, 'add_vnic', autospec=True)
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
@mock.patch.object(objects, 'Port', autospec=True)
def _test_add_vnic(self, mock_port, mock_power_action,
mock_add_vnic, initial_state=states.POWER_OFF):
def test_add_vnic(self, mock_port):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.power_state = initial_state
mock_port.return_value.extra = {}
task.driver.vendor.add_vnic(task, **TEST_DATA)
if initial_state != states.POWER_ON:
mock_power_action.assert_called_once_with(task, states.REBOOT)
else:
self.assertFalse(mock_power_action.called)
mock_port.assert_called_once_with(
task.context, node_id=task.node.id, address=TEST_DATA['mac'],
extra={"type": "tenant", "state": "DOWN",
"vif_port_id": TEST_DATA['uuid']})
extra={"type": "tenant", "state": "DOWN", 'seg_id': 600,
'pxe': False, "vif_port_id": TEST_DATA['uuid']})
mock_port.return_value.create.assert_called_once_with()
mock_add_vnic.assert_called_once_with(
task, TEST_DATA['uuid'], TEST_DATA['mac'],
TEST_DATA['vlan'], TEST_DATA['pxe'])
self.assertEqual(mock_port.return_value.extra['state'],
"UP")
mock_port.return_value.save.assert_called_once_with()
def test_add_vnic_node_off(self):
self._test_add_vnic()
def test_add_vnic_node_already_on(self):
self._test_add_vnic(initial_state=states.POWER_ON)
@mock.patch.object(common, 'add_vnic', autospec=True)
@mock.patch.object(objects, 'Port', autospec=True)
def test_add_vnic_fail(self, mock_port, mock_add_vnic):
def test_delete_vnic(self, mock_port):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.power_state = states.POWER_ON
mock_port.return_value.extra = {}
mock_add_vnic.side_effect = imcsdk.ImcException("Boom")
task.driver.vendor.add_vnic(task, **TEST_DATA)
self.assertEqual(mock_port.return_value.extra['state'],
"ERROR")
mock_port.return_value.save.assert_called_once_with()
@mock.patch.object(common, 'delete_vnic', autospec=True)
@mock.patch.object(manager_utils, 'node_power_action', autospec=True)
@mock.patch.object(objects, 'Port', autospec=True)
def _test_delete_vnic(self, mock_port, mock_power_action, mock_delete_vnic,
initial_state=states.POWER_OFF):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.power_state = initial_state
port1 = mock.MagicMock()
port1.__getitem__.return_value = {'vif_port_id': "1"}
port1.__getitem__.return_value = {'vif_port_id': "1",
'state': 'DOWN'}
port2 = mock.MagicMock()
port2.__getitem__.return_value = {'vif_port_id': "2"}
port2.__getitem__.return_value = {'vif_port_id': "2",
'state': 'DOWN'}
port3 = mock.MagicMock()
port3.__getitem__.return_value = {'vif_port_id': "3"}
port3.__getitem__.return_value = {'vif_port_id': "3",
'state': 'DOWN'}
mock_port.list_by_node_id.return_value = [port1, port2, port3]
task.driver.vendor.delete_vnic(task, uuid="1")
if initial_state != states.POWER_ON:
mock_power_action.assert_called_once_with(task, states.REBOOT)
else:
self.assertFalse(mock_power_action.called)
mock_port.list_by_node_id.assert_called_with(
task.context, task.node.id)
mock_delete_vnic.assert_called_once_with(task, "1")
def test_delete_vnic_node_off(self):
self._test_delete_vnic()
def test_delete_vnic_node_already_on(self):
self._test_delete_vnic(initial_state=states.POWER_ON)
port1.destroy.assert_called_once_with()
@mock.patch.object(objects, 'Port', autospec=True)
def test_delete_vnic_port_not_found(self, mock_port):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.power_state = states.POWER_ON
mock_port.list_by_node_id.return_value = []
self.assertRaises(exception.NotFound,

View File

@ -70,8 +70,7 @@ class CiscoIronicDriverTestCase(test.NoDBTestCase):
@mock.patch.object(neutron, 'get_client')
@mock.patch.object(FAKE_CLIENT.node, 'vendor_passthru')
@mock.patch.object(FAKE_CLIENT.port, 'get_by_address')
def test_plug_vifs_with_port(self, mock_address, mock_vp, mock_neutron):
def test_plug_vifs_with_port(self, mock_vp, mock_neutron):
node = ironic_utils.get_test_node()
instance = fake_instance.fake_instance_obj(self.ctx,
node=node.uuid)
@ -79,8 +78,6 @@ class CiscoIronicDriverTestCase(test.NoDBTestCase):
mock_neutron.return_value.show_network.return_value = {
'network': {
'provider:segmentation_id': 600}}
mock_address.return_value = ironic_utils.get_test_port(
extra={'state': 'UP'})
self.driver._plug_vifs(node, instance, network_info)
expected_info = {
@ -91,20 +88,16 @@ class CiscoIronicDriverTestCase(test.NoDBTestCase):
}
mock_vp.assert_called_once_with(node.uuid, 'add_vnic',
args=expected_info)
mock_address.assert_called_once_with(network_info[0]['address'])
@mock.patch.object(neutron, 'get_client')
@mock.patch.object(FAKE_CLIENT.node, 'vendor_passthru')
@mock.patch.object(FAKE_CLIENT.port, 'get_by_address')
def test_plug_vifs_no_network_info(self, mock_address, mock_vp,
mock_neutron):
def test_plug_vifs_no_network_info(self, mock_vp, mock_neutron):
node = ironic_utils.get_test_node()
instance = fake_instance.fake_instance_obj(self.ctx,
node=node.uuid)
network_info = []
self.driver._plug_vifs(node, instance, network_info)
self.assertFalse(mock_vp.called)
self.assertFalse(mock_address.called)
@mock.patch.object(FAKE_CLIENT.node, 'vendor_passthru')
def test_unplug_vifs(self, mock_vp):