From d1ea9b44f6f88638ff4d007a68d7dae9c359662b Mon Sep 17 00:00:00 2001 From: Vasyl Saienko Date: Thu, 30 Jun 2016 06:17:24 -0400 Subject: [PATCH] Updates supporting ironic-neutron integration This patchset adds port creation with new optional parameters specifying new port attributes (local_link_connection, pxe_enabled). It also adds new parameter network_interface to node object. Co-Authored-By: Vasyl Saienko (vsaienko@mirantis.com) Co-Authored-By: William Stevenson (will.stevenson@sap.com) Partial-bug: #1526403 Depends-on: I67495196c3334f51ed034f4ca6e32a3e01a58f15 Change-Id: If2fb996783b9ac26a5bae2aadd6387207750def9 --- ironicclient/osc/plugin.py | 2 +- ironicclient/osc/v1/baremetal_node.py | 18 +++++++++++- ironicclient/osc/v1/baremetal_port.py | 16 ++++++++++- .../tests/unit/osc/v1/test_baremetal_node.py | 28 ++++++++++++++++++- ironicclient/tests/unit/v1/test_node.py | 1 - ironicclient/tests/unit/v1/test_node_shell.py | 1 + ironicclient/tests/unit/v1/test_port.py | 10 +++++++ ironicclient/tests/unit/v1/test_port_shell.py | 2 +- ironicclient/v1/node.py | 3 +- ironicclient/v1/node_shell.py | 8 +++++- ironicclient/v1/port.py | 3 +- ironicclient/v1/port_shell.py | 16 ++++++++++- ironicclient/v1/resource_fields.py | 6 ++++ ...n-integration-fields-cee7596c49722de6.yaml | 14 ++++++++++ 14 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 releasenotes/notes/add-neutron-integration-fields-cee7596c49722de6.yaml diff --git a/ironicclient/osc/plugin.py b/ironicclient/osc/plugin.py index 7f54cc338..48e03718c 100644 --- a/ironicclient/osc/plugin.py +++ b/ironicclient/osc/plugin.py @@ -23,7 +23,7 @@ LOG = logging.getLogger(__name__) DEFAULT_BAREMETAL_API_VERSION = '1.6' API_VERSION_OPTION = 'os_baremetal_api_version' API_NAME = 'baremetal' -LAST_KNOWN_API_VERSION = 14 +LAST_KNOWN_API_VERSION = 20 API_VERSIONS = { '1.%d' % i: 'ironicclient.v1.client.Client' for i in range(1, LAST_KNOWN_API_VERSION + 1) diff --git a/ironicclient/osc/v1/baremetal_node.py b/ironicclient/osc/v1/baremetal_node.py index e8e1b4a31..ca1e61216 100644 --- a/ironicclient/osc/v1/baremetal_node.py +++ b/ironicclient/osc/v1/baremetal_node.py @@ -151,6 +151,11 @@ class CreateBaremetalNode(show.ShowOne): '--name', metavar='', help="Unique name for the node.") + parser.add_argument( + '--network-interface', + metavar='', + help='Network interface used for switching node to ' + 'cleaning/provisioning networks.') return parser @@ -160,7 +165,8 @@ class CreateBaremetalNode(show.ShowOne): baremetal_client = self.app.client_manager.baremetal field_list = ['chassis_uuid', 'driver', 'driver_info', - 'properties', 'extra', 'uuid', 'name'] + 'properties', 'extra', 'uuid', 'name', + 'network_interface'] fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and not (v is None)) fields = utils.args_array_to_dict(fields, 'driver_info') @@ -554,6 +560,11 @@ class SetBaremetalNode(command.Command): metavar="", help="Set the driver for the node", ) + parser.add_argument( + '--network-interface', + metavar='', + help='Set the network interface for the node', + ) parser.add_argument( "--property", metavar="", @@ -603,6 +614,11 @@ class SetBaremetalNode(command.Command): driver = ["driver=%s" % parsed_args.driver] properties.extend(utils.args_array_to_patch( 'add', driver)) + if parsed_args.network_interface: + network_interface = [ + "network_interface=%s" % parsed_args.network_interface] + properties.extend(utils.args_array_to_patch( + 'add', network_interface)) if parsed_args.property: properties.extend(utils.args_array_to_patch( 'add', ['properties/' + x for x in parsed_args.property])) diff --git a/ironicclient/osc/v1/baremetal_port.py b/ironicclient/osc/v1/baremetal_port.py index 3ae5fe978..30c063f4f 100644 --- a/ironicclient/osc/v1/baremetal_port.py +++ b/ironicclient/osc/v1/baremetal_port.py @@ -46,6 +46,18 @@ class CreateBaremetalPort(show.ShowOne): action='append', help="Record arbitrary key/value metadata. " "Can be specified multiple times.") + parser.add_argument( + '-l', '--local-link-connection', + metavar="", + action='append', + help="Key/value metadata describing Local link connection " + "information. Valid keys are switch_info, switch_id, " + "port_id. Can be specified multiple times.") + parser.add_argument( + '--pxe-enabled', + metavar='', + help='Indicates whether this Port should be used when ' + 'PXE booting this Node.') return parser @@ -53,10 +65,12 @@ class CreateBaremetalPort(show.ShowOne): self.log.debug("take_action(%s)" % parsed_args) baremetal_client = self.app.client_manager.baremetal - field_list = ['address', 'extra', 'node_uuid'] + field_list = ['address', 'extra', 'node_uuid', 'pxe_enabled', + 'local_link_connection'] fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and v is not None) fields = utils.args_array_to_dict(fields, 'extra') + fields = utils.args_array_to_dict(fields, 'local_link_connection') port = baremetal_client.port.create(**fields) data = dict([(f, getattr(port, f, '')) for f in diff --git a/ironicclient/tests/unit/osc/v1/test_baremetal_node.py b/ironicclient/tests/unit/osc/v1/test_baremetal_node.py index 538fa29fa..1be4b92df 100644 --- a/ironicclient/tests/unit/osc/v1/test_baremetal_node.py +++ b/ironicclient/tests/unit/osc/v1/test_baremetal_node.py @@ -162,6 +162,11 @@ class TestBaremetalCreate(TestBaremetal): [('name', 'name')], {'name': 'name'}) + def test_baremetal_create_with_network_interface(self): + self.check_with_options(['--network-interface', 'neutron'], + [('network_interface', 'neutron')], + {'network_interface': 'neutron'}) + class TestBaremetalDelete(TestBaremetal): def setUp(self): @@ -315,7 +320,8 @@ class TestBaremetalList(TestBaremetal): 'Target Power State', 'Target Provision State', 'Target RAID configuration', 'Updated At', 'Inspection Finished At', - 'Inspection Started At', 'UUID', 'Name') + 'Inspection Started At', 'UUID', 'Name', + 'Network Interface') self.assertEqual(collist, columns) datalist = (( '', @@ -345,6 +351,7 @@ class TestBaremetalList(TestBaremetal): '', baremetal_fakes.baremetal_uuid, baremetal_fakes.baremetal_name, + '', ), ) self.assertEqual(datalist, tuple(data)) @@ -801,6 +808,25 @@ class TestBaremetalSet(TestBaremetal): [{'path': '/driver', 'value': 'xxxxx', 'op': 'add'}] ) + def test_baremetal_set_network_interface(self): + arglist = [ + 'node_uuid', + '--network-interface', 'xxxxx', + ] + verifylist = [ + ('node', 'node_uuid'), + ('network_interface', 'xxxxx') + ] + + 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_interface', 'value': 'xxxxx', 'op': 'add'}] + ) + def test_baremetal_set_extra(self): arglist = [ 'node_uuid', diff --git a/ironicclient/tests/unit/v1/test_node.py b/ironicclient/tests/unit/v1/test_node.py index e4de7cd06..ad69b43b2 100644 --- a/ironicclient/tests/unit/v1/test_node.py +++ b/ironicclient/tests/unit/v1/test_node.py @@ -53,7 +53,6 @@ PORT = {'id': 456, 'node_id': 123, 'address': 'AA:AA:AA:AA:AA:AA', 'extra': {}} - POWER_STATE = {'power_state': 'power off', 'target_power_state': 'power on'} diff --git a/ironicclient/tests/unit/v1/test_node_shell.py b/ironicclient/tests/unit/v1/test_node_shell.py index 8825717a9..907a2e8f4 100644 --- a/ironicclient/tests/unit/v1/test_node_shell.py +++ b/ironicclient/tests/unit/v1/test_node_shell.py @@ -46,6 +46,7 @@ class NodeShellTest(utils.BaseTestCase): 'maintenance', 'maintenance_reason', 'name', + 'network_interface', 'power_state', 'properties', 'provision_state', diff --git a/ironicclient/tests/unit/v1/test_port.py b/ironicclient/tests/unit/v1/test_port.py index 1c7998a92..9004e9e0f 100644 --- a/ironicclient/tests/unit/v1/test_port.py +++ b/ironicclient/tests/unit/v1/test_port.py @@ -26,12 +26,16 @@ PORT = {'id': 987, 'uuid': '11111111-2222-3333-4444-555555555555', 'node_uuid': '55555555-4444-3333-2222-111111111111', 'address': 'AA:BB:CC:DD:EE:FF', + 'pxe_enabled': True, + 'local_link_connection': {}, 'extra': {}} PORT2 = {'id': 988, 'uuid': '55555555-4444-3333-2222-111111111111', 'node_uuid': '55555555-4444-3333-2222-111111111111', 'address': 'AA:AA:AA:BB:BB:BB', + 'pxe_enabled': True, + 'local_link_connection': {}, 'extra': {}} CREATE_PORT = copy.deepcopy(PORT) @@ -263,6 +267,9 @@ class PortManagerTest(testtools.TestCase): self.assertEqual(PORT['uuid'], port.uuid) self.assertEqual(PORT['address'], port.address) self.assertEqual(PORT['node_uuid'], port.node_uuid) + self.assertEqual(PORT['pxe_enabled'], port.pxe_enabled) + self.assertEqual(PORT['local_link_connection'], + port.local_link_connection) def test_ports_show_by_address(self): port = self.mgr.get_by_address(PORT['address']) @@ -274,6 +281,9 @@ class PortManagerTest(testtools.TestCase): self.assertEqual(PORT['uuid'], port.uuid) self.assertEqual(PORT['address'], port.address) self.assertEqual(PORT['node_uuid'], port.node_uuid) + self.assertEqual(PORT['pxe_enabled'], port.pxe_enabled) + self.assertEqual(PORT['local_link_connection'], + port.local_link_connection) def test_port_show_fields(self): port = self.mgr.get(PORT['uuid'], fields=['uuid', 'address']) diff --git a/ironicclient/tests/unit/v1/test_port_shell.py b/ironicclient/tests/unit/v1/test_port_shell.py index 71197288c..bd274fe77 100644 --- a/ironicclient/tests/unit/v1/test_port_shell.py +++ b/ironicclient/tests/unit/v1/test_port_shell.py @@ -32,7 +32,7 @@ class PortShellTest(utils.BaseTestCase): port = object() p_shell._print_port_show(port) exp = ['address', 'created_at', 'extra', 'node_uuid', 'updated_at', - 'uuid'] + 'uuid', 'pxe_enabled', 'local_link_connection'] act = actual.keys() self.assertEqual(sorted(exp), sorted(act)) diff --git a/ironicclient/v1/node.py b/ironicclient/v1/node.py index b6d52313f..bf91c0877 100644 --- a/ironicclient/v1/node.py +++ b/ironicclient/v1/node.py @@ -37,7 +37,8 @@ class Node(base.Resource): class NodeManager(base.CreateManager): resource_class = Node _creation_attributes = ['chassis_uuid', 'driver', 'driver_info', - 'extra', 'uuid', 'properties', 'name'] + 'extra', 'uuid', 'properties', 'name', + 'network_interface'] _resource_name = 'nodes' def list(self, associated=None, maintenance=None, marker=None, limit=None, diff --git a/ironicclient/v1/node_shell.py b/ironicclient/v1/node_shell.py index 7e7217520..69fbba810 100644 --- a/ironicclient/v1/node_shell.py +++ b/ironicclient/v1/node_shell.py @@ -202,10 +202,16 @@ def do_node_list(cc, args): '-n', '--name', metavar='', help="Unique name for the node.") +@cliutils.arg( + '--network-interface', + metavar='', + help='Network interface used for switching node to cleaning/provisioning ' + 'networks.') def do_node_create(cc, args): """Register a new node with the Ironic service.""" field_list = ['chassis_uuid', 'driver', 'driver_info', - 'properties', 'extra', 'uuid', 'name'] + 'properties', 'extra', 'uuid', 'name', + 'network_interface'] fields = dict((k, v) for (k, v) in vars(args).items() if k in field_list and not (v is None)) fields = utils.args_array_to_dict(fields, 'driver_info') diff --git a/ironicclient/v1/port.py b/ironicclient/v1/port.py index d84512db8..703437cb4 100644 --- a/ironicclient/v1/port.py +++ b/ironicclient/v1/port.py @@ -27,7 +27,8 @@ class Port(base.Resource): class PortManager(base.CreateManager): resource_class = Port - _creation_attributes = ['address', 'extra', 'node_uuid', 'uuid'] + _creation_attributes = ['address', 'extra', 'local_link_connection', + 'node_uuid', 'pxe_enabled', 'uuid'] _resource_name = 'ports' def list(self, address=None, limit=None, marker=None, sort_key=None, diff --git a/ironicclient/v1/port_shell.py b/ironicclient/v1/port_shell.py index 9ef1be2d1..492324ea9 100644 --- a/ironicclient/v1/port_shell.py +++ b/ironicclient/v1/port_shell.py @@ -143,6 +143,18 @@ def do_port_list(cc, args): metavar='', required=True, help='UUID of the node that this port belongs to.') +@cliutils.arg( + '-l', '--local-link-connection', + metavar="", + action='append', + help="Key/value metadata describing Local link connection information. " + "Valid keys are switch_info, switch_id, port_id." + "Can be specified multiple times.") +@cliutils.arg( + '--pxe-enabled', + metavar='', + help='Indicates whether this Port should be used when ' + 'PXE booting this Node.') @cliutils.arg( '-e', '--extra', metavar="", @@ -155,10 +167,12 @@ def do_port_list(cc, args): help="UUID of the port.") def do_port_create(cc, args): """Create a new port.""" - field_list = ['address', 'extra', 'node_uuid', 'uuid'] + field_list = ['address', 'extra', 'node_uuid', 'uuid', + 'local_link_connection', 'pxe_enabled'] fields = dict((k, v) for (k, v) in vars(args).items() if k in field_list and not (v is None)) fields = utils.args_array_to_dict(fields, 'extra') + fields = utils.args_array_to_dict(fields, 'local_link_connection') port = cc.port.create(**fields) data = dict([(f, getattr(port, f, '')) for f in field_list]) diff --git a/ironicclient/v1/resource_fields.py b/ironicclient/v1/resource_fields.py index f5f962013..a137b075e 100644 --- a/ironicclient/v1/resource_fields.py +++ b/ironicclient/v1/resource_fields.py @@ -64,6 +64,9 @@ class Resource(object): 'target_raid_config': 'Target RAID configuration', 'updated_at': 'Updated At', 'uuid': 'UUID', + 'local_link_connection': 'Local Link Connection', + 'pxe_enabled': 'PXE boot enabled', + 'network_interface': 'Network Interface', } def __init__(self, field_ids, sort_excluded=None): @@ -151,6 +154,7 @@ NODE_DETAILED_RESOURCE = Resource( 'inspection_started_at', 'uuid', 'name', + 'network_interface', ], sort_excluded=[ # The server cannot sort on "chassis_uuid" because it isn't a column in @@ -189,6 +193,8 @@ PORT_DETAILED_RESOURCE = Resource( 'created_at', 'extra', 'node_uuid', + 'local_link_connection', + 'pxe_enabled', 'updated_at', ], sort_excluded=[ diff --git a/releasenotes/notes/add-neutron-integration-fields-cee7596c49722de6.yaml b/releasenotes/notes/add-neutron-integration-fields-cee7596c49722de6.yaml new file mode 100644 index 000000000..e78de131e --- /dev/null +++ b/releasenotes/notes/add-neutron-integration-fields-cee7596c49722de6.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + Add support of new fields: + + * ``node.network_interface`` is introduced in API 1.20, + specifies the network interface to use for a node. + * ``port.local_link_connection`` contains the port binding + profile. + * ``port.pxe_enabled`` indicates whether PXE is enabled + for the port. + + The ``port.local_link_connection`` and ``port.pxe_enabled`` fields + were introduced in API 1.19.