2 # Copyright 2008 Google Inc. All Rights Reserved.
4 """This module contains the common behavior of some actions
6 Operations on ACLs or labels are very similar, so are creations and
7 deletions. The following classes provide the common handling.
9 In these case, the class inheritance is, taking the command
10 'atest label create' as an example:
23 For 'atest label add':
33 atest_add label_add_or_remove
43 import re
, socket
, types
44 from autotest_lib
.cli
import topic_common
50 class atest_list(topic_common
.atest
):
51 """atest <topic> list"""
55 def _convert_wildcard(self
, old_key
, new_key
,
56 value
, filters
, check_results
):
57 filters
[new_key
] = value
.rstrip('*')
58 check_results
[new_key
] = None
60 del check_results
[old_key
]
63 def _convert_name_wildcard(self
, key
, value
, filters
, check_results
):
64 if value
.endswith('*'):
65 # Could be __name, __login, __hostname
66 new_key
= key
+ '__startswith'
67 self
._convert
_wildcard
(key
, new_key
, value
, filters
, check_results
)
70 def _convert_in_wildcard(self
, key
, value
, filters
, check_results
):
71 if value
.endswith('*'):
72 assert key
.endswith('__in'), 'Key %s does not end with __in' % key
73 new_key
= key
.replace('__in', '__startswith', 1)
74 self
._convert
_wildcard
(key
, new_key
, value
, filters
, check_results
)
77 def check_for_wildcard(self
, filters
, check_results
):
78 """Check if there is a wilcard (only * for the moment)
79 and replace the request appropriately"""
80 for (key
, values
) in filters
.iteritems():
81 if isinstance(values
, types
.StringTypes
):
82 self
._convert
_name
_wildcard
(key
, values
,
83 filters
, check_results
)
86 if isinstance(values
, types
.ListType
):
88 self
._convert
_in
_wildcard
(key
, values
[0],
89 filters
, check_results
)
93 if value
.endswith('*'):
94 # Can only be a wildcard if it is by itelf
95 self
.invalid_syntax('Cannot mix wilcards and items')
98 def execute(self
, op
, filters
={}, check_results
={}):
99 """Generic list execute:
100 If no filters where specified, list all the items. If
101 some specific items where asked for, filter on those:
102 check_results has the same keys than filters. If only
103 one filter is set, we use the key from check_result to
105 self
.check_for_wildcard(filters
, check_results
)
107 results
= self
.execute_rpc(op
, **filters
)
109 for dbkey
in filters
.keys():
110 if not check_results
.get(dbkey
, None):
111 # Don't want to check the results
115 if len(results
) >= len(filters
[dbkey
]):
119 field
= check_results
[dbkey
]
120 # The filtering for the job is on the ID which is an int.
121 # Convert it as the jobids from the CLI args are strings.
122 good
= set(str(result
[field
]) for result
in results
)
123 self
.invalid_arg('Unknown %s(s): \n' % self
.msg_topic
,
124 ', '.join(set(filters
[dbkey
]) - good
))
128 def output(self
, results
, keys
, sublist_keys
=[]):
129 self
.print_table(results
, keys
, sublist_keys
)
133 # Creation & Deletion of a topic (ACL, label, user)
135 class atest_create_or_delete(topic_common
.atest
):
136 """atest <topic> [create|delete]
137 To subclass this, you must define:
139 self.topic 'acl_group'
140 self.op_action 'delete' Action to remove a 'topic'
141 self.data {} Additional args for the topic
143 self.msg_topic: 'ACL' The printable version of the topic.
144 self.msg_done: 'Deleted' The printable version of the action.
149 # Create or Delete the <topic> altogether
150 op
= '%s_%s' % (self
.op_action
, self
.topic
)
151 for item
in self
.get_items():
153 self
.data
[self
.data_item_key
] = item
154 new_id
= self
.execute_rpc(op
, item
=item
, **self
.data
)
156 except topic_common
.CliError
:
161 def output(self
, results
):
163 results
= ["'%s'" % r
for r
in results
]
164 self
.print_wrapped("%s %s" % (self
.msg_done
, self
.msg_topic
),
168 class atest_create(atest_create_or_delete
):
169 usage_action
= 'create'
174 class atest_delete(atest_create_or_delete
):
176 usage_action
= op_action
= 'delete'
181 # Adding or Removing things (users, hosts or labels) from a topic
182 # (ACL, Label or AtomicGroup)
184 class atest_add_or_remove(topic_common
.atest
):
185 """atest <topic> [add|remove]
186 To subclass this, you must define these attributes:
189 op_action 'remove' Action for adding users/hosts
190 add_remove_things {'users': 'user'} Dict of things to try add/removing.
191 Keys are the attribute names. Values
192 are the word to print for an
193 individual item of such a value.
196 add_remove_things
= {'users': 'user', 'hosts': 'host'} # Original behavior
199 def _add_remove_uh_to_topic(self
, item
, what
):
200 """Adds the 'what' (such as users or hosts) to the 'item'"""
201 uhs
= getattr(self
, what
)
203 # To skip the try/else
205 op
= '%s_%s_%s' % (self
.topic
, self
.op_action
, what
)
207 self
.execute_rpc(op
=op
, # The opcode
208 **{'id': item
, what
: uhs
}) # The data
209 setattr(self
, 'good_%s' % what
, uhs
)
210 except topic_common
.CliError
, full_error
:
211 bad_uhs
= self
.parse_json_exception(full_error
)
212 good_uhs
= list(set(uhs
) - set(bad_uhs
))
213 if bad_uhs
and good_uhs
:
214 self
.execute_rpc(op
=op
,
215 **{'id': item
, what
: good_uhs
})
216 setattr(self
, 'good_%s' % what
, good_uhs
)
222 """Adds or removes things (users, hosts, etc.) from a topic, e.g.:
226 self.op_action = 'add'
227 self.add_remove_things = {'users': 'user', 'hosts': 'host'}
228 self.get_items() = The labels/ACLs that the hosts
232 A dictionary of lists of things added successfully using the same
233 keys as self.add_remove_things.
236 for item
in self
.get_items():
238 # This reverse sorting is only here to avoid breaking many
239 # existing extremely fragile unittests which depend on the
240 # exact order of the calls made below. 'users' must be run
242 plurals
= reversed(sorted(self
.add_remove_things
.keys()))
245 self
._add
_remove
_uh
_to
_topic
(item
, what
)
246 except AttributeError:
248 except topic_common
.CliError
, err
:
249 # The error was already logged by
253 oks
.setdefault(item
, []).append(what
)
256 for thing
in self
.add_remove_things
:
257 things_ok
= [item
for item
, what
in oks
.items() if thing
in what
]
258 results
[thing
] = things_ok
263 def output(self
, results
):
264 for thing
, single_thing
in self
.add_remove_things
.iteritems():
265 # Enclose each of the elements in a single quote.
266 things_ok
= ["'%s'" % t
for t
in results
[thing
]]
268 self
.print_wrapped("%s %s %s %s" % (self
.msg_done
,
270 ', '.join(things_ok
),
272 getattr(self
, 'good_%s' % thing
))
275 class atest_add(atest_add_or_remove
):
276 usage_action
= op_action
= 'add'
277 msg_done
= 'Added to'
278 usage_words
= ('Add', 'to')
281 class atest_remove(atest_add_or_remove
):
282 usage_action
= op_action
= 'remove'
283 msg_done
= 'Removed from'
284 usage_words
= ('Remove', 'from')