Add support for conductor groups

Implements support for conductor groups when creating, updating and
listing nodes. Refactored some CLI code to avoid excessive copy-paste.

Change-Id: I16559bc3bb6dcbac996c768aa4514676cf4a98a8
Depends-On: https://review.openstack.org/#/c/581391/
Story: #2001795
Task: #23117
This commit is contained in:
Dmitry Tantsur 2018-07-23 16:19:49 +02:00
parent 7c77aba2b9
commit fb94fb825c
8 changed files with 186 additions and 90 deletions

View File

@ -43,7 +43,7 @@ from ironicclient import exc
# http://specs.openstack.org/openstack/ironic-specs/specs/kilo/api-microversions.html # noqa
# for full details.
DEFAULT_VER = '1.9'
LAST_KNOWN_API_VERSION = 45
LAST_KNOWN_API_VERSION = 46
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
LOG = logging.getLogger(__name__)

View File

@ -428,6 +428,10 @@ class CreateBaremetalNode(command.ShowOne):
'--resource-class',
metavar='<resource_class>',
help=_('Resource class for mapping nodes to Nova flavors'))
parser.add_argument(
'--conductor-group',
metavar='<conductor_group>',
help=_('Conductor group the node will belong to'))
return parser
@ -437,7 +441,7 @@ class CreateBaremetalNode(command.ShowOne):
baremetal_client = self.app.client_manager.baremetal
field_list = ['chassis_uuid', 'driver', 'driver_info',
'properties', 'extra', 'uuid', 'name',
'properties', 'extra', 'uuid', 'name', 'conductor_group',
'resource_class'] + ['%s_interface' % iface
for iface in SUPPORTED_INTERFACES]
fields = dict((k, v) for (k, v) in vars(parsed_args).items()
@ -593,6 +597,11 @@ class ListBaremetalNode(command.Lister):
dest='resource_class',
metavar='<resource class>',
help=_("Limit list to nodes with resource class <resource class>"))
parser.add_argument(
'--conductor-group',
metavar='<conductor_group>',
help=_("Limit list to nodes with conductor group <conductor "
"group>"))
parser.add_argument(
'--chassis',
dest='chassis',
@ -635,18 +644,13 @@ class ListBaremetalNode(command.Lister):
params['associated'] = True
if parsed_args.unassociated:
params['associated'] = False
if parsed_args.maintenance is not None:
params['maintenance'] = parsed_args.maintenance
if parsed_args.fault is not None:
params['fault'] = parsed_args.fault
if parsed_args.provision_state:
params['provision_state'] = parsed_args.provision_state
if parsed_args.driver:
params['driver'] = parsed_args.driver
if parsed_args.resource_class:
params['resource_class'] = parsed_args.resource_class
if parsed_args.chassis:
params['chassis'] = parsed_args.chassis
for field in ['maintenance', 'fault', 'conductor_group']:
if getattr(parsed_args, field) is not None:
params[field] = getattr(parsed_args, field)
for field in ['provision_state', 'driver', 'resource_class',
'chassis']:
if getattr(parsed_args, field):
params[field] = getattr(parsed_args, field)
if parsed_args.long:
params['detail'] = parsed_args.long
columns = res_fields.NODE_DETAILED_RESOURCE.fields
@ -1096,6 +1100,11 @@ class SetBaremetalNode(command.Command):
metavar='<resource_class>',
help=_('Set the resource class for the node'),
)
parser.add_argument(
'--conductor-group',
metavar='<conductor_group>',
help=_('Set the conductor group for the node'),
)
parser.add_argument(
'--target-raid-config',
metavar='<target_raid_config>',
@ -1152,22 +1161,13 @@ class SetBaremetalNode(command.Command):
raid_config)
properties = []
if parsed_args.instance_uuid:
instance_uuid = ["instance_uuid=%s" % parsed_args.instance_uuid]
properties.extend(utils.args_array_to_patch(
'add', instance_uuid))
if parsed_args.name:
name = ["name=%s" % parsed_args.name]
properties.extend(utils.args_array_to_patch(
'add', name))
if parsed_args.chassis_uuid:
chassis_uuid = ["chassis_uuid=%s" % parsed_args.chassis_uuid]
properties.extend(utils.args_array_to_patch(
'add', chassis_uuid))
if parsed_args.driver:
driver = ["driver=%s" % parsed_args.driver]
properties.extend(utils.args_array_to_patch(
'add', driver))
for field in ['instance_uuid', 'name', 'chassis_uuid', 'driver',
'resource_class', 'conductor_group']:
value = getattr(parsed_args, field)
if value:
properties.extend(utils.args_array_to_patch(
'add', ["%s=%s" % (field, value)]))
if parsed_args.reset_interfaces and not parsed_args.driver:
raise exc.CommandError(
_("--reset-interfaces can only be specified with --driver"))
@ -1183,11 +1183,6 @@ class SetBaremetalNode(command.Command):
properties.extend(utils.args_array_to_patch(
'remove', ['%s_interface' % iface]))
if parsed_args.resource_class:
resource_class = [
"resource_class=%s" % parsed_args.resource_class]
properties.extend(utils.args_array_to_patch(
'add', resource_class))
if parsed_args.property:
properties.extend(utils.args_array_to_patch(
'add', ['properties/' + x for x in parsed_args.property]))
@ -1417,6 +1412,12 @@ class UnsetBaremetalNode(command.Command):
action='store_true',
help=_('Unset vendor interface on this baremetal node'),
)
parser.add_argument(
"--conductor-group",
action="store_true",
help=_('Unset conductor group for this baremetal node (the '
'default group will be used)'),
)
return parser
@ -1432,15 +1433,16 @@ class UnsetBaremetalNode(command.Command):
baremetal_client.node.set_target_raid_config(parsed_args.node, {})
properties = []
if parsed_args.instance_uuid:
properties.extend(utils.args_array_to_patch('remove',
['instance_uuid']))
if parsed_args.name:
properties.extend(utils.args_array_to_patch('remove',
['name']))
if parsed_args.resource_class:
properties.extend(utils.args_array_to_patch('remove',
['resource_class']))
for field in ['instance_uuid', 'name', 'chassis_uuid',
'resource_class', 'conductor_group',
'bios_interface', 'boot_interface', 'console_interface',
'deploy_interface', 'inspect_interface',
'management_interface', 'network_interface',
'power_interface', 'raid_interface', 'rescue_interface',
'storage_interface', 'vendor_interface']:
if getattr(parsed_args, field):
properties.extend(utils.args_array_to_patch('remove', [field]))
if parsed_args.property:
properties.extend(utils.args_array_to_patch('remove',
['properties/' + x
@ -1456,45 +1458,6 @@ class UnsetBaremetalNode(command.Command):
properties.extend(utils.args_array_to_patch('remove',
['instance_info/' + x for x
in parsed_args.instance_info]))
if parsed_args.chassis_uuid:
properties.extend(utils.args_array_to_patch('remove',
['chassis_uuid']))
if parsed_args.bios_interface:
properties.extend(utils.args_array_to_patch('remove',
['bios_interface']))
if parsed_args.boot_interface:
properties.extend(utils.args_array_to_patch('remove',
['boot_interface']))
if parsed_args.console_interface:
properties.extend(utils.args_array_to_patch('remove',
['console_interface']))
if parsed_args.deploy_interface:
properties.extend(utils.args_array_to_patch('remove',
['deploy_interface']))
if parsed_args.inspect_interface:
properties.extend(utils.args_array_to_patch('remove',
['inspect_interface']))
if parsed_args.management_interface:
properties.extend(utils.args_array_to_patch('remove',
['management_interface']))
if parsed_args.network_interface:
properties.extend(utils.args_array_to_patch('remove',
['network_interface']))
if parsed_args.power_interface:
properties.extend(utils.args_array_to_patch('remove',
['power_interface']))
if parsed_args.raid_interface:
properties.extend(utils.args_array_to_patch('remove',
['raid_interface']))
if parsed_args.rescue_interface:
properties.extend(utils.args_array_to_patch('remove',
['rescue_interface']))
if parsed_args.storage_interface:
properties.extend(utils.args_array_to_patch('remove',
['storage_interface']))
if parsed_args.vendor_interface:
properties.extend(utils.args_array_to_patch('remove',
['vendor_interface']))
if properties:
baremetal_client.node.update(parsed_args.node, properties)
elif not parsed_args.target_raid_config:

View File

@ -450,6 +450,11 @@ class TestBaremetalCreate(TestBaremetal):
[('resource_class', 'foo')],
{'resource_class': 'foo'})
def test_baremetal_create_with_conductor_group(self):
self.check_with_options(['--conductor-group', 'conductor_group'],
[('conductor_group', 'conductor_group')],
{'conductor_group': 'conductor_group'})
class TestBaremetalDelete(TestBaremetal):
def setUp(self):
@ -594,10 +599,10 @@ class TestBaremetalList(TestBaremetal):
)
collist = ('Chassis UUID', 'Created At', 'Clean Step',
'Console Enabled', 'Deploy Step', 'Driver', 'Driver Info',
'Driver Internal Info', 'Extra', 'Instance Info',
'Instance UUID', 'Last Error', 'Maintenance',
'Maintenance Reason', 'Fault',
'Conductor Group', 'Console Enabled', 'Deploy Step',
'Driver', 'Driver Info', 'Driver Internal Info', 'Extra',
'Instance Info', 'Instance UUID', 'Last Error',
'Maintenance', 'Maintenance Reason', 'Fault',
'Power State', 'Properties', 'Provisioning State',
'Provision Updated At', 'Current RAID configuration',
'Reservation', 'Resource Class', 'Target Power State',
@ -622,6 +627,7 @@ class TestBaremetalList(TestBaremetal):
'',
'',
'',
'',
baremetal_fakes.baremetal_instance_uuid,
'',
baremetal_fakes.baremetal_maintenance,
@ -901,6 +907,56 @@ class TestBaremetalList(TestBaremetal):
**kwargs
)
def test_baremetal_list_conductor_group(self):
conductor_group = 'in-the-closet-to-the-left'
arglist = [
'--conductor-group', conductor_group,
]
verifylist = [
('conductor_group', conductor_group),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'marker': None,
'limit': None,
'conductor_group': conductor_group
}
self.baremetal_mock.node.list.assert_called_with(
**kwargs
)
def test_baremetal_list_empty_conductor_group(self):
conductor_group = ''
arglist = [
'--conductor-group', conductor_group,
]
verifylist = [
('conductor_group', conductor_group),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'marker': None,
'limit': None,
'conductor_group': conductor_group
}
self.baremetal_mock.node.list.assert_called_with(
**kwargs
)
def test_baremetal_list_fields(self):
arglist = [
'--fields', 'uuid', 'name',
@ -2205,6 +2261,26 @@ class TestBaremetalSet(TestBaremetal):
reset_interfaces=None,
)
def test_baremetal_set_conductor_group(self):
arglist = [
'node_uuid',
'--conductor-group', 'foo',
]
verifylist = [
('node', 'node_uuid'),
('conductor_group', 'foo')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid',
[{'path': '/conductor_group', 'value': 'foo', 'op': 'add'}],
reset_interfaces=None,
)
def test_baremetal_set_extra(self):
arglist = [
'node_uuid',
@ -2638,6 +2714,25 @@ class TestBaremetalUnset(TestBaremetal):
[{'path': '/resource_class', 'op': 'remove'}]
)
def test_baremetal_unset_conductor_group(self):
arglist = [
'node_uuid',
'--conductor-group',
]
verifylist = [
('node', 'node_uuid'),
('conductor_group', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid',
[{'path': '/conductor_group', 'op': 'remove'}]
)
def test_baremetal_unset_extra(self):
arglist = [
'node_uuid',

View File

@ -41,7 +41,8 @@ NODE1 = {'uuid': '66666666-7777-8888-9999-000000000000',
'properties': {'num_cpu': 4},
'name': 'fake-node-1',
'resource_class': 'foo',
'extra': {}}
'extra': {},
'conductor_group': 'in-the-closet-to-the-left'}
NODE2 = {'uuid': '66666666-7777-8888-9999-111111111111',
'instance_uuid': '66666666-7777-8888-9999-222222222222',
'chassis_uuid': 'aaaaaaaa-1111-bbbb-2222-cccccccccccc',
@ -200,6 +201,13 @@ fake_responses = {
{"nodes": [NODE1]},
)
},
'/v1/nodes/?conductor_group=foo':
{
'GET': (
{},
{"nodes": [NODE1]},
)
},
'/v1/nodes/?chassis_uuid=%s' % NODE2['chassis_uuid']:
{
'GET': (
@ -780,6 +788,15 @@ class NodeManagerTest(testtools.TestCase):
self.assertThat(nodes, HasLength(1))
self.assertEqual(NODE1['uuid'], getattr(nodes[0], 'uuid'))
def test_node_list_conductor_group(self):
nodes = self.mgr.list(conductor_group='foo')
expect = [
('GET', '/v1/nodes/?conductor_group=foo', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(nodes, HasLength(1))
self.assertEqual(NODE1['uuid'], getattr(nodes[0], 'uuid'))
def test_node_list_chassis(self):
ch2 = NODE2['chassis_uuid']
nodes = self.mgr.list(chassis=ch2)

View File

@ -36,6 +36,7 @@ class NodeShellTest(utils.BaseTestCase):
exp = ['chassis_uuid',
'clean_step',
'created_at',
'conductor_group',
'console_enabled',
'deploy_step',
'driver',

View File

@ -53,13 +53,14 @@ class NodeManager(base.CreateManager):
'network_interface', 'power_interface',
'raid_interface', 'rescue_interface',
'storage_interface', 'vendor_interface',
'resource_class']
'resource_class', 'conductor_group']
_resource_name = 'nodes'
def list(self, associated=None, maintenance=None, marker=None, limit=None,
detail=False, sort_key=None, sort_dir=None, fields=None,
provision_state=None, driver=None, resource_class=None,
chassis=None, fault=None, os_ironic_api_version=None):
chassis=None, fault=None, os_ironic_api_version=None,
conductor_group=None):
"""Retrieve a list of nodes.
:param associated: Optional. Either a Boolean or a string
@ -111,6 +112,9 @@ class NodeManager(base.CreateManager):
:param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used.
:param conductor_group: Optional. String value to get only nodes
with the given conductor group set.
:returns: A list of nodes.
"""
@ -137,6 +141,8 @@ class NodeManager(base.CreateManager):
filters.append('resource_class=%s' % resource_class)
if chassis is not None:
filters.append('chassis_uuid=%s' % chassis)
if conductor_group is not None:
filters.append('conductor_group=%s' % conductor_group)
path = ''
if detail:

View File

@ -39,6 +39,7 @@ class Resource(object):
'boot_index': 'Boot Index',
'chassis_uuid': 'Chassis UUID',
'clean_step': 'Clean Step',
'conductor_group': 'Conductor Group',
'console_enabled': 'Console Enabled',
'created_at': 'Created At',
'default_bios_interface': 'Default BIOS Interface',
@ -201,6 +202,7 @@ NODE_DETAILED_RESOURCE = Resource(
['chassis_uuid',
'created_at',
'clean_step',
'conductor_group',
'console_enabled',
'deploy_step',
'driver',

View File

@ -0,0 +1,12 @@
---
features:
- |
Adds support for the ``--conductor-group`` argument to the following
CLI commands:
* ``openstack baremetal node create``
* ``openstack baremetal node set``
* ``openstack baremetal node unset``
* ``openstack baremetal node list``
This feature requires bare metal API 1.46.