From 1a7284f63ad13f41c6ff4295d69f065310242524 Mon Sep 17 00:00:00 2001
From: Rui Chen <chenrui.momo@gmail.com>
Date: Sat, 28 May 2016 18:55:50 +0800
Subject: [PATCH] Support to set server state

When a server is unexpected state, OSC don't support reset
the server to active or error state, that's supported by
novaclient, and it's an important command for operators, the
patch implement this function.

Change-Id: I3e7800feb192832b0719ef9a353945beb6bfd509
Implements: blueprint server-reset-state
---
 doc/source/command-objects/server.rst         |  5 ++
 openstackclient/compute/v2/server.py          |  9 +++
 .../tests/compute/v2/test_server.py           | 61 +++++++++++++++++++
 .../server-set-state-214b12ec2161de4d.yaml    |  6 ++
 4 files changed, 81 insertions(+)
 create mode 100644 releasenotes/notes/server-set-state-214b12ec2161de4d.yaml

diff --git a/doc/source/command-objects/server.rst b/doc/source/command-objects/server.rst
index bf972986a9..f11355b6dc 100644
--- a/doc/source/command-objects/server.rst
+++ b/doc/source/command-objects/server.rst
@@ -559,6 +559,7 @@ Set server properties
         --property <key=value>
         [--property <key=value>] ...
         --root-password
+        --state <state>
         <server>
 
 .. option:: --name <new-name>
@@ -574,6 +575,10 @@ Set server properties
     Property to add/change for this server
     (repeat option to set multiple properties)
 
+.. option:: --state <state>
+
+    New server state (valid value: active, error)
+
 .. describe:: <server>
 
     Server (name or ID)
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 781ccb1b8e..2312575abf 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -1370,6 +1370,12 @@ class SetServer(command.Command):
             help=_('Property to add/change for this server '
                    '(repeat option to set multiple properties)'),
         )
+        parser.add_argument(
+            '--state',
+            metavar='<state>',
+            choices=['active', 'error'],
+            help=_('New server state (valid value: active, error)'),
+        )
         return parser
 
     def take_action(self, parsed_args):
@@ -1389,6 +1395,9 @@ class SetServer(command.Command):
                 parsed_args.property,
             )
 
+        if parsed_args.state:
+            server.reset_state(state=parsed_args.state)
+
         if parsed_args.root_password:
             p1 = getpass.getpass(_('New password: '))
             p2 = getpass.getpass(_('Retype new password: '))
diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py
index 7d184b3ae9..2dfdb68ae7 100644
--- a/openstackclient/tests/compute/v2/test_server.py
+++ b/openstackclient/tests/compute/v2/test_server.py
@@ -1213,6 +1213,67 @@ class TestServerResume(TestServer):
         self.run_method_with_servers('resume', 3)
 
 
+class TestServerSet(TestServer):
+
+    def setUp(self):
+        super(TestServerSet, self).setUp()
+
+        self.methods = {
+            'update': None,
+            'reset_state': None,
+            'change_password': None,
+        }
+
+        self.fake_servers = self.setup_servers_mock(2)
+
+        # Get the command object to test
+        self.cmd = server.SetServer(self.app, None)
+
+    def test_server_set_no_option(self):
+        arglist = [
+            'foo_vm'
+        ]
+        verifylist = [
+            ('server', 'foo_vm')
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        result = self.cmd.take_action(parsed_args)
+        self.assertNotCalled(self.fake_servers[0].update)
+        self.assertNotCalled(self.fake_servers[0].reset_state)
+        self.assertNotCalled(self.fake_servers[0].change_password)
+        self.assertNotCalled(self.servers_mock.set_meta)
+        self.assertIsNone(result)
+
+    def test_server_set_with_state(self):
+        for index, state in enumerate(['active', 'error']):
+            arglist = [
+                '--state', state,
+                'foo_vm',
+            ]
+            verifylist = [
+                ('state', state),
+                ('server', 'foo_vm'),
+            ]
+            parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+            result = self.cmd.take_action(parsed_args)
+            self.fake_servers[index].reset_state.assert_called_once_with(
+                state=state)
+            self.assertIsNone(result)
+
+    def test_server_set_with_invalid_state(self):
+        arglist = [
+            '--state', 'foo_state',
+            'foo_vm',
+        ]
+        verifylist = [
+            ('state', 'foo_state'),
+            ('server', 'foo_vm'),
+        ]
+        self.assertRaises(utils.ParserException,
+                          self.check_parser,
+                          self.cmd, arglist, verifylist)
+
+
 class TestServerShelve(TestServer):
 
     def setUp(self):
diff --git a/releasenotes/notes/server-set-state-214b12ec2161de4d.yaml b/releasenotes/notes/server-set-state-214b12ec2161de4d.yaml
new file mode 100644
index 0000000000..a48384a802
--- /dev/null
+++ b/releasenotes/notes/server-set-state-214b12ec2161de4d.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add ``--state`` option to ``server set`` command to set the server to
+    active or error state.
+    [Blueprint `server-reset-state <https://blueprints.launchpad.net/python-openstackclient/+spec/server-reset-state>`_]