1 # vim: set encoding=utf-8 et sw=4 sts=4 :
6 from new import instancemethod
8 from pymin.seqtools import Sequence
9 from pymin.dispatcher import handler, HandlerError, Handler
10 from pymin.services.util import Restorable, ConfigWriter, InitdHandler, \
11 TransactionalHandler, ParametersHandler, call
13 __ALL__ = ('DnsHandler', 'Error',
14 'ZoneError', 'ZoneNotFoundError', 'ZoneAlreadyExistsError',
15 'HostError', 'HostAlreadyExistsError', 'HostNotFoundError',
16 'MailExchangeError', 'MailExchangeAlreadyExistsError',
17 'MailExchangeNotFoundError', 'NameServerError',
18 'NameServerAlreadyExistsError', 'NameServerNotFoundError')
20 template_dir = path.join(path.dirname(__file__), 'templates')
23 class Error(HandlerError):
25 Error(command) -> Error instance :: Base DnsHandler exception class.
27 All exceptions raised by the DnsHandler inherits from this one, so you can
28 easily catch any DnsHandler exception.
30 message - A descriptive error message.
34 class ZoneError(Error, KeyError):
36 ZoneError(zonename) -> ZoneError instance
38 This is the base exception for all zone related errors.
41 def __init__(self, zonename):
42 r"Initialize the object. See class documentation for more info."
43 self.message = u'Zone error: "%s"' % zonename
45 class ZoneNotFoundError(ZoneError):
47 ZoneNotFoundError(hostname) -> ZoneNotFoundError instance
49 This exception is raised when trying to operate on a zone that doesn't
53 def __init__(self, zonename):
54 r"Initialize the object. See class documentation for more info."
55 self.message = u'zone not found: "%s"' % zonename
57 class ZoneAlreadyExistsError(ZoneError):
59 ZoneAlreadyExistsError(hostname) -> ZoneAlreadyExistsError instance
61 This exception is raised when trying to add a zonename that already exists.
64 def __init__(self, zonename):
65 r"Initialize the object. See class documentation for more info."
66 self.message = u'Zone already exists: "%s"' % zonename
68 class HostError(Error, KeyError):
70 HostError(hostname) -> HostError instance
72 This is the base exception for all host related errors.
75 def __init__(self, hostname):
76 r"Initialize the object. See class documentation for more info."
77 self.message = u'Host error: "%s"' % hostname
79 class HostAlreadyExistsError(HostError):
81 HostAlreadyExistsError(hostname) -> HostAlreadyExistsError instance
83 This exception is raised when trying to add a hostname that already exists.
86 def __init__(self, hostname):
87 r"Initialize the object. See class documentation for more info."
88 self.message = u'Host already exists: "%s"' % hostname
90 class HostNotFoundError(HostError):
92 HostNotFoundError(hostname) -> HostNotFoundError instance.
94 This exception is raised when trying to operate on a hostname that doesn't
98 def __init__(self, hostname):
99 r"Initialize the object. See class documentation for more info."
100 self.message = u'Host not found: "%s"' % hostname
102 class MailExchangeError(Error, KeyError):
104 MailExchangeError(hostname) -> MailExchangeError instance.
106 This is the base exception for all mail exchange related errors.
109 def __init__(self, mx):
110 r"Initialize the object. See class documentation for more info."
111 self.message = u'Mail Exchange error: "%s"' % mx
113 class MailExchangeAlreadyExistsError(MailExchangeError):
115 MailExchangeAlreadyExistsError(hostname) -> MailExchangeAlreadyExistsError.
117 This exception is raised when trying to add a mail exchange that already exists.
120 def __init__(self, mx):
121 r"Initialize the object. See class documentation for more info."
122 self.message = u'Mail Exchange already exists: "%s"' % mx
124 class MailExchangeNotFoundError(MailExchangeError):
126 MailExchangeNotFoundError(hostname) -> MailExchangeNotFoundError instance.
128 This exception is raised when trying to operate on a mail exchange that
132 def __init__(self, mx):
133 r"Initialize the object. See class documentation for more info."
134 self.message = 'Mail Exchange not found: "%s"' % mx
136 class NameServerError(Error, KeyError):
138 NameServerError(ns) -> NameServerError instance
140 This is the base exception for all name server related errors.
143 def __init__(self, ns):
144 r"Initialize the object. See class documentation for more info."
145 self.message = 'Name Server error: "%s"' % ns
147 class NameServerAlreadyExistsError(NameServerError):
149 NameServerAlreadyExistsError(hostname) -> NameServerAlreadyExistsError.
151 This exception is raised when trying to add a name server that already
155 def __init__(self, ns):
156 r"Initialize the object. See class documentation for more info."
157 self.message = 'Name server already exists: "%s"' % ns
159 class NameServerNotFoundError(NameServerError):
161 NameServerNotFoundError(hostname) -> NameServerNotFoundError instance.
163 This exception is raised when trying to operate on a name server that
167 def __init__(self, ns):
168 r"Initialize the object. See class documentation for more info."
169 self.message = 'Mail Exchange not found: "%s"' % ns
172 class Host(Sequence):
173 def __init__(self, name, ip):
178 return (self.name, self.ip)
180 class HostHandler(Handler):
182 handler_help = u"Manage DNS hosts"
184 def __init__(self,zones):
187 @handler(u'Adds a host to a zone')
188 def add(self, name, hostname, ip):
189 if not name in self.zones:
190 raise ZoneNotFoundError(name)
191 if hostname in self.zones[name].hosts:
192 raise HostAlreadyExistsError(hostname)
193 self.zones[name].hosts[hostname] = Host(hostname, ip)
194 self.zones[name].mod = True
196 @handler(u'Updates a host ip in a zone')
197 def update(self, name, hostname, ip):
198 if not name in self.zones:
199 raise ZoneNotFoundError(name)
200 if not hostname in self.zones[name].hosts:
201 raise HostNotFoundError(name)
202 self.zones[name].hosts[hostname].ip = ip
203 self.zones[name].mod = True
205 @handler(u'Deletes a host from a zone')
206 def delete(self, name, hostname):
207 if not name in self.zones:
208 raise ZoneNotFoundError(name)
209 if not hostname in self.zones[name].hosts:
210 raise HostNotFoundError(name)
211 del self.zones[name].hosts[hostname]
212 self.zones[name].mod = True
214 @handler(u'Lists hosts')
216 return self.zones.keys()
218 @handler(u'Get insormation about all hosts')
220 return self.zones.values()
223 class MailExchange(Sequence):
225 def __init__(self, mx, prio):
230 return (self.mx, self.prio)
232 class MailExchangeHandler(Handler):
234 handler_help = u"Manage DNS mail exchangers (MX)"
236 def __init__(self, zones):
239 @handler(u'Adds a mail exchange to a zone')
240 def add(self, zonename, mx, prio):
241 if not zonename in self.zones:
242 raise ZoneNotFoundError(zonename)
243 if mx in self.zones[zonename].mxs:
244 raise MailExchangeAlreadyExistsError(mx)
245 self.zones[zonename].mxs[mx] = MailExchange(mx, prio)
246 self.zones[zonename].mod = True
248 @handler(u'Updates a mail exchange priority')
249 def update(self, zonename, mx, prio):
250 if not zonename in self.zones:
251 raise ZoneNotFoundError(zonename)
252 if not mx in self.zones[zonename].mxs:
253 raise MailExchangeNotFoundError(mx)
254 self.zones[zonename].mxs[mx].prio = prio
255 self.zones[zonename].mod = True
257 @handler(u'Deletes a mail exchange from a zone')
258 def delete(self, zonename, mx):
259 if not zonename in self.zones:
260 raise ZoneNotFoundError(zonename)
261 if not mx in self.zones[zonename].mxs:
262 raise MailExchangeNotFoundError(mx)
263 del self.zones[zonename].mxs[mx]
264 self.zones[zonename].mod = True
266 @handler(u'Lists mail exchangers')
268 return self.zones.keys()
270 @handler(u'Get information about all mail exchangers')
272 return self.zones.values()
275 class NameServer(Sequence):
277 def __init__(self, name):
283 class NameServerHandler(Handler):
285 handler_help = u"Manage DNS name servers (NS)"
287 def __init__(self, zones):
290 @handler(u'Adds a name server to a zone')
291 def add(self, zone, ns):
292 if not zone in self.zones:
293 raise ZoneNotFoundError(zone)
294 if ns in self.zones[zone].nss:
295 raise NameServerAlreadyExistsError(ns)
296 self.zones[zone].nss[ns] = NameServer(ns)
297 self.zones[zone].mod = True
299 @handler(u'Deletes a name server from a zone')
300 def delete(self, zone, ns):
301 if not zone in self.zones:
302 raise ZoneNotFoundError(zone)
303 if not ns in self.zones[zone].nss:
304 raise NameServerNotFoundError(ns)
305 del self.zones[zone].nss[ns]
306 self.zones[zone].mod = True
308 @handler(u'Lists name servers')
310 return self.zones.keys()
312 @handler(u'Get information about all name servers')
314 return self.zones.values()
317 class Zone(Sequence):
318 def __init__(self, name):
328 return (self.name, self.hosts, self.mxs, self.nss)
330 class ZoneHandler(Handler):
331 r"""ZoneHandler(zones) -> ZoneHandler instance :: Handle a list of zones.
333 This class is a helper for DnsHandler to do all the work related to zone
336 zones - A dictionary with string keys (zone name) and Zone instances values.
339 handler_help = u"Manage DNS zones"
341 def __init__(self, zones):
344 @handler(u'Adds a zone')
346 if name in self.zones:
347 if self.zones[name].dele == True:
348 self.zones[name].dele = False
350 raise ZoneAlreadyExistsError(name)
351 self.zones[name] = Zone(name)
352 self.zones[name].mod = True
353 self.zones[name].new = True
356 @handler(u'Deletes a zone')
357 def delete(self, name):
358 r"delete(name) -> None :: Delete a zone from the zone list."
359 if not name in self.zones:
360 raise ZoneNotFoundError(name)
361 self.zones[name].dele = True
363 @handler(u'Lists zones')
365 return self.zones.keys()
367 @handler(u'Get information about all zones')
369 return self.zones.values()
372 class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler,
374 r"""DnsHandler([pickle_dir[, config_dir]]) -> DnsHandler instance.
376 Handles DNS service commands for the dns program.
378 pickle_dir - Directory where to write the persistent configuration data.
380 config_dir - Directory where to store de generated configuration files.
382 Both defaults to the current working directory.
385 handler_help = u"Manage DNS service"
387 _initd_name = 'named'
389 _persistent_attrs = ('params', 'zones')
391 _restorable_defaults = dict(
401 _config_writer_files = ('named.conf', 'zoneX.zone')
402 _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates')
404 def __init__(self, pickle_dir='.', config_dir='.'):
405 r"Initialize DnsHandler object, see class documentation for details."
406 self._persistent_dir = pickle_dir
407 self._config_writer_cfg_dir = config_dir
409 self._config_build_templates()
411 # FIXME self.mod = True
412 #if not self._restore():
417 self.host = HostHandler(self.zones)
418 self.zone = ZoneHandler(self.zones)
419 self.mx = MailExchangeHandler(self.zones)
420 self.ns = NameServerHandler(self.zones)
422 def _zone_filename(self, zone):
423 return zone.name + '.zone'
425 def _get_config_vars(self, config_file):
426 return dict(zones=self.zones.values(), **self.params)
428 def _write_config(self):
429 r"_write_config() -> None :: Generate all the configuration files."
430 delete_zones = list()
431 for a_zone in self.zones.values():
434 # TODO freeze de la zona
435 call(('rndc', 'freeze', a_zone.name))
438 hosts = a_zone.hosts.values(),
439 mxs = a_zone.mxs.values(),
440 nss = a_zone.nss.values()
442 self._write_single_config('zoneX.zone',
443 self._zone_filename(a_zone), vars)
446 # TODO unfreeze de la zona
447 call(('rndc', 'thaw', a_zone.name))
452 #borro el archivo .zone
455 unlink(self._zone_filename(a_zone))
457 #la excepcion pude darse en caso que haga un add de una zona y
458 #luego el del, como no hice commit, no se crea el archivo
460 delete_zones.append(a_zone.name)
462 for z in delete_zones:
466 self._write_single_config('named.conf')
471 if __name__ == '__main__':
475 dns.set('isp_dns1','la_garcha.com')
476 dns.set('bind_addr1','localhost')
477 dns.zone.add('zona_loca.com')
478 #dns.zone.update('zona_loca.com','ns1.dominio.com')
480 dns.host.add('zona_loca.com','hostname_loco','192.168.0.23')
481 dns.host.update('zona_loca.com','hostname_loco','192.168.0.66')
483 dns.host.add('zona_loca.com','hostname_kuak','192.168.0.23')
484 dns.host.delete('zona_loca.com','hostname_kuak')
486 dns.host.add('zona_loca.com','hostname_kuang','192.168.0.24')
487 dns.host.add('zona_loca.com','hostname_chan','192.168.0.25')
488 dns.host.add('zona_loca.com','hostname_kaine','192.168.0.26')
490 dns.mx.add('zona_loca.com','mx1.sarasa.com',10)
491 dns.mx.update('zona_loca.com','mx1.sarasa.com',20)
492 dns.mx.add('zona_loca.com','mx2.sarasa.com',30)
493 dns.mx.add('zona_loca.com','mx3.sarasa.com',40)
494 dns.mx.delete('zona_loca.com','mx3.sarasa.com')
496 dns.ns.add('zona_loca.com','ns1.jua.com')
497 dns.ns.add('zona_loca.com','ns2.jua.com')
498 dns.ns.add('zona_loca.com','ns3.jua.com')
499 dns.ns.delete('zona_loca.com','ns3.jua.com')
501 dns.zone.add('zona_oscura')
503 dns.host.add('zona_oscura','hostname_a','192.168.0.24')
504 dns.host.add('zona_oscura','hostname_b','192.168.0.25')
505 dns.host.add('zona_oscura','hostname_c','192.168.0.26')
507 dns.zone.delete('zona_oscura')
511 print 'ZONAS :', dns.zone.show()
512 print 'HOSTS :', dns.host.show()
516 # dns.zone.update('zone-sarasa','lalal')
517 #except ZoneNotFoundError, inst:
518 # print 'Error: ', inst
521 dns.zone.delete('zone-sarasa')
522 except ZoneNotFoundError, inst:
523 print 'Error: ', inst
526 # dns.zone.add('zona_loca.com','ns1.dom.com','ns2.dom.com')
527 #except ZoneAlreadyExistsError, inst:
528 # print 'Error: ', inst
532 dns.host.update('zone-sarasa','kuak','192.68')
533 except ZoneNotFoundError, inst:
534 print 'Error: ', inst
537 dns.host.update('zona_loca.com','kuak','192.68')
538 except HostNotFoundError, inst:
539 print 'Error: ', inst
542 dns.host.delete('zone-sarasa','lala')
543 except ZoneNotFoundError, inst:
544 print 'Error: ', inst
547 dns.host.delete('zona_loca.com','lala')
548 except HostNotFoundError, inst:
549 print 'Error: ', inst
552 dns.host.add('zona','hostname_loco','192.168.0.23')
553 except ZoneNotFoundError, inst:
554 print 'Error: ', inst
557 dns.host.add('zona_loca.com','hostname_loco','192.168.0.23')
558 except HostAlreadyExistsError, inst:
559 print 'Error: ', inst