Add `network_data` ironic node attribute support

Adds support for adding static network configuration to ironic node
object by adding ``network-data`` attribute to "node" osc
command.

Depends-On: https://review.opendev.org/#/c/687910/
Change-Id: I771911cbc2cfaef2cbac841fc76971e042c010c1
Story: 2006691
Task: 37072
This commit is contained in:
Ilya Etingof 2020-04-02 21:22:36 +02:00
parent fc31b5e1bd
commit a98cf22232
7 changed files with 96 additions and 4 deletions

View File

@ -37,7 +37,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 = 65
LAST_KNOWN_API_VERSION = 66
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
LOG = logging.getLogger(__name__)

View File

@ -39,6 +39,14 @@ CONFIG_DRIVE_ARG_HELP = _(
"more details).")
NETWORK_DATA_ARG_HELP = _(
"JSON string or a file or '-' for stdin to read static network "
"configuration for the baremetal node associated with this ironic node. "
"Format of this file should comply with Nova network data metadata "
"(network_data.json). Depending on ironic boot interface capabilities "
"being used, network configuration may or may not been served to the "
"node for offline network configuration.")
SUPPORTED_INTERFACES = ['bios', 'boot', 'console', 'deploy', 'inspect',
'management', 'network', 'power', 'raid', 'rescue',
'storage', 'vendor']
@ -402,6 +410,11 @@ class CreateBaremetalNode(command.ShowOne):
help=_('Management interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
parser.add_argument(
'--network-data',
metavar="<network data>",
dest='network_data',
help=NETWORK_DATA_ARG_HELP)
parser.add_argument(
'--network-interface',
metavar='<network_interface>',
@ -477,6 +490,9 @@ class CreateBaremetalNode(command.ShowOne):
if k in field_list and not (v is None))
fields = utils.args_array_to_dict(fields, 'driver_info')
fields = utils.args_array_to_dict(fields, 'extra')
if parsed_args.network_data:
fields['network_data'] = utils.handle_json_arg(
parsed_args.network_data, 'static network configuration')
fields = utils.args_array_to_dict(fields, 'properties')
node = baremetal_client.node.create(**fields)._info
@ -1123,6 +1139,12 @@ class SetBaremetalNode(command.Command):
reset_help=_('Reset the network interface to its hardware type '
'default'),
)
parser.add_argument(
'--network-data',
metavar="<network data>",
dest='network_data',
help=NETWORK_DATA_ARG_HELP
)
self._add_interface_args(
parser, 'power',
set_help=_('Set the power interface for the node'),
@ -1300,6 +1322,12 @@ class SetBaremetalNode(command.Command):
properties.extend(utils.args_array_to_patch(
'add', ['instance_info/' + x for x
in parsed_args.instance_info]))
if parsed_args.network_data:
network_data = utils.handle_json_arg(
parsed_args.network_data, 'static network configuration')
network_data = ["network_data=%s" % json.dumps(network_data)]
properties.extend(utils.args_array_to_patch('add', network_data))
if properties:
baremetal_client.node.update(
parsed_args.node, properties,
@ -1474,6 +1502,11 @@ class UnsetBaremetalNode(command.Command):
action='store_true',
help=_('Unset inspect interface on this baremetal node'),
)
parser.add_argument(
'--network-data',
action='store_true',
help=_("Unset network data on this baremetal port.")
)
parser.add_argument(
"--management-interface",
dest='management_interface',
@ -1607,6 +1640,9 @@ 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.network_data:
properties.extend(utils.args_array_to_patch(
'remove', ["network_data"]))
if properties:
baremetal_client.node.update(parsed_args.node, properties)
elif not parsed_args.target_raid_config:

View File

@ -415,6 +415,11 @@ class TestBaremetalCreate(TestBaremetal):
[('management_interface', 'management')],
{'management_interface': 'management'})
def test_baremetal_create_with_network_data(self):
self.check_with_options(['--network-data', '{"a": ["b"]}'],
[('network_data', '{"a": ["b"]}')],
{'network_data': {"a": ["b"]}})
def test_baremetal_create_with_network_interface(self):
self.check_with_options(['--network-interface', 'neutron'],
[('network_interface', 'neutron')],
@ -2779,10 +2784,33 @@ class TestBaremetalSet(TestBaremetal):
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.update.assert_called_once_with(
'node_uuid', [{'op': 'add',
'path': '/lessee',
'value': 'lessee 1'}],
reset_interfaces=None
)
@mock.patch.object(commonutils, 'get_from_stdin', autospec=True)
@mock.patch.object(commonutils, 'handle_json_or_file_arg', autospec=True)
def test_baremetal_set_network_data(self, mock_handle, mock_stdin):
self.cmd.log = mock.Mock(autospec=True)
network_data_string = '{"a": ["b"]}'
expected_network_data = {'a': ['b']}
mock_handle.return_value = expected_network_data.copy()
arglist = ['node_uuid',
'--network-data', network_data_string]
verifylist = [('node', 'node_uuid'),
('network_data', network_data_string)]
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': '/lessee',
'value': 'lessee 1',
[{'path': '/network_data', 'value': expected_network_data,
'op': 'add'}],
reset_interfaces=None,
)
@ -3405,6 +3433,25 @@ class TestBaremetalUnset(TestBaremetal):
[{'path': '/lessee', 'op': 'remove'}]
)
def test_baremetal_unset_network_data(self):
arglist = [
'node_uuid',
'--network-data',
]
verifylist = [
('node', 'node_uuid'),
('network_data', 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': '/network_data', 'op': 'remove'}]
)
class TestValidate(TestBaremetal):
def setUp(self):

View File

@ -36,6 +36,7 @@ NODE1 = {'uuid': '66666666-7777-8888-9999-000000000000',
'driver_info': {'user': 'foo', 'password': 'bar'},
'properties': {'num_cpu': 4},
'name': 'fake-node-1',
'network_data': {},
'resource_class': 'foo',
'extra': {},
'conductor_group': 'in-the-closet-to-the-left'}
@ -46,6 +47,7 @@ NODE2 = {'uuid': '66666666-7777-8888-9999-111111111111',
'driver': 'fake too',
'driver_info': {'user': 'foo', 'password': 'bar'},
'properties': {'num_cpu': 4},
'network_data': {},
'resource_class': 'bar',
'extra': {},
'owner': '33333333-2222-1111-0000-111111111111',

View File

@ -53,7 +53,7 @@ class NodeManager(base.CreateManager):
'raid_interface', 'rescue_interface',
'storage_interface', 'vendor_interface',
'resource_class', 'conductor_group',
'automated_clean']
'automated_clean', 'network_data']
_resource_name = 'nodes'
def list(self, associated=None, maintenance=None, marker=None,

View File

@ -93,6 +93,7 @@ class Resource(object):
'fault': 'Fault',
'mode': 'Mode',
'name': 'Name',
'network_data': 'Network Configuration',
'node_uuid': 'Node UUID',
'owner': 'Owner',
'power_state': 'Power State',

View File

@ -0,0 +1,6 @@
---
features:
- |
Adds support for adding static network configuration to ports and
portgroups by adding ``network-data`` attribute to port/portgroup
commands.