From 94abd4972da2af91e88a4496d51dc67b839ba1d0 Mon Sep 17 00:00:00 2001
From: Devananda van der Veen <devananda.vdv@gmail.com>
Date: Mon, 23 Feb 2015 16:47:30 -0800
Subject: [PATCH] Add support for logical names

Add client support for node logical names.

- update help strings to indicate places where Names can be used instead
  of UUIDs
- add new "-n" / "--name" parameter to "ironic node-create"
- add Name field to output of both "node-list" and "node-show"

Change-Id: Id16255e9611e95bd37a225ff475268f609726e37
Implements: blueprint logical-names
---
 ironicclient/tests/unit/v1/test_node.py       | 20 +++++++++++
 ironicclient/tests/unit/v1/test_node_shell.py |  9 +++++
 ironicclient/v1/node.py                       |  2 +-
 ironicclient/v1/node_shell.py                 | 36 +++++++++++--------
 ironicclient/v1/resource_fields.py            |  8 ++---
 5 files changed, 56 insertions(+), 19 deletions(-)

diff --git a/ironicclient/tests/unit/v1/test_node.py b/ironicclient/tests/unit/v1/test_node.py
index b834df2fc..801998940 100644
--- a/ironicclient/tests/unit/v1/test_node.py
+++ b/ironicclient/tests/unit/v1/test_node.py
@@ -32,6 +32,7 @@ NODE1 = {'id': 123,
          'driver': 'fake',
          'driver_info': {'user': 'foo', 'password': 'bar'},
          'properties': {'num_cpu': 4},
+         'name': 'fake-node-1',
          'extra': {}}
 NODE2 = {'id': 456,
          'uuid': '66666666-7777-8888-9999-111111111111',
@@ -165,6 +166,13 @@ fake_responses = {
             NODE2,
         ),
     },
+    '/v1/nodes/%s' % NODE1['name']:
+    {
+        'GET': (
+            {},
+            NODE1,
+        ),
+    },
     '/v1/nodes/%s/ports' % NODE1['uuid']:
     {
         'GET': (
@@ -342,6 +350,10 @@ class NodeManagerTest(testtools.TestCase):
         self.assertEqual(expect, self.api.calls)
         self.assertEqual(2, len(nodes))
 
+    def test_node_list_shows_name(self):
+        nodes = self.mgr.list()
+        self.assertIsNotNone(getattr(nodes[0], 'name'))
+
     def test_node_list_limit(self):
         self.api = utils.FakeAPI(fake_responses_pagination)
         self.mgr = node.NodeManager(self.api)
@@ -464,6 +476,14 @@ class NodeManagerTest(testtools.TestCase):
         self.assertEqual(expect, self.api.calls)
         self.assertEqual(NODE2['uuid'], node.uuid)
 
+    def test_node_show_by_name(self):
+        node = self.mgr.get(NODE1['name'])
+        expect = [
+            ('GET', '/v1/nodes/%s' % NODE1['name'], {}, None),
+        ]
+        self.assertEqual(expect, self.api.calls)
+        self.assertEqual(NODE1['uuid'], node.uuid)
+
     def test_create(self):
         node = self.mgr.create(**CREATE_NODE)
         expect = [
diff --git a/ironicclient/tests/unit/v1/test_node_shell.py b/ironicclient/tests/unit/v1/test_node_shell.py
index 1e6bf31fe..86807b63a 100644
--- a/ironicclient/tests/unit/v1/test_node_shell.py
+++ b/ironicclient/tests/unit/v1/test_node_shell.py
@@ -42,6 +42,7 @@ class NodeShellTest(utils.BaseTestCase):
                'last_error',
                'maintenance',
                'maintenance_reason',
+               'name',
                'power_state',
                'properties',
                'provision_state',
@@ -147,6 +148,14 @@ class NodeShellTest(utils.BaseTestCase):
         n_shell.do_node_create(client_mock, args)
         client_mock.node.create.assert_called_once_with(uuid=args.uuid)
 
+    def test_do_node_create_with_name(self):
+        client_mock = mock.MagicMock()
+        args = mock.MagicMock()
+        args.name = 'node_name'
+
+        n_shell.do_node_create(client_mock, args)
+        client_mock.node.create.assert_called_once_with(name=args.name)
+
     def test_do_node_show(self):
         client_mock = mock.MagicMock()
         args = mock.MagicMock()
diff --git a/ironicclient/v1/node.py b/ironicclient/v1/node.py
index 5624716f5..4ba8041c4 100644
--- a/ironicclient/v1/node.py
+++ b/ironicclient/v1/node.py
@@ -21,7 +21,7 @@ from ironicclient.common import utils
 from ironicclient import exc
 
 CREATION_ATTRIBUTES = ['chassis_uuid', 'driver', 'driver_info', 'extra',
-                       'uuid', 'properties']
+                       'uuid', 'properties', 'name']
 
 
 class Node(base.Resource):
diff --git a/ironicclient/v1/node_shell.py b/ironicclient/v1/node_shell.py
index 434775073..c4deff574 100644
--- a/ironicclient/v1/node_shell.py
+++ b/ironicclient/v1/node_shell.py
@@ -33,7 +33,8 @@ def _print_node_show(node):
 @cliutils.arg(
     'node',
     metavar='<id>',
-    help="UUID of the node (or instance UUID if --instance is specified).")
+    help="Name or UUID of the node "
+         "(or instance UUID if --instance is specified).")
 @cliutils.arg(
     '--instance',
     dest='instance_uuid',
@@ -151,10 +152,14 @@ def do_node_list(cc, args):
     '-u', '--uuid',
     metavar='<uuid>',
     help="Unique UUID for the node.")
+@cliutils.arg(
+    '-n', '--name',
+    metavar='<name>',
+    help="Unique name for the node.")
 def do_node_create(cc, args):
     """Register a new node with the Ironic service."""
     field_list = ['chassis_uuid', 'driver', 'driver_info',
-                  'properties', 'extra', 'uuid']
+                  'properties', 'extra', 'uuid', 'name']
     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')
@@ -166,7 +171,10 @@ def do_node_create(cc, args):
     cliutils.print_dict(data, wrap=72)
 
 
-@cliutils.arg('node', metavar='<node>', nargs='+', help="UUID of the node.")
+@cliutils.arg('node',
+              metavar='<node>',
+              nargs='+',
+              help="Name or UUID of the node.")
 def do_node_delete(cc, args):
     """Unregister a node from the Ironic service."""
     for n in args.node:
@@ -174,7 +182,7 @@ def do_node_delete(cc, args):
         print(_('Deleted node %s') % n)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 @cliutils.arg(
     'op',
     metavar='<op>',
@@ -197,7 +205,7 @@ def do_node_update(cc, args):
 
 @cliutils.arg('node',
               metavar='<node>',
-              help="UUID of the node.")
+              help="Name or UUID of the node.")
 @cliutils.arg('method',
               metavar='<method>',
               help="Vendor-passthru method to be called.")
@@ -279,7 +287,7 @@ def do_node_port_list(cc, args):
                         sortby_index=None)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 @cliutils.arg(
     'maintenance_mode',
     metavar='<maintenance-mode>',
@@ -300,7 +308,7 @@ def do_node_set_maintenance(cc, args):
                             maint_reason=args.reason)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 @cliutils.arg(
     'power_state',
     metavar='<power-state>',
@@ -311,7 +319,7 @@ def do_node_set_power_state(cc, args):
     cc.node.set_power_state(args.node, args.power_state)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 @cliutils.arg(
     'provision_state',
     metavar='<provision state>',
@@ -335,7 +343,7 @@ def do_node_set_provision_state(cc, args):
                                 configdrive=args.config_drive)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 def do_node_validate(cc, args):
     """Validate a node's driver interfaces."""
     ifaces = cc.node.validate(args.node)
@@ -349,14 +357,14 @@ def do_node_validate(cc, args):
     cliutils.print_list(obj_list, fields, field_labels=field_labels)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 def do_node_get_console(cc, args):
     """Get the connection information for a node's console, if enabled."""
     info = cc.node.get_console(args.node)
     cliutils.print_dict(info, wrap=72)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 @cliutils.arg(
     'enabled',
     metavar='<enabled>',
@@ -368,7 +376,7 @@ def do_node_set_console_mode(cc, args):
     cc.node.set_console_mode(args.node, args.enabled)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 @cliutils.arg(
     'device',
     metavar='<boot-device>',
@@ -385,14 +393,14 @@ def do_node_set_boot_device(cc, args):
     cc.node.set_boot_device(args.node, args.device, args.persistent)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 def do_node_get_boot_device(cc, args):
     """Get the current boot device for a node."""
     boot_device = cc.node.get_boot_device(args.node)
     cliutils.print_dict(boot_device, wrap=72)
 
 
-@cliutils.arg('node', metavar='<node>', help="UUID of the node.")
+@cliutils.arg('node', metavar='<node>', help="Name or UUID of the node.")
 def do_node_get_supported_boot_devices(cc, args):
     """Get the supported boot devices for a node."""
     boot_devices = cc.node.get_supported_boot_devices(args.node)
diff --git a/ironicclient/v1/resource_fields.py b/ironicclient/v1/resource_fields.py
index cff472abf..4c731014b 100644
--- a/ironicclient/v1/resource_fields.py
+++ b/ironicclient/v1/resource_fields.py
@@ -37,7 +37,7 @@ NODE_FIELDS = ['chassis_uuid', 'created_at', 'console_enabled', 'driver',
                'properties', 'provision_state', 'reservation',
                'target_power_state', 'target_provision_state',
                'updated_at', 'inspection_finished_at',
-               'inspection_started_at', 'uuid']
+               'inspection_started_at', 'uuid', 'name']
 
 NODE_FIELD_LABELS = ['Chassis UUID', 'Created At', 'Console Enabled', 'Driver',
                      'Driver Info', 'Driver Internal Info', 'Extra',
@@ -46,12 +46,12 @@ NODE_FIELD_LABELS = ['Chassis UUID', 'Created At', 'Console Enabled', 'Driver',
                      'Properties', 'Provision State', 'Reservation',
                      'Target Power State', 'Target Provision State',
                      'Updated At', 'Inspection Finished At',
-                     'Inspection Started At', 'UUID']
+                     'Inspection Started At', 'UUID', 'Name']
 
-NODE_LIST_FIELDS = ['uuid', 'instance_uuid', 'power_state',
+NODE_LIST_FIELDS = ['uuid', 'name', 'instance_uuid', 'power_state',
                     'provision_state', 'maintenance']
 
-NODE_LIST_FIELD_LABELS = ['UUID', 'Instance UUID', 'Power State',
+NODE_LIST_FIELD_LABELS = ['UUID', 'Name', 'Instance UUID', 'Power State',
                           'Provisioning State', 'Maintenance']