Traits support
This adds support for the traits APIs in both the node portion of the SDK, and the openstackclient plugin. We also bump the last known API version to 1.37 to get access to the new API. Change-Id: I72017d51dea194ec062a66cb19d718ba827e7427 Partial-Bug: #1722194 Depends-On: I313fa01fbf20bf0ff19f102ea63b02e72ac2b856
This commit is contained in:
committed by
Julia Kreger
parent
997e11177f
commit
bc2c3a2367
@@ -170,25 +170,34 @@ class Manager(object):
|
||||
|
||||
return object_list
|
||||
|
||||
def _list(self, url, response_key=None, obj_class=None, body=None):
|
||||
def __list(self, url, response_key=None, body=None):
|
||||
resp, body = self.api.json_request('GET', url)
|
||||
data = self._format_body_data(body, response_key)
|
||||
return data
|
||||
|
||||
def _list(self, url, response_key=None, obj_class=None, body=None):
|
||||
if obj_class is None:
|
||||
obj_class = self.resource_class
|
||||
|
||||
data = self._format_body_data(body, response_key)
|
||||
data = self.__list(url, response_key=response_key, body=body)
|
||||
return [obj_class(self, res, loaded=True) for res in data if res]
|
||||
|
||||
def _list_primitives(self, url, response_key=None):
|
||||
return self.__list(url, response_key=response_key)
|
||||
|
||||
def _update(self, resource_id, patch, method='PATCH'):
|
||||
"""Update a resource.
|
||||
|
||||
:param resource_id: Resource identifier.
|
||||
:param patch: New version of a given resource.
|
||||
:param patch: New version of a given resource, a dictionary or None.
|
||||
:param method: Name of the method for the request.
|
||||
"""
|
||||
|
||||
url = self._path(resource_id)
|
||||
resp, body = self.api.json_request(method, url, body=patch)
|
||||
kwargs = {}
|
||||
if patch is not None:
|
||||
kwargs['body'] = patch
|
||||
resp, body = self.api.json_request(method, url, **kwargs)
|
||||
# PATCH/PUT requests may not return a body
|
||||
if body:
|
||||
return self.resource_class(self, body)
|
||||
|
||||
@@ -44,7 +44,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 = 35
|
||||
LAST_KNOWN_API_VERSION = 37
|
||||
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@@ -1574,3 +1574,116 @@ class InjectNmiBaremetalNode(command.Command):
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
|
||||
baremetal_client.node.inject_nmi(parsed_args.node)
|
||||
|
||||
|
||||
class ListTraitsBaremetalNode(command.Lister):
|
||||
"""List a node's traits."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ListTraitsBaremetalNode")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListTraitsBaremetalNode, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'node',
|
||||
metavar='<node>',
|
||||
help=_("Name or UUID of the node"))
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
labels = res_fields.TRAIT_RESOURCE.labels
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
traits = baremetal_client.node.get_traits(parsed_args.node)
|
||||
|
||||
return (labels, [[trait] for trait in traits])
|
||||
|
||||
|
||||
class AddTraitBaremetalNode(command.Command):
|
||||
"""Add traits to a node."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".AddTraitBaremetalNode")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AddTraitBaremetalNode, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'node',
|
||||
metavar='<node>',
|
||||
help=_("Name or UUID of the node"))
|
||||
parser.add_argument(
|
||||
'traits',
|
||||
nargs='+',
|
||||
metavar='<trait>',
|
||||
help=_("Trait(s) to add"))
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
|
||||
failures = []
|
||||
for trait in parsed_args.traits:
|
||||
try:
|
||||
baremetal_client.node.add_trait(parsed_args.node, trait)
|
||||
print(_('Added trait %s') % trait)
|
||||
except exc.ClientException as e:
|
||||
failures.append(_("Failed to add trait %(trait)s: %(error)s")
|
||||
% {'trait': trait, 'error': e})
|
||||
|
||||
if failures:
|
||||
raise exc.ClientException("\n".join(failures))
|
||||
|
||||
|
||||
class RemoveTraitBaremetalNode(command.Command):
|
||||
"""Remove trait(s) from a node."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".RemoveTraitBaremetalNode")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemoveTraitBaremetalNode, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'node',
|
||||
metavar='<node>',
|
||||
help=_("Name or UUID of the node"))
|
||||
all_or_trait = parser.add_mutually_exclusive_group(required=True)
|
||||
all_or_trait.add_argument(
|
||||
'--all',
|
||||
dest='remove_all',
|
||||
action='store_true',
|
||||
help=_("Remove all traits"))
|
||||
all_or_trait.add_argument(
|
||||
'traits',
|
||||
metavar='<trait>',
|
||||
nargs='*',
|
||||
default=[],
|
||||
help=_("Trait(s) to remove"))
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
|
||||
failures = []
|
||||
if parsed_args.remove_all:
|
||||
baremetal_client.node.remove_all_traits(parsed_args.node)
|
||||
else:
|
||||
for trait in parsed_args.traits:
|
||||
try:
|
||||
baremetal_client.node.remove_trait(parsed_args.node, trait)
|
||||
print(_('Removed trait %s') % trait)
|
||||
except exc.ClientException as e:
|
||||
failures.append(_("Failed to remove trait %(trait)s: "
|
||||
"%(error)s")
|
||||
% {'trait': trait, 'error': e})
|
||||
|
||||
if failures:
|
||||
raise exc.ClientException("\n".join(failures))
|
||||
|
||||
@@ -137,6 +137,7 @@ PORTGROUP = {'uuid': baremetal_portgroup_uuid,
|
||||
}
|
||||
|
||||
VIFS = {'vifs': [{'id': 'aaa-aa'}]}
|
||||
TRAITS = ['CUSTOM_FOO', 'CUSTOM_BAR']
|
||||
|
||||
baremetal_volume_connector_uuid = 'vvv-cccccc-vvvv'
|
||||
baremetal_volume_connector_type = 'iqn'
|
||||
|
||||
@@ -591,7 +591,7 @@ class TestBaremetalList(TestBaremetal):
|
||||
'Current RAID configuration', 'Reservation',
|
||||
'Resource Class',
|
||||
'Target Power State', 'Target Provision State',
|
||||
'Target RAID configuration',
|
||||
'Target RAID configuration', 'Traits',
|
||||
'Updated At', 'Inspection Finished At',
|
||||
'Inspection Started At', 'UUID', 'Name',
|
||||
'Boot Interface', 'Console Interface',
|
||||
@@ -627,6 +627,7 @@ class TestBaremetalList(TestBaremetal):
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
baremetal_fakes.baremetal_uuid,
|
||||
baremetal_fakes.baremetal_name,
|
||||
'',
|
||||
@@ -2663,3 +2664,186 @@ class TestBaremetalInject(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.inject_nmi.assert_called_once_with(
|
||||
'node_uuid')
|
||||
|
||||
|
||||
class TestListTraits(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestListTraits, self).setUp()
|
||||
|
||||
self.baremetal_mock.node.get_traits.return_value = (
|
||||
baremetal_fakes.TRAITS)
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_node.ListTraitsBaremetalNode(self.app, None)
|
||||
|
||||
def test_baremetal_list_traits(self):
|
||||
arglist = ['node_uuid']
|
||||
verifylist = [('node', 'node_uuid')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.get_traits.assert_called_once_with(
|
||||
'node_uuid')
|
||||
|
||||
|
||||
class TestAddTrait(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestAddTrait, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_node.AddTraitBaremetalNode(self.app, None)
|
||||
|
||||
def test_baremetal_add_trait(self):
|
||||
arglist = ['node_uuid', 'CUSTOM_FOO']
|
||||
verifylist = [('node', 'node_uuid'), ('traits', ['CUSTOM_FOO'])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.add_trait.assert_called_once_with(
|
||||
'node_uuid', 'CUSTOM_FOO')
|
||||
|
||||
def test_baremetal_add_traits_multiple(self):
|
||||
arglist = ['node_uuid', 'CUSTOM_FOO', 'CUSTOM_BAR']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('traits', ['CUSTOM_FOO', 'CUSTOM_BAR'])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
expected_calls = [
|
||||
mock.call('node_uuid', 'CUSTOM_FOO'),
|
||||
mock.call('node_uuid', 'CUSTOM_BAR'),
|
||||
]
|
||||
self.assertEqual(expected_calls,
|
||||
self.baremetal_mock.node.add_trait.call_args_list)
|
||||
|
||||
def test_baremetal_add_traits_multiple_with_failure(self):
|
||||
arglist = ['node_uuid', 'CUSTOM_FOO', 'CUSTOM_BAR']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('traits', ['CUSTOM_FOO', 'CUSTOM_BAR'])]
|
||||
|
||||
self.baremetal_mock.node.add_trait.side_effect = [
|
||||
'', exc.ClientException]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.assertRaises(exc.ClientException,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
expected_calls = [
|
||||
mock.call('node_uuid', 'CUSTOM_FOO'),
|
||||
mock.call('node_uuid', 'CUSTOM_BAR'),
|
||||
]
|
||||
self.assertEqual(expected_calls,
|
||||
self.baremetal_mock.node.add_trait.call_args_list)
|
||||
|
||||
def test_baremetal_add_traits_no_traits(self):
|
||||
arglist = ['node_uuid']
|
||||
verifylist = [('node', 'node_uuid')]
|
||||
|
||||
self.assertRaises(oscutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd,
|
||||
arglist,
|
||||
verifylist)
|
||||
|
||||
|
||||
class TestRemoveTrait(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestRemoveTrait, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_node.RemoveTraitBaremetalNode(self.app, None)
|
||||
|
||||
def test_baremetal_remove_trait(self):
|
||||
arglist = ['node_uuid', 'CUSTOM_FOO']
|
||||
verifylist = [('node', 'node_uuid'), ('traits', ['CUSTOM_FOO'])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.remove_trait.assert_called_once_with(
|
||||
'node_uuid', 'CUSTOM_FOO')
|
||||
|
||||
def test_baremetal_remove_trait_multiple(self):
|
||||
arglist = ['node_uuid', 'CUSTOM_FOO', 'CUSTOM_BAR']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('traits', ['CUSTOM_FOO', 'CUSTOM_BAR'])]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
expected_calls = [
|
||||
mock.call('node_uuid', 'CUSTOM_FOO'),
|
||||
mock.call('node_uuid', 'CUSTOM_BAR'),
|
||||
]
|
||||
self.assertEqual(expected_calls,
|
||||
self.baremetal_mock.node.remove_trait.call_args_list)
|
||||
|
||||
def test_baremetal_remove_trait_multiple_with_failure(self):
|
||||
arglist = ['node_uuid', 'CUSTOM_FOO', 'CUSTOM_BAR']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('traits', ['CUSTOM_FOO', 'CUSTOM_BAR'])]
|
||||
|
||||
self.baremetal_mock.node.remove_trait.side_effect = [
|
||||
'', exc.ClientException]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.assertRaises(exc.ClientException,
|
||||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
expected_calls = [
|
||||
mock.call('node_uuid', 'CUSTOM_FOO'),
|
||||
mock.call('node_uuid', 'CUSTOM_BAR'),
|
||||
]
|
||||
self.assertEqual(expected_calls,
|
||||
self.baremetal_mock.node.remove_trait.call_args_list)
|
||||
|
||||
def test_baremetal_remove_trait_all(self):
|
||||
arglist = ['node_uuid', '--all']
|
||||
verifylist = [('node', 'node_uuid'), ('remove_all', True)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.remove_all_traits.assert_called_once_with(
|
||||
'node_uuid')
|
||||
|
||||
def test_baremetal_remove_trait_traits_and_all(self):
|
||||
arglist = ['node_uuid', 'CUSTOM_FOO', '--all']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('traits', ['CUSTOM_FOO']),
|
||||
('remove_all', True)]
|
||||
|
||||
self.assertRaises(oscutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd,
|
||||
arglist,
|
||||
verifylist)
|
||||
|
||||
self.baremetal_mock.node.remove_all_traits.assert_not_called()
|
||||
self.baremetal_mock.node.remove_trait.assert_not_called()
|
||||
|
||||
def test_baremetal_remove_traits_no_traits_no_all(self):
|
||||
arglist = ['node_uuid']
|
||||
verifylist = [('node', 'node_uuid')]
|
||||
|
||||
self.assertRaises(oscutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd,
|
||||
arglist,
|
||||
verifylist)
|
||||
|
||||
self.baremetal_mock.node.remove_all_traits.assert_not_called()
|
||||
self.baremetal_mock.node.remove_trait.assert_not_called()
|
||||
|
||||
@@ -103,6 +103,7 @@ NODE_VENDOR_PASSTHRU_METHOD = {"heartbeat": {"attach": "false",
|
||||
"async": "true"}}
|
||||
|
||||
VIFS = {'vifs': [{'id': 'aaa-aaa'}]}
|
||||
TRAITS = {'traits': ['CUSTOM_FOO', 'CUSTOM_BAR']}
|
||||
|
||||
CREATE_NODE = copy.deepcopy(NODE1)
|
||||
del CREATE_NODE['uuid']
|
||||
@@ -448,6 +449,32 @@ fake_responses = {
|
||||
{},
|
||||
VIFS,
|
||||
),
|
||||
},
|
||||
'/v1/nodes/%s/traits' % NODE1['uuid']:
|
||||
{
|
||||
'GET': (
|
||||
{},
|
||||
TRAITS,
|
||||
),
|
||||
'PUT': (
|
||||
{},
|
||||
None,
|
||||
),
|
||||
'DELETE': (
|
||||
{},
|
||||
None,
|
||||
),
|
||||
},
|
||||
'/v1/nodes/%s/traits/CUSTOM_FOO' % NODE1['uuid']:
|
||||
{
|
||||
'PUT': (
|
||||
{},
|
||||
None,
|
||||
),
|
||||
'DELETE': (
|
||||
{},
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1641,3 +1668,49 @@ class NodeManagerTest(testtools.TestCase):
|
||||
self.assertEqual(4, mock_get.call_count)
|
||||
mock_sleep.assert_called_with(node._DEFAULT_POLL_INTERVAL)
|
||||
self.assertEqual(3, mock_sleep.call_count)
|
||||
|
||||
def test_node_get_traits(self):
|
||||
traits = self.mgr.get_traits(NODE1['uuid'])
|
||||
expect = [
|
||||
('GET', '/v1/nodes/%s/traits' % NODE1['uuid'], {}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertEqual(TRAITS['traits'], traits)
|
||||
|
||||
def test_node_add_trait(self):
|
||||
trait = 'CUSTOM_FOO'
|
||||
resp = self.mgr.add_trait(NODE1['uuid'], trait)
|
||||
expect = [
|
||||
('PUT', '/v1/nodes/%s/traits/%s' % (NODE1['uuid'], trait),
|
||||
{}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertIsNone(resp)
|
||||
|
||||
def test_node_set_traits(self):
|
||||
traits = ['CUSTOM_FOO', 'CUSTOM_BAR']
|
||||
resp = self.mgr.set_traits(NODE1['uuid'], traits)
|
||||
expect = [
|
||||
('PUT', '/v1/nodes/%s/traits' % NODE1['uuid'],
|
||||
{}, {'traits': traits}),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertIsNone(resp)
|
||||
|
||||
def test_node_remove_all_traits(self):
|
||||
resp = self.mgr.remove_all_traits(NODE1['uuid'])
|
||||
expect = [
|
||||
('DELETE', '/v1/nodes/%s/traits' % NODE1['uuid'], {}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertIsNone(resp)
|
||||
|
||||
def test_node_remove_trait(self):
|
||||
trait = 'CUSTOM_FOO'
|
||||
resp = self.mgr.remove_trait(NODE1['uuid'], trait)
|
||||
expect = [
|
||||
('DELETE', '/v1/nodes/%s/traits/%s' % (NODE1['uuid'], trait),
|
||||
{}, None),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
self.assertIsNone(resp)
|
||||
|
||||
@@ -65,6 +65,7 @@ class NodeShellTest(utils.BaseTestCase):
|
||||
'resource_class',
|
||||
'target_power_state',
|
||||
'target_provision_state',
|
||||
'traits',
|
||||
'updated_at',
|
||||
'inspection_finished_at',
|
||||
'inspection_started_at',
|
||||
|
||||
@@ -553,6 +553,53 @@ class NodeManager(base.CreateManager):
|
||||
path = "%s/vendor_passthru/methods" % node_ident
|
||||
return self._get_as_dict(path)
|
||||
|
||||
def get_traits(self, node_ident):
|
||||
"""Get traits for a node.
|
||||
|
||||
:param node_ident: node UUID or name.
|
||||
"""
|
||||
path = "%s/traits" % node_ident
|
||||
return self._list_primitives(self._path(path), 'traits')
|
||||
|
||||
def add_trait(self, node_ident, trait):
|
||||
"""Add a trait to a node.
|
||||
|
||||
:param node_ident: node UUID or name.
|
||||
:param trait: trait to add to the node.
|
||||
"""
|
||||
path = "%s/traits/%s" % (node_ident, trait)
|
||||
return self.update(path, None, http_method='PUT')
|
||||
|
||||
def set_traits(self, node_ident, traits):
|
||||
"""Set traits for a node.
|
||||
|
||||
Removes any existing traits and adds the traits passed in to this
|
||||
method.
|
||||
|
||||
:param node_ident: node UUID or name.
|
||||
:param traits: list of traits to add to the node.
|
||||
"""
|
||||
path = "%s/traits" % node_ident
|
||||
body = {'traits': traits}
|
||||
return self.update(path, body, http_method='PUT')
|
||||
|
||||
def remove_trait(self, node_ident, trait):
|
||||
"""Remove a trait from a node.
|
||||
|
||||
:param node_ident: node UUID or name.
|
||||
:param trait: trait to remove from the node.
|
||||
"""
|
||||
path = "%s/traits/%s" % (node_ident, trait)
|
||||
return self.delete(path)
|
||||
|
||||
def remove_all_traits(self, node_ident):
|
||||
"""Remove all traits from a node.
|
||||
|
||||
:param node_ident: node UUID or name.
|
||||
"""
|
||||
path = "%s/traits" % node_ident
|
||||
return self.delete(path)
|
||||
|
||||
def wait_for_provision_state(self, node_ident, expected_state,
|
||||
timeout=0,
|
||||
poll_interval=_DEFAULT_POLL_INTERVAL,
|
||||
|
||||
@@ -87,6 +87,7 @@ class Resource(object):
|
||||
'target_power_state': 'Target Power State',
|
||||
'target_provision_state': 'Target Provision State',
|
||||
'target_raid_config': 'Target RAID configuration',
|
||||
'traits': 'Traits',
|
||||
'type': 'Type',
|
||||
'updated_at': 'Updated At',
|
||||
'uuid': 'UUID',
|
||||
@@ -210,6 +211,7 @@ NODE_DETAILED_RESOURCE = Resource(
|
||||
'target_power_state',
|
||||
'target_provision_state',
|
||||
'target_raid_config',
|
||||
'traits',
|
||||
'updated_at',
|
||||
'inspection_finished_at',
|
||||
'inspection_started_at',
|
||||
@@ -239,6 +241,7 @@ NODE_DETAILED_RESOURCE = Resource(
|
||||
'properties',
|
||||
'raid_config',
|
||||
'target_raid_config',
|
||||
'traits',
|
||||
])
|
||||
NODE_RESOURCE = Resource(
|
||||
['uuid',
|
||||
@@ -319,6 +322,10 @@ VIF_RESOURCE = Resource(
|
||||
['id'],
|
||||
)
|
||||
|
||||
TRAIT_RESOURCE = Resource(
|
||||
['traits'],
|
||||
)
|
||||
|
||||
# Drivers
|
||||
DRIVER_DETAILED_RESOURCE = Resource(
|
||||
['name',
|
||||
|
||||
20
releasenotes/notes/traits-support-8864f6816abecdb2.yaml
Normal file
20
releasenotes/notes/traits-support-8864f6816abecdb2.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds support for reading and modifying traits for a node, including adding
|
||||
traits to the detailed output of a node. This is available starting
|
||||
with Bare Metal API version 1.37.
|
||||
|
||||
The new commands are:
|
||||
|
||||
* ``openstack baremetal node trait list <node>``
|
||||
* ``openstack baremetal node add trait <node> <trait> [...]``
|
||||
* ``openstack baremetal node remove trait <node> [<trait> [...]] [--all]``
|
||||
|
||||
It also adds the following methods to the Python SDK:
|
||||
|
||||
* ``NodeManager.get_traits``
|
||||
* ``NodeManager.add_trait``
|
||||
* ``NodeManager.set_traits``
|
||||
* ``NodeManager.remove_trait``
|
||||
* ``NodeManager.remove_all_traits``
|
||||
@@ -42,6 +42,7 @@ openstack.baremetal.v1 =
|
||||
baremetal_driver_raid_property_list = ironicclient.osc.v1.baremetal_driver:ListBaremetalDriverRaidProperty
|
||||
baremetal_driver_show = ironicclient.osc.v1.baremetal_driver:ShowBaremetalDriver
|
||||
baremetal_node_abort = ironicclient.osc.v1.baremetal_node:AbortBaremetalNode
|
||||
baremetal_node_add_trait = ironicclient.osc.v1.baremetal_node:AddTraitBaremetalNode
|
||||
baremetal_node_adopt = ironicclient.osc.v1.baremetal_node:AdoptBaremetalNode
|
||||
baremetal_node_boot_device_set = ironicclient.osc.v1.baremetal_node:BootdeviceSetBaremetalNode
|
||||
baremetal_node_boot_device_show = ironicclient.osc.v1.baremetal_node:BootdeviceShowBaremetalNode
|
||||
@@ -64,8 +65,10 @@ openstack.baremetal.v1 =
|
||||
baremetal_node_provide = ironicclient.osc.v1.baremetal_node:ProvideBaremetalNode
|
||||
baremetal_node_reboot = ironicclient.osc.v1.baremetal_node:RebootBaremetalNode
|
||||
baremetal_node_rebuild = ironicclient.osc.v1.baremetal_node:RebuildBaremetalNode
|
||||
baremetal_node_remove_trait = ironicclient.osc.v1.baremetal_node:RemoveTraitBaremetalNode
|
||||
baremetal_node_set = ironicclient.osc.v1.baremetal_node:SetBaremetalNode
|
||||
baremetal_node_show = ironicclient.osc.v1.baremetal_node:ShowBaremetalNode
|
||||
baremetal_node_trait_list = ironicclient.osc.v1.baremetal_node:ListTraitsBaremetalNode
|
||||
baremetal_node_undeploy = ironicclient.osc.v1.baremetal_node:UndeployBaremetalNode
|
||||
baremetal_node_unset = ironicclient.osc.v1.baremetal_node:UnsetBaremetalNode
|
||||
baremetal_node_validate = ironicclient.osc.v1.baremetal_node:ValidateBaremetalNode
|
||||
|
||||
Reference in New Issue
Block a user