def addLogEntry(self, entry, save=True): """This is called when a new log entry is created""" # create log directory if it doesn't exist # error will be thrown if this is not possible _busy = Purr.BusyIndicator() self._initIndexDir() # discard temporary watchers -- these are only used to keep track of # deleted files self.temp_watchers = {} # ignored entries are only there to carry info on ignored data products # All we do is save them, and update DP policies based on them if entry.ignore: entry.save(self.logdir) # proper entries are added to list else: self.entries.append(entry) Purr.progressMessage("Saving new log entry") # find previous entry -- skip over "ignore" entries for prev in self.entries[-2::-1]: if not prev.ignore: break else: prev = None entry.setLogDirectory(self.logdir) entry.setPrevUpNextLinks(prev=prev, up=os.path.join("..", Purr.RenderIndex.INDEX)) entry.save() self.timestamp = self.last_scan_timestamp # regenerate links of previous entry prev and prev.generateIndex() # and our log may need to be regenerated if save: self.save() self.updatePoliciesFromEntry(entry, new=True)
def save(self, refresh=False): """Saves the log. If refresh is set to a timestamp, will regenerate everything from scratch. """ # create directory if it doesn't exist # error will be thrown if this is not possible _busy = Purr.BusyIndicator() Purr.progressMessage("Generating index in %s" % self.logdir) self._initIndexDir() # if refresh is True, re-save all entries. if refresh: refresh = time.time() for i, entry in enumerate(self.entries): entry.save(refresh=refresh) Purr.RenderIndex.writeLogIndex(self.logdir, self.logtitle, self.timestamp, self.entries, refresh=refresh) Purr.progressMessage("Wrote %s" % self.logdir)
def _attach(self, purrlog, watchdirs=None): """Attaches Purr to a purrlog directory, and loads content. Returns False if nothing new has been loaded (because directory is the same), or True otherwise.""" purrlog = os.path.abspath(purrlog) dprint(1, "attaching to purrlog", purrlog) self.logdir = purrlog self.indexfile = os.path.join(self.logdir, "index.html") self.logtitle = "Unnamed log" self.timestamp = self.last_scan_timestamp = time.time() self._initIndexDir() # reset internal state self.ignorelistfile = None self.autopounce = False self.watched_dirs = [] self.entries = [] self._default_dp_props = {} self.watchers = {} self.temp_watchers = {} self.attached = False self._watching_state = {} # check that we hold a lock on the directory self.lockfile = os.path.join(self.logdir, ".purrlock") # try to open lock file for r/w try: self.lockfile_fd = os.open(self.lockfile, os.O_RDWR | os.O_CREAT) except: raise Purrer.LockFailError( "failed to open lock file %s for writing" % self.lockfile) # try to acquire lock on the lock file try: fcntl.lockf(self.lockfile_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except: other_lock = os.fdopen(self.lockfile_fd, 'r').read() self.lockfile_fd = None raise Purrer.LockedError(other_lock) # got lock, write our ID to the lock file global _lockstring try: self.lockfile_fobj = os.fdopen(self.lockfile_fd, 'w') self.lockfile_fobj.write(_lockstring) self.lockfile_fobj.flush() os.fsync(self.lockfile_fd) except: raise # raise Purrer.LockFailError("cannot write to lock file %s"%self.lockfile) # load log state if log directory already exists if os.path.exists(self.logdir): _busy = Purr.BusyIndicator() if os.path.exists(self.indexfile): try: parser = Purr.Parsers.LogIndexParser() for line in open(self.indexfile): parser.feed(line) self.logtitle = parser.title or self.logtitle self.timestamp = parser.timestamp or self.timestamp dprintf( 2, "attached log '%s', timestamp %s\n", self.logtitle, time.strftime("%x %X", time.localtime(self.timestamp))) except: traceback.print_exc() print("Error parsing %s, reverting to defaults" % self.indexfile) # load log entries entries = [] for fname in os.listdir(self.logdir): pathname = os.path.join(self.logdir, fname) if Purr.LogEntry.isValidPathname(pathname): try: entry = Purr.LogEntry(load=pathname) dprint(2, "loaded log entry", pathname) except: print("Error loading entry %s, skipping" % fname) traceback.print_exc() continue entries.append(entry) else: dprint(2, fname, "is not a valid Purr entry") # sort log entires by timestamp entries.sort(lambda a, b: cmp(a.timestamp, b.timestamp)) self.setLogEntries(entries, save=False) # update own timestamp if entries: self.timestamp = max(self.timestamp, entries[-1].timestamp) # else logfile doesn't exist, create it else: self._initIndexDir() # load configuration if it exists # init config file self.dirconfig = configparser.RawConfigParser() self.dirconfigfile = os.path.join(self.logdir, "dirconfig") if os.path.exists(self.dirconfigfile): try: self.dirconfig.read(self.dirconfigfile) except: print("Error loading config file %s" % self.dirconfigfile) traceback.print_exc() # load directory configuration for dirname in self.dirconfig.sections(): try: watching = self.dirconfig.getint(dirname, "watching") except: watching = Purr.WATCHED dirname = os.path.expanduser(dirname) self.addWatchedDirectory(dirname, watching, save_config=False) # start watching the specified directories for name in (watchdirs or []): self.addWatchedDirectory(name, watching=None) # Finally, go through list of ignored files and mark their watchers accordingly. # The ignorelist is a list of lines of the form "timestamp filename", giving the timestamp when a # file was last "ignored" by the purrlog user. self.ignorelistfile = os.path.join(self.logdir, "ignorelist") if os.path.exists(self.ignorelistfile): # read lines from file, ignore exceptions ignores = {} try: for line in open(self.ignorelistfile).readlines(): timestamp, policy, filename = line.strip().split(" ", 2) # update dictiornary with latest timestamp ignores[filename] = int(timestamp), policy except: print("Error reading %s" % self.ignorelistfile) traceback.print_exc() # now scan all listed files, and make sure their watchers' mtime is no older than the given # last-ignore-timestamp. This ensures that we don't pounce on these files after restarting purr. for filename, (timestamp, policy) in ignores.items(): watcher = self.watchers.get(filename, None) if watcher: watcher.mtime = max(watcher.mtime, timestamp) # init complete self.attached = True return True
def restore_from_archive(self, parent=None): """Function to restore a DP from archived copy Asks for confirmation along the way if parent is not None (in which case it will be the parent widget for confirmation dialogs) """ from PyQt4.Qt import QMessageBox exists = os.path.exists(self.sourcepath) if parent: msg = """<P>Do you really want to restore <tt>%s</tt> from this entry's copy of <tt>%s</tt>?</P>""" % (self.sourcepath, self.filename) exists = os.path.exists(self.sourcepath) if exists: msg += """<P>Current file exists, and will be overwritten.</P>""" if QMessageBox.warning(parent, "Restoring from archive", msg, QMessageBox.Yes, QMessageBox.No) != QMessageBox.Yes: return False else: if QMessageBox.question(parent, "Restoring from archive", msg, QMessageBox.Yes, QMessageBox.No) != QMessageBox.Yes: return False busy = Purr.BusyIndicator() # remove file if in the way if exists: if os.system("/bin/rm -fr '%s'" % self.sourcepath): busy = None if parent: QMessageBox.warning( parent, "Error removing file", """<P> There was an error removing %s. Archived copy was not restored. The text console may have more information.</P>""" % self.sourcepath, QMessageBox.Ok, 0) return False # unpack archived file if self.fullpath.endswith('.tgz'): parent_dir = os.path.dirname(self.sourcepath.rstrip('/')) os.system("/bin/rm -fr %s" % self.sourcepath) if os.system("tar zxf '%s' -C '%s'" % (self.fullpath, parent_dir)): busy = None if parent: QMessageBox.warning( parent, "Error unpacking file", """<P> There was an error unpacking the archived version to %s. The text console may have more information.</P>""" % self.sourcepath, QMessageBox.Ok, 0) return False # else simply copy over else: if os.system("/bin/cp -a '%s' '%s'" % (self.fullpath, self.sourcepath)): busy = None if parent: QMessageBox.warning( parent, "Error copying file", """<P> There was an error copying the archived version to %s. The text console may have more information.</P>""" % self.sourcepath, QMessageBox.Ok, 0) return False busy = None if parent: QMessageBox.information( parent, "Restored file", """<P>Restored %s from this entry's archived copy.</P>""" % self.sourcepath, QMessageBox.Ok, 0) return True