vpackager / bot.py

#!/usr/bin/env python

#    This file is part of vpackager.
#
#    vpackager is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License v2 as published by
#    the Free Software Foundation.
#
#    vpackager is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with vpackager.  If not, see <http://www.gnu.org/licenses/>.

import gtk
from vpackager import guitools
from vpackager import dbutils
from vpackager import buildutils
import jobmanage
import historymanager
import threading
import time
import os
import sqlite3
import urllib2


__author__ = 'M0E-lnx'
__author_email__ = 'moc.liamg@xnl.E0M'[::-1]
__version__ = '0.1'

class Dispatcher(threading.Thread):
    """ Dispatcher thread used for dispatching jobs from the queue"""
    def __init__(self, parent=None):
        threading.Thread.__init__(self)
        self.name = 'job-dispatcher'
        self.parent = parent

    def _get_next_job(self):
        return dbutils.GetNextJob()
        db = sqlite3.connect('/tmp/vpackager.db')
        c = db.cursor()
        c.execute('select * from queue')
        ret = c.next()
        c.close()
        return ret

    def _log_job_start(self, _id):
        return dbutils.LogJobStart(_id)

    def _log_job_end(self, _id, stdoutpath, result):
        return dbutils.LogJobEnd(_id, stdoutpath, result)

    def _refresh_views(self):
        self.parent.historyman._refresh_history_display()
        self.parent.queueman._update_queue()

    def run(self):
        while self.parent._bot_run:
            time.sleep(0.25)
            njob = dbutils.GetNextJob()
            if njob is None:
                # NO new job in queue
                self.parent._bot_run = False
                print "Stopping bot because there is nothing to do"
                return
            # clear the textview
            buf = self.parent.outputplayer.get_buffer()
            buf.delete(buf.get_bounds()[0], buf.get_bounds()[1])

            (_id,pkg,ver,src,desc,rel,_type,toinstall) = njob
            # Log the build start time
            self._log_job_start(_id)
            # Update GUI displays for queue and history
            self._refresh_views()
            self.parent.job_indicator.set_markup('<b>%s-%s</b>'%(pkg,ver))
            if src.startswith('/'):
                srcob = buildutils.Source(src)
            else:
                srcob = buildutils.SourceURL(app = pkg, version=ver,
                        srctype=_type, link=src)
            sbpath = srcob.makeSlackBuild(buildno = rel)
            # cd to the right place
            os.chdir(os.path.dirname(sbpath))
            if desc.startswith('/'):
                shutil.copy2(desc, 'slack-desc')
            else:
                self.parent._play_output('Downloading description from %s \n'% desc)
                u = urllib2.urlopen(desc)
                f = open('slack-desc', 'w')
                f.write(u.read())
                f.close()
                u.close()
            job = srcob.buildSlackBuild()
            job.observer.add_callback(self.parent._play_output)
            job.run()
            while job._popen_running:
                time.sleep(1)
            retval = job.popen.returncode
            if retval > 0:
                result = 'Fail'
            else:
                result = 'Success'

            # Save the output to a log file
            buf = self.parent.outputplayer.get_buffer()
            #logpath = sbpath.replace('.SlackBuild', '.build_log')
            logpath = os.path.join(os.path.dirname(srcob.srclocation),
                    '%s.build_log'% srcob.app)
            output = buf.get_text(buf.get_bounds()[0], buf.get_bounds()[1])
            f = open(logpath, 'w')
            f.write(output)
            f.close()
            # Log the build result to the database
            self._log_job_end(_id, logpath, result)
            # Refresh the GUI display of the history and queue
            self._refresh_views()
            if result == "Success" and toinstall == "1":
                # install the package
<<<<<<< local
                print 'we should be installing this package here'
=======
                self.parent._play_output(
                        '\nInstalling package \n')
                try:
                    proc = srcob.install_pkg()
                    proc.observer.add_callback(self.parent._play_output)
                    proc.run()
                    while proc._popen_running:
                        time.sleep(1)
                    retval = proc.popen.returncode
                except:
                    print 'Error while installing package'
                    pass
>>>>>>> other

class Tab(gtk.VBox):
    ''' Generic tab for use in the tabstrip '''
    def __init__(self, label='', stock=None, parent=None):
        gtk.VBox.__init__(self)
        ibox = gtk.VBox()
        img = gtk.Image()
        img.set_from_stock(stock, gtk.ICON_SIZE_BUTTON)
        ibox.pack_start(img, False, False, 2)
        lbl = gtk.Label()
        lbl.set_use_markup(True)
        lbl.set_markup(label)
        ibox.pack_start(lbl, False, False, 2)
        self.label = ibox
        self.label.show_all()
        self._parent = parent

class TabStatus(Tab):
    ''' Bot control and status tab '''
    def __init__(self, *args, **kwargs):
        Tab.__init__(self, label='Bot Status', stock=gtk.STOCK_PREFERENCES,
                **kwargs)
        topbox = gtk.HBox()
        flt = gtk.Frame('Bot Status')
        lvbox = gtk.VBox()
        flt.add(lvbox)
        ltbox = gtk.HBox()
        lbbox = gtk.HBox()
        lvbox.pack_start(ltbox, False , True, 2)
        lvbox.pack_start(lbbox, False, True, 2)
        lb0 = gtk.Label()
        lb1 = gtk.Label()
        lb0.set_use_markup(True)
        lb1.set_use_markup(True)
        lb0.set_markup('Current Status:')
        lb1.set_markup('Current Job:')
        self.label_bot_status = gtk.Label()
        self.label_bot_status.set_use_markup(True)
        self.label_current_job = gtk.Label()
        self.label_current_job.set_use_markup(True)
        ltbox.pack_start(lb0, False, False, 2)
        ltbox.pack_start(self.label_bot_status, False, True, 2)
        lbbox.pack_start(lb1, False, False, 2)
        lbbox.pack_start(self.label_current_job, False, True, 2)
        self._parent.bot_indicator = self.label_bot_status
        self._parent.job_indicator = self.label_current_job

        self._parent.bot_indicator.set_markup('<b>Stopped</b>')
        self._parent.job_indicator.set_markup('<b>None</b>')

        frt = gtk.Frame('Bot Control')
        rvbox = gtk.VBox()
        frt.add(rvbox)
        rtbox = gtk.HBox()
        rbbox = gtk.HBox()

        btStopBot = guitools.vButton(label='Stop', stock=gtk.STOCK_NO)
        btStartBot = guitools.vButton(label='Start', stock=gtk.STOCK_YES)
        self.btStopBot = btStopBot
        self.btStartBot = btStartBot
        btStartBot.connect('clicked', self._start_bot)
        btStopBot.connect('clicked', self._stop_bot)

        rtbox.pack_start(btStartBot, False, False, 2)
        rtbox.pack_start(btStopBot, False, False, 2)

        rvbox.pack_start(rtbox, False, True, 2)
        rvbox.pack_start(rbbox, False, True, 2)

        topbox.pack_start(flt, True, True, 2)
        topbox.pack_start(frt, False, False, 2)
        self.pack_start(topbox, False, True, 4)

        # add a textview inside a scrolled window for output monitoring.
        self.scroll = gtk.ScrolledWindow()
        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        self.stdoutmon = gtk.TextView()
        self.stdoutmon.set_property('editable', False)
        for edge in (gtk.TEXT_WINDOW_LEFT, gtk.TEXT_WINDOW_RIGHT,
                gtk.TEXT_WINDOW_TOP, gtk.TEXT_WINDOW_BOTTOM):
            self.stdoutmon.set_border_window_size(edge, 4)
        self.scroll.add(self.stdoutmon)

        frmoutput = gtk.Frame('Build Output')
        frmoutput.add(self.scroll)
        self._parent.outputplayer = self.stdoutmon
        self._parent.scrollarea = self.scroll

        self.pack_start(frmoutput, True, True, 4)
        self.mon_thread = threading.Thread(target=self.__bot_monitor,
                name='vpackager-bot-monitor', args=())
        self.mon_thread.start()
        self.jobDispatcher = Dispatcher(parent = self._parent)

<<<<<<< local
=======

>>>>>>> other
    def __bot_monitor(self):
        while self._parent._bot_run:
            gtk.gdk.threads_enter()
            self.btStartBot.set_property('sensitive', False)
            self.btStopBot.set_property('sensitive', True)
            self._parent.bot_indicator.set_markup('<b>Running</b>')
            gtk.gdk.threads_leave()
            time.sleep(0.25)
        gtk.gdk.threads_enter()
        self.btStartBot.set_property('sensitive', True)
        self.btStopBot.set_property('sensitive', False)
        self._parent.bot_indicator.set_markup('<b>Stopped</b>')
        self._parent.job_indicator.set_markup('<b>None</b>')
        gtk.gdk.threads_leave()

    def __log_line(self, line):
        buf = self.stdoutmon.get_buffer()
        buf.insert(buf.get_end_iter(), line + '\n')

    def _stop_bot(self, widget=None):
        self._parent._bot_run = False
        return

    def _start_bot(self, widget=None):
        self._parent._bot_run = True
        if not self.mon_thread.isAlive():
            del self.mon_thread
            self.mon_thread = threading.Thread(target=self.__bot_monitor,
                    name='vpackager-bot-monitor', args=())
            self.mon_thread.start()
        if not self.jobDispatcher.isAlive():
            del self.jobDispatcher
            self.jobDispatcher = Dispatcher(parent=self._parent)
            self.jobDispatcher.start()
        return

class TabQueue(Tab):
    """ Job queue tab. Used to manage build jobs."""
    def __init__(self, *args, **kwargs):
        Tab.__init__(self, label = 'Job Queue',
                stock = gtk.STOCK_INDEX, **kwargs)
        cont = jobmanage.Main()
        self.pack_start(cont, True, True, 2)
        self._parent.queueman = cont

class TabHistory(Tab):
    """ Tab displaying the job build history"""
    def __init__(self, *args, **kwargs):
        Tab.__init__(self, label = 'Job History',
                stock=gtk.STOCK_JUSTIFY_FILL, **kwargs)
        cont = historymanager.Main()
        self.pack_start(cont, True, True, 2)
        self._parent.historyman = cont

class TabStrip(gtk.Notebook):
    """ Tabstrip used to display the main content of the application"""
    def __init__(self):
        gtk.Notebook.__init__(self)
        self.set_property('tab-pos', gtk.POS_LEFT)

class App(gtk.Window):
    """ Main app GUI """
    def __init__(self):
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        self.set_title('vpackager')
        self.connect('destroy', self._exit)
        self.set_icon_from_file('data/vpackager.png')
        self.set_size_request(950, 500)
        self.body = gtk.VBox()
        # make sure the db is ok
        self._check_db_sanity()
        self.outputplayer = None
        self.historyman = None
        self.queueman = None
        self._bot_run = False
        self.body.pack_start(self._get_menubar(), False, True, 4)
        self.body.pack_start(self._get_notebook(), True, True, 4)
        #self.body.pack_start(self._get_bottombar(), False, False, 4)

        self.add(self.body)

    def _get_menubar(self):
        mb = gtk.MenuBar()
        filemenu = gtk.Menu()
        filem = gtk.MenuItem("File")
        filem.set_submenu(filemenu)

        exit = gtk.MenuItem("Exit")
        exit.connect("activate", self._exit)
        filemenu.append(exit)

        mb.append(filem)

        helpm = gtk.MenuItem("Help")
        helpmenu = gtk.Menu()
        helpm.set_submenu(helpmenu)

        about = gtk.MenuItem("About")
        about.connect("activate", self._show_about)
        helpmenu.append(about)
        mb.append(helpm)
        return mb

    def _show_about(self, widget=None):
        """Display the about window"""
        dia = guitools.About()
        if dia.run():
            dia.destroy()

    def _play_output(self, line):
        buf = self.outputplayer.get_buffer()
        buf.insert(buf.get_end_iter(), line)
        gtk.gdk.threads_enter()
        vad = self.scrollarea.get_vadjustment()
        newpos = vad.get_upper() - vad.get_page_size()
        vad.set_value(newpos)
        self.scrollarea.set_vadjustment(vad)
        gtk.gdk.threads_leave()


    def _get_notebook(self):
        """ Main content notebook"""
        strip = TabStrip()
        stats = TabStatus(parent=self)
        strip.append_page(stats, stats.label)

        queuet = TabQueue(parent=self)
        strip.append_page(queuet, queuet.label)

        history = TabHistory(parent=self)
        strip.append_page(history, history.label)


        return strip

    def _get_bottombar(self):
        return gtk.Label('Bottom bar')

    def _exit(self, widget=None):
        self._stop_bot = True
        time.sleep(0.25)
        return gtk.main_quit()

    def _check_db_sanity(self):
        dbutils.create_vpackager_db()

if __name__ == '__main__':
    gtk.gdk.threads_init()
    w = App()
    w.show_all()
    gtk.gdk.threads_enter()
    gtk.main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.