Commit 62d2efd8 authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Merge branch 'feature-improve-wait-behavior' into develop

parents 176d8002 bf5408f4
......@@ -11,6 +11,10 @@ Changes:
wherever it is used
- Replace print methods with member print methods in _commands.* [#4292]
- kamaki.cli.config is now a directory package [#4058]
- Modify progress bar handler in cli.commands to allow count down and timeout
[#4352]
- Make wait progress bars in cyclades CLI to count down to timeout instead of
progressing forward [#4352]
Features:
......@@ -23,4 +27,5 @@ Features:
- Implement --type=SOFT/HARD argument for server reboot [#4338]
The old argument -f/--force <==> --type=HARD is kept for bw compatibility
A warning message guides users to stop using it
- Catch Keyboard Interupts in cli wait methods and exit gracefully [#4351]
......@@ -332,7 +332,8 @@ class ProgressBarArgument(FlagArgument):
newarg._value = self._value
return newarg
def get_generator(self, message, message_len=25):
def get_generator(
self, message, message_len=25, countdown=False, timeout=100):
"""Get a generator to handle progress of the bar (gen.next())"""
if self.value:
return None
......@@ -341,8 +342,19 @@ class ProgressBarArgument(FlagArgument):
except NameError:
self.value = None
return self.value
if countdown:
bar_phases = list(self.bar.phases)
self.bar.empty_fill, bar_phases[0] = bar_phases[-1], ''
bar_phases.reverse()
self.bar.phases = bar_phases
self.bar.bar_prefix = ' '
self.bar.bar_suffix = ' '
self.bar.max = timeout or 100
self.bar.suffix = '%(remaining)ds to timeout'
else:
self.bar.suffix = '%(percent)d%% - %(eta)ds'
self.bar.eta = timeout or 100
self.bar.message = message.ljust(message_len)
self.bar.suffix = '%(percent)d%% - %(eta)ds'
self.bar.start()
def progress_gen(n):
......
......@@ -369,13 +369,28 @@ class ProgressBarArgument(TestCase):
pba.value = None
msg, msg_len = 'message', 40
with patch('%s.KamakiProgressBar.start' % arg_path) as start:
pba.get_generator(msg, msg_len)
self.assertTrue(isinstance(pba.bar, argument.KamakiProgressBar))
self.assertNotEqual(pba.bar.message, msg)
self.assertEqual(
pba.bar.message, '%s%s' % (msg, ' ' * (msg_len - len(msg))))
self.assertEqual(pba.bar.suffix, '%(percent)d%% - %(eta)ds')
start.assert_called_once()
try:
pba.get_generator(msg, msg_len)
self.assertTrue(
isinstance(pba.bar, argument.KamakiProgressBar))
self.assertNotEqual(pba.bar.message, msg)
self.assertEqual(pba.bar.message, '%s%s' % (
msg, ' ' * (msg_len - len(msg))))
self.assertEqual(pba.bar.suffix, '%(percent)d%% - %(eta)ds')
start.assert_called_once()
pba.get_generator(msg, msg_len, countdown=True)
self.assertTrue(
isinstance(pba.bar, argument.KamakiProgressBar))
self.assertNotEqual(pba.bar.message, msg)
self.assertEqual(pba.bar.message, '%s%s' % (
msg, ' ' * (msg_len - len(msg))))
self.assertEqual(pba.bar.suffix, '%(remaining)ds to timeout')
finally:
try:
pba.finish()
except Exception:
pass
def test_finish(self):
pba = argument.ProgressBarArgument(parsed_name='--progress')
......
......@@ -177,12 +177,14 @@ class _command_init(object):
assert max_threads > 0, 'invalid max_threads config option'
self.client.MAX_THREADS = max_threads
def _safe_progress_bar(self, msg, arg='progress_bar'):
def _safe_progress_bar(
self, msg, arg='progress_bar', countdown=False, timeout=100):
"""Try to get a progress bar, but do not raise errors"""
try:
progress_bar = self.arguments[arg]
progress_bar.file = self._err
gen = progress_bar.get_generator(msg)
gen = progress_bar.get_generator(
msg, countdown=countdown, timeout=timeout)
except Exception:
return (None, None)
return (progress_bar, gen)
......
......@@ -75,34 +75,44 @@ class _service_wait(object):
'do not show progress bar', ('-N', '--no-progress-bar'), False)
)
def _wait(self, service, service_id, status_method, currect_status):
def _wait(
self, service, service_id, status_method, current_status,
countdown=True, timeout=60):
(progress_bar, wait_cb) = self._safe_progress_bar(
'%s %s still in %s mode' % (service, service_id, currect_status))
'%s %s: status is still %s' % (
service, service_id, current_status),
countdown=countdown, timeout=timeout)
try:
new_mode = status_method(
service_id, currect_status, wait_cb=wait_cb)
service_id, current_status, max_wait=timeout, wait_cb=wait_cb)
if new_mode:
self.error('%s %s: status is now %s' % (
service, service_id, new_mode))
else:
self.error('%s %s: status is still %s' % (
service, service_id, current_status))
except KeyboardInterrupt:
self.error('\n- canceled')
finally:
self._safe_progress_bar_finish(progress_bar)
if new_mode:
self.error('%s %s is now in %s mode' % (
service, service_id, new_mode))
else:
raiseCLIError(None, 'Time out')
class _server_wait(_service_wait):
def _wait(self, server_id, currect_status):
def _wait(self, server_id, current_status, timeout=60):
super(_server_wait, self)._wait(
'Server', server_id, self.client.wait_server, currect_status)
'Server', server_id, self.client.wait_server, current_status,
countdown=(current_status not in ('BUILD', )),
timeout=timeout if current_status not in ('BUILD', ) else 100)
class _network_wait(_service_wait):
def _wait(self, net_id, currect_status):
def _wait(self, net_id, current_status, timeout=60):
super(_network_wait, self)._wait(
'Network', net_id, self.client.wait_network, currect_status)
'Network', net_id, self.client.wait_network, current_status,
timeout=timeout)
class _init_cyclades(_command_init):
......@@ -692,15 +702,27 @@ class server_stats(_init_cyclades, _optional_json):
class server_wait(_init_cyclades, _server_wait):
"""Wait for server to finish [BUILD, STOPPED, REBOOT, ACTIVE]"""
arguments = dict(
timeout=IntArgument(
'Wait limit in seconds (default: 60)', '--timeout', default=60)
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id, currect_status):
self._wait(server_id, currect_status)
def _run(self, server_id, current_status):
r = self.client.get_server_details(server_id)
if r['status'].lower() == current_status.lower():
self._wait(server_id, current_status, timeout=self['timeout'])
else:
self.error(
'Server %s: Cannot wait for status %s, '
'status is already %s' % (
server_id, current_status, r['status']))
def main(self, server_id, currect_status='BUILD'):
def main(self, server_id, current_status='BUILD'):
super(self.__class__, self)._run()
self._run(server_id=server_id, currect_status=currect_status)
self._run(server_id=server_id, current_status=current_status)
@command(flavor_cmds)
......@@ -942,7 +964,7 @@ class network_create(_init_cyclades, _optional_json, _network_wait):
type=self['type'])
_add_name(self, r)
self._print(r, self.print_dict)
if self['wait']:
if self['wait'] and r['status'] in ('PENDING', ):
self._wait(r['id'], 'PENDING')
def main(self, name):
......@@ -1048,15 +1070,27 @@ class network_disconnect(_init_cyclades):
class network_wait(_init_cyclades, _network_wait):
"""Wait for server to finish [PENDING, ACTIVE, DELETED]"""
arguments = dict(
timeout=IntArgument(
'Wait limit in seconds (default: 60)', '--timeout', default=60)
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.network_id
def _run(self, network_id, currect_status):
self._wait(network_id, currect_status)
def _run(self, network_id, current_status):
net = self.client.get_network_details(network_id)
if net['status'].lower() == current_status.lower():
self._wait(network_id, current_status, timeout=self['timeout'])
else:
self.error(
'Network %s: Cannot wait for status %s, '
'status is already %s' % (
network_id, current_status, net['status']))
def main(self, network_id, currect_status='PENDING'):
def main(self, network_id, current_status='PENDING'):
super(self.__class__, self)._run()
self._run(network_id=network_id, currect_status=currect_status)
self._run(network_id=network_id, current_status=current_status)
@command(server_cmds)
......
......@@ -295,14 +295,20 @@ class CycladesClient(CycladesRestClient):
:returns: (str) the new mode if successful, (bool) False if timed out
"""
status, progress = get_status(self, item_id)
if status != current_status:
return status
old_wait = total_wait = 0
if wait_cb:
wait_gen = wait_cb(1 + max_wait // delay)
wait_gen = wait_cb(max_wait // delay)
wait_gen.next()
if status != current_status:
if wait_cb:
try:
wait_gen.next()
except Exception:
pass
return status
old_wait = total_wait = 0
while status == current_status and total_wait <= max_wait:
if wait_cb:
try:
......@@ -310,11 +316,8 @@ class CycladesClient(CycladesRestClient):
wait_gen.next()
except Exception:
break
else:
stdout.write('.')
stdout.flush()
old_wait = total_wait
total_wait = progress or (total_wait + 1)
total_wait = progress or total_wait + 1
sleep(delay)
status, progress = get_status(self, item_id)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment