Implement network wait
authorStavros Sachtouris <saxtouri@admin.grnet.gr>
Thu, 28 Nov 2013 15:59:40 +0000 (17:59 +0200)
committerStavros Sachtouris <saxtouri@admin.grnet.gr>
Thu, 28 Nov 2013 15:59:40 +0000 (17:59 +0200)
Refs: #4563

kamaki/cli/commands/network.py
kamaki/clients/__init__.py
kamaki/clients/cyclades/__init__.py

index 94758fb..ebfc956 100644 (file)
@@ -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
index 437f75b..0422c36 100644 (file)
@@ -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
index 35055f1..3117603 100644 (file)
 # 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)