Loading bin/isrepo +4 −4 Original line number Diff line number Diff line Loading @@ -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() Loading installsystems/database.py +17 −42 Original line number Diff line number Diff line Loading @@ -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): Loading @@ -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, Loading @@ -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, Loading @@ -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) installsystems/printer.py +5 −7 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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) installsystems/repository.py +83 −37 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading @@ -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()) Loading @@ -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 Loading Loading @@ -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() Loading @@ -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 Loading Loading
bin/isrepo +4 −4 Original line number Diff line number Diff line Loading @@ -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() Loading
installsystems/database.py +17 −42 Original line number Diff line number Diff line Loading @@ -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): Loading @@ -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, Loading @@ -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, Loading @@ -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)
installsystems/printer.py +5 −7 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -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)
installsystems/repository.py +83 −37 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading @@ -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()) Loading @@ -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 Loading Loading @@ -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() Loading @@ -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 Loading