commit 1cda81a93d89fd569364557696f7c842ad7ea0e7 Author: lxnay Date: Sat Jan 31 21:57:47 2009 +0000 Molecule: - first chunk of code - general infrastructure done - spec file parser done git-svn-id: http://svn.sabayonlinux.org/projects/molecule/trunk@2977 cd1c1023-2f26-0410-ae45-c471fc1f0318 diff --git a/examples/Sabayon4-x86.spec b/examples/Sabayon4-x86.spec new file mode 100644 index 0000000..9e58865 --- /dev/null +++ b/examples/Sabayon4-x86.spec @@ -0,0 +1,56 @@ +# Molecule example .spec file + +# pre chroot command, example, for 32bit chroots on 64bit system, you always have to append "linux32" +# prechroot: linux32 + +# Release string +# release_string: Sabayon Linux + +# Release Version +# release_version: 4 + +# Release Version string description +# release_desc: Lite MCE + +# Release file (inside chroot) +# release_file: /etc/sabayon-edition + +# Source chroot directory, where files are pulled from +# source_chroot: /var/tmp/catalyst/tmp/stage1-amd64-2006.0 + +# Destination chroot directory, where files are pushed to before creating the squashfs image +# destination_chroot: /var/tmp/catalyst/tmp/stage1-amd64-2006.0/default/livecd-stage2-amd64-2006.0 + +# Merge directory with destination chroot +# merge_destination_chroot: /path/to/your/chroot/overlay + +# Outer chroot script command, to be executed outside destination chroot before packing it +# outer_chroot_script: /path/to/script/to/be/executed/outside + +# Inner chroot script command, to be executed inside destination chroot before packing it +# inner_chroot_script: /path/to/script/to/be/executed/inside + +# Destination LiveCD root directory, where files are placed before getting mkisofs'ed +# destination_livecd_root: /path/to/dest/livecd + +# Merge directory with destination LiveCD root +# merge_livecd_root: /path/to/your/configured/bootloader dir + +# Extra mksquashfs parameters +# extra_mksquashfs_parameters: + +# Extra mkisofs parameters, perhaps something to include/use your bootloader +# extra_mkisofs_parameters: + +# Destination directory for the ISO image path +# destination_iso_directory: + +# Destination ISO image name, call whatever you want.iso, not mandatory +# destination_iso_image_name: + +# Directories to remove completely (comma separated) +# directories_to_remove: /path/to/abc, /path/to/xyz, + +# Directories to empty (comma separated) +# directories_to_empty: /path/to/abc, /path/to/xyz, + diff --git a/molecule.py b/molecule.py new file mode 100644 index 0000000..3d5968b --- /dev/null +++ b/molecule.py @@ -0,0 +1,30 @@ +#!/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. + +import sys +sys.path.insert(0,'molecule/') +import molecule.settings +import molecule.cmdline + +molecule_data = molecule.cmdline.parse() +if not molecule_data: + molecule.cmdline.print_help() + raise SystemExit(1) + +print "valid",molecule_data \ No newline at end of file diff --git a/molecule/__init__.py b/molecule/__init__.py new file mode 100644 index 0000000..14ad62e --- /dev/null +++ b/molecule/__init__.py @@ -0,0 +1,21 @@ +#!/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. + +import molecule.settings +Config = molecule.settings.Configuration() \ No newline at end of file diff --git a/molecule/cmdline.py b/molecule/cmdline.py new file mode 100644 index 0000000..33002bd --- /dev/null +++ b/molecule/cmdline.py @@ -0,0 +1,64 @@ +#!/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. + +import os +import sys +import molecule.utils +import molecule.output +from molecule.i18n import _ +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 + if "--nocolor" in myargs: + molecule.output.nocolor() + + for arg in args_to_remove: + if arg in myargs: myargs.remove(arg) + + 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 + +def print_help(): + help_data = [ + None, + (0," ~ Molecule %s ~ " % (Config.get('version'),) ,1,'Disc Image builder for Sabayon Linux - (C) %s' % (molecule.utils.get_year(),) ), + None, + (0,_('Basic Options'),0,None), + None, + (1,'--help',2,_('this output')), + (1,'--nocolor',1,_('disable colorized output')), + None, + (0,_('Application Options'),0,None), + (1,' ...',1,_('execute against specified specification files')), + None, + ] + molecule.output.print_menu(help_data) diff --git a/molecule/exception.py b/molecule/exception.py new file mode 100644 index 0000000..83b4dc6 --- /dev/null +++ b/molecule/exception.py @@ -0,0 +1,35 @@ +#!/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. + +class MoleculeException(Exception): + """General superclass for Entropy exceptions""" + def __init__(self,value): + self.value = value[:] + + def __str__(self): + if isinstance(self.value, basestring): + return self.value + else: + return repr(self.value) + +class EnvironmentError(MoleculeException): + """Environment error, self explanatory""" + +class SpecFileError(MoleculeException): + """Error inside spec file""" \ No newline at end of file diff --git a/molecule/i18n.py b/molecule/i18n.py new file mode 100644 index 0000000..c5e4f88 --- /dev/null +++ b/molecule/i18n.py @@ -0,0 +1,42 @@ +#!/usr/bin/python +# 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +""" +License: GPL +Author: Fabio Erculiani +""" +_LOCALE = None +try: + import gettext + import os + gettext.bindtextdomain('molecule', '/usr/share/locale') + gettext.textdomain('molecule') + gettext.install('molecule', unicode = True) + _ = _ + + _LOCALE_FULL = os.getenv('LC_ALL') + if _LOCALE_FULL == None: + _LOCALE_FULL = os.getenv('LANG') + if _LOCALE_FULL == None: + _LOCALE_FULL = os.getenv('LANGUAGE') + + if _LOCALE_FULL: + _LOCALE = _LOCALE_FULL.split('.')[0] + _LOCALE = _LOCALE.split('_')[0] + _LOCALE = _LOCALE.lower() + +except: + def _(s): + return s diff --git a/molecule/output.py b/molecule/output.py new file mode 100644 index 0000000..d125e5c --- /dev/null +++ b/molecule/output.py @@ -0,0 +1,402 @@ +#!/usr/bin/python -O +# -*- coding: iso-8859-1 -*- +# Molecule Disc Image builder for Sabayon Linux +# Copyright (C) 2009 Fabio Erculiani +# +# Copyright 1998-2004 Gentoo Foundation +# # $Id: output.py 4906 2006-11-01 23:55:29Z zmedico $ +# Copyright (C) 2007-2008 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 molecule.i18n import _ +import sys, os +import curses +stuff = {} +stuff['cols'] = 30 +try: + curses.setupterm() + stuff['cols'] = curses.tigetnum('cols') +except: + pass +stuff['cleanline'] = "" +def setcols(): + stuff['cleanline'] = "" + count = stuff['cols'] + while count: + stuff['cleanline'] += ' ' + count -= 1 +setcols() +stuff['cursor'] = False +stuff['ESC'] = chr(27) + +havecolor=1 +global dotitles +dotitles=1 + +esc_seq = "\x1b[" + +g_attr = {} +g_attr["normal"] = 0 + +g_attr["bold"] = 1 +g_attr["faint"] = 2 +g_attr["standout"] = 3 +g_attr["underline"] = 4 +g_attr["blink"] = 5 +g_attr["overline"] = 6 # Why is overline actually useful? +g_attr["reverse"] = 7 +g_attr["invisible"] = 8 + +g_attr["no-attr"] = 22 +g_attr["no-standout"] = 23 +g_attr["no-underline"] = 24 +g_attr["no-blink"] = 25 +g_attr["no-overline"] = 26 +g_attr["no-reverse"] = 27 +# 28 isn't defined? +# 29 isn't defined? +g_attr["black"] = 30 +g_attr["red"] = 31 +g_attr["green"] = 32 +g_attr["yellow"] = 33 +g_attr["blue"] = 34 +g_attr["magenta"] = 35 +g_attr["cyan"] = 36 +g_attr["white"] = 37 +# 38 isn't defined? +g_attr["default"] = 39 +g_attr["bg_black"] = 40 +g_attr["bg_red"] = 41 +g_attr["bg_green"] = 42 +g_attr["bg_yellow"] = 43 +g_attr["bg_blue"] = 44 +g_attr["bg_magenta"] = 45 +g_attr["bg_cyan"] = 46 +g_attr["bg_white"] = 47 +g_attr["bg_default"] = 49 + + +def color(fg, bg="default", attr=["normal"]): + mystr = esc_seq[:] + "%02d" % g_attr[fg] + for x in [bg]+attr: + mystr += ";%02d" % g_attr[x] + return mystr+"m" + + + +codes={} +codes["reset"] = esc_seq + "39;49;00m" + +codes["bold"] = esc_seq + "01m" +codes["faint"] = esc_seq + "02m" +codes["standout"] = esc_seq + "03m" +codes["underline"] = esc_seq + "04m" +codes["blink"] = esc_seq + "05m" +codes["overline"] = esc_seq + "06m" # Who made this up? Seriously. + +ansi_color_codes = [] +for x in xrange(30, 38): + ansi_color_codes.append("%im" % x) + ansi_color_codes.append("%i;01m" % x) + +rgb_ansi_colors = ['0x000000', '0x555555', '0xAA0000', '0xFF5555', '0x00AA00', + '0x55FF55', '0xAA5500', '0xFFFF55', '0x0000AA', '0x5555FF', '0xAA00AA', + '0xFF55FF', '0x00AAAA', '0x55FFFF', '0xAAAAAA', '0xFFFFFF'] + +for x in xrange(len(rgb_ansi_colors)): + codes[rgb_ansi_colors[x]] = esc_seq + ansi_color_codes[x] + +del x + +codes["black"] = codes["0x000000"] +codes["darkgray"] = codes["0x555555"] + +codes["red"] = codes["0xFF5555"] +codes["darkred"] = codes["0xAA0000"] + +codes["green"] = codes["0x55FF55"] +codes["darkgreen"] = codes["0x00AA00"] + +codes["yellow"] = codes["0xFFFF55"] +codes["brown"] = codes["0xAA5500"] + +codes["blue"] = codes["0x5555FF"] +codes["darkblue"] = codes["0x0000AA"] + +codes["fuchsia"] = codes["0xFF55FF"] +codes["purple"] = codes["0xAA00AA"] + +codes["turquoise"] = codes["0x55FFFF"] +codes["teal"] = codes["0x00AAAA"] + +codes["white"] = codes["0xFFFFFF"] +codes["lightgray"] = codes["0xAAAAAA"] + +codes["darkteal"] = codes["turquoise"] +codes["darkyellow"] = codes["brown"] +codes["fuscia"] = codes["fuchsia"] +codes["white"] = codes["bold"] + +# Colors from /sbin/functions.sh +codes["GOOD"] = codes["green"] +codes["WARN"] = codes["yellow"] +codes["BAD"] = codes["red"] +codes["HILITE"] = codes["teal"] +codes["BRACKET"] = codes["blue"] + +# Portage functions +codes["INFORM"] = codes["darkgreen"] +codes["UNMERGE_WARN"] = codes["red"] +codes["MERGE_LIST_PROGRESS"] = codes["yellow"] + +def xtermTitle(mystr, raw=False): + if dotitles and "TERM" in os.environ and sys.stderr.isatty(): + myt=os.environ["TERM"] + legal_terms = ["xterm","Eterm","aterm","rxvt","screen","kterm","rxvt-unicode","gnome"] + if myt in legal_terms: + if not raw: + mystr = "\x1b]0;%s\x07" % mystr + try: + sys.stderr.write(mystr) + except UnicodeEncodeError: + sys.stderr.write(mystr.encode('utf-8')) + sys.stderr.flush() + +default_xterm_title = None + +def xtermTitleReset(): + global default_xterm_title + if default_xterm_title is None: + prompt_command = os.getenv('PROMPT_COMMAND') + if prompt_command == "": + default_xterm_title = "" + elif prompt_command is not None: + import commands + default_xterm_title = commands.getoutput(prompt_command) + else: + pwd = os.getenv('PWD','') + home = os.getenv('HOME', '') + if home != '' and pwd.startswith(home): + pwd = '~' + pwd[len(home):] + default_xterm_title = '\x1b]0;%s@%s:%s\x07' % ( + os.getenv('LOGNAME', ''), os.getenv('HOSTNAME', '').split('.', 1)[0], pwd) + xtermTitle(default_xterm_title, raw=True) + +def notitles(): + "turn off title setting" + global dotitles + dotitles=0 + +def nocolor(): + "turn off colorization" + os.environ['MOL_NO_COLOR'] = "1" + global havecolor + havecolor=0 + +nc = os.getenv("MOL_NO_COLOR") +if nc: + nocolor() + +def resetColor(): + return codes["reset"] + +def colorize(color_key, text): + global havecolor + if havecolor: + return codes[color_key] + text + codes["reset"] + else: + return text + +compat_functions_colors = ["bold","white","teal","turquoise","darkteal", + "fuscia","fuchsia","purple","blue","darkblue","green","darkgreen","yellow", + "brown","darkyellow","red","darkred"] + +def create_color_func(color_key): + def derived_func(*args): + newargs = list(args) + newargs.insert(0, color_key) + return colorize(*newargs) + return derived_func + +for c in compat_functions_colors: + setattr(sys.modules[__name__], c, create_color_func(c)) + +def print_menu(data): + + def orig_myfunc(x): + return x + def orig_myfunc_desc(x): + return x + + for item in data: + + myfunc = orig_myfunc + myfunc_desc = orig_myfunc_desc + + if not item: + writechar("\n") + else: + n_ident = item[0] + name = item[1] + n_d_ident = item[2] + desc = item[3] + + if n_ident == 0: + writechar(" ") + # setup identation + while n_ident > 0: + n_ident -= 1 + writechar("\t") + n_ident = item[0] + + # write name + if n_ident == 0: + myfunc = darkgreen + elif n_ident == 1: + myfunc = blue + myfunc_desc = darkgreen + elif n_ident == 2: + if not name.startswith("--"): + myfunc = red + myfunc_desc = brown + elif n_ident == 3: + myfunc = darkblue + myfunc_desc = purple + try: + print myfunc(name), + except UnicodeEncodeError: + print myfunc(name.encode('utf-8')), + + # write desc + if desc: + while n_d_ident > 0: + n_d_ident -= 1 + writechar("\t") + try: + print myfunc_desc(desc), + except UnicodeEncodeError: + print myfunc_desc(desc.encode('utf-8')), + writechar("\n") + +def reset_cursor(): + if havecolor: + sys.stdout.write(stuff['ESC'] + '[2K') + flush_stdouterr() + +def flush_stdouterr(): + sys.stdout.flush() + sys.stderr.flush() + +def print_error(msg, back = False, flush = True): + if not back: setcols() + reset_cursor() + writechar("\r") + if back: + try: + print darkred(">>"),msg, + except UnicodeEncodeError: + print darkred(">>"),msg.encode('utf-8'), + else: + try: + print darkred(">>"),msg + except UnicodeEncodeError: + print darkred(">>"),msg.encode('utf-8') + if flush: + flush_stdouterr() + +def print_info(msg, back = False, flush = True): + if not back: + setcols() + reset_cursor() + writechar("\r") + if back: + try: + print darkgreen(">>"),msg, + except UnicodeEncodeError: + print darkgreen(">>"),msg.encode('utf-8'), + else: + try: + print darkgreen(">>"),msg + except UnicodeEncodeError: + print darkgreen(">>"),msg.encode('utf-8') + if flush: + flush_stdouterr() + +def print_warning(msg, back = False, flush = True): + if not back: setcols() + reset_cursor() + writechar("\r") + if back: + try: + print red(">>"),msg, + except UnicodeEncodeError: + print red(">>"),msg.encode('utf-8'), + else: + try: + print red(">>"),msg + except UnicodeEncodeError: + print red(">>"),msg.encode('utf-8') + if flush: + flush_stdouterr() + +def print_generic(msg): # here we'll wrap any nice formatting + writechar("\r") + try: + print msg + except UnicodeEncodeError: + print msg.encode('utf-8') + flush_stdouterr() + +def writechar(char): + try: + sys.stdout.write(char) + sys.stdout.flush() + except IOError, e: + if e.errno == 32: + return + raise + +def readtext(request, password = False): + xtermTitle(_("Molecule needs your attention")) + if password: + from getpass import getpass + try: + text = getpass(request+" ") + except UnicodeEncodeError: + text = getpass(request.encode('utf-8')+" ") + else: + try: + print request,"", + except UnicodeEncodeError: + print request.encode('utf-8'),"", + flush_stdouterr() + text = my_raw_input() + return text + +def my_raw_input(txt = ''): + if txt: + try: + print darkgreen(txt), + except UnicodeEncodeError: + print darkgreen(txt.encode('utf-8')), + flush_stdouterr() + response = '' + while 1: + y = sys.stdin.read(1) + if y in ('\n','\r',): break + response += y + flush_stdouterr() + return response diff --git a/molecule/settings.py b/molecule/settings.py new file mode 100644 index 0000000..e4e4d7d --- /dev/null +++ b/molecule/settings.py @@ -0,0 +1,270 @@ +#!/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 +import threading +from molecule.exception import SpecFileError, EnvironmentError +import molecule.utils + +class Dictionary: + + def __init__(self): + self._settings = {} + self.L = threading.Lock() + + def __setitem__(self, key, value): + with self.L: + self._settings[key] = value + + def __getitem__(self, key): + with self.L: + return self._settings[key] + + def __contains__(self, key): + with self.L: + return key in self._settings + + def __cmp__(self, other): + with self.L: + return cmp(self._settings,other) + + def __str__(self): + with self.L: + return str(self._settings) + + def get(self, key): + with self.L: + return self._settings.get(key) + + def has_key(self, key): + with self.L: + return self._settings.has_key(key) + + def clear(self): + with self.L: + self._settings.clear() + + def keys(self): + with self.L: + return self._settings.keys() + +class Constants(Dictionary): + + def __init__(self): + Dictionary.__init__(self) + self.load() + + def load(self): + with self.L: + ETC_DIR = '/etc' + CONFIG_FILE_NAME = 'molecule.conf' + + mysettings = { + 'config_file': os.path.join(ETC_DIR, CONFIG_FILE_NAME), + } + + self._settings.clear() + self._settings.update(mysettings) + +class Configuration(Dictionary): + + def __init__(self): + Dictionary.__init__(self) + self.Constants = Constants() + self.load() + + def load(self, mysettings = {}): + with self.L: + settings = { + 'version': "0.1", + 'chroot_compressor': "mksquashfs", + 'iso_builder': "mkisofs", + '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"] + for key in paths_to_check: + molecule.utils.valid_exec_check(self.get(key)) + +class SpecParser: + + def __init__(self, filepath): + + def ne_string(x): + return x + + def always_valid(*args): + return True + + def valid_path(x): + return os.path.lexists(x) + + def valid_file(x): + return os.path.isfile(x) + + def valid_dir(x): + return os.path.isdir(x) + + def ve_string_stripper(x): + return x.strip() + + def ve_string_splitter(x): + return x.strip().split() + + def valid_exec(x): + molecule.utils.is_exec_available(x) + return True + + def valid_ascii(x): + try: + x = str(x) + return x + except (UnicodeDecodeError,UnicodeEncodeError,): + return '' + + def valid_path_string(x): + try: + os.path.split(x) + except OSError: + return False + return True + + def valid_path_list(x): + return [x for x in x.split(",") if valid_path_string(x)] + + self.vital_parameters = [ + "release_string", + "source_chroot", + "destination_iso_directory", + ] + self.parser_data_path = { + 'prechroot': { + 'cb': valid_exec, + 've': ve_string_stripper, + }, + 'release_string': { + 'cb': ne_string, # validation callback + 've': ve_string_stripper, # value extractor + }, + 'release_version': { + 'cb': ne_string, + 've': ve_string_stripper, + }, + 'release_desc': { + 'cb': ne_string, + 've': ve_string_stripper, + }, + 'release_file': { + 'cb': ne_string, + 've': ve_string_stripper, + }, + 'source_chroot': { + 'cb': valid_dir, + 've': ve_string_stripper, + }, + 'destination_chroot': { + 'cb': valid_path_string, + 've': ve_string_stripper, + }, + 'merge_destination_chroot': { + 'cb': valid_dir, + 've': ve_string_stripper, + }, + 'outer_chroot_script': { + 'cb': valid_exec, + 've': ve_string_stripper, + }, + 'inner_chroot_script': { + 'cb': valid_path_string, + 've': ve_string_stripper, + }, + 'destination_livecd_root': { + 'cb': valid_path_string, + 've': ve_string_stripper, + }, + 'merge_livecd_root': { + 'cb': valid_dir, + 've': ve_string_stripper, + }, + 'extra_mksquashfs_parameters': { + 'cb': always_valid, + 've': ve_string_splitter, + }, + 'extra_mkisofs_parameters': { + 'cb': always_valid, + 've': ve_string_splitter, + }, + 'destination_iso_directory': { + 'cb': valid_dir, + 've': ve_string_stripper, + }, + 'destination_iso_image_name': { + 'cb': valid_ascii, + 've': ve_string_stripper, + }, + 'directories_to_remove': { + 'cb': valid_path_list, + 've': ve_string_stripper, + }, + 'directories_to_empty': { + 'cb': valid_path_list, + 've': ve_string_stripper, + }, + + } + + self.filepath = filepath[:] + + def parse(self): + mydict = {} + data = self.__generic_parser(self.filepath) + for line in data: + try: + key, value = line.split(":",1) + except (ValueError, IndexError,): + continue + key = key.strip() + check_dict = self.parser_data_path.get(key) + if not isinstance(check_dict,dict): continue + value = check_dict['ve'](value) + if not check_dict['cb'](value): continue + mydict[key] = value + self.validate_parse(mydict) + return mydict.copy() + + def validate_parse(self, mydata): + for param in self.vital_parameters: + if param not in mydata: + raise SpecFileError("SpecFileError: '%s' missing or invalid '%s' parameter, it's vital. Your specification file is incomplete!" % (self.filepath,param,)) + + def __generic_parser(self, filepath): + with open(filepath,"r") as f: + data = [] + content = f.readlines() + f.close() + # filter comments and white lines + content = [x.strip().rsplit("#",1)[0].strip() for x in content if not x.startswith("#") and x.strip()] + for line in content: + if line in data: continue + data.append(line) + return data diff --git a/molecule/utils.py b/molecule/utils.py new file mode 100644 index 0000000..cc3d583 --- /dev/null +++ b/molecule/utils.py @@ -0,0 +1,44 @@ +#!/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 +import time +import subprocess +from molecule.exception import EnvironmentError + +def get_year(): + return time.strftime("%Y") + +def valid_exec_check(path): + with open("/dev/null","w") as f: + p = subprocess.Popen([path], stdout = f, stderr = f) + rc = p.wait() + if rc == 127: + raise EnvironmentError("EnvironmentError: %s not found" % (path,)) + +def is_exec_available(exec_name): + paths = os.getenv("PATH") + if not paths: return False + paths = paths.split(":") + for path in paths: + path = os.path.join(path,exec_name) + if os.path.isfile(path) and os.access(path,os.X_OK): + return True + return False