#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
#
# Univention Corporate Client
#  A tool for creating UCC images
#
# Copyright (C) 2012-2016 Univention GmbH
#
# http://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <http://www.gnu.org/licenses/>.

from optparse import OptionParser
import ConfigParser

import os
import shutil
import string
import subprocess
import sys
import tempfile
import time
import traceback
import datetime
import commands
import univention.config_registry as ucr

configRegistry = ucr.ConfigRegistry()
configRegistry.load()


# Helper function to run a command and give an error message if the command failed
def _run_cmd(cmd_block, fd=sys.stdout, shell=False):
	env = os.environ
	env['DEBIAN_FRONTEND'] = 'noninteractive'

	fd.write('ucc-image cmd: %s\n' % ' '.join(cmd_block))
	fd.flush()
	retcode = subprocess.call(cmd_block, shell=shell, env=env, stdout=fd, stderr=fd)
	if retcode:
		fd.write('The command "%s" failed with the error code %d\n' % (string.join(cmd_block), retcode))
		msg = 'The command "%s" failed with the error code %d' % (string.join(cmd_block), retcode)
		raise RuntimeError(msg)
	return retcode

def _stop_services_in_bootstrap(bootstrap_dir, logfile_fd):
	# Stop all services that may have been started in the chroot
	try:
		cmd_block = ['fuser', '-k', bootstrap_dir]
		_run_cmd(cmd_block, logfile_fd)
	except RuntimeError:
		# ignore: If no process was killed, fuser return code will be 1
		pass

def _mount_in_bootstrap(bootstrap_dir, logfile_fd, mount=True):

	if mount:
                cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'mount', '-t', 'proc', 'proc', 'proc']
                _run_cmd(cmd_block, logfile_fd)
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'mount', '-t', 'sysfs', 'sysfs', 'sys']
		_run_cmd(cmd_block, logfile_fd)
	else:
		for i in ['/proc', '/sys']:
			cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'umount', i]
			_run_cmd(cmd_block, logfile_fd)

def _copy_system_to_image(options, image_file, bootstrap_dir, workingdir, logfile_fd):

	mount_dir = bootstrap_dir + "-img"
	if not os.path.exists(mount_dir):
		os.mkdir(mount_dir)

	_stop_services_in_bootstrap(bootstrap_dir, logfile_fd)
	_mount_in_bootstrap(bootstrap_dir, logfile_fd, mount=False)

	# clean up bootstrap dir
	cleanup_fd = os.path.join(bootstrap_dir, 'cleanup-ucc.sh')
	fd = open(cleanup_fd, 'w')
	fd.write('''#!/bin/sh
		set -e
		set -x
		rm /var/lib/apt/lists/* || true
		apt-get clean

		# Wait for two seconds, otherwise the umount might fail
		sleep 2

		exit 0
		''')

	fd.close()
	os.chmod(cleanup_fd, 0744)
	cmd_block = ['/usr/sbin/chroot', bootstrap_dir, './cleanup-ucc.sh']
	_run_cmd(cmd_block, logfile_fd)
	os.remove(os.path.join(bootstrap_dir, './cleanup-ucc.sh'))

	# mount the image
	cmd_block = ['mount', '-o', 'loop', image_file, mount_dir]
	_run_cmd(cmd_block, logfile_fd)

	# copy the system
	if options.cleanup:
		subprocess.call("mv %s/* %s/" % (bootstrap_dir, mount_dir), shell=True)
	else:
		subprocess.call("cp -a %s/* %s/" % (bootstrap_dir, mount_dir), shell=True)
	subprocess.call("/bin/sync", shell=False)

	# umount the image
	cmd_block = ['/bin/umount', '-l', mount_dir]
	_run_cmd(cmd_block, logfile_fd)

	# Try to remove empty mount directory
	try:
		os.rmdir(mount_dir)
	except OSError:
		pass


# Do some cleanup stuff
def _cleanup(options, workingdir, bootstrap_dir, logfile_fd):

	# this is also done in _copy_system_to_image, but in case
	# the bootstrap fails, we also need to do this here
	try:
		_stop_services_in_bootstrap(bootstrap_dir, logfile_fd)
		_mount_in_bootstrap(bootstrap_dir, logfile_fd, mount=False)
	except RuntimeError:
		pass

	if options.cleanup:
		# Try to remove empty debootstrap directory
		try:
			shutil.rmtree(bootstrap_dir)
		except OSError:
			pass

		cdrom_dir = os.path.join(workingdir, 'cdrom')
		if os.path.exists(cdrom_dir):
			try:
				shutil.rmtree(cdrom_dir)
			except OSError:
				pass

		if _remove_tempdir:
			try:
				shutil.rmtree(workingdir)
			except OSError:
				pass


class Parser(ConfigParser.ConfigParser):
	def get(self, section, option, default=None):
		if not self.has_section(section):
			return default
		if not self.has_option(section, option):
			return default
		return ConfigParser.ConfigParser.get(self, section, option)

	def getboolean(self, section, option, default=False):
		if not self.has_section(section):
			return default
		if not self.has_option(section, option):
			return default
		return ConfigParser.ConfigParser.getboolean(self, section, option)

	def get_mirror(self):
		return self.get('Software', 'mirror', 'http://ucc.software-univention.de/')

	def get_version(self):
		return self.get('General', 'version', 'trusty')

	def get_hostname(self):
		return config.get('General', 'hostname', 'ucc')

	def get_domainname(self):
		return config.get('General', 'domainname', 'local')


def _print_process(msg):
	print '%-65s' % msg,
	sys.stdout.flush()


def _print_done(msg='done'):
	print '%s' % msg
	sys.stdout.flush()

if __name__ == '__main__':

	usage = '''%s [options] -c config_file ''' % sys.argv[0]
	parser = OptionParser(usage=usage)

	parser.add_option("-c", "--conf", action="store", dest="config_file", help="Config file for ucc-image-creator")
	parser.add_option("-C", "--no-cleanup", action="store_false", dest="cleanup", default=True, help="Do not cleanup temporary files")
	parser.add_option("--compress", action="store_true", dest="compress", default=False, help="Use XZ compression for the image file")
	parser.add_option("-t", "--targetdir", action="store", dest="targetdir", help="Copy output files to targetdir")
	parser.add_option("-d", "--description", action="store", dest="description", help="Description for the UCC image")
	parser.add_option("-i", "--imageid", action="store", dest="imageid", help="Image ID, needed to track updated versions of UCC image")
	parser.add_option("-T", "--tempdir-base", action="store", dest="tempdir_base", help="Use a predefined tempdir for tempfile")
	parser.add_option("-b", "--workingdir", action="store", dest="workingdir", help="Use a predefined working dirctory, this is helpful if -C has been used in a previous run")
	parser.add_option("-B", "--skip-bootstrap", action="store_false", dest="bootstrap", default=True, help="Do not call debootstrap again a predefined temp dir, this is helpful if -C has been used in a previous run")
	parser.add_option("-D", "--debian_package_template", action="store", dest="debian_package_template", default="/usr/share/doc/ucc-image-toolkit/example/package-template", help="Use a different package template for the debian package")
	parser.add_option("-l", "--logfile", action="store", dest="logfile", default="/var/log/univention/ucc-image-toolkit.log", help="Logfile")

	(options, args) = parser.parse_args()

	if not options.config_file:
		print 'ERROR: Missing argument -c config_file.'
		print parser.print_help()
		sys.exit(1)

	if not options.description:
		print 'ERROR: Missing argument -d description.'
		print parser.print_help()
		sys.exit(1)

	if not options.imageid:
		print 'ERROR: Missing argument -i imageid.'
		print parser.print_help()
		sys.exit(1)

	if not os.path.exists(options.config_file):
		print 'ERROR: config_file %s was not found.' % options.config_file
		print parser.print_help()
		sys.exit(1)

	exitcode = 0

	config = Parser()
	config.read(options.config_file)

	# State variables, used for cleanup
	_remove_tempdir = False

	_print_process('Creating working directory')

	if options.workingdir:
		workingdir = options.workingdir
	else:
		workingdir = tempfile.mkdtemp(dir=options.tempdir_base)
		_remove_tempdir = True

	if options.targetdir:
		targetdir = options.targetdir
	else:
		targetdir = workingdir
		_remove_tempdir = False 	# Do not remove workingdir if we leave the image there

	if not os.path.exists(targetdir):
		os.mkdir(targetdir)

	_print_done('done (%s)' % workingdir)

	_print_process('Preparing logfile')
	logfile_fd = open(options.logfile, 'a+')
	logfile_fd.write('Started "%s" at %s\n' % (string.join(sys.argv, ' '), time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())))
	_print_done('done (%s)' % options.logfile)

	try:
		# TODO: call hooks

		_print_process('Creating debootstrap image')
		# Create the image
		image_name = config.get('Image', 'name', 'ucc-image.img')
		image_file = os.path.join(workingdir, image_name)
		image_size = float(config.get('Image', 'size', '20'))
		cmd_block = ['dd', 'if=/dev/zero', 'of=%s' % (image_file), 'bs=1M', 'count=1', 'seek=%d' % (image_size * 1024)]
		_run_cmd(cmd_block, logfile_fd)

		fs = config.get('Image', 'filesystem', 'ext4')
		cmd_block = ['mkfs.%s' % fs, '-F', image_file]
		_run_cmd(cmd_block, logfile_fd)

		bootstrap_dir = os.path.join(workingdir, 'bootstrap')
		os.mkdir(bootstrap_dir)
		_print_done()

		_print_process('Installing base system (this may take a while)')
		# Bootstrap the mounted image
		if not config.get('General', 'arch') in ['host', None]:
			arch = '--arch=%s' % config.get('General', 'arch')
		if options.bootstrap:
			cmd_block = ['/usr/sbin/debootstrap', arch, config.get_version(), bootstrap_dir, config.get_mirror()]
			_run_cmd(cmd_block, logfile_fd)
		_print_done()

		# Disable initctl in the chroot environment before installing or updating packages.
		# Calling upstart in the chroot environment /w precise does not work without an upstart outside the chroot env.
		# Bug 31028
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'dpkg-divert', '--local', '--add', '--rename', '/sbin/initctl']
		_run_cmd(cmd_block, logfile_fd)
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'ln', '-s', '/bin/true', '/sbin/initctl']
		_run_cmd(cmd_block, logfile_fd)

		_print_process('Preparing repository settings')
		# Modify sources.list
		sources_list = os.path.join(bootstrap_dir, 'etc/apt/sources.list')
		fd = open(sources_list, 'w')

		parts = ['main', 'restricted']
		for p in ['universe', 'multiverse']:
			if config.getboolean('Software', p, False):
				parts.append(p)

		fd.write('deb %s %s %s\n' % (config.get_mirror(), config.get_version(), string.join(parts)))

		for p in ['updates', 'security', 'backports']:
			if config.getboolean('Software', p, False):
				fd.write('deb %s %s-%s %s\n' % (config.get_mirror(), config.get_version(), p, string.join(parts)))

		if config.get('Software', 'sources_list'):
			fd.write(config.get('Software', 'sources_list'))
			fd.write('\n')

		fd.close()

		_mount_in_bootstrap(bootstrap_dir, logfile_fd, mount=True)

		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'apt-get', 'update']
		_run_cmd(cmd_block, logfile_fd)
		_print_done()

		_print_process('Installing base package updates')
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'apt-get', '-y', '--force-yes', '-u', 'dist-upgrade']
		_run_cmd(cmd_block, logfile_fd)
		_print_done()

		_print_process('Installing univention-config-registry')
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'apt-get', 'install', '--yes', '--force-yes', '--no-install-recommends', 'univention-config', 'bc', 'rsync', 'dosfstools']
		_run_cmd(cmd_block, logfile_fd)
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'ucr', 'set', 'hostname=%s' % config.get_hostname(), 'domainname=%s' % config.get_domainname()]
		_run_cmd(cmd_block, logfile_fd)
		# Bug 30286: Set nameserver 1 during bootstrap, as there is no dns resolver on a memberserver
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'ucr', 'set', 'nameserver1=%s' % configRegistry.get('nameserver1', '')]
		_run_cmd(cmd_block, logfile_fd)

		_print_done()

		# Set root password
		if config.get('General', 'root_password'):
			_print_process('Setting root password')
			cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'usermod', '-p', config.get('General', 'root_password'), 'root']
			_run_cmd(cmd_block, logfile_fd)
			_print_done()

		shutil.copy('/etc/resolv.conf', os.path.join(bootstrap_dir, 'etc'))

		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'mkdir', '-p', '/usr/share/initramfs-tools/scripts/ucc-config']
		_run_cmd(cmd_block, logfile_fd)

		for package in config.get('Software', 'packages', '').split('\n'):
			package = package.split('#')[0]
			package = package.strip()
			if package:
				_print_process('Installing package %s' % package)
				cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'apt-get', 'install', '--yes', '--force-yes'] + package.split(' ')
				_run_cmd(cmd_block, logfile_fd)
				cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'apt-get', 'clean']
				_run_cmd(cmd_block, logfile_fd)
				_print_done()

		for package in config.get('Software', 'packages_no_recommends', '').split('\n'):
			package = package.split('#')[0]
			package = package.strip()
			if package:
				_print_process('Installing package %s' % package)
				cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'apt-get', '--no-install-recommends', 'install', '--yes', '--force-yes', ] + package.split(' ')
				_run_cmd(cmd_block, logfile_fd)
				cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'apt-get', 'clean']
				_run_cmd(cmd_block, logfile_fd)
				_print_done()

		for package in config.get('Software', 'packages_hold', '').split('\n'):
			package = package.split('#')[0]
			package = package.strip()
			if package:
				_print_process('Setting package %s to hold' % package)
				cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'apt-mark', 'hold', ] + package.split(' ')
				_run_cmd(cmd_block, logfile_fd)
				_print_done()

		nameserver1_set = False
		if config.has_section('Configuration'):
			_print_process('Applying UCR settings')
			for line in config.get('Configuration', 'ucr_variables', None).split('\n'):
				if line:
					cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'ucr', 'set', line]
					_run_cmd(cmd_block, logfile_fd)
					if 'nameserver1=' in line:
						nameserver1_set = True

			_print_done()

		# revert initctl divert
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'rm', '/sbin/initctl']
		_run_cmd(cmd_block, logfile_fd)
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'dpkg-divert', '--local', '--remove', '--rename', '/sbin/initctl']
		_run_cmd(cmd_block, logfile_fd)

		# create symlink ldap.secret; this is required to not break joined clients during updates from ucc-1.0 Bug#31024
		# This should still be kept for UCC 2, as users updating from UCC 1 may not have the updated -sync-persistent
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'ln', '-s', '/etc/machine.secret', '/etc/ldap.secret']
		_run_cmd(cmd_block, logfile_fd)

		# Bug 30286: Unset nameserver 1, but only if it was not defined in the image config file
		if not nameserver1_set:
			cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'ucr', 'unset', 'nameserver1']
			_run_cmd(cmd_block, logfile_fd)

		# Write the partition settings into the image, this file will be used by
		# the partition script
		_print_process('Re-creating initrd')
		initrd_config_dir = os.path.join(bootstrap_dir, 'usr/share/initramfs-tools/scripts/ucc-config/')
		if config.has_section('Partitioning'):
			partition_file = os.path.join(initrd_config_dir, 'partition_info')
			fd = open(partition_file, 'w')
			for option in config.options('Partitioning'):
				if config.get('Partitioning', option):
					fd.write('%s="%s"\n' % (option, config.get('Partitioning', option)))
			fd.close()

		if config.get('Image', 'initramfs_modules'):
			fd = open(os.path.join(bootstrap_dir, 'etc/initramfs-tools/modules'), 'a+')
			fd.write(config.get('Image', 'initramfs_modules'))
			fd.close()

		# Re-create the initramfs
		cmd_block = ['/usr/sbin/chroot', bootstrap_dir, 'update-initramfs', '-k', 'all', '-c']
		_run_cmd(cmd_block, logfile_fd)
		_print_done()

		# Copy kernel
		kernel_link = os.path.join(bootstrap_dir, 'vmlinuz')
		try:
			kernel_src = os.path.join(bootstrap_dir, os.readlink(kernel_link).strip('/'))
		except OSError, e:
			kernel_src = kernel_link

		kernel_dest = os.path.join(workingdir, config.get('Image', 'kernel', 'ucc-image.kernel'))
		if os.path.exists(kernel_src):
			shutil.copy(kernel_src, kernel_dest)
		os.chmod(kernel_dest, 0644)

		# Copy initrd
		initrd_link = os.path.join(bootstrap_dir, 'initrd.img')
		try:
			initrd_src = os.path.join(bootstrap_dir, os.readlink(initrd_link).strip('/'))
		except OSError, e:
			initrd_src = initrd_link

		initrd_dest = os.path.join(workingdir, config.get('Image', 'initrd', 'ucc-image.initrd'))
		if os.path.exists(initrd_src):
			shutil.copy(initrd_src, initrd_dest)

		# Create an UMC register file
		joinscript_prefix = "95"
		umc_dest = os.path.join(workingdir, "%s%s.inst" % (joinscript_prefix, image_name))
		umc_dest_fd = open(umc_dest, 'w')
		umc_dest_fd.write('''#! /bin/sh
#
# Join script for UCC image
#
# Copyright (C) 2012-2016 Univention GmbH
#
# http://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <http://www.gnu.org/licenses/>.

VERSION=1

. /usr/share/univention-join/joinscripthelper.lib
joinscript_init

eval "$(ucr shell)"

udm container/cn create "$@" --ignore_exists \
	--position "cn=univention,$ldap_base" \
	--set name="UCC" || die

udm container/cn create "$@" --ignore_exists \
	--position "cn=UCC,cn=univention,$ldap_base" \
	--set name="Images" || die

udm settings/ucc_image create "$@" --ignore_exists \
	--position "cn=Images,cn=UCC,cn=univention,$ldap_base" \
	--set name="%(image)s"

udm settings/ucc_image modify "$@" --ignore_exists \
	--dn "univentionCorporateClientImageName=%(image)s,cn=Images,cn=UCC,cn=univention,$ldap_base" \
	--append server="$hostname.$domainname"

joinscript_save_current_version

exit 0

''' % {'image': image_name})
		umc_dest_fd.close()

		os.chmod(umc_dest, 0755)

		# Create ISO image
		if config.get('Image', 'iso'):
			# TODO: the creation of the ISO image should be done in the chroot environment
			iso_dest = os.path.join(workingdir, config.get('Image', 'iso'))
			cd_dir = os.path.join(workingdir, 'cdrom')
			os.mkdir(cd_dir)

			boot_dir = os.path.join(cd_dir, 'boot')
			shutil.copytree(os.path.join(bootstrap_dir, 'boot'), boot_dir)

			kernel = os.path.join('/', os.readlink(os.path.join(bootstrap_dir, 'vmlinuz')))
			initrd = os.path.join('/', os.readlink(os.path.join(bootstrap_dir, 'initrd.img')))

			grub_cfg = open(os.path.join(boot_dir, 'grub/grub.cfg'), 'w')
			grub_cfg.write('''
set timeout=-1
menuentry 'Univention Corporate Client - Boot from CDROM/USB device (Livesystem)' {
	linux %(kernel)s ro boot=ucc ucc=overlayfs quiet image=%(image)s
	initrd %(initrd)s
}
menuentry 'Univention Corporate Client - Image update check / Initial installation' {
	linux %(kernel)s ro boot=ucc ucc=rollout root=search quiet image=%(image)s
	initrd %(initrd)s
}
menuentry 'Univention Corporate Client - Image installation with forced repartition' {
	linux %(kernel)s ro boot=ucc ucc=rollout root=search quiet image=%(image)s repartition=y
	initrd %(initrd)s
}
menuentry 'Debug Shell' {
	linux %(kernel)s ro init=/bin/sh rootdelay=3
	initrd %(initrd)s
}
			''' % {'kernel': kernel, 'initrd': initrd, 'image': image_name})
			grub_cfg.close()

		# Sync to disk, otherwise not all data is written to the image before the hash is computed
		subprocess.call("/bin/sync", shell=False)

		# copy the boostrap system to the image
		_print_process('Copy system to image')
		_copy_system_to_image(options, image_file, bootstrap_dir, workingdir, logfile_fd)
		_print_done()

		# Create a md5 sum file
		md5_dest = os.path.join(workingdir, "%s.md5" % image_name)
		cmd = 'md5sum "%s" | sed -ne "s| .*||p" > "%s" ' % (image_file, md5_dest)
		retcode = subprocess.call(cmd, shell=True)
		if retcode:
			msg = 'The command "%s" failed with the error code %d' % (cmd, retcode)
			raise RuntimeError(msg)

		# Create ISO image
		if config.get('Image', 'iso'):
			if config.getboolean('Image', 'include_image_on_iso', False):
				shutil.copy(md5_dest, cd_dir)
				shutil.copy(image_file, cd_dir)
			_print_process('Create ISO image')
			cmd_block = ['grub-mkrescue', '-o', iso_dest, cd_dir, '--', '-iso-level 3']
			_run_cmd(' '.join(cmd_block), logfile_fd, shell=True)
			_print_done()

		if exitcode == 0:
			if options.compress:
				print "Compressing image"
                # Use /usr/bin/xz -0 to speed up compression significantly for tests
				subprocess.call("/usr/bin/xz -v " + image_file, shell=True)
				image_name = image_name + ".xz"
				image_file = os.path.join(workingdir, image_name)

			# Create the spec file
			print "Generating spec file"
			spec_dest = os.path.join(workingdir, "%s.spec" % image_name)
			specfd = open(spec_dest, "w")
			specfd.write("title: " + image_name + "\n")

			specfd.write("version: " + str(datetime.date.isoformat(datetime.datetime.now())) + "\n")

			totalsize = os.stat(image_file).st_size + os.stat(md5_dest).st_size + os.stat(kernel_dest).st_size + os.stat(initrd_dest).st_size

			specfd.write("total-size: " + str(totalsize) + "\n")
			specfd.write("description: " + options.description + "\n")
			specfd.write("id: " + options.imageid + "\n")
			specfd.write("file-img: " + os.path.basename(image_name) + "\n")
			specfd.write("file-initrd: " + os.path.basename(initrd_dest) + "\n")
			specfd.write("file-kernel: " + os.path.basename(kernel_dest) + "\n")
			specfd.write("file-md5: " + os.path.basename(md5_dest) + "\n")
			specfd.write("file-reg: " + os.path.basename(umc_dest) + "\n")
			specfd.write("hash-img: " + commands.getoutput("sha256sum " + image_file).split(" ")[0].strip() + "\n")
			specfd.write("hash-initrd: " + commands.getoutput("sha256sum " + initrd_dest).split(" ")[0].strip() + "\n")
			specfd.write("hash-kernel: " + commands.getoutput("sha256sum " + kernel_dest).split(" ")[0].strip() + "\n")
			specfd.write("hash-md5: " + commands.getoutput("sha256sum " + md5_dest).split(" ")[0].strip() + "\n")
			specfd.write("hash-reg: " + commands.getoutput("sha256sum " + umc_dest).split(" ")[0].strip() + "\n")
			specfd.close()

			if not targetdir == workingdir:
				print 'Moving files to %s' % os.path.abspath(targetdir)
				files = [image_file, kernel_dest, initrd_dest, umc_dest, md5_dest, spec_dest]
				if config.get('Image', 'iso'):
					files.append(iso_dest)

				for curr_file in files:
					cmd_block = ['mv', '-f', curr_file, targetdir]
					_run_cmd(cmd_block, logfile_fd)

			print '\nThe image creation was successful: (%s)' % os.path.abspath(targetdir)
			print 'Image                 : %s' % os.path.basename(image_file)
			print 'MD5sum file           : %s' % os.path.basename(md5_dest)
			print 'UMC Image join script : %s' % os.path.basename(umc_dest)
			print 'Kernel                : %s' % os.path.basename(kernel_dest)
			print 'Initrd                : %s' % os.path.basename(initrd_dest)
			if config.get('Image', 'iso'):
				print 'CDROM ISO Image       : %s' % os.path.basename(iso_dest)

	except (RuntimeError, KeyboardInterrupt, Exception), e:
		print '\nERROR: %s (abort)\n' % e
		traceback.print_exc(file=logfile_fd)
		exitcode = 100

	_cleanup(options, workingdir, bootstrap_dir, logfile_fd)

	if exitcode == 100:
		logfile_fd.write('Finished WITH ERROR %s at %s\n' % (string.join(sys.argv, ' '), time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())))
	else:
		logfile_fd.write('Finished successfully %s at %s\n' % (string.join(sys.argv, ' '), time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())))

	logfile_fd.close()

	sys.exit(exitcode)
