Modify progress bar behavior in cyclades wait
authorStavros Sachtouris <saxtouri@admin.grnet.gr>
Fri, 27 Sep 2013 10:49:09 +0000 (13:49 +0300)
committerStavros Sachtouris <saxtouri@admin.grnet.gr>
Fri, 27 Sep 2013 10:49:09 +0000 (13:49 +0300)
Refs: #4352

Progress bar is now able to "count down", be "eating" the bar instead of
filling it, thus has meaning for timeouts
All waits are time-outs, except for "server BUILD"

kamaki/cli/argument/__init__.py
kamaki/cli/argument/test.py
kamaki/cli/commands/__init__.py
kamaki/cli/commands/cyclades.py
kamaki/clients/cyclades/__init__.py

index 2067a52..4335944 100644 (file)
@@ -332,7 +332,7 @@ 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, timeout=False):
         """Get a generator to handle progress of the bar (gen.next())"""
         if self.value:
             return None
@@ -341,8 +341,18 @@ class ProgressBarArgument(FlagArgument):
         except NameError:
             self.value = None
             return self.value
+        if timeout:
+            bar_phases = list(self.bar.phases)
+            bar_phases[0], bar_phases[-1] = bar_phases[-1], ''
+            self.bar.phases = bar_phases
+            self.bar.empty_fill = bar_phases[0]
+            self.bar.bar_prefix = ' (Timeout:'
+            self.bar.bar_suffix = ' '
+            self.bar.suffix = '%(eta)ds)'
+            self.bar.eta = 120
+        else:
+            self.bar.suffix = '%(percent)d%% - %(eta)ds'
         self.bar.message = message.ljust(message_len)
-        self.bar.suffix = '%(percent)d%% - %(eta)ds'
         self.bar.start()
 
         def progress_gen(n):
index f77f13a..c6c3d07 100644 (file)
@@ -369,13 +369,29 @@ 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, timeout=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.bar_prefix, ' (Timeout:')
+                self.assertEqual(pba.bar.suffix, '%(eta)ds)')
+            finally:
+                try:
+                    pba.finish()
+                except Exception:
+                    pass
 
     def test_finish(self):
         pba = argument.ProgressBarArgument(parsed_name='--progress')
index b1fa398..3d20b96 100644 (file)
@@ -177,12 +177,12 @@ 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', timeout=False):
         """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, timeout=timeout)
         except Exception:
             return (None, None)
         return (progress_bar, gen)
index dd2b702..eec24ce 100644 (file)
@@ -75,20 +75,23 @@ 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,
+            timeout=True):
         (progress_bar, wait_cb) = self._safe_progress_bar(
-            '%s %s: periodically check if status is %s' % (
-                service, service_id, currect_status))
+            '%s %s: status is still %s' % (
+                service, service_id, current_status),
+            timeout=timeout)
 
         try:
             new_mode = status_method(
-                service_id, currect_status, wait_cb=wait_cb)
+                service_id, current_status, wait_cb=wait_cb)
             if new_mode:
-                self.error('\n%s %s: status is %s' % (
+                self.error('%s %s: status is now %s' % (
                     service, service_id, new_mode))
             else:
-                self.error('\nTime out: %s %s still in %s' % (
-                    service, service_id, currect_status))
+                self.error('%s %s: (timeout) status is still %s' % (
+                    service, service_id, current_status))
         except KeyboardInterrupt:
             self.error('\n- canceled')
         finally:
@@ -97,16 +100,17 @@ class _service_wait(object):
 
 class _server_wait(_service_wait):
 
-    def _wait(self, server_id, currect_status):
+    def _wait(self, server_id, current_status):
         super(_server_wait, self)._wait(
-            'Server', server_id, self.client.wait_server, currect_status)
+            'Server', server_id, self.client.wait_server, current_status,
+            timeout=(current_status not in ('BUILD', )))
 
 
 class _network_wait(_service_wait):
 
-    def _wait(self, net_id, currect_status):
+    def _wait(self, net_id, current_status):
         super(_network_wait, self)._wait(
-            'Network', net_id, self.client.wait_network, currect_status)
+            'Network', net_id, self.client.wait_network, current_status)
 
 
 class _init_cyclades(_command_init):
@@ -699,12 +703,19 @@ class server_wait(_init_cyclades, _server_wait):
     @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)
+        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)
@@ -946,7 +957,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):
@@ -1055,12 +1066,19 @@ class network_wait(_init_cyclades, _network_wait):
     @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)
+        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)
index 678b840..760ca2f 100644 (file)
@@ -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)