#!/usr/bin/env python # -*- coding: utf-8 -*- import ConfigParser import logging import paramiko import optparse import sys import os LOG = logging.getLogger('rpmbuild-remote') LOG.addHandler(logging.StreamHandler()) EXTENSIONS = ( 'patch', 'bz2', 'gz', 'tgz', 'lzma', 'xz', ) def get_parser(): parser = optparse.OptionParser(usage="%prog [options]... ...") home = os.path.expanduser("~") parser.set_conflict_handler("resolve") parser.add_option("-c", "--config", action="store", type="string", dest="config", default=os.path.join(home, ".rpmbuild-remote"), help="Configuration file to use for the build. " "[Default: ~/.rpmbuild-remote") parser.add_option("-s", "--system", action="store", type="string", dest="system", help="The remote machine to use (defined in config file)") parser.add_option("-k", "--ssh-key", action="store", type="string", dest="ssh_key", help="The private SSH key to use to login.") parser.add_option("-u", "--user", action="store", type="string", dest="user", default=os.getlogin(), help="The username on the remote system [Default: current user]") parser.add_option("-h", "--host", action="store", type="string", dest="host", help="The hostname/IP of the remote system") parser.add_option("-p", "--port", action="store", type="int", dest="port", default=21, help="The port to use on the remote system [Default: 21]") parser.add_option("-d", "--dest", action="store", type="string", dest="dest", default=os.path.join(home, "rpmbuild-remote"), help="Directory to place resulting files " "[Default: ~/rpmbuild-remote/]") parser.add_option("-m", "--mock", action="store", type="string", dest="mock", help="Mock configuration to use (only applies to SRPM files) " "[Default: Use rpmbuild --rebuild)]") return parser def read_section(config, section, values): if not config.has_section(section): return ret = {} for value, typ in values: if config.has_option(section, value): if typ == 'int': method = config.getint elif typ == 'float': method = config.getfloat elif typ == 'boolean': method = config.getboolean elif typ == 'str': method = config.get else: method = config.get LOG.warning("Unknown type %s, assuming string" % typ) ret[value] = method(section, value) return ret def read_preferences(opts): values = ( ('user', 'str'), ('ssh_key', 'str'), ('host', 'str'), ('port', 'int'), ) config = ConfigParser.SafeConfigParser() config.read(opts.config) system = read_section(config, opts.system, values) if system == {}: system = read_section(config, 'default', values) if 'user' not in system: system['user'] = opts.user if 'ssh_key' not in system: system['ssh_key'] = opts.ssh_key if 'host' not in system: if opts.host is None: LOG.error("Host not set") sys.exit(1) system['host'] = opts.host if 'port' not in system: system['port'] = opts.port return system def send_files(session, files, dest): sftp = session.open_sftp() for file in files: LOG.info("Writing %s to server (%s)" % (file, dest)) sftp.put(file, dest) sftp.close() def execute_jobs(opts, system, jobs): session = paramiko.SSHClient() LOG.info("Connecting to %(user)s@%(host)s:%(port)d" % system) session.connect(system['host'], port=system['port'], username=system['user'], key_filename=system['ssh_key']) for job in jobs: if type(job) == type(''): dest = "rpmbuild/SRPMS/" send_files(session, [job], dest) file = os.path.basename(job) if opts.mock is None: LOG.info("Building %s" % file) cmd = "rpmbuild --rebuild rpmbuild/SRPMS/%s" % file else: LOG.info("Building %s with mock config %s" % (file, opts.mock)) cmd = "mock -r %s rpmbuild/SRPMS/%s" % (opts.mock, file) else: dest = "rpmbuild/SPECS" send_files(session, [job[0]], dest) dest = "rpmbuild/SOURCES" send_files(session, job[1:], dest) file = os.path.basename(job[0]) LOG.info("Building %s from %d souces" % (file, len(job) - 1)) cmd = "rpmbuild -ba rpmbuild/SPECS/%s" % file) files = session.exec_command(cmd) # Write output files # Fetch RPM files session.close() def main(): parser = get_parser() opts, args = parser.parse_args() jobs = [] for arg in args: ext = os.path.splitext(arg)[1] if ext == '.srpm': jobs.append(arg) elif ext == '.spec': jobs.append([arg]) else: if ext not in EXTENSIONS: LOG.warning("Uncommon source extension %s for file %s. " "Please double check files." % (ext, arg)) if not len(jobs) or not type(jobs[-1]) == type([]): LOG.error("Source file given without spec file.") sys.exit(1) jobs[-1].append(arg) system = read_preferences(opts) execute_jobs(opts, system, jobs) if __name__ == '__main__': main()