From 19c4e26ebc3933045c2832900a4cde174f08fe81 Mon Sep 17 00:00:00 2001 From: Seblu <sebastien.luttringer@smartjog.com> Date: Tue, 31 May 2011 17:53:23 +0200 Subject: [PATCH] Allow chmod et chown, chgroup in repository config --- bin/isrepo | 41 +++++------- installsystems/config.py | 31 +++------ installsystems/database.py | 9 ++- installsystems/repository.py | 121 +++++++++++++++++++++++++++-------- installsystems/tools.py | 33 ++++++++-- 5 files changed, 153 insertions(+), 82 deletions(-) diff --git a/bin/isrepo b/bin/isrepo index c864932..c654fd8 100755 --- a/bin/isrepo +++ b/bin/isrepo @@ -26,27 +26,27 @@ def init(args): '''Create an empty fresh repo tree''' # call init from library try: - Repository.create(args.repo_image, args.repo_data, args.verbose) + Repository.create(args.repo, args.verbose) except Exception as e: - error("init failed: %s." % e) + raise Exception("init failed: %s" % e) def add(args): '''Add a package to repository''' try: - repo = Repository(args.repo_image, args.repo_data, args.verbose) + repo = Repository(args.repo, args.verbose) pkg = PackageImage(args.path, args.verbose) pkg.check_md5() repo.add(pkg) except Exception as e: - error("add failed: %s." % e) + raise Exception("add failed: %s" % e) def delete(args): '''Remove a package from repository''' try: - repo = Repository(args.repo_image, args.repo_data, args.verbose) + repo = Repository(args.repo, args.verbose) repo.delete(args.image_name, args.image_version) except Exception as e: - error("del failed: %s." % e) + raise Exception("del failed: %s" % e) # Top level argument parsing p_main = argparse.ArgumentParser() @@ -60,10 +60,6 @@ p_main.add_argument("-c", "--config", dest="config", type=str, default=None, help="config file path") p_main.add_argument("-r", "--repo-name", dest="repo_name", type=str, default=None, help="repository name") -p_main.add_argument("-I", "--repo-image", dest="repo_image", type=str, - help="image repository path") -p_main.add_argument("-D", "--repo-data", dest="repo_data", type=str, - help="data repository path") subparsers = p_main.add_subparsers() # Init command parser p_init = subparsers.add_parser("init", help=init.__doc__.lower()) @@ -86,21 +82,16 @@ try: args = p_main.parse_args() # load config config = ConfigFile("isrepo", args.config) - # get config repository - c_repo = config.repo(args.repo_name) - # looks for image repository in config, if needed - if args.repo_image is None and c_repo is not None: - args.repo_image = c_repo[0] - # looks for data repository in config, if needed - if args.repo_data is None and c_repo is not None: - args.repo_data = c_repo[1] - # final check of existance - if args.repo_image is None: - raise Exception("image repository missing") - elif args.repo_data is None: - raise Exception("data repository missing") - debug("Image repo: %s" % args.repo_image) - debug("Data repo: %s" % args.repo_data) + # get config repositories + repos = config.repos + if len(repos) == 1: + args.repo = repos[repos.keys()[0]] + elif args.repo_name in repos.keys(): + args.repo = repos[args.repo_name] + else: + raise Exception("No image repository found") + debug("Image repo: %s" % args.repo.image) + debug("Data repo: %s" % args.repo.data) args.func(args) except Exception as e: p_main.print_usage() diff --git a/installsystems/config.py b/installsystems/config.py index 55b7d94..087cacf 100644 --- a/installsystems/config.py +++ b/installsystems/config.py @@ -9,6 +9,7 @@ InstallSystems Configuration files class import os from ConfigParser import RawConfigParser from installsystems.printer import * +from installsystems.repository import RepositoryConfig class ConfigFile(object): '''Configuration class''' @@ -38,17 +39,19 @@ class ConfigFile(object): try: cp = RawConfigParser() cp.read(self.path) + # main configuration if cp.has_section(self.prefix): self._config = dict(cp.items(self.prefix)) cp.remove_section(self.prefix) + # each section is a repository for rep in cp.sections(): - img = data = None - if cp.has_option(rep, "image"): - img = cp.get(rep, "image") - if cp.has_option(rep, "data"): - data = cp.get(rep, "data") - self._repos[rep]= (img, data) + # check if its a repo section + if "image" not in cp.options(rep): + continue + # get all options in repo + self._repos[rep] = RepositoryConfig(rep, **dict(cp.items(rep))) except Exception as e: + raise raise Exception("Unable load file %s: %s" % (self.path, e)) else: debug("No config file found") @@ -87,18 +90,4 @@ class ConfigFile(object): @property def repos(self): '''Get a list of repository available''' - for r in self._repos: - yield (r, self._repos[r][0], self._repos[r][1]) - - def repo(self, name): - ''' - Return a reposiory by its name - name can be None if there is only one repository - ''' - if name is None and len(self._repos) == 1: - return self._repos[self._repos.keys()[0]] - elif name is not None: - if name in self._repos.keys(): - return self._repos[name] - return None - + return self._repos.copy() diff --git a/installsystems/database.py b/installsystems/database.py index e5a2092..d0bfbf3 100644 --- a/installsystems/database.py +++ b/installsystems/database.py @@ -10,6 +10,7 @@ import json import os import shutil import tarfile +import installsystems.tools as istools from installsystems.tarball import Tarball from installsystems.printer import * @@ -50,7 +51,9 @@ class Database(object): newdb.add_str(name, package.jdescription(), tarfile.REGTYPE, 0444) db.close() newdb.close() - shutil.move(newdb_path, self.path) + # preserve permission and stats when moving + shutil.copystat(self.path, newdb_path) + os.rename(newdb_path, self.path) except Exception as e: raise Exception("Adding metadata fail: %s" % e) @@ -67,7 +70,9 @@ class Database(object): newdb.addfile(ti, db.extractfile(ti)) db.close() newdb.close() - shutil.move(newdb_path, self.path) + # preserve permission and stats when moving + shutil.copystat(self.path, newdb_path) + os.rename(newdb_path, self.path) except Exception as e: raise Exception("Removing metadata fail: %s" % e) diff --git a/installsystems/repository.py b/installsystems/repository.py index 69d5d90..1675929 100644 --- a/installsystems/repository.py +++ b/installsystems/repository.py @@ -9,6 +9,8 @@ Repository stuff import os import time import shutil +import pwd +import grp import installsystems import installsystems.tools as istools from installsystems.printer import * @@ -19,40 +21,43 @@ from installsystems.database import Database class Repository(object): '''Repository class''' - last_name = "last" - - def __init__(self, image_path, data_path, verbose=True): - self.image_path = os.path.abspath(image_path) - self.last_path = os.path.join(image_path, self.last_name) - self.data_path = os.path.abspath(data_path) + def __init__(self, config, verbose=True): self.verbose = verbose - self.db = Database(os.path.join(image_path, "db"), verbose=self.verbose) + self.config = config + self.db = Database(os.path.join(config.image, config.db), verbose=self.verbose) + self.last_path = os.path.join(config.image, config.last) @classmethod - def create(cls, image_path, data_path, verbose=True): + def create(cls, config, verbose=True): '''Create an empty base repository''' # create base directories arrow("Creating base directories", 1, verbose) - try: - for d in (image_path, data_path): + for d in (config.image, config.data): + try: if os.path.exists(d): - arrow("%s already exists" % os.path.relpath(d), 2, verbose) + arrow("%s already exists" % d, 2, verbose) else: - os.mkdir(d) - arrow("%s directory created" % os.path.relpath(d), 2, verbose) - except Exception as e: - raise Exception("Unable to create directory %s: %s" % (d, e)) + istools.mkdir(d, config.chown, config.chgroup, config.dchmod) + arrow("%s directory created" % d, 2, verbose) + except Exception as e: + raise + raise Exception("Unable to create directory %s: %s" % (d, e)) # create database - d = Database.create(os.path.join(image_path, "db"), verbose=verbose) + dbpath = os.path.join(config.image, "db") + d = Database.create(dbpath, verbose=verbose) + istools.chrights(dbpath, config.chown, config.chgroup, config.fchmod) # create last file arrow("Creating last file", 1, verbose) - self = cls(image_path, data_path, verbose) + self = cls(config, verbose) self.update_last() + return self def update_last(self): '''Update last file to current time''' try: open(self.last_path, "w").write("%s\n" % int(time.time())) + os.chown(self.last_path, self.config.chown, self.config.chgroup) + os.chmod(self.last_path, self.config.fchmod) except Exception as e: raise Exception("Update last file failed: %s" % e) @@ -69,10 +74,12 @@ class Repository(object): # copy file to directory arrow("Adding file to directories", 1, self.verbose) arrow("Adding %s" % os.path.basename(package.path), 2, self.verbose) - shutil.copy(package.path, self.image_path) + istools.copy(package.path, self.config.image, + self.config.chown, self.config.chgroup, self.config.fchmod) for db in package.databalls(): arrow("Adding %s" % os.path.basename(db), 2, self.verbose) - shutil.copy(db, self.data_path) + istools.copy(db, self.config.data, + self.config.chown, self.config.chgroup, self.config.fchmod) # add file to db self.db.add(package) # update last file @@ -85,7 +92,7 @@ class Repository(object): error("Unable to find %s version %s in database" % (name, version)) # removing script tarballs arrow("Removing script tarball", 1, self.verbose) - tpath = os.path.join(self.image_path, "%s-%s%s" % (name, version, + tpath = os.path.join(self.config.image, "%s-%s%s" % (name, version, Image.image_extension)) if os.path.exists(tpath): os.unlink(tpath) @@ -93,7 +100,7 @@ class Repository(object): # removing data tarballs arrow("Removing data tarballs", 1, self.verbose) for tb in self.db.databalls(name, version): - tpath = os.path.join(self.data_path, tb) + tpath = os.path.join(self.config.data, tb) if os.path.exists(tpath): os.unlink(tpath) arrow("%s removed" % tb, 2, self.verbose) @@ -104,6 +111,67 @@ class Repository(object): self.update_last() +class RepositoryConfig(object): + '''Repository configuration container''' + + def __init__(self, *args, **kwargs): + # set default value for arguments + self.name = args[0] + self.db = "db" + self.last = "last" + self.image = "" + self.data = "" + self.chown = os.getuid() + self.chgroup = os.getgid() + umask = os.umask(0) + os.umask(umask) + self.fchmod = 0666 & ~umask + self.dchmod = 0777 & ~umask + self.update(**kwargs) + + def update(self, *args, **kwargs): + ''' + Update attribute with checking value + All attribute must already exists + ''' + # autoset parameter in cmdline + for k in kwargs: + if hasattr(self, k): + # attribute which are not in the following list cannot be loaded + # from configuration + try: + # convert userid + if k == "chown": + if not k.isdigit(): + kwargs[k] = pwd.getpwnam(kwargs[k]).pw_uid + setattr(self, k, int(kwargs[k])) + # convert gid + elif k == "chgroup": + if not k.isdigit(): + kwargs[k] = grp.getgrnam(kwargs[k]).gr_gid + setattr(self, k, int(kwargs[k])) + # convert file mode + elif k in ("fchmod", "dchmod"): + setattr(self, k, int(kwargs[k], 8)) + # convert repo path + elif k in ("image", "data"): + setattr(self, k, istools.abspath(kwargs[k])) + # else is string + else: + setattr(self, k, kwargs[k]) + except Exception as e: + warn("Unable to set config parameter %s in repository %s: %s" % (k, self.name, e)) + + def __eq__(self, other): + return vars(self) == vars(other) + + def __ne__(self, other): + return not (self == other) + + def __contains__(self, key): + return key in self.__dict__ + + class RepositoryCache(object): '''Local repository cache class''' @@ -124,9 +192,8 @@ class RepositoryCache(object): def register(self, iterepo): '''Register a repository to track''' for r in iterepo: - print r - self.repos[r[0]] = Repository(istools.complete_path(r[1]), - istools.complete_path(r[2]), + self.repos[r[0]] = Repository(istools.abspath(r[1]), + istools.abstpath(r[2]), verbose=self.verbose) def update(self): @@ -138,9 +205,9 @@ class RepositoryCache(object): self.last(r))) if self.repos[r].last() > self.last(r): # copy last file - istools.cp(self.repos[r].last_path, os.path.join(self.last_path, r)) + istools.copy(self.repos[r].last_path, os.path.join(self.last_path, r)) # copy db file - istools.cp(self.repos[r].db.path, os.path.join(self.db_path, r)) + istools.copy(self.repos[r].db.path, os.path.join(self.db_path, r)) arrow("%s updated" % r, 2, self.verbose) def last(self, reponame): @@ -162,7 +229,7 @@ class RepositoryCache(object): # get remote image remotepath = os.path.join(self.repos[reponame].image_path, filename) arrow("Copying from repository", 2, self.verbose) - istools.cp(remotepath, localpath) + istools.copy(remotepath, localpath) return localpath def find_image(self, name, version): diff --git a/installsystems/tools.py b/installsystems/tools.py index 717964c..b4a084b 100644 --- a/installsystems/tools.py +++ b/installsystems/tools.py @@ -16,18 +16,37 @@ def md5sum(path): m.update(open(path, "r").read()) return m.hexdigest() -def cp(source, destination): +def copy(source, destination, uid=None, gid=None, mode=None): '''Copy a source to destination. Take care of path type''' - stype = get_path_type(source) - dtype = get_path_type(destination) + stype = pathtype(source) + dtype = pathtype(destination) if stype == dtype == "file": shutil.copy(source, destination) elif stype == "file" and dtype == "": raise NotImplementedError else: raise NotImplementedError + # setting destination file rights + if dtype == "file": + if os.path.isdir(destination): + destination = os.path.join(destination, os.path.basename(source)) + chrights(destination, uid, gid, mode) -def get_path_type(path): +def mkdir(path, uid=None, gid=None, mode=None): + '''Create a directory and set rights''' + os.mkdir(path) + chrights(path, uid, gid, mode) + +def chrights(path, uid=None, gid=None, mode=None): + '''Set rights on a file''' + if uid is not None: + os.chown(path, uid, -1) + if gid is not None: + os.chown(path, -1, gid) + if mode is not None: + os.chmod(path, mode) + +def pathtype(path): '''Return path type. This is usefull to know what king of path is given''' from installsystems.image import Image if path.startswith("http://") or path.startswith("https://"): @@ -40,9 +59,9 @@ def get_path_type(path): return "name" return None -def complete_path(path): - '''Format a path to be complete''' - ptype = get_path_type(path) +def abspath(path): + '''Format a path to be absolute''' + ptype = pathtype(path) if ptype in ("http", "ssh"): return path elif ptype == "file": -- GitLab