Attached is the CLI code tarball. It is documented at http://test.kernel.org/autotest/CLIHowTo
From: [email protected]
git-svn-id: http://test.kernel.org/svn/autotest/trunk@1950 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/cli/action_common.py b/cli/action_common.py
new file mode 100755
index 0000000..b3ad8572
--- /dev/null
+++ b/cli/action_common.py
@@ -0,0 +1,263 @@
+#
+# Copyright 2008 Google Inc. All Rights Reserved.
+
+"""This module contains the common behavior of some actions
+
+Operations on ACLs or labels are very similar, so are creations and
+deletions. The following classes provide the common handling.
+
+In these case, the class inheritance is, taking the command
+'atest label create' as an example:
+
+ atest
+ / \
+ / \
+ / \
+ atest_create label
+ \ /
+ \ /
+ \ /
+ label_create
+
+
+For 'atest label add':
+
+ atest
+ / \
+ / \
+ / \
+ | label
+ | |
+ | |
+ | |
+ atest_add label_add_or_remove
+ \ /
+ \ /
+ \ /
+ label_add
+
+
+
+"""
+
+import re, socket, types
+from autotest_lib.cli import topic_common
+
+
+#
+# List action
+#
+class atest_list(topic_common.atest):
+ """atest <topic> list"""
+ usage_action = 'list'
+
+
+ def _convert_wildcard(self, old_key, new_key,
+ value, filters, check_results):
+ filters[new_key] = value.rstrip('*')
+ check_results[new_key] = None
+ del filters[old_key]
+ del check_results[old_key]
+
+
+ def _convert_name_wildcard(self, key, value, filters, check_results):
+ if value.endswith('*'):
+ # Could be __name, __login, __hostname
+ new_key = key + '__startswith'
+ self._convert_wildcard(key, new_key, value, filters, check_results)
+
+
+ def _convert_in_wildcard(self, key, value, filters, check_results):
+ if value.endswith('*'):
+ assert(key.endswith('__in'))
+ new_key = key.replace('__in', '__startswith', 1)
+ self._convert_wildcard(key, new_key, value, filters, check_results)
+
+
+ def check_for_wildcard(self, filters, check_results):
+ """Check if there is a wilcard (only * for the moment)
+ and replace the request appropriately"""
+ for (key, values) in filters.iteritems():
+ if isinstance(values, types.StringTypes):
+ self._convert_name_wildcard(key, values,
+ filters, check_results)
+ continue
+
+ if isinstance(values, types.ListType):
+ if len(values) == 1:
+ self._convert_in_wildcard(key, values[0],
+ filters, check_results)
+ continue
+
+ for value in values:
+ if value.endswith('*'):
+ # Can only be a wildcard if it is by itelf
+ self.invalid_syntax('Cannot mix wilcards and items')
+
+
+ def execute(self, op, filters={}, check_results={}):
+ """Generic list execute:
+ If no filters where specified, list all the items. If
+ some specific items where asked for, filter on those:
+ check_results has the same keys than filters. If only
+ one filter is set, we use the key from check_result to
+ print the error"""
+ self.check_for_wildcard(filters, check_results)
+
+ socket.setdefaulttimeout(topic_common.LIST_SOCKET_TIMEOUT)
+ results = self.execute_rpc(op, **filters)
+
+ for dbkey in filters.keys():
+ if not check_results.get(dbkey, None):
+ # Don't want to check the results
+ # for this key
+ continue
+
+ if len(results) == len(filters[dbkey]):
+ continue
+
+ # Some bad items
+ field = check_results[dbkey]
+ # The filtering for the job is on the ID which is an int.
+ # Convert it as the jobids from the CLI args are strings.
+ good = set(str(result[field]) for result in results)
+ self.invalid_arg('Unknown %s(s): \n' % self.msg_topic,
+ ', '.join(set(filters[dbkey]) - good))
+ return results
+
+
+ def output(self, results, keys, sublist_keys=[]):
+ self.print_table(results, keys, sublist_keys)
+
+
+#
+# Creation & Deletion of a topic (ACL, label, user)
+#
+class atest_create_or_delete(topic_common.atest):
+ """atest <topic> [create|delete]
+ To subclass this, you must define:
+ Example Comment
+ self.topic 'acl_group'
+ self.op_action 'delete' Action to remove a 'topic'
+ self.data {} Additional args for the topic
+ creation/deletion
+ self.msg_topic: 'ACL' The printable version of the topic.
+ self.msg_done: 'Deleted' The printable version of the action.
+ """
+ display_ids = False
+
+
+ def execute(self):
+ handled = []
+
+ # Create or Delete the <topic> altogether
+ op = '%s_%s' % (self.op_action, self.topic)
+ for item in self.get_items():
+ try:
+ self.data[self.data_item_key] = item
+ new_id = self.execute_rpc(op, item=item, **self.data)
+ if self.display_ids:
+ # For job create
+ handled.append('%s (id %s)' % (item, new_id))
+ else:
+ handled.append(item)
+ except topic_common.CliError:
+ pass
+ return handled
+
+
+ def output(self, results):
+ if results:
+ self.print_wrapped ("%s %s" % (self.msg_done, self.msg_topic),
+ results)
+
+
+class atest_create(atest_create_or_delete):
+ usage_action = 'create'
+ op_action = 'add'
+ msg_done = 'Created'
+
+
+class atest_delete(atest_create_or_delete):
+ data_item_key = 'id'
+ usage_action = op_action = 'delete'
+ msg_done = 'Deleted'
+
+
+#
+# Adding or Removing users or hosts from a topic (ACL or label)
+#
+class atest_add_or_remove(topic_common.atest):
+ """atest <topic> [add|remove]
+ To subclass this, you must define:
+ Example Comment
+ self.topic 'acl_group'
+ self.op_action 'remove' Action for adding users/hosts
+ """
+
+ def _add_remove_uh_to_topic(self, item, what):
+ """Adds the 'what' (users or hosts) to the 'item'"""
+ uhs = getattr(self, what)
+ if len(uhs) == 0:
+ # 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
+
+
+ def execute(self):
+ """Adds or removes users or hosts from a topic, e.g.:
+ add hosts to labels:
+ self.topic = 'label'
+ self.op_action = 'add'
+ self.get_items() = the labels that the hosts
+ should be added to"""
+ oks = {}
+ for item in self.get_items():
+ for what in ['users', 'hosts']:
+ try:
+ self._add_remove_uh_to_topic(item, what)
+ except AttributeError:
+ pass
+ except topic_common.CliError, err:
+ # The error was already logged by
+ # self.failure()
+ pass
+ else:
+ oks.setdefault(item, []).append(what)
+
+ users_ok = [item for (item, what) in oks.items() if 'users' in what]
+ hosts_ok = [item for (item, what) in oks.items() if 'hosts' in what]
+
+ return (users_ok, hosts_ok)
+
+
+ def output(self, results):
+ (users_ok, hosts_ok) = results
+ if users_ok:
+ self.print_wrapped("%s %s %s user" %
+ (self.msg_done,
+ self.msg_topic,
+ ', '.join(users_ok)),
+ self.users)
+
+ if hosts_ok:
+ self.print_wrapped("%s %s %s host" %
+ (self.msg_done,
+ self.msg_topic,
+ ', '.join(hosts_ok)),
+ self.hosts)
+
+
+class atest_add(atest_add_or_remove):
+ usage_action = op_action = 'add'
+ msg_done = 'Added to'
+ usage_words = ('Add', 'to')
+
+
+class atest_remove(atest_add_or_remove):
+ usage_action = op_action = 'remove'
+ msg_done = 'Removed from'
+ usage_words = ('Remove', 'from')