aboutsummaryrefslogtreecommitdiff
path: root/rpkid/rpki/irdb/router.py
blob: 1f27d0c90bfd4965e17d2b45e5cb38324ffc2b61 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# $Id$
# 
# Copyright (C) 2012  Internet Systems Consortium ("ISC")
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
# 
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.

"""
Django-style "Database router".

For most programs, you don't need this.  Django's normal mode of
behavior is to use a single SQL database for the IRDB, which is
normally what we want.  For certain test scenarios, however, it's
useful to be able to use the same Django ORM models and managers with
multiple databases without having to complicate the interface by
passing database names everywhere.  Using a database router
accomplishes this.
"""

class DBContextRouter(object):
  """
  A Django database router for use with multiple IRDBs.

  This router is designed to work in conjunction with the
  rpki.irdb.database context handler (q.v.).
  """

  _app = "irdb"

  _database = None

  def db_for_read(self, model, **hints):
    if model._meta.app_label == self._app:
      return self._database
    else:
      return None

  def db_for_write(self, model, **hints):
    if model._meta.app_label == self._app:
      return self._database
    else:
      return None

  def allow_relation(self, obj1, obj2, **hints):
    if self._database is None:
      return None
    elif obj1._meta.app_label == self._app and obj2._meta.app_label == self._app:
      return True
    else:
      return None

  def allow_syncdb(self, db, model):
    if db == self._database and model._meta.app_label == self._app:
      return True
    else:
      return None

class database(object):
  """
  Context manager for use with DBContextRouter.  Use thusly:

    with rpki.irdb.database("blarg"):
      do_stuff()

  This binds IRDB operations to database blarg for the duration of
  the call to do_stuff(), then restores the prior state.
  """

  def __init__(self, name, on_entry = None, on_exit = None):
    if not isinstance(name, str):
      raise ValueError("database name must be a string, not %r" % name)
    self.name = name
    self.on_entry = on_entry
    self.on_exit = on_exit

  def __enter__(self):
    if self.on_entry is not None:
      self.on_entry()
    self.former = DBContextRouter._database
    DBContextRouter._database = self.name

  def __exit__(self, _type, value, traceback):
    assert DBContextRouter._database is self.name
    DBContextRouter._database = self.former
    if self.on_exit is not None:
      self.on_exit()