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:
parent
7c77aba2b9
commit
fb94fb825c
@ -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__)
|
||||
|
@ -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:
|
||||
|
@ -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',
|
||||
|
@ -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)
|
||||
|
@ -36,6 +36,7 @@ class NodeShellTest(utils.BaseTestCase):
|
||||
exp = ['chassis_uuid',
|
||||
'clean_step',
|
||||
'created_at',
|
||||
'conductor_group',
|
||||
'console_enabled',
|
||||
'deploy_step',
|
||||
'driver',
|
||||
|
@ -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:
|
||||
|
@ -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',
|
||||
|
12
releasenotes/notes/conductor-group-9cfab3756aa108e4.yaml
Normal file
12
releasenotes/notes/conductor-group-9cfab3756aa108e4.yaml
Normal 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.
|
Loading…
Reference in New Issue
Block a user