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, \
14 __ALL__ = ('DnsHandler', 'Error',
15 'ZoneError', 'ZoneNotFoundError', 'ZoneAlreadyExistsError',
16 'HostError', 'HostAlreadyExistsError', 'HostNotFoundError',
17 'MailExchangeError', 'MailExchangeAlreadyExistsError',
18 'MailExchangeNotFoundError', 'NameServerError',
19 'NameServerAlreadyExistsError', 'NameServerNotFoundError')
21 template_dir = path.join(path.dirname(__file__), 'templates')
24 class Error(HandlerError):
26 Error(command) -> Error instance :: Base DnsHandler exception class.
28 All exceptions raised by the DnsHandler inherits from this one, so you can
29 easily catch any DnsHandler exception.
31 message - A descriptive error message.
35 class ZoneError(Error, KeyError):
37 ZoneError(zonename) -> ZoneError instance
39 This is the base exception for all zone related errors.
42 def __init__(self, zonename):
43 r"Initialize the object. See class documentation for more info."
44 self.message = u'Zone error: "%s"' % zonename
46 class ZoneNotFoundError(ZoneError):
48 ZoneNotFoundError(hostname) -> ZoneNotFoundError instance
50 This exception is raised when trying to operate on a zone that doesn't
54 def __init__(self, zonename):
55 r"Initialize the object. See class documentation for more info."
56 self.message = u'zone not found: "%s"' % zonename
58 class ZoneAlreadyExistsError(ZoneError):
60 ZoneAlreadyExistsError(hostname) -> ZoneAlreadyExistsError instance
62 This exception is raised when trying to add a zonename that already exists.
65 def __init__(self, zonename):
66 r"Initialize the object. See class documentation for more info."
67 self.message = u'Zone already exists: "%s"' % zonename
69 class HostError(Error, KeyError):
71 HostError(hostname) -> HostError instance
73 This is the base exception for all host related errors.
76 def __init__(self, hostname):
77 r"Initialize the object. See class documentation for more info."
78 self.message = u'Host error: "%s"' % hostname
80 class HostAlreadyExistsError(HostError):
82 HostAlreadyExistsError(hostname) -> HostAlreadyExistsError instance
84 This exception is raised when trying to add a hostname that already exists.
87 def __init__(self, hostname):
88 r"Initialize the object. See class documentation for more info."
89 self.message = u'Host already exists: "%s"' % hostname
91 class HostNotFoundError(HostError):
93 HostNotFoundError(hostname) -> HostNotFoundError instance.
95 This exception is raised when trying to operate on a hostname that doesn't
99 def __init__(self, hostname):
100 r"Initialize the object. See class documentation for more info."
101 self.message = u'Host not found: "%s"' % hostname
103 class MailExchangeError(Error, KeyError):
105 MailExchangeError(hostname) -> MailExchangeError instance.
107 This is the base exception for all mail exchange related errors.
110 def __init__(self, mx):
111 r"Initialize the object. See class documentation for more info."
112 self.message = u'Mail Exchange error: "%s"' % mx
114 class MailExchangeAlreadyExistsError(MailExchangeError):
116 MailExchangeAlreadyExistsError(hostname) -> MailExchangeAlreadyExistsError.
118 This exception is raised when trying to add a mail exchange that already exists.
121 def __init__(self, mx):
122 r"Initialize the object. See class documentation for more info."
123 self.message = u'Mail Exchange already exists: "%s"' % mx
125 class MailExchangeNotFoundError(MailExchangeError):
127 MailExchangeNotFoundError(hostname) -> MailExchangeNotFoundError instance.
129 This exception is raised when trying to operate on a mail exchange that
133 def __init__(self, mx):
134 r"Initialize the object. See class documentation for more info."
135 self.message = 'Mail Exchange not found: "%s"' % mx
137 class NameServerError(Error, KeyError):
139 NameServerError(ns) -> NameServerError instance
141 This is the base exception for all name server related errors.
144 def __init__(self, ns):
145 r"Initialize the object. See class documentation for more info."
146 self.message = 'Name Server error: "%s"' % ns
148 class NameServerAlreadyExistsError(NameServerError):
150 NameServerAlreadyExistsError(hostname) -> NameServerAlreadyExistsError.
152 This exception is raised when trying to add a name server that already
156 def __init__(self, ns):
157 r"Initialize the object. See class documentation for more info."
158 self.message = 'Name server already exists: "%s"' % ns
160 class NameServerNotFoundError(NameServerError):
162 NameServerNotFoundError(hostname) -> NameServerNotFoundError instance.
164 This exception is raised when trying to operate on a name server that
168 def __init__(self, ns):
169 r"Initialize the object. See class documentation for more info."
170 self.message = 'Mail Exchange not found: "%s"' % ns
173 class Host(Sequence):
174 def __init__(self, name, ip):
179 return (self.name, self.ip)
181 class HostHandler(SubHandler):
183 handler_help = u"Manage DNS hosts"
185 @handler(u'Adds a host to a zone')
186 def add(self, name, hostname, ip):
187 if not name in self.parent.zones:
188 raise ZoneNotFoundError(name)
189 if hostname in self.parent.zones[name].hosts:
190 raise HostAlreadyExistsError(hostname)
191 self.parent.zones[name].hosts[hostname] = Host(hostname, ip)
192 self.parent.zones[name].mod = True
194 @handler(u'Updates a host ip in a zone')
195 def update(self, name, hostname, ip):
196 if not name in self.parent.zones:
197 raise ZoneNotFoundError(name)
198 if not hostname in self.parent.zones[name].hosts:
199 raise HostNotFoundError(name)
200 self.parent.zones[name].hosts[hostname].ip = ip
201 self.parent.zones[name].mod = True
203 @handler(u'Deletes a host from a zone')
204 def delete(self, name, hostname):
205 if not name in self.parent.zones:
206 raise ZoneNotFoundError(name)
207 if not hostname in self.parent.zones[name].hosts:
208 raise HostNotFoundError(name)
209 del self.parent.zones[name].hosts[hostname]
210 self.parent.zones[name].mod = True
212 @handler(u'Lists hosts')
214 return self.parent.zones.keys()
216 @handler(u'Get insormation about all hosts')
218 return self.parent.zones.values()
221 class MailExchange(Sequence):
223 def __init__(self, mx, prio):
228 return (self.mx, self.prio)
230 class MailExchangeHandler(SubHandler):
232 handler_help = u"Manage DNS mail exchangers (MX)"
234 @handler(u'Adds a mail exchange to a zone')
235 def add(self, zonename, mx, prio):
236 if not zonename in self.parent.zones:
237 raise ZoneNotFoundError(zonename)
238 if mx in self.parent.zones[zonename].mxs:
239 raise MailExchangeAlreadyExistsError(mx)
240 self.parent.zones[zonename].mxs[mx] = MailExchange(mx, prio)
241 self.parent.zones[zonename].mod = True
243 @handler(u'Updates a mail exchange priority')
244 def update(self, zonename, mx, prio):
245 if not zonename in self.parent.zones:
246 raise ZoneNotFoundError(zonename)
247 if not mx in self.parent.zones[zonename].mxs:
248 raise MailExchangeNotFoundError(mx)
249 self.parent.zones[zonename].mxs[mx].prio = prio
250 self.parent.zones[zonename].mod = True
252 @handler(u'Deletes a mail exchange from a zone')
253 def delete(self, zonename, mx):
254 if not zonename in self.parent.zones:
255 raise ZoneNotFoundError(zonename)
256 if not mx in self.parent.zones[zonename].mxs:
257 raise MailExchangeNotFoundError(mx)
258 del self.parent.zones[zonename].mxs[mx]
259 self.parent.zones[zonename].mod = True
261 @handler(u'Lists mail exchangers')
263 return self.parent.zones.keys()
265 @handler(u'Get information about all mail exchangers')
267 return self.parent.zones.values()
270 class NameServer(Sequence):
272 def __init__(self, name):
278 class NameServerHandler(SubHandler):
280 handler_help = u"Manage DNS name servers (NS)"
282 @handler(u'Adds a name server to a zone')
283 def add(self, zone, ns):
284 if not zone in self.parent.zones:
285 raise ZoneNotFoundError(zone)
286 if ns in self.parent.zones[zone].nss:
287 raise NameServerAlreadyExistsError(ns)
288 self.parent.zones[zone].nss[ns] = NameServer(ns)
289 self.parent.zones[zone].mod = True
291 @handler(u'Deletes a name server from a zone')
292 def delete(self, zone, ns):
293 if not zone in self.parent.zones:
294 raise ZoneNotFoundError(zone)
295 if not ns in self.parent.zones[zone].nss:
296 raise NameServerNotFoundError(ns)
297 del self.parent.zones[zone].nss[ns]
298 self.parent.zones[zone].mod = True
300 @handler(u'Lists name servers')
302 return self.parent.zones.keys()
304 @handler(u'Get information about all name servers')
306 return self.parent.zones.values()
309 class Zone(Sequence):
310 def __init__(self, name):
320 return (self.name, self.hosts, self.mxs, self.nss)
322 class ZoneHandler(SubHandler):
323 r"""ZoneHandler(parent.zones) -> ZoneHandler instance :: Handle a list of zones.
325 This class is a helper for DnsHandler to do all the work related to zone
328 parent - The parent service handler.
331 handler_help = u"Manage DNS zones"
333 @handler(u'Adds a zone')
335 if name in self.parent.zones:
336 if self.parent.zones[name].dele == True:
337 self.parent.zones[name].dele = False
339 raise ZoneAlreadyExistsError(name)
340 self.parent.zones[name] = Zone(name)
341 self.parent.zones[name].mod = True
342 self.parent.zones[name].new = True
344 @handler(u'Deletes a zone')
345 def delete(self, name):
346 r"delete(name) -> None :: Delete a zone from the zone list."
347 if not name in self.parent.zones:
348 raise ZoneNotFoundError(name)
349 self.parent.zones[name].dele = True
351 @handler(u'Lists zones')
353 return self.parent.zones.keys()
355 @handler(u'Get information about all zones')
357 return self.parent.zones.values()
360 class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler,
362 r"""DnsHandler([pickle_dir[, config_dir]]) -> DnsHandler instance.
364 Handles DNS service commands for the dns program.
366 pickle_dir - Directory where to write the persistent configuration data.
368 config_dir - Directory where to store de generated configuration files.
370 Both defaults to the current working directory.
373 handler_help = u"Manage DNS service"
375 _initd_name = 'named'
377 _persistent_attrs = ('params', 'zones')
379 _restorable_defaults = dict(
389 _config_writer_files = ('named.conf', 'zoneX.zone')
390 _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates')
392 def __init__(self, pickle_dir='.', config_dir='.'):
393 r"Initialize DnsHandler object, see class documentation for details."
394 self._persistent_dir = pickle_dir
395 self._config_writer_cfg_dir = config_dir
397 self._config_build_templates()
399 # FIXME self.mod = True
400 #if not self._restore():
405 self.host = HostHandler(self)
406 self.zone = ZoneHandler(self)
407 self.mx = MailExchangeHandler(self)
408 self.ns = NameServerHandler(self)
410 def _zone_filename(self, zone):
411 return zone.name + '.zone'
413 def _get_config_vars(self, config_file):
414 return dict(zones=self.zones.values(), **self.params)
416 def _write_config(self):
417 r"_write_config() -> None :: Generate all the configuration files."
418 delete_zones = list()
419 for a_zone in self.zones.values():
422 # TODO freeze de la zona
423 call(('rndc', 'freeze', a_zone.name))
426 hosts = a_zone.hosts.values(),
427 mxs = a_zone.mxs.values(),
428 nss = a_zone.nss.values()
430 self._write_single_config('zoneX.zone',
431 self._zone_filename(a_zone), vars)
434 # TODO unfreeze de la zona
435 call(('rndc', 'thaw', a_zone.name))
440 #borro el archivo .zone
443 unlink(self._zone_filename(a_zone))
445 #la excepcion pude darse en caso que haga un add de una zona y
446 #luego el del, como no hice commit, no se crea el archivo
448 delete_zones.append(a_zone.name)
450 for z in delete_zones:
454 self._write_single_config('named.conf')
459 if __name__ == '__main__':
463 dns.set('isp_dns1','la_garcha.com')
464 dns.set('bind_addr1','localhost')
465 dns.zone.add('zona_loca.com')
466 #dns.zone.update('zona_loca.com','ns1.dominio.com')
468 dns.host.add('zona_loca.com','hostname_loco','192.168.0.23')
469 dns.host.update('zona_loca.com','hostname_loco','192.168.0.66')
471 dns.host.add('zona_loca.com','hostname_kuak','192.168.0.23')
472 dns.host.delete('zona_loca.com','hostname_kuak')
474 dns.host.add('zona_loca.com','hostname_kuang','192.168.0.24')
475 dns.host.add('zona_loca.com','hostname_chan','192.168.0.25')
476 dns.host.add('zona_loca.com','hostname_kaine','192.168.0.26')
478 dns.mx.add('zona_loca.com','mx1.sarasa.com',10)
479 dns.mx.update('zona_loca.com','mx1.sarasa.com',20)
480 dns.mx.add('zona_loca.com','mx2.sarasa.com',30)
481 dns.mx.add('zona_loca.com','mx3.sarasa.com',40)
482 dns.mx.delete('zona_loca.com','mx3.sarasa.com')
484 dns.ns.add('zona_loca.com','ns1.jua.com')
485 dns.ns.add('zona_loca.com','ns2.jua.com')
486 dns.ns.add('zona_loca.com','ns3.jua.com')
487 dns.ns.delete('zona_loca.com','ns3.jua.com')
489 dns.zone.add('zona_oscura')
491 dns.host.add('zona_oscura','hostname_a','192.168.0.24')
492 dns.host.add('zona_oscura','hostname_b','192.168.0.25')
493 dns.host.add('zona_oscura','hostname_c','192.168.0.26')
495 dns.zone.delete('zona_oscura')
499 print 'ZONAS :', dns.zone.show()
500 print 'HOSTS :', dns.host.show()
504 # dns.zone.update('zone-sarasa','lalal')
505 #except ZoneNotFoundError, inst:
506 # print 'Error: ', inst
509 dns.zone.delete('zone-sarasa')
510 except ZoneNotFoundError, inst:
511 print 'Error: ', inst
514 # dns.zone.add('zona_loca.com','ns1.dom.com','ns2.dom.com')
515 #except ZoneAlreadyExistsError, inst:
516 # print 'Error: ', inst
520 dns.host.update('zone-sarasa','kuak','192.68')
521 except ZoneNotFoundError, inst:
522 print 'Error: ', inst
525 dns.host.update('zona_loca.com','kuak','192.68')
526 except HostNotFoundError, inst:
527 print 'Error: ', inst
530 dns.host.delete('zone-sarasa','lala')
531 except ZoneNotFoundError, inst:
532 print 'Error: ', inst
535 dns.host.delete('zona_loca.com','lala')
536 except HostNotFoundError, inst:
537 print 'Error: ', inst
540 dns.host.add('zona','hostname_loco','192.168.0.23')
541 except ZoneNotFoundError, inst:
542 print 'Error: ', inst
545 dns.host.add('zona_loca.com','hostname_loco','192.168.0.23')
546 except HostAlreadyExistsError, inst:
547 print 'Error: ', inst