Commit 6f2b87c1 authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Implement network wait

Refs: #4563
parent 8c8a95b6
......@@ -39,11 +39,13 @@ from kamaki.cli.command_tree import CommandTree
from kamaki.cli.errors import (
CLISyntaxError, CLIBaseUrlError, CLIInvalidArgument)
from kamaki.clients.cyclades import CycladesNetworkClient
from kamaki.cli.argument import FlagArgument, ValueArgument, RepeatableArgument
from kamaki.cli.argument import (
FlagArgument, ValueArgument, RepeatableArgument, IntArgument)
from kamaki.cli.commands import _command_init, errors, addLogSettings
from kamaki.cli.commands import (
_optional_output_cmd, _optional_json, _name_filter, _id_filter)
from kamaki.cli.utils import filter_dicts_by_dict
from kamaki.cli.commands.cyclades import _service_wait
network_cmds = CommandTree('network', 'Networking API network commands')
......@@ -58,6 +60,14 @@ about_authentication = '\nUser Authentication:\
\n* to set authentication token: /config set cloud.<cloud>.token <token>'
class _network_wait(_service_wait):
def _wait(self, net_id, current_status, timeout=60):
super(_network_wait, self)._wait(
'Network', net_id, self.client.wait_network, current_status,
timeout=timeout)
class _init_network(_command_init):
@errors.generic.all
@addLogSettings
......@@ -164,7 +174,7 @@ class NetworkTypeArgument(ValueArgument):
@command(network_cmds)
class network_create(_init_network, _optional_json):
class network_create(_init_network, _optional_json, _network_wait):
"""Create a new network"""
arguments = dict(
......@@ -173,7 +183,8 @@ class network_create(_init_network, _optional_json):
'Make network shared (special privileges required)', '--shared'),
network_type=NetworkTypeArgument(
'Valid network types: %s' % (', '.join(NetworkTypeArgument.types)),
'--type')
'--type'),
wait=FlagArgument('Wait network to build', ('-w', '--wait')),
)
required = ('network_type', )
......@@ -183,6 +194,8 @@ class network_create(_init_network, _optional_json):
def _run(self, network_type):
net = self.client.create_network(
network_type, name=self['name'], shared=self['shared'])
if self['wait']:
self._wait(net['id'], net['status'])
self._print(net, self.print_dict)
def main(self):
......@@ -225,6 +238,33 @@ class network_modify(_init_network, _optional_json):
self._run(network_id=network_id)
@command(network_cmds)
class network_wait(_init_network, _network_wait):
"""Wait for network 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, 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, current_status='PENDING'):
super(self.__class__, self)._run()
self._run(network_id=network_id, current_status=current_status)
@command(subnet_cmds)
class subnet_list(_init_network, _optional_json, _name_filter, _id_filter):
"""List subnets
......
......@@ -492,3 +492,61 @@ class Client(Logged):
def move(self, path, **kwargs):
return self.request('move', path, **kwargs)
class Waiter(object):
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)
: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: (method(total steps)) returns a generator for
reporting progress or timeouts i.e., for a progress bar
:returns: (str) the new mode if successful, (bool) False if timed out
"""
status, progress = get_status(self, item_id)
if wait_cb:
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:
for i in range(total_wait - old_wait):
wait_gen.next()
except Exception:
break
old_wait = total_wait
total_wait = progress or total_wait + 1
sleep(delay)
status, progress = get_status(self, item_id)
if total_wait < max_wait:
if wait_cb:
try:
for i in range(max_wait):
wait_gen.next()
except:
pass
return status if status != current_status else False
......@@ -31,15 +31,13 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from time import sleep
from kamaki.clients.cyclades.rest_api import CycladesRestClient
from kamaki.clients.network import NetworkClient
from kamaki.clients.utils import path4url
from kamaki.clients import ClientError
from kamaki.clients import ClientError, Waiter
class CycladesClient(CycladesRestClient):
class CycladesClient(CycladesRestClient, Waiter):
"""Synnefo Cyclades Compute API client"""
def create_server(
......@@ -283,60 +281,6 @@ class CycladesClient(CycladesRestClient):
req = dict(remove=dict(attachment=nic))
self.networks_post(netid, 'action', json_data=req)
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)
: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 progress bar is used to show progress
:returns: (str) the new mode if successful, (bool) False if timed out
"""
status, progress = get_status(self, item_id)
if wait_cb:
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:
for i in range(total_wait - old_wait):
wait_gen.next()
except Exception:
break
old_wait = total_wait
total_wait = progress or total_wait + 1
sleep(delay)
status, progress = get_status(self, item_id)
if total_wait < max_wait:
if wait_cb:
try:
for i in range(max_wait):
wait_gen.next()
except:
pass
return status if status != current_status else False
def wait_server(
self, server_id,
current_status='BUILD',
......@@ -414,7 +358,7 @@ class CycladesClient(CycladesRestClient):
server_id, current_status, get_status, delay, max_wait, wait_cb)
class CycladesNetworkClient(NetworkClient):
class CycladesNetworkClient(NetworkClient, Waiter):
"""Cyclades Network API extentions"""
network_types = (
......@@ -451,3 +395,28 @@ class CycladesNetworkClient(NetworkClient):
port['fixed_ips'] = fixed_ips
r = self.ports_post(json_data=dict(port=port), success=201)
return r.json['port']
def wait_network(
self, net_id,
current_status='PENDING', 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
:max_wait: (int) timeout in secconds
: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)
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