diff --git a/molecule/settings.py b/molecule/settings.py index 6cc7b0e..bb8c0c9 100644 --- a/molecule/settings.py +++ b/molecule/settings.py @@ -1,5 +1,4 @@ -#!/usr/bin/python -O -# -*- coding: iso-8859-1 -*- +# -*- coding: utf-8 -*- # Molecule Disc Image builder for Sabayon Linux # Copyright (C) 2009 Fabio Erculiani # @@ -21,6 +20,7 @@ from __future__ import with_statement import os import threading from molecule.exception import SpecFileError +from molecule.specs import SPEC_PLUGS, LivecdSpec, GenericSpec import molecule.utils class Constants(dict): @@ -49,7 +49,7 @@ class Configuration(dict): def load(self, mysettings = {}): settings = { - 'version': "0.1", + 'version': "0.3", 'chroot_compressor': "mksquashfs", 'iso_builder': "mkisofs", 'mirror_syncer': "rsync", @@ -68,153 +68,53 @@ class Configuration(dict): for key in paths_to_check: molecule.utils.valid_exec_check(self.get(key)) + + class SpecParser: + # FIXME: kept for backward .spec files compatibility where + # execution_strategy argument is not set + DEFAULT_PARSER = LivecdSpec + def __init__(self, filepath): - def ne_string(x): - return x,'raw_unicode_escape' - - def ne_list(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 unicode(x,'raw_unicode_escape').strip() - - def ve_string_splitter(x): - return unicode(x,'raw_unicode_escape').strip().split() - - def valid_exec(x): - molecule.utils.is_exec_available(x) - return x - - def valid_exec_first_list_item(x): - if not x: return False - myx = x[0] - molecule.utils.is_exec_available(myx) - 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 [y.strip() for y in unicode(x,'raw_unicode_escape').split(",") if valid_path_string(y) and y.strip()] - - self.vital_parameters = [ - "release_string", - "source_chroot", - "destination_iso_directory", - "destination_livecd_root", - ] - self.parser_data_path = { - 'prechroot': { - 'cb': valid_exec_first_list_item, - 've': ve_string_splitter, - }, - '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, - }, - 'extra_rsync_parameters': { - 'cb': valid_path_string, - 've': ve_string_splitter, - }, - '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, - 'mod': ve_string_splitter, - }, - 'pre_iso_script': { - 'cb': valid_exec, - 've': ve_string_stripper, - }, - 'destination_iso_directory': { - 'cb': valid_dir, - 've': ve_string_stripper, - }, - 'destination_iso_image_name': { - 'cb': valid_ascii, - 've': ve_string_stripper, - }, - 'paths_to_remove': { - 'cb': ne_list, - 've': valid_path_list, - }, - 'paths_to_empty': { - 'cb': ne_list, - 've': valid_path_list, - }, - - } - self.filepath = filepath[:] + execution_strategy = self.parse_execution_strategy() + if execution_strategy is None: + execution_strategy = DEFAULT_PARSER.execution_strategy() + + plugin = SPEC_PLUGS.get(execution_strategy) + if plugin is None: + raise SpecFileError("Execution strategy provided in %s spec file" + " not supported, strategy: %s" % ( + self.filepath, execution_strategy,)) + + self.__plugin = plugin() + self.vital_parameters = self.__plugin.vital_parameters() + self.parser_data_path = self.__plugin.parser_data_path() + + + def parse_execution_strategy(self): + data = self.__generic_parser(self.filepath) + exc_str = GenericSpec.EXECUTION_STRATEGY_KEY + for line in data: + if not line.startswith(exc_str): + continue + key, value = self.parse_line_statement(line) + if key is None: + continue + if key != exc_str: + # wtf? + continue + return value + + def parse_line_statement(self, line_stmt): + try: + key, value = line_stmt.split(":", 1) + except ValueError: + return None, None + key, value = key.strip(), value.strip() + return key, value def parse(self): mydict = {} @@ -223,24 +123,25 @@ class SpecParser: old_key = None for line in data: if ":" in line: - try: - key, value = line.split(":",1) - except ValueError: + key, value = self.parse_line_statement(line) + if key is None: continue - key = key.strip() old_key = key - elif isinstance(old_key,basestring): + elif isinstance(old_key, basestring): key = old_key value = line.strip() - if not value: continue + if not value: + continue check_dict = self.parser_data_path.get(key) - if not isinstance(check_dict,dict): continue + if not isinstance(check_dict, dict): + continue value = check_dict['ve'](value) - if not check_dict['cb'](value): continue + if not check_dict['cb'](value): + continue if key in mydict: - if isinstance(value,basestring): + if isinstance(value, basestring): mydict[key] += " %s" % (value,) - elif isinstance(value,list): + elif isinstance(value, list): mydict[key] += value else: continue @@ -264,8 +165,10 @@ class SpecParser: 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()] + 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 + if line in data: + continue data.append(line) return data diff --git a/molecule/specs/__init__.py b/molecule/specs/__init__.py new file mode 100644 index 0000000..34a4a20 --- /dev/null +++ b/molecule/specs/__init__.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +# 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 molecule.utils + +class GenericSpecFunctions: + + def ne_string(self, x): + return x, 'raw_unicode_escape' + + def ne_list(self, x): + return x + + def always_valid(self, *args): + return True + + def valid_path(self, x): + return os.path.lexists(x) + + def valid_file(self, x): + return os.path.isfile(x) + + def valid_dir(self, x): + return os.path.isdir(x) + + def ve_string_stripper(self, x): + return unicode(x,'raw_unicode_escape').strip() + + def ve_string_splitter(self, x): + return unicode(x,'raw_unicode_escape').strip().split() + + def valid_exec(self, x): + molecule.utils.is_exec_available(x) + return x + + def valid_exec_first_list_item(self, x): + if not x: + return False + myx = x[0] + molecule.utils.is_exec_available(myx) + return True + + def valid_ascii(self, x): + try: + x = str(x) + return x + except (UnicodeDecodeError,UnicodeEncodeError,): + return '' + + def valid_path_string(self, x): + try: + os.path.split(x) + except OSError: + return False + return True + + def valid_path_list(self, x): + return [y.strip() for y in \ + unicode(x,'raw_unicode_escape').split(",") if \ + valid_path_string(y) and y.strip()] + +class GenericSpec(GenericSpecFunctions): + + EXECUTION_STRATEGY_KEY = "execution_strategy" + + @staticmethod + def execution_strategy(): + """ + Return a string that describes the supported execution strategy. + Such as "remaster", "livecd", etc. + + @return: execution strategy string id + @rtype: string + """ + raise NotImplementedError() + + def vital_parameters(self): + """ + Return a list of vital .spec file parameters + + @return: list of vital .spec file parameters + @rtype: list + """ + raise NotImplementedError() + + def parser_data_path(self): + """ + Return a dictionary containing parameter names as key and + dict containing keys 've' and 'cb' which values are three + callable functions that respectively do value extraction (ve), + value verification (cb) and value modding (mod). + + @return: data path dictionary (see ChrootSpec code for more info) + @rtype: dict + """ + raise NotImplementedError() + +class LivecdSpec(GenericSpec): + + @staticmethod + def execution_strategy(): + return "livecd" + + def vital_parameters(self): + return [ + "release_string", + "source_chroot", + "destination_iso_directory", + "destination_livecd_root", + ] + + def parser_data_path(self): + return { + 'prechroot': { + 'cb': self.valid_exec_first_list_item, + 've': self.ve_string_splitter, + }, + 'release_string': { + 'cb': self.ne_string, # validation callback + 've': self.ve_string_stripper, # value extractor + }, + 'release_version': { + 'cb': self.ne_string, + 've': self.ve_string_stripper, + }, + 'release_desc': { + 'cb': self.ne_string, + 've': self.ve_string_stripper, + }, + 'release_file': { + 'cb': self.ne_string, + 've': self.ve_string_stripper, + }, + 'source_chroot': { + 'cb': self.valid_dir, + 've': self.ve_string_stripper, + }, + 'destination_chroot': { + 'cb': self.valid_path_string, + 've': self.ve_string_stripper, + }, + 'extra_rsync_parameters': { + 'cb': self.valid_path_string, + 've': self.ve_string_splitter, + }, + 'merge_destination_chroot': { + 'cb': self.valid_dir, + 've': self.ve_string_stripper, + }, + 'outer_chroot_script': { + 'cb': self.valid_exec, + 've': self.ve_string_stripper, + }, + 'inner_chroot_script': { + 'cb': self.valid_path_string, + 've': self.ve_string_stripper, + }, + 'destination_livecd_root': { + 'cb': self.valid_path_string, + 've': self.ve_string_stripper, + }, + 'merge_livecd_root': { + 'cb': self.valid_dir, + 've': self.ve_string_stripper, + }, + 'extra_mksquashfs_parameters': { + 'cb': self.always_valid, + 've': self.ve_string_splitter, + }, + 'extra_mkisofs_parameters': { + 'cb': self.always_valid, + 've': self.ve_string_splitter, + 'mod': self.ve_string_splitter, + }, + 'pre_iso_script': { + 'cb': self.valid_exec, + 've': self.ve_string_stripper, + }, + 'destination_iso_directory': { + 'cb': self.valid_dir, + 've': self.ve_string_stripper, + }, + 'destination_iso_image_name': { + 'cb': self.valid_ascii, + 've': self.ve_string_stripper, + }, + 'paths_to_remove': { + 'cb': self.ne_list, + 've': self.valid_path_list, + }, + 'paths_to_empty': { + 'cb': self.ne_list, + 've': self.valid_path_list, + }, + + } + + + + +# FIXME: this will need to be pluggable (and plugin factory is required) +SPEC_PLUGS = dict((x.execution_strategy(), x,) for x in \ + (LivecdSpec, RemasterSpec))