public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/grss:master commit in: grs/, tests/
@ 2015-07-09 23:05 Anthony G. Basile
  0 siblings, 0 replies; 3+ messages in thread
From: Anthony G. Basile @ 2015-07-09 23:05 UTC (permalink / raw
  To: gentoo-commits

commit:     a83efe19256bf9d63a5039e7448155531cf56ee3
Author:     Anthony G. Basile <blueness <AT> gentoo <DOT> org>
AuthorDate: Thu Jul  9 23:08:08 2015 +0000
Commit:     Anthony G. Basile <blueness <AT> gentoo <DOT> org>
CommitDate: Thu Jul  9 23:08:08 2015 +0000
URL:        https://gitweb.gentoo.org/proj/grss.git/commit/?id=a83efe19

grs/Rotator.py: document and improve.

 grs/Log.py         |  6 ++----
 grs/PivotChroot.py |  3 +--
 grs/Rotator.py     | 38 ++++++++++++++++++++++++++++++++------
 grs/Seed.py        |  4 +---
 tests/test-log.py  | 31 +++++++++++++++++++++----------
 5 files changed, 57 insertions(+), 25 deletions(-)

diff --git a/grs/Log.py b/grs/Log.py
index 201eeb9..5a87a92 100644
--- a/grs/Log.py
+++ b/grs/Log.py
@@ -26,8 +26,6 @@ class Log(Rotator):
             f.write('%s\n' % msg)
 
 
-    def rotate_logs(self):
-        self.rotate(self.logfile)
-        if os.path.isfile(self.logfile):
-            shutil.move(self.logfile, '%s.0' % self.logfile)
+    def rotate_logs(self, upper_limit = 20):
+        self.full_rotate(self.logfile, upper_limit=upper_limit)
         open('%s' % self.logfile, 'a').close()

diff --git a/grs/PivotChroot.py b/grs/PivotChroot.py
index ff0fb69..cbfb29f 100644
--- a/grs/PivotChroot.py
+++ b/grs/PivotChroot.py
@@ -35,8 +35,7 @@ class PivotChroot(Rotator):
 
         # Move portage_configroot out of the way to system.0,
         # then pivot out the inner chroot to system.
-        self.rotate(self.portage_configroot)
-        shutil.move(self.portage_configroot, '%s.0' % self.portage_configroot)
+        self.full_rotate(self.portage_configroot)
         inner_chroot = os.path.join('%s.0' % self.portage_configroot, subchroot)
         shutil.move(inner_chroot, os.path.join(self.tmpdir, 'system'))
 

diff --git a/grs/Rotator.py b/grs/Rotator.py
index d82c9fd..0dccdf8 100644
--- a/grs/Rotator.py
+++ b/grs/Rotator.py
@@ -2,16 +2,29 @@
 
 import glob
 import re
+import os
 import shutil
 
 class Rotator():
-    """ doc here
-        more doc
-    """
+    """ Super class for rotating files or directories.  """
 
-    def rotate(self, obj):
-        """ doc here
-            more doc
+    def rotate(self, obj, upper_limit = 20):
+        """ Does the work of rotating objects fitting the pattern obj.(d+).
+
+            obj -> The absolute path to the objects to be rotated.  The
+            obj's are assumed to have a pattern obj.(d+) and does NOT
+            include a bare obj not followed by a decimal.  (For that,
+            use full_rotate() below).  Note that gaps in numbers are
+            are preserved.  Eg.
+
+                Old Name        New Name
+                log             (untouched)
+                log.0           log.1
+                log.1           log.2
+                log.3           log.4 (Note the gap is preserved.)
+                log.4           log.5
+
+            obj's paste an upper limit are deleted.
         """
         objs = glob.glob('%s.*' % obj)
         indexed_obj = {}
@@ -23,6 +36,19 @@ class Rotator():
         count.reverse()
         for c in count:
             current_obj = indexed_obj[c]
+            if c >= upper_limit:
+                try:
+                    shutil.rmtree(current_obj)
+                except NotADirectoryError:
+                    os.unlink(current_obj)
+                continue
             m = re.search('^(.+)\.\d+$', current_obj)
             next_obj = '%s.%d' % (m.group(1), c+1)
             shutil.move(current_obj, next_obj)
+
+
+    def full_rotate(self, obj, upper_limit = 20):
+        """ Rotate both obj and obj.(d+). """
+        self.rotate(obj, upper_limit = upper_limit)
+        if os.path.exists(obj):
+            shutil.move(obj, '%s.0' % obj)

diff --git a/grs/Seed.py b/grs/Seed.py
index aad5d20..1ce5f48 100644
--- a/grs/Seed.py
+++ b/grs/Seed.py
@@ -35,9 +35,7 @@ class Seed(Rotator):
         """
         # Rotate the old portage_configroot and package out of the way
         for directory in [self.portage_configroot, self.package]:
-            self.rotate(directory)
-            if os.path.isdir(directory):
-                shutil.move(directory, '%s.0' % directory)
+            self.full_rotate(directory)
             os.makedirs(directory, mode=0o755, exist_ok=False)
 
         # Download a stage tarball if we don't have one

diff --git a/tests/test-log.py b/tests/test-log.py
index f132ca0..b419fbb 100755
--- a/tests/test-log.py
+++ b/tests/test-log.py
@@ -10,16 +10,10 @@ from grs import Log
 
 logdir = '/tmp/test-log'
 
-def doit(stamped = False):
-    if os.path.isdir(logdir):
-        shutil.rmtree(logdir)
-    os.makedirs(logdir)
-    logfile = os.path.join(logdir, 'test.log')
-
-    lo = Log(logfile)
+def doit(lo, stamped = False):
+    # Create a log with knowing contents and rotate 3 times.
     for i in range(10):
         lo.log('first %d' % i, stamped)
-
     lo.rotate_logs()
     lo.rotate_logs()
     lo.rotate_logs()
@@ -28,6 +22,7 @@ def doit(stamped = False):
 
 
 def hashtest(expect_pass = True):
+    # Hash up our log and three rotations.  Do we get what we expected?
     m = hashlib.md5()
     for i in [ '', '.0', '.1', '.2']:
         log = os.path.join(logdir, 'test.log%s' % i)
@@ -39,7 +34,23 @@ def hashtest(expect_pass = True):
         assert(m.hexdigest() != '485b8bf3a9e08bd5ccfdff7e1a8fe4e1')
 
 if __name__ == "__main__":
-    doit(stamped=False)
+    if os.path.isdir(logdir):
+        shutil.rmtree(logdir)
+    os.makedirs(logdir)
+    logfile = os.path.join(logdir, 'test.log')
+    lo = Log(logfile)
+
+    doit(lo, stamped=False)
     hashtest(expect_pass=True)
-    doit(stamped=True)
+    doit(lo, stamped=True)
     hashtest(expect_pass=False)
+
+    # Make sure we're dropping past the upper limit.
+    lo.rotate_logs(upper_limit=2)
+    assert(os.path.isfile(logfile))
+    assert(os.path.isfile(logfile+'.0'))
+    assert(os.path.isfile(logfile+'.1'))
+    assert(os.path.isfile(logfile+'.2'))
+    assert(not os.path.isfile(logfile+'.3'))
+    assert(not os.path.isfile(logfile+'.4'))
+    assert(not os.path.isfile(logfile+'.5'))


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [gentoo-commits] proj/grss:master commit in: grs/, tests/
@ 2015-07-10  1:23 Anthony G. Basile
  0 siblings, 0 replies; 3+ messages in thread
From: Anthony G. Basile @ 2015-07-10  1:23 UTC (permalink / raw
  To: gentoo-commits

commit:     07baec53eaadb095a61f9c6c6f21109a20f4cad6
Author:     Anthony G. Basile <blueness <AT> gentoo <DOT> org>
AuthorDate: Fri Jul 10 01:25:50 2015 +0000
Commit:     Anthony G. Basile <blueness <AT> gentoo <DOT> org>
CommitDate: Fri Jul 10 01:25:50 2015 +0000
URL:        https://gitweb.gentoo.org/proj/grss.git/commit/?id=07baec53

grs/Constants.py: add documentation.

 grs/Constants.py   | 82 ++++++++++++++++++++++++++++++++++++++++++++----------
 tests/systems.conf |  1 -
 2 files changed, 68 insertions(+), 15 deletions(-)

diff --git a/grs/Constants.py b/grs/Constants.py
index 501d79d..b9ea1d9 100644
--- a/grs/Constants.py
+++ b/grs/Constants.py
@@ -5,30 +5,62 @@ import sys
 import configparser
 from copy import deepcopy
 
-CONFIG = '/etc/grs/systems.conf'
 
 class Constants():
-    """ doc here
-        more doc
+    """ Read a global configuration file and set/override constants for
+        each GRS spec.  These constants are exported in the form:
+
+            CONST.nameservers[x] contains the namserver for the xth GRS spec
+            CONST.repo_uris[x] contains the repo_uri for the xth GRS spec
+            etc.
+
+        Notice the 's' added to the list name to distinguish the list from
+        the constant it holds.  Here the x is an integer corresponding to a
+        section in a global config file, which by default is located at
+        '/etc/grs/systes.conf'.  This file is in configparser format and
+        each section introduces a new GRS namespace.  The default values
+        for all possible constants in any given GRS namespace are defined
+        by the space[] dictionary below, but these can be overridden using
+        the item:value pairs from the section of any given GRS namespace.
+        E.g. Suppose /etc/grs/systems.conf contains
+
+        [my-cool-desktop]
+        kernelroot : /tmp/kernel_src_tree
+
+        [my-cool-server]
+        nameserver : 192.168.100.1
+
+        Then CONST.kernelroots[0] is '/tmp/kernel_src_tree' rather than the
+        default value '/var/tmp/grs/my-cool-desktop/kernel'.  The remainder
+        of the constants default as delineated in the space[] dictionary with
+        %s replaced by 'my-cool-desktop'.  Similarly CONST.my-cool-servers[1]
+        is 192.168.100.1 rather than 8.8.8.8.
+
+        Finally, the that class overrides __setattr__, __gettattr__ and
+        __delattr__ so that you cannot add/change/delete constants in
+        a GRS namespace.
     """
 
-    def __init__(self, configfile = CONFIG):
+    def __init__(self, configfile = '/etc/grs/systems.conf'):
+        # If there's no config file, we're dead in the water.
         if not os.path.isfile(configfile):
-            sys.stderr.write('Configuration file %s not found\n' % configfile)
-            sys.exit(1)
+            raise Exception('Configuration file %s not found\n' % configfile)
+
         self.config = configparser.ConfigParser(delimiters = ':', comment_prefixes = '#')
         self.config.read(configfile)
 
-        self.names = list(self.config.sections())
-
+        # These values will probably fail in the future, but that's okay
+        # because they really should never be used.  They live outside of
+        # any GRS namespace and are just 'defaults'.
         server    = 'http://distfiles.gentoo.org/'
-        stagedir  = 'gentoo/releases/amd64/autobuilds/current-stage3-amd64-uclibc-hardened/'
+        stagedir  = 'releases/amd64/autobuilds/current-stage3-amd64-uclibc-hardened/'
         stagefile = 'stage3-amd64-uclibc-hardened-20150510.tar.bz2'
         default_stage_uri =  server + stagedir + stagefile
 
+        # This is the space of all possible constants for any given GRS namespace
         space = {
             'nameserver'          : '8.8.8.8',
-            'repo_uri'            : 'git://tweedledum.dyc.edu/grs',
+            'repo_uri'            : 'git://anongit.gentoo.org/proj/grs.git',
             'stage_uri'           : default_stage_uri,
             'libdir'              : '/var/lib/grs/%s',
             'logfile'             : '/var/log/grs/%s.log',
@@ -40,49 +72,70 @@ class Constants():
             'pidfile'             : '/run/grs-%s.pid'
         }
 
+        # We add an 's' to each list for a particular constant,
+        # and initialize the list to be empty.
         for key in space:
             self.__dict__[key+'s'] = []
 
+        # Each section is a 'namespace' corresponding to each GRS spec.
+        # We export these in the CONST.names[] list.
+        self.names = list(self.config.sections())
+
+        # We go over all the sections in the config file.  The
+        # order here had better be the same as self.names[], else
+        # CONST.names[x] doesn't corresponde to the other CONST.foo[x]
+        # for every x.
         for section in self.config.sections():
             overrides = dict(self.config[section].items())
 
+            # Either we have an override value from the config
+            # file, else we contruct a default name.
             for key in space:
                 if key in overrides:
                     value = overrides[key]
                 else:
+                    # Either the default name has a slot %s to
+                    # file or else it doesn't.
                     try:
                         value = space[key] % section
                     except TypeError:
                         value = space[key]
+                # We're counting on the order in which we append here to
+                # correspond to the GRS namespace for the key:value pair.
                 self.__dict__[key+'s'].append(value)
 
 
+    # Allow CONST.foo = bar only once!
     def __setattr__(self, key, value):
         if not key in self.__dict__:
             self.__dict__[key] = value
         else:
             pass
 
-
+    # Don't retrieve the original else you can overwrite it,
+    # rather deep copy it.
     def __getattr__(self, key, value = None):
         if key in self.__dict__:
             return deepcopy(self.__dict__[key])
 
-
+    # You can't del(CONST.foo).
     def __delattr__(self, key):
         if key in self.__dict__:
             pass
 
 
+# Instantiate once and export all our constant in CONST.
 CONST = Constants()
 
+# Constants outside any GRS namespace.
 CONST.PACKAGE_NAME        = "Gentoo Reference System"
 CONST.PACKAGE_VERSION     = 0.0
 CONST.PACKAGE_DESCRIPTION = "Update a GRS by cloning a predefined system."
 CONST.BUG_REPORTS         = 'http://bugs.gentoo.org'
 
-# The are defaults in case objects are instantiated without namespaces
-# but they should not be used under normal working condidtions.
+# The are defaults in case objects of other classes which depend on values
+# of libdir, logfile, etc. are instantiated outside of any namespaces.
+# They should not be needed under normal working condidtions.
 CONST.LIBDIR              = '/var/lib/grs'
 CONST.LOGFILE             = '/var/log/grs.log'
 CONST.TMPDIR              = '/var/tmp/grs'
@@ -92,6 +145,7 @@ CONST.KERNELROOT          = '/var/tmp/grs/kernel'
 CONST.PORTAGE_CONFIGROOT  = '/var/tmp/grs/system'
 CONST.PIDFILE             = '/run/grs.pid'
 
+# These are used by grsup and are hard coded values.
 CONST.PORTAGE_CONFIGDIR   = '/etc/portage'
 CONST.PORTAGE_DIRTYFILE   = '/etc/portage/.grs_dirty'
 CONST.WORLD_CONFIG        = '/etc/grs/world.conf'

diff --git a/tests/systems.conf b/tests/systems.conf
deleted file mode 120000
index b92ef4f..0000000
--- a/tests/systems.conf
+++ /dev/null
@@ -1 +0,0 @@
-../systems.conf
\ No newline at end of file


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [gentoo-commits] proj/grss:master commit in: grs/, tests/
@ 2015-07-10 19:07 Anthony G. Basile
  0 siblings, 0 replies; 3+ messages in thread
From: Anthony G. Basile @ 2015-07-10 19:07 UTC (permalink / raw
  To: gentoo-commits

commit:     3905cabb9652305dcd220b96bb53d8d1617d30ca
Author:     Anthony G. Basile <blueness <AT> gentoo <DOT> org>
AuthorDate: Fri Jul 10 19:09:48 2015 +0000
Commit:     Anthony G. Basile <blueness <AT> gentoo <DOT> org>
CommitDate: Fri Jul 10 19:09:48 2015 +0000
URL:        https://gitweb.gentoo.org/proj/grss.git/commit/?id=3905cabb

grs/Daemon.py: remove unused stop() and restart().

 grs/Daemon.py        | 73 ++++++++++------------------------------------------
 tests/test-daemon.py | 12 ++-------
 2 files changed, 16 insertions(+), 69 deletions(-)

diff --git a/grs/Daemon.py b/grs/Daemon.py
index 0d2d988..38b7d9a 100644
--- a/grs/Daemon.py
+++ b/grs/Daemon.py
@@ -11,12 +11,12 @@ class Daemon:
         See: http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
 
         To use, inherit by a subclass which overrides run() and does all the
-        daemon work.  You start the daemon with
+        work.  You start the daemon with
 
-            d = MyDaemon(pidfile, foo='1', bar='2')  # Any number for kwargs after pidfile
+            d = MyDaemon(pidfile, foo='1', bar='2')  # Any **kwargs after pidfile
             d.start()   # to start the daemon
-            d.restart() # to restart the daemon
-            d.stop()    # to stop the daemon
+
+        All signal handling should be defined in a subfunction within run().
 
         Note: This isn't completely general daemon code as it doesn't close stdout/stderr.
         Rather these are redirected to /var/log/grs/grs-daemon-<pid>.err to capture any
@@ -24,13 +24,13 @@ class Daemon:
     """
 
     def __init__(self, pidfile, **kwargs):
-        """ Since this will be used as a super class, we'll accept any **kwargs
-            and insert them to our internal __dict__.
-        """
+        # Since this will be used as a super class, we'll accept any **kwargs
+        # and insert them to our internal __dict__.
         self.pidfile = pidfile
         for k in kwargs:
             self.__dict__[k] = kwargs[k]
 
+
     def daemonize(self):
         try:
             pid = os.fork()
@@ -76,11 +76,14 @@ class Daemon:
         os.remove(self.pidfile)
 
 
+    def run(self):
+        pass
+
+
     def start(self):
-        # If there's a pidfile when we try to startup, then either
-        # its stale or we're already running.  If the pidfile is stale,
-        # remove it and startup as usual.  If we're already running,
-        # then don't start a second instance.
+        # If there's a pidfile when we try to startup, then:
+        # 1) If the pidfile is stale, remove it and startup as usual.
+        # 2) If we're already running, then don't start a second instance.
         try:
             with open(self.pidfile, 'r') as pf:
                 pid = int(pf.read().strip())
@@ -97,51 +100,3 @@ class Daemon:
 
         self.daemonize()
         self.run()
-
-
-    def stop(self):
-        # Try to open our pidfile and read our pid.  If you have a pid but
-        # there is no process at that pid, then we're not running and all
-        # we have to do is cleanup our stale pidfile.a  If we can't get a
-        # pid from our pidfile, then we've lost the original process.  Either
-        # it crashed or something else killed the pidfile.  We don't know.
-        # Finally if have a valid pid, send it a bunch of SIGTERMS followed
-        # by SIGKILLS just in case.
-        try:
-            with open(self.pidfile,'r') as pf:
-                pid = int(pf.read().strip())
-        except IOError:
-            pid = None
-
-        if pid and not os.path.exists('/proc/%d' % pid):
-            sys.stderr.write('process not running\n')
-            sys.stderr.write('unlinking stale pid file %s\n' % self.pidfile)
-            os.unlink(self.pidfile)
-            return
-
-        if not pid:
-            sys.stderr.write('process not running\n')
-            return # not an error in a restart
-
-        try:
-            for i in range(10):
-                os.kill(pid, signal.SIGTERM)
-                time.sleep(0.2)
-            while True:
-                os.kill(pid, signal.SIGKILL)
-                time.sleep(0.2)
-        except ProcessLookupError as err:
-            try:
-                os.remove(self.pidfile)
-            except IOError as err:
-                sys.stderr.write('%s\n' % err)
-        except OSError as err:
-            sys.stderr.write('%s\n' %err)
-            return
-
-    def restart(self):
-        self.stop()
-        self.start()
-
-    def run(self):
-        pass

diff --git a/tests/test-daemon.py b/tests/test-daemon.py
index 27300e2..f121dae 100755
--- a/tests/test-daemon.py
+++ b/tests/test-daemon.py
@@ -46,26 +46,18 @@ if __name__ == "__main__":
     Execute(cmd)
 
     if len(sys.argv) != 2:
-        print('%s [start1 start2 startb stop1 stop2 restart1 restart2 pids killall]' % sys.argv[0])
+        print('%s [start1 start2 start12 pids killall]' % sys.argv[0])
         sys.exit(1)
 
     if 'start1' == sys.argv[1]:
         daemon1.start()
     elif 'start2' == sys.argv[1]:
         daemon2.start()
-    elif 'startb' == sys.argv[1]:
+    elif 'start12' == sys.argv[1]:
         if not os.fork():
             daemon1.start()
         elif not os.fork():
             daemon2.start()
-    elif 'stop1' == sys.argv[1]:
-        daemon1.stop()
-    elif 'stop2' == sys.argv[1]:
-        daemon2.stop()
-    elif 'restart1' == sys.argv[1]:
-        daemon1.restart()
-    elif 'restart2' == sys.argv[1]:
-        daemon2.restart()
     elif 'pids' == sys.argv[1]:
         try:
             print('daemon1:\n%s' % open(mypid1, 'r').read())


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2015-07-10 19:07 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-07-10  1:23 [gentoo-commits] proj/grss:master commit in: grs/, tests/ Anthony G. Basile
  -- strict thread matches above, loose matches on Subject: below --
2015-07-10 19:07 Anthony G. Basile
2015-07-09 23:05 Anthony G. Basile

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox