aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2012-10-26 15:15:48 +0000
committerRob Austein <sra@hactrn.net>2012-10-26 15:15:48 +0000
commit0382c2893986cbe187f8435bccd8e1ba6e4b76fc (patch)
treeacebbea6b9788832170a7d1b1e68f11a26d428dc
parentc0bf5482815e73d65490b8fc60753f18f233ee11 (diff)
Teach MySQLdb converter interface about rpki.sundial.datetime.
svn path=/branches/tk274/; revision=4794
-rw-r--r--rpkid/rpki/gui/app/views.py2
-rw-r--r--rpkid/rpki/irdb/models.py7
-rw-r--r--rpkid/rpki/mysql_import.py2
-rw-r--r--rpkid/rpki/sql.py10
-rw-r--r--rpkid/rpki/sundial.py46
-rw-r--r--rpkid/tests/smoketest.py25
-rw-r--r--rpkid/tests/yamlconf.py2
-rw-r--r--rpkid/tests/yamltest.py2
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(