diff options
author | Rob Austein <sra@hactrn.net> | 2012-10-26 15:15:48 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2012-10-26 15:15:48 +0000 |
commit | 0382c2893986cbe187f8435bccd8e1ba6e4b76fc (patch) | |
tree | acebbea6b9788832170a7d1b1e68f11a26d428dc | |
parent | c0bf5482815e73d65490b8fc60753f18f233ee11 (diff) |
Teach MySQLdb converter interface about rpki.sundial.datetime.
svn path=/branches/tk274/; revision=4794
-rw-r--r-- | rpkid/rpki/gui/app/views.py | 2 | ||||
-rw-r--r-- | rpkid/rpki/irdb/models.py | 7 | ||||
-rw-r--r-- | rpkid/rpki/mysql_import.py | 2 | ||||
-rw-r--r-- | rpkid/rpki/sql.py | 10 | ||||
-rw-r--r-- | rpkid/rpki/sundial.py | 46 | ||||
-rw-r--r-- | rpkid/tests/smoketest.py | 25 | ||||
-rw-r--r-- | rpkid/tests/yamlconf.py | 2 | ||||
-rw-r--r-- | rpkid/tests/yamltest.py | 2 |
8 files changed, 67 insertions, 29 deletions
diff --git a/rpkid/rpki/gui/app/views.py b/rpkid/rpki/gui/app/views.py index 6ba6f1c4..cd4a58f7 100644 --- a/rpkid/rpki/gui/app/views.py +++ b/rpkid/rpki/gui/app/views.py @@ -414,7 +414,7 @@ def child_edit(request, pk): if request.method == 'POST': form = form_class(request.POST, request.FILES) if form.is_valid(): - child.valid_until = sundial.datetime.fromdatetime(form.cleaned_data.get('valid_until')) + child.valid_until = sundial.datetime.from_datetime(form.cleaned_data.get('valid_until')) child.save() # remove AS & prefixes that are not selected in the form models.ChildASN.objects.filter(child=child).exclude(pk__in=form.cleaned_data.get('as_ranges')).delete() diff --git a/rpkid/rpki/irdb/models.py b/rpkid/rpki/irdb/models.py index e408612e..5c6d7701 100644 --- a/rpkid/rpki/irdb/models.py +++ b/rpkid/rpki/irdb/models.py @@ -98,14 +98,14 @@ class SundialField(django.db.models.DateTimeField): def to_python(self, value): if isinstance(value, rpki.sundial.pydatetime.datetime): - return rpki.sundial.datetime.fromdatetime( + return rpki.sundial.datetime.from_datetime( django.db.models.DateTimeField.to_python(self, value)) else: return value def get_prep_value(self, value): if isinstance(value, rpki.sundial.datetime): - return value.to_sql() + return value.to_datetime() else: return value @@ -307,8 +307,7 @@ class CA(django.db.models.Model): def generate_crl(self): now = rpki.sundial.now() self.revocations.filter(expires__lt = now).delete() - revoked = [(r.serial, rpki.sundial.datetime.fromdatetime(r.revoked)) - for r in self.revocations.all()] + revoked = [(r.serial, r.revoked) for r in self.revocations.all()] self.latest_crl = rpki.x509.CRL.generate( keypair = self.private_key, issuer = self.certificate, diff --git a/rpkid/rpki/mysql_import.py b/rpkid/rpki/mysql_import.py index 40ccc348..e7b54dde 100644 --- a/rpkid/rpki/mysql_import.py +++ b/rpkid/rpki/mysql_import.py @@ -61,3 +61,5 @@ else: import _mysql_exceptions warnings.simplefilter("error", _mysql_exceptions.Warning) + +import MySQLdb.converters diff --git a/rpkid/rpki/sql.py b/rpkid/rpki/sql.py index c4c9a7ae..d4426680 100644 --- a/rpkid/rpki/sql.py +++ b/rpkid/rpki/sql.py @@ -59,13 +59,21 @@ class session(object): self.database = cfg.get("sql-database") self.password = cfg.get("sql-password") + self.conv = MySQLdb.converters.conversions.copy() + self.conv.update({ + rpki.sundial.datetime : MySQLdb.converters.DateTime2literal, + MySQLdb.converters.FIELD_TYPE.DATETIME : rpki.sundial.datetime.DateTime_or_None }) + self.cache = weakref.WeakValueDictionary() self.dirty = set() self.connect() def connect(self): - self.db = MySQLdb.connect(user = self.username, db = self.database, passwd = self.password) + self.db = MySQLdb.connect(user = self.username, + db = self.database, + passwd = self.password, + conv = self.conv) self.cur = self.db.cursor() self.db.autocommit(True) self.timestamp = rpki.sundial.now() diff --git a/rpkid/rpki/sundial.py b/rpkid/rpki/sundial.py index 2f333b40..95a44142 100644 --- a/rpkid/rpki/sundial.py +++ b/rpkid/rpki/sundial.py @@ -91,13 +91,24 @@ class datetime(pydatetime.datetime): return self.toXMLtime() @classmethod - def fromdatetime(cls, x): + def from_datetime(cls, x): """ Convert a datetime.datetime object into this subclass. This is whacky due to the weird constructors for datetime. """ return cls.combine(x.date(), x.time()) + def to_datetime(self): + """ + Convert to a datetime.datetime object. In most cases this + shouldn't be necessary, but convincing SQL interfaces to use + subclasses of datetime can be hard. + """ + return pydatetime.datetime(year = self.year, month = self.month, day = self.day, + hour = self.hour, minute = self.minute, second = self.second, + microsecond = 0, tzinfo = None) + + @classmethod def fromOpenSSL(cls, x): """ @@ -113,22 +124,13 @@ class datetime(pydatetime.datetime): """ Convert from SQL storage format. """ - return cls.fromdatetime(x) + return cls.from_datetime(x) def to_sql(self): """ Convert to SQL storage format. - - There's something whacky going on in the MySQLdb module, it throws - range errors when storing a derived type into a DATETIME column. - Investigate some day, but for now brute force this by copying the - relevant fields into a datetime.datetime for MySQLdb's - consumption. - """ - return pydatetime.datetime(year = self.year, month = self.month, day = self.day, - hour = self.hour, minute = self.minute, second = self.second, - microsecond = 0, tzinfo = None) + return self.to_datetime() def later(self, other): """ @@ -147,6 +149,24 @@ class datetime(pydatetime.datetime): def __rsub__(self, y): return _cast(pydatetime.datetime.__rsub__(self, y)) def __sub__(self, y): return _cast(pydatetime.datetime.__sub__(self, y)) + @classmethod + def DateTime_or_None(cls, s): + """ + MySQLdb converter. Parse as this class if we can, let the default + MySQLdb DateTime_or_None() converter deal with failure cases. + """ + + for sep in " T": + d, _, t = s.partition(sep) + if t: + try: + return cls(*[int(x) for x in d.split("-") + t.split(":")]) + except: + break + + from rpki.mysql_import import MySQLdb + return MySQLdb.times.DateTime_or_None(s) + class timedelta(pydatetime.timedelta): """ Timedelta with text parsing. This accepts two input formats: @@ -245,7 +265,7 @@ def _cast(x): Cast result of arithmetic operations back into correct subtype. """ if isinstance(x, pydatetime.datetime): - return datetime.fromdatetime(x) + return datetime.from_datetime(x) if isinstance(x, pydatetime.timedelta): return timedelta.fromtimedelta(x) return x diff --git a/rpkid/tests/smoketest.py b/rpkid/tests/smoketest.py index 4bc6e715..67e31fed 100644 --- a/rpkid/tests/smoketest.py +++ b/rpkid/tests/smoketest.py @@ -174,6 +174,11 @@ class CouldntIssueBSCEECertificate(Exception): Couldn't issue BSC EE certificate """ +sql_conversions = MySQLdb.converters.conversions.copy() +sql_conversions.update({ + rpki.sundial.datetime : MySQLdb.converters.DateTime2literal, + MySQLdb.converters.FIELD_TYPE.DATETIME : rpki.sundial.datetime.DateTime_or_None }) + def main(): """ Main program. @@ -500,7 +505,7 @@ class allocation(object): self.kids = [allocation(k, db, self) for k in yaml.get("kids", ())] valid_until = None if "valid_until" in yaml: - valid_until = rpki.sundial.datetime.fromdatetime(yaml.get("valid_until")) + valid_until = rpki.sundial.datetime.from_datetime(yaml.get("valid_until")) if valid_until is None and "valid_for" in yaml: valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(yaml["valid_for"]) self.base = rpki.resource_set.resource_bag( @@ -573,7 +578,7 @@ class allocation(object): cb() def apply_valid_until(self, stamp, cb): - self.base.valid_until = rpki.sundial.datetime.fromdatetime(stamp) + self.base.valid_until = rpki.sundial.datetime.from_datetime(stamp) cb() def apply_valid_for(self, text, cb): @@ -729,7 +734,8 @@ class allocation(object): Set up this entity's IRDB. """ rpki.log.info("Setting up MySQL for %s" % self.name) - db = MySQLdb.connect(user = "rpki", db = self.rpki_db_name, passwd = rpki_db_pass) + db = MySQLdb.connect(user = "rpki", db = self.rpki_db_name, passwd = rpki_db_pass, + conv = sql_conversions) cur = db.cursor() db.autocommit(True) for sql in rpki_sql: @@ -739,7 +745,8 @@ class allocation(object): if "DROP TABLE IF EXISTS" not in sql.upper(): raise db.close() - db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass) + db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass, + conv = sql_conversions) cur = db.cursor() db.autocommit(True) for sql in irdb_sql: @@ -751,7 +758,7 @@ class allocation(object): for s in [self] + self.hosts: for kid in s.kids: cur.execute("INSERT registrant (registrant_handle, registry_handle, valid_until) VALUES (%s, %s, %s)", - (kid.name, s.name, kid.resources.valid_until.to_sql())) + (kid.name, s.name, kid.resources.valid_until)) db.close() def sync_sql(self): @@ -761,7 +768,8 @@ class allocation(object): this entity. """ rpki.log.info("Updating MySQL data for IRDB %s" % self.name) - db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass) + db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass, + conv = sql_conversions) cur = db.cursor() db.autocommit(True) cur.execute("DELETE FROM registrant_asn") @@ -778,7 +786,7 @@ class allocation(object): cur.execute("INSERT registrant_net (start_ip, end_ip, version, registrant_id) VALUES (%s, %s, 4, %s)", (v4_range.min, v4_range.max, registrant_id)) for v6_range in kid.resources.v6: cur.execute("INSERT registrant_net (start_ip, end_ip, version, registrant_id) VALUES (%s, %s, 6, %s)", (v6_range.min, v6_range.max, registrant_id)) - cur.execute("UPDATE registrant SET valid_until = %s WHERE registrant_id = %s", (kid.resources.valid_until.to_sql(), registrant_id)) + cur.execute("UPDATE registrant SET valid_until = %s WHERE registrant_id = %s", (kid.resources.valid_until, registrant_id)) for r in s.roa_requests: cur.execute("INSERT roa_request (roa_request_handle, asn) VALUES (%s, %s)", (s.name, r.asn)) roa_request_id = cur.lastrowid @@ -1201,7 +1209,8 @@ def setup_publication(pubd_sql): if not rsyncd_dir.endswith("/"): rsyncd_dir += "/" os.makedirs(rsyncd_dir + "root/trunk") - db = MySQLdb.connect(db = pubd_db_name, user = pubd_db_user, passwd = pubd_db_pass) + db = MySQLdb.connect(db = pubd_db_name, user = pubd_db_user, passwd = pubd_db_pass, + conv = sql_conversions) cur = db.cursor() db.autocommit(True) for sql in pubd_sql: diff --git a/rpkid/tests/yamlconf.py b/rpkid/tests/yamlconf.py index deb7890f..f9f69ba1 100644 --- a/rpkid/tests/yamlconf.py +++ b/rpkid/tests/yamlconf.py @@ -189,7 +189,7 @@ class allocation(object): self.kids = [allocation(k, db, self) for k in y.get("kids", ())] valid_until = None if "valid_until" in y: - valid_until = rpki.sundial.datetime.fromdatetime(y.get("valid_until")) + valid_until = rpki.sundial.datetime.from_datetime(y.get("valid_until")) if valid_until is None and "valid_for" in y: valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(y["valid_for"]) self.base = rpki.resource_set.resource_bag( diff --git a/rpkid/tests/yamltest.py b/rpkid/tests/yamltest.py index a5f72788..609a2599 100644 --- a/rpkid/tests/yamltest.py +++ b/rpkid/tests/yamltest.py @@ -202,7 +202,7 @@ class allocation(object): self.kids = [allocation(k, db, self) for k in yaml.get("kids", ())] valid_until = None if "valid_until" in yaml: - valid_until = rpki.sundial.datetime.fromdatetime(yaml.get("valid_until")) + valid_until = rpki.sundial.datetime.from_datetime(yaml.get("valid_until")) if valid_until is None and "valid_for" in yaml: valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(yaml["valid_for"]) self.base = rpki.resource_set.resource_bag( |