Merge "Add Virtual Network Interface RPC APIs"

This commit is contained in:
Jenkins 2016-12-29 17:28:19 +00:00 committed by Gerrit Code Review
commit e5fbfcfac2
4 changed files with 260 additions and 3 deletions

View File

@ -83,7 +83,7 @@ class ConductorManager(base_manager.BaseConductorManager):
"""Ironic Conductor manager main class."""
# NOTE(rloo): This must be in sync with rpcapi.ConductorAPI's.
RPC_API_VERSION = '1.37'
RPC_API_VERSION = '1.38'
target = messaging.Target(version=RPC_API_VERSION)
@ -1777,7 +1777,6 @@ class ConductorManager(base_manager.BaseConductorManager):
with task_manager.acquire(context, port_obj.node_id,
purpose='port update') as task:
node = task.node
# Only allow updating MAC addresses for active nodes if maintenance
# mode is on.
if ((node.provision_state == states.ACTIVE or node.instance_uuid)
@ -2324,6 +2323,86 @@ class ConductorManager(base_manager.BaseConductorManager):
task.spawn_after(self._spawn_worker, task.driver.deploy.heartbeat,
task, callback_url)
@METRICS.timer('ConductorManager.vif_list')
@messaging.expected_exceptions(exception.NetworkError,
exception.InvalidParameterValue)
def vif_list(self, context, node_id):
"""List attached VIFs for a node
:param context: request context.
:param node_id: node ID or UUID.
:returns: List of VIF dictionaries, each dictionary will have an
'id' entry with the ID of the VIF.
:raises: NetworkError, if something goes wrong during list the VIFs.
:raises: InvalidParameterValue, if a parameter that's required for
VIF list is wrong/missing.
"""
LOG.debug("RPC vif_list called for the node %s", node_id)
with task_manager.acquire(context, node_id,
purpose='list vifs',
shared=True) as task:
task.driver.network.validate(task)
return task.driver.network.vif_list(task)
@METRICS.timer('ConductorManager.vif_attach')
@messaging.expected_exceptions(exception.NodeLocked,
exception.NetworkError,
exception.VifAlreadyAttached,
exception.NoFreePhysicalPorts,
exception.InvalidParameterValue)
def vif_attach(self, context, node_id, vif_info):
"""Attach a VIF to a node
:param context: request context.
:param node_id: node ID or UUID.
:param vif_info: a dictionary representing VIF object.
It must have an 'id' key, whose value is a unique
identifier for that VIF.
:raises: VifAlreadyAttached, if VIF is already attached to node
:raises: NoFreePhysicalPorts, if no free physical ports left to attach
:raises: NodeLocked, if node has an exclusive lock held on it
:raises: NetworkError, if an error occurs during attaching the VIF.
:raises: InvalidParameterValue, if a parameter that's required for
VIF attach is wrong/missing.
"""
LOG.debug("RPC vif_attach called for the node %(node_id)s with "
"vif_info %(vif_info)s", {'node_id': node_id,
'vif_info': vif_info})
with task_manager.acquire(context, node_id,
purpose='attach vif') as task:
task.driver.network.validate(task)
task.driver.network.vif_attach(task, vif_info)
LOG.info(_LI("VIF %(vif_id)s successfully attached to node "
"%(node_id)s"), {'vif_id': vif_info['id'],
'node_id': node_id})
@METRICS.timer('ConductorManager.vif_detach')
@messaging.expected_exceptions(exception.NodeLocked,
exception.NetworkError,
exception.VifNotAttached,
exception.InvalidParameterValue)
def vif_detach(self, context, node_id, vif_id):
"""Detach a VIF from a node
:param context: request context.
:param node_id: node ID or UUID.
:param vif_id: A VIF ID.
:raises: VifNotAttached, if VIF not attached to node
:raises: NodeLocked, if node has an exclusive lock held on it
:raises: NetworkError, if an error occurs during detaching the VIF.
:raises: InvalidParameterValue, if a parameter that's required for
VIF detach is wrong/missing.
"""
LOG.debug("RPC vif_detach called for the node %(node_id)s with "
"vif_id %(vif_id)s", {'node_id': node_id, 'vif_id': vif_id})
with task_manager.acquire(context, node_id,
purpose='detach vif') as task:
task.driver.network.validate(task)
task.driver.network.vif_detach(task, vif_id)
LOG.info(_LI("VIF %(vif_id)s successfully detached from node "
"%(node_id)s"), {'vif_id': vif_id,
'node_id': node_id})
def _object_dispatch(self, target, method, context, args, kwargs):
"""Dispatch a call to an object method.

View File

@ -84,11 +84,12 @@ class ConductorAPI(object):
| 1.35 - Added destroy_volume_connector and update_volume_connector
| 1.36 - Added create_node
| 1.37 - Added destroy_volume_target and update_volume_target
| 1.38 - Added vif_attach, vif_detach, vif_list
"""
# NOTE(rloo): This must be in sync with manager.ConductorManager's.
RPC_API_VERSION = '1.37'
RPC_API_VERSION = '1.38'
def __init__(self, topic=None):
super(ConductorAPI, self).__init__()
@ -839,3 +840,52 @@ class ConductorAPI(object):
cctxt = self.client.prepare(topic=topic or self.topic, version='1.37')
return cctxt.call(context, 'update_volume_target',
target=target)
def vif_attach(self, context, node_id, vif_info, topic=None):
"""Attach VIF to a node
:param context: request context.
:param node_id: node ID or UUID.
:param vif_info: a dictionary representing VIF object.
It must have an 'id' key, whose value is a unique
identifier for that VIF.
:param topic: RPC topic. Defaults to self.topic.
:raises: NodeLocked, if node has an exclusive lock held on it
:raises: NetworkError, if an error occurs during attaching the VIF.
:raises: InvalidParameterValue, if a parameter that's required for
VIF attach is wrong/missing.
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.38')
return cctxt.call(context, 'vif_attach', node_id=node_id,
vif_info=vif_info)
def vif_detach(self, context, node_id, vif_id, topic=None):
"""Detach VIF from a node
:param context: request context.
:param node_id: node ID or UUID.
:param vif_id: an ID of a VIF.
:param topic: RPC topic. Defaults to self.topic.
:raises: NodeLocked, if node has an exclusive lock held on it
:raises: NetworkError, if an error occurs during detaching the VIF.
:raises: InvalidParameterValue, if a parameter that's required for
VIF detach is wrong/missing.
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.38')
return cctxt.call(context, 'vif_detach', node_id=node_id,
vif_id=vif_id)
def vif_list(self, context, node_id, topic=None):
"""List attached VIFs for a node
:param context: request context.
:param node_id: node ID or UUID.
:param topic: RPC topic. Defaults to self.topic.
:returns: List of VIF dictionaries, each dictionary will have an
'id' entry with the ID of the VIF.
:raises: NetworkError, if an error occurs during listing the VIFs.
:raises: InvalidParameterValue, if a parameter that's required for
VIF list is wrong/missing.
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.38')
return cctxt.call(context, 'vif_list', node_id=node_id)

View File

@ -3322,6 +3322,114 @@ class UpdatePortTestCase(mgr_utils.ServiceSetUpMixin,
exc.exc_info[0])
@mgr_utils.mock_record_keepalive
@mock.patch.object(n_flat.FlatNetwork, 'validate', autospec=True)
class VifTestCase(mgr_utils.ServiceSetUpMixin, tests_db_base.DbTestCase):
def setUp(self):
super(VifTestCase, self).setUp()
self.vif = {'id': 'fake'}
@mock.patch.object(n_flat.FlatNetwork, 'vif_list', autospec=True)
def test_vif_list(self, mock_list, mock_valid):
mock_list.return_value = ['VIF_ID']
node = obj_utils.create_test_node(self.context, driver='fake')
data = self.service.vif_list(self.context, node.uuid)
mock_list.assert_called_once_with(mock.ANY, mock.ANY)
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
self.assertEqual(mock_list.return_value, data)
@mock.patch.object(n_flat.FlatNetwork, 'vif_attach', autospec=True)
def test_vif_attach(self, mock_attach, mock_valid):
node = obj_utils.create_test_node(self.context, driver='fake')
self.service.vif_attach(self.context, node.uuid, self.vif)
mock_attach.assert_called_once_with(mock.ANY, mock.ANY, self.vif)
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
@mock.patch.object(n_flat.FlatNetwork, 'vif_attach', autospec=True)
def test_vif_attach_node_locked(self, mock_attach, mock_valid):
node = obj_utils.create_test_node(self.context, driver='fake',
reservation='fake-reserv')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vif_attach,
self.context, node.uuid, self.vif)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeLocked, exc.exc_info[0])
self.assertFalse(mock_attach.called)
self.assertFalse(mock_valid.called)
@mock.patch.object(n_flat.FlatNetwork, 'vif_attach', autospec=True)
def test_vif_attach_raises_network_error(self, mock_attach,
mock_valid):
mock_attach.side_effect = exception.NetworkError("BOOM")
node = obj_utils.create_test_node(self.context, driver='fake')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vif_attach,
self.context, node.uuid, self.vif)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NetworkError, exc.exc_info[0])
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
mock_attach.assert_called_once_with(mock.ANY, mock.ANY, self.vif)
@mock.patch.object(n_flat.FlatNetwork, 'vif_attach', autpspec=True)
def test_vif_attach_validate_error(self, mock_attach,
mock_valid):
mock_valid.side_effect = exception.MissingParameterValue("BOOM")
node = obj_utils.create_test_node(self.context, driver='fake')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vif_attach,
self.context, node.uuid, self.vif)
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.MissingParameterValue, exc.exc_info[0])
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
self.assertFalse(mock_attach.called)
@mock.patch.object(n_flat.FlatNetwork, 'vif_detach', autpspec=True)
def test_vif_detach(self, mock_detach, mock_valid):
node = obj_utils.create_test_node(self.context, driver='fake')
self.service.vif_detach(self.context, node.uuid, "interface")
mock_detach.assert_called_once_with(mock.ANY, "interface")
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
@mock.patch.object(n_flat.FlatNetwork, 'vif_detach', autpspec=True)
def test_vif_detach_node_locked(self, mock_detach, mock_valid):
node = obj_utils.create_test_node(self.context, driver='fake',
reservation='fake-reserv')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vif_detach,
self.context, node.uuid, "interface")
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NodeLocked, exc.exc_info[0])
self.assertFalse(mock_detach.called)
self.assertFalse(mock_valid.called)
@mock.patch.object(n_flat.FlatNetwork, 'vif_detach', autpspec=True)
def test_vif_detach_raises_network_error(self, mock_detach,
mock_valid):
mock_detach.side_effect = exception.NetworkError("BOOM")
node = obj_utils.create_test_node(self.context, driver='fake')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vif_detach,
self.context, node.uuid, "interface")
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.NetworkError, exc.exc_info[0])
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
mock_detach.assert_called_once_with(mock.ANY, "interface")
@mock.patch.object(n_flat.FlatNetwork, 'vif_detach', autpspec=True)
def test_vif_detach_validate_error(self, mock_detach,
mock_valid):
mock_valid.side_effect = exception.MissingParameterValue("BOOM")
node = obj_utils.create_test_node(self.context, driver='fake')
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.vif_detach,
self.context, node.uuid, "interface")
# Compare true exception hidden by @messaging.expected_exceptions
self.assertEqual(exception.MissingParameterValue, exc.exc_info[0])
mock_valid.assert_called_once_with(mock.ANY, mock.ANY)
self.assertFalse(mock_detach.called)
@mgr_utils.mock_record_keepalive
class UpdatePortgroupTestCase(mgr_utils.ServiceSetUpMixin,
tests_db_base.DbTestCase):

View File

@ -434,3 +434,23 @@ class RPCAPITestCase(base.DbTestCase):
'call',
version='1.37',
target=fake_volume_target)
def test_vif_attach(self):
self._test_rpcapi('vif_attach',
'call',
node_id='fake-node',
vif_info={"id": "vif"},
version='1.38')
def test_vif_detach(self):
self._test_rpcapi('vif_detach',
'call',
node_id='fake-node',
vif_id="vif",
version='1.38')
def test_vif_list(self):
self._test_rpcapi('vif_list',
'call',
node_id='fake-node',
version='1.38')