From 70e6333e7d4b3fa87496ff3a888527693875273b Mon Sep 17 00:00:00 2001
From: Terry Howe <terrylhowe@gmail.com>
Date: Thu, 6 Mar 2014 12:50:37 -0700
Subject: [PATCH] Add ability to set key value pairs in projects

Add supporto of extra key value pairs for projects (aka tenants)
* Added option --property key=value to create and set commands
* Support for versions v2 and v3

Change-Id: I84064b8b308579d1b66c83b1ed3d1a37614ec087
Closes-Bug: #1220280
---
 openstackclient/identity/v2_0/project.py      | 22 ++++++
 openstackclient/identity/v3/project.py        | 22 ++++++
 .../tests/identity/v2_0/test_project.py       | 65 +++++++++++++++++
 .../tests/identity/v3/test_project.py         | 69 +++++++++++++++++++
 4 files changed, 178 insertions(+)

diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py
index 2d0acb8fe0..60a52ad4a6 100644
--- a/openstackclient/identity/v2_0/project.py
+++ b/openstackclient/identity/v2_0/project.py
@@ -22,6 +22,7 @@ from cliff import command
 from cliff import lister
 from cliff import show
 
+from openstackclient.common import parseractions
 from openstackclient.common import utils
 
 
@@ -53,6 +54,13 @@ class CreateProject(show.ShowOne):
             action='store_true',
             help='Disable project',
         )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add for this project '
+                 '(repeat option to set multiple properties)',
+        )
         return parser
 
     def take_action(self, parsed_args):
@@ -62,11 +70,15 @@ class CreateProject(show.ShowOne):
         enabled = True
         if parsed_args.disable:
             enabled = False
+        kwargs = {}
+        if parsed_args.property:
+            kwargs = parsed_args.property.copy()
 
         project = identity_client.tenants.create(
             parsed_args.name,
             description=parsed_args.description,
             enabled=enabled,
+            **kwargs
         )
 
         info = {}
@@ -163,6 +175,13 @@ class SetProject(command.Command):
             action='store_true',
             help='Disable project',
         )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add for this project '
+                 '(repeat option to set multiple properties)',
+        )
         return parser
 
     def take_action(self, parsed_args):
@@ -172,6 +191,7 @@ class SetProject(command.Command):
         if (not parsed_args.name
                 and not parsed_args.description
                 and not parsed_args.enable
+                and not parsed_args.property
                 and not parsed_args.disable):
             return
 
@@ -189,6 +209,8 @@ class SetProject(command.Command):
             kwargs['enabled'] = True
         if parsed_args.disable:
             kwargs['enabled'] = False
+        if parsed_args.property:
+            kwargs.update(parsed_args.property)
         if 'id' in kwargs:
             del kwargs['id']
         if 'name' in kwargs:
diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py
index f245a88898..ebae733df9 100644
--- a/openstackclient/identity/v3/project.py
+++ b/openstackclient/identity/v3/project.py
@@ -22,6 +22,7 @@ from cliff import command
 from cliff import lister
 from cliff import show
 
+from openstackclient.common import parseractions
 from openstackclient.common import utils
 
 
@@ -58,6 +59,13 @@ class CreateProject(show.ShowOne):
             action='store_true',
             help='Disable project',
         )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add for this project '
+                 '(repeat option to set multiple properties)',
+        )
         return parser
 
     def take_action(self, parsed_args):
@@ -75,12 +83,16 @@ class CreateProject(show.ShowOne):
         enabled = True
         if parsed_args.disable:
             enabled = False
+        kwargs = {}
+        if parsed_args.property:
+            kwargs = parsed_args.property.copy()
 
         project = identity_client.projects.create(
             parsed_args.name,
             domain,
             description=parsed_args.description,
             enabled=enabled,
+            **kwargs
         )
 
         info = {}
@@ -182,6 +194,13 @@ class SetProject(command.Command):
             action='store_true',
             help='Disable project',
         )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add for this project '
+                 '(repeat option to set multiple properties)',
+        )
         return parser
 
     def take_action(self, parsed_args):
@@ -192,6 +211,7 @@ class SetProject(command.Command):
                 and not parsed_args.description
                 and not parsed_args.domain
                 and not parsed_args.enable
+                and not parsed_args.property
                 and not parsed_args.disable):
             return
 
@@ -214,6 +234,8 @@ class SetProject(command.Command):
             kwargs['enabled'] = True
         if parsed_args.disable:
             kwargs['enabled'] = False
+        if parsed_args.property:
+            kwargs.update(parsed_args.property)
         if 'id' in kwargs:
             del kwargs['id']
         if 'domain_id' in kwargs:
diff --git a/openstackclient/tests/identity/v2_0/test_project.py b/openstackclient/tests/identity/v2_0/test_project.py
index 30f4278bea..d046cd4796 100644
--- a/openstackclient/tests/identity/v2_0/test_project.py
+++ b/openstackclient/tests/identity/v2_0/test_project.py
@@ -182,6 +182,43 @@ class TestProjectCreate(TestProject):
         )
         self.assertEqual(data, datalist)
 
+    def test_project_create_property(self):
+        arglist = [
+            '--property', 'fee=fi',
+            '--property', 'fo=fum',
+            identity_fakes.project_name,
+        ]
+        verifylist = [
+            ('property', {'fee': 'fi', 'fo': 'fum'}),
+            ('name', identity_fakes.project_name),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        # DisplayCommandBase.take_action() returns two tuples
+        columns, data = self.cmd.take_action(parsed_args)
+
+        # Set expected values
+        kwargs = {
+            'description': None,
+            'enabled': True,
+            'fee': 'fi',
+            'fo': 'fum',
+        }
+        self.projects_mock.create.assert_called_with(
+            identity_fakes.project_name,
+            **kwargs
+        )
+
+        collist = ('description', 'enabled', 'id', 'name')
+        self.assertEqual(columns, collist)
+        datalist = (
+            identity_fakes.project_description,
+            True,
+            identity_fakes.project_id,
+            identity_fakes.project_name,
+        )
+        self.assertEqual(data, datalist)
+
 
 class TestProjectDelete(TestProject):
 
@@ -412,6 +449,34 @@ class TestProjectSet(TestProject):
             **kwargs
         )
 
+    def test_project_set_property(self):
+        arglist = [
+            '--property', 'fee=fi',
+            '--property', 'fo=fum',
+            identity_fakes.project_name,
+        ]
+        verifylist = [
+            ('property', {'fee': 'fi', 'fo': 'fum'}),
+            ('project', identity_fakes.project_name),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        result = self.cmd.run(parsed_args)
+        self.assertEqual(result, 0)
+
+        # Set expected values
+        kwargs = {
+            'description': identity_fakes.project_description,
+            'enabled': True,
+            'tenant_name': identity_fakes.project_name,
+            'fee': 'fi',
+            'fo': 'fum',
+        }
+        self.projects_mock.update.assert_called_with(
+            identity_fakes.project_id,
+            **kwargs
+        )
+
 
 class TestProjectShow(TestProject):
 
diff --git a/openstackclient/tests/identity/v3/test_project.py b/openstackclient/tests/identity/v3/test_project.py
index 02cb41bedb..517c73c594 100644
--- a/openstackclient/tests/identity/v3/test_project.py
+++ b/openstackclient/tests/identity/v3/test_project.py
@@ -245,6 +245,46 @@ class TestProjectCreate(TestProject):
         )
         self.assertEqual(data, datalist)
 
+    def test_project_create_property(self):
+        arglist = [
+            '--property', 'fee=fi',
+            '--property', 'fo=fum',
+            identity_fakes.project_name,
+        ]
+        verifylist = [
+            ('property', {'fee': 'fi', 'fo': 'fum'}),
+            ('name', identity_fakes.project_name),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        # DisplayCommandBase.take_action() returns two tuples
+        columns, data = self.cmd.take_action(parsed_args)
+
+        # Set expected values
+        kwargs = {
+            'description': None,
+            'enabled': True,
+            'fee': 'fi',
+            'fo': 'fum',
+        }
+        # ProjectManager.create(name, domain, description=, enabled=, **kwargs)
+        self.projects_mock.create.assert_called_with(
+            identity_fakes.project_name,
+            None,
+            **kwargs
+        )
+
+        collist = ('description', 'domain_id', 'enabled', 'id', 'name')
+        self.assertEqual(columns, collist)
+        datalist = (
+            identity_fakes.project_description,
+            identity_fakes.domain_id,
+            True,
+            identity_fakes.project_id,
+            identity_fakes.project_name,
+        )
+        self.assertEqual(data, datalist)
+
 
 class TestProjectDelete(TestProject):
 
@@ -488,6 +528,35 @@ class TestProjectSet(TestProject):
             **kwargs
         )
 
+    def test_project_set_property(self):
+        arglist = [
+            '--property', 'fee=fi',
+            '--property', 'fo=fum',
+            identity_fakes.project_name,
+        ]
+        verifylist = [
+            ('property', {'fee': 'fi', 'fo': 'fum'}),
+            ('project', identity_fakes.project_name),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        result = self.cmd.run(parsed_args)
+        self.assertEqual(result, 0)
+
+        # Set expected values
+        kwargs = {
+            'description': identity_fakes.project_description,
+            'domain': identity_fakes.domain_id,
+            'enabled': True,
+            'name': identity_fakes.project_name,
+            'fee': 'fi',
+            'fo': 'fum',
+        }
+        self.projects_mock.update.assert_called_with(
+            identity_fakes.project_id,
+            **kwargs
+        )
+
 
 class TestProjectShow(TestProject):