Package backend :: Module mockremote
[hide private]
[frames] | no frames]

Source Code for Module backend.mockremote

  1  #!/usr/bin/python -tt 
  2  # by skvidal 
  3  # This program is free software; you can redistribute it and/or modify 
  4  # it under the terms of the GNU General Public License as published by 
  5  # the Free Software Foundation; either version 2 of the License, or 
  6  # (at your option) any later version. 
  7  # 
  8  # This program is distributed in the hope that it will be useful, 
  9  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 11  # GNU Library General Public License for more details. 
 12  # 
 13  # You should have received a copy of the GNU General Public License 
 14  # along with this program; if not, write to the Free Software 
 15  # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA. 
 16  # copyright 2012 Red Hat, Inc. 
 17   
 18   
 19  # take list of pkgs 
 20  # take single hostname 
 21  # send 1 pkg at a time to host 
 22  # build in remote w/mockchain 
 23  # rsync results back 
 24  # repeat 
 25  # take args from mockchain (more or less) 
 26   
 27  from __future__ import print_function 
 28   
 29  import os 
 30  import sys 
 31  import time 
 32  import fcntl 
 33  import pipes 
 34  import socket 
 35  import urllib 
 36  import optparse 
 37  import subprocess 
 38  from operator import methodcaller 
 39   
 40  import ansible.runner 
 41   
 42  # where we should execute mockchain from on the remote 
 43  mockchain = "/usr/bin/mockchain" 
 44  # rsync path 
 45  rsync = "/usr/bin/rsync" 
 46   
 47  DEF_REMOTE_BASEDIR = "/var/tmp" 
 48  DEF_TIMEOUT = 3600 
 49  DEF_REPOS = [] 
 50  DEF_CHROOT = None 
 51  DEF_USER = "mockbuilder" 
 52  DEF_DESTDIR = os.getcwd() 
 53  DEF_MACROS = {} 
 54  DEF_BUILDROOT_PKGS = "" 
55 56 57 -class SortedOptParser(optparse.OptionParser):
58 59 """Optparser which sorts the options by opt before outputting --help""" 60
61 - def format_help(self, formatter=None):
62 self.option_list.sort(key=methodcaller("get_opt_string")) 63 return optparse.OptionParser.format_help(self, formatter=None)
64
65 66 -def createrepo(path, lock=None):
67 if "epel-5" in path: 68 # this is because rhel-5 doesn't know sha256 69 if os.path.exists(path + '/repodata/repomd.xml'): 70 comm = ['/usr/bin/createrepo_c', '--database', '--ignore-lock', 71 '--update', '-s', 'sha', path] 72 else: 73 comm = ['/usr/bin/createrepo_c', '--database', '--ignore-lock', 74 '-s', 'sha', path] 75 else: 76 if os.path.exists(path + '/repodata/repomd.xml'): 77 comm = ['/usr/bin/createrepo_c', '--database', '--ignore-lock', 78 '--update', path] 79 else: 80 comm = ['/usr/bin/createrepo_c', '--database', '--ignore-lock', 81 path] 82 if lock: 83 lock.acquire() 84 cmd = subprocess.Popen(comm, 85 stdout=subprocess.PIPE, stderr=subprocess.PIPE) 86 out, err = cmd.communicate() 87 if lock: 88 lock.release() 89 return cmd.returncode, out, err
90
91 92 -def read_list_from_file(fn):
93 lst = [] 94 f = open(fn, "r") 95 for line in f.readlines(): 96 line = line.replace("\n", "") 97 line = line.strip() 98 if line.startswith("#"): 99 continue 100 lst.append(line) 101 102 return lst
103
104 105 -def log(lf, msg, quiet=None):
106 if lf: 107 now = time.time() 108 try: 109 with open(lf, "a") as lfh: 110 fcntl.flock(lfh, fcntl.LOCK_EX) 111 lfh.write(str(now) + ":" + msg + "\n") 112 fcntl.flock(lfh, fcntl.LOCK_UN) 113 except (IOError, OSError) as e: 114 sys.stderr.write( 115 "Could not write to logfile {0} - {1}\n".format(lf, str(e))) 116 if not quiet: 117 print(msg)
118
119 120 -def get_ans_results(results, hostname):
121 if hostname in results["dark"]: 122 return results["dark"][hostname] 123 if hostname in results["contacted"]: 124 return results["contacted"][hostname] 125 126 return {}
127
128 129 -def _create_ans_conn(hostname, username, timeout):
130 ans_conn = ansible.runner.Runner(remote_user=username, 131 host_list=hostname + ",", 132 pattern=hostname, 133 forks=1, 134 transport="ssh", 135 timeout=timeout) 136 return ans_conn
137
138 139 -def check_for_ans_error(results, hostname, err_codes=None, success_codes=None, 140 return_on_error=None):
141 """ 142 Return True or False + dict 143 dict includes 'msg' 144 may include 'rc', 'stderr', 'stdout' and any other requested result codes 145 """ 146 147 if err_codes is None: 148 err_codes = [] 149 if success_codes is None: 150 success_codes = [0] 151 if return_on_error is None: 152 return_on_error = ["stdout", "stderr"] 153 err_results = {} 154 155 if "dark" in results and hostname in results["dark"]: 156 err_results["msg"] = "Error: Could not contact/connect" \ 157 " to {0}.".format(hostname) 158 159 return (True, err_results) 160 161 error = False 162 163 if err_codes or success_codes: 164 if hostname in results["contacted"]: 165 if "rc" in results["contacted"][hostname]: 166 rc = int(results["contacted"][hostname]["rc"]) 167 err_results["rc"] = rc 168 # check for err codes first 169 if rc in err_codes: 170 error = True 171 err_results["msg"] = "rc {0} matched err_codes".format(rc) 172 elif rc not in success_codes: 173 error = True 174 err_results["msg"] = "rc {0} not in success_codes".format(rc) 175 176 elif ("failed" in results["contacted"][hostname] and 177 results["contacted"][hostname]["failed"]): 178 179 error = True 180 err_results["msg"] = "results included failed as true" 181 182 if error: 183 for item in return_on_error: 184 if item in results["contacted"][hostname]: 185 err_results[item] = results["contacted"][hostname][item] 186 187 return error, err_results
188
189 190 -class MockRemoteError(Exception):
191
192 - def __init__(self, msg):
193 super(MockRemoteError, self).__init__() 194 self.msg = msg
195
196 - def __str__(self):
197 return self.msg
198
199 200 -class BuilderError(MockRemoteError):
201 pass
202
203 204 -class DefaultCallBack(object):
205
206 - def __init__(self, **kwargs):
207 self.quiet = kwargs.get("quiet", False) 208 self.logfn = kwargs.get("logfn", None)
209
210 - def start_build(self, pkg):
211 pass
212
213 - def end_build(self, pkg):
214 pass
215
216 - def start_download(self, pkg):
217 pass
218
219 - def end_download(self, pkg):
220 pass
221
222 - def error(self, msg):
223 self.log("Error: {0}".format(msg))
224
225 - def log(self, msg):
226 if not self.quiet: 227 print(msg)
228
229 230 -class CliLogCallBack(DefaultCallBack):
231
232 - def __init__(self, **kwargs):
233 DefaultCallBack.__init__(self, **kwargs)
234
235 - def start_build(self, pkg):
236 msg = "Start build: {0}".format(pkg) 237 self.log(msg)
238
239 - def end_build(self, pkg):
240 msg = "End Build: {0}".format(pkg) 241 self.log(msg)
242
243 - def start_download(self, pkg):
244 msg = "Start retrieve results for: {0}".format(pkg) 245 self.log(msg)
246
247 - def end_download(self, pkg):
248 msg = "End retrieve results for: {0}".format(pkg) 249 self.log(msg)
250
251 - def error(self, msg):
252 self.log("Error: {0}".format(msg))
253
254 - def log(self, msg):
255 log(self.logfn, msg, self.quiet)
256
257 258 -class Builder(object):
259
260 - def __init__(self, hostname, username, 261 timeout, mockremote, buildroot_pkgs):
262 263 self.hostname = hostname 264 self.username = username 265 self.timeout = timeout 266 self.chroot = mockremote.chroot 267 self.repos = mockremote.repos 268 self.mockremote = mockremote 269 270 if buildroot_pkgs is None: 271 self.buildroot_pkgs = "" 272 else: 273 self.buildroot_pkgs = buildroot_pkgs 274 275 self.checked = False 276 self._tempdir = None 277 # if we're at this point we've connected and done stuff on the host 278 self.conn = _create_ans_conn( 279 self.hostname, self.username, self.timeout) 280 self.root_conn = _create_ans_conn(self.hostname, "root", self.timeout) 281 # check out the host - make sure it can build/be contacted/etc 282 self.check()
283 284 @property
285 - def remote_build_dir(self):
286 return self.tempdir + "/build/"
287 288 @property
289 - def tempdir(self):
290 if self.mockremote.remote_tempdir: 291 return self.mockremote.remote_tempdir 292 293 if self._tempdir: 294 return self._tempdir 295 296 cmd = "/bin/mktemp -d {0}/{1}-XXXXX".format( 297 self.mockremote.remote_basedir, "mockremote") 298 299 self.conn.module_name = "shell" 300 self.conn.module_args = str(cmd) 301 results = self.conn.run() 302 tempdir = None 303 for _, resdict in results["contacted"].items(): 304 tempdir = resdict["stdout"] 305 306 # if still nothing then we"ve broken 307 if not tempdir: 308 raise BuilderError("Could not make tmpdir on {0}".format( 309 self.hostname)) 310 311 cmd = "/bin/chmod 755 {0}".format(tempdir) 312 self.conn.module_args = str(cmd) 313 self.conn.run() 314 self._tempdir = tempdir 315 316 return self._tempdir
317 318 @tempdir.setter
319 - def tempdir(self, value):
320 self._tempdir = value
321
322 - def _get_remote_pkg_dir(self, pkg):
323 # the pkg will build into a dir by mockchain named: 324 # $tempdir/build/results/$chroot/$packagename 325 s_pkg = os.path.basename(pkg) 326 pdn = s_pkg.replace(".src.rpm", "") 327 remote_pkg_dir = os.path.normpath( 328 os.path.join(self.remote_build_dir, 329 "results", 330 self.chroot, 331 pdn)) 332 333 return remote_pkg_dir
334
335 - def modify_base_buildroot(self):
336 """ 337 Modify mock config for current chroot. 338 339 Packages in buildroot_pkgs are added to minimal buildroot 340 """ 341 342 if ("'{0} '".format(self.buildroot_pkgs) != 343 pipes.quote(str(self.buildroot_pkgs) + ' ')): 344 345 # just different test if it contains only alphanumeric characters 346 # allowed in packages name 347 raise BuilderError("Do not try this kind of attack on me") 348 349 self.root_conn.module_name = "lineinfile" 350 if self.chroot == "epel-7-x86_64": 351 self.root_conn.module_args = ( 352 "dest=/etc/mock/epel-7-x86_64.cfg" 353 " line=\"config_opts['chroot_setup_cmd'] = 'install" 354 " bash bzip2 coreutils cpio diffutils findutils" 355 " gawk gcc gcc-c++ grep gzip info make patch" 356 " redhat-release-server redhat-rpm-config rpm-build" 357 " sed shadow-utils tar unzip util-linux which xz {0}'\"" 358 " regexp=\"^.*chroot_setup_cmd.*$\"".format( 359 self.buildroot_pkgs)) 360 else: 361 self.root_conn.module_args = ( 362 "dest=/etc/mock/{0}.cfg" 363 " line=\"config_opts['chroot_setup_cmd'] =" 364 " 'install @buildsys-build {1}'\"" 365 " regexp=\"^.*chroot_setup_cmd.*$\"".format( 366 self.chroot, self.buildroot_pkgs)) 367 368 self.mockremote.callback.log( 369 "putting {0} into minimal buildroot of {1}".format( 370 self.buildroot_pkgs, self.chroot)) 371 372 results = self.root_conn.run() 373 374 is_err, err_results = check_for_ans_error( 375 results, self.hostname, success_codes=[0], 376 return_on_error=["stdout", "stderr"]) 377 378 if is_err: 379 self.mockremote.callback.log("Error: {0}".format(err_results)) 380 myresults = get_ans_results(results, self.hostname) 381 self.mockremote.callback.log("{0}".format(myresults))
382
383 - def build(self, pkg):
384 385 # build the pkg passed in 386 # add pkg to various lists 387 # check for success/failure of build 388 # return success/failure,stdout,stderr of build command 389 # returns success_bool, out, err 390 391 success = False 392 build_details = {} 393 self.modify_base_buildroot() 394 395 # check if pkg is local or http 396 dest = None 397 if os.path.exists(pkg): 398 dest = os.path.normpath( 399 os.path.join(self.tempdir, os.path.basename(pkg))) 400 401 self.conn.module_name = "copy" 402 margs = "src={0} dest={1}".format(pkg, dest) 403 self.conn.module_args = margs 404 self.mockremote.callback.log( 405 "Sending {0} to {1} to build".format( 406 os.path.basename(pkg), self.hostname)) 407 408 # FIXME should probably check this but <shrug> 409 self.conn.run() 410 else: 411 dest = pkg 412 413 # srpm version 414 self.conn.module_name = "shell" 415 self.conn.module_args = "rpm -qp --qf \"%{VERSION}\n\" "+pkg 416 self.mockremote.callback.log("Getting package information: version") 417 results = self.conn.run() 418 if "contacted" in results: 419 build_details["pkg_version"] = results["contacted"].itervalues().next()[u"stdout"] 420 421 # construct the mockchain command 422 buildcmd = "{0} -r {1} -l {2} ".format( 423 mockchain, pipes.quote(self.chroot), 424 pipes.quote(self.remote_build_dir)) 425 426 for r in self.repos: 427 if "rawhide" in self.chroot: 428 r = r.replace("$releasever", "rawhide") 429 430 buildcmd += "-a {0} ".format(pipes.quote(r)) 431 432 if self.mockremote.macros: 433 for k, v in self.mockremote.macros.items(): 434 mock_opt = "--define={0} {1}".format(k, v) 435 buildcmd += "-m {0} ".format(pipes.quote(mock_opt)) 436 437 buildcmd += dest 438 439 # run the mockchain command async 440 # this runs it sync - FIXME 441 self.mockremote.callback.log("executing: {0}".format(buildcmd)) 442 self.conn.module_name = "shell" 443 self.conn.module_args = buildcmd 444 results = self.conn.run() 445 446 is_err, err_results = check_for_ans_error( 447 results, self.hostname, success_codes=[0], 448 return_on_error=["stdout", "stderr"]) 449 450 if is_err: 451 return (success, err_results.get("stdout", ""), 452 err_results.get("stderr", ""), build_details) 453 454 # we know the command ended successfully but not if the pkg built 455 # successfully 456 myresults = get_ans_results(results, self.hostname) 457 out = myresults.get("stdout", "") 458 err = myresults.get("stderr", "") 459 460 successfile = os.path.join(self._get_remote_pkg_dir(pkg), "success") 461 testcmd = "/usr/bin/test -f {0}".format(successfile) 462 self.conn.module_args = testcmd 463 results = self.conn.run() 464 is_err, err_results = check_for_ans_error( 465 results, self.hostname, success_codes=[0]) 466 467 if not is_err: 468 success = True 469 470 self.mockremote.callback.log("Listing built binary packages") 471 self.conn.module_name = "shell" 472 self.conn.module_args = pipes.quote("cd {0} && for f in `ls *.rpm | grep -v \"src.rpm$\"`; do rpm -qp --qf \"%{{NAME}} %{{VERSION}}\n\" $f; done".format(self._get_remote_pkg_dir(pkg))) 473 results = self.conn.run() 474 build_details["built_packages"] = results["contacted"].itervalues().next()[u"stdout"] 475 self.mockremote.callback.log("Packages:\n"+build_details["built_packages"]) 476 477 478 return success, out, err, build_details
479
480 - def download(self, pkg, destdir):
481 # download the pkg to destdir using rsync + ssh 482 # return success/failure, stdout, stderr 483 484 success = False 485 rpd = self._get_remote_pkg_dir(pkg) 486 # make spaces work w/our rsync command below :( 487 destdir = "'" + destdir.replace("'", "'\\''") + "'" 488 # build rsync command line from the above 489 remote_src = "{0}@{1}:{2}".format(self.username, self.hostname, rpd) 490 ssh_opts = "'ssh -o PasswordAuthentication=no -o StrictHostKeyChecking=no'" 491 command = "{0} -avH -e {1} {2} {3}/".format( 492 rsync, ssh_opts, remote_src, destdir) 493 494 cmd = subprocess.Popen(command, shell=True, 495 stdout=subprocess.PIPE, stderr=subprocess.PIPE) 496 497 # rsync results into opts.destdir 498 out, err = cmd.communicate() 499 if cmd.returncode: 500 success = False 501 else: 502 success = True 503 504 return success, out, err
505
506 - def check(self):
507 # do check of host 508 # set checked if successful 509 # return success/failure, errorlist 510 511 if self.checked: 512 return True, [] 513 514 errors = [] 515 516 try: 517 socket.gethostbyname(self.hostname) 518 except socket.gaierror: 519 raise BuilderError("{0} could not be resolved".format( 520 self.hostname)) 521 522 self.conn.module_name = "shell" 523 self.conn.module_args = "/bin/rpm -q mock rsync" 524 res = self.conn.run() 525 526 # check for mock/rsync from results 527 is_err, err_results = check_for_ans_error( 528 res, self.hostname, success_codes=[0]) 529 530 if is_err: 531 if "rc" in err_results: 532 errors.append( 533 "Warning: {0} does not have mock or rsync installed" 534 .format(self.hostname)) 535 else: 536 errors.append(err_results["msg"]) 537 538 # test for path existence for mockchain and chroot config for this 539 # chroot 540 self.conn.module_name = "shell" 541 self.conn.module_args = "/usr/bin/test -f {0}" \ 542 " && /usr/bin/test -f /etc/mock/{1}.cfg".format( 543 mockchain, self.chroot) 544 res = self.conn.run() 545 546 is_err, err_results = check_for_ans_error( 547 res, self.hostname, success_codes=[0]) 548 549 if is_err: 550 if "rc" in err_results: 551 errors.append( 552 "Warning: {0} lacks mockchain on chroot {1}".format( 553 self.hostname, self.chroot)) 554 else: 555 errors.append(err_results["msg"]) 556 557 if not errors: 558 self.checked = True 559 else: 560 msg = "\n".join(errors) 561 raise BuilderError(msg)
562
563 564 -class MockRemote(object):
565
566 - def __init__(self, builder=None, user=DEF_USER, timeout=DEF_TIMEOUT, 567 destdir=DEF_DESTDIR, chroot=DEF_CHROOT, cont=False, 568 recurse=False, repos=None, callback=None, 569 remote_basedir=DEF_REMOTE_BASEDIR, remote_tempdir=None, 570 macros=None, lock=None, 571 buildroot_pkgs=DEF_BUILDROOT_PKGS):
572 573 if repos is None: 574 repos = DEF_REPOS 575 if macros is None: 576 macros = DEF_MACROS 577 self.destdir = destdir 578 self.chroot = chroot 579 self.repos = repos 580 self.cont = cont 581 self.recurse = recurse 582 self.callback = callback 583 self.remote_basedir = remote_basedir 584 self.remote_tempdir = remote_tempdir 585 self.macros = macros 586 self.lock = lock 587 588 if not self.callback: 589 self.callback = DefaultCallBack() 590 591 self.callback.log("Setting up builder: {0}".format(builder)) 592 self.builder = Builder(builder, user, timeout, self, buildroot_pkgs) 593 594 if not self.chroot: 595 raise MockRemoteError("No chroot specified!") 596 597 self.failed = [] 598 self.finished = [] 599 self.pkg_list = []
600
601 - def _get_pkg_destpath(self, pkg):
602 s_pkg = os.path.basename(pkg) 603 pdn = s_pkg.replace(".src.rpm", "") 604 resdir = "{0}/{1}/{2}".format(self.destdir, self.chroot, pdn) 605 resdir = os.path.normpath(resdir) 606 return resdir
607
608 - def build_pkgs(self, pkgs=None):
609 610 if not pkgs: 611 pkgs = self.pkg_list 612 613 built_pkgs = [] 614 downloaded_pkgs = {} 615 616 build_details = {} 617 skipped = False 618 619 try_again = True 620 to_be_built = pkgs 621 while try_again: 622 self.failed = [] 623 just_built = [] 624 for pkg in to_be_built: 625 pkg = urllib.unquote(str(pkg)) 626 if pkg in just_built: 627 self.callback.log( 628 "skipping duplicate pkg in this list: {0}".format(pkg)) 629 continue 630 else: 631 just_built.append(pkg) 632 633 p_path = self._get_pkg_destpath(pkg) 634 635 # check the destdir to see if these pkgs need to be built 636 if os.path.exists(p_path): 637 if os.path.exists(os.path.join(p_path, "success")): 638 skipped = True 639 self.callback.log( 640 "Skipping already built pkg {0}".format( 641 os.path.basename(pkg))) 642 643 continue 644 # if we"re asking to build it and it is marked as fail - nuke 645 # the failure and try rebuilding it 646 elif os.path.exists(os.path.join(p_path, "fail")): 647 os.unlink(os.path.join(p_path, "fail")) 648 649 # off to the builder object 650 # building 651 self.callback.start_build(pkg) 652 b_status, b_out, b_err, build_details = self.builder.build(pkg) 653 self.callback.end_build(pkg) 654 655 # downloading 656 self.callback.start_download(pkg) 657 # mockchain makes things with the chroot appended - so suck down 658 # that pkg subdir from w/i that location 659 chroot_dir = os.path.normpath( 660 os.path.join(self.destdir, self.chroot)) 661 662 d_ret, d_out, d_err = self.builder.download(pkg, chroot_dir) 663 if not d_ret: 664 msg = "Failure to download {0}: {1}".format( 665 pkg, d_out + d_err) 666 667 if not self.cont: 668 raise MockRemoteError(msg) 669 self.callback.error(msg) 670 671 self.callback.end_download(pkg) 672 # write out whatever came from the builder call into the 673 # destdir/chroot 674 if not os.path.exists(chroot_dir): 675 os.makedirs( 676 os.path.join(self.destdir, self.chroot)) 677 678 r_log = open(os.path.join(chroot_dir, "mockchain.log"), 'a') 679 fcntl.flock(r_log, fcntl.LOCK_EX) 680 r_log.write("\n\n{0}\n\n".format(pkg)) 681 r_log.write(b_out) 682 if b_err: 683 r_log.write("\nstderr\n") 684 r_log.write(b_err) 685 fcntl.flock(r_log, fcntl.LOCK_UN) 686 r_log.close() 687 688 # checking where to stick stuff 689 if not b_status: 690 if self.recurse: 691 self.failed.append(pkg) 692 self.callback.error( 693 "Error building {0}, will try again".format( 694 os.path.basename(pkg))) 695 else: 696 msg = "Error building {0}\nSee logs/results in {1}" \ 697 .format(os.path.basename(pkg), self.destdir) 698 699 if not self.cont: 700 raise MockRemoteError(msg) 701 self.callback.error(msg) 702 703 else: 704 self.callback.log("Success building {0}".format( 705 os.path.basename(pkg))) 706 built_pkgs.append(pkg) 707 # createrepo with the new pkgs 708 _, _, err = createrepo(chroot_dir, self.lock) 709 if err.strip(): 710 self.callback.error( 711 "Error making local repo: {0}".format(chroot_dir)) 712 713 self.callback.error(str(err)) 714 # FIXME - maybe clean up .repodata and .olddata 715 # here? 716 717 if self.failed: 718 if len(self.failed) != len(to_be_built): 719 to_be_built = self.failed 720 try_again = True 721 self.callback.log( 722 "Trying to rebuild {0} failed pkgs".format( 723 len(self.failed))) 724 else: 725 self.callback.log( 726 "Tried twice - following pkgs could not be" 727 " successfully built:") 728 729 for pkg in self.failed: 730 msg = pkg 731 if pkg in downloaded_pkgs: 732 msg = downloaded_pkgs[pkg] 733 self.callback.log(msg) 734 735 try_again = False 736 else: 737 try_again = False 738 739 return skipped, build_details
740
741 742 -def parse_args(args):
743 744 parser = SortedOptParser( 745 "mockremote -b hostname -u user -r chroot pkg pkg pkg") 746 parser.add_option("-r", "--root", default=DEF_CHROOT, dest="chroot", 747 help="chroot config name/base to use in the mock build") 748 parser.add_option("-c", "--continue", default=False, action="store_true", 749 dest="cont", 750 help="if a pkg fails to build, continue to the next one") 751 parser.add_option("-a", "--addrepo", default=DEF_REPOS, action="append", 752 dest="repos", 753 help="add these repo baseurls to the chroot's yum config") 754 parser.add_option("--recurse", default=False, action="store_true", 755 help="if more than one pkg and it fails to build," 756 " try to build the rest and come back to it") 757 parser.add_option("--log", default=None, dest="logfile", 758 help="log to the file named by this option," 759 " defaults to not logging") 760 parser.add_option("-b", "--builder", dest="builder", default=None, 761 help="builder to use") 762 parser.add_option("-u", dest="user", default=DEF_USER, 763 help="user to run as/connect as on builder systems") 764 parser.add_option("-t", "--timeout", dest="timeout", type="int", 765 default=DEF_TIMEOUT, 766 help="maximum time in seconds a build can take to run") 767 parser.add_option("--destdir", dest="destdir", default=DEF_DESTDIR, 768 help="place to download all the results/packages") 769 parser.add_option("--packages", dest="packages_file", default=None, 770 help="file to read list of packages from") 771 parser.add_option("-q", "--quiet", dest="quiet", default=False, 772 action="store_true", 773 help="output very little to the terminal") 774 775 opts, args = parser.parse_args(args) 776 777 if not opts.builder: 778 sys.stderr.write("Must specify a system to build on") 779 sys.exit(1) 780 781 if opts.packages_file and os.path.exists(opts.packages_file): 782 args.extend(read_list_from_file(opts.packages_file)) 783 784 # args = list(set(args)) # poor man's 'unique' - this also changes the order 785 # :( 786 787 if not args: 788 sys.stderr.write("Must specify at least one pkg to build") 789 sys.exit(1) 790 791 if not opts.chroot: 792 sys.stderr.write("Must specify a mock chroot") 793 sys.exit(1) 794 795 for url in opts.repos: 796 if not (url.startswith("http://") or 797 url.startswith("https://") or url.startswith("file://")): 798 799 sys.stderr.write("Only http[s] or file urls allowed for repos") 800 sys.exit(1) 801 802 return opts, args
803
804 805 # FIXME 806 # play with createrepo run at the end of each build 807 # need to output the things that actually worked :) 808 809 810 -def main(args):
811 812 # parse args 813 opts, pkgs = parse_args(args) 814 815 if not os.path.exists(opts.destdir): 816 os.makedirs(opts.destdir) 817 818 try: 819 # setup our callback 820 callback = CliLogCallBack(logfn=opts.logfile, quiet=opts.quiet) 821 # our mockremote instance 822 mr = MockRemote(builder=opts.builder, 823 user=opts.user, 824 timeout=opts.timeout, 825 destdir=opts.destdir, 826 chroot=opts.chroot, 827 cont=opts.cont, 828 recurse=opts.recurse, 829 repos=opts.repos, 830 callback=callback) 831 832 # FIXMES 833 # things to think about doing: 834 # output the remote tempdir when you start up 835 # output the number of pkgs 836 # output where you're writing things to 837 # consider option to sync over destdir to the remote system to use 838 # as a local repo for the build 839 # 840 841 if not opts.quiet: 842 print("Building {0} pkgs".format(len(pkgs))) 843 844 mr.build_pkgs(pkgs) 845 846 if not opts.quiet: 847 print("Output written to: {0}".format(mr.destdir)) 848 849 except MockRemoteError as e: 850 sys.stderr.write("Error on build:\n") 851 sys.stderr.write("{0}\n".format(e)) 852 return
853 854 855 if __name__ == "__main__": 856 main(sys.argv[1:]) 857