aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xrp/rcynic/rcynicng450
-rw-r--r--rpki/rcynicdb/models.py33
2 files changed, 192 insertions, 291 deletions
diff --git a/rp/rcynic/rcynicng b/rp/rcynic/rcynicng
index 167a3b56..760aa6ec 100755
--- a/rp/rcynic/rcynicng
+++ b/rp/rcynic/rcynicng
@@ -33,26 +33,6 @@ logger = logging.getLogger("rcynicng")
codes = rpki.POW.validation_status
-class Generation(object):
-
- all = []
-
- def __init__(self, name, tree):
- self.name = name
- self.tree = tree
- self.all.append(self)
- self.pos = len(self.all)
- setattr(self.__class__, name, self)
-
- def __hash__(self):
- return hash(self.name)
-
- def __cmp__(self, other):
- return cmp(self.pos, 0 if other is None else other.pos)
-
- def __str__(self):
- return self.name
-
class Status(object):
"""
@@ -66,96 +46,55 @@ class Status(object):
db = dict()
- def __init__(self, uri, generation):
- assert generation is None or isinstance(generation, Generation)
+ def __init__(self, uri):
self.uri = uri
- self._generation = generation
self._timestamp = None
self.status = set()
def __str__(self):
- return "{my.timestamp} {my.uri} {status} {my.generation}".format(
+ return "{my.timestamp} {my.uri} {status}".format(
my = self, status = ",".join(str(s) for s in sorted(self.status)))
@property
def timestamp(self):
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(self._timestamp))
- @property
- def generation(self):
- return str(self._generation)
-
@classmethod
- def get(cls, uri, generation):
+ def get(cls, uri):
try:
- return cls.db[uri, generation].status
+ return cls.db[uri].status
except KeyError:
return None
@classmethod
- def update(cls, uri, generation):
+ def update(cls, uri):
try:
- key = (uri, generation)
- self = cls.db[key]
+ self = cls.db[uri]
except KeyError:
- self = cls.db[key] = cls(uri, generation)
+ self = cls.db[uri] = cls(uri)
self._timestamp = time.time()
return self.status
@classmethod
- def add(cls, uri, generation, *codes):
- status = cls.update(uri, generation)
+ def add(cls, uri, *codes):
+ status = cls.update(uri)
for code in codes:
status.add(code)
@classmethod
- def remove(cls, uri, generation, *codes):
- key = (uri, generation)
- if key in cls.db:
+ def remove(cls, uri, *codes):
+ if uri in cls.db:
for code in codes:
- cls.db[key].status.discard(code)
+ cls.db[uri].status.discard(code)
@classmethod
- def test(cls, uri, generation, code):
- key = (uri, generation)
- return key in cls.db and code in cls.db[key].status
+ def test(cls, uri, code):
+ return uri in cls.db and code in cls.db[uri].status
def install_object(obj):
- fn = uri_to_filename(obj.uri, new_authenticated)
- dn = os.path.dirname(fn)
- #logger.debug("Installing %r by linking %s to %s", obj, obj.fn, fn)
- if not os.path.isdir(dn):
- os.makedirs(dn)
- os.link(obj.fn, fn)
-
-
-def final_install():
-
- cur_link = old_authenticated
- new_link = cur_link + ".new"
- old_link = cur_link + ".old"
- dir_base = os.path.realpath(cur_link + ".")
- new_real = os.path.realpath(new_authenticated)
- old_real = os.path.realpath(old_authenticated)
-
- if os.path.islink(old_link):
- logger.debug("Unlinking %s", old_link)
- os.unlink(old_link)
-
- if os.path.isdir(old_real):
- logger.debug("Symlinking %s to %s", os.path.basename(old_real), old_link)
- os.symlink(os.path.basename(old_real), old_link)
-
- logger.debug("Symlinking %s to %s", os.path.basename(new_real), cur_link)
- os.symlink(os.path.basename(new_real), new_link)
- os.rename(new_link, cur_link)
-
- for path in os.listdir(os.path.dirname(dir_base)):
- path = os.path.realpath(os.path.join(os.path.dirname(dir_base), path))
- if path.startswith(dir_base) and path not in (new_real, old_real) and os.path.isdir(path):
- logger.debug("Removing %s", path)
- shutil.rmtree(path)
+ obj.obj.authenticated.add(authenticated)
+ obj.obj.save()
class X509StoreCTX(rpki.POW.X509StoreCTX):
@@ -184,7 +123,7 @@ class X509(rpki.POW.X509):
def __repr__(self):
try:
- return "<X509 \"{}\" {} at 0x{:x}>".format(self.uri, self.generation, id(self))
+ return "<X509 \"{}\" at 0x{:x}>".format(self.uri, id(self))
except:
return "<X509 at 0x{:x}>".format(id(self))
@@ -199,30 +138,31 @@ class X509(rpki.POW.X509):
uri = uri,
aki = "" if aki is None else aki.encode("hex"),
ski = "" if ski is None else ski.encode("hex"),
- hash = sha256(der).encode("hex"),
+ sha256 = sha256(der).encode("hex"),
retrieved = retrieval))
+ @property
+ def uri(self):
+ return self.obj.uri
+
+ @property
+ def aki(self):
+ return self.obj.aki
+
+ @property
+ def ski(self):
+ return self.obj.ski
+
@classmethod
- def derReadURI(cls, uri, generation, cms = None):
- fn = uri_to_filename(uri, generation.tree)
- if not os.path.exists(fn):
- Status.add(uri, generation, codes.OBJECT_NOT_FOUND)
- return None
- if cms is None:
- with open(fn, "rb") as f:
- der = f.read()
- else:
- # XXX awful kludge to work around current lack of subclass
- # support in rpki.POW.CMS.certs().
+ def load(cls, obj, cms = None):
+ if cms is not None:
+ # XXX Kludge to work around lack of subclass support in rpki.POW.CMS.certs().
der = cms.certs()[0].derWrite()
+ else:
+ der = obj.der
self = cls.derRead(der)
- self.uri = uri
- self.fn = fn
- self.generation = generation
- self.sha256 = sha256(der) if cms is None else None
+ self.obj = obj
self.bc = self.getBasicConstraints()
- self.aki = self.getAKI()
- self.ski = self.getSKI()
self.eku = self.getEKU()
self.aia = self.getAIA()
self.sia = self.getSIA()
@@ -243,7 +183,7 @@ class X509(rpki.POW.X509):
def check(self, trusted, crl):
#logger.debug("Starting checks for %r", self)
- status = Status.update(self.uri, self.generation)
+ status = Status.update(self.uri)
is_ta = trusted is None
is_routercert = (self.eku is not None and id_kp_bgpsec_router in self.eku and
not self.is_ca and self.uri.endswith(".cer"))
@@ -263,7 +203,7 @@ class X509(rpki.POW.X509):
status.add(codes.CRLDP_EXTENSION_FORBIDDEN)
if not is_ta and self.crldp is None:
status.add(codes.CRLDP_EXTENSION_MISSING)
- if not is_ta and self.aki is None:
+ if not is_ta and not self.aki:
status.add(codes.AKI_EXTENSION_MISSING)
elif not is_ta and self.aki != trusted[0].ski:
status.add(codes.AKI_EXTENSION_ISSUER_MISMATCH)
@@ -306,7 +246,7 @@ class CRL(rpki.POW.CRL):
def __repr__(self):
try:
- return "<CRL \"{}\" {} at 0x{:x}>".format(self.uri, self.generation, id(self))
+ return "<CRL \"{}\" at 0x{:x}>".format(self.uri, id(self))
except:
return "<CRL at 0x{:x}>".format(id(self))
@@ -320,30 +260,32 @@ class CRL(rpki.POW.CRL):
uri = uri,
aki = "" if aki is None else aki.encode("hex"),
ski = "",
- hash = sha256(der).encode("hex"),
+ sha256 = sha256(der).encode("hex"),
retrieved = retrieval))
+ @property
+ def uri(self):
+ return self.obj.uri
+
+ @property
+ def aki(self):
+ return self.obj.aki
+
+ @property
+ def ski(self):
+ return ""
+
@classmethod
- def derReadURI(cls, uri, generation):
- fn = uri_to_filename(uri, generation.tree)
- if not os.path.exists(fn):
- Status.add(uri, generation, codes.OBJECT_NOT_FOUND)
- return None
- with open(fn, "rb") as f:
- der = f.read()
- self = cls.derRead(der)
- self.uri = uri
- self.fn = fn
- self.generation = generation
- self.sha256 = sha256(der)
- self.aki = self.getAKI()
+ def load(cls, obj):
+ self = cls.derRead(obj.der)
+ self.obj = obj
self.thisUpdate = self.getThisUpdate()
self.nextUpdate = self.getNextUpdate()
self.number = self.getCRLNumber()
return self
def check(self, issuer):
- status = Status.update(self.uri, self.generation)
+ status = Status.update(self.uri)
self.checkRPKIConformance(status = status, issuer = issuer)
try:
self.verify(issuer)
@@ -366,7 +308,7 @@ class CRL(rpki.POW.CRL):
status.add(codes.CRL_NUMBER_OUT_OF_RANGE)
if self.getIssuer() != issuer.getSubject():
status.add(codes.CRL_ISSUER_NAME_MISMATCH)
- if self.aki is None:
+ if not self.aki:
status.add(codes.AKI_EXTENSION_MISSING)
elif self.aki != issuer.ski:
status.add(codes.AKI_EXTENSION_ISSUER_MISMATCH)
@@ -388,37 +330,40 @@ class CMS_Mixin(object):
uri = uri,
aki = "" if aki is None else aki.encode("hex"),
ski = "" if ski is None else ski.encode("hex"),
- hash = sha256(der).encode("hex"),
+ sha256 = sha256(der).encode("hex"),
retrieved = retrieval))
+ @property
+ def uri(self):
+ return self.obj.uri
+
+ @property
+ def aki(self):
+ return self.obj.aki
+
+ @property
+ def ski(self):
+ return self.obj.ski
+
class Ghostbuster(rpki.POW.CMS, CMS_Mixin):
def __repr__(self):
try:
- return "<Ghostbuster \"{}\" {} at 0x{:x}>".format(self.uri, self.generation, id(self))
+ return "<Ghostbuster \"{}\" at 0x{:x}>".format(self.uri, id(self))
except:
return "<Ghostbuster at 0x{:x}>".format(id(self))
@classmethod
- def derReadURI(cls, uri, generation):
- fn = uri_to_filename(uri, generation.tree)
- if not os.path.exists(fn):
- Status.add(uri, generation, codes.OBJECT_NOT_FOUND)
- return None
- with open(fn, "rb") as f:
- der = f.read()
- self = cls.derRead(der)
- self.uri = uri
- self.fn = fn
- self.generation = generation
- self.sha256 = sha256(der)
- self.ee = X509.derReadURI(uri, generation, self)
+ def load(cls, obj):
+ self = cls.derRead(obj.der)
+ self.obj = obj
+ self.ee = X509.load(obj, self)
self.vcard = None
return self
def check(self, trusted, crl):
- status = Status.update(self.uri, self.generation)
+ status = Status.update(self.uri)
self.ee.check(trusted = trusted, crl = crl)
try:
self.vcard = self.verify()
@@ -434,32 +379,23 @@ class Manifest(rpki.POW.Manifest, CMS_Mixin):
def __repr__(self):
try:
- return "<Manifest \"{}\" {} at 0x{:x}>".format(self.uri, self.generation, id(self))
+ return "<Manifest \"{}\" at 0x{:x}>".format(self.uri, id(self))
except:
return "<Manifest at 0x{:x}>".format(id(self))
@classmethod
- def derReadURI(cls, uri, generation):
- fn = uri_to_filename(uri, generation.tree)
- if not os.path.exists(fn):
- Status.add(uri, generation, codes.OBJECT_NOT_FOUND)
- return None
- with open(fn, "rb") as f:
- der = f.read()
- self = cls.derRead(der)
- self.uri = uri
- self.fn = fn
- self.ee = X509.derReadURI(uri, generation, self)
+ def load(cls, obj):
+ self = cls.derRead(obj.der)
+ self.obj = obj
+ self.ee = X509.load(obj, self)
self.fah = None
- self.generation = generation
- self.sha256 = sha256(der)
self.thisUpdate = None
self.nextUpdate = None
self.number = None
return self
def check(self, trusted, crl):
- status = Status.update(self.uri, self.generation)
+ status = Status.update(self.uri)
self.ee.check(trusted = trusted, crl = crl)
try:
self.verify()
@@ -483,41 +419,32 @@ class Manifest(rpki.POW.Manifest, CMS_Mixin):
codes.normalize(status)
return not any(s.kind == "bad" for s in status)
- def find_crl_uris(self):
+ def find_crl_candidate_hashes(self):
diruri = self.uri[:self.uri.rindex("/") + 1]
for fn, digest in self.fah:
if fn.endswith(".crl"):
- yield diruri + fn, digest
+ yield digest.encode("hex")
class ROA(rpki.POW.ROA, CMS_Mixin):
def __repr__(self):
try:
- return "<ROA \"{}\" {} at 0x{:x}>".format(self.uri, self.generation, id(self))
+ return "<ROA \"{}\" at 0x{:x}>".format(self.uri, id(self))
except:
return "<ROA at 0x{:x}>".format(id(self))
@classmethod
- def derReadURI(cls, uri, generation):
- fn = uri_to_filename(uri, generation.tree)
- if not os.path.exists(fn):
- Status.add(uri, generation, codes.OBJECT_NOT_FOUND)
- return None
- with open(fn, "rb") as f:
- der = f.read()
- self = cls.derRead(der)
- self.uri = uri
- self.fn = fn
- self.generation = generation
- self.sha256 = sha256(der)
- self.ee = X509.derReadURI(uri, generation, self)
+ def load(cls, obj):
+ self = cls.derRead(obj.der)
+ self.obj = obj
+ self.ee = X509.load(obj, self)
self.asn = None
self.prefixes = None
return self
def check(self, trusted, crl):
- status = Status.update(self.uri, self.generation)
+ status = Status.update(self.uri)
self.ee.check(trusted = trusted, crl = crl)
try:
vcard = self.verify()
@@ -543,6 +470,24 @@ def uri_to_class(uri):
return cls
+# If we find ourselves using this same ordering for every retrieval from the RPKIObjects model, we
+# can add it as a Meta option for the model and omit it in the query expressions, like this:
+#
+# class RPKIObjects(models.Model):
+# ...
+# class Meta:
+# ordering = ["-retrieved__started"]
+#
+# https://docs.djangoproject.com/en/1.8/ref/models/querysets/#order-by
+# https://docs.djangoproject.com/en/1.8/ref/models/options/#django.db.models.Options.ordering
+
+def fetch_objects(**kwargs):
+ for obj in rpki.rcynicdb.models.RPKIObject.objects.filter(**kwargs).order_by("-retrieved__started"):
+ cls = uri_to_class(obj.uri)
+ if cls is not None:
+ yield cls.load(obj)
+
+
class WalkFrame(object):
"""
Certificate tree walk stack frame. This is basically just a
@@ -591,81 +536,56 @@ class WalkFrame(object):
#logger.debug("%r scanning products", self)
- mft_uri = first_rsync_uri(self.cer.rpkiManifest)
-
- crl_candidates = []
-
# NB: CRL checks on manifest EE certificates deferred until we've picked a CRL.
- current_mft = Manifest.derReadURI(mft_uri, Generation.current)
- if current_mft is not None and current_mft.check(trusted = self.trusted, crl = None):
- crl_candidates.extend(current_mft.find_crl_uris())
- else:
- current_mft = None
+ mft_candidates = []
+ crl_candidates = []
+ crl_candidate_hashes = set()
- backup_mft = Manifest.derReadURI(mft_uri, Generation.backup)
- if backup_mft is not None and backup_mft.check(trusted = self.trusted, crl = None):
- crl_candidates.extend(backup_mft.find_crl_uris())
- else:
- backup_mft = None
- Status.remove(mft_uri, Generation.backup, codes.OBJECT_NOT_FOUND)
+ for mft in fetch_objects(aki = self.cer.ski, uri__endswith = ".mft"):
+ if mft.check(trusted = self.trusted, crl = None):
+ mft_candidates.append(mft)
+ crl_candidate_hashes.update(mft.find_crl_candidate_hashes())
- if current_mft is None and backup_mft is None:
+ if not mft_candidates:
wsk.pop()
return
- self.crl = None
- crls = {}
- for uri, digest in crl_candidates:
- for generation in (Generation.current, Generation.backup):
- try:
- crl = crls[uri, generation]
- except KeyError:
- crl = crls[uri, generation] = CRL.derReadURI(uri, generation)
- if crl == self.crl:
- continue
- if crl is None and generation == Generation.backup:
- Status.remove(uri, generation, codes.OBJECT_NOT_FOUND)
- if crl is None:
- continue
- if crl.sha256 != digest:
- #Status.add(uri, generation, codes.DIGEST_MISMATCH)
- continue
- if not crl.check(self.trusted[0]) or (self.crl is not None and crl.number < self.crl.number):
- continue
- if self.crl is None or crl.number > self.crl.number or crl.thisUpdate > self.crl.thisUpdate:
- self.crl = crl
+ for crl in fetch_objects(aki = self.cer.ski, uri__endswith = ".crl", sha256__in = crl_candidate_hashes):
+ if crl.check(self.trusted[0]):
+ crl_candidates.append(crl)
+
+ mft_candidates.sort(reverse = True, key = lambda x: (x.number, x.thisUpdate, x.obj.retrieved.started))
+ crl_candidates.sort(reverse = True, key = lambda x: (x.number, x.thisUpdate, x.obj.retrieved.started))
- if self.crl is None:
+ if not crl_candidates:
wsk.pop()
return
- install_object(self.crl)
- Status.add(self.crl.uri, self.crl.generation, codes.OBJECT_ACCEPTED)
-
- #logger.debug("Picked %s CRL %s", self.crl.generation, self.crl.uri)
+ self.crl = crl_candidates[0]
- if current_mft is not None and self.crl.isRevoked(current_mft.ee):
- Status.add(current_mft.uri, current_mft.generation, codes.MANIFEST_EE_REVOKED)
- current_mft = None
+ install_object(self.crl)
+ Status.add(self.crl.uri, codes.OBJECT_ACCEPTED)
- if backup_mft is not None and self.crl.isRevoked(backup_mft.ee):
- Status.add(backup_mft.uri, backup_mft.generation, codes.MANIFEST_EE_REVOKED)
- backup_mft = None
+ #logger.debug("Picked CRL %s", self.crl.uri)
- if current_mft is not None:
- self.mft = current_mft
- elif backup_mft is not None:
- self.mft = backup_mft
+ for mft in mft_candidates:
+ if self.crl.isRevoked(mft.ee):
+ Status.add(mft.obj.uri, codes.MANIFEST_EE_REVOKED)
+ continue
+ self.mft = mft
+ break
else:
wsk.pop()
return
install_object(self.mft)
- Status.add(mft_uri, self.mft.generation, codes.OBJECT_ACCEPTED)
+ Status.add(self.mft.obj.uri, codes.OBJECT_ACCEPTED)
+
+ self.stale_crl = Status.test(self.crl.uri, codes.STALE_CRL_OR_MANIFEST)
+ self.stale_mft = Status.test(self.mft.uri, codes.STALE_CRL_OR_MANIFEST)
- self.stale_crl = Status.test(self.crl.uri, self.crl.generation, codes.STALE_CRL_OR_MANIFEST)
- self.stale_mft = Status.test(self.mft.uri, self.mft.generation, codes.STALE_CRL_OR_MANIFEST)
+ # Issue warnings on mft and crl URI mismatches?
# Use an explicit iterator so we can resume it; run loop in separate method, same reason.
@@ -697,33 +617,29 @@ class WalkFrame(object):
Status.add(uri, None, codes.INAPPROPRIATE_OBJECT_TYPE_SKIPPED)
continue
- for generation in (Generation.current, Generation.backup):
- obj = cls.derReadURI(uri, generation)
- if obj is None and generation is Generation.current:
- Status.add(uri, generation, codes.OBJECT_NOT_FOUND)
+ for obj in fetch_objects(sha256 = digest.encode("hex")):
+
+ # This can't happen as currently written, but we need to handle OBJECT_NOT_FOUND somehow,
+ # this will do as a placeholder until we figure out how this should work.
+
if obj is None:
+ Status.add(uri, codes.OBJECT_NOT_FOUND)
continue
+
if self.stale_crl:
- Status.add(uri, generation, codes.TAINTED_BY_STALE_CRL)
+ Status.add(uri, codes.TAINTED_BY_STALE_CRL)
if self.stale_mft:
- Status.add(uri, generation, codes.TAINTED_BY_STALE_MANIFEST)
- ok = obj.check(trusted = self.trusted, crl = self.crl)
- if obj.sha256 != digest:
- Status.add(uri, generation, codes.DIGEST_MISMATCH)
- ok = False
- if ok:
+ Status.add(uri, codes.TAINTED_BY_STALE_MANIFEST)
+
+ if obj.check(trusted = self.trusted, crl = self.crl):
install_object(obj)
- Status.add(uri, generation, codes.OBJECT_ACCEPTED)
+ Status.add(uri, codes.OBJECT_ACCEPTED)
+ if cls is X509 and obj.is_ca:
+ wsk.push(obj)
+ return
break
else:
- Status.add(uri, generation, codes.OBJECT_REJECTED)
-
- else:
- continue
-
- if ok and cls is X509 and obj.is_ca:
- wsk.push(obj)
- return
+ Status.add(uri, codes.OBJECT_REJECTED)
wsk.pop()
@@ -945,7 +861,7 @@ class Fetcher(object):
with open(fn, "rb") as f:
cls.store_if_new(f.read(), uri, retrieval)
except:
- Status.add(uri, Generation.current, codes.UNREADABLE_OBJECT)
+ Status.add(uri, codes.UNREADABLE_OBJECT)
logger.exception("Couldn't read %s from rsync tree", uri)
finally:
@@ -974,27 +890,24 @@ class CheckTALTask(object):
@tornado.gen.coroutine
def __call__(self):
yield Fetcher(self.uri).fetch()
- if self.check(Generation.current):
- yield task_queue.put(WalkTask(cer = self.cer))
- elif self.check(Generation.backup):
- yield task_queue.put(WalkTask(cer = self.cer))
+ for cer in fetch_objects(uri = self.uri):
+ if self.check(cer):
+ yield task_queue.put(WalkTask(cer = cer))
+ break
else:
- Status.add(self.uri, None, codes.TRUST_ANCHOR_SKIPPED)
-
- def check(self, generation):
- self.cer = X509.derReadURI(self.uri, generation)
- ok = False
- if self.cer is None:
- Status.add(self.uri, generation, codes.UNREADABLE_TRUST_ANCHOR)
- elif self.key.derWritePublic() != self.cer.getPublicKey().derWritePublic():
- Status.add(self.uri, generation, codes.TRUST_ANCHOR_KEY_MISMATCH)
+ Status.add(self.uri, codes.TRUST_ANCHOR_SKIPPED)
+
+ def check(self, cer):
+ if self.key.derWritePublic() != cer.getPublicKey().derWritePublic():
+ Status.add(self.uri, codes.TRUST_ANCHOR_KEY_MISMATCH)
+ ok = False
else:
- ok = self.cer.check(trusted = None, crl = None)
+ ok = cer.check(trusted = None, crl = None)
if ok:
- install_object(self.cer)
- Status.add(self.uri, generation, codes.OBJECT_ACCEPTED)
+ install_object(cer)
+ Status.add(self.uri, codes.OBJECT_ACCEPTED)
else:
- Status.add(self.uri, generation, codes.OBJECT_REJECTED)
+ Status.add(self.uri, codes.OBJECT_REJECTED)
return ok
@@ -1021,20 +934,16 @@ def final_report():
for s in Status.db.itervalues():
if codes.OBJECT_ACCEPTED in s.status:
s.status.discard(codes.OBJECT_REJECTED)
- if s.generation is Generation.backup:
- if Status.test(s.uri, Generation.current, codes.OBJECT_ACCEPTED):
- s.status.discard(codes.OBJECT_REJECTED)
- s.status.discard(codes.OBJECT_NOT_FOUND)
doc = Element("rcynic-summary") # rcynic-version = "", summary-version = "", reporting-hostname = ""
labels = SubElement(doc, "labels")
for code in codes.all():
SubElement(labels, code.name).text = code.text
- for uri, generation in Status.db:
- for sym in sorted(Status.db[uri, generation].status):
+ for uri in Status.db:
+ for sym in sorted(Status.db[uri].status):
SubElement(doc, "validation_status",
- timestamp = str(Status.db[uri, generation].timestamp),
+ timestamp = str(Status.db[uri].timestamp),
status = str(sym),
- generation = str(generation)
+ generation = "None" # Historical relic, remove eventually
).text = uri
#
# Should generate <rsync_history/> elements here too, later
@@ -1090,22 +999,19 @@ def main():
global rpki
import rpki.rcynicdb
- global new_authenticated, old_authenticated
- new_authenticated = args.authenticated.rstrip("/") + time.strftime(".%Y-%m-%dT%H:%M:%SZ")
- old_authenticated = args.authenticated.rstrip("/")
-
- Generation("current", args.unauthenticated)
- Generation("backup", old_authenticated)
-
logging.basicConfig(level = logging.DEBUG, format = "%(asctime)s %(message)s", datefmt = "%Y-%m-%d %H:%M:%S")
+ global authenticated
+ authenticated = rpki.rcynicdb.models.Authenticated.objects.create(started = datetime.datetime.now())
+
global task_queue
task_queue = tornado.queues.Queue()
tornado.ioloop.IOLoop.current().run_sync(launcher)
final_report()
- final_install()
+ authenticated.finished = datetime.datetime.now()
+ authenticated.save()
if __name__ == "__main__":
diff --git a/rpki/rcynicdb/models.py b/rpki/rcynicdb/models.py
index 318f87e3..32b894fc 100644
--- a/rpki/rcynicdb/models.py
+++ b/rpki/rcynicdb/models.py
@@ -32,16 +32,17 @@ from django.db import models
# be specific to the original poster's desired semantics.
class Retrieval(models.Model):
- uri = models.TextField()
- started = models.DateTimeField()
- finished = models.DateTimeField()
+ uri = models.TextField()
+ started = models.DateTimeField()
+ finished = models.DateTimeField()
successful = models.BooleanField()
# Collection of validated objects (like current
# rsync-data/authenticated.yyyy-mm-ddTHH:MM:SS/ tree)
class Authenticated(models.Model):
- timestamp = models.DateTimeField()
+ started = models.DateTimeField()
+ finished = models.DateTimeField(null = True)
# One instance of an RRDP snapshot.
#
@@ -55,24 +56,18 @@ class Authenticated(models.Model):
class RRDPSnapshot(models.Model):
timestamp = models.DateTimeField()
- uuid = models.UUIDField()
- serial = models.BigIntegerField()
+ uuid = models.UUIDField()
+ serial = models.BigIntegerField()
retrieved = models.OneToOneField(Retrieval)
# RPKI objects.
class RPKIObject(models.Model):
- der = models.BinaryField(unique = True)
- uri = models.TextField()
- aki = models.SlugField(max_length = 40) # hex SHA-1
- ski = models.SlugField(max_length = 40) # hex SHA-1
- hash = models.SlugField(max_length = 64) # hex SHA-256
- retrieved = models.ForeignKey(Retrieval)
+ der = models.BinaryField(unique = True)
+ uri = models.TextField()
+ aki = models.SlugField(max_length = 40) # hex SHA-1
+ ski = models.SlugField(max_length = 40) # hex SHA-1
+ sha256 = models.SlugField(max_length = 64) # hex SHA-256
+ retrieved = models.ForeignKey(Retrieval)
authenticated = models.ManyToManyField(Authenticated)
- snapshot = models.ManyToManyField(RRDPSnapshot)
-
-# No exact analogue to current unauthenticated tree. Generally, when
-# we would have looked in the unauthenticated tree we want the most
-# recently retrieved copy of a particular object, but particular
-# object gets a little weird in RRDP universe. See Tim's draft, not
-# gospel but best worked example available to date.
+ snapshot = models.ManyToManyField(RRDPSnapshot)