From 1b99b9dbae4f34d9da8a14f8f96685dff716675c Mon Sep 17 00:00:00 2001 From: lxnay Date: Mon, 2 Feb 2009 14:46:34 +0000 Subject: [PATCH] Molecule: - started to implement handlers git-svn-id: http://svn.sabayonlinux.org/projects/molecule/trunk@2980 cd1c1023-2f26-0410-ae45-c471fc1f0318 --- examples/Sabayon4-x86.spec | 1 + molecule.py | 13 ++- molecule/__init__.py | 4 +- molecule/cmdline.py | 10 ++- molecule/exception.py | 3 + molecule/handlers.py | 146 ++++++++++++++++++++++++++++++ molecule/output.py | 180 +++++++++++++++++++++++++++++++++++++ molecule/settings.py | 3 +- 8 files changed, 351 insertions(+), 9 deletions(-) create mode 100644 molecule/handlers.py diff --git a/examples/Sabayon4-x86.spec b/examples/Sabayon4-x86.spec index 9e58865..bb98e0c 100644 --- a/examples/Sabayon4-x86.spec +++ b/examples/Sabayon4-x86.spec @@ -19,6 +19,7 @@ # source_chroot: /var/tmp/catalyst/tmp/stage1-amd64-2006.0 # Destination chroot directory, where files are pushed to before creating the squashfs image +# NOTE: data will be stored inside an auto-generated subdir # destination_chroot: /var/tmp/catalyst/tmp/stage1-amd64-2006.0/default/livecd-stage2-amd64-2006.0 # Merge directory with destination chroot diff --git a/molecule.py b/molecule.py index 3d5968b..dfc6b6b 100644 --- a/molecule.py +++ b/molecule.py @@ -19,12 +19,17 @@ import sys sys.path.insert(0,'molecule/') -import molecule.settings import molecule.cmdline +from molecule.handlers import Runner -molecule_data = molecule.cmdline.parse() -if not molecule_data: +molecule_data, molecule_data_order = molecule.cmdline.parse() +if not molecule_data_order: molecule.cmdline.print_help() raise SystemExit(1) -print "valid",molecule_data \ No newline at end of file +for el in molecule_data_order: + my = Runner(el, molecule_data.get(el)) + rc = my.run() + my.kill() + if rc != 0: raise SystemExit(rc) +raise SystemExit(0) \ No newline at end of file diff --git a/molecule/__init__.py b/molecule/__init__.py index 14ad62e..9ac8640 100644 --- a/molecule/__init__.py +++ b/molecule/__init__.py @@ -18,4 +18,6 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import molecule.settings -Config = molecule.settings.Configuration() \ No newline at end of file +import molecule.output +Config = molecule.settings.Configuration() +Output = molecule.output.Output() \ No newline at end of file diff --git a/molecule/cmdline.py b/molecule/cmdline.py index 33002bd..1dd721a 100644 --- a/molecule/cmdline.py +++ b/molecule/cmdline.py @@ -26,26 +26,30 @@ from molecule import Config from molecule.settings import SpecParser def parse(): + args_to_remove = ["--nocolor"] data = {} myargs = sys.argv[1:] if "--help" in myargs: - return data + return data, [] if "--nocolor" in myargs: molecule.output.nocolor() for arg in args_to_remove: if arg in myargs: myargs.remove(arg) + data_order = [] for el in myargs: if os.path.isfile(el) and os.access(el,os.R_OK): obj = SpecParser(el) el_data = obj.parse() del obj - if el_data: data[el] = el_data - return data + if el_data: + data_order.append(el) + data[el] = el_data + return data, data_order def print_help(): help_data = [ diff --git a/molecule/exception.py b/molecule/exception.py index 83b4dc6..111001f 100644 --- a/molecule/exception.py +++ b/molecule/exception.py @@ -32,4 +32,7 @@ class EnvironmentError(MoleculeException): """Environment error, self explanatory""" class SpecFileError(MoleculeException): + """Error inside spec file""" + +class NotImplementedError(MoleculeException): """Error inside spec file""" \ No newline at end of file diff --git a/molecule/handlers.py b/molecule/handlers.py new file mode 100644 index 0000000..e5fe93f --- /dev/null +++ b/molecule/handlers.py @@ -0,0 +1,146 @@ +#!/usr/bin/python -O +# -*- coding: iso-8859-1 -*- +# Molecule Disc Image builder for Sabayon Linux +# Copyright (C) 2009 Fabio Erculiani +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +from __future__ import with_statement +import os +from molecule.i18n import _ +from molecule.output import red, brown, blue, green, purple, darkgreen, darkred, bold, darkblue, readtext +from molecule.exception import EnvironmentError, NotImplementedError + +# This is an interface that has to be reimplemented in order to make it doing something useful +class GenericHandlerInterface: + + def __init__(self, spec_path, metadata): + from molecule import Output, Config + self.Output = Output + self.Config = Config + self.spec_path = spec_path + self.metadata = metadata + self.spec_name = os.path.basename(self.spec_path) + + def pre_run(self): + raise NotImplementedError("NotImplementedError: this needs to be reimplemented") + + def run(self): + raise NotImplementedError("NotImplementedError: this needs to be reimplemented") + + def post_run(self): + raise NotImplementedError("NotImplementedError: this needs to be reimplemented") + + def kill(self): + raise NotImplementedError("NotImplementedError: this needs to be reimplemented") + +class MirrorHandler(GenericHandlerInterface): + + def __init__(self, *args, **kwargs): + GenericHandlerInterface.__init__(self, *args, **kwargs) + + def pre_run(self): + self.Output.updateProgress( "[%s|%s] %s" % (blue("MirrorHandler"),darkred(self.spec_name),_("executing pre_run"),)) + return 0 + + def post_run(self): + self.Output.updateProgress( "[%s|%s] %s" % (blue("MirrorHandler"),darkred(self.spec_name),_("executing post_run"),)) + return 0 + + def kill(self): + self.Output.updateProgress( "[%s|%s] %s" % (blue("MirrorHandler"),darkred(self.spec_name),_("executing kill"),)) + return 0 + + def run(self): + self.Output.updateProgress( "[%s|%s] %s" % (blue("MirrorHandler"),darkred(self.spec_name),_("executing run"),)) + # running the syncer + source_dir = self.metadata['source_chroot'] + dest_dir = os.path.join(self.metadata['destination_chroot'],os.path.basename(source_dir)) + if not os.path.isdir(dest_dir): + os.makedirs(dest_dir,0755) + # FIXME: complete this + return 0 + +class ChrootHandler(GenericHandlerInterface): + + def __init__(self, *args, **kwargs): + GenericHandlerInterface.__init__(self, *args, **kwargs) + + def pre_run(self): + self.Output.updateProgress( "[%s|%s] %s" % (blue("ChrootHandler"),darkred(self.spec_name),_("executing pre_run"),)) + return 0 + + def post_run(self): + self.Output.updateProgress( "[%s|%s] %s" % (blue("ChrootHandler"),darkred(self.spec_name),_("executing post_run"),)) + return 0 + + def kill(self): + self.Output.updateProgress( "[%s|%s] %s" % (blue("ChrootHandler"),darkred(self.spec_name),_("executing kill"),)) + return 0 + + def run(self): + self.Output.updateProgress( "[%s|%s] %s" % (blue("ChrootHandler"),darkred(self.spec_name),_("executing run"),)) + return 0 + + +class Runner(GenericHandlerInterface): + + def __init__(self, spec_path, metadata): + GenericHandlerInterface.__init__(self, spec_path, metadata) + self.execution_order = [MirrorHandler,ChrootHandler] + + def pre_run(self): + return 0 + + def post_run(self): + return 0 + + def kill(self): + return 0 + + def run(self): + + self.pre_run() + count = 0 + maxcount = len(self.execution_order) + self.Output.updateProgress( "[%s|%s] %s" % (darkgreen("Runner"),brown(self.spec_name),_("preparing execution"),), count = (count,maxcount,)) + for myclass in self.execution_order: + + count += 1 + self.Output.updateProgress( "[%s|%s] %s %s" % (darkgreen("Runner"),brown(self.spec_name),_("executing"),str(myclass),), count = (count,maxcount,)) + my = myclass(self.spec_path, self.metadata) + + rc = 0 + while 1: + # pre-run + rc = my.pre_run() + if rc: break + # run + rc = my.run() + if rc: break + # post-run + rc = my.post_run() + if rc: break + break + + my.kill() + if rc: return rc + + self.post_run() + self.Output.updateProgress( "[%s|%s] %s!" % (darkgreen("Runner"),brown(self.spec_name),_("All done"),)) + return 0 + + + diff --git a/molecule/output.py b/molecule/output.py index d125e5c..7215c34 100644 --- a/molecule/output.py +++ b/molecule/output.py @@ -400,3 +400,183 @@ def my_raw_input(txt = ''): response += y flush_stdouterr() return response + +class Output: + + # @input text: text to write + # @input back: write on on the same line? + # @input importance: + # values: 0,1,2,3 (latter is a blocker - popup menu on a GUI env) + # used to specify information importance, 0 1: + if percent: + count_str = " ("+str(round((float(count[0])/count[1])*100,1))+"%) " + else: + count_str = " (%s/%s) " % (red(str(count[0])),blue(str(count[1])),) + if importance == 0: + myfunc(header+count_str+text+footer, back = back, flush = False) + elif importance == 1: + myfunc(header+count_str+text+footer, back = back, flush = False) + elif importance in (2,3): + myfunc(header+count_str+text+footer, back = back, flush = False) + + flush_stdouterr() + + # @input question: question to do + # + # @input importance: + # values: 0,1,2 (latter is a blocker - popup menu on a GUI env) + # used to specify information importance, 0 len(colours): + import exceptionTools + raise exceptionTools.IncorrectParameter("IncorrectParameter: %s = %s" % (_("maximum responses length"),len(colours),)) + try: + print darkgreen(question), + except UnicodeEncodeError: + print darkgreen(question.encode('utf-8')), + flush_stdouterr() + try: + while True: + xtermTitle(_("Entropy got a question for you")) + flush_stdouterr() + response = my_raw_input("["+"/".join([colours[i](responses[i]) for i in range(len(responses))])+"] ") + flush_stdouterr() + for key in responses: + # An empty response will match the first value in responses. + if response.upper() == key[:len(response)].upper(): + xtermTitleReset() + return key + ''' + try: + print "%s '%s'" % (_("I cannot understand"),response,), + except UnicodeEncodeError: + print "%s '%s'" % (_("I cannot understand"),response.encode('utf-8'),), + ''' + flush_stdouterr() + except (EOFError, KeyboardInterrupt): + print "%s." % (_("Interrupted"),) + xtermTitleReset() + raise SystemExit(100) + xtermTitleReset() + flush_stdouterr() + + ''' + @ title: title of the input box + @ input_parameters: [ + ('identifier 1','input text 1',input_verification_callback,False), + ('password','Password',input_verification_callback,True), + ('item_3',('checkbox','Checkbox option (boolean request) - please choose',),input_verification_callback,True), + ('item_4',('combo',('Select your favorite option',['option 1', 'option 2', 'option 3']),),input_verification_callback,True) + ] + @ cancel_button: show cancel button ? + @ output: dictionary as follows: + {'identifier 1': result, 'identifier 2': result} + ''' + def inputBox(self, title, input_parameters, cancel_button = True): + results = {} + if title: + try: + print title + except UnicodeEncodeError: + print title.encode('utf-8') + flush_stdouterr() + + def option_chooser(option_data): + mydict = {} + counter = 1 + option_text, option_list = option_data + self.updateProgress(option_text) + for item in option_list: + mydict[counter] = item + txt = "[%s] %s" % (darkgreen(str(counter)), blue(item),) + self.updateProgress(txt) + counter += 1 + while 1: + myresult = readtext("%s:" % (_('Selected number'),)).decode('utf-8') + try: + myresult = int(myresult) + except ValueError: + continue + selected = mydict.get(myresult) + if selected != None: + return myresult, selected + + for identifier, input_text, callback, password in input_parameters: + while 1: + try: + if isinstance(input_text,tuple): + myresult = False + input_type, data = input_text + if input_type == "checkbox": + answer = self.askQuestion(data) + if answer == "Yes": myresult = True + if input_type == "combo": + myresult = option_chooser(data) + else: + myresult = readtext(input_text+":", password = password).decode('utf-8') + except (KeyboardInterrupt,EOFError,): + if not cancel_button: # use with care + continue + return None + valid = callback(myresult) + if valid: + results[identifier] = myresult + break + return results + + # useful for reimplementation + # in this wait you can send a signal to a widget (total progress bar?) + def cycleDone(self): + return + + def setTitle(self, title): + xtermTitle(title) + + def setTotalCycles(self, total): + return + + def outputInstanceTest(self): + return + + def nocolor(self): + nocolor() + + def notitles(self): + notitles() diff --git a/molecule/settings.py b/molecule/settings.py index e4e4d7d..33998be 100644 --- a/molecule/settings.py +++ b/molecule/settings.py @@ -96,13 +96,14 @@ class Configuration(Dictionary): 'version': "0.1", 'chroot_compressor': "mksquashfs", 'iso_builder': "mkisofs", + 'mirror_syncer': "rsync", 'iso_builder_builtin_args': ["-J","-R","-l","-no-emul-boot","-boot-load-size","4","-udf","-boot-info-table"], } self._settings.clear() self._settings.update(settings) self._settings.update(mysettings) - paths_to_check = ["chroot_compressor","iso_builder"] + paths_to_check = ["chroot_compressor","iso_builder","mirror_syncer"] for key in paths_to_check: molecule.utils.valid_exec_check(self.get(key))