[*] complete migration from molecule.git, fix Makefile and tests
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
PREFIX = /usr
|
||||
BINDIR = $(PREFIX)/bin
|
||||
DESTDIR =
|
||||
LIBDIR = $(PREFIX)/lib
|
||||
SYSCONFDIR = /etc
|
||||
|
||||
all:
|
||||
|
||||
|
||||
clean:
|
||||
|
||||
|
||||
install:
|
||||
install -d $(DESTDIR)$(LIBDIR)/molecule/molecule/specs/plugins
|
||||
install -m 644 *.py $(DESTDIR)$(LIBDIR)/molecule/molecule/specs/plugins/
|
||||
@@ -0,0 +1,17 @@
|
||||
# -*- 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.
|
||||
@@ -0,0 +1,772 @@
|
||||
# -*- 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 shutil
|
||||
import tempfile
|
||||
import glob
|
||||
|
||||
from molecule.compat import get_stringtype
|
||||
from molecule.i18n import _
|
||||
from molecule.output import red, brown, blue, green, purple, darkgreen, \
|
||||
darkred, bold, darkblue, readtext
|
||||
from molecule.specs.skel import GenericExecutionStep, GenericSpec
|
||||
|
||||
import molecule.utils
|
||||
|
||||
class BuiltinHandlerMixin:
|
||||
"""
|
||||
This class contains code in common between built-in handler classes.
|
||||
"""
|
||||
def _run_error_script(self, source_chroot_dir, chroot_dir, cdroot_dir,
|
||||
env = None):
|
||||
|
||||
error_script = self.metadata.get('error_script')
|
||||
if error_script:
|
||||
|
||||
if env is None:
|
||||
env = os.environ.copy()
|
||||
else:
|
||||
env = env.copy()
|
||||
|
||||
if source_chroot_dir:
|
||||
env['SOURCE_CHROOT_DIR'] = source_chroot_dir
|
||||
if chroot_dir:
|
||||
env['CHROOT_DIR'] = chroot_dir
|
||||
if cdroot_dir:
|
||||
env['CDROOT_DIR'] = cdroot_dir
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("BuiltinHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(error_script),
|
||||
)
|
||||
)
|
||||
molecule.utils.exec_cmd(error_script, env = env)
|
||||
|
||||
def _exec_inner_script(self, exec_script, dest_chroot):
|
||||
|
||||
source_exec = exec_script[0]
|
||||
with open(source_exec, "rb") as f_src:
|
||||
tmp_fd, tmp_exec = tempfile.mkstemp(dir = dest_chroot,
|
||||
prefix = "molecule_inner")
|
||||
f_dst = os.fdopen(tmp_fd, "wb")
|
||||
try:
|
||||
shutil.copyfileobj(f_src, f_dst)
|
||||
finally:
|
||||
f_dst.flush()
|
||||
f_dst.close()
|
||||
shutil.copystat(source_exec, tmp_exec)
|
||||
os.chmod(tmp_exec, 0o744)
|
||||
|
||||
dest_exec = os.path.basename(tmp_exec)
|
||||
if not dest_exec.startswith("/"):
|
||||
dest_exec = "/%s" % (dest_exec,)
|
||||
|
||||
rc = 0
|
||||
try:
|
||||
rc = molecule.utils.exec_chroot_cmd([dest_exec] + exec_script[1:],
|
||||
dest_chroot, pre_chroot = self.metadata.get('prechroot', []))
|
||||
except:
|
||||
# kill all the pids inside chroot, no matter what just happened
|
||||
molecule.utils.kill_chroot_pids(dest_chroot, sleep = True)
|
||||
raise
|
||||
finally:
|
||||
os.remove(tmp_exec)
|
||||
|
||||
if rc != 0:
|
||||
molecule.utils.kill_chroot_pids(dest_chroot, sleep = True)
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("BuiltinHandler"), darkred(self.spec_name),
|
||||
_("inner chroot hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
class MirrorHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
|
||||
def setup(self):
|
||||
# creating destination chroot dir
|
||||
self.source_dir = self.metadata['source_chroot']
|
||||
self.dest_dir = os.path.join(
|
||||
self.metadata['destination_chroot'], "chroot",
|
||||
os.path.basename(self.source_dir)
|
||||
)
|
||||
if not os.path.isdir(self.dest_dir):
|
||||
os.makedirs(self.dest_dir, 0o755)
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("MirrorHandler"), darkred(self.spec_name),
|
||||
_("executing pre_run"),
|
||||
)
|
||||
)
|
||||
|
||||
exec_script = self.metadata.get('inner_source_chroot_script')
|
||||
if exec_script:
|
||||
if os.path.isfile(exec_script[0]) and \
|
||||
os.access(exec_script[0], os.R_OK):
|
||||
rc = self._exec_inner_script(exec_script, self.source_dir)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("MirrorHandler"), darkred(self.spec_name),
|
||||
_("inner_source_chroot_script failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("MirrorHandler"), darkred(self.spec_name),
|
||||
_("pre_run completed successfully"),
|
||||
)
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
def post_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("MirrorHandler"), darkred(self.spec_name),
|
||||
_("executing post_run"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
if not success:
|
||||
self._run_error_script(self.source_dir, self.dest_dir, None)
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("MirrorHandler"), darkred(self.spec_name),
|
||||
_("executing kill"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("MirrorHandler"), darkred(self.spec_name),
|
||||
_("mirroring running"),
|
||||
)
|
||||
)
|
||||
# running sync
|
||||
args = [self._config['mirror_syncer']]
|
||||
args.extend(self._config['mirror_syncer_builtin_args'])
|
||||
args.extend(self.metadata.get('extra_rsync_parameters', []))
|
||||
args.append(self.source_dir + "/")
|
||||
args.append(self.dest_dir + "/")
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("MirrorHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(args),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("MirrorHandler"), darkred(self.spec_name),
|
||||
_("mirroring failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("MirrorHandler"), darkred(self.spec_name),
|
||||
_("mirroring completed successfully"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
class ChrootHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
|
||||
def setup(self):
|
||||
self.source_dir = self.metadata['source_chroot']
|
||||
self.dest_dir = os.path.join(
|
||||
self.metadata['destination_chroot'], "chroot",
|
||||
os.path.basename(self.source_dir)
|
||||
)
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("executing pre_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# run outer chroot script
|
||||
exec_script = self.metadata.get('outer_chroot_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['CHROOT_DIR'] = self.source_dir
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("outer chroot hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def post_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("executing post_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# now remove paths to empty
|
||||
empty_paths = self.metadata.get('paths_to_empty', [])
|
||||
for mypath in empty_paths:
|
||||
mypath = self.dest_dir+mypath
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("emptying dir"), mypath,
|
||||
)
|
||||
)
|
||||
if os.path.isdir(mypath):
|
||||
molecule.utils.empty_dir(mypath)
|
||||
|
||||
# now remove paths to remove (...)
|
||||
remove_paths = self.metadata.get('paths_to_remove', [])
|
||||
|
||||
# setup sandbox
|
||||
sb_dirs = [self.dest_dir]
|
||||
sb_env = {
|
||||
'SANDBOX_WRITE': ':'.join(sb_dirs),
|
||||
}
|
||||
myenv = os.environ.copy()
|
||||
myenv.update(sb_env)
|
||||
|
||||
for mypath in remove_paths:
|
||||
mypath = self.dest_dir+mypath
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("removing dir"), mypath,
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.remove_path_sandbox(mypath, sb_env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("removal failed for"), mypath, rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
# write release file
|
||||
release_file = self.metadata.get('release_file')
|
||||
if isinstance(release_file, get_stringtype()) and release_file:
|
||||
if release_file[0] == os.sep:
|
||||
release_file = release_file[len(os.sep):]
|
||||
release_file = os.path.join(self.dest_dir, release_file)
|
||||
if os.path.lexists(release_file) and not os.path.isfile(release_file):
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("release file creation failed, not a file"),
|
||||
release_file,
|
||||
)
|
||||
)
|
||||
return 1
|
||||
release_string = self.metadata.get('release_string', '')
|
||||
release_version = self.metadata.get('release_version', '')
|
||||
release_desc = self.metadata.get('release_desc', '')
|
||||
file_string = "%s %s %s\n" % (release_string, release_version, release_desc,)
|
||||
try:
|
||||
f = open(release_file, "w")
|
||||
f.write(file_string)
|
||||
f.flush()
|
||||
f.close()
|
||||
except (IOError, OSError,) as e:
|
||||
self._output.output("[%s|%s] %s: %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("release file creation failed, system error"),
|
||||
release_file, e,
|
||||
)
|
||||
)
|
||||
return 1
|
||||
|
||||
# run outer chroot script after
|
||||
exec_script = self.metadata.get('outer_chroot_script_after')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['CHROOT_DIR'] = self.source_dir
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("outer chroot hook (after inner) failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
if not success:
|
||||
self._run_error_script(self.source_dir, self.dest_dir, None)
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"),
|
||||
darkred(self.spec_name), _("executing kill"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("hooks running"),
|
||||
)
|
||||
)
|
||||
|
||||
# run inner chroot script
|
||||
exec_script = self.metadata.get('inner_chroot_script')
|
||||
if exec_script:
|
||||
if os.path.isfile(exec_script[0]) and os.access(exec_script[0], os.R_OK):
|
||||
rc = self._exec_inner_script(exec_script, self.dest_dir)
|
||||
if rc != 0:
|
||||
return rc
|
||||
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("hooks completed succesfully"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
class CdrootHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
|
||||
def setup(self):
|
||||
self.source_chroot = os.path.join(
|
||||
self.metadata['destination_chroot'], "chroot",
|
||||
os.path.basename(self.metadata['source_chroot'])
|
||||
)
|
||||
self.dest_root = os.path.join(
|
||||
self.metadata['destination_livecd_root'], "livecd",
|
||||
os.path.basename(self.metadata['source_chroot'])
|
||||
)
|
||||
if os.path.isdir(self.dest_root):
|
||||
molecule.utils.empty_dir(self.dest_root)
|
||||
if not os.path.isdir(self.dest_root):
|
||||
os.makedirs(self.dest_root, 0o755)
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("executing pre_run"),
|
||||
)
|
||||
)
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("preparing environment"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
def post_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("executing post_run"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
if not success:
|
||||
self._run_error_script(None, self.source_chroot,
|
||||
self.dest_root)
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("executing kill"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("compressing chroot"),
|
||||
)
|
||||
)
|
||||
args = [self._config['chroot_compressor']]
|
||||
comp_output = self._config['chroot_compressor_output_file']
|
||||
if "chroot_compressor_output_file" in self.metadata:
|
||||
comp_output = self.metadata.get('chroot_compressor_output_file')
|
||||
comp_output = os.path.join(self.dest_root, comp_output)
|
||||
args.extend([self.source_chroot, comp_output])
|
||||
args.extend(self._config['chroot_compressor_builtin_args'])
|
||||
args.extend(self.metadata.get('extra_mksquashfs_parameters', []))
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(args),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("chroot compression failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("chroot compressed successfully"),
|
||||
)
|
||||
)
|
||||
|
||||
# merge dir
|
||||
merge_dir = self.metadata.get('merge_livecd_root')
|
||||
if merge_dir:
|
||||
if os.path.isdir(merge_dir):
|
||||
self._output.output("[%s|%s] %s %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("merging livecd root"), merge_dir,)
|
||||
)
|
||||
import stat
|
||||
try:
|
||||
content = os.listdir(merge_dir)
|
||||
for mypath in content:
|
||||
mysource = os.path.join(merge_dir, mypath)
|
||||
mydest = os.path.join(self.dest_root, mypath)
|
||||
copystat = False
|
||||
|
||||
if os.path.islink(mysource):
|
||||
tolink = os.readlink(mysource)
|
||||
os.symlink(tolink, mydest)
|
||||
elif os.path.isfile(mysource) or os.path.islink(mysource):
|
||||
copystat = True
|
||||
shutil.copy2(mysource, mydest)
|
||||
elif os.path.isdir(mysource):
|
||||
copystat = True
|
||||
shutil.copytree(mysource, mydest)
|
||||
|
||||
if copystat:
|
||||
user = os.stat(mysource)[stat.ST_UID]
|
||||
group = os.stat(mysource)[stat.ST_GID]
|
||||
os.chown(mydest, user, group)
|
||||
shutil.copystat(mysource, mydest)
|
||||
except OSError as err:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("CdrootHandler"), darkred(self.spec_name),
|
||||
_("cannot merge livecd root, improper usage or system error"), err,)
|
||||
)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
class IsoHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
MD5_EXT = ".md5"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
|
||||
def setup(self):
|
||||
# setup paths
|
||||
self.source_path = os.path.join(
|
||||
self.metadata['destination_livecd_root'], "livecd",
|
||||
os.path.basename(self.metadata['source_chroot'])
|
||||
)
|
||||
dest_iso_dir = self.metadata['destination_iso_directory']
|
||||
if not os.path.isdir(dest_iso_dir):
|
||||
os.makedirs(dest_iso_dir, 0o755)
|
||||
dest_iso_filename = self.metadata.get('destination_iso_image_name')
|
||||
release_string = self.metadata.get('release_string', '')
|
||||
release_version = self.metadata.get('release_version', '')
|
||||
release_desc = self.metadata.get('release_desc', '')
|
||||
if not dest_iso_filename:
|
||||
dest_iso_filename = "%s_%s_%s.iso" % (
|
||||
release_string.replace(' ', '_'),
|
||||
release_version.replace(' ', '_'),
|
||||
release_desc.replace(' ', '_'),
|
||||
)
|
||||
self.dest_iso = os.path.join(dest_iso_dir, dest_iso_filename)
|
||||
self.iso_title = "%s %s %s" % (release_string, release_version, release_desc,)
|
||||
self.source_chroot = self.metadata['source_chroot']
|
||||
self.chroot_dir = os.path.join(
|
||||
self.metadata['destination_chroot'], "chroot",
|
||||
os.path.basename(self.source_chroot)
|
||||
)
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("executing pre_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# run pre iso script
|
||||
exec_script = self.metadata.get('pre_iso_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['SOURCE_CHROOT_DIR'] = self.source_chroot
|
||||
env['CHROOT_DIR'] = self.chroot_dir
|
||||
env['CDROOT_DIR'] = self.source_path
|
||||
env['ISO_PATH'] = self.dest_iso
|
||||
env['ISO_CHECKSUM_PATH'] = self.dest_iso + IsoHandler.MD5_EXT
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("pre iso hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def post_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("executing post_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# run post iso script
|
||||
exec_script = self.metadata.get('post_iso_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['ISO_PATH'] = self.dest_iso
|
||||
env['ISO_CHECKSUM_PATH'] = self.dest_iso + IsoHandler.MD5_EXT
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("post iso hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
if not success:
|
||||
self._run_error_script(self.source_chroot, self.chroot_dir,
|
||||
self.source_path)
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("executing kill"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("building ISO image"),
|
||||
)
|
||||
)
|
||||
|
||||
args = [self._config['iso_builder']]
|
||||
args.extend(self._config['iso_builder_builtin_args'])
|
||||
args.extend(self.metadata.get('extra_mkisofs_parameters', []))
|
||||
if self.iso_title.strip():
|
||||
args.extend(["-V", self.iso_title[:32]])
|
||||
args.extend(['-o', self.dest_iso, self.source_path])
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(args),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("ISO image build failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("built ISO image"), self.dest_iso,
|
||||
)
|
||||
)
|
||||
if os.path.isfile(self.dest_iso) and os.access(self.dest_iso, os.R_OK):
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoHandler"), darkred(self.spec_name),
|
||||
_("generating md5 for"), self.dest_iso,
|
||||
)
|
||||
)
|
||||
digest = molecule.utils.md5sum(self.dest_iso)
|
||||
md5file = self.dest_iso + IsoHandler.MD5_EXT
|
||||
with open(md5file, "w") as f:
|
||||
f.write("%s %s\n" % (digest, os.path.basename(self.dest_iso),))
|
||||
f.flush()
|
||||
|
||||
return 0
|
||||
|
||||
class LivecdSpec(GenericSpec):
|
||||
|
||||
PLUGIN_API_VERSION = 0
|
||||
|
||||
@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 {
|
||||
'execution_strategy': {
|
||||
'cb': self.ne_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'prechroot': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_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.always_valid,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'merge_destination_chroot': {
|
||||
'cb': self.valid_dir,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'error_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_chroot_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'inner_source_chroot_script': {
|
||||
'cb': self.valid_path_string_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'inner_chroot_script': {
|
||||
'cb': self.valid_path_string_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_chroot_script_after': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'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_command_splitter,
|
||||
},
|
||||
'extra_mkisofs_parameters': {
|
||||
'cb': self.always_valid,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'pre_iso_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'post_iso_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'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,
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
def get_execution_steps(self):
|
||||
return [MirrorHandler, ChrootHandler, CdrootHandler, IsoHandler]
|
||||
@@ -0,0 +1,685 @@
|
||||
# -*- 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 array
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import gc
|
||||
import errno
|
||||
|
||||
from molecule.i18n import _
|
||||
from molecule.output import blue, darkred
|
||||
from molecule.specs.skel import GenericExecutionStep, GenericSpec
|
||||
|
||||
import molecule.utils
|
||||
|
||||
from .builtin_plugin import BuiltinHandlerMixin
|
||||
from .remaster_plugin import IsoUnpackHandler as RemasterIsoUnpackHandler, \
|
||||
ChrootHandler as RemasterChrootHandler
|
||||
|
||||
|
||||
class ImageHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
LOSETUP_EXEC = "/sbin/losetup"
|
||||
MB_IN_BYTES = 1024000
|
||||
DEFAULT_IMAGE_FORMATTER = ["/sbin/mkfs.ext3"]
|
||||
DEFAULT_IMAGE_MOUNTER = ["/bin/mount", "-o", "loop,rw"]
|
||||
DEFAULT_IMAGE_UMOUNTER = ["/bin/umount"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
|
||||
# init variables
|
||||
self.loop_device = None
|
||||
self._tmp_loop_device_fd = None
|
||||
self.tmp_loop_device_file = None
|
||||
self.image_mb = 0
|
||||
self.randomize = False
|
||||
self.image_mounted = False
|
||||
self.tmp_image_mount = None
|
||||
|
||||
def setup(self):
|
||||
|
||||
self.image_mb = self.metadata['image_mb']
|
||||
if self.metadata.get('image_randomize') == "yes":
|
||||
self.randomize = True
|
||||
|
||||
sts, loop_device = molecule.utils.exec_cmd_get_status_output(
|
||||
[ImageHandler.LOSETUP_EXEC, "-f"])
|
||||
if sts != 0:
|
||||
# ouch
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("setup hook failed"), _("cannot setup loop device"),
|
||||
)
|
||||
)
|
||||
return sts
|
||||
try:
|
||||
self._tmp_loop_device_fd, self.tmp_loop_device_file = \
|
||||
tempfile.mkstemp(prefix = "molecule", dir = self._config['tmp_dir'])
|
||||
except (OSError, IOError,) as err:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("setup hook failed"), _("cannot create temporary file"),
|
||||
)
|
||||
)
|
||||
return 1
|
||||
|
||||
# bind loop device
|
||||
args = [ImageHandler.LOSETUP_EXEC, loop_device,
|
||||
self.tmp_loop_device_file]
|
||||
rc = molecule.utils.exec_cmd(args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("setup hook failed"), _("cannot bind loop device"),
|
||||
)
|
||||
)
|
||||
return 1
|
||||
self.loop_device = loop_device
|
||||
|
||||
self.tmp_image_mount = molecule.utils.mkdtemp()
|
||||
|
||||
# setup metadata for next phases
|
||||
self.metadata['ImageHandler_loop_device_file'] = \
|
||||
self.tmp_loop_device_file
|
||||
self.metadata['ImageHandler_tmp_image_mount'] = self.tmp_image_mount
|
||||
self.metadata['ImageHandler_loop_device'] = self.loop_device
|
||||
self.metadata['ImageHandler_kill_loop_device'] = self._kill_loop_device
|
||||
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("executing pre_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# run pre image script
|
||||
exec_script = self.metadata.get('pre_image_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['TMP_IMAGE_PATH'] = self.tmp_loop_device_file
|
||||
env['LOOP_DEVICE'] = self.loop_device
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("pre image hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def _fill_image_file(self):
|
||||
"""
|
||||
Fill image file (using _tmp_loop_device_fd) with either zeroes or
|
||||
random data of image_mb size.
|
||||
|
||||
@raises IOError: if space is not enough
|
||||
@raises OSError: well, sorry
|
||||
"""
|
||||
if self.randomize:
|
||||
self._output.output("[%s|%s] %s => %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("generating random base image file"),
|
||||
self.tmp_loop_device_file,
|
||||
)
|
||||
)
|
||||
else:
|
||||
self._output.output("[%s|%s] %s => %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("generating zeroed base image file"),
|
||||
self.tmp_loop_device_file,
|
||||
)
|
||||
)
|
||||
|
||||
image_mb = self.image_mb
|
||||
loop_f = os.fdopen(self._tmp_loop_device_fd, "wb")
|
||||
self._tmp_loop_device_fd = None
|
||||
arr = None
|
||||
mb_bytes = ImageHandler.MB_IN_BYTES
|
||||
while image_mb > 0:
|
||||
image_mb -= 1
|
||||
if self.randomize:
|
||||
arr = array.array('c', os.urandom(mb_bytes))
|
||||
else:
|
||||
arr = array.array('c', chr(0)*mb_bytes)
|
||||
arr.tofile(loop_f)
|
||||
|
||||
del arr
|
||||
gc.collect()
|
||||
# file self._tmp_loop_device_fd is closed here.
|
||||
# no more writes needed
|
||||
loop_f.flush()
|
||||
loop_f.close()
|
||||
|
||||
# last but not least, tell the loop device that the file size changed
|
||||
args = [ImageHandler.LOSETUP_EXEC, "-c", self.loop_device]
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(args),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("image file resize failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
def run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("run hook called"),
|
||||
)
|
||||
)
|
||||
|
||||
# fill image file
|
||||
try:
|
||||
rc = self._fill_image_file()
|
||||
if rc != 0:
|
||||
return rc
|
||||
except (IOError, OSError) as err:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("image file generation failed"), err,
|
||||
)
|
||||
)
|
||||
return 1
|
||||
|
||||
# format image file
|
||||
image_formatter = self.metadata.get('image_formatter',
|
||||
ImageHandler.DEFAULT_IMAGE_FORMATTER)
|
||||
formatter_args = image_formatter + [self.loop_device]
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(formatter_args),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(formatter_args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("image formatter hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
# mount image file
|
||||
mounter = self.metadata.get('image_mounter',
|
||||
ImageHandler.DEFAULT_IMAGE_MOUNTER)
|
||||
mount_args = mounter + [self.loop_device, self.tmp_image_mount]
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(mount_args),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(mount_args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("image mount failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
self.image_mounted = True
|
||||
|
||||
return 0
|
||||
|
||||
def post_run(self):
|
||||
""" Nothing to do """
|
||||
return 0
|
||||
|
||||
def _kill_loop_device(self, preserve_loop_device_file = False):
|
||||
|
||||
kill_rc = 0
|
||||
if self.image_mounted:
|
||||
umounter = self.metadata.get('image_umounter',
|
||||
ImageHandler.DEFAULT_IMAGE_UMOUNTER)
|
||||
args = umounter + [self.tmp_image_mount]
|
||||
rc = molecule.utils.exec_cmd(args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("unable to umount loop device"), self.loop_device,
|
||||
)
|
||||
)
|
||||
kill_rc = rc
|
||||
else:
|
||||
self.image_mounted = False
|
||||
|
||||
if self.tmp_image_mount is not None:
|
||||
try:
|
||||
os.rmdir(self.tmp_image_mount)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# kill loop device
|
||||
if self.loop_device is not None:
|
||||
rc = molecule.utils.exec_cmd([ImageHandler.LOSETUP_EXEC, "-d",
|
||||
self.loop_device])
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("unable to kill loop device"), self.loop_device,
|
||||
)
|
||||
)
|
||||
kill_rc = rc
|
||||
else:
|
||||
self.loop_device = None
|
||||
|
||||
if (self.tmp_loop_device_file is not None) and \
|
||||
(not preserve_loop_device_file):
|
||||
try:
|
||||
os.remove(self.tmp_loop_device_file)
|
||||
self.tmp_loop_device_file = None
|
||||
except OSError as err:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("unable to remove temp. loop device file"),
|
||||
err,
|
||||
)
|
||||
)
|
||||
kill_rc = 1
|
||||
|
||||
return kill_rc
|
||||
|
||||
def kill(self, success = True):
|
||||
|
||||
# kill tmp files
|
||||
if self._tmp_loop_device_fd is not None:
|
||||
try:
|
||||
os.close(self._tmp_loop_device_fd)
|
||||
except IOError as err:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("unable to close temp fd"), err,
|
||||
)
|
||||
)
|
||||
self._tmp_loop_device_fd = None
|
||||
|
||||
if not success:
|
||||
env = os.environ.copy()
|
||||
env["LOOP_DEVICE"] = self.loop_device
|
||||
self._run_error_script(None, None, None, env = env)
|
||||
self._kill_loop_device()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class ImageIsoUnpackHandler(RemasterIsoUnpackHandler):
|
||||
|
||||
def setup(self):
|
||||
|
||||
unpack_prefix = molecule.utils.mkdtemp(suffix = "chroot")
|
||||
|
||||
self.metadata['chroot_tmp_dir'] = unpack_prefix
|
||||
self.metadata['chroot_unpack_path'] = \
|
||||
self.metadata['ImageHandler_tmp_image_mount']
|
||||
self.dest_root = os.path.join(unpack_prefix, "cdroot")
|
||||
|
||||
# setup upcoming new chroot path
|
||||
self.chroot_dir = self.metadata['chroot_unpack_path']
|
||||
# explicitly set this to None
|
||||
self.metadata['cdroot_path'] = None
|
||||
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
|
||||
self._output.output("[%s|%s] %s: %s => %s" % (
|
||||
blue("ImageIsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("iso unpacker running"), self.tmp_squash_mount,
|
||||
self.metadata['chroot_unpack_path'],
|
||||
)
|
||||
)
|
||||
|
||||
def dorm():
|
||||
if self.metadata['chroot_tmp_dir'] is not None:
|
||||
shutil.rmtree(self.metadata['chroot_tmp_dir'], True)
|
||||
|
||||
# copy data into chroot, in our case, destination dir already
|
||||
# exists, so copy_dir() is a bit tricky
|
||||
try:
|
||||
rc = molecule.utils.copy_dir_existing_dest(self.tmp_squash_mount,
|
||||
self.metadata['chroot_unpack_path'])
|
||||
except:
|
||||
dorm()
|
||||
raise
|
||||
|
||||
if rc != 0:
|
||||
dorm()
|
||||
|
||||
return rc
|
||||
|
||||
def kill(self, success = True):
|
||||
# ImageHandler sets this
|
||||
if not success:
|
||||
self.metadata['ImageHandler_kill_loop_device']()
|
||||
RemasterIsoUnpackHandler.kill(self, success = success)
|
||||
|
||||
# we don't need the whole dir
|
||||
tmp_dir = self.metadata['chroot_tmp_dir']
|
||||
if os.path.isdir(tmp_dir):
|
||||
try:
|
||||
shutil.rmtree(tmp_dir, True)
|
||||
except (shutil.Error, OSError,):
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageIsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("unable to remove temp. dir"), tmp_dir,
|
||||
)
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class ImageChrootHandler(RemasterChrootHandler):
|
||||
|
||||
def setup(self):
|
||||
# to make superclass working
|
||||
self.source_dir = self.metadata['chroot_unpack_path']
|
||||
self.dest_dir = self.source_dir
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ImageChrootHandler"),
|
||||
darkred(self.spec_name), _("executing kill"),
|
||||
)
|
||||
)
|
||||
if not success:
|
||||
env = os.environ.copy()
|
||||
loop_device = self.metadata.get('ImageHandler_loop_device')
|
||||
if loop_device is not None:
|
||||
env["LOOP_DEVICE"] = loop_device
|
||||
self._run_error_script(self.source_dir, self.dest_dir, None,
|
||||
env = env)
|
||||
self.metadata['ImageHandler_kill_loop_device']()
|
||||
return 0
|
||||
|
||||
|
||||
class FinalImageHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
MD5_EXT = ".md5"
|
||||
IMAGE_EXT = ".img"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
self._loop_device_killed = False
|
||||
self._loop_device_file_removed = False
|
||||
|
||||
def setup(self):
|
||||
# ImageHandler sets this
|
||||
self.tmp_loop_device_file = \
|
||||
self.metadata['ImageHandler_loop_device_file']
|
||||
# umount all, but don't remove our loop device file
|
||||
kill_rc = self.metadata['ImageHandler_kill_loop_device'](
|
||||
preserve_loop_device_file = True)
|
||||
if kill_rc:
|
||||
self._loop_device_killed = True
|
||||
|
||||
image_name = self.metadata.get('image_name',
|
||||
os.path.basename(self.metadata['source_iso']) + \
|
||||
FinalImageHandler.IMAGE_EXT)
|
||||
self.dest_path = os.path.join(
|
||||
self.metadata['destination_image_directory'], image_name)
|
||||
|
||||
dest_path_dir = os.path.dirname(self.dest_path)
|
||||
if (not os.path.lexists(dest_path_dir)) and \
|
||||
(not os.path.isdir(dest_path_dir)):
|
||||
os.makedirs(dest_path_dir, 0o755)
|
||||
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
""" Nothing to do """
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
self._output.output("[%s|%s] %s: %s %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("run hook called"), _("moving image file to destination"),
|
||||
self.dest_path,
|
||||
)
|
||||
)
|
||||
try:
|
||||
os.rename(self.tmp_loop_device_file, self.dest_path)
|
||||
except OSError as err:
|
||||
if err.errno != errno.EXDEV:
|
||||
raise
|
||||
# cannot move atomically, fallback to shutil.move
|
||||
try:
|
||||
shutil.move(self.tmp_loop_device_file, self.dest_path)
|
||||
except shutil.Error:
|
||||
raise
|
||||
self._loop_device_file_removed = True
|
||||
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("built image"), self.dest_path,
|
||||
)
|
||||
)
|
||||
if os.path.isfile(self.dest_path) and os.access(self.dest_path, os.R_OK):
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("generating md5 for"), self.dest_path,
|
||||
)
|
||||
)
|
||||
digest = molecule.utils.md5sum(self.dest_path)
|
||||
md5file = self.dest_path + FinalImageHandler.MD5_EXT
|
||||
with open(md5file, "w") as f:
|
||||
f.write("%s %s\n" % (digest,
|
||||
os.path.basename(self.dest_path),))
|
||||
f.flush()
|
||||
|
||||
def post_run(self):
|
||||
# run post tar script
|
||||
exec_script = self.metadata.get('post_image_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['IMAGE_PATH'] = self.dest_path
|
||||
env['IMAGE_CHECKSUM_PATH'] = self.dest_path + \
|
||||
FinalImageHandler.MD5_EXT
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("post image hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
if not success:
|
||||
if not (self._loop_device_file_removed and \
|
||||
self._loop_device_killed):
|
||||
self.metadata['ImageHandler_kill_loop_device']()
|
||||
""" Nothing to do """
|
||||
return 0
|
||||
|
||||
class IsoToImageSpec(GenericSpec):
|
||||
|
||||
PLUGIN_API_VERSION = 0
|
||||
|
||||
@staticmethod
|
||||
def execution_strategy():
|
||||
return "iso_to_image"
|
||||
|
||||
def vital_parameters(self):
|
||||
return [
|
||||
"source_iso",
|
||||
"destination_image_directory",
|
||||
"image_mb",
|
||||
]
|
||||
|
||||
def parser_data_path(self):
|
||||
return {
|
||||
'execution_strategy': {
|
||||
'cb': self.ne_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'prechroot': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_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_iso': {
|
||||
'cb': self.valid_path_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'image_name': {
|
||||
'cb': self.ne_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'image_formatter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'image_mounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'image_umounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'image_mb': {
|
||||
'cb': self.valid_integer,
|
||||
've': self.ve_integer_converter,
|
||||
},
|
||||
'image_randomize': {
|
||||
'cb': self.valid_ascii,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'error_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_chroot_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'inner_chroot_script': {
|
||||
'cb': self.valid_path_string_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'inner_chroot_script_after': {
|
||||
'cb': self.valid_path_string_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_chroot_script_after': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'destination_image_directory': {
|
||||
'cb': self.valid_dir,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'pre_image_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'post_image_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'iso_mounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'iso_umounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'squash_mounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'squash_umounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'custom_packages_remove_cmd': {
|
||||
'cb': self.valid_ascii,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'custom_packages_add_cmd': {
|
||||
'cb': self.valid_ascii,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'packages_to_remove': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_comma_sep_list,
|
||||
},
|
||||
'packages_to_add': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_comma_sep_list,
|
||||
},
|
||||
'repositories_update_cmd': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'execute_repositories_update': {
|
||||
'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,
|
||||
},
|
||||
}
|
||||
|
||||
def get_execution_steps(self):
|
||||
return [ImageHandler, ImageIsoUnpackHandler, ImageChrootHandler,
|
||||
FinalImageHandler]
|
||||
@@ -0,0 +1,490 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Molecule Disc Image builder for Sabayon Linux
|
||||
# Copyright (C) 2012 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 shutil
|
||||
import tempfile
|
||||
import errno
|
||||
|
||||
from molecule.i18n import _
|
||||
from molecule.output import blue, darkred
|
||||
from molecule.specs.skel import GenericExecutionStep, GenericSpec
|
||||
|
||||
import molecule.utils
|
||||
|
||||
from .builtin_plugin import BuiltinHandlerMixin
|
||||
|
||||
|
||||
class ChrootHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
self.source_dir = None
|
||||
|
||||
def setup(self):
|
||||
self.source_dir = self.metadata['source_chroot']
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("executing pre_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# run outer chroot script
|
||||
exec_script = self.metadata.get('outer_source_chroot_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['IMAGE_NAME'] = self.metadata['image_name']
|
||||
env['DESTINATION_IMAGE_DIR'] = \
|
||||
self.metadata['destination_image_directory']
|
||||
env['CHROOT_DIR'] = self.source_dir
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("outer chroot hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def post_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("executing post_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# run outer chroot script after
|
||||
exec_script = self.metadata.get('outer_source_chroot_script_after')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['IMAGE_NAME'] = self.metadata['image_name']
|
||||
env['DESTINATION_IMAGE_DIR'] = \
|
||||
self.metadata['destination_image_directory']
|
||||
env['CHROOT_DIR'] = self.source_dir
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("outer chroot hook (after inner) failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
""" Nothing to do """
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("hooks running"),
|
||||
)
|
||||
)
|
||||
|
||||
# run inner chroot script
|
||||
exec_script = self.metadata.get('inner_source_chroot_script')
|
||||
if exec_script:
|
||||
if os.path.isfile(exec_script[0]) and \
|
||||
os.access(exec_script[0], os.R_OK):
|
||||
rc = self._exec_inner_script(exec_script, self.source_dir)
|
||||
if rc != 0:
|
||||
return rc
|
||||
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("hooks completed succesfully"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
class ImageHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
LOSETUP_EXEC = "/sbin/losetup"
|
||||
MB_IN_BYTES = 1024000
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
# init variables
|
||||
self.image_mb = 0
|
||||
self._tmp_image_file_fd = None
|
||||
self._tmp_image_file = None
|
||||
self.source_dir = None
|
||||
|
||||
def setup(self):
|
||||
|
||||
self.source_dir = self.metadata['source_chroot']
|
||||
self.image_mb = self.metadata['image_mb']
|
||||
|
||||
try:
|
||||
self._tmp_image_file_fd, self._tmp_image_file = \
|
||||
tempfile.mkstemp(prefix = "molecule",
|
||||
dir = self._config['tmp_dir'],
|
||||
suffix=".mmc_img")
|
||||
except (OSError, IOError,) as err:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("setup hook failed"), _("cannot create temporary file"),
|
||||
)
|
||||
)
|
||||
return 1
|
||||
|
||||
self.metadata['MmcImageHandler_image_file'] = self._tmp_image_file
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("executing pre_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# run pre image script
|
||||
exec_script = self.metadata.get('pre_image_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['IMAGE_NAME'] = self.metadata['image_name']
|
||||
env['DESTINATION_IMAGE_DIR'] = \
|
||||
self.metadata['destination_image_directory']
|
||||
env['CHROOT_DIR'] = self.source_dir
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("pre image hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("run hook called"),
|
||||
)
|
||||
)
|
||||
|
||||
# run pre image script
|
||||
oexec_script = self.metadata.get('image_generator_script')
|
||||
if oexec_script:
|
||||
exec_script = oexec_script + [
|
||||
self._tmp_image_file, str(self.metadata['image_mb']),
|
||||
self.metadata['source_boot_directory'],
|
||||
self.metadata['source_chroot']]
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATHS_TO_REMOVE'] = ";".join(
|
||||
self.metadata.get('paths_to_remove', []))
|
||||
env['PATHS_TO_EMPTY'] = ";".join(
|
||||
self.metadata.get('paths_to_empty', []))
|
||||
env['RELEASE_STRING'] = self.metadata['release_string']
|
||||
env['RELEASE_VERSION'] = self.metadata['release_version']
|
||||
env['RELEASE_DESC'] = self.metadata['release_desc']
|
||||
env['RELEASE_FILE'] = self.metadata['release_file']
|
||||
env['IMAGE_NAME'] = self.metadata['image_name']
|
||||
env['PACKAGES_TO_ADD'] = " ".join(self.metadata.get('packages_to_add', []))
|
||||
env['PACKAGES_TO_REMOVE'] = " ".join(self.metadata.get('packages_to_remove', []))
|
||||
env['DESTINATION_IMAGE_DIR'] = \
|
||||
self.metadata['destination_image_directory']
|
||||
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("image hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def post_run(self):
|
||||
""" Nothing to run """
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
|
||||
# kill tmp files
|
||||
if self._tmp_image_file_fd is not None:
|
||||
try:
|
||||
os.close(self._tmp_image_file_fd)
|
||||
except IOError as err:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("ImageHandler"), darkred(self.spec_name),
|
||||
_("unable to close temp fd"), err,
|
||||
)
|
||||
)
|
||||
self._tmp_image_file_fd = None
|
||||
|
||||
if not success:
|
||||
env = os.environ.copy()
|
||||
self._run_error_script(None, None, None, env = env)
|
||||
if self._tmp_image_file is not None:
|
||||
os.remove(self._tmp_image_file)
|
||||
self._tmp_image_file = None
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class FinalImageHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
MD5_EXT = ".md5"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
self.image_name = None
|
||||
self.dest_path = None
|
||||
self._tmp_image_file = None
|
||||
self.source_dir = None
|
||||
|
||||
def setup(self):
|
||||
self.source_dir = self.metadata['source_chroot']
|
||||
self._tmp_image_file = self.metadata['MmcImageHandler_image_file']
|
||||
|
||||
self.image_name = self.metadata.get('image_name')
|
||||
self.dest_path = os.path.join(
|
||||
self.metadata['destination_image_directory'],
|
||||
self.image_name)
|
||||
|
||||
dest_path_dir = os.path.dirname(self.dest_path)
|
||||
if (not os.path.lexists(dest_path_dir)) and \
|
||||
(not os.path.isdir(dest_path_dir)):
|
||||
os.makedirs(dest_path_dir, 0o755)
|
||||
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
""" Nothing to do """
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
self._output.output("[%s|%s] %s: %s %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("run hook called"), _("moving image file to destination"),
|
||||
self.dest_path,
|
||||
)
|
||||
)
|
||||
try:
|
||||
os.rename(self._tmp_image_file, self.dest_path)
|
||||
except OSError as err:
|
||||
if err.errno != errno.EXDEV:
|
||||
raise
|
||||
# cannot move atomically, fallback to shutil.move
|
||||
try:
|
||||
shutil.move(self._tmp_image_file, self.dest_path)
|
||||
except shutil.Error:
|
||||
raise
|
||||
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("built image"), self.dest_path,
|
||||
)
|
||||
)
|
||||
if os.path.isfile(self.dest_path) and \
|
||||
os.access(self.dest_path, os.R_OK):
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("generating md5 for"), self.dest_path,
|
||||
)
|
||||
)
|
||||
digest = molecule.utils.md5sum(self.dest_path)
|
||||
md5file = self.dest_path + FinalImageHandler.MD5_EXT
|
||||
with open(md5file, "w") as f:
|
||||
f.write("%s %s\n" % (digest,
|
||||
os.path.basename(self.dest_path),))
|
||||
f.flush()
|
||||
|
||||
def post_run(self):
|
||||
# run post tar script
|
||||
exec_script = self.metadata.get('post_image_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['IMAGE_NAME'] = self.image_name # self.metadata['image_name']
|
||||
env['DESTINATION_IMAGE_DIR'] = self.dest_path
|
||||
# self.metadata['destination_image_directory']
|
||||
env['CHROOT_DIR'] = self.source_dir
|
||||
env['IMAGE_PATH'] = self.dest_path
|
||||
env['IMAGE_CHECKSUM_PATH'] = self.dest_path + \
|
||||
FinalImageHandler.MD5_EXT
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("FinalImageHandler"), darkred(self.spec_name),
|
||||
_("post image hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
if not success:
|
||||
if self._tmp_image_file is not None:
|
||||
if os.path.isfile(self._tmp_image_file):
|
||||
os.remove(self._tmp_image_file)
|
||||
return 0
|
||||
|
||||
class ChrootToMmcImageSpec(GenericSpec):
|
||||
|
||||
PLUGIN_API_VERSION = 0
|
||||
|
||||
@staticmethod
|
||||
def execution_strategy():
|
||||
return "chroot_to_mmc"
|
||||
|
||||
def vital_parameters(self):
|
||||
return [
|
||||
"release_string",
|
||||
"release_version",
|
||||
"release_desc",
|
||||
"release_file",
|
||||
"source_chroot",
|
||||
"source_boot_directory",
|
||||
"destination_image_directory",
|
||||
"image_generator_script",
|
||||
"image_mb",
|
||||
"image_name",
|
||||
]
|
||||
|
||||
def parser_data_path(self):
|
||||
return {
|
||||
'execution_strategy': {
|
||||
'cb': self.ne_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'prechroot': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_source_chroot_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'inner_source_chroot_script': {
|
||||
'cb': self.valid_path_string_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_source_chroot_script_after': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_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,
|
||||
},
|
||||
'image_name': {
|
||||
'cb': self.ne_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'image_mb': {
|
||||
'cb': self.valid_integer,
|
||||
've': self.ve_integer_converter,
|
||||
},
|
||||
'image_generator_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'error_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'destination_image_directory': {
|
||||
'cb': self.valid_dir,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'source_boot_directory': {
|
||||
'cb': self.valid_dir,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'pre_image_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'post_image_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'packages_to_remove': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_comma_sep_list,
|
||||
},
|
||||
'packages_to_add': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_comma_sep_list,
|
||||
},
|
||||
'paths_to_remove': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_path_list,
|
||||
},
|
||||
'paths_to_empty': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_path_list,
|
||||
},
|
||||
}
|
||||
|
||||
def get_execution_steps(self):
|
||||
return [ChrootHandler, ImageHandler, FinalImageHandler]
|
||||
@@ -0,0 +1,505 @@
|
||||
# -*- 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 tempfile
|
||||
import shutil
|
||||
|
||||
from molecule.i18n import _
|
||||
from molecule.output import red, blue, green, purple, darkgreen, \
|
||||
darkred, bold, darkblue, readtext
|
||||
from molecule.specs.skel import GenericExecutionStep, GenericSpec
|
||||
|
||||
import molecule.utils
|
||||
|
||||
from .builtin_plugin import ChrootHandler as BuiltinChrootHandler
|
||||
from .builtin_plugin import CdrootHandler as BuiltinCdrootHandler
|
||||
from .builtin_plugin import IsoHandler as BuiltinIsoHandler
|
||||
from .builtin_plugin import BuiltinHandlerMixin
|
||||
|
||||
class IsoUnpackHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
|
||||
self.tmp_mount = molecule.utils.mkdtemp()
|
||||
self.tmp_squash_mount = molecule.utils.mkdtemp()
|
||||
self.iso_mounted = False
|
||||
self.squash_mounted = False
|
||||
self.metadata['cdroot_path'] = None
|
||||
|
||||
# if you want to subclass, override setup() and tweak these
|
||||
self.chroot_dir = None
|
||||
self.dest_root = None
|
||||
self.metadata['chroot_tmp_dir'] = None
|
||||
self.metadata['chroot_unpack_path'] = None
|
||||
self.metadata['cdroot_path'] = None
|
||||
|
||||
def setup(self):
|
||||
|
||||
# setup chroot unpack dir
|
||||
# can't use /tmp because it could be mounted with "special" options
|
||||
unpack_prefix = molecule.utils.mkdtemp(suffix = "chroot")
|
||||
self.metadata['chroot_tmp_dir'] = unpack_prefix
|
||||
self.metadata['chroot_unpack_path'] = os.path.join(unpack_prefix,
|
||||
"root")
|
||||
|
||||
# setup upcoming new chroot path
|
||||
self.dest_root = os.path.join(unpack_prefix, "cdroot")
|
||||
self.chroot_dir = self.metadata['chroot_unpack_path']
|
||||
self.metadata['cdroot_path'] = self.dest_root
|
||||
|
||||
def pre_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("executing pre_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# setup paths
|
||||
self.iso_image = self.metadata['source_iso']
|
||||
|
||||
# mount
|
||||
mounter = self.metadata.get('iso_mounter', self._config['iso_mounter'])
|
||||
mount_args = mounter + [self.iso_image, self.tmp_mount]
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(mount_args),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(mount_args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("iso mount failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
# copy iso content over, including squashfs yeah, it will be
|
||||
# replaced later on
|
||||
# this is mandatory and used to make iso recreation easier
|
||||
rc = molecule.utils.copy_dir(self.tmp_mount, self.dest_root)
|
||||
if rc != 0:
|
||||
return rc
|
||||
|
||||
self.iso_mounted = True
|
||||
|
||||
# mount squash
|
||||
mounter = self.metadata.get('squash_mounter',
|
||||
self._config['squash_mounter'])
|
||||
squash_file = os.path.join(self.tmp_mount,
|
||||
self._config['chroot_compressor_output_file'])
|
||||
mount_args = mounter + [squash_file, self.tmp_squash_mount]
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(mount_args),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(mount_args)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("squash mount failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
self.squash_mounted = True
|
||||
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
|
||||
self._output.output("[%s|%s] %s: %s => %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("iso unpacker running"), self.tmp_squash_mount,
|
||||
self.metadata['chroot_unpack_path'],
|
||||
)
|
||||
)
|
||||
|
||||
def dorm():
|
||||
if self.metadata['chroot_tmp_dir'] is not None:
|
||||
shutil.rmtree(self.metadata['chroot_tmp_dir'], True)
|
||||
|
||||
# create chroot path
|
||||
try:
|
||||
rc = molecule.utils.copy_dir(self.tmp_squash_mount,
|
||||
self.metadata['chroot_unpack_path'])
|
||||
except:
|
||||
dorm()
|
||||
raise
|
||||
|
||||
if rc != 0:
|
||||
dorm()
|
||||
|
||||
return rc
|
||||
|
||||
def post_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("executing post_run"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("executing kill"),
|
||||
)
|
||||
)
|
||||
|
||||
if not success:
|
||||
self._run_error_script(None, self.chroot_dir, self.dest_root)
|
||||
|
||||
rc = 0
|
||||
if self.squash_mounted:
|
||||
umounter = self.metadata.get('squash_umounter',
|
||||
self._config['squash_umounter'])
|
||||
args = umounter + [self.tmp_squash_mount]
|
||||
rc = molecule.utils.exec_cmd(args)
|
||||
|
||||
if rc == 0:
|
||||
try:
|
||||
os.rmdir(self.tmp_squash_mount)
|
||||
except OSError:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("unable to remove temp. dir"), self.tmp_squash_mount,
|
||||
)
|
||||
)
|
||||
|
||||
rc = 0
|
||||
if self.iso_mounted:
|
||||
umounter = self.metadata.get('iso_umounter',
|
||||
self._config['iso_umounter'])
|
||||
args = umounter + [self.tmp_mount]
|
||||
rc = molecule.utils.exec_cmd(args)
|
||||
|
||||
if rc == 0:
|
||||
try:
|
||||
os.rmdir(self.tmp_mount)
|
||||
except OSError:
|
||||
# if not empty, skip
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("unable to remove temp. dir"), self.tmp_mount,
|
||||
)
|
||||
)
|
||||
|
||||
if not success:
|
||||
tmp_dir = self.metadata['chroot_tmp_dir']
|
||||
if tmp_dir is not None:
|
||||
try:
|
||||
shutil.rmtree(tmp_dir, True)
|
||||
except (shutil.Error, OSError,):
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("IsoUnpackHandler"), darkred(self.spec_name),
|
||||
_("unable to remove temp. dir"), tmp_dir,
|
||||
)
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class ChrootHandler(BuiltinChrootHandler):
|
||||
|
||||
def setup(self):
|
||||
# to make superclass working
|
||||
self.source_dir = self.metadata['chroot_unpack_path']
|
||||
self.dest_dir = self.source_dir
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
BuiltinChrootHandler.kill(self, success = success)
|
||||
if not success:
|
||||
try:
|
||||
shutil.rmtree(self.metadata['chroot_tmp_dir'], True)
|
||||
except (shutil.Error, OSError,):
|
||||
pass
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
|
||||
packages_to_add = self.metadata.get('packages_to_add', [])
|
||||
if packages_to_add:
|
||||
|
||||
# update repos first?
|
||||
do_update = self.metadata.get('execute_repositories_update', 'no')
|
||||
if do_update == 'yes':
|
||||
update_cmd = self.metadata.get('repositories_update_cmd',
|
||||
self._config['pkgs_updater'])
|
||||
try:
|
||||
rc = molecule.utils.exec_chroot_cmd(update_cmd,
|
||||
self.source_dir,
|
||||
pre_chroot = self.metadata.get('prechroot', []))
|
||||
except:
|
||||
molecule.utils.kill_chroot_pids(self.source_dir,
|
||||
sleep = True)
|
||||
raise
|
||||
if rc != 0:
|
||||
molecule.utils.kill_chroot_pids(self.source_dir,
|
||||
sleep = True)
|
||||
return rc
|
||||
|
||||
rc = BuiltinChrootHandler.run(self)
|
||||
if rc != 0:
|
||||
return rc
|
||||
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("ChrootHandler"), darkred(self.spec_name),
|
||||
_("hooks running"),
|
||||
)
|
||||
)
|
||||
|
||||
packages_to_add = self.metadata.get('packages_to_add', [])
|
||||
if packages_to_add:
|
||||
|
||||
add_cmd = self.metadata.get('custom_packages_add_cmd',
|
||||
self._config['pkgs_adder'])
|
||||
args = add_cmd + packages_to_add
|
||||
try:
|
||||
rc = molecule.utils.exec_chroot_cmd(args,
|
||||
self.source_dir,
|
||||
pre_chroot = self.metadata.get('prechroot', []))
|
||||
except:
|
||||
molecule.utils.kill_chroot_pids(self.source_dir,
|
||||
sleep = True)
|
||||
raise
|
||||
if rc != 0:
|
||||
molecule.utils.kill_chroot_pids(self.source_dir,
|
||||
sleep = True)
|
||||
return rc
|
||||
|
||||
packages_to_remove = self.metadata.get('packages_to_remove', [])
|
||||
if packages_to_remove:
|
||||
rm_cmd = self.metadata.get('custom_packages_remove_cmd',
|
||||
self._config['pkgs_remover'])
|
||||
args = rm_cmd + packages_to_remove
|
||||
try:
|
||||
rc = molecule.utils.exec_chroot_cmd(args,
|
||||
self.source_dir,
|
||||
pre_chroot = self.metadata.get('prechroot', []))
|
||||
except:
|
||||
molecule.utils.kill_chroot_pids(self.source_dir,
|
||||
sleep = True)
|
||||
raise
|
||||
if rc != 0:
|
||||
molecule.utils.kill_chroot_pids(self.source_dir,
|
||||
sleep = True)
|
||||
return rc
|
||||
|
||||
# run inner chroot script after pkgs handling
|
||||
exec_script = self.metadata.get('inner_chroot_script_after')
|
||||
if exec_script:
|
||||
if os.path.isfile(exec_script[0]) and \
|
||||
os.access(exec_script[0], os.R_OK):
|
||||
rc = self._exec_inner_script(exec_script, self.source_dir)
|
||||
if rc != 0:
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class CdrootHandler(BuiltinCdrootHandler):
|
||||
|
||||
def setup(self):
|
||||
self.dest_root = self.metadata['cdroot_path']
|
||||
self.source_chroot = self.metadata['chroot_unpack_path']
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
BuiltinCdrootHandler.kill(self, success = success)
|
||||
if not success:
|
||||
try:
|
||||
shutil.rmtree(self.metadata['chroot_tmp_dir'], True)
|
||||
except (shutil.Error, OSError,):
|
||||
pass
|
||||
return 0
|
||||
|
||||
class IsoHandler(BuiltinIsoHandler):
|
||||
|
||||
def setup(self):
|
||||
# cdroot dir
|
||||
self.source_path = self.metadata['cdroot_path']
|
||||
dest_iso_filename = self.metadata.get('destination_iso_image_name',
|
||||
"remaster_" + os.path.basename(self.metadata['source_iso']))
|
||||
self.dest_iso = os.path.join(self.metadata['destination_iso_directory'],
|
||||
dest_iso_filename)
|
||||
self.iso_title = self.metadata.get('iso_title', 'Molecule remaster')
|
||||
self.source_chroot = self.metadata['chroot_unpack_path']
|
||||
self.chroot_dir = self.source_chroot
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
BuiltinIsoHandler.kill(self, success = success)
|
||||
try:
|
||||
shutil.rmtree(self.metadata['chroot_tmp_dir'], True)
|
||||
except (shutil.Error, OSError,):
|
||||
pass
|
||||
return 0
|
||||
|
||||
|
||||
class RemasterSpec(GenericSpec):
|
||||
|
||||
PLUGIN_API_VERSION = 0
|
||||
|
||||
@staticmethod
|
||||
def execution_strategy():
|
||||
return "iso_remaster"
|
||||
|
||||
def vital_parameters(self):
|
||||
return [
|
||||
"source_iso",
|
||||
"destination_iso_directory",
|
||||
]
|
||||
|
||||
def parser_data_path(self):
|
||||
return {
|
||||
'execution_strategy': {
|
||||
'cb': self.ne_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'prechroot': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_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_iso': {
|
||||
'cb': self.valid_path_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'iso_title': {
|
||||
'cb': self.ne_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'error_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_chroot_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'inner_chroot_script': {
|
||||
'cb': self.valid_path_string_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'inner_chroot_script_after': {
|
||||
'cb': self.valid_path_string_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_chroot_script_after': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'extra_mksquashfs_parameters': {
|
||||
'cb': self.always_valid,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'extra_mkisofs_parameters': {
|
||||
'cb': self.always_valid,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'pre_iso_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'post_iso_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'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,
|
||||
},
|
||||
'iso_mounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'iso_umounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'squash_mounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'squash_umounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'merge_livecd_root': {
|
||||
'cb': self.valid_dir,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'custom_packages_remove_cmd': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'custom_packages_add_cmd': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'packages_to_remove': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_comma_sep_list,
|
||||
},
|
||||
'packages_to_add': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_comma_sep_list,
|
||||
},
|
||||
'repositories_update_cmd': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'execute_repositories_update': {
|
||||
'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,
|
||||
},
|
||||
}
|
||||
|
||||
def get_execution_steps(self):
|
||||
return [IsoUnpackHandler, ChrootHandler, CdrootHandler, IsoHandler]
|
||||
@@ -0,0 +1,307 @@
|
||||
# -*- 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 shutil
|
||||
|
||||
from molecule.i18n import _
|
||||
from molecule.output import blue, darkred
|
||||
from molecule.specs.skel import GenericExecutionStep, GenericSpec
|
||||
|
||||
from .builtin_plugin import BuiltinHandlerMixin
|
||||
from .remaster_plugin import IsoUnpackHandler, ChrootHandler
|
||||
import molecule.utils
|
||||
|
||||
SUPPORTED_COMPRESSION_METHODS = ["bz2", "gz"]
|
||||
|
||||
class TarHandler(GenericExecutionStep, BuiltinHandlerMixin):
|
||||
|
||||
_TAR_EXEC = "/bin/tar"
|
||||
_TAR_COMP_METHODS = {
|
||||
"gz": "z",
|
||||
"bz2": "j",
|
||||
}
|
||||
MD5_EXT = ".md5"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
GenericExecutionStep.__init__(self, *args, **kwargs)
|
||||
|
||||
def _get_tar_comp_method(self):
|
||||
comp_method = self.metadata.get('compression_method', "gz")
|
||||
return TarHandler._TAR_COMP_METHODS[comp_method]
|
||||
|
||||
def setup(self):
|
||||
# setup compression method, default is gz
|
||||
tar_name = self.metadata.get('tar_name',
|
||||
os.path.basename(self.metadata['source_iso']) + ".tar." + \
|
||||
self.metadata.get('compression_method', "gz"))
|
||||
self.dest_path = os.path.join(
|
||||
self.metadata['destination_tar_directory'], tar_name)
|
||||
self.chroot_path = self.metadata['chroot_unpack_path']
|
||||
return 0
|
||||
|
||||
def pre_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("TarHandler"), darkred(self.spec_name),
|
||||
_("executing pre_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# run pre tar script
|
||||
exec_script = self.metadata.get('pre_tar_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['CHROOT_DIR'] = self.chroot_path
|
||||
env['TAR_PATH'] = self.dest_path
|
||||
env['TAR_CHECKSUM_PATH'] = self.dest_path + \
|
||||
TarHandler.MD5_EXT
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("TarHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("TarHandler"), darkred(self.spec_name),
|
||||
_("pre tar hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
self._output.output("[%s|%s] %s => %s" % (
|
||||
blue("TarHandler"), darkred(self.spec_name),
|
||||
_("compressing chroot"),
|
||||
self.chroot_path,
|
||||
)
|
||||
)
|
||||
dest_path_dir = os.path.dirname(self.dest_path)
|
||||
if not os.path.isdir(dest_path_dir) and not \
|
||||
os.path.lexists(dest_path_dir):
|
||||
os.makedirs(dest_path_dir, 0o755)
|
||||
|
||||
current_dir = os.getcwd()
|
||||
# change dir to chroot dir
|
||||
os.chdir(self.chroot_path)
|
||||
|
||||
args = (TarHandler._TAR_EXEC, "cfp" + self._get_tar_comp_method(),
|
||||
self.dest_path, ".", "--atime-preserve", "--numeric-owner")
|
||||
rc = molecule.utils.exec_cmd(args)
|
||||
os.chdir(current_dir)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("TarHandler"), darkred(self.spec_name),
|
||||
_("chroot compression failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("TarHandler"), darkred(self.spec_name),
|
||||
_("generating md5 for"), self.dest_path,
|
||||
)
|
||||
)
|
||||
digest = molecule.utils.md5sum(self.dest_path)
|
||||
md5file = self.dest_path + TarHandler.MD5_EXT
|
||||
with open(md5file, "w") as f:
|
||||
f.write("%s %s\n" % (digest, os.path.basename(self.dest_path),))
|
||||
f.flush()
|
||||
|
||||
return 0
|
||||
|
||||
def post_run(self):
|
||||
self._output.output("[%s|%s] %s" % (
|
||||
blue("TarHandler"), darkred(self.spec_name),
|
||||
_("executing post_run"),
|
||||
)
|
||||
)
|
||||
|
||||
# run post tar script
|
||||
exec_script = self.metadata.get('post_tar_script')
|
||||
if exec_script:
|
||||
env = os.environ.copy()
|
||||
env['CHROOT_DIR'] = self.chroot_path
|
||||
env['TAR_PATH'] = self.dest_path
|
||||
env['TAR_CHECKSUM_PATH'] = self.dest_path + \
|
||||
TarHandler.MD5_EXT
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("TarHandler"), darkred(self.spec_name),
|
||||
_("spawning"), " ".join(exec_script),
|
||||
)
|
||||
)
|
||||
rc = molecule.utils.exec_cmd(exec_script, env = env)
|
||||
if rc != 0:
|
||||
self._output.output("[%s|%s] %s: %s" % (
|
||||
blue("TarHandler"), darkred(self.spec_name),
|
||||
_("post tar hook failed"), rc,
|
||||
)
|
||||
)
|
||||
return rc
|
||||
|
||||
return 0
|
||||
|
||||
def kill(self, success = True):
|
||||
if not success:
|
||||
self._run_error_script(None, self.chroot_path, None)
|
||||
try:
|
||||
shutil.rmtree(self.metadata['chroot_tmp_dir'], True)
|
||||
except (shutil.Error, OSError,):
|
||||
pass
|
||||
return 0
|
||||
|
||||
class IsoToTarSpec(GenericSpec):
|
||||
|
||||
PLUGIN_API_VERSION = 0
|
||||
|
||||
@staticmethod
|
||||
def execution_strategy():
|
||||
return "iso_to_tar"
|
||||
|
||||
def supported_compression_method(self, comp_m):
|
||||
return comp_m in SUPPORTED_COMPRESSION_METHODS
|
||||
|
||||
def vital_parameters(self):
|
||||
return [
|
||||
"source_iso",
|
||||
"destination_tar_directory",
|
||||
]
|
||||
|
||||
def parser_data_path(self):
|
||||
return {
|
||||
'execution_strategy': {
|
||||
'cb': self.ne_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'prechroot': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_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_iso': {
|
||||
'cb': self.valid_path_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'tar_name': {
|
||||
'cb': self.ne_string,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'compression_method': {
|
||||
'cb': self.supported_compression_method,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'error_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_chroot_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'inner_chroot_script': {
|
||||
'cb': self.valid_path_string_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'inner_chroot_script_after': {
|
||||
'cb': self.valid_path_string_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'outer_chroot_script_after': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'destination_tar_directory': {
|
||||
'cb': self.valid_dir,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'pre_tar_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'post_tar_script': {
|
||||
'cb': self.valid_exec_first_list_item,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'iso_mounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'iso_umounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'squash_mounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'squash_umounter': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'custom_packages_remove_cmd': {
|
||||
'cb': self.valid_ascii,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'custom_packages_add_cmd': {
|
||||
'cb': self.valid_ascii,
|
||||
've': self.ve_string_stripper,
|
||||
},
|
||||
'packages_to_remove': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_comma_sep_list,
|
||||
},
|
||||
'packages_to_add': {
|
||||
'cb': self.ne_list,
|
||||
've': self.valid_comma_sep_list,
|
||||
},
|
||||
'repositories_update_cmd': {
|
||||
'cb': self.ne_list,
|
||||
've': self.ve_command_splitter,
|
||||
},
|
||||
'execute_repositories_update': {
|
||||
'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,
|
||||
},
|
||||
}
|
||||
|
||||
def get_execution_steps(self):
|
||||
return [IsoUnpackHandler, ChrootHandler, TarHandler]
|
||||
Reference in New Issue
Block a user