1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 import os
29 import sys
30 import subprocess
31
32 import ansible.runner
33 import optparse
34 from operator import methodcaller
35 import time
36 import socket
37 import traceback
38
39
40 mockchain='/usr/bin/mockchain'
41
42 rsync='/usr/bin/rsync'
43
44 DEF_REMOTE_BASEDIR='/var/tmp'
45 DEF_TIMEOUT=3600
46 DEF_REPOS = []
47 DEF_CHROOT= None
48 DEF_USER = 'mockbuilder'
49 DEF_DESTDIR = os.getcwd()
52 '''Optparser which sorts the options by opt before outputting --help'''
56
59 if os.path.exists(path + '/repodata/repomd.xml'):
60 comm = ['/usr/bin/createrepo', '--database', '--update', path]
61 else:
62 comm = ['/usr/bin/createrepo', '--database', path]
63 cmd = subprocess.Popen(comm,
64 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
65 out, err = cmd.communicate()
66 return cmd.returncode, out, err
67
69 lst = []
70 f = open(fn, 'r')
71 for line in f.readlines():
72 line = line.replace('\n','')
73 line = line.strip()
74 if line.startswith('#'):
75 continue
76 lst.append(line)
77
78 return lst
79
81 if lf:
82 now = time.time()
83 try:
84 open(lf, 'a').write(str(now) + ':' + msg + '\n')
85 except (IOError, OSError), e:
86 print 'Could not write to logfile %s - %s' % (lf, str(e))
87 print msg
88
90 if hostname in results['dark']:
91 return results['dark'][hostname]
92 if hostname in results['contacted']:
93 return results['contacted'][hostname]
94
95 return {}
96
98 ans_conn = ansible.runner.Runner(remote_user=username,
99 host_list=hostname + ',', pattern=hostname, forks=1,
100 timeout=timeout)
101 return ans_conn
102
103 -def check_for_ans_error(results, hostname, err_codes=[], success_codes=[0],
104 return_on_error=['stdout', 'stderr']):
105
106
107
108
109 err_results = {}
110
111 if 'dark' in results and hostname in results['dark']:
112 err_results['msg'] = "Error: Could not contact/connect to %s." % hostname
113 return (True, err_results)
114
115 error = False
116
117 if err_codes or success_codes:
118 if hostname in results['contacted']:
119 if 'rc' in results['contacted'][hostname]:
120 rc = int(results['contacted'][hostname]['rc'])
121 err_results['rc'] = rc
122
123 if rc in err_codes:
124 error = True
125 err_results['msg'] = 'rc %s matched err_codes' % rc
126 elif rc not in success_codes:
127 error = True
128 err_results['msg'] = 'rc %s not in success_codes' % rc
129 elif 'failed' in results['contacted'][hostname] and results['contacted'][hostname]['failed']:
130 error = True
131 err_results['msg'] = 'results included failed as true'
132
133 if error:
134 for item in return_on_error:
135 if item in results['contacted'][hostname]:
136 err_results[item] = results['contacted'][hostname][item]
137
138 return error, err_results
139
148
151
154 self.quiet = kwargs.get('quiet', False)
155 self.logfn = kwargs.get('logfn', None)
156
159
162
165
168
170 self.log("Error: %s" % msg)
171
172 - def log(self, msg):
173 if not self.quiet:
174 print msg
175
179
181 msg = "Start build: %s" % pkg
182 self.log(msg)
183
184
186 msg = "End Build: %s" % pkg
187 self.log(msg)
188
190 msg = "Start retrieve results for: %s" % pkg
191 self.log(msg)
192
194 msg = "End retrieve results for: %s" % pkg
195 self.log(msg)
196
198 self.log("Error: %s" % msg)
199
200 - def log(self, msg):
201 if self.logfn:
202 now = time.time()
203 try:
204 open(self.logfn, 'a').write(str(now) + ':' + msg + '\n')
205 except (IOError, OSError), e:
206 print >>sys.stderr, 'Could not write to logfile %s - %s' % (self.logfn, str(e))
207 if not self.quiet:
208 print msg
209
211 - def __init__(self, hostname, username, timeout, mockremote):
224
225 @property
227 return self.tempdir + '/build/'
228
229 @property
231 if self.mockremote.remote_tempdir:
232 return self.mockremote.remote_tempdir
233
234 if self._tempdir:
235 return self._tempdir
236
237 cmd='/bin/mktemp -d %s/%s-XXXXX' % (self.mockremote.remote_basedir, 'mockremote')
238 self.conn.module_name="shell"
239 self.conn.module_args = str(cmd)
240 results = self.conn.run()
241 tempdir = None
242 for hn, resdict in results['contacted'].items():
243 tempdir = resdict['stdout']
244
245
246 if not tempdir:
247 raise BuilderError('Could not make tmpdir on %s' % self.hostname)
248
249 cmd = "/bin/chmod 755 %s" % tempdir
250 self.conn.module_args = str(cmd)
251 self.conn.run()
252 self._tempdir = tempdir
253
254 return self._tempdir
255
256 @tempdir.setter
258 self._tempdir = value
259
261
262
263 s_pkg = os.path.basename(pkg)
264 pdn = s_pkg.replace('.src.rpm', '')
265 remote_pkg_dir = self.remote_build_dir + '/results/' + self.chroot + '/' + pdn
266 return remote_pkg_dir
267
269
270
271
272
273
274
275
276 success = False
277
278
279 dest = None
280 if os.path.exists(pkg):
281 dest = self.tempdir + '/' + os.path.basename(pkg)
282 self.conn.module_name="copy"
283 margs = 'src=%s dest=%s' % (pkg, dest)
284 self.conn.module_args = str(margs)
285 self.mockremote.callback.log("Sending %s to %s to build" % (os.path.basename(pkg), self.hostname))
286
287
288 self.conn.run()
289 else:
290 dest = pkg
291
292
293 buildcmd = '%s -r %s -l %s ' % (mockchain, self.chroot, self.remote_build_dir)
294 for r in self.repos:
295 buildcmd += '-a %s ' % r
296
297 buildcmd += dest
298
299
300
301
302 self.mockremote.callback.log('executing: %r' % buildcmd)
303 self.conn.module_name="shell"
304 self.conn.module_args = str(buildcmd)
305 results = self.conn.run()
306
307 is_err, err_results = check_for_ans_error(results, self.hostname, success_codes=[0],
308 return_on_error=['stdout', 'stderr'])
309 if is_err:
310 return success, err_results.get('stdout', ''), err_results.get('stderr', '')
311
312
313 myresults = get_ans_results(results, self.hostname)
314 out = myresults.get('stdout', '')
315 err = myresults.get('stderr', '')
316
317 successfile = self._get_remote_pkg_dir(pkg) + '/success'
318 testcmd = '/usr/bin/test -f %s' % successfile
319 self.conn.module_args = str(testcmd)
320 results = self.conn.run()
321 is_err, err_results = check_for_ans_error(results, self.hostname, success_codes=[0])
322 if not is_err:
323 success = True
324
325 return success, out, err
326
328
329
330
331 success = False
332 rpd = self._get_remote_pkg_dir(pkg)
333 destdir = "'" + destdir.replace("'", "'\\''") + "'"
334
335 remote_src = '%s@%s:%s' % (self.username, self.hostname, rpd)
336 ssh_opts = "'ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no'"
337 command = "%s -avH -e %s %s %s/" % (rsync, ssh_opts, remote_src, destdir)
338 cmd = subprocess.Popen(command, shell=True,
339 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
340
341
342 out, err = cmd.communicate()
343 if cmd.returncode:
344 success = False
345 else:
346 success = True
347
348 return success, out, err
349
351
352
353
354
355 if self.checked:
356 return True, []
357
358 errors = []
359
360 try:
361 socket.gethostbyname(self.hostname)
362 except socket.gaierror:
363 raise BuilderError('%s could not be resolved' % self.hostname)
364
365
366
367 ans = ansible.runner.Runner(host_list=self.hostname + ',', pattern='*',
368 remote_user=self.username, forks=1, timeout=20)
369 ans.module_name = "shell"
370 ans.module_args = str("/bin/rpm -q mock rsync")
371 res = ans.run()
372
373 is_err, err_results = check_for_ans_error(res, self.hostname, success_codes=[0])
374 if is_err:
375 if 'rc' in err_results:
376 errors.append('Warning: %s does not have mock or rsync installed' % self.hostname)
377 else:
378 errors.append(err_results['msg'])
379
380
381
382 ans.module_name = "shell"
383 ans.module_args = str("/usr/bin/test -f %s && /usr/bin/test -f /etc/mock/%s.cfg" % (mockchain, self.chroot))
384 res = ans.run()
385
386 is_err, err_results = check_for_ans_error(res, self.hostname, success_codes=[0])
387 if is_err:
388 if 'rc' in err_results:
389 errors.append('Warning: %s lacks mockchain or the chroot %s' % (self.hostname, self.chroot))
390 else:
391 errors.append(err_results['msg'])
392
393 if not errors:
394 self.checked = True
395 else:
396 msg = '\n'.join(errors)
397 raise BuilderError(msg)
398
401 - def __init__(self, builder=None, user=DEF_USER, timeout=DEF_TIMEOUT,
402 destdir=DEF_DESTDIR, chroot=DEF_CHROOT, cont=False, recurse=False,
403 repos=DEF_REPOS, callback=None,
404 remote_basedir=DEF_REMOTE_BASEDIR, remote_tempdir=None):
405
406 self.destdir = destdir
407 self.chroot = chroot
408 self.repos = repos
409 self.cont = cont
410 self.recurse = recurse
411 self.callback = callback
412 self.remote_basedir = remote_basedir
413 self.remote_tempdir = remote_tempdir
414
415 if not self.callback:
416 self.callback = DefaultCallBack()
417
418 self.callback.log("Setting up builder: %s" % builder)
419 self.builder = Builder(builder, user, timeout, self)
420
421 if not self.chroot:
422 raise MockRemoteError("No chroot specified!")
423
424
425 self.failed = []
426 self.finished = []
427 self.pkg_list = []
428
429
431 s_pkg = os.path.basename(pkg)
432 pdn = s_pkg.replace('.src.rpm', '')
433 resdir = '%s/%s/%s' % (self.destdir, self.chroot, pdn)
434 resdir = os.path.normpath(resdir)
435 return resdir
436
438
439 if not pkgs:
440 pkgs = self.pkg_list
441
442 built_pkgs = []
443 downloaded_pkgs = {}
444
445 try_again = True
446 to_be_built = pkgs
447 while try_again:
448 self.failed = []
449 just_built = []
450 for pkg in to_be_built:
451 if pkg in just_built:
452 self.callback.log("skipping duplicate pkg in this list: %s" % pkg)
453 continue
454 else:
455 just_built.append(pkg)
456
457 p_path = self._get_pkg_destpath(pkg)
458
459
460 if os.path.exists(p_path):
461 if os.path.exists(p_path + '/success'):
462 self.callback.log("Skipping already built pkg %s" % os.path.basename(pkg))
463 continue
464
465
466 elif os.path.exists(p_path + '/fail'):
467 os.unlink(p_path + '/fail')
468
469
470
471 self.callback.start_build(pkg)
472 b_status, b_out, b_err = self.builder.build(pkg)
473 self.callback.end_build(pkg)
474
475
476 self.callback.start_download(pkg)
477
478
479 d_ret, d_out, d_err = self.builder.download(pkg, self.destdir + '/' + self.chroot)
480 if not d_ret:
481 msg = "Failure to download %s: %s" % (pkg, d_out + d_err)
482 if not self.cont:
483 raise MockRemoteError, msg
484 self.callback.error(msg)
485
486 self.callback.end_download(pkg)
487
488 if not os.path.exists(self.destdir + '/' + self.chroot):
489 os.makedirs(self.destdir + '/' + self.chroot)
490 r_log = open(self.destdir + '/' + self.chroot + '/mockchain.log', 'a')
491 r_log.write('\n\n%s\n\n' % pkg)
492 r_log.write(b_out)
493 if b_err:
494 r_log.write('\nstderr\n')
495 r_log.write(b_err)
496 r_log.close()
497
498
499
500 if not b_status:
501 if self.recurse:
502 self.failed.append(pkg)
503 self.callback.error("Error building %s, will try again" % os.path.basename(pkg))
504 else:
505 msg = "Error building %s\nSee logs/resultsin %s" % (os.path.basename(pkg), self.destdir)
506 if not self.cont:
507 raise MockRemoteError, msg
508 self.callback.error(msg)
509
510 else:
511 self.callback.log("Success building %s" % os.path.basename(pkg))
512 built_pkgs.append(pkg)
513
514 for d in [self.destdir, self.destdir + '/' + self.chroot]:
515 rc, out, err = createrepo(d)
516 if err.strip():
517 self.callback.error("Error making local repo: %s" % d)
518 self.callback.error("%s" % err)
519
520
521 if self.failed:
522 if len(self.failed) != len(to_be_built):
523 to_be_built = self.failed
524 try_again = True
525 self.callback.log('Trying to rebuild %s failed pkgs' % len(self.failed))
526 else:
527 self.callback.log("Tried twice - following pkgs could not be successfully built:")
528 for pkg in self.failed:
529 msg = pkg
530 if pkg in downloaded_pkgs:
531 msg = downloaded_pkgs[pkg]
532 self.callback.log(msg)
533
534 try_again = False
535 else:
536 try_again = False
537
541
542 parser = SortedOptParser("mockremote -b hostname -u user -r chroot pkg pkg pkg")
543 parser.add_option('-r', '--root', default=DEF_CHROOT, dest='chroot',
544 help="chroot config name/base to use in the mock build")
545 parser.add_option('-c', '--continue', default=False, action='store_true',
546 dest='cont',
547 help="if a pkg fails to build, continue to the next one")
548 parser.add_option('-a','--addrepo', default=DEF_REPOS, action='append',
549 dest='repos',
550 help="add these repo baseurls to the chroot's yum config")
551 parser.add_option('--recurse', default=False, action='store_true',
552 help="if more than one pkg and it fails to build, try to build the rest and come back to it")
553 parser.add_option('--log', default=None, dest='logfile',
554 help="log to the file named by this option, defaults to not logging")
555 parser.add_option("-b", "--builder", dest='builder', default=None,
556 help="builder to use")
557 parser.add_option("-u", dest="user", default=DEF_USER,
558 help="user to run as/connect as on builder systems")
559 parser.add_option("-t", "--timeout", dest="timeout", type="int",
560 default=DEF_TIMEOUT, help="maximum time in seconds a build can take to run")
561 parser.add_option("--destdir", dest="destdir", default=DEF_DESTDIR,
562 help="place to download all the results/packages")
563 parser.add_option("--packages", dest="packages_file", default=None,
564 help="file to read list of packages from")
565 parser.add_option("-q","--quiet", dest="quiet", default=False, action="store_true",
566 help="output very little to the terminal")
567
568 opts,args = parser.parse_args(args)
569
570 if not opts.builder:
571 print "Must specify a system to build on"
572 sys.exit(1)
573
574 if opts.packages_file and os.path.exists(opts.packages_file):
575 args.extend(read_list_from_file(opts.packages_file))
576
577
578
579
580 if not args:
581 print "Must specify at least one pkg to build"
582 sys.exit(1)
583
584 if not opts.chroot:
585 print "Must specify a mock chroot"
586 sys.exit(1)
587
588 for url in opts.repos:
589 if not (url.startswith('http') or url.startswith('file://')):
590 print "Only http[s] or file urls allowed for repos"
591 sys.exit(1)
592
593 return opts, args
594
595
596
597
598
599
600
601 -def main(args):
602
603
604 opts,pkgs = parse_args(args)
605
606 if not os.path.exists(opts.destdir):
607 os.makedirs(opts.destdir)
608
609 try:
610
611 callback = CliLogCallBack(logfn=opts.logfile, quiet=opts.quiet)
612
613 mr = MockRemote(builder=opts.builder, user=opts.user,
614 timeout=opts.timeout, destdir=opts.destdir, chroot=opts.chroot,
615 cont=opts.cont, recurse=opts.recurse, repos=opts.repos,
616 callback=callback)
617
618
619
620
621
622
623
624
625
626
627 if not opts.quiet:
628 print "Building %s pkgs" % len(pkgs)
629
630 mr.build_pkgs(pkgs)
631
632 if not opts.quiet:
633 print "Output written to: %s" % mr.destdir
634
635 except MockRemoteError, e:
636 print >>sys.stderr, "Error on build:"
637 print >>sys.stderr, str(e)
638 return
639
640
641 if __name__ == '__main__':
642 try:
643 main(sys.argv[1:])
644 except Exception, e:
645
646 print "ERROR: %s - %s" % (str(type(e)), str(e))
647 traceback.print_exc()
648 sys.exit(1)
649