From 4855fef8b8f29542ebdaf86aa5da4daa1f97b060 Mon Sep 17 00:00:00 2001
From: tianhui <tianhui@awcloud.com>
Date: Mon, 25 Jun 2018 09:46:06 +0000
Subject: [PATCH] Compute: Add 'keypair create --type' parameter

Change-Id: I2d251e1b97fb9a8069431c867fb7fc5f42d1fd6e
Story: 2002606
Task: 22225
---
 openstackclient/compute/v2/keypair.py         |  42 +++--
 .../tests/unit/compute/v2/fakes.py            |   1 +
 .../tests/unit/compute/v2/test_keypair.py     | 144 +++++++++++++++---
 ...keypair-support-type-6f7c32aab3b61f7b.yaml |   5 +
 4 files changed, 161 insertions(+), 31 deletions(-)
 create mode 100644 releasenotes/notes/keypair-support-type-6f7c32aab3b61f7b.yaml

diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py
index 2b365cebff..6affaca302 100644
--- a/openstackclient/compute/v2/keypair.py
+++ b/openstackclient/compute/v2/keypair.py
@@ -20,6 +20,7 @@ import logging
 import os
 import sys
 
+from novaclient import api_versions
 from osc_lib.command import command
 from osc_lib import exceptions
 from osc_lib import utils
@@ -53,6 +54,15 @@ class CreateKeypair(command.ShowOne):
             help=_("Filename for private key to save. If not used, "
                    "print private key in console.")
         )
+        parser.add_argument(
+            '--type',
+            metavar='<type>',
+            choices=['ssh', 'x509'],
+            help=_(
+                "Keypair type. Can be ssh or x509. "
+                "(Supported by API versions '2.2' - '2.latest')"
+            ),
+        )
         return parser
 
     def take_action(self, parsed_args):
@@ -70,17 +80,28 @@ class CreateKeypair(command.ShowOne):
                            "exception": e}
                 )
 
-        keypair = compute_client.keypairs.create(
-            parsed_args.name,
-            public_key=public_key,
-        )
+        kwargs = {
+            'name': parsed_args.name,
+            'public_key': public_key,
+        }
+        if parsed_args.type:
+            if compute_client.api_version < api_versions.APIVersion('2.2'):
+                msg = _(
+                    '--os-compute-api-version 2.2 or greater is required to '
+                    'support the --type option.'
+                )
+                raise exceptions.CommandError(msg)
+
+            kwargs['key_type'] = parsed_args.type
+
+        keypair = compute_client.keypairs.create(**kwargs)
 
         private_key = parsed_args.private_key
         # Save private key into specified file
         if private_key:
             try:
                 with io.open(
-                        os.path.expanduser(parsed_args.private_key), 'w+'
+                    os.path.expanduser(parsed_args.private_key), 'w+'
                 ) as p:
                     p.write(keypair.private_key)
             except IOError as e:
@@ -150,10 +171,13 @@ class ListKeypair(command.Lister):
         )
         data = compute_client.keypairs.list()
 
-        return (columns,
-                (utils.get_item_properties(
-                    s, columns,
-                ) for s in data))
+        if compute_client.api_version >= api_versions.APIVersion('2.2'):
+            columns += ("Type", )
+
+        return (
+            columns,
+            (utils.get_item_properties(s, columns) for s in data),
+        )
 
 
 class ShowKeypair(command.ShowOne):
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 3143098474..2d66f53c41 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -877,6 +877,7 @@ class FakeKeypair(object):
         # Set default attributes.
         keypair_info = {
             'name': 'keypair-name-' + uuid.uuid4().hex,
+            'type': 'ssh',
             'fingerprint': 'dummy',
             'public_key': 'dummy',
             'user_id': 'user'
diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py
index 1f3f56f989..ca3bfe7c8b 100644
--- a/openstackclient/tests/unit/compute/v2/test_keypair.py
+++ b/openstackclient/tests/unit/compute/v2/test_keypair.py
@@ -17,6 +17,7 @@ from unittest import mock
 from unittest.mock import call
 import uuid
 
+from novaclient import api_versions
 from osc_lib import exceptions
 from osc_lib import utils
 
@@ -45,11 +46,13 @@ class TestKeypairCreate(TestKeypair):
         self.columns = (
             'fingerprint',
             'name',
+            'type',
             'user_id'
         )
         self.data = (
             self.keypair.fingerprint,
             self.keypair.name,
+            self.keypair.type,
             self.keypair.user_id
         )
 
@@ -71,7 +74,7 @@ class TestKeypairCreate(TestKeypair):
         columns, data = self.cmd.take_action(parsed_args)
 
         self.keypairs_mock.create.assert_called_with(
-            self.keypair.name,
+            name=self.keypair.name,
             public_key=None
         )
 
@@ -87,6 +90,7 @@ class TestKeypairCreate(TestKeypair):
         self.data = (
             self.keypair.fingerprint,
             self.keypair.name,
+            self.keypair.type,
             self.keypair.user_id
         )
 
@@ -96,7 +100,7 @@ class TestKeypairCreate(TestKeypair):
         ]
         verifylist = [
             ('public_key', self.keypair.public_key),
-            ('name', self.keypair.name)
+            ('name', self.keypair.name),
         ]
 
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -109,8 +113,8 @@ class TestKeypairCreate(TestKeypair):
             columns, data = self.cmd.take_action(parsed_args)
 
             self.keypairs_mock.create.assert_called_with(
-                self.keypair.name,
-                public_key=self.keypair.public_key
+                name=self.keypair.name,
+                public_key=self.keypair.public_key,
             )
 
             self.assertEqual(self.columns, columns)
@@ -124,7 +128,7 @@ class TestKeypairCreate(TestKeypair):
         ]
         verifylist = [
             ('private_key', tmp_pk_file),
-            ('name', self.keypair.name)
+            ('name', self.keypair.name),
         ]
 
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -136,8 +140,8 @@ class TestKeypairCreate(TestKeypair):
             columns, data = self.cmd.take_action(parsed_args)
 
             self.keypairs_mock.create.assert_called_with(
-                self.keypair.name,
-                public_key=None
+                name=self.keypair.name,
+                public_key=None,
             )
 
             mock_open.assert_called_once_with(tmp_pk_file, 'w+')
@@ -146,6 +150,79 @@ class TestKeypairCreate(TestKeypair):
             self.assertEqual(self.columns, columns)
             self.assertEqual(self.data, data)
 
+    def test_keypair_create_with_key_type(self):
+        self.app.client_manager.compute.api_version = api_versions.APIVersion(
+            '2.2')
+
+        for key_type in ['x509', 'ssh']:
+            self.keypair = compute_fakes.FakeKeypair.create_one_keypair(
+                no_pri=True)
+            self.keypairs_mock.create.return_value = self.keypair
+
+            self.data = (
+                self.keypair.fingerprint,
+                self.keypair.name,
+                self.keypair.type,
+                self.keypair.user_id,
+            )
+            arglist = [
+                '--public-key', self.keypair.public_key,
+                self.keypair.name,
+                '--type', key_type,
+            ]
+            verifylist = [
+                ('public_key', self.keypair.public_key),
+                ('name', self.keypair.name),
+                ('type', key_type),
+            ]
+            parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+            with mock.patch('io.open') as mock_open:
+                mock_open.return_value = mock.MagicMock()
+                m_file = mock_open.return_value.__enter__.return_value
+                m_file.read.return_value = 'dummy'
+                columns, data = self.cmd.take_action(parsed_args)
+
+            self.keypairs_mock.create.assert_called_with(
+                name=self.keypair.name,
+                public_key=self.keypair.public_key,
+                key_type=key_type,
+            )
+
+            self.assertEqual(self.columns, columns)
+            self.assertEqual(self.data, data)
+
+    def test_keypair_create_with_key_type_pre_v22(self):
+        self.app.client_manager.compute.api_version = api_versions.APIVersion(
+            '2.1')
+
+        for key_type in ['x509', 'ssh']:
+            arglist = [
+                '--public-key', self.keypair.public_key,
+                self.keypair.name,
+                '--type', 'ssh',
+            ]
+            verifylist = [
+                ('public_key', self.keypair.public_key),
+                ('name', self.keypair.name),
+                ('type', 'ssh'),
+            ]
+            parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+            with mock.patch('io.open') as mock_open:
+                mock_open.return_value = mock.MagicMock()
+                m_file = mock_open.return_value.__enter__.return_value
+                m_file.read.return_value = 'dummy'
+
+                ex = self.assertRaises(
+                    exceptions.CommandError,
+                    self.cmd.take_action,
+                    parsed_args)
+
+            self.assertIn(
+                '--os-compute-api-version 2.2 or greater is required',
+                str(ex))
+
 
 class TestKeypairDelete(TestKeypair):
 
@@ -227,16 +304,6 @@ class TestKeypairList(TestKeypair):
     # Return value of self.keypairs_mock.list().
     keypairs = compute_fakes.FakeKeypair.create_keypairs(count=1)
 
-    columns = (
-        "Name",
-        "Fingerprint"
-    )
-
-    data = ((
-        keypairs[0].name,
-        keypairs[0].fingerprint
-    ), )
-
     def setUp(self):
         super(TestKeypairList, self).setUp()
 
@@ -247,8 +314,7 @@ class TestKeypairList(TestKeypair):
 
     def test_keypair_list_no_options(self):
         arglist = []
-        verifylist = [
-        ]
+        verifylist = []
 
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
@@ -261,8 +327,39 @@ class TestKeypairList(TestKeypair):
 
         self.keypairs_mock.list.assert_called_with()
 
-        self.assertEqual(self.columns, columns)
-        self.assertEqual(tuple(self.data), tuple(data))
+        self.assertEqual(('Name', 'Fingerprint'), columns)
+        self.assertEqual(
+            ((self.keypairs[0].name, self.keypairs[0].fingerprint), ),
+            tuple(data)
+        )
+
+    def test_keypair_list_v22(self):
+        self.app.client_manager.compute.api_version = api_versions.APIVersion(
+            '2.2')
+
+        arglist = []
+        verifylist = []
+
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        # In base command class Lister in cliff, abstract method take_action()
+        # returns a tuple containing the column names and an iterable
+        # containing the data to be listed.
+        columns, data = self.cmd.take_action(parsed_args)
+
+        # Set expected values
+
+        self.keypairs_mock.list.assert_called_with()
+
+        self.assertEqual(('Name', 'Fingerprint', 'Type'), columns)
+        self.assertEqual(
+            ((
+                self.keypairs[0].name,
+                self.keypairs[0].fingerprint,
+                self.keypairs[0].type,
+            ), ),
+            tuple(data)
+        )
 
 
 class TestKeypairShow(TestKeypair):
@@ -279,16 +376,18 @@ class TestKeypairShow(TestKeypair):
         self.columns = (
             "fingerprint",
             "name",
+            "type",
             "user_id"
         )
 
         self.data = (
             self.keypair.fingerprint,
             self.keypair.name,
+            self.keypair.type,
             self.keypair.user_id
         )
 
-    def test_show_no_options(self):
+    def test_keypair_show_no_options(self):
 
         arglist = []
         verifylist = []
@@ -306,6 +405,7 @@ class TestKeypairShow(TestKeypair):
         self.data = (
             self.keypair.fingerprint,
             self.keypair.name,
+            self.keypair.type,
             self.keypair.user_id
         )
 
diff --git a/releasenotes/notes/keypair-support-type-6f7c32aab3b61f7b.yaml b/releasenotes/notes/keypair-support-type-6f7c32aab3b61f7b.yaml
new file mode 100644
index 0000000000..549629d8a4
--- /dev/null
+++ b/releasenotes/notes/keypair-support-type-6f7c32aab3b61f7b.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - Add ``--key-type`` option to ``keypair create`` command to set keypair
+    type. Can be ssh or x509. Note that ``--os-compute-api-version 2.2`` or
+    later is required.