Pidora-Update-Source

From CDOT Wiki
Revision as of 12:00, 28 June 2013 by Oatley (talk | contribs) (Created page with '{{Admon/important| Warning! | This script has barely been tested as of this posting. May be riddled with bugs or may not function as expected.}} = Pidora Update Source = * This…')
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Important.png
Warning!
This script has barely been tested as of this posting. May be riddled with bugs or may not function as expected.

Pidora Update Source

  • This script was kinda rushed, probably need to go back over it and fix some things
    • Complete testing of all functions in this script has not been completed
  • To show/track which version is posted here
commit 7e5e19709b8ffb3f06ea7287da5a058445123fd3
Author: Andrew Oatley-Willis <andrew.oatley-willis@senecacollege.ca>
Date:   Fri Jun 28 11:46:44 2013 -0400

    Bugs fixed
     - Stop rsync if packages are unsigned
    
    Added descriptions
     - Added mash, sign, and rsync descriptions
     - Output to show when a process has started and ended
  • Source code of pidora-update.py
#!/usr/bin/env python
# Andrew Oatley-Willis
# Multi-purpose tool written in python for linux
# This script will allow for a single safeguarded process to run: sigul, mash, and rsync to pidora.ca
# It will do a sanity check on everything that is happening and prevent manual errors that could occur
# Every configuration is customizable on the command line with well named options
import optparse
import pysftp
import sys
import urllib2
import getpass
import crypt
import random
import re

class tools:
    def __init__(self):
        # Default configuration values
        sigulhost = "england"
        mashhost = "japan"
        rsynchost = "pidora.ca"
        siguluser = "agreene"
        mashuser = "root"
        rsyncuser = "pidorapr"
        mashdir = "/usr/local/bin/mash-pidora"
        kojitags = ['f18-updates', 'f18-rpfr-updates', 'f18-updates-testing', 'f18-rpfr-updates-testing']

        # Create command line options
        parser = optparse.OptionParser()
        parser = optparse.OptionParser(usage='Usage: %prog [options]')
        parser.add_option('-i', '--info',  help='check machine status and configuration', dest='status', default=False, action='store_true')
        parser.add_option('-a', '--all',  help='sign, mash, rsync', dest='all', default=False, action='store_true')
        parser.add_option('-s', '--sign',  help='sign all packages in listed tag', dest='sign', default=False, action='store_true')
        parser.add_option('-m', '--mash',  help='start a mash run', dest='mash', default=False, action='store_true')
        parser.add_option('-r', '--rync',  help='perform a rsync of the mash repos', dest='rsync', default=False, action='store_true')
        parser.add_option('-l', '--list-unsigned',  help='list unsigned rpms', dest='listunsigned', default=False, action='store_true')
        parser.add_option('--koji-tag',  help='specify the koji tag to sign', dest='kojitag', default=False, action='store')
        parser.add_option('--sigul-user',  help='specify the user for sigul', dest='siguluser', default=siguluser, action='store', metavar=siguluser)
        parser.add_option('--sigul-host',  help='specify the host for sigul', dest='sigulhost', default=sigulhost, action='store', metavar=sigulhost)
        parser.add_option('--mash-user',  help='specify the user for mash', dest='mashuser', default=mashuser, action='store', metavar=mashuser)
        parser.add_option('--mash-host',  help='specify the host for mash', dest='mashhost', default=mashhost, action='store', metavar=mashhost)
        parser.add_option('--rsync-user',  help='specify the user for rsync', dest='rsyncuser', default=rsyncuser, action='store', metavar=rsyncuser)
        parser.add_option('--rsync-host',  help='specify the host for rsync', dest='rsynchost', default=rsynchost, action='store', metavar=rsynchost)
        (opts, args) = parser.parse_args()

        # Check number of arguments and check for option switches
        if len(sys.argv[1:]) == 0:
            parser.print_help()
            exit(-1)
        if opts.kojitag:
            kojitags = [opts.kojitag]
        if opts.sigulhost:
            sigulhost = opts.sigulhost
        if opts.mashhost:
            mashhost = opts.mashhost
        if opts.rsynchost:
            rsynchost = opts.rsynchost
        if opts.siguluser:
            siguluser = opts.siguluser
        if opts.mashuser:
            mashuser = opts.mashuser
        if opts.rsyncuser:
            rsyncuser = opts.rsyncuser

        # Create lists of successful and failed hosts
        mhosts, mfail = self.get_status(mashhost, mashuser)
        shosts, sfail = self.get_status(sigulhost, siguluser)
        rhosts, rfail = self.get_status(rsynchost, rsyncuser)
        hosts = mhosts + shosts + rhosts
        fhosts = mfail + sfail + rfail
       
        # Start the main tasks
        if opts.status:
            print '\nsigulhost = ' + sigulhost
            print 'mashhost = ' + mashhost
            print 'rsynchost = ' + rsynchost
            print 'siguluser = ' + siguluser
            print 'mashuser = ' + mashuser
            print 'rsyncuser = ' + rsyncuser
            print 'mashdir = ' + mashdir
            print 'kojitags = ', kojitags
            print '\nworking hosts: ', hosts 
            print 'failed hosts: ', fhosts 
            print ""
            exit(0)
        elif sigulhost not in hosts: # Check connection with sigul host
            print 'Cannot connect to sigul: failed hosts: ', fhosts
            exit(1)
        elif opts.listunsigned:
            for tag in kojitags:
                print 'Unsigned packages: ' + tag
                self.checksign(sigulhost, siguluser, tag)
            exit(0)
        elif opts.sign:
            self.run_sign(sigulhost, siguluser, kojitags)
            exit(0)
        elif mashhost not in hosts: # Check connection with mash host
            print 'Cannot connect to mash hosts: failed hosts: ', fhosts
            exit(1)
        elif opts.mash:
            self.run_mash(mashhost, mashuser, kojitags, sigulhost, siguluser)
            exit(0)
        elif rsynchost not in hosts: # Check connection with rsync host
            print 'Cannot connect to rsync hosts: failed hosts: ', fhosts
            exit(1)
        elif opts.rsync:
            for tag in kojitags:
                if self.checksign(sigulhost, siguluser, tag):
                    print 'Unsigned packages in: ' + tag
                    print 'Run script with options: --list-unsigned to see unsigned packages'
                    print '== Exiting due to unsigned packages =='
                    exit(1)
            self.checkmash(mashhost, mashuser)
            self.rsync(rsynchost, rsyncuser)
            exit(0)
        elif opts.everything:
            self.run_sign(sigulhost, siguluser, kojitags)
            self.run_mash(mashhost, mashuser, kojitags, sigulhost, siguluser)
            self.rsync(rsynchost, rsyncuser)
            exit(0)
    
    # Call sign over multiple koji tags and ask only once for the password
    def run_sign(self, sigulhost, siguluser, kojitags):
        print '\n== Start: Sign run ==\n'
        print 'Koji tags marked for signing:'
        for tag in kojitags:
            print tag.strip()
        print '\nEnter sigul key passphrase:'
        password = getpass.getpass()
        for tag in kojitags:
            self.sign(sigulhost, siguluser, tag, password)
            print ""

    # Call mash and check that all packages are signed
    def run_mash(self, mashhost, mashuser, kojitags, sigulhost, siguluser):
        print '\n== Start: Mash run ==\n'
        for tag in kojitags: # Check for unsigned packages before mashing
            if self.checksign(sigulhost, siguluser, tag):
                print 'Cannot mash while packages are unsigned: '
                self.checksign(sigulhost, siguluser, tag)
                exit(1)
        self.mash(mashhost, mashuser)

    # Check if hosts are online and can establish connection, return lists of failed and succesful hosts
    def get_status(self, host, username):
        hostname = []
        fhost = []
        check = self.connect(host, username)
        if check:
            hostname.append(host)
        else:
            fhost.append(host)
        return (hostname, fhost)
    
    # Connect to the hosts, return True or False
    def connect(self, host, username):
        try:
            response=urllib2.urlopen('http://'+host,timeout=1)
            srv = pysftp.Connection(host=host, username=username, log=True)
            srv.close()
            return True
        except urllib2.URLError as err:pass
        except:pass
        return False

    # Start a signing run across a designated tag
    def sign(self, host, username, tag, password):
        print "Signing packages in tag: " + tag
        print "Packages found: "
        print self.checksign(host, username, tag)
        tempfile1 = crypt.crypt(str(random.random()), "pidora" ) + '.log'
        tempfile = tempfile1.replace("/", "")
        tempdir = '~/.pidora/'
        srv = pysftp.Connection(host=host, username=username, log=True)
        errors = srv.execute('mkdir ' + tempdir + ' 2>/dev/null')
        errors = srv.execute('touch ' + tempdir + tempfile + '2>/dev/null')
        output = srv.execute('~/.sigul/sigulsign_unsigned.py -v --password=' + password + ' --write-all --tag=' + tag + " pidora-18 2>" + tempdir + tempfile)
        errors = srv.execute('cat ' + tempdir + tempfile)
        srv.close()
        # Scan through output and find errors! If errors are found, stop program and spit out error warnings
        outputs = output + errors
        errors = False
        for output in outputs:
            print output.strip()
            if re.search('^ERROR:.*$', output):
                errors = True
        if errors:
            print "\n== Error signing stopping program =="
            exit(1)

    # Check koji for unsigned packages, returns True if unsigned rpms are found
    def checksign(self, host, username, tag):
        check = False
        srv = pysftp.Connection(host=host, username=username, log=True)
        output = srv.execute("~/.sigul/sigulsign_unsigned.py --just-list --tag=" + tag + " pidora-18")
        srv.close()
        for rpm in output:
            print rpm.strip()
            if rpm.strip() != "":
                check = "unsigned rpms found"
        if check:
            return True

    # Run mash and search through the log file for failed mash errors
    def mash(self, host, username):
        srv = pysftp.Connection(host=host, username=username, log=True)
        output = srv.execute('/usr/local/bin/mashrun-pidora-18')
        srv.close()
        self.checkmash(host, username)

    def checkmash(self, host, username):
        errors = False
        errorline = []
        srv = pysftp.Connection(host=host, username=username, log=True)
        output = srv.execute('cat /mnt/koji/mash/pidora-mash-latest')
        srv.close()
        for line in output:
            if re.search('^mash failed .*$', line):
                errorline.append(line.strip())
                errors = True
        if errors:
            print "\n== mash failed on repo stopping program ==\n"
            for line in errorline:
                print line
            exit(1)
        
    def rsync(self, host, username):
        print '\n== Start: Rsync ==\n'
        srv = pysftp.Connection(host=host, username=username, log=True)
        output = srv.execute('/home/pidorapr/bin/rsync-japan')
        srv.close()
        for line in output:
            print line.strip()

if __name__ == '__main__':
    tools()