]> git.ipfire.org Git - ddns.git/blame - src/ddns/database.py
database: Remove some unused code to close the database
[ddns.git] / src / ddns / database.py
CommitLineData
91aead36 1#!/usr/bin/python3
37e24fbf
MT
2###############################################################################
3# #
4# ddns - A dynamic DNS client for IPFire #
5# Copyright (C) 2014 IPFire development team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
21
22import datetime
63e16fee 23import os
37e24fbf
MT
24import sqlite3
25
26# Initialize the logger.
27import logging
28logger = logging.getLogger("ddns.database")
29logger.propagate = 1
30
31class DDNSDatabase(object):
32 def __init__(self, core, path):
33 self.core = core
63e16fee 34 self.path = path
37e24fbf 35
63e16fee
MT
36 # We won't open the connection to the database directly
37 # so that we do not do it unnecessarily.
38 self._db = None
37e24fbf 39
37e24fbf
MT
40 def _open_database(self, path):
41 logger.debug("Opening database %s" % path)
42
43 exists = os.path.exists(path)
44
45 conn = sqlite3.connect(path, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
46 conn.isolation_level = None
47
63e16fee 48 if not exists and self.is_writable():
37e24fbf
MT
49 logger.debug("Initialising database layout")
50 c = conn.cursor()
51 c.executescript("""
52 CREATE TABLE updates (
53 hostname TEXT NOT NULL,
54 status TEXT NOT NULL,
55 message TEXT,
56 timestamp timestamp NOT NULL
57 );
58
59 CREATE TABLE settings (
60 k TEXT NOT NULL,
61 v TEXT NOT NULL
62 );
a2857a13
MT
63
64 CREATE INDEX idx_updates_hostname ON updates(hostname);
37e24fbf
MT
65 """)
66 c.execute("INSERT INTO settings(k, v) VALUES(?, ?)", ("version", "1"))
67
68 return conn
69
63e16fee
MT
70 def is_writable(self):
71 # Check if the database file exists and is writable.
72 ret = os.access(self.path, os.W_OK)
73 if ret:
74 return True
75
76 # If not, we check if we are able to write to the directory.
77 # In that case the database file will be created in _open_database().
78 return os.access(os.path.dirname(self.path), os.W_OK)
79
37e24fbf 80 def _execute(self, query, *parameters):
63e16fee
MT
81 if self._db is None:
82 self._db = self._open_database(self.path)
83
37e24fbf
MT
84 c = self._db.cursor()
85 try:
86 c.execute(query, parameters)
87 finally:
88 c.close()
89
90 def add_update(self, hostname, status, message=None):
63e16fee
MT
91 if not self.is_writable():
92 logger.warning("Could not log any updates because the database is not writable")
93 return
94
37e24fbf
MT
95 self._execute("INSERT INTO updates(hostname, status, message, timestamp) \
96 VALUES(?, ?, ?, ?)", hostname, status, message, datetime.datetime.utcnow())
97
98 def log_success(self, hostname):
99 logger.debug("Logging successful update for %s" % hostname)
100
101 return self.add_update(hostname, "success")
102
103 def log_failure(self, hostname, exception):
104 if exception:
105 message = "%s: %s" % (exception.__class__.__name__, exception.reason)
106 else:
107 message = None
108
109 logger.debug("Logging failed update for %s: %s" % (hostname, message or ""))
110
111 return self.add_update(hostname, "failure", message=message)
112
112d3fb8
MT
113 def last_update(self, hostname, status=None):
114 """
115 Returns the timestamp of the last update (with the given status code).
116 """
f62fa5ba
MT
117 if self._db is None:
118 self._db = self._open_database(self.path)
119
37e24fbf
MT
120 c = self._db.cursor()
121
122 try:
112d3fb8
MT
123 if status:
124 c.execute("SELECT timestamp FROM updates WHERE hostname = ? AND status = ? \
125 ORDER BY timestamp DESC LIMIT 1", (hostname, status))
126 else:
127 c.execute("SELECT timestamp FROM updates WHERE hostname = ? \
128 ORDER BY timestamp DESC LIMIT 1", (hostname,))
129
130 for row in c:
131 return row[0]
132 finally:
133 c.close()
134
135 def last_update_status(self, hostname):
136 """
137 Returns the update status of the last update.
138 """
f62fa5ba
MT
139 if self._db is None:
140 self._db = self._open_database(self.path)
141
112d3fb8
MT
142 c = self._db.cursor()
143
144 try:
145 c.execute("SELECT status FROM updates WHERE hostname = ? \
146 ORDER BY timestamp DESC LIMIT 1", (hostname,))
147
148 for row in c:
149 return row[0]
150 finally:
151 c.close()
152
153 def last_update_failure_message(self, hostname):
154 """
155 Returns the reason string for the last failed update (if any).
156 """
f62fa5ba
MT
157 if self._db is None:
158 self._db = self._open_database(self.path)
159
112d3fb8
MT
160 c = self._db.cursor()
161
162 try:
163 c.execute("SELECT message FROM updates WHERE hostname = ? AND status = ? \
164 ORDER BY timestamp DESC LIMIT 1", (hostname, "failure"))
37e24fbf
MT
165
166 for row in c:
167 return row[0]
168 finally:
169 c.close()