From 4a68ba625ce39de6e6d260ff06e0b0d88512f794 Mon Sep 17 00:00:00 2001
From: tianhui <tianhui@awcloud.com>
Date: Thu, 14 Jun 2018 18:20:04 +0800
Subject: [PATCH] Compute: Add description support for flavor

Co-Authored-By: Fan Zhang <zh.f@outlook.com>
Change-Id: I0dc80bee3ba6ff4ec8cc3fc113b6de7807e0bf2a
Story: 2002196
Task: 21681
---
 doc/source/cli/command-objects/flavor.rst     |  10 ++
 openstackclient/compute/v2/flavor.py          |  28 +++-
 .../tests/unit/compute/v2/fakes.py            |   1 +
 .../tests/unit/compute/v2/test_flavor.py      | 136 +++++++++++++++++-
 ...avor-add-description-b618abd4a7fb6545.yaml |   6 +
 5 files changed, 177 insertions(+), 4 deletions(-)
 create mode 100644 releasenotes/notes/flavor-add-description-b618abd4a7fb6545.yaml

diff --git a/doc/source/cli/command-objects/flavor.rst b/doc/source/cli/command-objects/flavor.rst
index 1cbd2df3bb..2d946de308 100644
--- a/doc/source/cli/command-objects/flavor.rst
+++ b/doc/source/cli/command-objects/flavor.rst
@@ -24,6 +24,7 @@ Create new flavor
         [--property <key=value> [...] ]
         [--project <project>]
         [--project-domain <project-domain>]
+        [--description <description>]
         <flavor-name>
 
 .. option:: --id <id>
@@ -76,6 +77,10 @@ Create new flavor
     Domain the project belongs to (name or ID).
     This can be used in case collisions between project names exist.
 
+.. option:: --description <description>
+
+    Description to add for this flavor
+
 .. _flavor_create-flavor-name:
 .. describe:: <flavor-name>
 
@@ -148,6 +153,7 @@ Set flavor properties
         [--property <key=value> [...] ]
         [--project <project>]
         [--project-domain <project-domain>]
+        [--description <description>]
         <flavor>
 
 .. option:: --property <key=value>
@@ -168,6 +174,10 @@ Set flavor properties
     Remove all properties from this flavor (specify both --no-property and --property
     to remove the current properties before setting new properties.)
 
+.. option:: --description <description>
+
+    Set description to this flavor
+
 .. describe:: <flavor>
 
     Flavor to modify (name or ID)
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index 0f5dd7421e..2cc5f1e891 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -17,6 +17,7 @@
 
 import logging
 
+from novaclient import api_versions
 from osc_lib.cli import parseractions
 from osc_lib.command import command
 from osc_lib import exceptions
@@ -134,6 +135,12 @@ class CreateFlavor(command.ShowOne):
             help=_("Allow <project> to access private flavor (name or ID) "
                    "(Must be used with --private option)"),
         )
+        parser.add_argument(
+            '--description',
+            metavar='<description>',
+            help=_("Description for the flavor.(Supported by API versions "
+                   "'2.55' - '2.latest'")
+        )
         identity_common.add_project_domain_option_to_parser(parser)
         return parser
 
@@ -145,6 +152,11 @@ class CreateFlavor(command.ShowOne):
             msg = _("--project is only allowed with --private")
             raise exceptions.CommandError(msg)
 
+        if parsed_args.description:
+            if compute_client.api_version < api_versions.APIVersion("2.55"):
+                msg = _("--os-compute-api-version 2.55 or later is required")
+                raise exceptions.CommandError(msg)
+
         args = (
             parsed_args.name,
             parsed_args.ram,
@@ -154,7 +166,8 @@ class CreateFlavor(command.ShowOne):
             parsed_args.ephemeral,
             parsed_args.swap,
             parsed_args.rxtx_factor,
-            parsed_args.public
+            parsed_args.public,
+            parsed_args.description
         )
 
         flavor = compute_client.flavors.create(*args)
@@ -332,6 +345,12 @@ class SetFlavor(command.Command):
             help=_('Set flavor access to project (name or ID) '
                    '(admin only)'),
         )
+        parser.add_argument(
+            '--description',
+            metavar='<description>',
+            help=_("Set description for the flavor.(Supported by API "
+                   "versions '2.55' - '2.latest'")
+        )
         identity_common.add_project_domain_option_to_parser(parser)
 
         return parser
@@ -380,6 +399,13 @@ class SetFlavor(command.Command):
             raise exceptions.CommandError(_("Command Failed: One or more of"
                                             " the operations failed"))
 
+        if parsed_args.description:
+            if compute_client.api_version < api_versions.APIVersion("2.55"):
+                msg = _("--os-compute-api-version 2.55 or later is required")
+                raise exceptions.CommandError(msg)
+            compute_client.flavors.update(flavor=parsed_args.flavor,
+                                          description=parsed_args.description)
+
 
 class ShowFlavor(command.ShowOne):
     _description = _("Display flavor details")
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 46fa599284..9a065be13d 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -765,6 +765,7 @@ class FakeFlavor(object):
             'rxtx_factor': 1.0,
             'OS-FLV-DISABLED:disabled': False,
             'os-flavor-access:is_public': True,
+            'description': 'description',
             'OS-FLV-EXT-DATA:ephemeral': 0,
             'properties': {'property': 'value'},
         }
diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py
index 4cdbb25ba0..a112fc1fae 100644
--- a/openstackclient/tests/unit/compute/v2/test_flavor.py
+++ b/openstackclient/tests/unit/compute/v2/test_flavor.py
@@ -16,6 +16,7 @@
 import mock
 from mock import call
 
+import novaclient
 from osc_lib import exceptions
 from osc_lib import utils
 
@@ -50,6 +51,7 @@ class TestFlavorCreate(TestFlavor):
     columns = (
         'OS-FLV-DISABLED:disabled',
         'OS-FLV-EXT-DATA:ephemeral',
+        'description',
         'disk',
         'id',
         'name',
@@ -63,6 +65,7 @@ class TestFlavorCreate(TestFlavor):
     data = (
         flavor.disabled,
         flavor.ephemeral,
+        flavor.description,
         flavor.disk,
         flavor.id,
         flavor.name,
@@ -101,7 +104,8 @@ class TestFlavorCreate(TestFlavor):
             0,
             0,
             1.0,
-            True
+            True,
+            None,
         )
         columns, data = self.cmd.take_action(parsed_args)
         self.flavors_mock.create.assert_called_once_with(*default_args)
@@ -120,6 +124,7 @@ class TestFlavorCreate(TestFlavor):
             '--vcpus', str(self.flavor.vcpus),
             '--rxtx-factor', str(self.flavor.rxtx_factor),
             '--public',
+            '--description', str(self.flavor.description),
             '--property', 'property=value',
             self.flavor.name,
         ]
@@ -132,6 +137,7 @@ class TestFlavorCreate(TestFlavor):
             ('vcpus', self.flavor.vcpus),
             ('rxtx_factor', self.flavor.rxtx_factor),
             ('public', True),
+            ('description', self.flavor.description),
             ('property', {'property': 'value'}),
             ('name', self.flavor.name),
         ]
@@ -147,8 +153,13 @@ class TestFlavorCreate(TestFlavor):
             self.flavor.swap,
             self.flavor.rxtx_factor,
             self.flavor.is_public,
+            self.flavor.description,
         )
-        columns, data = self.cmd.take_action(parsed_args)
+        self.app.client_manager.compute.api_version = 2.55
+        with mock.patch.object(novaclient.api_versions,
+                               'APIVersion',
+                               return_value=2.55):
+            columns, data = self.cmd.take_action(parsed_args)
         self.flavors_mock.create.assert_called_once_with(*args)
         self.flavor.set_keys.assert_called_once_with({'property': 'value'})
         self.flavor.get_keys.assert_called_once_with()
@@ -168,6 +179,7 @@ class TestFlavorCreate(TestFlavor):
             '--vcpus', str(self.flavor.vcpus),
             '--rxtx-factor', str(self.flavor.rxtx_factor),
             '--private',
+            '--description', str(self.flavor.description),
             '--project', self.project.id,
             '--property', 'key1=value1',
             '--property', 'key2=value2',
@@ -181,6 +193,7 @@ class TestFlavorCreate(TestFlavor):
             ('vcpus', self.flavor.vcpus),
             ('rxtx_factor', self.flavor.rxtx_factor),
             ('public', False),
+            ('description', 'description'),
             ('project', self.project.id),
             ('property', {'key1': 'value1', 'key2': 'value2'}),
             ('name', self.flavor.name),
@@ -197,8 +210,13 @@ class TestFlavorCreate(TestFlavor):
             self.flavor.swap,
             self.flavor.rxtx_factor,
             self.flavor.is_public,
+            self.flavor.description,
         )
-        columns, data = self.cmd.take_action(parsed_args)
+        self.app.client_manager.compute.api_version = 2.55
+        with mock.patch.object(novaclient.api_versions,
+                               'APIVersion',
+                               return_value=2.55):
+            columns, data = self.cmd.take_action(parsed_args)
         self.flavors_mock.create.assert_called_once_with(*args)
         self.flavor_access_mock.add_tenant_access.assert_called_with(
             self.flavor.id,
@@ -234,6 +252,79 @@ class TestFlavorCreate(TestFlavor):
                           arglist,
                           verifylist)
 
+    def test_flavor_create_with_description_api_newer(self):
+        arglist = [
+            '--id', self.flavor.id,
+            '--ram', str(self.flavor.ram),
+            '--disk', str(self.flavor.disk),
+            '--ephemeral', str(self.flavor.ephemeral),
+            '--swap', str(self.flavor.swap),
+            '--vcpus', str(self.flavor.vcpus),
+            '--rxtx-factor', str(self.flavor.rxtx_factor),
+            '--private',
+            '--description', 'fake description',
+            self.flavor.name,
+        ]
+        verifylist = [
+            ('id', self.flavor.id),
+            ('ram', self.flavor.ram),
+            ('disk', self.flavor.disk),
+            ('ephemeral', self.flavor.ephemeral),
+            ('swap', self.flavor.swap),
+            ('vcpus', self.flavor.vcpus),
+            ('rxtx_factor', self.flavor.rxtx_factor),
+            ('public', False),
+            ('description', 'fake description'),
+            ('name', self.flavor.name),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        self.app.client_manager.compute.api_version = 2.55
+        with mock.patch.object(novaclient.api_versions,
+                               'APIVersion',
+                               return_value=2.55):
+            columns, data = self.cmd.take_action(parsed_args)
+
+        args = (
+            self.flavor.name,
+            self.flavor.ram,
+            self.flavor.vcpus,
+            self.flavor.disk,
+            self.flavor.id,
+            self.flavor.ephemeral,
+            self.flavor.swap,
+            self.flavor.rxtx_factor,
+            False,
+            'fake description',
+        )
+
+        self.flavors_mock.create.assert_called_once_with(*args)
+
+        self.assertEqual(self.columns, columns)
+        self.assertEqual(self.data, data)
+
+    def test_flavor_create_with_description_api_older(self):
+        arglist = [
+            '--id', self.flavor.id,
+            '--ram', str(self.flavor.ram),
+            '--vcpus', str(self.flavor.vcpus),
+            '--description', 'description',
+            self.flavor.name,
+        ]
+        verifylist = [
+            ('ram', self.flavor.ram),
+            ('vcpus', self.flavor.vcpus),
+            ('description', 'description'),
+            ('name', self.flavor.name),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        self.app.client_manager.compute.api_version = 2.54
+        with mock.patch.object(novaclient.api_versions,
+                               'APIVersion',
+                               return_value=2.55):
+            self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+                              parsed_args)
+
 
 class TestFlavorDelete(TestFlavor):
 
@@ -622,6 +713,42 @@ class TestFlavorSet(TestFlavor):
         self.flavor_access_mock.add_tenant_access.assert_not_called()
         self.assertIsNone(result)
 
+    def test_flavor_set_description_api_newer(self):
+        arglist = [
+            '--description', 'description',
+            self.flavor.id,
+        ]
+        verifylist = [
+            ('description', 'description'),
+            ('flavor', self.flavor.id),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        self.app.client_manager.compute.api_version = 2.55
+        with mock.patch.object(novaclient.api_versions,
+                               'APIVersion',
+                               return_value=2.55):
+            result = self.cmd.take_action(parsed_args)
+            self.flavors_mock.update.assert_called_with(
+                flavor=self.flavor.id, description='description')
+            self.assertIsNone(result)
+
+    def test_flavor_set_description_api_older(self):
+        arglist = [
+            '--description', 'description',
+            self.flavor.id,
+        ]
+        verifylist = [
+            ('description', 'description'),
+            ('flavor', self.flavor.id),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        self.app.client_manager.compute.api_version = 2.54
+        with mock.patch.object(novaclient.api_versions,
+                               'APIVersion',
+                               return_value=2.55):
+            self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+                              parsed_args)
+
 
 class TestFlavorShow(TestFlavor):
 
@@ -633,6 +760,7 @@ class TestFlavorShow(TestFlavor):
         'OS-FLV-DISABLED:disabled',
         'OS-FLV-EXT-DATA:ephemeral',
         'access_project_ids',
+        'description',
         'disk',
         'id',
         'name',
@@ -648,6 +776,7 @@ class TestFlavorShow(TestFlavor):
         flavor.disabled,
         flavor.ephemeral,
         None,
+        flavor.description,
         flavor.disk,
         flavor.id,
         flavor.name,
@@ -710,6 +839,7 @@ class TestFlavorShow(TestFlavor):
             private_flavor.disabled,
             private_flavor.ephemeral,
             self.flavor_access.tenant_id,
+            private_flavor.description,
             private_flavor.disk,
             private_flavor.id,
             private_flavor.name,
diff --git a/releasenotes/notes/flavor-add-description-b618abd4a7fb6545.yaml b/releasenotes/notes/flavor-add-description-b618abd4a7fb6545.yaml
new file mode 100644
index 0000000000..f148175f79
--- /dev/null
+++ b/releasenotes/notes/flavor-add-description-b618abd4a7fb6545.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - Add ``--description`` option to ``flavor set`` command to update the
+    description of the server.
+  - Add ``--description`` option to ``flavor create`` command to set the
+    description of the server.