Skip to content
image.py 44.7 KiB
Newer Older
Seblu's avatar
Seblu committed
            dest = os.path.join(dest, self.filename)
        # try to create leading directories
        elif not os.path.exists(os.path.dirname(dest)):
            istools.mkdir(os.path.dirname(dest))
        # check validity of dest
        if os.path.exists(dest):
            if os.path.isdir(dest):
                raise Exception("Destination %s is a directory" % dest)
            if not force:
                raise Exception("File %s already exists" % dest)
        # Open remote file
        debug("Downloading payload %s from %s" % (self.filename, self.path))
        fs = PipeFile(self.path, progressbar=True)
        # check if announced file size is good
        if fs.size is not None and self.size != fs.size:
            raise Exception("Downloading payload %s failed: Invalid announced size" %
                            self.name)
Seblu's avatar
Seblu committed
        fd = open(dest, "wb")
Seblu's avatar
Seblu committed
        fs.close()
        fd.close()
        # checking download size
        if self.size != fs.read_size:
            raise Exception("Downloading payload %s failed: Invalid size" % self.name)
        if self.md5 != fs.md5:
Seblu's avatar
Seblu committed
            raise Exception("Downloading payload %s failed: Invalid MD5" % self.name)

Seblu's avatar
Seblu committed
    def extract(self, dest, force=False, filelist=None):
        '''
        Extract payload into dest
        filelist is a filter of file in tarball
        force will overwrite existing file if exists
        '''
        try:
            if self.isdir:
                self.extract_tar(dest, force=force, filelist=filelist)
            else:
                self.extract_file(dest, force=force)
        except Exception as e:
            raise Exception("Extracting payload %s failed: %s" % (self.name, e))
Seblu's avatar
Seblu committed

    def extract_tar(self, dest, force=False, filelist=None):
        '''
        Extract a payload which is a tarball.
        This is used mainly to extract payload from a directory
        '''
        # check validity of dest
        if os.path.exists(dest):
            if not os.path.isdir(dest):
                raise Exception("Destination %s is not a directory" % dest)
            if not force and len(os.listdir(dest)) > 0:
                raise Exception("Directory %s is not empty (need force)" % dest)
        else:
Seblu's avatar
Seblu committed
            istools.mkdir(dest)
Seblu's avatar
Seblu committed
        # try to open payload file
            fo = PipeFile(self.path, progressbar=True)
        except Exception as e:
            raise Exception("Unable to open %s" % self.path)
        # check if announced file size is good
        if fo.size is not None and self.size != fo.size:
            raise Exception("Invalid announced size on %s" % self.path)
        # get compressor argv (first to escape file creation if not found)
        a_comp = istools.get_compressor_path(self.compressor, compress=False)
        a_tar = ["tar", "--extract", "--numeric-owner", "--ignore-zeros",
                 "--preserve-permissions", "--directory", dest]
        # add optionnal selected filename for decompression
        if filelist is not None:
            a_tar += filelist
        p_tar = subprocess.Popen(a_tar, shell=False, close_fds=True,
                                 stdin=subprocess.PIPE)
        p_comp = subprocess.Popen(a_comp, shell=False, close_fds=True,
                                  stdin=subprocess.PIPE, stdout=p_tar.stdin)
        # close tar fd
        p_tar.stdin.close()
        # push data into compressor
        fo.consume(p_comp.stdin)
Seblu's avatar
Seblu committed
        fo.close()
        # checking downloaded size
        if self.size != fo.read_size:
            raise Exception("Invalid size")
        # checking downloaded md5
        if self.md5 != fo.md5:
            raise Exception("Invalid MD5")
        # close compressor pipe
        p_comp.stdin.close()
        # check compressor return 0
        if p_comp.wait() != 0:
            raise Exception("Compressor %s return is not zero" % a_comp[0])
        # check tar return 0
        if p_tar.wait() != 0:
            raise Exception("Tar return is not zero")
Seblu's avatar
Seblu committed

    def extract_file(self, dest, force=False):
        '''
        Copy a payload directly to a file
        Check md5 on the fly
        '''
        # if dest is a directory try to create file inside
        if os.path.isdir(dest):
            dest = os.path.join(dest, self.name)
Seblu's avatar
Seblu committed
        # try to create leading directories
        elif not os.path.exists(os.path.dirname(dest)):
            istools.mkdir(os.path.dirname(dest))
Seblu's avatar
Seblu committed
        # check validity of dest
        if os.path.exists(dest):
            if os.path.isdir(dest):
                raise Exception("Destination %s is a directory" % dest)
Seblu's avatar
Seblu committed
            if not force:
                raise Exception("File %s already exists" % dest)
        # get compressor argv (first to escape file creation if not found)
        a_comp = istools.get_compressor_path(self.compressor, compress=False)
        # try to open payload file (source)
Seblu's avatar
Seblu committed
        try:
            f_src = PipeFile(self.path, "r", progressbar=True)
Seblu's avatar
Seblu committed
        except Exception as e:
            raise Exception("Unable to open payload file %s: %s" % (self.path, e))
        # check if announced file size is good
        if f_src.size is not None and self.size != f_src.size:
            raise Exception("Invalid announced size on %s" % self.path)
        # opening destination
Seblu's avatar
Seblu committed
        try:
            f_dst = open(dest, "wb")
Seblu's avatar
Seblu committed
        except Exception as e:
            raise Exception("Unable to open destination file %s: %s" % (dest, e))
        # run compressor process
        p_comp = subprocess.Popen(a_comp, shell=False, close_fds=True,
                                  stdin=subprocess.PIPE, stdout=f_dst)
        # close destination file
Seblu's avatar
Seblu committed
        f_dst.close()
        # push data into compressor
        f_src.consume(p_comp.stdin)
Seblu's avatar
Seblu committed
        f_src.close()
        # checking download size
        if self.size != f_src.read_size:
            raise Exception("Invalid size")
        # checking downloaded md5
        if self.md5 != f_src.md5:
            raise Exception("Invalid MD5")
        # close compressor pipe
        p_comp.stdin.close()
        # check compressor return 0
        if p_comp.wait() != 0:
            raise Exception("Compressor %s return is not zero" % a_comp[0])
Seblu's avatar
Seblu committed
        # settings file orginal rights
        istools.chrights(dest, self.uid, self.gid, self.mode, self.mtime)
class Changelog(dict):
    '''
    Object representing a changelog in memory
    '''
    def __init__(self, data):
        self.verbatim = ""
        self.load(data)

    def load(self, data):
        '''
        Load a changelog file
        '''
        version = None
        lines = data.split("\n")
        for line in lines:
            # ignore empty lines
            if len(line.strip()) == 0:
                continue
            # ignore comments
            if line.lstrip().startswith("#"):
                continue
            # try to match a new version
            m = re.match("\[(\d+)\]", line.lstrip())
            if m is not None:
                version = int(m.group(1))
                self[version] = []
                continue
            # if line are out of a version => invalid format
            if version is None:
                raise Exception("Invalid format: Line outside version")
            # add line to version changelog
            self[version] += [line]
        # save original
        self.verbatim = data

    def show(self, version=None, verbose=False):
        '''
        Show changelog for a given version or all
        '''
        out('#light##yellow#Changelog:#reset#')
        # if no version take the hightest
        if version is None:
            version = max(self)
        # display asked version
        if version in self:
            self._show_version(version)
        # display all version in verbose mode
        if verbose:
            for ver in sorted((k for k in self if k < version), reverse=True):
                self._show_version(ver)

    def _show_version(self, version):
        '''
        Display a version content
        '''
        out('  #yellow#Version:#reset# %s' % version)
        for line in self[version]:
            out("    %s" % line)