From 81431d24a9f94f56c4c39cb12bf846871f6230d8 Mon Sep 17 00:00:00 2001
From: Huanxuan Ao <huanxuan.ao@easystack.cn>
Date: Tue, 16 Aug 2016 18:46:55 +0800
Subject: [PATCH] Add "volume service set" command

Add "volume service set" command in volume v1 and v2
(v1 is the same as v2) to disable or enable volume service.

Change-Id: Ibb2db7e93b24cb2e0d2a7c28b6fd8bcc851b8d2f
Closes-Bug: #1613597
---
 doc/source/command-objects/volume-service.rst |  34 ++++
 .../tests/volume/v1/test_service.py           | 145 ++++++++++++++++++
 .../tests/volume/v2/test_service.py           | 145 ++++++++++++++++++
 openstackclient/volume/v1/service.py          |  56 +++++++
 openstackclient/volume/v2/service.py          |  56 +++++++
 .../notes/bug-1613597-b1545148b0755e6f.yaml   |   4 +
 setup.cfg                                     |   2 +
 7 files changed, 442 insertions(+)
 create mode 100644 releasenotes/notes/bug-1613597-b1545148b0755e6f.yaml

diff --git a/doc/source/command-objects/volume-service.rst b/doc/source/command-objects/volume-service.rst
index aa9fa6d262..d7495968fb 100644
--- a/doc/source/command-objects/volume-service.rst
+++ b/doc/source/command-objects/volume-service.rst
@@ -29,3 +29,37 @@ List volume service
 .. option:: --long
 
     List additional fields in output
+
+volume service set
+------------------
+
+Set volume service properties
+
+.. program:: volume service set
+.. code:: bash
+
+    os volume service set
+        [--enable | --disable]
+        [--disable-reason <reason>]
+        <host> <service>
+
+.. option:: --enable
+
+    Enable volume service
+
+.. option:: --disable
+
+    Disable volume service
+
+.. option:: --disable-reason <reason>
+
+    Reason for disabling the service (should be used with --disable option)
+
+.. _volume-service-set:
+.. describe:: <host>
+
+    Name of host
+
+.. describe:: <service>
+
+    Name of service (Binary name)
diff --git a/openstackclient/tests/volume/v1/test_service.py b/openstackclient/tests/volume/v1/test_service.py
index 7168434496..2578d76b59 100644
--- a/openstackclient/tests/volume/v1/test_service.py
+++ b/openstackclient/tests/volume/v1/test_service.py
@@ -12,6 +12,7 @@
 #   under the License.
 #
 
+from osc_lib import exceptions
 
 from openstackclient.tests.volume.v1 import fakes as service_fakes
 from openstackclient.volume.v1 import service
@@ -139,3 +140,147 @@ class TestServiceList(TestService):
             self.services.host,
             self.services.binary,
         )
+
+
+class TestServiceSet(TestService):
+
+    service = service_fakes.FakeService.create_one_service()
+
+    def setUp(self):
+        super(TestServiceSet, self).setUp()
+
+        self.service_mock.enable.return_value = self.service
+        self.service_mock.disable.return_value = self.service
+        self.service_mock.disable_log_reason.return_value = self.service
+
+        self.cmd = service.SetService(self.app, None)
+
+    def test_service_set_nothing(self):
+        arglist = [
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        result = self.cmd.take_action(parsed_args)
+
+        self.service_mock.enable.assert_not_called()
+        self.service_mock.disable.assert_not_called()
+        self.service_mock.disable_log_reason.assert_not_called()
+        self.assertIsNone(result)
+
+    def test_service_set_enable(self):
+        arglist = [
+            '--enable',
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('enable', True),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        result = self.cmd.take_action(parsed_args)
+
+        self.service_mock.enable.assert_called_with(
+            self.service.host,
+            self.service.binary
+        )
+        self.service_mock.disable.assert_not_called()
+        self.service_mock.disable_log_reason.assert_not_called()
+        self.assertIsNone(result)
+
+    def test_service_set_disable(self):
+        arglist = [
+            '--disable',
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('disable', True),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        result = self.cmd.take_action(parsed_args)
+
+        self.service_mock.disable.assert_called_with(
+            self.service.host,
+            self.service.binary
+        )
+        self.service_mock.enable.assert_not_called()
+        self.service_mock.disable_log_reason.assert_not_called()
+        self.assertIsNone(result)
+
+    def test_service_set_disable_with_reason(self):
+        reason = 'earthquake'
+        arglist = [
+            '--disable',
+            '--disable-reason', reason,
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('disable', True),
+            ('disable_reason', reason),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        result = self.cmd.take_action(parsed_args)
+
+        self.service_mock.disable_log_reason.assert_called_with(
+            self.service.host,
+            self.service.binary,
+            reason
+        )
+        self.assertIsNone(result)
+
+    def test_service_set_only_with_disable_reason(self):
+        reason = 'earthquake'
+        arglist = [
+            '--disable-reason', reason,
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('disable_reason', reason),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        try:
+            self.cmd.take_action(parsed_args)
+            self.fail("CommandError should be raised.")
+        except exceptions.CommandError as e:
+            self.assertEqual("Cannot specify option --disable-reason without "
+                             "--disable specified.", str(e))
+
+    def test_service_set_enable_with_disable_reason(self):
+        reason = 'earthquake'
+        arglist = [
+            '--enable',
+            '--disable-reason', reason,
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('enable', True),
+            ('disable_reason', reason),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        try:
+            self.cmd.take_action(parsed_args)
+            self.fail("CommandError should be raised.")
+        except exceptions.CommandError as e:
+            self.assertEqual("Cannot specify option --disable-reason without "
+                             "--disable specified.", str(e))
diff --git a/openstackclient/tests/volume/v2/test_service.py b/openstackclient/tests/volume/v2/test_service.py
index ba2e1b3217..2c432b2b2a 100644
--- a/openstackclient/tests/volume/v2/test_service.py
+++ b/openstackclient/tests/volume/v2/test_service.py
@@ -12,6 +12,7 @@
 #   under the License.
 #
 
+from osc_lib import exceptions
 
 from openstackclient.tests.volume.v2 import fakes as service_fakes
 from openstackclient.volume.v2 import service
@@ -139,3 +140,147 @@ class TestServiceList(TestService):
             self.services.host,
             self.services.binary,
         )
+
+
+class TestServiceSet(TestService):
+
+    service = service_fakes.FakeService.create_one_service()
+
+    def setUp(self):
+        super(TestServiceSet, self).setUp()
+
+        self.service_mock.enable.return_value = self.service
+        self.service_mock.disable.return_value = self.service
+        self.service_mock.disable_log_reason.return_value = self.service
+
+        self.cmd = service.SetService(self.app, None)
+
+    def test_service_set_nothing(self):
+        arglist = [
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        result = self.cmd.take_action(parsed_args)
+
+        self.service_mock.enable.assert_not_called()
+        self.service_mock.disable.assert_not_called()
+        self.service_mock.disable_log_reason.assert_not_called()
+        self.assertIsNone(result)
+
+    def test_service_set_enable(self):
+        arglist = [
+            '--enable',
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('enable', True),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        result = self.cmd.take_action(parsed_args)
+
+        self.service_mock.enable.assert_called_with(
+            self.service.host,
+            self.service.binary
+        )
+        self.service_mock.disable.assert_not_called()
+        self.service_mock.disable_log_reason.assert_not_called()
+        self.assertIsNone(result)
+
+    def test_service_set_disable(self):
+        arglist = [
+            '--disable',
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('disable', True),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        result = self.cmd.take_action(parsed_args)
+
+        self.service_mock.disable.assert_called_with(
+            self.service.host,
+            self.service.binary
+        )
+        self.service_mock.enable.assert_not_called()
+        self.service_mock.disable_log_reason.assert_not_called()
+        self.assertIsNone(result)
+
+    def test_service_set_disable_with_reason(self):
+        reason = 'earthquake'
+        arglist = [
+            '--disable',
+            '--disable-reason', reason,
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('disable', True),
+            ('disable_reason', reason),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        result = self.cmd.take_action(parsed_args)
+
+        self.service_mock.disable_log_reason.assert_called_with(
+            self.service.host,
+            self.service.binary,
+            reason
+        )
+        self.assertIsNone(result)
+
+    def test_service_set_only_with_disable_reason(self):
+        reason = 'earthquake'
+        arglist = [
+            '--disable-reason', reason,
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('disable_reason', reason),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        try:
+            self.cmd.take_action(parsed_args)
+            self.fail("CommandError should be raised.")
+        except exceptions.CommandError as e:
+            self.assertEqual("Cannot specify option --disable-reason without "
+                             "--disable specified.", str(e))
+
+    def test_service_set_enable_with_disable_reason(self):
+        reason = 'earthquake'
+        arglist = [
+            '--enable',
+            '--disable-reason', reason,
+            self.service.host,
+            self.service.binary,
+        ]
+        verifylist = [
+            ('enable', True),
+            ('disable_reason', reason),
+            ('host', self.service.host),
+            ('service', self.service.binary),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+        try:
+            self.cmd.take_action(parsed_args)
+            self.fail("CommandError should be raised.")
+        except exceptions.CommandError as e:
+            self.assertEqual("Cannot specify option --disable-reason without "
+                             "--disable specified.", str(e))
diff --git a/openstackclient/volume/v1/service.py b/openstackclient/volume/v1/service.py
index 2df38573d4..867c4b9c7f 100644
--- a/openstackclient/volume/v1/service.py
+++ b/openstackclient/volume/v1/service.py
@@ -15,6 +15,7 @@
 """Service action implementations"""
 
 from osc_lib.command import command
+from osc_lib import exceptions
 from osc_lib import utils
 
 from openstackclient.i18n import _
@@ -72,3 +73,58 @@ class ListService(command.Lister):
                 (utils.get_item_properties(
                     s, columns,
                 ) for s in data))
+
+
+class SetService(command.Command):
+    """Set volume service properties"""
+
+    def get_parser(self, prog_name):
+        parser = super(SetService, self).get_parser(prog_name)
+        parser.add_argument(
+            "host",
+            metavar="<host>",
+            help=_("Name of host")
+        )
+        parser.add_argument(
+            "service",
+            metavar="<service>",
+            help=_("Name of service (Binary name)")
+        )
+        enabled_group = parser.add_mutually_exclusive_group()
+        enabled_group.add_argument(
+            "--enable",
+            action="store_true",
+            help=_("Enable volume service")
+        )
+        enabled_group.add_argument(
+            "--disable",
+            action="store_true",
+            help=_("Disable volume service")
+        )
+        parser.add_argument(
+            "--disable-reason",
+            metavar="<reason>",
+            help=_("Reason for disabling the service "
+                   "(should be used with --disable option)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        if parsed_args.disable_reason and not parsed_args.disable:
+            msg = _("Cannot specify option --disable-reason without "
+                    "--disable specified.")
+            raise exceptions.CommandError(msg)
+
+        service_client = self.app.client_manager.volume
+        if parsed_args.enable:
+            service_client.services.enable(
+                parsed_args.host, parsed_args.service)
+        if parsed_args.disable:
+            if parsed_args.disable_reason:
+                service_client.services.disable_log_reason(
+                    parsed_args.host,
+                    parsed_args.service,
+                    parsed_args.disable_reason)
+            else:
+                service_client.services.disable(
+                    parsed_args.host, parsed_args.service)
diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py
index 2df38573d4..867c4b9c7f 100644
--- a/openstackclient/volume/v2/service.py
+++ b/openstackclient/volume/v2/service.py
@@ -15,6 +15,7 @@
 """Service action implementations"""
 
 from osc_lib.command import command
+from osc_lib import exceptions
 from osc_lib import utils
 
 from openstackclient.i18n import _
@@ -72,3 +73,58 @@ class ListService(command.Lister):
                 (utils.get_item_properties(
                     s, columns,
                 ) for s in data))
+
+
+class SetService(command.Command):
+    """Set volume service properties"""
+
+    def get_parser(self, prog_name):
+        parser = super(SetService, self).get_parser(prog_name)
+        parser.add_argument(
+            "host",
+            metavar="<host>",
+            help=_("Name of host")
+        )
+        parser.add_argument(
+            "service",
+            metavar="<service>",
+            help=_("Name of service (Binary name)")
+        )
+        enabled_group = parser.add_mutually_exclusive_group()
+        enabled_group.add_argument(
+            "--enable",
+            action="store_true",
+            help=_("Enable volume service")
+        )
+        enabled_group.add_argument(
+            "--disable",
+            action="store_true",
+            help=_("Disable volume service")
+        )
+        parser.add_argument(
+            "--disable-reason",
+            metavar="<reason>",
+            help=_("Reason for disabling the service "
+                   "(should be used with --disable option)")
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        if parsed_args.disable_reason and not parsed_args.disable:
+            msg = _("Cannot specify option --disable-reason without "
+                    "--disable specified.")
+            raise exceptions.CommandError(msg)
+
+        service_client = self.app.client_manager.volume
+        if parsed_args.enable:
+            service_client.services.enable(
+                parsed_args.host, parsed_args.service)
+        if parsed_args.disable:
+            if parsed_args.disable_reason:
+                service_client.services.disable_log_reason(
+                    parsed_args.host,
+                    parsed_args.service,
+                    parsed_args.disable_reason)
+            else:
+                service_client.services.disable(
+                    parsed_args.host, parsed_args.service)
diff --git a/releasenotes/notes/bug-1613597-b1545148b0755e6f.yaml b/releasenotes/notes/bug-1613597-b1545148b0755e6f.yaml
new file mode 100644
index 0000000000..09b3538a23
--- /dev/null
+++ b/releasenotes/notes/bug-1613597-b1545148b0755e6f.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - Add ``volume service set`` commands in volume v1 and v2.
+    [Bug `1613597 <https://bugs.launchpad.net/python-openstackclient/+bug/1613597>`_]
diff --git a/setup.cfg b/setup.cfg
index 78eaf34424..7af6e0c7e2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -481,6 +481,7 @@ openstack.volume.v1 =
     volume_qos_unset = openstackclient.volume.v1.qos_specs:UnsetQos
 
     volume_service_list = openstackclient.volume.v1.service:ListService
+    volume_service_set = openstackclient.volume.v1.service:SetService
 
     volume_transfer_request_list = openstackclient.volume.v1.volume_transfer_request:ListTransferRequests
 
@@ -528,6 +529,7 @@ openstack.volume.v2 =
     volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos
 
     volume_service_list = openstackclient.volume.v2.service:ListService
+    volume_service_set = openstackclient.volume.v2.service:SetService
 
     volume_transfer_request_list = openstackclient.volume.v2.volume_transfer_request:ListTransferRequests