Continue to add/remove the valid hosts or users to the label or ACL in
case of failures.
Modified the corresponding unittests:
 . Adding check_playback() everywhere.
 . Changing to the new Exception messages.
 . New test for partial failure.

Risk: medium
Visibility: medium

Signed-off-by: Jean-Marc Eurin <[email protected]>



git-svn-id: http://test.kernel.org/svn/autotest/trunk@2525 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/cli/acl_unittest.py b/cli/acl_unittest.py
index 89ec59e..9f5c9ff 100755
--- a/cli/acl_unittest.py
+++ b/cli/acl_unittest.py
@@ -292,9 +292,40 @@
                            {'id': 'acl0',
                             'users': ['user0', 'user1']},
                             False,
-                            'DoesNotExist: User matching query '
-                            'does not exist.')],
-                     err_words_ok=['acl0', 'user0', 'user1'])
+                            'DoesNotExist: The following Users do not exist: '
+                            'user0, user1')],
+                     err_words_ok=['user0', 'user1'])
+
+
+    def test_acl_add_bad_users_hosts(self):
+        self.run_cmd(argv=['atest', 'acl', 'add', 'acl0',
+                           '-u', 'user0,user1', '-m', 'host0'],
+                     rpcs=[('acl_group_add_users',
+                           {'id': 'acl0',
+                            'users': ['user0', 'user1']},
+                            False,
+                            'DoesNotExist: The following Users do not exist: '
+                            'user0'),
+                           ('acl_group_add_users',
+                           {'id': 'acl0',
+                            'users': ['user1']},
+                            True,
+                            None),
+                           ('acl_group_add_hosts',
+                            {'id': 'acl0',
+                             'hosts': ['host0', 'host1']},
+                            False,
+                            'DoesNotExist: The following Hosts do not exist: '
+                            'host1'),
+                           ('acl_group_add_hosts',
+                            {'id': 'acl0',
+                             'hosts': ['host0']},
+                            True,
+                            None)],
+                     out_words_ok=['acl0', 'user1', 'host0'],
+                     out_words_no=['user0', 'host1'],
+                     err_words_ok=['user0', 'host1'],
+                     err_words_no=['user1', 'host0'])
 
 
 class acl_remove_unittest(cli_mock.cli_unittest):
diff --git a/cli/action_common.py b/cli/action_common.py
index 13e1ac5..edcd8aa 100755
--- a/cli/action_common.py
+++ b/cli/action_common.py
@@ -217,9 +217,19 @@
             # To skip the try/else
             raise AttributeError
         op = '%s_%s_%s' % (self.topic, self.op_action, what)
-        self.execute_rpc(op=op,                                 # The opcode
-                         item='%s (%s)' %(item, ','.join(uhs)), # The error
-                         **{'id': item, what: uhs})             # The data
+        try:
+            self.execute_rpc(op=op,                       # The opcode
+                             **{'id': item, what: uhs})   # The data
+            setattr(self, 'good_%s' % what, uhs)
+        except topic_common.CliError, full_error:
+            bad_uhs = self.parse_json_exception(full_error)
+            good_uhs = list(set(uhs) - set(bad_uhs))
+            if bad_uhs and good_uhs:
+                self.execute_rpc(op=op,
+                                 **{'id': item, what: good_uhs})
+                setattr(self, 'good_%s' % what, good_uhs)
+            else:
+                raise
 
 
     def execute(self):
@@ -227,7 +237,7 @@
         add hosts to labels:
           self.topic = 'label'
           self.op_action = 'add'
-          self.get_items() = the labels that the hosts
+          self.get_items() = the labels/ACLs that the hosts
                              should be added to"""
         oks = {}
         for item in self.get_items():
@@ -256,14 +266,14 @@
                                (self.msg_done,
                                 self.msg_topic,
                                 ', '.join(users_ok)),
-                               self.users)
+                               self.good_users)
 
         if hosts_ok:
             self.print_wrapped("%s %s %s host" %
                                (self.msg_done,
                                 self.msg_topic,
                                 ', '.join(hosts_ok)),
-                               self.hosts)
+                               self.good_hosts)
 
 
 class atest_add(atest_add_or_remove):
diff --git a/cli/action_common_unittest.py b/cli/action_common_unittest.py
index 84c340f..95ef576 100755
--- a/cli/action_common_unittest.py
+++ b/cli/action_common_unittest.py
@@ -198,6 +198,7 @@
                          {'name': 'label0', 'platform': False},
                          True, 42)])
         ret = acr.execute()
+        self.god.check_playback()
         self.assert_(['label0'], ret)
 
 
@@ -210,6 +211,7 @@
                          {'name': 'label1', 'platform': False},
                          True, 43)])
         ret = acr.execute()
+        self.god.check_playback()
         self.assertEqualNoOrder(['label0', 'label1'], ret)
 
 
@@ -221,23 +223,10 @@
                          '''ValidationError:
                          {'name': 'This value must be unique (label0)'}''')])
         ret = acr.execute()
+        self.god.check_playback()
         self.assertEqualNoOrder([], ret)
 
 
-    def test_execute_create_error_dup(self):
-        acr = self._create_cr_del(['label0'])
-        self.mock_rpcs([('add_label',
-                         {'name': 'label0', 'platform': False},
-                         True, 42),
-                        ('add_label',
-                         {'name': 'label0', 'platform': False},
-                         False,
-                         '''ValidationError:
-                         {'name': 'This value must be unique (label0)'}''')])
-        ret = acr.execute()
-        self.assertEqualNoOrder(['label0'], ret)
-
-
 
 #
 # Adding or Removing users or hosts from a topic(ACL or label)
@@ -254,8 +243,9 @@
             addrm.hosts = hosts
 
         addrm.topic = 'acl_group'
-        addrm.usage_topic = 'ACL'
+        addrm.msg_topic = 'ACL'
         addrm.op_action = 'add'
+        addrm.msg_done = 'Added to'
         addrm.get_items = _items
         return addrm
 
@@ -269,6 +259,7 @@
                          True,
                          None)])
         acl_addrm._add_remove_uh_to_topic('acl0', 'users')
+        self.god.check_playback()
 
 
     def test__add_remove_uh_to_topic_raise(self):
@@ -288,6 +279,7 @@
                          True,
                          None)])
         (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
         self.assertEqualNoOrder(['acl0'], users_ok)
         self.assertEqual([], hosts_ok)
 
@@ -308,29 +300,79 @@
                          True,
                          None)])
         (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
         self.assertEqualNoOrder(['acl0'], users_ok)
         self.assertEqualNoOrder(['acl0'], hosts_ok)
-        self.god.check_playback()
 
 
     def test_execute_add_or_remove_uh_to_topic_acl_bad_users(self):
         acl_addrm = self._create_add_remove('acl0',
-                                        users=['user0', 'user1'])
+                                            users=['user0', 'user1'])
         self.mock_rpcs([('acl_group_add_users',
                          {'id': 'acl0',
                           'users': ['user0', 'user1']},
                          False,
-                         'DoesNotExist: User matching query does not exist.')])
+                         'DoesNotExist: The following users do not exist: '
+                         'user0, user1')])
         (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
         self.assertEqual([], users_ok)
         self.assertEqual([], hosts_ok)
-        self.assertOutput(acl_addrm,
-                          err_words_ok=['DoesNotExist', 'acl0',
+        self.assertOutput(acl_addrm, (users_ok, hosts_ok),
+                          err_words_ok=['DoesNotExist',
                                         'acl_group_add_users',
                                         'user0', 'user1'],
                           err_words_no = ['acl_group_add_hosts'])
 
 
+    def test_execute_add_or_remove_uh_to_topic_acl_bad_users_partial(self):
+        acl_addrm = self._create_add_remove('acl0',
+                                            users=['user0', 'user1'])
+        self.mock_rpcs([('acl_group_add_users',
+                         {'id': 'acl0',
+                          'users': ['user0', 'user1']},
+                         False,
+                         'DoesNotExist: The following users do not exist: '
+                         'user0'),
+                        ('acl_group_add_users',
+                         {'id': 'acl0',
+                          'users': ['user1']},
+                         True,
+                         None)])
+        (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
+        self.assertEqual(['acl0'], users_ok)
+        self.assertEqual([], hosts_ok)
+        self.assertOutput(acl_addrm, (users_ok, hosts_ok),
+                          out_words_ok=['Added to ACL acl0', 'user1'],
+                          err_words_ok=['DoesNotExist',
+                                        'acl_group_add_users',
+                                        'user0'],
+                          err_words_no = ['acl_group_add_hosts'])
+
+
+    def test_execute_add_or_remove_uh_to_topic_acl_bad_u_partial_kill(self):
+        acl_addrm = self._create_add_remove('acl0',
+                                            users=['user0', 'user1'])
+        acl_addrm.kill_on_failure = True
+        self.mock_rpcs([('acl_group_add_users',
+                         {'id': 'acl0',
+                          'users': ['user0', 'user1']},
+                         False,
+                         'DoesNotExist: The following users do not exist: '
+                         'user0')])
+        sys.exit.expect_call(1).and_raises(cli_mock.ExitException)
+        self.god.mock_io()
+        self.assertRaises(cli_mock.ExitException, acl_addrm.execute)
+        (out, err) = self.god.unmock_io()
+        self.god.check_playback()
+        self._check_output(out=out, err=err,
+                          err_words_ok=['DoesNotExist',
+                                        'acl_group_add_users',
+                                        'user0'],
+                          err_words_no = ['acl_group_add_hosts'])
+
+
     def test_execute_add_or_remove_uh_to_topic_acl_bad_users_good_hosts(self):
         acl_addrm = self._create_add_remove('acl0',
                                         users=['user0', 'user1'],
@@ -339,7 +381,8 @@
                          {'id': 'acl0',
                           'users': ['user0', 'user1']},
                          False,
-                         'DoesNotExist: User matching query does not exist.'),
+                         'DoesNotExist: The following users do not exist: '
+                         'user0, user1'),
                         ('acl_group_add_hosts',
                          {'id': 'acl0',
                           'hosts': ['host0', 'host1']},
@@ -347,10 +390,13 @@
                          None)])
 
         (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
         self.assertEqual([], users_ok)
         self.assertEqual(['acl0'], hosts_ok)
-        self.assertOutput(acl_addrm,
-                          err_words_ok=['DoesNotExist', 'acl0',
+        self.assertOutput(acl_addrm, (users_ok, hosts_ok),
+                          out_words_ok=['Added to ACL acl0 hosts:',
+                                        'host0', 'host1'],
+                          err_words_ok=['DoesNotExist',
                                         'acl_group_add_users',
                                         'user0', 'user1'],
                           err_words_no = ['acl_group_add_hosts'])
@@ -369,18 +415,56 @@
                          {'id': 'acl0',
                           'hosts': ['host0', 'host1']},
                          False,
-                         'DoesNotExist: Host matching query does not exist.')])
+                         'DoesNotExist: The following hosts do not exist: '
+                         'host0, host1')])
 
         (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
         self.assertEqual(['acl0'], users_ok)
         self.assertEqual([], hosts_ok)
-        self.assertOutput(acl_addrm,
-                          err_words_ok=['DoesNotExist', 'acl0',
+        self.assertOutput(acl_addrm, (users_ok, hosts_ok),
+                          out_words_ok=['Added to ACL acl0 users:',
+                                        'user0', 'user1'],
+                          err_words_ok=['DoesNotExist',
                                         'acl_group_add_hosts',
                                         'host0', 'host1'],
                           err_words_no = ['acl_group_add_users'])
 
 
+    def test_exe_add_or_remove_uh_to_topic_acl_good_u_bad_hosts_partial(self):
+        acl_addrm = self._create_add_remove('acl0',
+                                        users=['user0', 'user1'],
+                                        hosts=['host0', 'host1'])
+        self.mock_rpcs([('acl_group_add_users',
+                         {'id': 'acl0',
+                          'users': ['user0', 'user1']},
+                         True,
+                         None),
+                        ('acl_group_add_hosts',
+                         {'id': 'acl0',
+                          'hosts': ['host0', 'host1']},
+                         False,
+                         'DoesNotExist: The following hosts do not exist: '
+                         'host1'),
+                        ('acl_group_add_hosts',
+                         {'id': 'acl0',
+                          'hosts': ['host0']},
+                         True,
+                         None)])
+
+        (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
+        self.assertEqual(['acl0'], users_ok)
+        self.assertEqual(['acl0'], hosts_ok)
+        self.assertOutput(acl_addrm, (users_ok, hosts_ok),
+                          out_words_ok=['Added to ACL acl0 users:',
+                                        'user0', 'user1', 'host0'],
+                          err_words_ok=['DoesNotExist',
+                                        'acl_group_add_hosts',
+                                        'host1'],
+                          err_words_no = ['acl_group_add_users'])
+
+
     def test_execute_add_or_remove_uh_to_topic_acl_bad_users_bad_hosts(self):
         acl_addrm = self._create_add_remove('acl0',
                                         users=['user0', 'user1'],
@@ -389,24 +473,69 @@
                          {'id': 'acl0',
                           'users': ['user0', 'user1']},
                          False,
-                         'DoesNotExist: User matching query does not exist.'),
+                         'DoesNotExist: The following users do not exist: '
+                         'user0, user1'),
                         ('acl_group_add_hosts',
                          {'id': 'acl0',
                           'hosts': ['host0', 'host1']},
                          False,
-                         'DoesNotExist: Host matching query does not exist.')])
+                         'DoesNotExist: The following hosts do not exist: '
+                         'host0, host1')])
+
 
         (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
         self.assertEqual([], users_ok)
         self.assertEqual([], hosts_ok)
-        self.assertOutput(acl_addrm,
-                          err_words_ok=['DoesNotExist', 'acl0',
+        self.assertOutput(acl_addrm, (users_ok, hosts_ok),
+                          err_words_ok=['DoesNotExist',
                                         'acl_group_add_hosts',
                                         'host0', 'host1',
                                         'acl_group_add_users',
                                         'user0', 'user1'])
 
 
+    def test_execute_add_or_remove_uh_to_topic_acl_bad_u_bad_h_partial(self):
+        acl_addrm = self._create_add_remove('acl0',
+                                        users=['user0', 'user1'],
+                                        hosts=['host0', 'host1'])
+        self.mock_rpcs([('acl_group_add_users',
+                         {'id': 'acl0',
+                          'users': ['user0', 'user1']},
+                         False,
+                         'DoesNotExist: The following users do not exist: '
+                         'user0'),
+                        ('acl_group_add_users',
+                         {'id': 'acl0',
+                          'users': ['user1']},
+                         True,
+                         None),
+                        ('acl_group_add_hosts',
+                         {'id': 'acl0',
+                          'hosts': ['host0', 'host1']},
+                         False,
+                         'DoesNotExist: The following hosts do not exist: '
+                         'host1'),
+                        ('acl_group_add_hosts',
+                         {'id': 'acl0',
+                          'hosts': ['host0']},
+                         True,
+                         None)])
+        (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
+        self.assertEqual(['acl0'], users_ok)
+        self.assertEqual(['acl0'], hosts_ok)
+        self.assertOutput(acl_addrm, (users_ok, hosts_ok),
+                          out_words_ok=['Added to ACL acl0 user:',
+                                        'Added to ACL acl0 host:',
+                                        'user1', 'host0'],
+                          err_words_ok=['DoesNotExist',
+                                        'acl_group_add_hosts',
+                                        'host1',
+                                        'acl_group_add_users',
+                                        'user0'])
+
+
     def test_execute_add_or_remove_to_topic_bad_acl_uh(self):
         acl_addrm = self._create_add_remove('acl0',
                                         users=['user0', 'user1'],
@@ -424,15 +553,13 @@
                          'DoesNotExist: acl_group matching '
                          'query does not exist.')])
         (users_ok, hosts_ok) = acl_addrm.execute()
+        self.god.check_playback()
         self.assertEqual([], users_ok)
         self.assertEqual([], hosts_ok)
-        self.assertOutput(acl_addrm,
-                          err_words_ok=['DoesNotExist', 'acl0',
-                                        'acl_group_add_hosts', 'host0',
-                                        'host1', 'acl_group_add_users',
-                                        'user0', 'user1'])
-
-
+        self.assertOutput(acl_addrm, (users_ok, hosts_ok),
+                          err_words_ok=['DoesNotExist',
+                                        'acl_group_add_hosts',
+                                        'acl_group_add_users'])
 
 
 if __name__ == '__main__':
diff --git a/cli/cli_mock.py b/cli/cli_mock.py
index 59d15b5..4acf976 100755
--- a/cli/cli_mock.py
+++ b/cli/cli_mock.py
@@ -62,10 +62,11 @@
             self.assertEqual('', err)
 
 
-    def assertOutput(self, obj,
+    def assertOutput(self, obj, results,
                      out_words_ok=[], out_words_no=[],
                      err_words_ok=[], err_words_no=[]):
         self.god.mock_io()
+        obj.output(results)
         obj.show_all_failures()
         (out, err) = self.god.unmock_io()
         self._check_output(out, out_words_ok, out_words_no,
diff --git a/cli/topic_common.py b/cli/topic_common.py
index 8092bac..ec6a6bc 100755
--- a/cli/topic_common.py
+++ b/cli/topic_common.py
@@ -171,6 +171,21 @@
         sys.exit(1)
 
 
+    def parse_json_exception(self, full_error):
+        """Parses the JSON exception to extract the bad
+        items and returns them
+        This is very kludgy for the moment, but we would need
+        to refactor the exceptions sent from the front end
+        to make this better"""
+        errmsg = str(full_error).split('Traceback')[0].rstrip('\n')
+        parts = errmsg.split(':')
+        # Kludge: If there are 2 colons the last parts contains
+        # the items that failed.
+        if len(parts) != 3:
+            return []
+        return [item.strip() for item in parts[2].split(',') if item.strip()]
+
+
     def failure(self, full_error, item=None, what_failed=''):
         """If kill_on_failure, print this error and die,
         otherwise, queue the error and accumulate all the items