Commit 93a4d595 authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Merge branch 'feature-expand-wait-functionality' into develop

parents e3f01d64 77df3d4a
......@@ -26,5 +26,7 @@ Features:
- Implement floating IP commands
floatingip list/create/delete/info/pools
server ip attach/detach
- Implement --wait for server_create/delete/start/reboot/shutdown [#3867]
- Implement network_wait [#3862]
......@@ -39,7 +39,7 @@ from inspect import getargspec
from kamaki.cli.argument import ArgumentParseManager
from kamaki.cli.history import History
from kamaki.cli.utils import print_dict, red, magenta, yellow
from kamaki.cli.errors import CLIError
from kamaki.cli.errors import CLIError, CLICmdSpecError
from kamaki.cli import logger
_help = False
......@@ -146,9 +146,13 @@ def command(cmd_tree, prefix='', descedants_depth=1):
kloger.warning('%s failed max_len test' % cls_name)
return None
(
cls.description, sep, cls.long_description
) = cls.__doc__.partition('\n')
try:
(
cls.description, sep, cls.long_description
) = cls.__doc__.partition('\n')
except AttributeError:
raise CLICmdSpecError(
'No commend in %s (acts as cmd description)' % cls.__name__)
_construct_command_syntax(cls)
cmd_tree.add_command(cls_name, cls.description, cls)
......
......@@ -66,6 +66,10 @@ class _command_init(object):
arguments.update(self.oo_arguments)
if isinstance(self, _optional_json):
arguments.update(self.oj_arguments)
try:
arguments.update(self.wait_arguments)
except AttributeError:
pass
self.arguments = dict(arguments)
try:
self.config = self['config']
......
......@@ -67,6 +67,64 @@ howto_personality = [
' MODEL: permition in octal (e.g. 0777 or o+rwx)']
class _server_wait(object):
wait_arguments = dict(
progress_bar=ProgressBarArgument(
'do not show progress bar',
('-N', '--no-progress-bar'),
False
)
)
def _wait(self, server_id, currect_status):
(progress_bar, wait_cb) = self._safe_progress_bar(
'Server %s still in %s mode' % (server_id, currect_status))
try:
new_mode = self.client.wait_server(
server_id,
currect_status,
wait_cb=wait_cb)
except Exception:
raise
finally:
self._safe_progress_bar_finish(progress_bar)
if new_mode:
print('Server %s is now in %s mode' % (server_id, new_mode))
else:
raiseCLIError(None, 'Time out')
class _network_wait(object):
wait_arguments = dict(
progress_bar=ProgressBarArgument(
'do not show progress bar',
('-N', '--no-progress-bar'),
False
)
)
def _wait(self, net_id, currect_status):
(progress_bar, wait_cb) = self._safe_progress_bar(
'Network %s still in %s mode' % (net_id, currect_status))
try:
new_mode = self.client.wait_network(
net_id,
currect_status,
wait_cb=wait_cb)
except Exception:
raise
finally:
self._safe_progress_bar_finish(progress_bar)
if new_mode:
print('Network %s is now in %s mode' % (net_id, new_mode))
else:
raiseCLIError(None, 'Time out')
class _init_cyclades(_command_init):
@errors.generic.all
@addLogSettings
......@@ -191,7 +249,7 @@ class PersonalityArgument(KeyValueArgument):
@command(server_cmds)
class server_create(_init_cyclades, _optional_json):
class server_create(_init_cyclades, _optional_json, _server_wait):
"""Create a server (aka Virtual Machine)
Parameters:
- name: (single quoted text)
......@@ -201,7 +259,8 @@ class server_create(_init_cyclades, _optional_json):
arguments = dict(
personality=PersonalityArgument(
(80 * ' ').join(howto_personality), ('-p', '--personality'))
(80 * ' ').join(howto_personality), ('-p', '--personality')),
wait=FlagArgument('Wait server to build', ('-w', '--wait'))
)
@errors.generic.all
......@@ -209,10 +268,11 @@ class server_create(_init_cyclades, _optional_json):
@errors.plankton.id
@errors.cyclades.flavor_id
def _run(self, name, flavor_id, image_id):
self._print(
self.client.create_server(
name, int(flavor_id), image_id, self['personality']),
print_dict)
r = self.client.create_server(
name, int(flavor_id), image_id, self['personality'])
self._print(r, print_dict)
if self['wait']:
self._wait(r['id'], r['status'])
def main(self, name, flavor_id, image_id):
super(self.__class__, self)._run()
......@@ -238,14 +298,27 @@ class server_rename(_init_cyclades, _optional_output_cmd):
@command(server_cmds)
class server_delete(_init_cyclades, _optional_output_cmd):
class server_delete(_init_cyclades, _optional_output_cmd, _server_wait):
"""Delete a server (VM)"""
arguments = dict(
wait=FlagArgument('Wait server to be destroyed', ('-w', '--wait'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
self._optional_output(self.client.delete_server(int(server_id)))
status = 'DELETED'
if self['wait']:
details = self.client.get_server_details(server_id)
status = details['status']
r = self.client.delete_server(int(server_id))
self._optional_output(r)
if self['wait']:
self._wait(server_id, status)
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -253,19 +326,23 @@ class server_delete(_init_cyclades, _optional_output_cmd):
@command(server_cmds)
class server_reboot(_init_cyclades, _optional_output_cmd):
class server_reboot(_init_cyclades, _optional_output_cmd, _server_wait):
"""Reboot a server (VM)"""
arguments = dict(
hard=FlagArgument('perform a hard reboot', ('-f', '--force'))
hard=FlagArgument('perform a hard reboot', ('-f', '--force')),
wait=FlagArgument('Wait server to be destroyed', ('-w', '--wait'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
self._optional_output(
self.client.reboot_server(int(server_id), self['hard']))
r = self.client.reboot_server(int(server_id), self['hard'])
self._optional_output(r)
if self['wait']:
self._wait(server_id, 'REBOOT')
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -273,14 +350,29 @@ class server_reboot(_init_cyclades, _optional_output_cmd):
@command(server_cmds)
class server_start(_init_cyclades, _optional_output_cmd):
class server_start(_init_cyclades, _optional_output_cmd, _server_wait):
"""Start an existing server (VM)"""
arguments = dict(
wait=FlagArgument('Wait server to be destroyed', ('-w', '--wait'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
self._optional_output(self.client.start_server(int(server_id)))
status = 'ACTIVE'
if self['wait']:
details = self.client.get_server_details(server_id)
status = details['status']
if status in ('ACTIVE', ):
return
r = self.client.start_server(int(server_id))
self._optional_output(r)
if self['wait']:
self._wait(server_id, status)
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -288,14 +380,29 @@ class server_start(_init_cyclades, _optional_output_cmd):
@command(server_cmds)
class server_shutdown(_init_cyclades, _optional_output_cmd):
class server_shutdown(_init_cyclades, _optional_output_cmd, _server_wait):
"""Shutdown an active server (VM)"""
arguments = dict(
wait=FlagArgument('Wait server to be destroyed', ('-w', '--wait'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
self._optional_output(self.client.shutdown_server(int(server_id)))
status = 'STOPPED'
if self['wait']:
details = self.client.get_server_details(server_id)
status = details['status']
if status in ('STOPPED', ):
return
r = self.client.shutdown_server(int(server_id))
self._optional_output(r)
if self['wait']:
self._wait(server_id, status)
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -476,38 +583,14 @@ class server_stats(_init_cyclades, _optional_json):
@command(server_cmds)
class server_wait(_init_cyclades):
class server_wait(_init_cyclades, _server_wait):
"""Wait for server to finish [BUILD, STOPPED, REBOOT, ACTIVE]"""
arguments = dict(
progress_bar=ProgressBarArgument(
'do not show progress bar',
('-N', '--no-progress-bar'),
False
)
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id, currect_status):
(progress_bar, wait_cb) = self._safe_progress_bar(
'Server %s still in %s mode' % (server_id, currect_status))
try:
new_mode = self.client.wait_server(
server_id,
currect_status,
wait_cb=wait_cb)
except Exception:
self._safe_progress_bar_finish(progress_bar)
raise
finally:
self._safe_progress_bar_finish(progress_bar)
if new_mode:
print('Server %s is now in %s mode' % (server_id, new_mode))
else:
raiseCLIError(None, 'Time out')
self._wait(server_id, currect_status)
def main(self, server_id, currect_status='BUILD'):
super(self.__class__, self)._run()
......@@ -613,7 +696,7 @@ class network_list(_init_cyclades, _optional_json):
@command(network_cmds)
class network_create(_init_cyclades, _optional_json):
class network_create(_init_cyclades, _optional_json, _network_wait):
"""Create an (unconnected) network"""
arguments = dict(
......@@ -624,19 +707,24 @@ class network_create(_init_cyclades, _optional_json):
'Valid network types are '
'CUSTOM, IP_LESS_ROUTED, MAC_FILTERED (default), PHYSICAL_VLAN',
'--with-type',
default='MAC_FILTERED')
default='MAC_FILTERED'),
wait=FlagArgument('Wait network to build', ('-w', '--wait'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.network_max
def _run(self, name):
self._print(self.client.create_network(
r = self.client.create_network(
name,
cidr=self['cidr'],
gateway=self['gateway'],
dhcp=self['dhcp'],
type=self['type']), print_dict)
type=self['type'])
self._print(r, print_dict)
if self['wait']:
self._wait(r['id'], 'PENDING')
def main(self, name):
super(self.__class__, self)._run()
......@@ -660,15 +748,30 @@ class network_rename(_init_cyclades, _optional_output_cmd):
@command(network_cmds)
class network_delete(_init_cyclades, _optional_output_cmd):
class network_delete(_init_cyclades, _optional_output_cmd, _network_wait):
"""Delete a network"""
arguments = dict(
wait=FlagArgument('Wait network to build', ('-w', '--wait'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.network_id
@errors.cyclades.network_in_use
def _run(self, network_id):
self._optional_output(self.client.delete_network(int(network_id)))
status = 'DELETED'
if self['wait']:
r = self.client.get_network_details(network_id)
status = r['status']
if status in ('DELETED', ):
return
r = self.client.delete_network(int(network_id))
self._optional_output(r)
if self['wait']:
self._wait(network_id, status)
def main(self, network_id):
super(self.__class__, self)._run()
......@@ -723,6 +826,21 @@ class network_disconnect(_init_cyclades):
self._run(nic_id=nic_id, server_id=server_id)
@command(network_cmds)
class network_wait(_init_cyclades, _network_wait):
"""Wait for server to finish [PENDING, ACTIVE, DELETED]"""
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.network_id
def _run(self, network_id, currect_status):
self._wait(network_id, currect_status)
def main(self, network_id, currect_status='PENDING'):
super(self.__class__, self)._run()
self._run(network_id=network_id, currect_status=currect_status)
@command(floatingip_cmds)
class floatingip_pools(_init_cyclades, _optional_json):
"""List all floating pools of floating ips"""
......
......@@ -257,66 +257,104 @@ class CycladesClient(CycladesRestClient):
req = dict(remove=dict(attachment=nic))
self.networks_post(netid, 'action', json_data=req)
def wait_server(
self,
server_id,
current_status='BUILD',
delay=0.5,
max_wait=128,
wait_cb=None):
"""Wait for server while its status is current_status
def _wait(
self, item_id, current_status, get_status,
delay=1, max_wait=100, wait_cb=None):
"""Wait for item while its status is current_status
:param server_id: integer (str or int)
:param current_status: (str) BUILD|ACTIVE|STOPPED|DELETED|REBOOT
:param current_status: (str)
:param get_status: (method(self, item_id)) if called, returns
(status, progress %) If no way to tell progress, return None
:param delay: time interval between retries
:param wait_cb: if set a progressbar is used to show progress
:param wait_cb: if set a progress bar is used to show progress
:returns: (str) the new mode if succesfull, (bool) False if timed out
:returns: (str) the new mode if successful, (bool) False if timed out
"""
r = self.get_server_details(server_id)
if r['status'] != current_status:
return r['status']
status, progress = get_status(self, item_id)
if status != current_status:
return status
old_wait = total_wait = 0
if current_status == 'BUILD':
max_wait = 100
wait_gen = wait_cb(max_wait) if wait_cb else None
elif wait_cb:
if wait_cb:
wait_gen = wait_cb(1 + max_wait // delay)
wait_gen.next()
while r['status'] == current_status and total_wait <= max_wait:
if current_status == 'BUILD':
total_wait = int(r['progress'])
if wait_cb:
for i in range(int(old_wait), int(total_wait)):
while status == current_status and total_wait <= max_wait:
if wait_cb:
try:
for i in range(total_wait - old_wait):
wait_gen.next()
old_wait = total_wait
else:
stdout.write('.')
stdout.flush()
except Exception:
break
else:
if wait_cb:
wait_gen.next()
else:
stdout.write('.')
stdout.flush()
total_wait += delay
stdout.write('.')
stdout.flush()
old_wait = total_wait
total_wait = progress or (total_wait + 1)
sleep(delay)
r = self.get_server_details(server_id)
status, progress = get_status(self, item_id)
if r['status'] != current_status:
if total_wait < max_wait:
if wait_cb:
try:
while True:
for i in range(max_wait):
wait_gen.next()
except:
pass
return r['status']
return False
return status if status != current_status else False
def wait_server(
self, server_id,
current_status='BUILD',
delay=1, max_wait=100, wait_cb=None):
"""Wait for server while its status is current_status
:param server_id: integer (str or int)
:param current_status: (str) BUILD|ACTIVE|STOPPED|DELETED|REBOOT
:param delay: time interval between retries
:param wait_cb: if set a progressbar is used to show progress
:returns: (str) the new mode if succesfull, (bool) False if timed out
"""
def get_status(self, server_id):
r = self.get_server_details(server_id)
return r['status'], (r.get('progress', None) if (
current_status in ('BUILD', )) else None)
return self._wait(
server_id, current_status, get_status, delay, max_wait, wait_cb)
def wait_network(
self, net_id,
current_status='LALA', delay=1, max_wait=100, wait_cb=None):
"""Wait for network while its status is current_status
:param net_id: integer (str or int)
:param current_status: (str) PENDING | ACTIVE | DELETED
:param delay: time interval between retries
:param wait_cb: if set a progressbar is used to show progress
:returns: (str) the new mode if succesfull, (bool) False if timed out
"""
def get_status(self, net_id):
r = self.get_network_details(net_id)
return r['status'], None
return self._wait(
net_id, current_status, get_status, delay, max_wait, wait_cb)
def get_floating_ip_pools(self):
"""
......
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