Skip to content

Commit eccd943

Browse files
committed
Support compute service force down/up
Aims to evacuate servers from compute host as soon as possible, operators might set the compute service force down manually. Novaclient support the behavior, this patch support it in OSC. Change-Id: I22ff1c5d670c449771fdcb3f4f39cd82f428531a Closes-Bug: #1589348
1 parent 5b36898 commit eccd943

4 files changed

Lines changed: 165 additions & 20 deletions

File tree

doc/source/command-objects/compute-service.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Set service command
5858
os compute service set
5959
[--enable | --disable]
6060
[--disable-reason <reason>]
61+
[--up | --down]
6162
<host> <service>
6263
6364
.. _compute-service-set:
@@ -73,6 +74,14 @@ Set service command
7374

7475
Reason for disabling the service (in quotes). Should be used with --disable option.
7576

77+
.. option:: --up
78+
79+
Force up service
80+
81+
.. option:: --down
82+
83+
Force down service
84+
7685
.. describe:: <host>
7786

7887
Name of host

openstackclient/compute/v2/service.py

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from osc_lib import utils
2121

2222
from openstackclient.i18n import _
23+
from openstackclient.i18n import _LE
2324

2425

2526
class DeleteService(command.Command):
@@ -127,6 +128,17 @@ def get_parser(self, prog_name):
127128
help=_("Reason for disabling the service (in quotas). "
128129
"Should be used with --disable option.")
129130
)
131+
up_down_group = parser.add_mutually_exclusive_group()
132+
up_down_group.add_argument(
133+
'--up',
134+
action='store_true',
135+
help=_('Force up service'),
136+
)
137+
up_down_group.add_argument(
138+
'--down',
139+
action='store_true',
140+
help=_('Force down service'),
141+
)
130142
return parser
131143

132144
def take_action(self, parsed_args):
@@ -139,20 +151,45 @@ def take_action(self, parsed_args):
139151
"--disable specified.")
140152
raise exceptions.CommandError(msg)
141153

154+
result = 0
142155
enabled = None
143-
if parsed_args.enable:
144-
enabled = True
145-
if parsed_args.disable:
146-
enabled = False
147-
148-
if enabled is None:
149-
return
150-
elif enabled:
151-
cs.enable(parsed_args.host, parsed_args.service)
152-
else:
153-
if parsed_args.disable_reason:
154-
cs.disable_log_reason(parsed_args.host,
155-
parsed_args.service,
156-
parsed_args.disable_reason)
157-
else:
158-
cs.disable(parsed_args.host, parsed_args.service)
156+
try:
157+
if parsed_args.enable:
158+
enabled = True
159+
if parsed_args.disable:
160+
enabled = False
161+
162+
if enabled is not None:
163+
if enabled:
164+
cs.enable(parsed_args.host, parsed_args.service)
165+
else:
166+
if parsed_args.disable_reason:
167+
cs.disable_log_reason(parsed_args.host,
168+
parsed_args.service,
169+
parsed_args.disable_reason)
170+
else:
171+
cs.disable(parsed_args.host, parsed_args.service)
172+
except Exception:
173+
status = "enabled" if enabled else "disabled"
174+
self.log.error(_LE("Failed to set service status to %s"), status)
175+
result += 1
176+
177+
force_down = None
178+
try:
179+
if parsed_args.down:
180+
force_down = True
181+
if parsed_args.up:
182+
force_down = False
183+
if force_down is not None:
184+
cs.force_down(parsed_args.host, parsed_args.service,
185+
force_down=force_down)
186+
except Exception:
187+
state = "down" if force_down else "up"
188+
self.log.error(_LE("Failed to set service state to %s"), state)
189+
result += 1
190+
191+
if result > 0:
192+
msg = _("Compute service %(service)s of host %(host)s failed to "
193+
"set.") % {"service": parsed_args.service,
194+
"host": parsed_args.host}
195+
raise exceptions.CommandError(msg)

openstackclient/tests/compute/v2/test_service.py

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
# under the License.
1414
#
1515

16+
import mock
17+
1618
from osc_lib import exceptions
1719

1820
from openstackclient.compute.v2 import service
@@ -225,8 +227,12 @@ def test_service_set_only_with_disable_reason(self):
225227
('service', self.service.binary),
226228
]
227229
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
228-
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
229-
parsed_args)
230+
try:
231+
self.cmd.take_action(parsed_args)
232+
self.fail("CommandError should be raised.")
233+
except exceptions.CommandError as e:
234+
self.assertEqual("Cannot specify option --disable-reason without "
235+
"--disable specified.", str(e))
230236

231237
def test_service_set_enable_with_disable_reason(self):
232238
reason = 'earthquake'
@@ -243,5 +249,93 @@ def test_service_set_enable_with_disable_reason(self):
243249
('service', self.service.binary),
244250
]
245251
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
246-
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
247-
parsed_args)
252+
try:
253+
self.cmd.take_action(parsed_args)
254+
self.fail("CommandError should be raised.")
255+
except exceptions.CommandError as e:
256+
self.assertEqual("Cannot specify option --disable-reason without "
257+
"--disable specified.", str(e))
258+
259+
def test_service_set_state_up(self):
260+
arglist = [
261+
'--up',
262+
self.service.host,
263+
self.service.binary,
264+
]
265+
verifylist = [
266+
('up', True),
267+
('host', self.service.host),
268+
('service', self.service.binary),
269+
]
270+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
271+
result = self.cmd.take_action(parsed_args)
272+
self.service_mock.force_down.assert_called_once_with(
273+
self.service.host, self.service.binary, force_down=False)
274+
self.assertNotCalled(self.service_mock.enable)
275+
self.assertNotCalled(self.service_mock.disable)
276+
self.assertIsNone(result)
277+
278+
def test_service_set_state_down(self):
279+
arglist = [
280+
'--down',
281+
self.service.host,
282+
self.service.binary,
283+
]
284+
verifylist = [
285+
('down', True),
286+
('host', self.service.host),
287+
('service', self.service.binary),
288+
]
289+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
290+
result = self.cmd.take_action(parsed_args)
291+
self.service_mock.force_down.assert_called_once_with(
292+
self.service.host, self.service.binary, force_down=True)
293+
self.assertNotCalled(self.service_mock.enable)
294+
self.assertNotCalled(self.service_mock.disable)
295+
self.assertIsNone(result)
296+
297+
def test_service_set_enable_and_state_down(self):
298+
arglist = [
299+
'--enable',
300+
'--down',
301+
self.service.host,
302+
self.service.binary,
303+
]
304+
verifylist = [
305+
('enable', True),
306+
('down', True),
307+
('host', self.service.host),
308+
('service', self.service.binary),
309+
]
310+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
311+
result = self.cmd.take_action(parsed_args)
312+
self.service_mock.enable.assert_called_once_with(
313+
self.service.host, self.service.binary)
314+
self.service_mock.force_down.assert_called_once_with(
315+
self.service.host, self.service.binary, force_down=True)
316+
self.assertIsNone(result)
317+
318+
def test_service_set_enable_and_state_down_with_exception(self):
319+
arglist = [
320+
'--enable',
321+
'--down',
322+
self.service.host,
323+
self.service.binary,
324+
]
325+
verifylist = [
326+
('enable', True),
327+
('down', True),
328+
('host', self.service.host),
329+
('service', self.service.binary),
330+
]
331+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
332+
333+
with mock.patch.object(self.cmd.log, 'error') as mock_log:
334+
with mock.patch.object(self.service_mock, 'enable',
335+
side_effect=Exception()):
336+
self.assertRaises(exceptions.CommandError,
337+
self.cmd.take_action, parsed_args)
338+
mock_log.assert_called_once_with(
339+
"Failed to set service status to %s", "enabled")
340+
self.service_mock.force_down.assert_called_once_with(
341+
self.service.host, self.service.binary, force_down=True)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
features:
3+
- Add options ``--up`` and ``--down`` for compute v2 ``compute service set``
4+
command to support force up/down compute service.
5+
[Bug `1589348 <https://bugs.launchpad.net/python-openstackclient/+bug/1589348>`_]

0 commit comments

Comments
 (0)