Skip to content
image.py 44.7 KiB
Newer Older
# -*- python -*-
# -*- coding: utf-8 -*-
# Started 10/05/2011 by Seblu <seblu@seblu.net>

'''
Image stuff
'''

import ConfigParser
import cStringIO
import difflib
import json
import re
import shutil
import stat
import subprocess
import tarfile
import time
import installsystems
import installsystems.template as istemplate
import installsystems.tools as istools
from installsystems.printer import *
from installsystems.tools import PipeFile
from installsystems.tarball import Tarball
Seblu's avatar
Seblu committed

class Image(object):
Seblu's avatar
Seblu committed
    '''
    Abstract class of images
    '''
    # format should be a float  X.Y but for compatibility reason it's a string
    # before version 6, it's strict string comparaison
Seblu's avatar
Seblu committed
    extension = ".isimage"
    @staticmethod
    def check_image_name(buf):
Seblu's avatar
Seblu committed
        '''
        Check if @buf is a valid image name
Seblu's avatar
Seblu committed
        '''
        if re.match("^[-_\w]+$", buf) is None:
            raise Exception("Invalid image name %s" % buf)

    @staticmethod
    def check_image_version(buf):
Seblu's avatar
Seblu committed
        '''
        Check if @buf is a valid image version
Seblu's avatar
Seblu committed
        '''
        if re.match("^\d+$", buf) is None:
            raise Exception("Invalid image version %s" % buf)

    @staticmethod
    def compare_versions(v1, v2):
        '''
        For backward compatibility, image class offer a method to compare image versions
        But code is now inside tools
        return istools.compare_versions(v1, v2)
class SourceImage(Image):
Seblu's avatar
Seblu committed
    '''
    Image source manipulation class
    '''
    @classmethod
    def create(cls, path, force=False):
Seblu's avatar
Seblu committed
        '''
        Create an empty source image
        '''
        # check local repository
        if not istools.isfile(path):
            raise NotImplementedError("SourceImage must be local")
        # main path
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        build_path = os.path.join(path, "build")
        parser_path = os.path.join(path, "parser")
        setup_path = os.path.join(path, "setup")
Seblu's avatar
Seblu committed
        payload_path = os.path.join(path, "payload")
        # create base directories
Seblu's avatar
Seblu committed
        arrow("Creating base directories")
Sebastien Luttringer's avatar
Sebastien Luttringer committed
            for d in (path, build_path, parser_path, setup_path, payload_path):
                if not os.path.exists(d) or not os.path.isdir(d):
                    os.mkdir(d)
        except Exception as e:
            raise Exception("Unable to create directory: %s: %s" % (d, e))
        # create example files
Seblu's avatar
Seblu committed
        arrow("Creating examples")
        arrowlevel(1)
        # create dict of file to create
        examples = {}
        # create description example from template
        examples["description"] = {"path": "description",
                                   "content": istemplate.description % {
                "name": "",
                "version": "1",
                "description": "",
                "author": "",
                "is_min_version": installsystems.version}}
        # create changelog example from template
        examples["changelog"] = {"path": "changelog", "content": istemplate.changelog}
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        # create build example from template
        examples["build"] = {"path": "build/01-build.py", "content": istemplate.build}
        # create parser example from template
        examples["parser"] = {"path": "parser/01-parser.py", "content": istemplate.parser}
        # create setup example from template
        examples["setup"] = {"path": "setup/01-setup.py", "content": istemplate.setup}
        for name in examples:
            try:
                arrow("Creating %s example" % name)
                expath = os.path.join(path, examples[name]["path"])
                if not force and os.path.exists(expath):
                    warn("%s already exists. Skipping!" % expath)
                    continue
                open(expath, "w").write(examples[name]["content"])
            except Exception as e:
                raise Exception("Unable to create example file: %s" % e)
            # setting executable rights on files in setup and parser
Seblu's avatar
Seblu committed
            arrow("Setting executable rights on scripts")
            umask = os.umask(0)
            os.umask(umask)
Sebastien Luttringer's avatar
Sebastien Luttringer committed
            for dpath in (build_path, parser_path, setup_path):
                for f in os.listdir(dpath):
Seblu's avatar
Seblu committed
                    istools.chrights(os.path.join(dpath, f), mode=0777 & ~umask)
        except Exception as e:
            raise Exception("Unable to set rights on %s: %s" % (pf, e))
Seblu's avatar
Seblu committed
        arrowlevel(-1)
Seblu's avatar
Seblu committed
    def __init__(self, path):
        # check local repository
        if not istools.isfile(path):
            raise NotImplementedError("SourceImage must be local")
Seblu's avatar
Seblu committed
        Image.__init__(self)
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        self.base_path = os.path.abspath(path)
        for pathtype in ("build", "parser", "setup", "payload"):
            setattr(self, "%s_path" % pathtype, os.path.join(self.base_path, pathtype))
        self.check_source_image()
        self.description = self.parse_description()
        self.changelog = self.parse_changelog()
Seblu's avatar
Seblu committed
        # script tarball path
        self.image_name = "%s-%s%s" % (self.description["name"],
                                       self.description["version"],
                                       self.extension)
Sebastien Luttringer's avatar
Sebastien Luttringer committed
    def check_source_image(self):
Seblu's avatar
Seblu committed
        '''
        Check if we are a valid SourceImage directories
        '''
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        for d in (self.base_path, self.build_path, self.parser_path,
                  self.setup_path, self.payload_path):
            if not os.path.exists(d):
Sebastien Luttringer's avatar
Sebastien Luttringer committed
                raise Exception("Invalid source image: directory %s is missing" % d)
            if not os.path.isdir(d):
                raise Exception("Invalid source image: %s is not a directory" % d)
            if not os.access(d, os.R_OK|os.X_OK):
                raise Exception("Invalid source image: unable to access to %s" % d)
Seblu's avatar
Seblu committed
        if not os.path.exists(os.path.join(self.base_path, "description")):
            raise Exception("Invalid source image: no description file")
Sebastien Luttringer's avatar
Sebastien Luttringer committed
    def build(self, force=False, force_payload=False, check=True, script=True):
Seblu's avatar
Seblu committed
        '''
        Create packaged image
        '''
        # check if free to create script tarball
        if os.path.exists(self.image_name) and force == False:
            raise Exception("Tarball already exists. Remove it before")
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        # check python scripts
Sebastien Luttringer's avatar
Sebastien Luttringer committed
            self.check_scripts(self.build_path)
Seblu's avatar
Seblu committed
            self.check_scripts(self.parser_path)
            self.check_scripts(self.setup_path)
Sebastien Luttringer's avatar
Sebastien Luttringer committed
        # remove list
        rl = set()
        # run build script
        if script:
            rl |= set(self.run_scripts(self.build_path, self.payload_path))
        if force_payload:
            rl |= set(self.select_payloads())
        # remove payloads
        self.remove_payloads(rl)
        # create payload files
        self.create_payloads()
        # generate a json description
        jdesc = self.generate_json_description()
        # creating scripts tarball
Seblu's avatar
Seblu committed
        self.create_image(jdesc)
Seblu's avatar
Seblu committed

    def create_image(self, jdescription):
Seblu's avatar
Seblu committed
        '''
        Create a script tarball in current directory
        '''
        # create tarball
Seblu's avatar
Seblu committed
        arrow("Creating image tarball")
        arrowlevel(1)
        arrow("Name %s" % self.image_name)
Loading full blame...