| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 1 | # Copyright Martin J. Bligh, Google Inc 2008 | 
 | 2 | # Released under the GPL v2 | 
 | 3 |  | 
 | 4 | """ | 
 | 5 | This class allows you to communicate with the frontend to submit jobs etc | 
 | 6 | It is designed for writing more sophisiticated server-side control files that | 
 | 7 | can recursively add and manage other jobs. | 
 | 8 |  | 
 | 9 | We turn the JSON dictionaries into real objects that are more idiomatic | 
 | 10 |  | 
| mbligh | c31e402 | 2008-12-11 19:32:30 +0000 | [diff] [blame] | 11 | For docs, see: | 
 | 12 |     http://autotest/afe/server/noauth/rpc/ | 
 | 13 |     http://autotest/new_tko/server/noauth/rpc/ | 
 | 14 |     http://docs.djangoproject.com/en/dev/ref/models/querysets/#queryset-api | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 15 | """ | 
 | 16 |  | 
| mbligh | 1f23f36 | 2008-12-22 14:46:12 +0000 | [diff] [blame] | 17 | import os, time, traceback, re | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 18 | import common | 
 | 19 | from autotest_lib.frontend.afe import rpc_client_lib | 
| mbligh | 37eceaa | 2008-12-15 22:56:37 +0000 | [diff] [blame] | 20 | from autotest_lib.client.common_lib import global_config | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 21 | from autotest_lib.client.common_lib import utils | 
| mbligh | 4e57661 | 2008-12-22 14:56:36 +0000 | [diff] [blame] | 22 | try: | 
 | 23 |     from autotest_lib.server.site_common import site_utils as server_utils | 
 | 24 | except: | 
 | 25 |     from autotest_lib.server import utils as server_utils | 
 | 26 | form_ntuples_from_machines = server_utils.form_ntuples_from_machines | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 27 |  | 
| mbligh | 37eceaa | 2008-12-15 22:56:37 +0000 | [diff] [blame] | 28 | GLOBAL_CONFIG = global_config.global_config | 
 | 29 | DEFAULT_SERVER = 'autotest' | 
 | 30 |  | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 31 |  | 
 | 32 | def dump_object(header, obj): | 
 | 33 |     """ | 
 | 34 |     Standard way to print out the frontend objects (eg job, host, acl, label) | 
 | 35 |     in a human-readable fashion for debugging | 
 | 36 |     """ | 
 | 37 |     result = header + '\n' | 
 | 38 |     for key in obj.hash: | 
 | 39 |         if key == 'afe' or key == 'hash': | 
 | 40 |             continue | 
 | 41 |         result += '%20s: %s\n' % (key, obj.hash[key]) | 
 | 42 |     return result | 
 | 43 |  | 
 | 44 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 45 | class RpcClient(object): | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 46 |     """ | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 47 |     Abstract RPC class for communicating with the autotest frontend | 
 | 48 |     Inherited for both TKO and AFE uses. | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 49 |  | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 50 |     All the constructors go in the afe / tko class.  | 
 | 51 |     Manipulating methods go in the object classes themselves | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 52 |     """ | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 53 |     def __init__(self, path, user, server, print_log, debug): | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 54 |         """ | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 55 |         Create a cached instance of a connection to the frontend | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 56 |  | 
 | 57 |             user: username to connect as | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 58 |             server: frontend server to connect to | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 59 |             print_log: pring a logging message to stdout on every operation | 
 | 60 |             debug: print out all RPC traffic | 
 | 61 |         """ | 
| mbligh | c31e402 | 2008-12-11 19:32:30 +0000 | [diff] [blame] | 62 |         if not user: | 
 | 63 |             user = os.environ.get('LOGNAME') | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 64 |         if not server: | 
| mbligh | 475f776 | 2009-01-30 00:34:04 +0000 | [diff] [blame] | 65 |             if 'AUTOTEST_WEB' in os.environ: | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 66 |                 server = os.environ['AUTOTEST_WEB'] | 
| mbligh | 475f776 | 2009-01-30 00:34:04 +0000 | [diff] [blame] | 67 |             else: | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 68 |                 server = GLOBAL_CONFIG.get_config_value('SERVER', 'hostname', | 
 | 69 |                                                         default=DEFAULT_SERVER) | 
 | 70 |         self.server = server | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 71 |         self.user = user | 
 | 72 |         self.print_log = print_log | 
 | 73 |         self.debug = debug | 
 | 74 |         headers = {'AUTHORIZATION' : self.user} | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 75 |         rpc_server = 'http://' + server + path | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 76 |         if debug: | 
 | 77 |             print 'SERVER: %s' % rpc_server | 
 | 78 |             print 'HEADERS: %s' % headers | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 79 |         self.proxy = rpc_client_lib.get_proxy(rpc_server, headers=headers) | 
 | 80 |  | 
 | 81 |  | 
 | 82 |     def run(self, call, **dargs): | 
 | 83 |         """ | 
 | 84 |         Make a RPC call to the AFE server | 
 | 85 |         """ | 
 | 86 |         rpc_call = getattr(self.proxy, call) | 
 | 87 |         if self.debug: | 
 | 88 |             print 'DEBUG: %s %s' % (call, dargs) | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 89 |         try: | 
 | 90 |             return utils.strip_unicode(rpc_call(**dargs)) | 
 | 91 |         except Exception: | 
 | 92 |             print 'FAILED RPC CALL: %s %s' % (call, dargs) | 
 | 93 |             raise | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 94 |  | 
 | 95 |  | 
 | 96 |     def log(self, message): | 
 | 97 |         if self.print_log: | 
 | 98 |             print message | 
 | 99 |  | 
 | 100 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 101 | class TKO(RpcClient): | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 102 |     def __init__(self, user=None, server=None, print_log=True, debug=False): | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 103 |         super(TKO, self).__init__('/new_tko/server/noauth/rpc/', user, | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 104 |                                   server, print_log, debug) | 
| mbligh | c31e402 | 2008-12-11 19:32:30 +0000 | [diff] [blame] | 105 |  | 
 | 106 |  | 
 | 107 |     def get_status_counts(self, job, **data): | 
 | 108 |         entries = self.run('get_status_counts', | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 109 |                            group_by=['hostname', 'test_name', 'reason'],  | 
| mbligh | c31e402 | 2008-12-11 19:32:30 +0000 | [diff] [blame] | 110 |                            job_tag__startswith='%s-' % job, **data) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 111 |         return [TestStatus(self, e) for e in entries['groups']] | 
| mbligh | c31e402 | 2008-12-11 19:32:30 +0000 | [diff] [blame] | 112 |  | 
 | 113 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 114 | class AFE(RpcClient): | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 115 |     def __init__(self, user=None, server=None, print_log=True, debug=False): | 
 | 116 |         super(AFE, self).__init__('/afe/server/noauth/rpc/', user, server, | 
| mbligh | c31e402 | 2008-12-11 19:32:30 +0000 | [diff] [blame] | 117 |                                   print_log, debug) | 
 | 118 |  | 
 | 119 |      | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 120 |     def host_statuses(self, live=None): | 
 | 121 |         dead_statuses = ['Dead', 'Repair Failed'] | 
 | 122 |         statuses = self.run('get_static_data')['host_statuses'] | 
 | 123 |         if live == True: | 
 | 124 |             return list(set(statuses) - set(['Dead', 'Repair Failed'])) | 
 | 125 |         if live == False: | 
 | 126 |             return dead_statuses | 
 | 127 |         else: | 
 | 128 |             return statuses | 
 | 129 |  | 
 | 130 |  | 
 | 131 |     def get_hosts(self, **dargs): | 
 | 132 |         hosts = self.run('get_hosts', **dargs) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 133 |         return [Host(self, h) for h in hosts] | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 134 |  | 
 | 135 |  | 
 | 136 |     def create_host(self, hostname, **dargs): | 
| mbligh | 54459c7 | 2009-01-21 19:26:44 +0000 | [diff] [blame] | 137 |         id = self.run('add_host', hostname=hostname, **dargs) | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 138 |         return self.get_hosts(id=id)[0] | 
 | 139 |  | 
 | 140 |  | 
 | 141 |     def get_labels(self, **dargs): | 
 | 142 |         labels = self.run('get_labels', **dargs) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 143 |         return [Label(self, l) for l in labels] | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 144 |  | 
 | 145 |  | 
 | 146 |     def create_label(self, name, **dargs): | 
| mbligh | 54459c7 | 2009-01-21 19:26:44 +0000 | [diff] [blame] | 147 |         id = self.run('add_label', name=name, **dargs) | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 148 |         return self.get_labels(id=id)[0] | 
 | 149 |  | 
 | 150 |  | 
 | 151 |     def get_acls(self, **dargs): | 
 | 152 |         acls = self.run('get_acl_groups', **dargs) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 153 |         return [Acl(self, a) for a in acls] | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 154 |  | 
 | 155 |  | 
 | 156 |     def create_acl(self, name, **dargs): | 
| mbligh | 54459c7 | 2009-01-21 19:26:44 +0000 | [diff] [blame] | 157 |         id = self.run('add_acl_group', name=name, **dargs) | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 158 |         return self.get_acls(id=id)[0] | 
 | 159 |  | 
 | 160 |  | 
| mbligh | 54459c7 | 2009-01-21 19:26:44 +0000 | [diff] [blame] | 161 |     def get_users(self, **dargs): | 
 | 162 |         users = self.run('get_users', **dargs) | 
 | 163 |         return [User(self, u) for u in users] | 
 | 164 |  | 
 | 165 |  | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 166 |     def generate_control_file(self, tests, **dargs): | 
 | 167 |         ret = self.run('generate_control_file', tests=tests, **dargs) | 
 | 168 |         return ControlFile(self, ret) | 
 | 169 |  | 
 | 170 |  | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 171 |     def get_jobs(self, summary=False, **dargs): | 
 | 172 |         if summary: | 
 | 173 |             jobs_data = self.run('get_jobs_summary', **dargs) | 
 | 174 |         else: | 
 | 175 |             jobs_data = self.run('get_jobs', **dargs) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 176 |         return [Job(self, j) for j in jobs_data] | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 177 |  | 
 | 178 |  | 
 | 179 |     def get_host_queue_entries(self, **data): | 
 | 180 |         entries = self.run('get_host_queue_entries', **data) | 
| mbligh | f9e3586 | 2009-02-26 01:03:11 +0000 | [diff] [blame^] | 181 |         job_statuses = [JobStatus(self, e) for e in entries] | 
 | 182 |         # filter job statuses that have either host or meta_host | 
 | 183 |         return [status for status in job_statuses if (status.host or | 
 | 184 |                                                       status.meta_host)] | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 185 |  | 
 | 186 |  | 
| mbligh | 4e57661 | 2008-12-22 14:56:36 +0000 | [diff] [blame] | 187 |     def create_job_by_test(self, tests, hosts, kernel=None, use_container=False, | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 188 |                            **dargs): | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 189 |         """ | 
 | 190 |         Given a test name, fetch the appropriate control file from the server | 
| mbligh | 4e57661 | 2008-12-22 14:56:36 +0000 | [diff] [blame] | 191 |         and submit it. | 
 | 192 |  | 
 | 193 |         Returns a list of job objects | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 194 |         """ | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 195 |         control_file = self.generate_control_file(tests=tests, kernel=kernel, | 
 | 196 |                                                   use_container=use_container, | 
 | 197 |                                                   do_push_packages=True) | 
 | 198 |         if control_file.is_server: | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 199 |             dargs['control_type'] = 'Server' | 
 | 200 |         else: | 
 | 201 |             dargs['control_type'] = 'Client' | 
 | 202 |         dargs['dependencies'] = dargs.get('dependencies', []) + \ | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 203 |                                 control_file.dependencies | 
 | 204 |         dargs['control_file'] = control_file.control_file | 
| mbligh | 2a89410 | 2009-02-26 00:48:58 +0000 | [diff] [blame] | 205 |         if 'synch_count' not in dargs: | 
| mbligh | b1ce3b3 | 2009-02-17 16:00:50 +0000 | [diff] [blame] | 206 |             dargs['synch_count'] = control_file.synch_count | 
| mbligh | 2a89410 | 2009-02-26 00:48:58 +0000 | [diff] [blame] | 207 |         else: | 
 | 208 |             control_file.synch_count = dargs['synch_count'] | 
| mbligh | 4e57661 | 2008-12-22 14:56:36 +0000 | [diff] [blame] | 209 |         jobs = [] | 
 | 210 |         if control_file.synch_count > 1: | 
 | 211 |             # We don't trust the scheduler to do the groupings for us. | 
 | 212 |             synch_count = control_file.synch_count | 
 | 213 |             (pairs, failures) = form_ntuples_from_machines(hosts, synch_count) | 
 | 214 |             for machines in pairs: | 
 | 215 |                 jobs.append(self.create_job(hosts=machines, **dargs)) | 
 | 216 |         else: | 
 | 217 |             jobs.append(self.create_job(hosts=hosts, **dargs)) | 
 | 218 |         return jobs | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 219 |  | 
 | 220 |  | 
 | 221 |     def create_job(self, control_file, name=' ', priority='Medium', | 
 | 222 |                 control_type='Client', **dargs): | 
 | 223 |         id = self.run('create_job', name=name, priority=priority, | 
 | 224 |                  control_file=control_file, control_type=control_type, **dargs) | 
 | 225 |         return self.get_jobs(id=id)[0] | 
 | 226 |  | 
 | 227 |  | 
| mbligh | 1f23f36 | 2008-12-22 14:46:12 +0000 | [diff] [blame] | 228 |     def run_test_suites(self, pairings, kernel, kernel_label, priority='Medium', | 
 | 229 |                         wait=True, poll_interval=5, email_from=None, | 
| mbligh | 7b31228 | 2009-01-07 16:45:43 +0000 | [diff] [blame] | 230 |                         email_to=None, timeout=168): | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 231 |         """ | 
 | 232 |         Run a list of test suites on a particular kernel. | 
 | 233 |      | 
 | 234 |         Poll for them to complete, and return whether they worked or not. | 
 | 235 |      | 
 | 236 |             pairings: list of MachineTestPairing objects to invoke | 
 | 237 |             kernel: name of the kernel to run | 
 | 238 |             kernel_label: label of the kernel to run | 
 | 239 |                                         (<kernel-version> : <config> : <date>) | 
 | 240 |             wait: boolean - wait for the results to come back? | 
 | 241 |             poll_interval: interval between polling for job results (in minutes) | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 242 |             email_from: send notification email upon completion from here | 
 | 243 |             email_from: send notification email upon completion to here | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 244 |         """ | 
 | 245 |         jobs = [] | 
 | 246 |         for pairing in pairings: | 
| mbligh | 7b31228 | 2009-01-07 16:45:43 +0000 | [diff] [blame] | 247 |             new_jobs = self.invoke_test(pairing, kernel, kernel_label, priority, | 
 | 248 |                                         timeout=timeout) | 
| mbligh | 4e57661 | 2008-12-22 14:56:36 +0000 | [diff] [blame] | 249 |             for job in new_jobs: | 
 | 250 |                 job.notified = False | 
 | 251 |             jobs += new_jobs | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 252 |             # disabled - this is just for debugging: mbligh | 
 | 253 |             # if email_from and email_to: | 
 | 254 |             #     subject = 'Testing started: %s : %s' % (job.name, job.id) | 
 | 255 |             #     utils.send_email(email_from, email_to, subject, subject) | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 256 |         if not wait: | 
 | 257 |             return | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 258 |         tko = TKO() | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 259 |         while True: | 
 | 260 |             time.sleep(60 * poll_interval) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 261 |             result = self.poll_all_jobs(tko, jobs, email_from, email_to) | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 262 |             if result is not None: | 
 | 263 |                 return result | 
 | 264 |  | 
 | 265 |  | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 266 |     def result_notify(self, job, email_from, email_to): | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 267 |         """ | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 268 |         Notify about the result of a job. Will always print, if email data | 
 | 269 |         is provided, will send email for it as well. | 
 | 270 |  | 
 | 271 |             job: job object to notify about | 
 | 272 |             email_from: send notification email upon completion from here | 
 | 273 |             email_from: send notification email upon completion to here | 
 | 274 |         """ | 
 | 275 |         if job.result == True: | 
 | 276 |             subject = 'Testing PASSED: ' | 
 | 277 |         else: | 
 | 278 |             subject = 'Testing FAILED: ' | 
 | 279 |         subject += '%s : %s\n' % (job.name, job.id) | 
 | 280 |         text = [] | 
 | 281 |         for platform in job.results_platform_map: | 
 | 282 |             for status in job.results_platform_map[platform]: | 
 | 283 |                 if status == 'Total': | 
 | 284 |                     continue | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 285 |                 for host in job.results_platform_map[platform][status]: | 
 | 286 |                     text.append('%20s %10s %10s' % (platform, status, host)) | 
 | 287 |                     if status == 'Failed': | 
 | 288 |                         for test_status in job.test_status[host].fail: | 
 | 289 |                             text.append('(%s, %s) : %s' % \ | 
 | 290 |                                         (host, test_status.test_name, | 
 | 291 |                                          test_status.reason)) | 
 | 292 |                         text.append('') | 
| mbligh | 37eceaa | 2008-12-15 22:56:37 +0000 | [diff] [blame] | 293 |  | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 294 |         base_url = 'http://' + self.server | 
| mbligh | 37eceaa | 2008-12-15 22:56:37 +0000 | [diff] [blame] | 295 |  | 
 | 296 |         params = ('columns=test', | 
 | 297 |                   'rows=machine_group', | 
 | 298 |                   "condition=tag~'%s-%%25'" % job.id, | 
 | 299 |                   'title=Report') | 
 | 300 |         query_string = '&'.join(params) | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 301 |         url = '%s/tko/compose_query.cgi?%s' % (base_url, query_string) | 
 | 302 |         text.append(url + '\n') | 
 | 303 |         url = '%s/afe/#tab_id=view_job&object_id=%s' % (base_url, job.id) | 
 | 304 |         text.append(url + '\n') | 
| mbligh | 37eceaa | 2008-12-15 22:56:37 +0000 | [diff] [blame] | 305 |  | 
 | 306 |         body = '\n'.join(text) | 
 | 307 |         print '---------------------------------------------------' | 
 | 308 |         print 'Subject: ', subject | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 309 |         print body | 
| mbligh | 37eceaa | 2008-12-15 22:56:37 +0000 | [diff] [blame] | 310 |         print '---------------------------------------------------' | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 311 |         if email_from and email_to: | 
| mbligh | 37eceaa | 2008-12-15 22:56:37 +0000 | [diff] [blame] | 312 |             print 'Sending email ...' | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 313 |             utils.send_email(email_from, email_to, subject, body) | 
 | 314 |         print | 
| mbligh | 37eceaa | 2008-12-15 22:56:37 +0000 | [diff] [blame] | 315 |  | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 316 |  | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 317 |     def print_job_result(self, job): | 
 | 318 |         """ | 
 | 319 |         Print the result of a single job. | 
 | 320 |             job: a job object | 
 | 321 |         """ | 
 | 322 |         if job.result is None: | 
 | 323 |             print 'PENDING', | 
 | 324 |         elif job.result == True: | 
 | 325 |             print 'PASSED', | 
 | 326 |         elif job.result == False: | 
 | 327 |             print 'FAILED', | 
 | 328 |         print ' %s : %s' % (job.id, job.name) | 
 | 329 |  | 
 | 330 |  | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 331 |     def poll_all_jobs(self, tko, jobs, email_from=None, email_to=None): | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 332 |         """ | 
 | 333 |         Poll all jobs in a list. | 
 | 334 |             jobs: list of job objects to poll | 
 | 335 |             email_from: send notification email upon completion from here | 
 | 336 |             email_from: send notification email upon completion to here | 
 | 337 |  | 
 | 338 |         Returns: | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 339 |             a) All complete successfully (return True) | 
 | 340 |             b) One or more has failed (return False) | 
 | 341 |             c) Cannot tell yet (return None) | 
 | 342 |         """ | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 343 |         results = [] | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 344 |         for job in jobs: | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 345 |             job.result = self.poll_job_results(tko, job) | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 346 |             results.append(job.result) | 
 | 347 |             if job.result is not None and not job.notified: | 
 | 348 |                 self.result_notify(job, email_from, email_to) | 
 | 349 |                 job.notified = True | 
 | 350 |  | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 351 |             self.print_job_result(job) | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 352 |  | 
 | 353 |         if None in results: | 
 | 354 |             return None | 
 | 355 |         elif False in results: | 
 | 356 |             return False | 
 | 357 |         else: | 
 | 358 |             return True | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 359 |  | 
 | 360 |  | 
| mbligh | 1f23f36 | 2008-12-22 14:46:12 +0000 | [diff] [blame] | 361 |     def _included_platform(self, host, platforms): | 
 | 362 |         """ | 
 | 363 |         See if host's platforms matches any of the patterns in the included | 
 | 364 |         platforms list. | 
 | 365 |         """ | 
 | 366 |         if not platforms: | 
 | 367 |             return True        # No filtering of platforms | 
 | 368 |         for platform in platforms: | 
 | 369 |             if re.search(platform, host.platform): | 
 | 370 |                 return True | 
 | 371 |         return False | 
 | 372 |  | 
 | 373 |  | 
| mbligh | 7b31228 | 2009-01-07 16:45:43 +0000 | [diff] [blame] | 374 |     def invoke_test(self, pairing, kernel, kernel_label, priority='Medium', | 
 | 375 |                     **dargs): | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 376 |         """ | 
 | 377 |         Given a pairing of a control file to a machine label, find all machines | 
 | 378 |         with that label, and submit that control file to them. | 
 | 379 |      | 
| mbligh | 4e57661 | 2008-12-22 14:56:36 +0000 | [diff] [blame] | 380 |         Returns a list of job objects | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 381 |         """ | 
 | 382 |         job_name = '%s : %s' % (pairing.machine_label, kernel_label) | 
 | 383 |         hosts = self.get_hosts(multiple_labels=[pairing.machine_label]) | 
| mbligh | 1f23f36 | 2008-12-22 14:46:12 +0000 | [diff] [blame] | 384 |         platforms = pairing.platforms | 
 | 385 |         hosts = [h for h in hosts if self._included_platform(h, platforms)] | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 386 |         host_list = [h.hostname for h in hosts if h.status != 'Repair Failed'] | 
| mbligh | 1f23f36 | 2008-12-22 14:46:12 +0000 | [diff] [blame] | 387 |         print 'HOSTS: %s' % host_list | 
| mbligh | 2a89410 | 2009-02-26 00:48:58 +0000 | [diff] [blame] | 388 |         if pairing.synch_job: | 
 | 389 |             dargs['synch_count'] = len(host_list) | 
| mbligh | 4e57661 | 2008-12-22 14:56:36 +0000 | [diff] [blame] | 390 |         new_jobs = self.create_job_by_test(name=job_name, | 
| mbligh | 7b31228 | 2009-01-07 16:45:43 +0000 | [diff] [blame] | 391 |                                            dependencies=[pairing.machine_label], | 
 | 392 |                                            tests=[pairing.control_file], | 
 | 393 |                                            priority=priority, | 
 | 394 |                                            hosts=host_list, | 
 | 395 |                                            kernel=kernel, | 
 | 396 |                                            use_container=pairing.container, | 
 | 397 |                                            **dargs) | 
| mbligh | 4e57661 | 2008-12-22 14:56:36 +0000 | [diff] [blame] | 398 |         for new_job in new_jobs: | 
 | 399 |             print 'Invoked test %s : %s' % (new_job.id, job_name) | 
 | 400 |         return new_jobs | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 401 |  | 
 | 402 |  | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 403 |     def _job_test_results(self, tko, job, debug): | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 404 |         """ | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 405 |         Retrieve test results for a job | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 406 |         """ | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 407 |         job.test_status = {} | 
 | 408 |         try: | 
 | 409 |             test_statuses = tko.get_status_counts(job=job.id) | 
 | 410 |         except Exception: | 
 | 411 |             print "Ignoring exception on poll job; RPC interface is flaky" | 
 | 412 |             traceback.print_exc() | 
 | 413 |             return | 
 | 414 |  | 
 | 415 |         for test_status in test_statuses: | 
| mbligh | 7479a18 | 2009-01-07 16:46:24 +0000 | [diff] [blame] | 416 |             # SERVER_JOB is buggy, and often gives false failures. Ignore it. | 
 | 417 |             if test_status.test_name == 'SERVER_JOB': | 
 | 418 |                 continue | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 419 |             if debug: | 
 | 420 |                 print test_status | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 421 |             hostname = test_status.hostname | 
 | 422 |             if hostname not in job.test_status: | 
 | 423 |                 job.test_status[hostname] = TestResults() | 
 | 424 |             job.test_status[hostname].add(test_status) | 
 | 425 |  | 
 | 426 |  | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 427 |     def _job_results_platform_map(self, job, debug): | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 428 |         job.results_platform_map = {} | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 429 |         try: | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 430 |             job_statuses = self.get_host_queue_entries(job=job.id) | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 431 |         except Exception: | 
 | 432 |             print "Ignoring exception on poll job; RPC interface is flaky" | 
 | 433 |             traceback.print_exc() | 
 | 434 |             return None | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 435 |  | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 436 |         platform_map = {} | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 437 |         job.job_status = {} | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 438 |         job.metahost_index = {} | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 439 |         for job_status in job_statuses: | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 440 |             if job_status.host: | 
 | 441 |                 hostname = job_status.host.hostname | 
 | 442 |             else:              # This is a metahost | 
 | 443 |                 metahost = job_status.meta_host | 
 | 444 |                 index = job.metahost_index.get(metahost, 1) | 
 | 445 |                 job.metahost_index[metahost] = index + 1 | 
 | 446 |                 hostname = '%s.%s' % (metahost, index) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 447 |             job.job_status[hostname] = job_status.status | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 448 |             status = job_status.status | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 449 |             # Skip hosts that failed verify - that's a machine failure, | 
 | 450 |             # not a job failure | 
 | 451 |             if hostname in job.test_status: | 
 | 452 |                 verify_failed = False | 
 | 453 |                 for failure in job.test_status[hostname].fail: | 
 | 454 |                     if failure.test_name == 'verify': | 
 | 455 |                         verify_failed = True | 
 | 456 |                         break | 
 | 457 |                 if verify_failed: | 
 | 458 |                     continue | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 459 |             if hostname in job.test_status and job.test_status[hostname].fail: | 
 | 460 |                 # Job status doesn't reflect failed tests, override that | 
 | 461 |                 status = 'Failed' | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 462 |             if job_status.host: | 
 | 463 |                 platform = job_status.host.platform | 
 | 464 |             else:              # This is a metahost | 
 | 465 |                 platform = job_status.meta_host | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 466 |             if platform not in platform_map: | 
 | 467 |                 platform_map[platform] = {'Total' : [hostname]} | 
 | 468 |             else: | 
 | 469 |                 platform_map[platform]['Total'].append(hostname) | 
 | 470 |             new_host_list = platform_map[platform].get(status, []) + [hostname] | 
 | 471 |             platform_map[platform][status] = new_host_list | 
| mbligh | 45ffc43 | 2008-12-09 23:35:17 +0000 | [diff] [blame] | 472 |         job.results_platform_map = platform_map | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 473 |  | 
 | 474 |  | 
 | 475 |     def poll_job_results(self, tko, job, debug=False): | 
 | 476 |         """ | 
 | 477 |         Analyse all job results by platform, return: | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 478 |      | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 479 |             False: if any platform has more than one failure | 
 | 480 |             None:  if any platform has more than one machine not yet Good. | 
 | 481 |             True:  if all platforms have at least all-but-one machines Good. | 
 | 482 |         """ | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 483 |         self._job_test_results(tko, job, debug) | 
 | 484 |         self._job_results_platform_map(job, debug) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 485 |  | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 486 |         good_platforms = [] | 
 | 487 |         bad_platforms = [] | 
 | 488 |         unknown_platforms = [] | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 489 |         platform_map = job.results_platform_map | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 490 |         for platform in platform_map: | 
 | 491 |             total = len(platform_map[platform]['Total']) | 
 | 492 |             completed = len(platform_map[platform].get('Completed', [])) | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 493 |             failed = len(platform_map[platform].get('Failed', [])) + \ | 
 | 494 |                      len(platform_map[platform].get('Aborted', [])) | 
 | 495 |             if (failed * 2 >= total) or (failed > 1): | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 496 |                 bad_platforms.append(platform) | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 497 |             elif (completed >= 1) and (completed + 1 >= total): | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 498 |                 # if all or all but one are good, call the job good. | 
 | 499 |                 good_platforms.append(platform) | 
 | 500 |             else: | 
 | 501 |                 unknown_platforms.append(platform) | 
 | 502 |             detail = [] | 
 | 503 |             for status in platform_map[platform]: | 
 | 504 |                 if status == 'Total': | 
 | 505 |                     continue | 
 | 506 |                 detail.append('%s=%s' % (status,platform_map[platform][status])) | 
 | 507 |             if debug: | 
 | 508 |                 print '%20s %d/%d %s' % (platform, completed, total,  | 
 | 509 |                                          ' '.join(detail)) | 
 | 510 |                 print | 
 | 511 |      | 
 | 512 |         if len(bad_platforms) > 0: | 
 | 513 |             if debug: | 
 | 514 |                 print 'Result bad - platforms: ' + ' '.join(bad_platforms) | 
 | 515 |             return False | 
 | 516 |         if len(unknown_platforms) > 0: | 
 | 517 |             if debug: | 
 | 518 |                 platform_list = ' '.join(unknown_platforms) | 
 | 519 |                 print 'Result unknown - platforms: ', platform_list | 
 | 520 |             return None | 
 | 521 |         if debug: | 
 | 522 |             platform_list = ' '.join(good_platforms) | 
 | 523 |             print 'Result good - all platforms passed: ', platform_list | 
 | 524 |         return True | 
 | 525 |  | 
 | 526 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 527 | class TestResults(object): | 
 | 528 |     """ | 
 | 529 |     Container class used to hold the results of the tests for a job | 
 | 530 |     """ | 
 | 531 |     def __init__(self): | 
 | 532 |         self.good = [] | 
 | 533 |         self.fail = [] | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 534 |         self.pending = [] | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 535 |  | 
 | 536 |  | 
 | 537 |     def add(self, result): | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 538 |         if result.complete_count > result.pass_count: | 
 | 539 |             self.fail.append(result) | 
 | 540 |         elif result.incomplete_count > 0: | 
 | 541 |             self.pending.append(result) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 542 |         else: | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 543 |             self.good.append(result) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 544 |  | 
 | 545 |  | 
 | 546 | class RpcObject(object): | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 547 |     """ | 
 | 548 |     Generic object used to construct python objects from rpc calls | 
 | 549 |     """ | 
 | 550 |     def __init__(self, afe, hash): | 
 | 551 |         self.afe = afe | 
 | 552 |         self.hash = hash | 
 | 553 |         self.__dict__.update(hash) | 
 | 554 |  | 
 | 555 |  | 
 | 556 |     def __str__(self): | 
 | 557 |         return dump_object(self.__repr__(), self) | 
 | 558 |  | 
 | 559 |  | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 560 | class ControlFile(RpcObject): | 
 | 561 |     """ | 
 | 562 |     AFE control file object | 
 | 563 |  | 
 | 564 |     Fields: synch_count, dependencies, control_file, is_server | 
 | 565 |     """ | 
 | 566 |     def __repr__(self): | 
 | 567 |         return 'CONTROL FILE: %s' % self.control_file | 
 | 568 |  | 
 | 569 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 570 | class Label(RpcObject): | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 571 |     """ | 
 | 572 |     AFE label object | 
 | 573 |  | 
 | 574 |     Fields: | 
 | 575 |         name, invalid, platform, kernel_config, id, only_if_needed | 
 | 576 |     """ | 
 | 577 |     def __repr__(self): | 
 | 578 |         return 'LABEL: %s' % self.name | 
 | 579 |  | 
 | 580 |  | 
 | 581 |     def add_hosts(self, hosts): | 
 | 582 |         return self.afe.run('label_add_hosts', self.id, hosts) | 
 | 583 |  | 
 | 584 |  | 
 | 585 |     def remove_hosts(self, hosts): | 
 | 586 |         return self.afe.run('label_remove_hosts', self.id, hosts) | 
 | 587 |  | 
 | 588 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 589 | class Acl(RpcObject): | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 590 |     """ | 
 | 591 |     AFE acl object | 
 | 592 |  | 
 | 593 |     Fields: | 
 | 594 |         users, hosts, description, name, id | 
 | 595 |     """ | 
 | 596 |     def __repr__(self): | 
 | 597 |         return 'ACL: %s' % self.name | 
 | 598 |  | 
 | 599 |  | 
 | 600 |     def add_hosts(self, hosts): | 
 | 601 |         self.afe.log('Adding hosts %s to ACL %s' % (hosts, self.name)) | 
 | 602 |         return self.afe.run('acl_group_add_hosts', self.id, hosts) | 
 | 603 |  | 
 | 604 |  | 
 | 605 |     def remove_hosts(self, hosts): | 
 | 606 |         self.afe.log('Removing hosts %s from ACL %s' % (hosts, self.name)) | 
 | 607 |         return self.afe.run('acl_group_remove_hosts', self.id, hosts) | 
 | 608 |  | 
 | 609 |  | 
| mbligh | 54459c7 | 2009-01-21 19:26:44 +0000 | [diff] [blame] | 610 |     def add_users(self, users): | 
 | 611 |         self.afe.log('Adding users %s to ACL %s' % (users, self.name)) | 
 | 612 |         return self.afe.run('acl_group_add_users', id=self.name, users=users) | 
 | 613 |  | 
 | 614 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 615 | class Job(RpcObject): | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 616 |     """ | 
 | 617 |     AFE job object | 
 | 618 |  | 
 | 619 |     Fields: | 
 | 620 |         name, control_file, control_type, synch_count, reboot_before, | 
 | 621 |         run_verify, priority, email_list, created_on, dependencies, | 
 | 622 |         timeout, owner, reboot_after, id | 
 | 623 |     """ | 
 | 624 |     def __repr__(self): | 
 | 625 |         return 'JOB: %s' % self.id | 
 | 626 |  | 
 | 627 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 628 | class JobStatus(RpcObject): | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 629 |     """ | 
 | 630 |     AFE job_status object | 
 | 631 |  | 
 | 632 |     Fields: | 
 | 633 |         status, complete, deleted, meta_host, host, active, execution_subdir, id | 
 | 634 |     """ | 
 | 635 |     def __init__(self, afe, hash): | 
 | 636 |         # This should call super | 
 | 637 |         self.afe = afe | 
 | 638 |         self.hash = hash | 
 | 639 |         self.__dict__.update(hash) | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 640 |         self.job = Job(afe, self.job) | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 641 |         if self.host: | 
| mbligh | f9e3586 | 2009-02-26 01:03:11 +0000 | [diff] [blame^] | 642 |             # get list of hosts from AFE; if a host is not present in autotest | 
 | 643 |             # anymore, this returns an empty list. | 
 | 644 |             afe_hosts = afe.get_hosts(hostname=self.host['hostname']) | 
 | 645 |             if len(afe_hosts): | 
 | 646 |                 # host present, assign it! | 
 | 647 |                 self.host = afe_hosts[0] | 
 | 648 |             else: | 
 | 649 |                 # AFE does not contain info anymore, set host to None | 
 | 650 |                 self.host = None | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 651 |  | 
 | 652 |  | 
 | 653 |     def __repr__(self): | 
| mbligh | 451ede1 | 2009-02-12 21:54:03 +0000 | [diff] [blame] | 654 |         if self.host and self.host.hostname: | 
 | 655 |             hostname = self.host.hostname | 
 | 656 |         else: | 
 | 657 |             hostname = 'None' | 
 | 658 |         return 'JOB STATUS: %s-%s' % (self.job.id, hostname) | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 659 |  | 
 | 660 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 661 | class Host(RpcObject): | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 662 |     """ | 
 | 663 |     AFE host object | 
 | 664 |  | 
 | 665 |     Fields: | 
 | 666 |         status, lock_time, locked_by, locked, hostname, invalid, | 
 | 667 |         synch_id, labels, platform, protection, dirty, id | 
 | 668 |     """ | 
 | 669 |     def __repr__(self): | 
 | 670 |         return 'HOST OBJECT: %s' % self.hostname | 
 | 671 |  | 
 | 672 |  | 
 | 673 |     def show(self): | 
 | 674 |         labels = list(set(self.labels) - set([self.platform])) | 
 | 675 |         print '%-6s %-7s %-7s %-16s %s' % (self.hostname, self.status, | 
 | 676 |                                            self.locked, self.platform, | 
 | 677 |                                            ', '.join(labels)) | 
 | 678 |  | 
 | 679 |  | 
| mbligh | 54459c7 | 2009-01-21 19:26:44 +0000 | [diff] [blame] | 680 |     def delete(self): | 
 | 681 |         return self.afe.run('delete_host', id=self.id) | 
 | 682 |  | 
 | 683 |  | 
| mbligh | 6463c4b | 2009-01-30 00:33:37 +0000 | [diff] [blame] | 684 |     def modify(self, **dargs): | 
 | 685 |         return self.afe.run('modify_host', id=self.id, **dargs) | 
 | 686 |  | 
 | 687 |  | 
| mbligh | 6764715 | 2008-11-19 00:18:14 +0000 | [diff] [blame] | 688 |     def get_acls(self): | 
 | 689 |         return self.afe.get_acls(hosts__hostname=self.hostname) | 
 | 690 |  | 
 | 691 |  | 
 | 692 |     def add_acl(self, acl_name): | 
 | 693 |         self.afe.log('Adding ACL %s to host %s' % (acl_name, self.hostname)) | 
 | 694 |         return self.afe.run('acl_group_add_hosts', id=acl_name, | 
 | 695 |                             hosts=[self.hostname]) | 
 | 696 |  | 
 | 697 |  | 
 | 698 |     def remove_acl(self, acl_name): | 
 | 699 |         self.afe.log('Removing ACL %s from host %s' % (acl_name, self.hostname)) | 
 | 700 |         return self.afe.run('acl_group_remove_hosts', id=acl_name, | 
 | 701 |                             hosts=[self.hostname]) | 
 | 702 |  | 
 | 703 |  | 
 | 704 |     def get_labels(self): | 
 | 705 |         return self.afe.get_labels(host__hostname__in=[self.hostname]) | 
 | 706 |  | 
 | 707 |  | 
 | 708 |     def add_labels(self, labels): | 
 | 709 |         self.afe.log('Adding labels %s to host %s' % (labels, self.hostname)) | 
 | 710 |         return self.afe.run('host_add_labels', id=self.id, labels=labels) | 
 | 711 |  | 
 | 712 |  | 
 | 713 |     def remove_labels(self, labels): | 
 | 714 |         self.afe.log('Removing labels %s from host %s' % (labels,self.hostname)) | 
 | 715 |         return self.afe.run('host_remove_labels', id=self.id, labels=labels) | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 716 |  | 
 | 717 |  | 
| mbligh | 54459c7 | 2009-01-21 19:26:44 +0000 | [diff] [blame] | 718 | class User(RpcObject): | 
 | 719 |     def __repr__(self): | 
 | 720 |         return 'USER: %s' % self.login | 
 | 721 |  | 
 | 722 |  | 
| mbligh | 5280e3b | 2008-12-22 14:39:28 +0000 | [diff] [blame] | 723 | class TestStatus(RpcObject): | 
| mbligh | c31e402 | 2008-12-11 19:32:30 +0000 | [diff] [blame] | 724 |     """ | 
 | 725 |     TKO test status object | 
 | 726 |  | 
 | 727 |     Fields: | 
 | 728 |         test_idx, hostname, testname, id | 
 | 729 |         complete_count, incomplete_count, group_count, pass_count | 
 | 730 |     """ | 
 | 731 |     def __repr__(self): | 
 | 732 |         return 'TEST STATUS: %s' % self.id | 
 | 733 |  | 
 | 734 |  | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 735 | class MachineTestPairing(object): | 
 | 736 |     """ | 
 | 737 |     Object representing the pairing of a machine label with a control file | 
| mbligh | 1f23f36 | 2008-12-22 14:46:12 +0000 | [diff] [blame] | 738 |  | 
 | 739 |     machine_label: use machines from this label | 
 | 740 |     control_file: use this control file (by name in the frontend) | 
 | 741 |     platforms: list of rexeps to filter platforms by. [] => no filtering | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 742 |     """ | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 743 |     def __init__(self, machine_label, control_file, platforms=[], | 
| mbligh | b1ce3b3 | 2009-02-17 16:00:50 +0000 | [diff] [blame] | 744 |                  container=False, synch_job=False): | 
| mbligh | 5b61838 | 2008-12-03 15:24:01 +0000 | [diff] [blame] | 745 |         self.machine_label = machine_label | 
 | 746 |         self.control_file = control_file | 
| mbligh | 1f23f36 | 2008-12-22 14:46:12 +0000 | [diff] [blame] | 747 |         self.platforms = platforms | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 748 |         self.container = container | 
| mbligh | 2a89410 | 2009-02-26 00:48:58 +0000 | [diff] [blame] | 749 |         self.synch_job = synch_job | 
| mbligh | 1354c9d | 2008-12-22 14:56:13 +0000 | [diff] [blame] | 750 |  | 
 | 751 |  | 
 | 752 |     def __repr__(self): | 
 | 753 |         return '%s %s %s %s' % (self.machine_label, self.control_file, | 
 | 754 |                                 self.platforms, self.container) |