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
This commit is contained in:
parent
3cc313a60d
commit
65d2a14e3e
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from openstackclient.common import exceptions
|
from openstackclient.common import exceptions
|
||||||
@ -155,3 +156,35 @@ def get_client_class(api_name, version, version_map):
|
|||||||
raise exceptions.UnsupportedVersion(msg)
|
raise exceptions.UnsupportedVersion(msg)
|
||||||
|
|
||||||
return import_class(client_path)
|
return import_class(client_path)
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_status(status_f,
|
||||||
|
res_id,
|
||||||
|
status_field='status',
|
||||||
|
success_status=['active'],
|
||||||
|
sleep_time=5,
|
||||||
|
callback=None):
|
||||||
|
"""Wait for status change on a resource during a long-running operation
|
||||||
|
|
||||||
|
:param status_f: a status function that takes a single id argument
|
||||||
|
:param res_id: the resource id to watch
|
||||||
|
:param success_status: a list of status strings for successful completion
|
||||||
|
:param status_field: the status attribute in the returned resource object
|
||||||
|
:param sleep_time: wait this long (seconds)
|
||||||
|
:param callback: called per sleep cycle, useful to display progress
|
||||||
|
:rtype: True on success
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
res = status_f(res_id)
|
||||||
|
status = getattr(res, status_field, '').lower()
|
||||||
|
if status in success_status:
|
||||||
|
retval = True
|
||||||
|
break
|
||||||
|
elif status == 'error':
|
||||||
|
retval = False
|
||||||
|
break
|
||||||
|
if callback:
|
||||||
|
progress = getattr(res, 'progress', None) or 0
|
||||||
|
callback(progress)
|
||||||
|
time.sleep(sleep_time)
|
||||||
|
return retval
|
||||||
|
@ -20,7 +20,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import six
|
import six
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
|
|
||||||
from cliff import command
|
from cliff import command
|
||||||
from cliff import lister
|
from cliff import lister
|
||||||
@ -90,38 +89,10 @@ def _prep_server_detail(compute_client, server):
|
|||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
def _wait_for_status(poll_fn, obj_id, final_ok_states, poll_period=5,
|
def _show_progress(progress):
|
||||||
status_field="status"):
|
if progress:
|
||||||
"""Block while an action is being performed
|
sys.stdout.write('\rProgress: %s' % progress)
|
||||||
|
sys.stdout.flush()
|
||||||
:param poll_fn: a function to retrieve the state of the object
|
|
||||||
:param obj_id: the id of the object
|
|
||||||
:param final_ok_states: a tuple of the states of the object that end the
|
|
||||||
wait as success, ex ['active']
|
|
||||||
:param poll_period: the wait time between checks of object status
|
|
||||||
:param status_field: field name containing the status to be checked
|
|
||||||
"""
|
|
||||||
log = logging.getLogger(__name__ + '._wait_for_status')
|
|
||||||
while True:
|
|
||||||
obj = poll_fn(obj_id)
|
|
||||||
|
|
||||||
status = getattr(obj, status_field)
|
|
||||||
|
|
||||||
if status:
|
|
||||||
status = status.lower()
|
|
||||||
|
|
||||||
if status in final_ok_states:
|
|
||||||
log.debug('Wait terminated with success')
|
|
||||||
retval = True
|
|
||||||
break
|
|
||||||
elif status == "error":
|
|
||||||
log.error('Wait terminated with an error')
|
|
||||||
retval = False
|
|
||||||
break
|
|
||||||
|
|
||||||
time.sleep(poll_period)
|
|
||||||
|
|
||||||
return retval
|
|
||||||
|
|
||||||
|
|
||||||
class AddServerVolume(command.Command):
|
class AddServerVolume(command.Command):
|
||||||
@ -263,9 +234,9 @@ class CreateServer(show.ShowOne):
|
|||||||
help='Maximum number of servers to launch (default=1)')
|
help='Maximum number of servers to launch (default=1)')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--wait',
|
'--wait',
|
||||||
dest='wait',
|
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Wait for servers to become active')
|
help='Wait for build to complete',
|
||||||
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
@ -359,8 +330,17 @@ class CreateServer(show.ShowOne):
|
|||||||
server = compute_client.servers.create(*boot_args, **boot_kwargs)
|
server = compute_client.servers.create(*boot_args, **boot_kwargs)
|
||||||
|
|
||||||
if parsed_args.wait:
|
if parsed_args.wait:
|
||||||
_wait_for_status(compute_client.servers.get, server._info['id'],
|
if utils.wait_for_status(
|
||||||
['active'])
|
compute_client.servers.get,
|
||||||
|
server.id,
|
||||||
|
callback=_show_progress,
|
||||||
|
):
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
else:
|
||||||
|
self.log.error('Error creating server: %s' %
|
||||||
|
parsed_args.server_name)
|
||||||
|
sys.stdout.write('\nError creating server')
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
details = _prep_server_detail(compute_client, server)
|
details = _prep_server_detail(compute_client, server)
|
||||||
return zip(*sorted(details.iteritems()))
|
return zip(*sorted(details.iteritems()))
|
||||||
@ -641,7 +621,7 @@ class PauseServer(command.Command):
|
|||||||
|
|
||||||
|
|
||||||
class RebootServer(command.Command):
|
class RebootServer(command.Command):
|
||||||
"""Reboot server command"""
|
"""Perform a hard or soft server reboot"""
|
||||||
|
|
||||||
log = logging.getLogger(__name__ + '.RebootServer')
|
log = logging.getLogger(__name__ + '.RebootServer')
|
||||||
|
|
||||||
@ -650,7 +630,8 @@ class RebootServer(command.Command):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'server',
|
'server',
|
||||||
metavar='<server>',
|
metavar='<server>',
|
||||||
help='Name or ID of server to reboot')
|
help='Server (name or ID)',
|
||||||
|
)
|
||||||
group = parser.add_mutually_exclusive_group()
|
group = parser.add_mutually_exclusive_group()
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
'--hard',
|
'--hard',
|
||||||
@ -658,19 +639,21 @@ class RebootServer(command.Command):
|
|||||||
action='store_const',
|
action='store_const',
|
||||||
const=servers.REBOOT_HARD,
|
const=servers.REBOOT_HARD,
|
||||||
default=servers.REBOOT_SOFT,
|
default=servers.REBOOT_SOFT,
|
||||||
help='Perform a hard reboot')
|
help='Perform a hard reboot',
|
||||||
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
'--soft',
|
'--soft',
|
||||||
dest='reboot_type',
|
dest='reboot_type',
|
||||||
action='store_const',
|
action='store_const',
|
||||||
const=servers.REBOOT_SOFT,
|
const=servers.REBOOT_SOFT,
|
||||||
default=servers.REBOOT_SOFT,
|
default=servers.REBOOT_SOFT,
|
||||||
help='Perform a soft reboot')
|
help='Perform a soft reboot',
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--wait',
|
'--wait',
|
||||||
dest='wait',
|
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Wait for server to become active to return')
|
help='Wait for reboot to complete',
|
||||||
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
@ -681,14 +664,19 @@ class RebootServer(command.Command):
|
|||||||
server.reboot(parsed_args.reboot_type)
|
server.reboot(parsed_args.reboot_type)
|
||||||
|
|
||||||
if parsed_args.wait:
|
if parsed_args.wait:
|
||||||
_wait_for_status(compute_client.servers.get, server.id,
|
if utils.wait_for_status(
|
||||||
['active'])
|
compute_client.servers.get,
|
||||||
|
server.id,
|
||||||
return
|
callback=_show_progress,
|
||||||
|
):
|
||||||
|
sys.stdout.write('\nReboot complete\n')
|
||||||
|
else:
|
||||||
|
sys.stdout.write('\nError rebooting server\n')
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
|
||||||
class RebuildServer(show.ShowOne):
|
class RebuildServer(show.ShowOne):
|
||||||
"""Rebuild server command"""
|
"""Rebuild server"""
|
||||||
|
|
||||||
log = logging.getLogger(__name__ + '.RebuildServer')
|
log = logging.getLogger(__name__ + '.RebuildServer')
|
||||||
|
|
||||||
@ -697,22 +685,24 @@ class RebuildServer(show.ShowOne):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'server',
|
'server',
|
||||||
metavar='<server>',
|
metavar='<server>',
|
||||||
help='Server name or ID')
|
help='Server (name or ID)',
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--image',
|
'--image',
|
||||||
metavar='<image>',
|
metavar='<image>',
|
||||||
required=True,
|
required=True,
|
||||||
help='Recreate server from this image')
|
help='Recreate server from this image',
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--password',
|
'--password',
|
||||||
metavar='<password>',
|
metavar='<password>',
|
||||||
default=False,
|
help="Set the password on the rebuilt instance",
|
||||||
help="Set the provided password on the rebuild instance")
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--wait',
|
'--wait',
|
||||||
dest='wait',
|
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Wait for server to become active to return')
|
help='Wait for rebuild to complete',
|
||||||
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
@ -725,17 +715,17 @@ class RebuildServer(show.ShowOne):
|
|||||||
server = utils.find_resource(
|
server = utils.find_resource(
|
||||||
compute_client.servers, parsed_args.server)
|
compute_client.servers, parsed_args.server)
|
||||||
|
|
||||||
_password = None
|
server = server.rebuild(image, parsed_args.password)
|
||||||
if parsed_args.password is not False:
|
|
||||||
_password = parsed_args.password
|
|
||||||
|
|
||||||
kwargs = {}
|
|
||||||
server = server.rebuild(image, _password, **kwargs)
|
|
||||||
|
|
||||||
# TODO(dtroyer): force silent=True if output filter != table
|
|
||||||
if parsed_args.wait:
|
if parsed_args.wait:
|
||||||
_wait_for_status(compute_client.servers.get, server._info['id'],
|
if utils.wait_for_status(
|
||||||
['active'])
|
compute_client.servers.get,
|
||||||
|
server.id,
|
||||||
|
callback=_show_progress,
|
||||||
|
):
|
||||||
|
sys.stdout.write('\nComplete\n')
|
||||||
|
else:
|
||||||
|
sys.stdout.write('\nError rebuilding server')
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
details = _prep_server_detail(compute_client, server)
|
details = _prep_server_detail(compute_client, server)
|
||||||
return zip(*sorted(details.iteritems()))
|
return zip(*sorted(details.iteritems()))
|
||||||
@ -806,6 +796,67 @@ class RescueServer(show.ShowOne):
|
|||||||
return zip(*sorted(six.iteritems(server._info)))
|
return zip(*sorted(six.iteritems(server._info)))
|
||||||
|
|
||||||
|
|
||||||
|
class ResizeServer(command.Command):
|
||||||
|
"""Convert server to a new flavor"""
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__ + '.ResizeServer')
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ResizeServer, self).get_parser(prog_name)
|
||||||
|
phase_group = parser.add_mutually_exclusive_group()
|
||||||
|
phase_group.add_argument(
|
||||||
|
'--flavor',
|
||||||
|
metavar='<flavor>',
|
||||||
|
help='Resize server to this flavor',
|
||||||
|
)
|
||||||
|
phase_group.add_argument(
|
||||||
|
'--verify',
|
||||||
|
action="store_true",
|
||||||
|
help='Verify previous server resize',
|
||||||
|
)
|
||||||
|
phase_group.add_argument(
|
||||||
|
'--revert',
|
||||||
|
action="store_true",
|
||||||
|
help='Restore server before resize',
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--wait',
|
||||||
|
action='store_true',
|
||||||
|
help='Wait for resize to complete',
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.log.debug('take_action(%s)' % parsed_args)
|
||||||
|
|
||||||
|
compute_client = self.app.client_manager.compute
|
||||||
|
server = utils.find_resource(
|
||||||
|
compute_client.servers,
|
||||||
|
parsed_args.server,
|
||||||
|
)
|
||||||
|
if parsed_args.flavor:
|
||||||
|
flavor = utils.find_resource(
|
||||||
|
compute_client.flavors,
|
||||||
|
parsed_args.flavor,
|
||||||
|
)
|
||||||
|
server.resize(flavor)
|
||||||
|
if parsed_args.wait:
|
||||||
|
if utils.wait_for_status(
|
||||||
|
compute_client.servers.get,
|
||||||
|
server.id,
|
||||||
|
success_status=['active', 'verify_resize'],
|
||||||
|
callback=_show_progress,
|
||||||
|
):
|
||||||
|
sys.stdout.write('Complete\n')
|
||||||
|
else:
|
||||||
|
sys.stdout.write('\nError resizing server')
|
||||||
|
raise SystemExit
|
||||||
|
elif parsed_args.verify:
|
||||||
|
server.confirm_resize()
|
||||||
|
elif parsed_args.revert:
|
||||||
|
server.revert_resize()
|
||||||
|
|
||||||
|
|
||||||
class ResumeServer(command.Command):
|
class ResumeServer(command.Command):
|
||||||
"""Resume server"""
|
"""Resume server"""
|
||||||
|
|
||||||
|
@ -221,6 +221,7 @@ openstack.compute.v2 =
|
|||||||
server_rebuild = openstackclient.compute.v2.server:RebuildServer
|
server_rebuild = openstackclient.compute.v2.server:RebuildServer
|
||||||
server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume
|
server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume
|
||||||
server_rescue = openstackclient.compute.v2.server:RescueServer
|
server_rescue = openstackclient.compute.v2.server:RescueServer
|
||||||
|
server_resize = openstackclient.compute.v2.server:ResizeServer
|
||||||
server_resume = openstackclient.compute.v2.server:ResumeServer
|
server_resume = openstackclient.compute.v2.server:ResumeServer
|
||||||
server_set = openstackclient.compute.v2.server:SetServer
|
server_set = openstackclient.compute.v2.server:SetServer
|
||||||
server_show = openstackclient.compute.v2.server:ShowServer
|
server_show = openstackclient.compute.v2.server:ShowServer
|
||||||
|
Loading…
Reference in New Issue
Block a user