diff options
author | Rob Austein <sra@hactrn.net> | 2007-12-23 23:11:37 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2007-12-23 23:11:37 +0000 |
commit | e6bb8b2f0fdba19eb44122f65953e467b11e4869 (patch) | |
tree | 42c09d8d41d3b1d442e079f4061507ed5e333ba8 | |
parent | 47ad8a1ae2427c228c344157ff65f3d0566a9287 (diff) |
Add timedelta support
svn path=/scripts/testdb.py; revision=1431
-rw-r--r-- | scripts/testdb.py | 95 | ||||
-rw-r--r-- | scripts/testdb1.yaml | 26 |
2 files changed, 90 insertions, 31 deletions
diff --git a/scripts/testdb.py b/scripts/testdb.py index fee0339e..3251dcae 100644 --- a/scripts/testdb.py +++ b/scripts/testdb.py @@ -1,6 +1,6 @@ # $Id$ -import os, yaml, MySQLdb, subprocess, signal, time +import os, yaml, MySQLdb, subprocess, signal, time, datetime, re import rpki.resource_set, rpki.sundial, rpki.x509, rpki.https, rpki.log, rpki.left_right # Most of these globals probably belong in a config file. @@ -36,10 +36,15 @@ prog_openssl = "../../openssl/openssl/apps/openssl" def main(): - rootd_process = None + os.environ["TZ"] = "UTC" + time.tzset() rpki.log.init(irbe_name) + signal.signal(signal.SIGALRM, wakeup) + + rootd_process = None + try: os.chdir(work_dir) except: @@ -157,18 +162,13 @@ def main(): os.kill(rootd_process.pid, signal.SIGTERM) except Exception, data: rpki.log.warn("Couldn't clean up daemons (%s), continuing" % data) - -# Signal handling and commands to make use of it def wakeup(signum, frame): """Handler called when we receive a SIGALRM signal.""" rpki.log.info("Wakeup call received, continuing") -signal.signal(signal.SIGALRM, wakeup) - def cmd_sleep(seconds = None): """Set an alarm, then wait for it to go off.""" - if seconds is None: rpki.log.info("Pausing indefinitely, send a SIGALRM to wake me up") else: @@ -176,11 +176,50 @@ def cmd_sleep(seconds = None): signal.alarm(int(seconds)) signal.pause() +## @var cmds +# Dispatch table for commands embedded in delta sections + cmds = { "sleep" : cmd_sleep } +class timedelta(datetime.timedelta): + """Timedelta with text parsing. This accepts two input formats: + + - A simple integer, indicating a number of seconds. + + - A string of the form "wD xH yM zS" where w, x, y, and z are integers + and D, H, M, and S indicate days, hours, minutes, and seconds. + All of the fields are optional, but at least one must be specified. + Eg, "3D4H" means "three days plus four hours". + """ + + ## @var regexp + # Hideously ugly regular expression to parse the complex text form. + # Tags are intended for use with re.MatchObject.groupdict() and map + # directly to the keywords expected by the timedelta constructor. + + regexp = re.compile("\\s*(?:(?P<days>\\d+)D)?" + + "\\s*(?:(?P<hours>\\d+)H)?" + + "\\s*(?:(?P<minutes>\\d+)M)?" + + "\\s*(?:(?P<seconds>\\d+)S)?\\s*", re.I) + + @classmethod + def parse(cls, arg): + """Parse text into a timedelta object.""" + if isinstance(arg, int): + return cls(seconds = arg) + assert isinstance(arg, str) + if (arg.isdigit()): + return cls(seconds = arg) + else: + return cls(**dict((k, int(v)) for (k, v) in cls.regexp.match(arg).groupdict().items() if v is not None)) + class allocation_db(list): + """Representation of all the entities and allocations in the test system. + Almost everything is generated out of this database. + """ def __init__(self, yaml): + """Initialize database from the (first) YAML document.""" self.root = allocation(yaml, self) assert self.root.is_root() for a in self: @@ -196,6 +235,7 @@ class allocation_db(list): a.set_engine_number(i) def apply_delta(self, delta): + """Apply a delta or run a command.""" for d in delta: if isinstance(d, str): c = d.split() @@ -205,6 +245,7 @@ class allocation_db(list): self.root.closure() def dump(self): + """Print content of the database.""" for a in self: print a @@ -217,19 +258,23 @@ class allocation(object): rpki_port = None def __init__(self, yaml, db, parent = None): + """Initialize one entity and insert it into the database.""" db.append(self) self.name = yaml["name"] self.parent = parent self.kids = [allocation(k, db, self) for k in yaml.get("kids", ())] + valid_until = yaml.get("valid_until") + if valid_until is None and "valid_for" in yaml: + valid_until = datetime.datetime.utcnow() + timedelta.parse(yaml["valid_for"]) self.base = rpki.resource_set.resource_bag( as = rpki.resource_set.resource_set_as(yaml.get("asn")), v4 = rpki.resource_set.resource_set_ipv4(yaml.get("ipv4")), v6 = rpki.resource_set.resource_set_ipv6(yaml.get("ipv6")), - valid_until = yaml.get("valid_until")) + valid_until = valid_until) self.sia_base = yaml.get("sia_base") def closure(self): - """Compute the transitive resource closure for one resource attribute.""" + """Compute the transitive resource closure.""" resources = self.base for kid in self.kids: resources = resources.union(kid.closure()) @@ -237,6 +282,7 @@ class allocation(object): return resources def apply_delta(self, yaml): + """Apply deltas to this entity.""" rpki.log.info("Applying delta: %s" % yaml) for k,v in yaml.items(): if k != "name": @@ -249,6 +295,9 @@ class allocation(object): def apply_sub_v4(self, text): self.base.v4 = self.base.v4.difference(rpki.resource_set.resource_set_ipv4(text)) def apply_sub_v6(self, text): self.base.v6 = self.base.v6.difference(rpki.resource_set.resource_set_ipv6(text)) def apply_valid_until(self, stamp): self.base.valid_until = stamp + def apply_valid_for(self, text): self.base.valid_until = datetime.datetime.utcnow() + timedelta.parse(text) + def apply_valid_add(self, text): self.base.valid_until += timedelta.parse(text) + def apply_valid_sub(self, text): self.base.valid_until -= timedelta.parse(text) def __str__(self): s = self.name + "\n" @@ -265,20 +314,23 @@ class allocation(object): def is_twig(self): return self.parent is not None and self.kids def set_engine_number(self, n): + """Set the engine number for this entity.""" if n >= max_engines: - raise RuntimeError, "You asked for more rpki engine instances than I can handle, maximum is %d, sorry" % max_engines + raise RuntimeError, "You asked for more RPKI engine instances than I can handle, maximum is %d, sorry" % max_engines self.irdb_db_name = "irdb%d" % n self.irdb_port = irdb_base_port + n self.rpki_db_name = "rpki%d" % n self.rpki_port = rpki_base_port + n def setup_biz_certs(self): + """Create business certs for this entity.""" rpki.log.info("Biz certs for %s" % self.name) for tag in ("RPKI", "IRDB"): setup_biz_cert_chain(self.name + "-" + tag) self.rpkid_ta = rpki.x509.X509(PEM_file = self.name + "-RPKI-TA.cer") def setup_conf_file(self): + """Write config files for this entity.""" rpki.log.info("Config files for %s" % self.name) d = { "my_name" : self.name, "irbe_name" : irbe_name, @@ -293,6 +345,7 @@ class allocation(object): f.close() def setup_sql(self, rpki_sql, irdb_sql): + """Set up this entity's IRDB.""" rpki.log.info("MySQL setup for %s" % self.name) db = MySQLdb.connect(user = "rpki", db = self.rpki_db_name, passwd = rpki_db_pass) cur = db.cursor() @@ -308,6 +361,10 @@ class allocation(object): db.close() def sync_sql(self): + """Whack this entity's IRDB to match our master database. We do + this once during setup, then do it again every time we apply a + delta to this entity. + """ rpki.log.info("MySQL sync for %s" % self.name) db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass) cur = db.cursor() @@ -326,11 +383,13 @@ class allocation(object): db.close() def run_daemons(self): + """Run daemons for this entity.""" rpki.log.info("Running daemons for %s" % self.name) self.rpkid_process = subprocess.Popen((prog_python, prog_rpkid, "-c", self.name + ".conf")) self.irdbd_process = subprocess.Popen((prog_python, prog_irdbd, "-c", self.name + ".conf")) def kill_daemons(self): + """Kill daemons for this entity.""" rpki.log.info("Killing daemons for %s" % self.name) for proc in (self.rpkid_process, self.irdbd_process): try: @@ -340,6 +399,9 @@ class allocation(object): proc.wait() def call_rpkid(self, pdu): + """Send a left-right message to this entity's RPKI daemon and + return the response. + """ rpki.log.info("Calling rpkid for %s" % self.name) pdu.type = "query" elt = rpki.left_right.msg((pdu,)).toXML() @@ -380,22 +442,26 @@ class allocation(object): that one is the magic self-signed micro engine. """ - rpki.log.info("Creating rpkid objects %s" % self.name) - + rpki.log.info("Creating rpkid self object for %s" % self.name) self.self_id = self.call_rpkid(rpki.left_right.self_elt.make_pdu(action = "create", crl_interval = 84600)).self_id + rpki.log.info("Creating rpkid BSC object for %s" % self.name) pdu = self.call_rpkid(rpki.left_right.bsc_elt.make_pdu(action = "create", self_id = self.self_id, generate_keypair = True)) self.bsc_id = pdu.bsc_id + rpki.log.info("Issuing BSC EE cert for %s" % self.name) cmd = (prog_openssl, "x509", "-req", "-CA", self.name + "-RPKI-CA.cer", "-CAkey", self.name + "-RPKI-CA.key", "-CAserial", self.name + "-RPKI-CA.srl") signer = subprocess.Popen(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) bsc_ee = rpki.x509.X509(PEM = signer.communicate(input = pdu.pkcs10_cert_request.get_PEM())[0]) + rpki.log.info("Installing BSC EE cert for %s" % self.name) self.call_rpkid(rpki.left_right.bsc_elt.make_pdu(action = "set", self_id = self.self_id, bsc_id = self.bsc_id, signing_cert = [bsc_ee, rpki.x509.X509(PEM_file = self.name + "-RPKI-CA.cer")])) + rpki.log.info("Creating rpkid repository object for %s" % self.name) self.repository_id = self.call_rpkid(rpki.left_right.repository_elt.make_pdu(action = "create", self_id = self.self_id, bsc_id = self.bsc_id)).repository_id + rpki.log.info("Creating rpkid parent object for %s" % self.name) if self.parent is None: self.parent_id = self.call_rpkid(rpki.left_right.parent_elt.make_pdu( action = "create", self_id = self.self_id, bsc_id = self.bsc_id, repository_id = self.repository_id, sia_base = self.sia_base, @@ -407,6 +473,7 @@ class allocation(object): cms_ta = self.parent.rpkid_ta, https_ta = self.parent.rpkid_ta, sender_name = self.name, recipient_name = self.parent.name, peer_contact_uri = "https://localhost:%s/up-down/%s" % (self.parent.rpki_port, self.child_id))).parent_id + rpki.log.info("Creating rpkid child objects for %s" % self.name) db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass) cur = db.cursor() for kid in self.kids: @@ -438,7 +505,6 @@ class allocation(object): """Trigger cron run for this engine.""" rpki.log.info("Running cron for %s" % self.name) - rpki.https.client(privateKey = irbe_key, certChain = irbe_certs, x509TrustList = rpki.x509.X509_chain(self.rpkid_ta), @@ -446,11 +512,13 @@ class allocation(object): msg = "Run cron now, please") def run_yaml(self): + """Run YAML scripts for this leaf entity.""" rpki.log.info("Running YAML for %s" % self.name) subprocess.check_call((prog_python, prog_poke, "-c", self.name + ".yaml", "-r", "list")) subprocess.check_call((prog_python, prog_poke, "-c", self.name + ".yaml", "-r", "issue")) def setup_biz_cert_chain(name): + """Build a set of business certs.""" s = "exec >/dev/null 2>&1\n" for kind in ("EE", "CA", "TA"): d = { "name" : name, @@ -465,6 +533,7 @@ def setup_biz_cert_chain(name): subprocess.check_call(s + (biz_cert_fmt_3 % { "name" : name, "openssl" : prog_openssl }), shell=True) def setup_rootd(rpkid_name): + """Write the config files for rootd.""" rpki.log.info("Config files for %s" % rootd_name) d = { "rootd_name" : rootd_name, "rootd_port" : rootd_port, diff --git a/scripts/testdb1.yaml b/scripts/testdb1.yaml index c1379808..32ea29ca 100644 --- a/scripts/testdb1.yaml +++ b/scripts/testdb1.yaml @@ -1,7 +1,8 @@ # $Id$ name: Root -valid_until: 2008-07-14T12:30:00Z +#valid_until: 2008-07-14T12:30:00Z +valid_for: 2d sia_base: "rsync://wombat.invalid/" kids: - name: R0 @@ -10,27 +11,16 @@ kids: ipv4: 192.0.2.1-192.0.2.33 asn: 64533 --- -- sleep ---- - sleep 5 - name: Alice add_as: 33 - valid_until: 2009-07-14T12:30:00Z ---- -- name: Alice - valid_until: 2009-04-01T00:00:00 ---- -- name: Alice - valid_until: 2009-07-14T12:30:00Z ---- -- name: Alice - valid_until: 2009-04-01T00:00:00 ---- -- name: Alice - valid_until: 2009-07-14T12:30:00Z + valid_add: 2d +# valid_until: 2009-07-14T12:30:00Z --- - name: Alice - valid_until: 2009-04-01T00:00:00 +# valid_until: 2009-04-01T00:00:00 + valid_sub: 2d --- - name: Alice - valid_until: 2009-07-14T12:30:00Z +# valid_until: 2009-04-01T00:00:00 + valid_for: 10d |