]>
Commit | Line | Data |
---|---|---|
1336cd4e SS |
1 | ############################################################################### |
2 | # # | |
3 | # pyPDNS - A PDNS administration tool, written in pure python. # | |
9deb9e05 | 4 | # Copyright (C) 2012 IPFire development team # |
1336cd4e SS |
5 | # # |
6 | # This program is free software: you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation, either version 3 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # This program is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | ############################################################################### | |
20 | # # | |
a8b3a646 SS |
21 | # Basic information about the database layout can be found here: # |
22 | # http://doc.powerdns.com/gsqlite.html # | |
23 | # # | |
1336cd4e SS |
24 | # More details about the database tables and fields can be found here: # |
25 | # http://wiki.powerdns.com/trac/wiki/fields # | |
26 | # # | |
27 | ############################################################################### | |
28 | ||
29 | import database | |
a7ceb5de | 30 | import sqlite3 |
1336cd4e | 31 | |
9253a8af SS |
32 | from errors import * |
33 | ||
1336cd4e SS |
34 | DB = "/var/lib/pdns/pdns.db" |
35 | ||
36 | # Create the primary DNS class. | |
1336cd4e | 37 | class DNS(object): |
a7ceb5de SS |
38 | """ |
39 | Primary DNS class. | |
259f19af | 40 | |
a7ceb5de SS |
41 | Uses the database class from imported database module. |
42 | Connects to the PDNS sqlite database. | |
43 | """ | |
1336cd4e | 44 | def __init__(self, db): |
a7ceb5de SS |
45 | # Try to connect to database or raise an exception. |
46 | try: | |
47 | self.db = database.Database(db) | |
48 | ||
49 | except sqlite3.OperationalError, e: | |
9253a8af | 50 | raise DatabaseException, "Could not open database: %s" % e |
a7ceb5de SS |
51 | |
52 | ||
1336cd4e SS |
53 | |
54 | # Get all configured domains. | |
55 | def get_domains(self): | |
259f19af SS |
56 | """ |
57 | Fetch all configured domains. | |
58 | """ | |
59 | # Create an empty list. | |
1336cd4e SS |
60 | domains = [] |
61 | ||
259f19af | 62 | # Add fetched domains to the previous created empty list. |
1336cd4e SS |
63 | for row in self.db.query("SELECT id FROM domains"): |
64 | domain = Domain(self, row.id) | |
65 | domains.append(domain) | |
66 | ||
67 | return domains | |
68 | ||
69 | # Get a domain by it's name. | |
70 | def get_domain(self, name): | |
259f19af SS |
71 | """ |
72 | Get a domain by a given name. | |
73 | """ | |
1336cd4e | 74 | row = self.db.get("SELECT id FROM domains WHERE name = ?", name) |
1336cd4e | 75 | |
209345ca | 76 | # Check if an id has been returned from database or return None. |
9253a8af SS |
77 | if not row: |
78 | return None | |
79 | ||
80 | return Domain(self, row.id) | |
d267bece | 81 | |
1336cd4e SS |
82 | |
83 | # Create Domain class. | |
1336cd4e | 84 | class Domain(object): |
a7ceb5de SS |
85 | """ |
86 | Domain class. | |
259f19af | 87 | |
209345ca SS |
88 | Uses query method from database module to get requested information |
89 | from domain. | |
90 | ||
a7ceb5de SS |
91 | The domain is specified by it's unique database id. |
92 | """ | |
1336cd4e SS |
93 | def __init__(self, dns, domain_id): |
94 | self.dns = dns | |
95 | self.id = domain_id | |
96 | ||
9253a8af SS |
97 | self.__data = None |
98 | ||
1336cd4e SS |
99 | @property |
100 | def db(self): | |
101 | return self.dns.db | |
102 | ||
9253a8af SS |
103 | # Cache. |
104 | @property | |
105 | def data(self): | |
106 | if self.__data is None: | |
209345ca SS |
107 | self.__data = self.db.get("SELECT * FROM domains \ |
108 | WHERE id = ?", self.id) | |
9253a8af SS |
109 | assert self.__data |
110 | ||
111 | return self.__data | |
112 | ||
1336cd4e SS |
113 | # Determine the name of the zone by a given id. |
114 | @property | |
115 | def name(self): | |
9253a8af | 116 | return self.data.name |
1336cd4e | 117 | |
209345ca SS |
118 | # Get information of the master nameserver from which the domain should |
119 | # be slaved. | |
1336cd4e SS |
120 | @property |
121 | def master(self): | |
9253a8af | 122 | return self.data.master |
1336cd4e SS |
123 | |
124 | # Fetch data of the last check from the domain. | |
125 | @property | |
126 | def last_check(self): | |
9253a8af | 127 | return self.data.last_check |
1336cd4e SS |
128 | |
129 | # Get the type of the domain. | |
130 | @property | |
131 | def type(self): | |
9253a8af | 132 | return self.data.type |
1336cd4e SS |
133 | |
134 | # Get the last notified serial of a used master domain. | |
135 | @property | |
136 | def notified_serial(self): | |
9253a8af | 137 | return self.data.notified_serial |
1336cd4e SS |
138 | |
139 | # Gain if a certain host is a supermaster for a certain domain name. | |
140 | @property | |
141 | def account(self): | |
9253a8af | 142 | return self.data.account |
1336cd4e | 143 | |
209345ca SS |
144 | # Get count of records of a zone. Return true if there is at least one |
145 | # or false. | |
146 | def has_records(self): | |
147 | count = self.db.get("SELECT COUNT(*) AS num FROM records \ | |
148 | WHERE domain_id = ?", self.id) | |
149 | ||
150 | if count.num > 0: | |
151 | return True | |
152 | ||
153 | return False | |
154 | ||
1336cd4e SS |
155 | # Get all records from zone. |
156 | @property | |
157 | def records(self): | |
259f19af SS |
158 | """ |
159 | Get all records from the zone. | |
160 | """ | |
209345ca SS |
161 | # Fetch records from zone and categorize them into their |
162 | # different record types. | |
163 | for row in self.db.query("SELECT id, type FROM records \ | |
164 | WHERE domain_id = ?", self.id): | |
165 | ||
abccf64a SS |
166 | if row.type == "SOA": |
167 | record = SOARecord(self, row.id) | |
168 | elif row.type == "A": | |
169 | record = ARecord(self, row.id) | |
170 | else: | |
9253a8af | 171 | record = Record(self, row.id) |
1336cd4e | 172 | |
9253a8af | 173 | yield record |
1336cd4e | 174 | |
abccf64a SS |
175 | # Get records by a specified type. |
176 | def get_records_by_type(self, type): | |
177 | records = [] | |
178 | for record in self.records: | |
179 | if record.type == type: | |
180 | records.append(record) | |
181 | ||
182 | return records | |
183 | ||
184 | # Quick function to get the first SOA record from the domain. | |
185 | @property | |
186 | def SOA(self): | |
187 | records = self.get_records_by_type("SOA") | |
188 | if records: | |
189 | return records[0] | |
1336cd4e SS |
190 | |
191 | ||
192 | # Create class for domain records. | |
1336cd4e | 193 | class Record(object): |
a7ceb5de SS |
194 | """ |
195 | Record class | |
259f19af | 196 | |
a7ceb5de SS |
197 | It is used to get details about configured records. |
198 | The domain and record is's are specified by their unique database id's. | |
199 | """ | |
1336cd4e SS |
200 | def __init__(self, domain, record_id): |
201 | self.domain = domain | |
202 | self.id = record_id | |
203 | ||
9253a8af SS |
204 | # Cache. |
205 | self.__data = None | |
206 | ||
1336cd4e SS |
207 | @property |
208 | def db(self): | |
209 | return self.domain.db | |
210 | ||
9253a8af SS |
211 | @property |
212 | def data(self): | |
213 | if self.__data is None: | |
209345ca SS |
214 | self.__data = self.db.get("SELECT * FROM records \ |
215 | WHERE id = ?", self.id) | |
9253a8af SS |
216 | assert self.__data |
217 | ||
218 | return self.__data | |
219 | ||
1336cd4e SS |
220 | # Determine the type of the record. |
221 | @property | |
222 | def type(self): | |
9253a8af | 223 | return self.data.type |
1336cd4e SS |
224 | |
225 | # Get the configured DNS name of the record. | |
226 | @property | |
227 | def dnsname(self): | |
9253a8af | 228 | return self.data.name |
1336cd4e SS |
229 | |
230 | ||
231 | # Fetch content like the address to which the record points. | |
232 | @property | |
233 | def content(self): | |
9253a8af | 234 | return self.data.content |
1336cd4e SS |
235 | |
236 | # Get the "Time to live" for the record. | |
237 | @property | |
238 | def ttl(self): | |
9253a8af | 239 | return self.data.ttl |
1336cd4e SS |
240 | |
241 | # Gain the configured record priority. | |
242 | @property | |
243 | def priority(self): | |
9253a8af | 244 | return self.data.prio |
1336cd4e SS |
245 | |
246 | # Get the change_date. | |
247 | @property | |
248 | def change_date(self): | |
9253a8af | 249 | return self.data.change_date |
1336cd4e SS |
250 | |
251 | # Fetch the ordername. | |
252 | @property | |
253 | def ordername(self): | |
9253a8af | 254 | return self.data.ordername |
1336cd4e | 255 | |
abccf64a | 256 | # Gain all information about records authentication. |
1336cd4e | 257 | @property |
abccf64a | 258 | def authentication(self): |
9253a8af | 259 | return self.data.auth |
abccf64a SS |
260 | |
261 | ||
262 | # Create an own class to deal with "SOA" records. | |
abccf64a | 263 | class SOARecord(Record): |
a7ceb5de SS |
264 | """ |
265 | SOA Record class. | |
266 | This is an own class to deal with "SOA" records. | |
259f19af | 267 | |
209345ca SS |
268 | Uses splitt() to generate a list of the content string from the |
269 | database. | |
270 | ||
a7ceb5de SS |
271 | Returns the requested entries. |
272 | """ | |
abccf64a SS |
273 | def __init__(self, domain, record_id): |
274 | Record.__init__(self, domain, record_id) | |
275 | ||
276 | self.soa_attrs = self.content.split() | |
277 | ||
209345ca SS |
278 | # Check if the content from database is valid. |
279 | # (It contains all 7 required information) | |
abccf64a | 280 | if not len(self.soa_attrs) == 7: |
a7ceb5de SS |
281 | raise InvalidRecordDataException, "Your SOA record \ |
282 | doesn't contain all required seven elements." | |
abccf64a | 283 | |
209345ca SS |
284 | # Primary NS - the domain name of the name server that was the |
285 | # original source of the data. | |
abccf64a SS |
286 | @property |
287 | def mname(self): | |
288 | return self.soa_attrs[0] | |
289 | ||
290 | # E-mail address of the person which is responsible for this domain. | |
291 | @property | |
292 | def email(self): | |
293 | return self.soa_attrs[1] | |
294 | ||
209345ca SS |
295 | # The serial which increases allways after a change on the domain has |
296 | # been made. | |
abccf64a SS |
297 | @property |
298 | def serial(self): | |
299 | return self.soa_attrs[2] | |
300 | ||
209345ca SS |
301 | # The number of seconds between the time that a secondary name server |
302 | # gets a copy of the domain. | |
abccf64a SS |
303 | @property |
304 | def refresh(self): | |
305 | return self.soa_attrs[3] | |
306 | ||
209345ca SS |
307 | # The number of seconds during the next refresh attempt if the |
308 | # previous fails. | |
abccf64a SS |
309 | @property |
310 | def retry(self): | |
311 | return self.soa_attrs[4] | |
312 | ||
209345ca SS |
313 | # The number of seconds that lets the secondary name server(s) know |
314 | # how long they can hold the information. | |
abccf64a SS |
315 | @property |
316 | def expire(self): | |
317 | return self.soa_attrs[5] | |
318 | ||
319 | # The number of seconds that the records in the domain are valid. | |
320 | @property | |
321 | def minimum(self): | |
322 | return self.soa_attrs[6] | |
323 | ||
324 | ||
325 | ||
326 | class ARecord(Record): | |
327 | pass |