diff options
author | Rob Austein <sra@hactrn.net> | 2015-10-11 00:11:46 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2015-10-11 00:11:46 +0000 |
commit | 79bf6b0c42fd6e19cf215cc44137650b976e070e (patch) | |
tree | 202401575498608e805359135fe96dc9f5323391 | |
parent | 74210f99e4433ea25a95a8efee5f71ab804e1ded (diff) |
Add XMLTemplate class in an attempt to encapsulate omst of the
XML-related voodoo for models exposed in the left-right protocol.
svn path=/branches/tk705/; revision=6110
-rw-r--r-- | rpki/left_right.py | 22 | ||||
-rw-r--r-- | rpki/rpkidb/models.py | 174 |
2 files changed, 149 insertions, 47 deletions
diff --git a/rpki/left_right.py b/rpki/left_right.py index 3652bfa2..9704291f 100644 --- a/rpki/left_right.py +++ b/rpki/left_right.py @@ -85,7 +85,6 @@ class base_elt(rpki.sql.sql_persistent): attributes = () elements = () booleans = () - text_attribute = None self_id = None self_handle = None @@ -107,8 +106,6 @@ class base_elt(rpki.sql.sql_persistent): setattr(self, key, val) for key in self.booleans: setattr(self, key, elt.get(key, False)) - if self.text_attribute is not None: - setattr(self, self.text_attribute, elt.text) for b64 in elt: assert b64.tag.startswith(xmlns) setattr(self, b64.tag[len(xmlns):], self.elements[b64.tag](Base64 = b64.text)) @@ -439,12 +436,6 @@ class self_elt(base_elt): repositories = set() objects = dict() - def list_handler(r_pdu, repository): - rpki.publication.raise_if_error(r_pdu) - assert r_pdu.tag == rpki.publication.tag_list - assert r_pdu.get("uri") not in objects - objects[r_pdu.get("uri")] = (r_pdu.get("hash"), repository) - def loop(iterator, parent): repository = parent.repository if repository.peer_contact_uri in repositories: @@ -453,11 +444,14 @@ class self_elt(base_elt): q_msg = Element(rpki.publication.tag_msg, nsmap = rpki.publication.nsmap, type = "query", version = rpki.publication.version) SubElement(q_msg, rpki.publication.tag_list, tag = "list") - def handler(r_pdu): - list_handler(r_pdu, repository) - repository.call_pubd(iterator, eb, q_msg, - handlers = dict(list = handler), - length_check = False) + def list_handler(r_pdu): + rpki.publication.raise_if_error(r_pdu) + assert r_pdu.tag == rpki.publication.tag_list + assert r_pdu.get("uri") not in objects + objects[r_pdu.get("uri")] = (r_pdu.get("hash"), repository) + repository.call_pubd(iterator, eb, + q_msg, length_check = False, + handlers = dict(list = list_handler)) def reconcile(uri, obj, repository): h, r = objects.pop(uri, (None, None)) diff --git a/rpki/rpkidb/models.py b/rpki/rpkidb/models.py index f62783ee..8963c1ef 100644 --- a/rpki/rpkidb/models.py +++ b/rpki/rpkidb/models.py @@ -3,13 +3,20 @@ Django ORM models for rpkid. """ from __future__ import unicode_literals + +import logging + from django.db import models + import rpki.left_right from rpki.fields import (EnumField, SundialField, BlobField, CertificateField, KeyField, CRLField, PKCS10Field, ManifestField, ROAField, GhostbusterField) +from lxml.etree import Element, SubElement, tostring as ElementToString + +logger = logging.getLogger(__name__) # The objects available via the left-right protocol allow NULL values # in places we wouldn't otherwise (eg, bpki_cert fields), to support @@ -17,20 +24,113 @@ from rpki.fields import (EnumField, SundialField, BlobField, # gradually. We may want to rethink this eventually, but that yak can # wait for its shave, particularly since disallowing null should be a # very simple change given migrations. + +# The <self/> element was really badly named, but we weren't using +# Python when we named it. Perhaps <tenant/> would be a better name? +# Would want to rename it in left-right too. +# +# To make things worse, <self/> elements are handled slightly +# differently in many places, so there are a number of occurances of +# "self" or "self_handle" as special case magic. Feh. # -# At least for the moment, we use trivial custom managers on these -# classes to provide a simple way of looking up objects from lxml -# objects. Rethink this later if it proves tedious. +# Cope for now, just be careful. + +class XMLTemplate(object): + """ + Encapsulate all the voodoo for transcoding between lxml and ORM. + """ + + # Type map to simplify declaration of Base64 sub-elements. + + element_type = dict(bpki_cert = rpki.x509.X509, + bpki_glue = rpki.x509.X509, + bpki_cms_cert = rpki.x509.X509, + bpki_cms_glue = rpki.x509.X509, + pkcs10_request = rpki.x509.PKCS10, + signing_cert = rpki.x509.X509, + signing_cert_crl = rpki.x509.CRL) + + def __init__(self, name, attributes = (), booleans = (), elements = (), handles = ()): + self.name = name + self.handles = handles + self.attributes = attributes + self.booleans = booleans + self.elements = elements + + def encode(self, obj): + """ + Encode an ORM object as XML. + """ + + xml = Element(rpki.left_right.xmlns + self.name, nsmap = rpki.left_right.nsmap) + xml.set(self.name + "_handle", getattr(obj, self.name + "_handle")) + for k in self.handles: + v = getattr(obj, k.xml.name) + if v is not None: + xml.set(k.xml.name + "_handle", getattr(v, k.xml.name + "_handle")) + for k in self.attributes: + v = getattr(obj, k) + if v is not None: + xml.set(k, str(v)) + for k in self.booleans: + if getattr(obj, k): + xml.set(k, "yes") + for k in self.elements: + v = getattr(obj, k) + if v is not None and not v.empty(): + SubElement(xml, rpki.left_right.xmlns + k).text = v.get_Base64() + return xml + + def decode(self, obj, xml): + """ + Decode XML into an ORM object. + """ + + assert xml.tag == rpki.left_right.xmlns + self.name + setattr(obj, self.name + "_handle", xml.get(self.name + "_handle")) + for k in self.handles: + v = xml.get(k.xml.name + "_handle") + if v is not None: + d = { k.xml.name + "_handle" : v } + if k.xml.name != "self": + d.update(self = obj.self) + setattr(obj, k.xml.name, k.objects.get(**d)) + for k in self.attributes: + v = xml.get(k) + if v is not None: + v.encode("ascii") + if v.isdigit(): + v = long(v) + setattr(obj, k, v) + for k in self.booleans: + v = xml.get(k) + if v is not None: + setattr(obj, k, v == "yes") + for k in self.elements: + v = xml.findtext(rpki.left_right.xmlns + k) + if v and v.strip(): + setattr(obj, k, self.element_type[k](Base64 = v)) -# "self" was a really bad name for this, but we weren't using Python -# when we named it. Perhaps "Tenant" would be a better name? Even -# means sort of the right thing, well, in French anyway. -# Eventually rename in left-right too, I guess. -class SelfManager(models.Manager): - def find_from_xml(self, elt): - assert elt.tag == rpki.left_right.tag_self - return self.get(self_handle = elt.get("self_handle")) +class XMLManager(models.Manager): + """ + Add a .xml_find() method which looks up the object corresponding to + the handles in an XML element. + + This assumes that models which use it have an "xml" class attribute + holding an XMLTemplate object (above). + """ + + def xml_find(self, xml): + name = self.model.xml.name + assert xml.tag == rpki.left_right.xmlns + name + d = { name + "_handle" : xml.get(name + "_handle") } + if name != "self": + d.update(self__self_handle = xml.get("self_handle")) + return self.get(**d) + + +# Models class Self(models.Model): self_handle = models.SlugField(max_length = 255) @@ -39,12 +139,12 @@ class Self(models.Model): regen_margin = models.BigIntegerField(null = True) bpki_cert = CertificateField(null = True) bpki_glue = CertificateField(null = True) - objects = SelfManager() + objects = XMLManager() -class BSCManager(models.Manager): - def find_from_xml(self, elt): - assert elt.tag == rpki.left_right.tag_bsc - return self.get(self__self_handle = elt.get("self_handle"), bsc_handle = elt.get("bsc_handle")) + xml = XMLTemplate(name = "self", + attributes = ("crl_interval", "regen_margin"), + booleans = ("use_hsm",), + elements = ("bpki_cert", "bpki_glue")) class BSC(models.Model): bsc_handle = models.SlugField(max_length = 255) @@ -54,14 +154,14 @@ class BSC(models.Model): signing_cert = CertificateField(null = True) signing_cert_crl = CRLField(null = True) self = models.ForeignKey(Self) - objects = BSCManager() + objects = XMLManager() + class Meta: unique_together = ("self", "bsc_handle") -class RepositoryManager(models.Manager): - def find_from_xml(self, elt): - assert elt.tag == rpki.left_right.tag_repository - return self.get(self__self_handle = elt.get("self_handle"), repository_handle = elt.get("repository_handle")) + xml = XMLTemplate(name = "bsc", + handles = (Self,), + elements = ("signing_cert", "signing_cert_crl", "pkcs10_request")) class Repository(models.Model): repository_handle = models.SlugField(max_length = 255) @@ -71,14 +171,16 @@ class Repository(models.Model): last_cms_timestamp = SundialField(null = True) bsc = models.ForeignKey(BSC) self = models.ForeignKey(Self) - objects = RepositoryManager() + objects = XMLManager() + class Meta: unique_together = ("self", "repository_handle") -class ParentManager(models.Manager): - def find_from_xml(self, elt): - assert elt.tag == rpki.left_right.tag_parent - return self.get(self__self_handle = elt.get("self_handle"), parent_handle = elt.get("parent_handle")) + xml = XMLTemplate(name = "repository", + handles = (Self, BSC), + attributes = ("peer_contact_uri",), + elements = ("bpki_cert", "bpki_glue")) + class Parent(models.Model): parent_handle = models.SlugField(max_length = 255) @@ -92,10 +194,16 @@ class Parent(models.Model): self = models.ForeignKey(Self) bsc = models.ForeignKey(BSC) repository = models.ForeignKey(Repository) - objects = ParentManager() + objects = XMLManager() + class Meta: unique_together = ("self", "parent_handle") + xml = XMLTemplate(name = "parent", + handles = (Self, BSC, Repository), + attributes = ("peer_contact_uri", "sia_base", "sender_name", "recipient_name"), + elements = ("bpki_cms_cert", "bpki_cms_glue")) + class CA(models.Model): last_crl_sn = models.BigIntegerField() last_manifest_sn = models.BigIntegerField() @@ -121,11 +229,6 @@ class CADetail(models.Model): ca_cert_uri = models.TextField(null = True) ca = models.ForeignKey(CA) -class ChildManager(models.Manager): - def find_from_xml(self, elt): - assert elt.tag == rpki.left_right.tag_child - return self.get(self__self_handle = elt.get("self_handle"), child_handle = elt.get("child_handle")) - class Child(models.Model): child_handle = models.SlugField(max_length = 255) bpki_cert = CertificateField(null = True) @@ -133,10 +236,15 @@ class Child(models.Model): last_cms_timestamp = SundialField(null = True) self = models.ForeignKey(Self) bsc = models.ForeignKey(BSC) - objects = ChildManager() + objects = XMLManager() + class Meta: unique_together = ("self", "child_handle") + xml = XMLTemplate(name = "child", + handles = (Self, BSC), + elements = ("bpki_cert", "bpki_glue")) + class ChildCert(models.Model): cert = CertificateField() published = SundialField(null = True) |