Merge "Add restrictions for changing portgroup-node association"

This commit is contained in:
Jenkins 2016-06-13 15:43:32 +00:00 committed by Gerrit Code Review
commit 518f3b8eaa
2 changed files with 128 additions and 1 deletions

View File

@ -1727,7 +1727,9 @@ class ConductorManager(base_manager.BaseConductorManager):
@messaging.expected_exceptions(exception.NodeLocked,
exception.FailedToUpdateMacOnPort,
exception.PortgroupMACAlreadyExists)
exception.PortgroupMACAlreadyExists,
exception.PortgroupNotEmpty,
exception.InvalidState)
def update_portgroup(self, context, portgroup_obj):
"""Update a portgroup.
@ -1738,6 +1740,11 @@ class ConductorManager(base_manager.BaseConductorManager):
failed.
:raises: PortgroupMACAlreadyExists if the update is setting a MAC which
is registered on another portgroup already.
:raises: InvalidState if portgroup-node association is updated while
node not in a MANAGEABLE or ENROLL or INSPECTING state or not
in MAINTENANCE mode.
:raises: PortgroupNotEmpty if there are ports associated with this
portgroup.
"""
portgroup_uuid = portgroup_obj.uuid
LOG.debug("RPC update_portgroup called for portgroup %s.",
@ -1747,6 +1754,40 @@ class ConductorManager(base_manager.BaseConductorManager):
portgroup_obj.node_id,
purpose=lock_purpose) as task:
node = task.node
if 'node_id' in portgroup_obj.obj_what_changed():
# NOTE(zhenguo): If portgroup update is modifying the
# portgroup-node association then node should be in
# MANAGEABLE/INSPECTING/ENROLL provisioning state or in
# maintenance mode, otherwise InvalidState is raised.
allowed_update_states = [states.ENROLL,
states.INSPECTING,
states.MANAGEABLE]
if (node.provision_state not in allowed_update_states
and not node.maintenance):
action = _("Portgroup %(portgroup)s can not be associated "
"to node %(node)s unless the node is in a "
"%(allowed)s state or in maintenance mode.")
raise exception.InvalidState(
action % {'portgroup': portgroup_uuid,
'node': node.uuid,
'allowed': ', '.join(allowed_update_states)})
# NOTE(zhenguo): If portgroup update is modifying the
# portgroup-node association then there should not be
# any Port associated to the PortGroup, otherwise
# PortgroupNotEmpty exception is raised.
associated_ports = self.dbapi.get_ports_by_portgroup_id(
portgroup_uuid)
if associated_ports:
action = _("Portgroup %(portgroup)s can not be associated "
"with node %(node)s because there are ports "
"associated with this portgroup.")
raise exception.PortgroupNotEmpty(
action % {'portgroup': portgroup_uuid,
'node': node.uuid})
if 'address' in portgroup_obj.obj_what_changed():
vif = portgroup_obj.extra.get('vif_portgroup_id')
if vif:

View File

@ -2947,6 +2947,92 @@ class UpdatePortgroupTestCase(mgr_utils.ServiceSetUpMixin,
portgroup.refresh()
self.assertEqual(old_extra, portgroup.extra)
def test_update_portgroup_to_node_in_deleting_state(self):
node = obj_utils.create_test_node(self.context, driver='fake')
portgroup = obj_utils.create_test_portgroup(self.context,
node_id=node.id,
extra={'foo': 'bar'})
update_node = obj_utils.create_test_node(
self.context, driver='fake',
provision_state=states.DELETING,
uuid=uuidutils.generate_uuid())
old_node_id = portgroup.node_id
portgroup.node_id = update_node.id
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.update_portgroup,
self.context, portgroup)
self.assertEqual(exception.InvalidState, exc.exc_info[0])
portgroup.refresh()
self.assertEqual(old_node_id, portgroup.node_id)
@mock.patch.object(dbapi.IMPL, 'get_ports_by_portgroup_id')
def test_update_portgroup_to_node_in_manageable_state(self,
mock_get_ports):
node = obj_utils.create_test_node(self.context, driver='fake')
portgroup = obj_utils.create_test_portgroup(self.context,
node_id=node.id,
extra={'foo': 'bar'})
update_node = obj_utils.create_test_node(
self.context, driver='fake',
provision_state=states.MANAGEABLE,
uuid=uuidutils.generate_uuid())
mock_get_ports.return_value = []
self._start_service()
portgroup.node_id = update_node.id
self.service.update_portgroup(self.context, portgroup)
portgroup.refresh()
self.assertEqual(update_node.id, portgroup.node_id)
mock_get_ports.assert_called_once_with(portgroup.uuid)
@mock.patch.object(dbapi.IMPL, 'get_ports_by_portgroup_id')
def test_update_portgroup_to_node_in_active_state_and_maintenance(
self, mock_get_ports):
node = obj_utils.create_test_node(self.context, driver='fake')
portgroup = obj_utils.create_test_portgroup(self.context,
node_id=node.id,
extra={'foo': 'bar'})
update_node = obj_utils.create_test_node(
self.context, driver='fake',
provision_state=states.ACTIVE,
maintenance=True,
uuid=uuidutils.generate_uuid())
mock_get_ports.return_value = []
self._start_service()
portgroup.node_id = update_node.id
self.service.update_portgroup(self.context, portgroup)
portgroup.refresh()
self.assertEqual(update_node.id, portgroup.node_id)
mock_get_ports.assert_called_once_with(portgroup.uuid)
@mock.patch.object(dbapi.IMPL, 'get_ports_by_portgroup_id')
def test_update_portgroup_association_with_ports(self, mock_get_ports):
node = obj_utils.create_test_node(self.context, driver='fake')
portgroup = obj_utils.create_test_portgroup(self.context,
node_id=node.id,
extra={'foo': 'bar'})
update_node = obj_utils.create_test_node(
self.context, driver='fake',
maintenance=True,
uuid=uuidutils.generate_uuid())
mock_get_ports.return_value = ['test_port']
self._start_service()
old_node_id = portgroup.node_id
portgroup.node_id = update_node.id
exc = self.assertRaises(messaging.rpc.ExpectedException,
self.service.update_portgroup,
self.context, portgroup)
self.assertEqual(exception.PortgroupNotEmpty, exc.exc_info[0])
portgroup.refresh()
self.assertEqual(old_node_id, portgroup.node_id)
mock_get_ports.assert_called_once_with(portgroup.uuid)
@mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.update_port_address')
def test_update_portgroup_address(self, mac_update_mock):
node = obj_utils.create_test_node(self.context, driver='fake')