blob: 5f0fcf7e1cb560b8b39590bcf7de74a10127f0f6 [file] [log] [blame]
jamesren7a522042010-06-10 22:53:55 +00001#!/usr/bin/python
2
3"""A script that provides convertion between models.job and a protocol
4buffer object.
5
6This script contains only one class that takes an job instance and
7convert it into a protocol buffer object. The class will also be
8responsible for serializing the job instance via protocol buffers.
9
10"""
11
12# import python libraries
13import os
14import datetime
15import time
16import random
17
18# import autotest libraries
19from autotest_lib.tko import models
20from autotest_lib.tko import tko_pb2
21
22__author__ = 'darrenkuo@google.com (Darren Kuo)'
23
24mktime = time.mktime
25datetime = datetime.datetime
26
27class JobSerializer(object):
28 """A class that takes a job object of the tko module and package
29 it with a protocol buffer.
30
31 This class will take a model.job object as input and create a
32 protocol buffer to include all the content of the job object. This
33 protocol buffer object will be serialized into a binary file.
34 """
35
36 def __init__(self):
37
38 self.job_type_dict = {'dir':str, 'tests':list, 'user':str,
39 'label':str, 'machine':str,
40 'queued_time':datetime,
41 'started_time':datetime,
42 'finished_time':datetime,
43 'machine_owner':str,
44 'machine_group':str, 'aborted_by':str,
45 'aborted_on':datetime,
46 'keyval_dict':dict}
47
48 self.test_type_dict = {'subdir':str, 'testname':str,
49 'status':str, 'reason':str,
50 'kernel':models.kernel, 'machine':str,
51 'started_time':datetime,
52 'finished_time':datetime,
53 'iterations':list, 'attributes':dict,
54 'labels':list}
55
56 self.kernel_type_dict = {'base':str, 'patches':list,
57 'kernel_hash':str }
58
59 self.iteration_type_dict = {'index':int, 'attr_keyval':dict,
60 'perf_keyval':dict }
61
62 self.patch_type_dict = {'spec':str, 'reference':str,
63 'hash':int}
64
65
66 def deserialize_from_binary(self, infile):
67 """Takes in a binary file name and returns a tko job object.
68
69 The method first deserialize the binary into a protocol buffer
70 job object and then converts the job object into a tko job
71 object.
72
73 @param
74 infile: the name of the binary file that will be deserialized.
75
76 @return a tko job that is represented by the binary file will
77 be returned.
78 """
79
jamesrenfe229d42010-06-10 23:55:06 +000080 job_pb = tko_pb2.Job()
jamesren7a522042010-06-10 22:53:55 +000081
82 binary = open(infile, 'r')
83 try:
84 job_pb.ParseFromString(binary.read())
85 finally:
86 binary.close()
87
88 return self.get_tko_job(job_pb)
89
90
91 def serialize_to_binary(self, the_job, binaryfilename):
92 """Serializes the tko job object into a binary by using a
93 protocol buffer.
94
95 The method takes a tko job object and constructs a protocol
96 buffer job object. Then invokes the native serializing
97 function on the object to get a binary string. The string is
98 then written to outfile.
99
100 Precondition: Assumes that all the information about the job
101 is already in the job object. Any fields that is None will be
102 provided a default value.
103
104 @param
105 the_job: the tko job object that will be serialized.
106 binaryfilename: the name of the file that will be written to
107
108 @return the filename of the file that contains the
109 binary of the serialized object.
110 """
111
jamesrenfe229d42010-06-10 23:55:06 +0000112 job_pb = tko_pb2.Job()
jamesren7a522042010-06-10 22:53:55 +0000113 self.set_pb_job(the_job, job_pb)
114
115 out = open(binaryfilename, 'wb')
116 try:
117 out.write(job_pb.SerializeToString())
118 finally:
119 out.close()
120
121
122 # getter setter methods
123 def get_tko_job(self, job):
124 """Creates a a new tko job object from the pb job object.
125
126 Uses getter methods on the pb objects to extract all the
127 attributes and finally constructs a tko job object using the
128 models.job constructor.
129
130 @param
131 job: a pb job where data is being extracted from.
132
133 @return a tko job object.
134 """
135
136 fields_dict = self.get_trivial_attr(job, self.job_type_dict)
137
138 fields_dict['tests'] = [self.get_tko_test(test) for test in job.tests]
139
140 fields_dict['keyval_dict'] = dict((keyval.name, keyval.value)
141 for keyval in job.keyval_dict)
142
143 newjob = models.job(fields_dict['dir'], fields_dict['user'],
144 fields_dict['label'],
145 fields_dict['machine'],
146 fields_dict['queued_time'],
147 fields_dict['started_time'],
148 fields_dict['finished_time'],
149 fields_dict['machine_owner'],
150 fields_dict['machine_group'],
151 fields_dict['aborted_by'],
152 fields_dict['aborted_on'],
153 fields_dict['keyval_dict'])
154
155 newjob.tests.extend(fields_dict['tests'])
156
157 return newjob
158
159
160 def set_pb_job(self, tko_job, pb_job):
161 """Set the fields for the new job object.
162
163 Method takes in a tko job and an empty protocol buffer job
164 object. Then safely sets all the appropriate field by first
165 testing if the value in the original object is None.
166
167 @param
168 tko_job: a tko job instance that will have it's values
169 transfered to the new job
170 pb_job: a new instance of the job class provided in the
171 protocol buffer.
172
173 """
174
175 self.set_trivial_attr(tko_job, pb_job, self.job_type_dict)
176
177 for test in tko_job.tests:
178 newtest = pb_job.tests.add()
179 self.set_pb_test(test, newtest)
180
181 for key, val in tko_job.keyval_dict.iteritems():
182 newkeyval = pb_job.keyval_dict.add()
183 newkeyval.name = key
184 newkeyval.value = str(val)
185
186
187 def get_tko_test(self, test):
188 fields_dict = self.get_trivial_attr(test, self.test_type_dict)
189
190 fields_dict['kernel'] = self.get_tko_kernel(test.kernel)
191
192 fields_dict['iterations'] = [self.get_tko_iteration(iteration)
193 for iteration in test.iterations]
194
195 fields_dict['attributes'] = dict((keyval.name, keyval.value)
196 for keyval in test.attributes)
197
198 fields_dict['labels'] = list(test.labels)
199
200 return models.test(fields_dict['subdir'],
201 fields_dict['testname'],
202 fields_dict['status'],
203 fields_dict['reason'],
204 fields_dict['kernel'],
205 fields_dict['machine'],
206 fields_dict['started_time'],
207 fields_dict['finished_time'],
208 fields_dict['iterations'],
209 fields_dict['attributes'],
210 fields_dict['labels'])
211
212
213 def set_pb_test(self, tko_test, pb_test):
214 """Sets the various fields of test object of the tko protocol.
215
216 Method takes a tko test and a new test of the protocol buffer and
217 transfers the values in the tko test to the new test.
218
219 @param
220 tko_test: a tko test instance.
221 pb_test: an empty protocol buffer test instance.
222
223 """
224
225 self.set_trivial_attr(tko_test, pb_test, self.test_type_dict)
226
227 self.set_pb_kernel(tko_test.kernel, pb_test.kernel)
228
229 for current_iteration in tko_test.iterations:
230 pb_iteration = pb_test.iterations.add()
231 self.set_pb_iteration(current_iteration, pb_iteration)
232
233 for key, val in tko_test.attributes.iteritems():
234 newkeyval = pb_test.attributes.add()
235 newkeyval.name = key
236 newkeyval.value = str(val)
237
238 for current_label in tko_test.labels:
239 pb_test.labels.append(current_label)
240
241
242 def get_tko_kernel(self, kernel):
243 """Constructs a new tko kernel object from a pb kernel object.
244
245 Uses all the getter methods on the pb kernel object to extract
246 the attributes and constructs a new tko kernel object using
247 the model.kernel constructor.
248
249 @param
250 kernel: a pb kernel object where data will be extracted.
251
252 @return a new tko kernel object.
253 """
254
255 fields_dict = self.get_trivial_attr(kernel, self.kernel_type_dict)
256
257 fields_dict['patches'] = [self.get_tko_patch(patch) for patch
258 in kernel.patches]
259
260 return models.kernel(fields_dict['base'], fields_dict['patches'],
261 fields_dict['kernel_hash'])
262
263
264 def set_pb_kernel(self, tko_kernel, pb_kernel):
265 """Set a specific kernel of a test.
266
267 Takes the same form of all the other setting methods. It
268 seperates the string variables from the int variables and set
269 them safely.
270
271 @param
272 tko_kernel: a tko kernel.
273 pb_kernel: an empty protocol buffer kernel.
274
275 """
276
277 self.set_trivial_attr(tko_kernel, pb_kernel, self.kernel_type_dict)
278
279 for patch in tko_kernel.patches:
280 newpatch = pb_kernel.patches.add()
281 self.set_pb_patch(patch, newpatch)
282
283
284 def get_tko_patch(self, patch):
285 """Constructs a new tko patch object from the provided pb
286 patch instance.
287
288 Extracts data from the provided pb patch and creates a new tko
289 patch using the models.patch constructor.
290
291 @param
292 patch: a pb patch that contains the data for the new tko patch
293
294 @return a new tko patch with the same data as in the pb patch.
295 """
296
297 fields_dict = self.get_trivial_attr(patch, self.patch_type_dict)
298 return models.patch(fields_dict['spec'],
299 fields_dict['reference'],
300 fields_dict['hash'])
301
302
303 def set_pb_patch(self, tko_patch, pb_patch):
304 """Set a specific patch of a kernel.
305
306 Takes the same form of all the other setting methods. It
307 seperates the string variables from the int variables and set
308 them safely.
309
310 @param
311 tko_patch: a tko patch.
312 pb_patch: an empty protocol buffer patch.
313
314 """
315
316 self.set_trivial_attr(tko_patch, pb_patch, self.patch_type_dict)
317
318
319 def get_tko_iteration(self, iteration):
320 """Creates a new tko iteration with the data in the provided
321 pb iteration.
322
323 Uses the data in the pb iteration and the models.iteration
324 constructor to create a new tko iterations
325
326 @param
327 iteration: a pb iteration instance
328
329 @return a tko iteration instance with the same data.
330 """
331
332 fields_dict = self.get_trivial_attr(iteration,
333 self.iteration_type_dict)
334
335 fields_dict['attr_keyval'] = dict((keyval.name, keyval.value)
336 for keyval in iteration.attr_keyval)
337
338 fields_dict['perf_keyval'] = dict((keyval.name, keyval.value)
339 for keyval in iteration.perf_keyval)
340
341 return models.iteration(fields_dict['index'],
342 fields_dict['attr_keyval'],
343 fields_dict['perf_keyval'])
344
345
346 def set_pb_iteration(self, tko_iteration, pb_iteration):
347 """Sets all fields for a particular iteration.
348
349 Takes same form as all the other setting methods. Sets int,
350 str and datetime variables safely.
351
352 @param
353 tko_iteration: a tko test iteration.
354 pb_iteration: an empty pb test iteration.
355
356 """
357
358 self.set_trivial_attr(tko_iteration, pb_iteration,
359 self.iteration_type_dict)
360
361 for key, val in tko_iteration.attr_keyval.iteritems():
362 newkeyval = pb_iteration.attr_keyval.add()
363 newkeyval.name = key
364 newkeyval.value = str(val)
365
366 for key, val in tko_iteration.perf_keyval.iteritems():
367 newkeyval = pb_iteration.perf_keyval.add()
368 newkeyval.name = key
369 newkeyval.value = str(val)
370
371
372 def get_trivial_attr(self, obj, objdict):
373 """Get all trivial attributes from the object.
374
375 This function is used to extract attributes from a pb job. The
376 dictionary specifies the types of each attribute in each tko
377 class.
378
379 @param
380 obj: the pb object that is being extracted.
381 objdict: the dict that specifies the type.
382
383 @return a dict of each attr name and it's corresponding value.
384 """
385
386 resultdict = {}
387 for field, field_type in objdict.items():
388 value = getattr(obj, field)
389 if field_type in (str, int, long):
390 resultdict[field] = field_type(value)
391 elif field_type == datetime:
392 resultdict[field] = (
393 datetime.fromtimestamp(value/1000.0))
394
395 return resultdict
396
397
398 def set_trivial_attr(self, tko_obj, pb_obj, objdict):
399 """Sets all the easy attributes appropriately according to the
400 type.
401
402 This function is used to set all the trivial attributes
403 provided by objdict, the dictionary that specifies the types
404 of each attribute in each tko class.
405
406 @param
407 tko_obj: the original object that has the data being copied.
408 pb_obj: the new pb object that is being copied into.
409 objdict: specifies the type of each attribute in the class we
410 are working with.
411
412 """
413 for attr, attr_type in objdict.iteritems():
414 if attr_type == datetime:
415 t = getattr(tko_obj, attr)
416 if not t:
417 self.set_attr_safely(pb_obj, attr, t, int)
418 else:
419 t = mktime(t.timetuple()) + 1e-6 * t.microsecond
420 setattr(pb_obj, attr, long(t*1000))
421 else:
422 value = getattr(tko_obj, attr)
423 self.set_attr_safely(pb_obj, attr, value, attr_type)
424
425
426 def set_attr_safely(self, var, attr, value, vartype):
427 """Sets a particular attribute of var if the provided value is
428 not None.
429
430 Checks if value is None. If not, set the attribute of the var
431 to be the default value. This is necessary for the special
432 required fields of the protocol buffer.
433
434 @param
435 var: the variable of which one of the attribute is being set.
436 attr: the attribute that is being set.
437 value: the value that is being checked
438 vartype: the expected type of the attr
439
440 """
441
442 supported_types = [int, long, str]
443 if vartype in supported_types:
444 if value is None:
445 value = vartype()
446 else:
447 assert isinstance(value, vartype), (
448 'Unexpected type %s for attr %s, should be %s' %
449 (type(value), attr, vartype))
450
451 setattr(var, attr, value)