aboutsummaryrefslogtreecommitdiff
path: root/rpki/pubd.py
diff options
context:
space:
mode:
Diffstat (limited to 'rpki/pubd.py')
-rw-r--r--rpki/pubd.py151
1 files changed, 133 insertions, 18 deletions
diff --git a/rpki/pubd.py b/rpki/pubd.py
index 42e18e10..46e431c4 100644
--- a/rpki/pubd.py
+++ b/rpki/pubd.py
@@ -107,6 +107,8 @@ class main(object):
self.publication_multimodule = self.cfg.getboolean("publication-multimodule", False)
+ self.session = session_obj.fetch(self)
+
rpki.http.server(
host = self.http_server_host,
port = self.http_server_port,
@@ -183,8 +185,12 @@ class session_obj(rpki.sql.sql_persistent):
sql_template = rpki.sql.template(
"session",
"session_id",
- "uuid",
- "serial")
+ "uuid")
+
+ ## @var expiration_interval
+ # How long to wait after retiring a snapshot before purging it from the database.
+
+ expiration_interval = rpki.sundial.timedelta(hours = 6)
def __repr__(self):
return rpki.log.log_repr(self, self.uuid, self.serial)
@@ -201,27 +207,118 @@ class session_obj(rpki.sql.sql_persistent):
self.gctx = gctx
self.session_id = 1
self.uuid = uuid.uuid4()
- self.serial = 1
self.sql_store()
return self
@property
- @rpki.sql.cache_reference
def objects(self):
- return object_obj.sql_fetch_where(self.gctx, "session_id = %s", (self.session_id))
+ return object_obj.sql_fetch_where(self.gctx, "session_id = %s", (self.session_id,))
- def next_serial_number(self):
+ @property
+ def snapshots(self):
+ return snapshot_obj.sql_fetch_where(self.gctx, "session_id = %s", (self.session_id,))
+
+ @property
+ def current_snapshot(self):
+ return snapshot_obj.sql_fetch_where1(self.gctx,
+ "session_id = %s AND activated IS NOT NULL AND expires IS NULL",
+ (self.session_id,))
+
+ def new_snapshot(self):
+ return snapshot_obj.create(self)
+
+ def add_snapshot(self, new_snapshot):
+ now = rpki.sundial.now()
+ old_snapshot = self.current_snapshot
+ if old_snapshot is not None:
+ old_snapshot.expires = now + self.expiration_interval
+ old_snapshot.sql_store()
+ new_snapshot.activated = now
+ new_snapshot.sql_store()
+
+ def expire_snapshots(self):
+ for snapshot in snapshot_obj.sql_fetch_where(self.gctx,
+ "session_id = %s AND expires IS NOT NULL AND expires < %s",
+ (self.session_id, rpki.sundial.now())):
+ snapshot.sql_delete()
+
+
+class snapshot_obj(rpki.sql.sql_persistent):
+ """
+ An RRDP session snapshot.
+ """
+
+ sql_template = rpki.sql.template(
+ "snapshot",
+ "snapshot_id",
+ ("activated", rpki.sundial.datetime),
+ ("expires", rpki.sundial.datetime),
+ "session_id")
+
+ @property
+ @rpki.sql.cache_reference
+ def session(self):
+ return session_obj.sql_fetch(self.gctx, self.session_id)
+
+ @classmethod
+ def create(cls, session):
+ self = cls()
+ self.gctx = session.gctx
+ self.session_id = session.session_id
+ self.activated = None
+ self.expires = None
+ self.sql_store()
+ return self
+
+ @property
+ def serial(self):
"""
- Bump serial number
+ I know that using an SQL ID for any other purpose is usually a bad
+ idea, but in this case it has exactly the right properties, and we
+ really do want both the autoincrement behavior and the foreign key
+ behavior to tie to the snapshot serial numbers. So risk it.
+
+ Well, OK, only almost the right properties. auto-increment
+ probably does not back up if we ROLLBACK, which could leave gaps
+ in the sequence. So may need to rework this. Ignore for now.
"""
- self.serial += 1
- self.sql_mark_dirty()
- return self.serial
+ return self.snapshot_id
- # More methods when I know what they look like
+ def publish(self, client, obj, uri):
+ # Still a bit confused as to what we should do here. The
+ # overwrite <publish/> with another <publish/> model doens't
+ # really match the IXFR model. Current proposal is an attribute
+ # on <publish/> to say that this is an overwrite, haven't
+ # implemented that yet. Would need to push knowledge of when
+ # we're overwriting all the way from rpkid code that decides to
+ # write each kind of object. In most cases it looks like we
+ # already know, a priori, might be a few corner cases.
+ # Temporary kludge
+ if True:
+ try:
+ self.withdraw(client, uri)
+ except rpki.exceptions.NoObjectAtURI:
+ logger.debug("Withdrew %s", uri)
+ else:
+ logger.debug("No prior %s", uri)
+
+ logger.debug("Publishing %s", uri)
+ return object_obj.create(client, self, obj, uri)
+
+ def withdraw(self, client, uri):
+ obj = object_obj.sql_fetch_where1(self.gctx,
+ "session_id = %s AND client_id = %s AND withdrawn_snapshot_id IS NULL AND uri = %s",
+ (self.session_id, client.client_id, uri))
+ if obj is None:
+ raise rpki.exceptions.NoObjectAtURI("No object published at %s" % uri)
+ logger.debug("Withdrawing %s", uri)
+ obj.delete(self)
+
+
+
class object_obj(rpki.sql.sql_persistent):
"""
A published object.
@@ -233,15 +330,13 @@ class object_obj(rpki.sql.sql_persistent):
"uri",
"hash",
"payload",
- "published",
- "withdrawn")
-
- uri = None
- published = None
- withdrawn = None
+ "published_snapshot_id",
+ "withdrawn_snapshot_id",
+ "client_id",
+ "session_id")
def __repr__(self):
- return rpki.log.log_repr(self, self.uri, self.published, self.withdrawn)
+ return rpki.log.log_repr(self, self.uri, self.published_snapshot_id, self.withdrawn_snapshot_id)
@property
@rpki.sql.cache_reference
@@ -252,3 +347,23 @@ class object_obj(rpki.sql.sql_persistent):
@rpki.sql.cache_reference
def client(self):
return rpki.publication_control.client_elt.sql_fetch(self.gctx, self.client_id)
+
+ @classmethod
+ def create(cls, client, snapshot, obj, uri):
+ self = cls()
+ self.gctx = snapshot.gctx
+ self.uri = uri
+ self.payload = obj
+ self.hash = rpki.x509.sha256(obj.get_Base64())
+ logger.debug("Computed hash %s of %r", self.hash.encode("hex"), obj)
+ self.published_snapshot_id = snapshot.snapshot_id
+ self.withdrawn_snapshot_id = None
+ self.session_id = snapshot.session_id
+ self.client_id = client.client_id
+ self.sql_mark_dirty()
+ return self
+
+ def delete(self, snapshot):
+ self.withdrawn_snapshot_id = snapshot.snapshot_id
+ #self.sql_mark_dirty()
+ self.sql_store()