1. stacklet
  2. stacklet

Source

stacklet / src / stacklet / task / distro / gentoo.py

# Copyright (C) 2009, dfn@stacklet.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.

# 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.

"""Tasks for creating gentoo-based images.

Please see docstring of each class for usage.

"""
from stacklet.util.objectify import _XO_
from stacklet.util.objectify import *
from stacklet.task.stacktask import *
from stacklet.task.stackerror import *

class _XO_bootstrap(_XO_, StackTask):
    """Bootstrap a gentoo-based image.  Image will be chrootable upon completion
       of this task with network access to a package repository.


    Task Arguments:
    url -- the location to download packages from.
    arch -- the architecture (eg x86)
    release -- the release (eg 2008.0)
    proxy -- (optional) proxyhost[:port]

    """
    _TASK_NAME = 'bootstrap'

    def __init__(self):
        pass

    def process(self, buildContext):
        try:
            try:
                self._buildContext = buildContext
                self.log_info(buildContext, 'entering')
                self.precheck(buildContext)

                env = {'http_proxy': self._proxy}

                if not self.is_empty(self._release):
                  cmd = 'wget -4 -c ' + self._url + 'releases/' + self._arch + '/' + self._release + '/stages/stage3-' + self._arch + '-' + self._release + '.tar.bz2  -P ' + buildContext.mountPoint
                else:
                  stage3 = buildContext.mountPoint + '/stage3.txt'
                  cmd = 'echo -n %sreleases/%s/autobuilds/ > %s' % (self._url, self._arch, stage3)
                  self.exec_command(buildContext, cmd, env=env)
                  cmd = "wget -4 -O - %sreleases/%s/autobuilds/latest-stage3-%s.txt | grep stage3 | tail -n 1 | sed 's/nomultilib-//' >> %s" % (self._url, self._arch, self._arch, stage3)
                  self.exec_command(buildContext, cmd, env=env)
                  cmd = 'wget -4 -c -P %s -i %s' % (buildContext.mountPoint, stage3)

                self.exec_command(buildContext, cmd, env=env)

                cmd = 'tar -xjpf ' + buildContext.mountPoint + '/stage3*.bz2' + ' -C ' + buildContext.mountPoint
                self.exec_command(buildContext, cmd)

                cmd = 'wget -4 -c ' + self._url + '/snapshots/portage-latest.tar.bz2 -P ' + buildContext.mountPoint + '/usr'
                self.exec_command(buildContext, cmd, env=env)

                cmd = 'tar -xjpf ' + buildContext.mountPoint + '/usr/portage*.bz2' + ' -C ' + buildContext.mountPoint + '/usr'
                self.exec_command(buildContext, cmd)

                cmd='cp /etc/resolv.conf ' + buildContext.mountPoint + '/etc/'
                self.exec_command(buildContext, cmd)

            except TaskError, taskErr:
                if taskErr.criticality == TaskError.ABORT_BUILD:
                    raise
                self.log_error(buildContext, taskErr)
        finally:
            self.log_info(buildContext, 'leaving')

    def precheck(self, buildContext):
        self.is_valid_build_context(buildContext)

        self._url = self.get_value('url')
        self._arch = self.get_value('arch')
        self._release = self.get_value('release')
        self._proxy = self.get_value('proxy')

        if self.is_empty(self._url):
            raise FieldError(self._TASK_NAME, TaskError.ABORT_TASK, 'url is required')

        if self.is_empty(self._arch):
            raise FieldError(self._TASK_NAME, TaskError.ABORT_TASK, 'arch is required')


class _XO_package(_XO_, StackTask):
    """Install and/or remove package(s)

    Task Arguments:
    add -- space-delimited list of packages to install
    remove -- space-delimited list of packages to remove
    update -- space-delimited list of packages to update

    """
    _TASK_NAME = 'package'

    def __init__(self):
        pass

    def process(self, buildContext):
        try:
            try:
                self._buildContext = buildContext
                self.log_info(buildContext, 'entering')
                self.precheck(buildContext)

                cmds = list()

                if not self.is_empty(self._update):
                    self.log_info(buildContext, "update " + self._update)
                    if self._update == 'all':
                        cmd = 'emerge --sync'
                        cmds.append(cmd)
                        cmd = 'emerge -uD world'
                        cmds.append(cmd)
                    else:
                        self.log_info(buildContext, "update " + self._update)
                        cmd='emerge -u ' + self._update
                        cmds.append(cmd)

                if not self.is_empty(self._add):
                    self.log_info(buildContext, "add " + self._add)
                    cmd='emerge -u ' + self._add
                    cmds.append(cmd)

                if not self.is_empty(self._remove):
                    self.log_info(buildContext, "remove " + self._remove)
                    cmd='emerge --unmerge ' + self._remove
                    cmds.append(cmd)

                self.exec_command_chroot(buildContext, cmds)

            except TaskError, taskErr:
                if taskErr.criticality == TaskError.ABORT_BUILD:
                    raise
                self.log_error(buildContext, taskErr)
        finally:
            self.log_info(buildContext, 'leaving')

    def precheck(self, buildContext):
        self.is_valid_build_context(buildContext)

        self._add = self.get_value('add')
        self._remove = self.get_value('remove')
        self._update = self.get_value('update')

        if self.is_empty(self._add) and self.is_empty(self._remove) and self.is_empty(self._update):
            raise FieldError(self._TASK_NAME, TaskError.ABORT_TASK, 'add or remove or update is required')


class _XO_service(_XO_, StackTask):
    """Enable and/or disable service(s)

    Task Arguments:
    enable -- space-delimited list of services to enable
    disable -- space-delimited list of services to disable
    """
    _TASK_NAME = 'service'

    def __init__(self):
        pass

    def process(self, buildContext):
        try:
            try:
                self._buildContext = buildContext
                self.log_info(buildContext, 'entering')
                self.precheck(buildContext)
                cmds = list()

                if not self.is_empty(self._enable):
                    self.log_info(buildContext, "enable " + self._enable)
                    for serv in self._enable.split():
                        cmd='rc-update add ' + serv + ' default'
                        cmds.append(cmd)

                if not self.is_empty(self._disable):
                    self.log_info(buildContext, "disable " + self._disable)
                    for serv in self._disable.split():
                        cmd='rc-update del ' + serv + ' default'
                        cmds.append(cmd)

                self.exec_command_chroot(buildContext, cmds)
            except TaskError, taskErr:
                if taskErr.criticality == TaskError.ABORT_BUILD:
                    raise
                self.log_error(buildContext, taskErr)
        finally:
            self.log_info(buildContext, 'leaving')


    def precheck(self, buildContext):
        self.is_valid_build_context(buildContext)

        self._enable = self.get_value('enable')
        self._disable = self.get_value('disable')

        if self.is_empty(self._enable) and self.is_empty(self._disable):
            raise FieldError(self._TASK_NAME, TaskError.ABORT_TASK, 'enable or disable is required')

class _XO_cleanup(_XO_, StackTask):
    """Cleanup log files, /tmp, etc...

    Task Arguments:
    zerofs -- if 'true' the empty space in the image will be zero'ed for better compressibility
    """
    _TASK_NAME = 'cleanup'


    def __init__(self):
        pass

    def process(self, buildContext):
        try:
            try:
                self._buildContext = buildContext
                self.log_info(buildContext, 'entering')
                self.precheck(buildContext)

                cmds = list()
                cmds.append('cd /usr/src/linux/ && make mrproper')
                cmds.append('emerge --unmerge gentoo-sources') 
                cmds.append('rm -f /var/lib/dhcpcd/*')
                cmds.append('rm -f /var/log/*')
                cmds.append('rm -f /var/run/*.pid')
                cmds.append('rm -rf /var/tmp/*')
                cmds.append('rm -f /var/lock/*')
                cmds.append('rm -rf /root/.ssh')
                cmds.append('rm -f /root/.bash_history')
                cmds.append('rm -f /root/.nano_history')
                cmds.append('rm -f /etc/resolv.conf*')
                cmds.append('rm -rf /usr/portage/distfiles/*')
                cmds.append('rm -rf /usr/portage/metadata/cache/*')
                cmds.append('rm -rf /usr/portage/metadata/md5-cache/*')
                cmds.append('rm -rf /var/cache/edb/*')
                cmds.append('rm -rf /var/cache/genkernel/*')
                cmds.append('rm -f /stage3*.bz2')
                cmds.append('rm -f /stage3.txt')
                cmds.append('rm -f /usr/portage-latest.tar.bz2')
                cmds.append('rm -rf /tmp/*')

                self.exec_command_chroot(buildContext, cmds, checkReturnValue=False)

                if self._zerofs == 'true':
                    self.zero_empty_space(buildContext)
            except TaskError, taskErr:
                if taskErr.criticality == TaskError.ABORT_BUILD:
                    raise
                self.log_error(buildContext, taskErr)
        finally:
            self.log_info(buildContext, 'leaving')

    def onerr(self, buildContext, cmd, retval):
        pass

    def precheck(self, buildContext):
        self.is_valid_build_context(buildContext)
        self._zerofs = self.get_value('zerofs')


stacklet.util.objectify._XO_bootstrap = _XO_bootstrap
stacklet.util.objectify._XO_stk_bootstrap = _XO_bootstrap
stacklet.util.objectify._XO_package = _XO_package
stacklet.util.objectify._XO_stk_package = _XO_package
stacklet.util.objectify._XO_service = _XO_service
stacklet.util.objectify._XO_stk_service = _XO_service
stacklet.util.objectify._XO_cleanup = _XO_cleanup
stacklet.util.objectify._XO_stk_cleanup = _XO_cleanup