Skip to content

Commit 65d2a14

Browse files
author
Dean Troyer
committed
Add server resize command
* add server resize * update --wait handling for server create, reboot, rebuild * move _wait_for_status to utils Blueprint: nova-client Rebased after https://review.openstack.org/38162 was committed Change-Id: I7a43b996feecadc7628fcfe20cd5b17333762739
1 parent 3cc313a commit 65d2a14

3 files changed

Lines changed: 149 additions & 64 deletions

File tree

‎openstackclient/common/utils.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import os
1919
import sys
20+
import time
2021
import uuid
2122

2223
from openstackclient.common import exceptions
@@ -155,3 +156,35 @@ def get_client_class(api_name, version, version_map):
155156
raise exceptions.UnsupportedVersion(msg)
156157

157158
return import_class(client_path)
159+
160+
161+
def wait_for_status(status_f,
162+
res_id,
163+
status_field='status',
164+
success_status=['active'],
165+
sleep_time=5,
166+
callback=None):
167+
"""Wait for status change on a resource during a long-running operation
168+
169+
:param status_f: a status function that takes a single id argument
170+
:param res_id: the resource id to watch
171+
:param success_status: a list of status strings for successful completion
172+
:param status_field: the status attribute in the returned resource object
173+
:param sleep_time: wait this long (seconds)
174+
:param callback: called per sleep cycle, useful to display progress
175+
:rtype: True on success
176+
"""
177+
while True:
178+
res = status_f(res_id)
179+
status = getattr(res, status_field, '').lower()
180+
if status in success_status:
181+
retval = True
182+
break
183+
elif status == 'error':
184+
retval = False
185+
break
186+
if callback:
187+
progress = getattr(res, 'progress', None) or 0
188+
callback(progress)
189+
time.sleep(sleep_time)
190+
return retval

‎openstackclient/compute/v2/server.py‎

Lines changed: 115 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import os
2121
import six
2222
import sys
23-
import time
2423

2524
from cliff import command
2625
from cliff import lister
@@ -90,38 +89,10 @@ def _prep_server_detail(compute_client, server):
9089
return info
9190

9291

93-
def _wait_for_status(poll_fn, obj_id, final_ok_states, poll_period=5,
94-
status_field="status"):
95-
"""Block while an action is being performed
96-
97-
:param poll_fn: a function to retrieve the state of the object
98-
:param obj_id: the id of the object
99-
:param final_ok_states: a tuple of the states of the object that end the
100-
wait as success, ex ['active']
101-
:param poll_period: the wait time between checks of object status
102-
:param status_field: field name containing the status to be checked
103-
"""
104-
log = logging.getLogger(__name__ + '._wait_for_status')
105-
while True:
106-
obj = poll_fn(obj_id)
107-
108-
status = getattr(obj, status_field)
109-
110-
if status:
111-
status = status.lower()
112-
113-
if status in final_ok_states:
114-
log.debug('Wait terminated with success')
115-
retval = True
116-
break
117-
elif status == "error":
118-
log.error('Wait terminated with an error')
119-
retval = False
120-
break
121-
122-
time.sleep(poll_period)
123-
124-
return retval
92+
def _show_progress(progress):
93+
if progress:
94+
sys.stdout.write('\rProgress: %s' % progress)
95+
sys.stdout.flush()
12596

12697

12798
class AddServerVolume(command.Command):
@@ -263,9 +234,9 @@ def get_parser(self, prog_name):
263234
help='Maximum number of servers to launch (default=1)')
264235
parser.add_argument(
265236
'--wait',
266-
dest='wait',
267237
action='store_true',
268-
help='Wait for servers to become active')
238+
help='Wait for build to complete',
239+
)
269240
return parser
270241

271242
def take_action(self, parsed_args):
@@ -359,8 +330,17 @@ def take_action(self, parsed_args):
359330
server = compute_client.servers.create(*boot_args, **boot_kwargs)
360331

361332
if parsed_args.wait:
362-
_wait_for_status(compute_client.servers.get, server._info['id'],
363-
['active'])
333+
if utils.wait_for_status(
334+
compute_client.servers.get,
335+
server.id,
336+
callback=_show_progress,
337+
):
338+
sys.stdout.write('\n')
339+
else:
340+
self.log.error('Error creating server: %s' %
341+
parsed_args.server_name)
342+
sys.stdout.write('\nError creating server')
343+
raise SystemExit
364344

365345
details = _prep_server_detail(compute_client, server)
366346
return zip(*sorted(details.iteritems()))
@@ -641,7 +621,7 @@ def take_action(self, parsed_args):
641621

642622

643623
class RebootServer(command.Command):
644-
"""Reboot server command"""
624+
"""Perform a hard or soft server reboot"""
645625

646626
log = logging.getLogger(__name__ + '.RebootServer')
647627

@@ -650,27 +630,30 @@ def get_parser(self, prog_name):
650630
parser.add_argument(
651631
'server',
652632
metavar='<server>',
653-
help='Name or ID of server to reboot')
633+
help='Server (name or ID)',
634+
)
654635
group = parser.add_mutually_exclusive_group()
655636
group.add_argument(
656637
'--hard',
657638
dest='reboot_type',
658639
action='store_const',
659640
const=servers.REBOOT_HARD,
660641
default=servers.REBOOT_SOFT,
661-
help='Perform a hard reboot')
642+
help='Perform a hard reboot',
643+
)
662644
group.add_argument(
663645
'--soft',
664646
dest='reboot_type',
665647
action='store_const',
666648
const=servers.REBOOT_SOFT,
667649
default=servers.REBOOT_SOFT,
668-
help='Perform a soft reboot')
650+
help='Perform a soft reboot',
651+
)
669652
parser.add_argument(
670653
'--wait',
671-
dest='wait',
672654
action='store_true',
673-
help='Wait for server to become active to return')
655+
help='Wait for reboot to complete',
656+
)
674657
return parser
675658

676659
def take_action(self, parsed_args):
@@ -681,14 +664,19 @@ def take_action(self, parsed_args):
681664
server.reboot(parsed_args.reboot_type)
682665

683666
if parsed_args.wait:
684-
_wait_for_status(compute_client.servers.get, server.id,
685-
['active'])
686-
687-
return
667+
if utils.wait_for_status(
668+
compute_client.servers.get,
669+
server.id,
670+
callback=_show_progress,
671+
):
672+
sys.stdout.write('\nReboot complete\n')
673+
else:
674+
sys.stdout.write('\nError rebooting server\n')
675+
raise SystemExit
688676

689677

690678
class RebuildServer(show.ShowOne):
691-
"""Rebuild server command"""
679+
"""Rebuild server"""
692680

693681
log = logging.getLogger(__name__ + '.RebuildServer')
694682

@@ -697,22 +685,24 @@ def get_parser(self, prog_name):
697685
parser.add_argument(
698686
'server',
699687
metavar='<server>',
700-
help='Server name or ID')
688+
help='Server (name or ID)',
689+
)
701690
parser.add_argument(
702691
'--image',
703692
metavar='<image>',
704693
required=True,
705-
help='Recreate server from this image')
694+
help='Recreate server from this image',
695+
)
706696
parser.add_argument(
707697
'--password',
708698
metavar='<password>',
709-
default=False,
710-
help="Set the provided password on the rebuild instance")
699+
help="Set the password on the rebuilt instance",
700+
)
711701
parser.add_argument(
712702
'--wait',
713-
dest='wait',
714703
action='store_true',
715-
help='Wait for server to become active to return')
704+
help='Wait for rebuild to complete',
705+
)
716706
return parser
717707

718708
def take_action(self, parsed_args):
@@ -725,17 +715,17 @@ def take_action(self, parsed_args):
725715
server = utils.find_resource(
726716
compute_client.servers, parsed_args.server)
727717

728-
_password = None
729-
if parsed_args.password is not False:
730-
_password = parsed_args.password
731-
732-
kwargs = {}
733-
server = server.rebuild(image, _password, **kwargs)
734-
735-
# TODO(dtroyer): force silent=True if output filter != table
718+
server = server.rebuild(image, parsed_args.password)
736719
if parsed_args.wait:
737-
_wait_for_status(compute_client.servers.get, server._info['id'],
738-
['active'])
720+
if utils.wait_for_status(
721+
compute_client.servers.get,
722+
server.id,
723+
callback=_show_progress,
724+
):
725+
sys.stdout.write('\nComplete\n')
726+
else:
727+
sys.stdout.write('\nError rebuilding server')
728+
raise SystemExit
739729

740730
details = _prep_server_detail(compute_client, server)
741731
return zip(*sorted(details.iteritems()))
@@ -806,6 +796,67 @@ def take_action(self, parsed_args):
806796
return zip(*sorted(six.iteritems(server._info)))
807797

808798

799+
class ResizeServer(command.Command):
800+
"""Convert server to a new flavor"""
801+
802+
log = logging.getLogger(__name__ + '.ResizeServer')
803+
804+
def get_parser(self, prog_name):
805+
parser = super(ResizeServer, self).get_parser(prog_name)
806+
phase_group = parser.add_mutually_exclusive_group()
807+
phase_group.add_argument(
808+
'--flavor',
809+
metavar='<flavor>',
810+
help='Resize server to this flavor',
811+
)
812+
phase_group.add_argument(
813+
'--verify',
814+
action="store_true",
815+
help='Verify previous server resize',
816+
)
817+
phase_group.add_argument(
818+
'--revert',
819+
action="store_true",
820+
help='Restore server before resize',
821+
)
822+
parser.add_argument(
823+
'--wait',
824+
action='store_true',
825+
help='Wait for resize to complete',
826+
)
827+
return parser
828+
829+
def take_action(self, parsed_args):
830+
self.log.debug('take_action(%s)' % parsed_args)
831+
832+
compute_client = self.app.client_manager.compute
833+
server = utils.find_resource(
834+
compute_client.servers,
835+
parsed_args.server,
836+
)
837+
if parsed_args.flavor:
838+
flavor = utils.find_resource(
839+
compute_client.flavors,
840+
parsed_args.flavor,
841+
)
842+
server.resize(flavor)
843+
if parsed_args.wait:
844+
if utils.wait_for_status(
845+
compute_client.servers.get,
846+
server.id,
847+
success_status=['active', 'verify_resize'],
848+
callback=_show_progress,
849+
):
850+
sys.stdout.write('Complete\n')
851+
else:
852+
sys.stdout.write('\nError resizing server')
853+
raise SystemExit
854+
elif parsed_args.verify:
855+
server.confirm_resize()
856+
elif parsed_args.revert:
857+
server.revert_resize()
858+
859+
809860
class ResumeServer(command.Command):
810861
"""Resume server"""
811862

‎setup.cfg‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ openstack.compute.v2 =
221221
server_rebuild = openstackclient.compute.v2.server:RebuildServer
222222
server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume
223223
server_rescue = openstackclient.compute.v2.server:RescueServer
224+
server_resize = openstackclient.compute.v2.server:ResizeServer
224225
server_resume = openstackclient.compute.v2.server:ResumeServer
225226
server_set = openstackclient.compute.v2.server:SetServer
226227
server_show = openstackclient.compute.v2.server:ShowServer

0 commit comments

Comments
 (0)