diff --git a/ironicclient/osc/v1/baremetal_node.py b/ironicclient/osc/v1/baremetal_node.py index 463e0b1b7..fd1e2db74 100755 --- a/ironicclient/osc/v1/baremetal_node.py +++ b/ironicclient/osc/v1/baremetal_node.py @@ -441,6 +441,10 @@ class CreateBaremetalNode(command.ShowOne): '--owner', metavar='', help=_('Owner of the node.')) + parser.add_argument( + '--description', + metavar='', + help=_("Description for the node.")) return parser @@ -451,7 +455,7 @@ class CreateBaremetalNode(command.ShowOne): field_list = ['automated_clean', 'chassis_uuid', 'driver', 'driver_info', 'properties', 'extra', 'uuid', 'name', - 'conductor_group', 'owner', + 'conductor_group', 'owner', 'description', 'resource_class'] + ['%s_interface' % iface for iface in SUPPORTED_INTERFACES] fields = dict((k, v) for (k, v) in vars(parsed_args).items() @@ -626,6 +630,11 @@ class ListBaremetalNode(command.Lister): metavar='', help=_("Limit list to nodes with owner " "")) + parser.add_argument( + '--description-contains', + metavar='', + help=_("Limit list to nodes with description contains " + "")) display_group = parser.add_mutually_exclusive_group(required=False) display_group.add_argument( '--long', @@ -667,7 +676,8 @@ class ListBaremetalNode(command.Lister): if getattr(parsed_args, field) is not None: params[field] = getattr(parsed_args, field) for field in ['provision_state', 'driver', 'resource_class', - 'chassis', 'conductor', 'owner']: + 'chassis', 'conductor', 'owner', + 'description_contains']: if getattr(parsed_args, field): params[field] = getattr(parsed_args, field) if parsed_args.long: @@ -1179,6 +1189,11 @@ class SetBaremetalNode(command.Command): "--owner", metavar='', help=_('Set the owner for the node')), + parser.add_argument( + "--description", + metavar='', + help=_('Set the description for the node'), + ) return parser @@ -1202,7 +1217,7 @@ class SetBaremetalNode(command.Command): for field in ['automated_clean', 'instance_uuid', 'name', 'chassis_uuid', 'driver', 'resource_class', 'conductor_group', 'protected', 'protected_reason', - 'owner']: + 'owner', 'description']: value = getattr(parsed_args, field) if value: properties.extend(utils.args_array_to_patch( @@ -1480,6 +1495,11 @@ class UnsetBaremetalNode(command.Command): action="store_true", help=_('Unset the owner field of the node'), ) + parser.add_argument( + "--description", + action="store_true", + help=_('Unset the description field of the node'), + ) return parser @@ -1502,7 +1522,8 @@ class UnsetBaremetalNode(command.Command): 'management_interface', 'network_interface', 'power_interface', 'raid_interface', 'rescue_interface', 'storage_interface', 'vendor_interface', - 'protected', 'protected_reason', 'owner']: + 'protected', 'protected_reason', 'owner', + 'description']: if getattr(parsed_args, field): properties.extend(utils.args_array_to_patch('remove', [field])) diff --git a/ironicclient/tests/unit/osc/v1/test_baremetal_node.py b/ironicclient/tests/unit/osc/v1/test_baremetal_node.py index c11854d91..0156d00d5 100644 --- a/ironicclient/tests/unit/osc/v1/test_baremetal_node.py +++ b/ironicclient/tests/unit/osc/v1/test_baremetal_node.py @@ -465,6 +465,11 @@ class TestBaremetalCreate(TestBaremetal): [('owner', 'owner 1')], {'owner': 'owner 1'}) + def test_baremetal_create_with_description(self): + self.check_with_options(['--description', 'there is no spoon'], + [('description', 'there is no spoon')], + {'description': 'there is no spoon'}) + class TestBaremetalDelete(TestBaremetal): def setUp(self): @@ -623,6 +628,7 @@ class TestBaremetalList(TestBaremetal): 'Current RAID configuration', 'Deploy Interface', 'Deploy Step', + 'Description', 'Driver', 'Driver Info', 'Driver Internal Info', @@ -1006,11 +1012,35 @@ class TestBaremetalList(TestBaremetal): # DisplayCommandBase.take_action() returns two tuples self.cmd.take_action(parsed_args) - # Set excepted values kwargs = { 'marker': None, 'limit': None, - 'owner': owner + 'owner': owner, + } + + self.baremetal_mock.node.list.assert_called_with( + **kwargs + ) + + def test_baremetal_list_has_description(self): + description = 'there is no spoon' + arglist = [ + '--description-contains', description, + ] + verifylist = [ + ('description_contains', description), + ] + + 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, + 'description_contains': description } self.baremetal_mock.node.list.assert_called_with( @@ -2575,6 +2605,7 @@ class TestBaremetalSet(TestBaremetal): ('node', 'node_uuid'), ('owner', 'owner 1') ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) @@ -2587,6 +2618,28 @@ class TestBaremetalSet(TestBaremetal): reset_interfaces=None, ) + def test_baremetal_set_description(self): + arglist = [ + 'node_uuid', + '--description', 'there is no spoon', + ] + verifylist = [ + ('node', 'node_uuid'), + ('description', 'there is no spoon') + ] + + 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': '/description', + 'value': 'there is no spoon', + 'op': 'add'}], + reset_interfaces=None, + ) + class TestBaremetalShow(TestBaremetal): def setUp(self): @@ -3129,6 +3182,25 @@ class TestBaremetalUnset(TestBaremetal): [{'path': '/owner', 'op': 'remove'}] ) + def test_baremetal_unset_description(self): + arglist = [ + 'node_uuid', + '--description', + ] + verifylist = [ + ('node', 'node_uuid'), + ('description', 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': '/description', 'op': 'remove'}] + ) + class TestValidate(TestBaremetal): def setUp(self): diff --git a/ironicclient/tests/unit/v1/test_node_shell.py b/ironicclient/tests/unit/v1/test_node_shell.py index 4c05f8a74..72c0e605d 100644 --- a/ironicclient/tests/unit/v1/test_node_shell.py +++ b/ironicclient/tests/unit/v1/test_node_shell.py @@ -41,6 +41,7 @@ class NodeShellTest(utils.BaseTestCase): 'conductor_group', 'console_enabled', 'deploy_step', + 'description', 'driver', 'driver_info', 'driver_internal_info', diff --git a/ironicclient/v1/resource_fields.py b/ironicclient/v1/resource_fields.py index a0d6f3e7b..46c2e0e56 100644 --- a/ironicclient/v1/resource_fields.py +++ b/ironicclient/v1/resource_fields.py @@ -223,6 +223,7 @@ NODE_DETAILED_RESOURCE = Resource( 'raid_config', 'deploy_interface', 'deploy_step', + 'description', 'driver', 'driver_info', 'driver_internal_info', diff --git a/releasenotes/notes/add-node-description-support-6efd0882eaa0c788.yaml b/releasenotes/notes/add-node-description-support-6efd0882eaa0c788.yaml new file mode 100644 index 000000000..977c536f3 --- /dev/null +++ b/releasenotes/notes/add-node-description-support-6efd0882eaa0c788.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Adds ``description`` field support, which is introduced in ironic API + 1.51. This field is used to store informational text about the node. + User can also do queries for nodes where their ``description`` field + contains specified substring.