Commit 09f8b869 authored by Seblu's avatar Seblu
Browse files

Add/Delete package in repository

parent fce99ab1
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -81,10 +81,10 @@ p_add = subparsers.add_parser("add", help = add.__doc__.lower())
p_add.add_argument("path")
p_add.set_defaults(func = add)
# Del command parser
#p_del =  subparsers.add_parser("del", help=delete.__doc__.lower())
#p_del.add_argument("image_name", type=str)
#p_del.add_argument("image_version", type=str)
#p_del.set_defaults(func=delete)
p_del =  subparsers.add_parser("del", help = delete.__doc__.lower())
p_del.add_argument("image_name")
p_del.add_argument("image_version")
p_del.set_defaults(func = delete)
try:
    # Parse and run
    args = p_main.parse_args()
+17 −42
Original line number Diff line number Diff line
@@ -50,26 +50,23 @@ class Database(object):
        self.conn = sqlite3.connect(self.path, isolation_level=None)
        self.conn.execute("PRAGMA foreign_keys = ON")

    def get(self, name, version):
        '''Return a description dict from a image name'''
        # parse tarball
        try:
            self.file.seek(0)
            tarball = Tarball.open(fileobj=self.file, mode="r:gz")
            rdata = tarball.get_str("%s-%s" % (name, version))
            tarball.close()
        except KeyError:
            raise Exception("No image %s version %s in metadata" % (name, version))
        except Exception as e:
            raise Exception("Unable to read db %s version %s: %s" % (name, version, e))
        # convert loaded data into dict (json parser)
        try:
            return json.loads(rdata)
        except Exception as e:
            raise Exception("Invalid metadata in image %s version %s: e" % (name, version, e))
    def begin(self):
        '''
        Start a db transaction
        '''
        self.conn.execute("BEGIN TRANSACTION")

    def commit(self):
        '''
        Commit current db transaction
        '''
        self.conn.execute("COMMIT TRANSACTION")


    def ask(self, sql, args=()):
        '''Ask question to db'''
        '''
        Ask question to db
        '''
        return self.conn.execute(sql, args)

    def add(self, image):
@@ -81,7 +78,7 @@ class Database(object):
            self.conn.execute("BEGIN TRANSACTION")
            # insert image information
            arrow("Add image metadata")
            self.conn.execute("INSERT OR REPLACE INTO image values (?,?,?,?,?,?,?)",
            self.conn.execute("INSERT INTO image values (?,?,?,?,?,?,?)",
                              (image.md5,
                               image.name,
                               image.version,
@@ -93,7 +90,7 @@ class Database(object):
            # insert data informations
            arrow("Add payload metadata")
            for name, obj in image.payload.items():
                self.conn.execute("INSERT OR REPLACE INTO payload values (?,?,?,?,?)",
                self.conn.execute("INSERT INTO payload values (?,?,?,?,?)",
                                  (obj.md5,
                                   image.md5,
                                   name,
@@ -106,25 +103,3 @@ class Database(object):
            arrowlevel(-1)
        except Exception as e:
            raise Exception("Adding metadata fail: %s" % e)

    def delete(self, name, version):
        '''Delete a packaged image'''
        arrow("Removing metadata from db")
        # check locality
        if istools.pathtype(self.path) != "file":
            raise NotImplementedError("Database deletion must be local")
        newdb_path = "%s.new" % self.path
        fname = "%s-%s.json" % (name, version)
        try:
            db = Tarball.open(self.path, mode='r:gz')
            newdb = Tarball.open(newdb_path, mode='w:gz')
            for ti in db.getmembers():
                if ti.name != fname:
                    newdb.addfile(ti, db.extractfile(ti))
            db.close()
            newdb.close()
            # 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)
+5 −7
Original line number Diff line number Diff line
@@ -79,12 +79,11 @@ def arrowlevel(inc=None,level=None):
        _arrow_level = max(1, min(4, _arrow_level + inc))
    return old_level

def arrow(message, level=None, fd=sys.stdout, endl=os.linesep):
def arrow(message, inclevel=None, level=None, fd=sys.stdout, endl=os.linesep):
    if installsystems.quiet:
        return
    # set a one shot level
    if level is not None:
        old_level = arrowlevel(level=level)
    # define new level
    old_level = arrowlevel(inc=inclevel, level=level)
    if _arrow_level == 1:
        out("#light##red#=>#reset# %s" % message)
    elif _arrow_level == 2:
@@ -94,5 +93,4 @@ def arrow(message, level=None, fd=sys.stdout, endl=os.linesep):
    elif _arrow_level == 4:
        out("  #light##green#=>#reset# %s" % message)
    # restore old on one shot level
    if level is not None:
    arrowlevel(level = old_level)
+83 −37
Original line number Diff line number Diff line
@@ -31,10 +31,12 @@ class Repository(object):

    @classmethod
    def create(cls, config):
        '''Create an empty base repository'''
        '''
        Create an empty base repository
        '''
        # check local repository
        if istools.pathtype(config.path) != "file":
            raise NotImplementedError("Repository creation must be local")
            raise Exception("Repository creation must be local")
        # create base directories
        arrow("Creating base directories")
        arrowlevel(1)
@@ -58,10 +60,12 @@ class Repository(object):
        return self

    def update_last(self):
        '''Update last file to current time'''
        '''
        Update last file to current time
        '''
        # check local repository
        if istools.pathtype(self.config.path) != "file":
            raise NotImplementedError("Repository addition must be local")
            raise Exception("Repository addition must be local")
        try:
            arrow("Updating last file")
            last_path = os.path.join(self.config.path, self.config.lastname)
@@ -71,7 +75,9 @@ class Repository(object):
            raise Exception("Update last file failed: %s" % e)

    def last(self):
        '''Return the last value'''
        '''
        Return the last value
        '''
        try:
            last_path = os.path.join(config.path, config.lastname)
            return int(istools.uopen(last_path, "r").read().rstrip())
@@ -80,10 +86,15 @@ class Repository(object):
        return 0

    def add(self, image):
        '''Add a packaged image to repository'''
        '''
        Add a packaged image to repository
        '''
        # check local repository
        if istools.pathtype(self.config.path) != "file":
            raise NotImplementedError("Repository addition must be local")
            raise Exception("Repository addition must be local")
        # cannot add already existant image
        if self.has(image.name, image.version):
            raise Exception("Image already in database, delete first!")
        # checking data tarballs md5 before copy
        image.check("Check image and payload before copy")
        # adding file to repository
@@ -112,43 +123,60 @@ class Repository(object):
        self.update_last()

    def delete(self, name, version):
        '''Delete an image from repository'''
        raise NotImplementedError()
        '''
        Delete an image from repository
        '''
        # check local repository
        if istools.pathtype(self.config.path) != "file":
            raise NotImplementedError("Repository deletion must be local")
        desc = self.db.find(name, version)
        if desc is None:
            error("Unable to find %s version %s in database" % (name, version))
        # removing script tarballs
        arrow("Removing script tarball")
        arrowlevel(1)
        tpath = os.path.join(self.config.path,
                             "%s-%s%s" % (name, version, Image.extension))
        if os.path.exists(tpath):
            os.unlink(tpath)
            arrow("%s removed" % os.path.basename(tpath))
        arrowlevel(1)
        # removing data tarballs
        arrow("Removing data tarballs")
            raise Exception("Repository deletion must be local")
        # get md5 of files related to images (exception is raised if not exists
        md5s = self.getmd5(name, version)
        # cleaning db (must be done before cleaning)
        arrow("Cleaning database")
        arrow("Remove payloads from database", 1)
        self.db.begin()
        for md5 in md5s[1:]:
            self.db.ask("DELETE FROM payload WHERE md5 = ? AND image_md5 = ?",
                        (md5, md5s[0])).fetchone()
        arrow("Remove image from database", 1)
        self.db.ask("DELETE FROM image WHERE md5 = ?",
                        (md5s[0],)).fetchone()
        self.db.commit()
        # Removing script image
        arrow("Removing files from pool")
        arrowlevel(1)
        for tb in self.db.databalls(name, version):
            tpath = os.path.join(self.config.data, tb)
            if os.path.exists(tpath):
                os.unlink(tpath)
                arrow("%s removed" % tb)
        for md5 in md5s:
            self._remove_file(md5)
        arrowlevel(-1)
        # removing metadata
        self.db.delete(name, version)
        # update last file
        arrow("Updating last file")
        self.update_last()

    def _remove_file(self, filename):
        '''
        Remove a filename from pool. Check if it's not needed by db before
        '''
        # check existance in table image
        have = False
        for table in ("image", "payload"):
            have = have or  self.db.ask("SELECT md5 FROM %s WHERE md5 = ? LIMIT 1" % table,
                                        (filename,)).fetchone() is not None
        # if no reference, delete!
        if not have:
            arrow("%s, deleted" % filename)
            os.unlink(os.path.join(self.config.path, filename))
        else:
            arrow("%s, skipped" % filename)

    def has(self, name, version):
        return self.db.ask("select name,version from image where name = ? and version = ? limit 1", (name,version)).fetchone() is not None
        '''
        Return the existance of a package
        '''
        return self.db.ask("SELECT name,version FROM image WHERE name = ? AND version = ? LIMIT 1", (name,version)).fetchone() is not None

    def get(self, name, version):
        '''return a image from a name and version of pakage'''
        '''
        return a image from a name and version
        '''
        # get file md5 from db
        r = self.db.ask("select md5 from image where name = ? and version = ? limit 1",
                        (name,version)).fetchone()
@@ -156,11 +184,29 @@ class Repository(object):
            raise Exception("No such image %s version %s" % name, version)
        path = os.path.join(self.config.path, r[0])
        debug("Getting %s v%s from %s" % (name, version, path))
        return PackageImage(path, md5name=True)
        pkg = PackageImage(path, md5name=True)
        pkg.md5 = r[0]
        return pkg

    def getmd5(self, name, version):
        '''
        return a image md5 and payload md5 from name and version. Order matter !
        image md5 will still be the first
        '''
        # get file md5 from db
        a = self.db.ask("SELECT md5 FROM image WHERE name = ? AND version = ? LIMIT 1",
                        (name,version)).fetchone()
        if a is None:
            raise Exception("No such image %s version %s" % name, version)
        b = self.db.ask("SELECT md5 FROM payload WHERE image_md5 = ?",
                        (a[0],)).fetchall()
        return [ a[0] ] + [ x[0] for x in b ]

    def last(self, name):
        '''Return last version of name in repo or -1 if not found'''
        r = self.db.ask("select version from image where name = ? order by version desc limit 1", (name,)).fetchone()
        '''
        Return last version of name in repo or -1 if not found
        '''
        r = self.db.ask("SELECT version FROM image WHERE name = ? ORDER BY version DESC LIMIT 1", (name,)).fetchone()
        # no row => no way
        if r is None:
            return -1