1 # vim: set encoding=utf-8 et sw=4 sts=4 :
6 from new import instancemethod
8 from seqtools import Sequence
9 from dispatcher import handler, HandlerError, Handler
10 from services.util import Restorable, ConfigWriter, call
11 from services.util import InitdHandler, TransactionalHandler, ParametersHandler
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.
33 def __init__(self, message):
34 r"Initialize the Error object. See class documentation for more info."
35 self.message = message
40 class ZoneError(Error, KeyError):
42 ZoneError(zonename) -> ZoneError instance
44 This is the base exception for all zone related errors.
47 def __init__(self, zonename):
48 r"Initialize the object. See class documentation for more info."
49 self.message = 'Zone error: "%s"' % zonename
52 class ZoneNotFoundError(ZoneError):
54 ZoneNotFoundError(hostname) -> ZoneNotFoundError instance
56 This exception is raised when trying to operate on a zone that doesn't
60 def __init__(self, zonename):
61 r"Initialize the object. See class documentation for more info."
62 self.message = 'zone not found: "%s"' % zonename
65 class ZoneAlreadyExistsError(ZoneError):
67 ZoneAlreadyExistsError(hostname) -> ZoneAlreadyExistsError instance
69 This exception is raised when trying to add a zonename that already exists.
72 def __init__(self, zonename):
73 r"Initialize the object. See class documentation for more info."
74 self.message = 'Zone already exists: "%s"' % zonename
77 class HostError(Error, KeyError):
79 HostError(hostname) -> HostError instance
81 This is the base exception for all host related errors.
84 def __init__(self, hostname):
85 r"Initialize the object. See class documentation for more info."
86 self.message = 'Host error: "%s"' % hostname
88 class HostAlreadyExistsError(HostError):
90 HostAlreadyExistsError(hostname) -> HostAlreadyExistsError instance
92 This exception is raised when trying to add a hostname that already exists.
95 def __init__(self, hostname):
96 r"Initialize the object. See class documentation for more info."
97 self.message = 'Host already exists: "%s"' % hostname
99 class HostNotFoundError(HostError):
101 HostNotFoundError(hostname) -> HostNotFoundError instance
103 This exception is raised when trying to operate on a hostname that doesn't
107 def __init__(self, hostname):
108 r"Initialize the object. See class documentation for more info."
109 self.message = 'Host not found: "%s"' % hostname
112 class MailExchangeError(Error, KeyError):
114 MailExchangeError(hostname) -> MailExchangeError instance
116 This is the base exception for all mail exchange related errors.
119 def __init__(self, mx):
120 r"Initialize the object. See class documentation for more info."
121 self.message = 'Mail Exchange error: "%s"' % mx
124 class MailExchangeAlreadyExistsError(MailExchangeError):
126 MailExchangeAlreadyExistsError(hostname) -> MailExchangeAlreadyExistsError instance
128 This exception is raised when trying to add a mail exchange that already exists.
131 def __init__(self, mx):
132 r"Initialize the object. See class documentation for more info."
133 self.message = 'Mail Exchange already exists: "%s"' % mx
136 class MailExchangeNotFoundError(MailExchangeError):
138 MailExchangeNotFoundError(hostname) -> MailExchangeNotFoundError instance
140 This exception is raised when trying to operate on a mail exchange that doesn't
144 def __init__(self, mx):
145 r"Initialize the object. See class documentation for more info."
146 self.message = 'Mail Exchange not found: "%s"' % mx
150 class NameServerError(Error, KeyError):
152 NameServerError(ns) -> NameServerError instance
154 This is the base exception for all name server related errors.
157 def __init__(self, ns):
158 r"Initialize the object. See class documentation for more info."
159 self.message = 'Name Server error: "%s"' % ns
161 class NameServerAlreadyExistsError(NameServerError):
163 NameServerAlreadyExistsError(hostname) -> NameServerAlreadyExistsError instance
165 This exception is raised when trying to add a name server that already exists.
168 def __init__(self, ns):
169 r"Initialize the object. See class documentation for more info."
170 self.message = 'Name server already exists: "%s"' % ns
172 class NameServerNotFoundError(NameServerError):
174 NameServerNotFoundError(hostname) -> NameServerNotFoundError instance
176 This exception is raised when trying to operate on a name server that doesn't
180 def __init__(self, ns):
181 r"Initialize the object. See class documentation for more info."
182 self.message = 'Mail Exchange not found: "%s"' % ns
185 class ParameterError(Error, KeyError):
187 ParameterError(paramname) -> ParameterError instance
189 This is the base exception for all DhcpHandler parameters related errors.
192 def __init__(self, paramname):
193 r"Initialize the object. See class documentation for more info."
194 self.message = 'Parameter error: "%s"' % paramname
196 class ParameterNotFoundError(ParameterError):
198 ParameterNotFoundError(hostname) -> ParameterNotFoundError instance
200 This exception is raised when trying to operate on a parameter that doesn't
204 def __init__(self, paramname):
205 r"Initialize the object. See class documentation for more info."
206 self.message = 'Parameter not found: "%s"' % paramname
208 class Host(Sequence):
209 def __init__(self, name, ip):
214 return (self.name, self.ip)
216 class HostHandler(Handler):
217 def __init__(self,zones):
220 @handler(u'Adds a host to a zone')
221 def add(self, name, hostname, ip):
222 if not name in self.zones:
223 raise ZoneNotFoundError(name)
224 if hostname in self.zones[name].hosts:
225 raise HostAlreadyExistsError(hostname)
226 self.zones[name].hosts[hostname] = Host(hostname, ip)
227 self.zones[name].mod = True
229 @handler(u'Updates a host ip in a zone')
230 def update(self, name, hostname, ip):
231 if not name in self.zones:
232 raise ZoneNotFoundError(name)
233 if not hostname in self.zones[name].hosts:
234 raise HostNotFoundError(name)
235 self.zones[name].hosts[hostname].ip = ip
236 self.zones[name].mod = True
238 @handler(u'Deletes a host from a zone')
239 def delete(self, name, hostname):
240 if not name in self.zones:
241 raise ZoneNotFoundError(name)
242 if not hostname in self.zones[name].hosts:
243 raise HostNotFoundError(name)
244 del self.zones[name].hosts[hostname]
245 self.zones[name].mod = True
247 @handler(u'Lists hosts')
249 return self.zones.keys()
251 @handler(u'Get insormation about all hosts')
253 return self.zones.values()
256 class MailExchange(Sequence):
258 def __init__(self, mx, prio):
263 return (self.mx, self.prio)
265 class MailExchangeHandler(Handler):
267 def __init__(self, zones):
270 @handler(u'Adds a mail exchange to a zone')
271 def add(self, zonename, mx, prio):
272 if not zonename in self.zones:
273 raise ZoneNotFoundError(zonename)
274 if mx in self.zones[zonename].mxs:
275 raise MailExchangeAlreadyExistsError(mx)
276 self.zones[zonename].mxs[mx] = MailExchange(mx, prio)
277 self.zones[zonename].mod = True
279 @handler(u'Updates a mail exchange priority')
280 def update(self, zonename, mx, prio):
281 if not zonename in self.zones:
282 raise ZoneNotFoundError(zonename)
283 if not mx in self.zones[zonename].mxs:
284 raise MailExchangeNotFoundError(mx)
285 self.zones[zonename].mxs[mx].prio = prio
286 self.zones[zonename].mod = True
288 @handler(u'Deletes a mail exchange from a zone')
289 def delete(self, zonename, mx):
290 if not zonename in self.zones:
291 raise ZoneNotFoundError(zonename)
292 if not mx in self.zones[zonename].mxs:
293 raise MailExchangeNotFoundError(mx)
294 del self.zones[zonename].mxs[mx]
295 self.zones[zonename].mod = True
297 @handler(u'Lists mail exchangers')
299 return self.zones.keys()
301 @handler(u'Get information about all mail exchangers')
303 return self.zones.values()
306 class NameServer(Sequence):
308 def __init__(self, name):
314 class NameServerHandler(Handler):
316 def __init__(self, zones):
319 @handler(u'Adds a name server to a zone')
320 def add(self, zone, ns):
321 if not zone in self.zones:
322 raise ZoneNotFoundError(zone)
323 if ns in self.zones[zone].nss:
324 raise NameServerAlreadyExistsError(ns)
325 self.zones[zone].nss[ns] = NameServer(ns)
326 self.zones[zone].mod = True
328 @handler(u'Deletes a name server from a zone')
329 def delete(self, zone, ns):
330 if not zone in self.zones:
331 raise ZoneNotFoundError(zone)
332 if not ns in self.zones[zone].nss:
333 raise NameServerNotFoundError(ns)
334 del self.zones[zone].nss[ns]
335 self.zones[zone].mod = True
337 @handler(u'Lists name servers')
339 return self.zones.keys()
341 @handler(u'Get information about all name servers')
343 return self.zones.values()
346 class Zone(Sequence):
347 def __init__(self, name):
357 return (self.name, self.hosts, self.mxs, self.nss)
359 class ZoneHandler(Handler):
361 r"""ZoneHandler(zones) -> ZoneHandler instance :: Handle a list of zones.
363 This class is a helper for DnsHandler to do all the work related to zone
366 zones - A dictionary with string keys (zone name) and Zone instances values.
368 def __init__(self, zones):
371 @handler(u'Adds a zone')
373 if name in self.zones:
374 if self.zones[name].dele == True:
375 self.zones[name].dele = False
377 raise ZoneAlreadyExistsError(name)
378 self.zones[name] = Zone(name)
379 self.zones[name].mod = True
380 self.zones[name].new = True
383 @handler(u'Deletes a zone')
384 def delete(self, name):
385 r"delete(name) -> None :: Delete a zone from the zone list."
386 if not name in self.zones:
387 raise ZoneNotFoundError(name)
388 self.zones[name].dele = True
390 @handler(u'Lists zones')
392 return self.zones.keys()
394 @handler(u'Get information about all zones')
396 return self.zones.values()
398 class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler,
400 r"""DnsHandler([pickle_dir[, config_dir]]) -> DnsHandler instance.
402 Handles DNS service commands for the dns program.
404 pickle_dir - Directory where to write the persistent configuration data.
406 config_dir - Directory where to store de generated configuration files.
408 Both defaults to the current working directory.
413 _persistent_vars = ('params', 'zones')
415 _restorable_defaults = dict(
425 _config_writer_files = ('named.conf', 'zoneX.zone')
426 _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates')
428 def __init__(self, pickle_dir='.', config_dir='.'):
429 r"Initialize DnsHandler object, see class documentation for details."
430 self._persistent_dir = pickle_dir
431 self._config_writer_cfg_dir = config_dir
433 self._config_build_templates()
435 self.host = HostHandler(self.zones)
436 self.zone = ZoneHandler(self.zones)
437 self.mx = MailExchangeHandler(self.zones)
438 self.ns = NameServerHandler(self.zones)
440 def _zone_filename(self, zone):
441 return zone.name + '.zone'
443 def _get_config_vars(self, config_file):
444 return dict(zones=self.zones.values(), **self.params)
446 def _write_config(self):
447 r"_write_config() -> None :: Generate all the configuration files."
448 delete_zones = list()
449 for a_zone in self.zones.values():
452 # TODO freeze de la zona
453 call(('dns', 'freeze', a_zone.name))
456 hosts = a_zone.hosts.values(),
457 mxs = a_zone.mxs.values(),
458 nss = a_zone.nss.values()
460 self._write_single_config('zoneX.zone',
461 self._zone_filename(a_zone), vars)
464 # TODO unfreeze de la zona
465 call(('dns', 'unfreeze', a_zone.name))
470 #borro el archivo .zone
473 unlink(self._zone_filename(a_zone))
475 #la excepcion pude darse en caso que haga un add de una zona y
476 #luego el del, como no hice commit, no se crea el archivo
478 delete_zones.append(a_zone.name)
480 for z in delete_zones:
484 self._write_single_config('named.conf')
488 if __name__ == '__main__':
492 dns.set('isp_dns1','la_garcha.com')
493 dns.set('bind_addr1','localhost')
494 dns.zone.add('zona_loca.com')
495 #dns.zone.update('zona_loca.com','ns1.dominio.com')
497 dns.host.add('zona_loca.com','hostname_loco','192.168.0.23')
498 dns.host.update('zona_loca.com','hostname_loco','192.168.0.66')
500 dns.host.add('zona_loca.com','hostname_kuak','192.168.0.23')
501 dns.host.delete('zona_loca.com','hostname_kuak')
503 dns.host.add('zona_loca.com','hostname_kuang','192.168.0.24')
504 dns.host.add('zona_loca.com','hostname_chan','192.168.0.25')
505 dns.host.add('zona_loca.com','hostname_kaine','192.168.0.26')
507 dns.mx.add('zona_loca.com','mx1.sarasa.com',10)
508 dns.mx.update('zona_loca.com','mx1.sarasa.com',20)
509 dns.mx.add('zona_loca.com','mx2.sarasa.com',30)
510 dns.mx.add('zona_loca.com','mx3.sarasa.com',40)
511 dns.mx.delete('zona_loca.com','mx3.sarasa.com')
513 dns.ns.add('zona_loca.com','ns1.jua.com')
514 dns.ns.add('zona_loca.com','ns2.jua.com')
515 dns.ns.add('zona_loca.com','ns3.jua.com')
516 dns.ns.delete('zona_loca.com','ns3.jua.com')
518 dns.zone.add('zona_oscura')
520 dns.host.add('zona_oscura','hostname_a','192.168.0.24')
521 dns.host.add('zona_oscura','hostname_b','192.168.0.25')
522 dns.host.add('zona_oscura','hostname_c','192.168.0.26')
524 dns.zone.delete('zona_oscura')
528 print 'ZONAS :', dns.zone.show()
529 print 'HOSTS :', dns.host.show()
533 # dns.zone.update('zone-sarasa','lalal')
534 #except ZoneNotFoundError, inst:
535 # print 'Error: ', inst
538 dns.zone.delete('zone-sarasa')
539 except ZoneNotFoundError, inst:
540 print 'Error: ', inst
543 # dns.zone.add('zona_loca.com','ns1.dom.com','ns2.dom.com')
544 #except ZoneAlreadyExistsError, inst:
545 # print 'Error: ', inst
549 dns.host.update('zone-sarasa','kuak','192.68')
550 except ZoneNotFoundError, inst:
551 print 'Error: ', inst
554 dns.host.update('zona_loca.com','kuak','192.68')
555 except HostNotFoundError, inst:
556 print 'Error: ', inst
559 dns.host.delete('zone-sarasa','lala')
560 except ZoneNotFoundError, inst:
561 print 'Error: ', inst
564 dns.host.delete('zona_loca.com','lala')
565 except HostNotFoundError, inst:
566 print 'Error: ', inst
569 dns.host.add('zona','hostname_loco','192.168.0.23')
570 except ZoneNotFoundError, inst:
571 print 'Error: ', inst
574 dns.host.add('zona_loca.com','hostname_loco','192.168.0.23')
575 except HostAlreadyExistsError, inst:
576 print 'Error: ', inst