NEW server method: wait_server + cli command
authorStavros Sachtouris <saxtouri@admin.grnet.gr>
Thu, 8 Nov 2012 12:48:27 +0000 (14:48 +0200)
committerStavros Sachtouris <saxtouri@admin.grnet.gr>
Thu, 8 Nov 2012 12:48:27 +0000 (14:48 +0200)
wait for server to reach state (e.g. while building)

kamaki/cli/argument.py
kamaki/cli/commands/cyclades_cli.py
kamaki/cli/commands/pithos_cli.py
kamaki/clients/cyclades.py

index 5233095..da6b2eb 100644 (file)
 from kamaki.cli.config import Config
 from kamaki.cli.errors import CLISyntaxError
 
+try:
+    from progress.bar import IncrementalBar
+except ImportError:
+    # progress not installed - pls, pip install progress
+    pass
+
 
 class Argument(object):
     """An argument that can be parsed from command line or otherwise"""
@@ -218,6 +224,40 @@ class KeyValueArgument(Argument):
                     details='%s is missing a "=" (usage: key1=val1 )\n' % pair)
             self._value[key.strip()] = val.strip()
 
+
+class ProgressBarArgument(FlagArgument):
+
+    def __init__(self, help='', parsed_name='', default=True):
+        self.suffix = '%(percent)d%%'
+        super(ProgressBarArgument, self).__init__(help, parsed_name, default)
+        try:
+            self.bar = IncrementalBar()
+        except NameError:
+            print('Waring: no progress bar functionality')
+
+    def get_generator(self, message, message_len=25):
+        if self.value:
+            return None
+        try:
+            bar = ProgressBar(message.ljust(message_len))
+        except NameError:
+            return None
+        return bar.get_generator()
+
+
+try:
+    class ProgressBar(IncrementalBar):
+        suffix = '%(percent)d%% - %(eta)ds'
+
+        def get_generator(self):
+            def progress_gen(n):
+                for i in self.iter(range(int(n))):
+                    yield
+                yield
+            return progress_gen
+except NameError:
+    pass
+
 _arguments = dict(config=_config_arg,
     help=Argument(0, 'Show help message', ('-h', '--help')),
     debug=FlagArgument('Include debug output', ('-d', '--debug')),
index 9b75447..a0663de 100644 (file)
@@ -36,6 +36,7 @@ from kamaki.cli.utils import print_dict, print_items, print_list, bold
 from kamaki.cli.errors import CLIError, raiseCLIError, CLISyntaxError
 from kamaki.clients.cyclades import CycladesClient, ClientError
 from kamaki.cli.argument import FlagArgument, ValueArgument
+from kamaki.cli.argument import ProgressBarArgument
 from kamaki.cli.commands import _command_init
 
 from base64 import b64encode
@@ -169,6 +170,12 @@ class server_create(_init_cyclades):
                 self.get_argument('personality'))
         except ClientError as err:
             raiseCLIError(err)
+        except ValueError as err:
+            raise CLIError('Invalid flavor id %s ' % flavor_id,
+                details='Flavor id must be a positive integer',
+                importance=1)
+        except Exception as err:
+            raise CLIError('Syntax error: %s\n' % err, importance=1)
         print_dict(reply)
 
 
@@ -183,7 +190,8 @@ class server_rename(_init_cyclades):
         except ClientError as err:
             raiseCLIError(err)
         except ValueError:
-            raise CLIError(message='Server id must be positive integer',
+            raise CLIError('Invalid server id %s ' % server_id,
+                details='Server id must be positive integer\n',
                 importance=1)
 
 
@@ -382,6 +390,32 @@ class server_stats(_init_cyclades):
 
 
 @command()
+class server_wait(_init_cyclades):
+    """Wait for server to finish [BUILD, STOPPED, REBOOT, ACTIVE]"""
+
+    def __init__(self, arguments={}):
+        super(self.__class__, self).__init__(arguments)
+        self.arguments['progress_bar'] = ProgressBarArgument(\
+            'do not show progress bar', '--no-progress-bar', False)
+
+    def main(self, server_id, currect_status='BUILD'):
+        super(self.__class__, self).main()
+        try:
+            progress_bar = self.arguments['progress_bar']
+            wait_cb = progress_bar.get_generator(\
+                'Server %s still in %s mode' % (server_id, currect_status))
+        except Exception:
+            wait_cb = None
+        new_mode = self.client.wait_server(server_id,
+            currect_status,
+            wait_cb=wait_cb)
+        if new_mode:
+            print('\nServer %s is now in %s mode' % (server_id, new_mode))
+        else:
+            print('\nTime out')
+
+
+@command()
 class flavor_list(_init_cyclades):
     """List flavors"""
 
index 6a5ac78..6131ee8 100644 (file)
@@ -35,6 +35,7 @@ from kamaki.cli import command
 from kamaki.cli.errors import CLIError, raiseCLIError
 from kamaki.cli.utils import format_size, print_dict, pretty_keys
 from kamaki.cli.argument import FlagArgument, ValueArgument, IntArgument
+from kamaki.cli.argument import ProgressBarArgument
 from kamaki.cli.commands import _command_init
 from kamaki.clients.pithos import PithosClient, ClientError
 from kamaki.cli.utils import bold
@@ -45,11 +46,6 @@ from datetime import datetime as dtm
 
 API_DESCRIPTION = dict(store='Pithos+ storage commands')
 
-try:
-    from progress.bar import IncrementalBar
-except ImportError:
-    # progress not installed - pls, pip install progress
-    pass
 
 # Argument functionality
 
@@ -88,40 +84,6 @@ class MetaArgument(ValueArgument):
         self._value = newvalue
 
 
-class ProgressBarArgument(FlagArgument):
-
-    def __init__(self, help='', parsed_name='', default=True):
-        self.suffix = '%(percent)d%%'
-        super(ProgressBarArgument, self).__init__(help, parsed_name, default)
-        try:
-            self.bar = IncrementalBar()
-        except NameError:
-            print('Waring: no progress bar functionality')
-
-    def get_generator(self, message, message_len=25):
-        if self.value:
-            return None
-        try:
-            bar = ProgressBar(message.ljust(message_len))
-        except NameError:
-            return None
-        return bar.get_generator()
-
-
-try:
-    class ProgressBar(IncrementalBar):
-        suffix = '%(percent)d%% - %(eta)ds'
-
-        def get_generator(self):
-            def progress_gen(n):
-                for i in self.iter(range(n)):
-                    yield
-                yield
-            return progress_gen
-except NameError:
-    pass
-
-
 class SharingArgument(ValueArgument):
     @property
     def value(self):
@@ -259,7 +221,7 @@ class _store_container_command(_store_account_command):
             else:
                 self.path = container_with_path
             if not path_is_optional and self.path is None:
-                raise CLIError(message="Object path is missing", status=11)
+                raise CLIError('Object path is missing\n', importance=1)
             return
         cnp = container_with_path.split(':')
         self.container = cnp[0]
@@ -269,7 +231,7 @@ class _store_container_command(_store_account_command):
             if path_is_optional:
                 self.path = None
             else:
-                raise CLIError(message="Object path is missing", status=11)
+                raise CLIError('Object path is missing\n', importance=1)
 
     def main(self, container_with_path=None, path_is_optional=True):
         super(_store_container_command, self).main()
@@ -629,7 +591,6 @@ class store_manifest(_store_container_command):
 class store_upload(_store_container_command):
     """Upload a file"""
 
-
     def __init__(self, arguments={}):
         super(self.__class__, self).__init__(arguments)
         self.arguments['use_hashes'] = FlagArgument(\
index 8f88064..7d2cf8d 100644 (file)
@@ -34,6 +34,7 @@
 from kamaki.clients.compute import ComputeClient, ClientError
 from kamaki.clients.utils import path4url
 import json
+from time import sleep
 
 
 class CycladesClient(ComputeClient):
@@ -212,3 +213,50 @@ class CycladesClient(ComputeClient):
         for nic in r:
             req = dict(remove=dict(attachment=nic))
             r = 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 to reach a status different from current_status
+            @return the new mode if succesfull, False if timed out
+            @server_id
+            @current_status
+            @delay time interval between retries
+            @wait_cb if set a progressbar is used to show progress
+        """
+        r = self.get_server_details(server_id)
+        if r['status'] != current_status:
+            return r['status']
+        total_wait = 1
+        old_wait = total_wait
+
+        if current_status == 'BUILD':
+            max_wait = delay * 100
+
+        if wait_cb:
+            wait_gen = wait_cb(1 + max_wait // delay)
+
+        while r['status'] == current_status and total_wait <= max_wait:
+            if current_status == 'BUILD':
+                total_wait = r['progress']
+                if wait_cb:
+                    for i in range(old_wait, total_wait):
+                        wait_gen.next()
+                    old_wait = total_wait
+            else:
+                if wait_cb:
+                    wait_gen.next()
+                total_wait += delay
+            sleep(delay)
+            r = self.get_server_details(server_id)
+
+        if wait_cb and r['status'] != current_status:
+            try:
+                while True:
+                    wait_gen.next()
+            except:
+                pass
+            return r['status']
+        return False