211 Build directive from optional parts |
215 Build directive from optional parts |
212 """ |
216 """ |
213 |
217 |
214 return cls(directive, arguments, **opts) |
218 return cls(directive, arguments, **opts) |
215 |
219 |
|
220 @classmethod |
|
221 def INCLUDE (cls, path, origin=None, **opts): |
|
222 """ |
|
223 Build $INCLUDE "path" [origin] |
|
224 |
|
225 Note that origin should be a FQDN, or it will be interpreted relative to the current origin! |
|
226 """ |
|
227 |
|
228 if origin: |
|
229 return cls.build('INCLUDE', zone_quote(path), origin, **opts) |
|
230 else: |
|
231 return cls.build('INCLUDE', zone_quote(path), **opts) |
|
232 |
216 def __init__ (self, directive, arguments, comment=None, line=None, origin=None): |
233 def __init__ (self, directive, arguments, comment=None, line=None, origin=None): |
217 """ |
234 """ |
218 directive - uppercase directive name, withtout leading $ |
235 directive - uppercase directive name, withtout leading $ |
219 arguments [str] - list of directive arguments |
236 arguments [str] - list of directive arguments |
220 comment - optional trailing comment |
237 comment - optional trailing comment |
324 log.debug("%s: %s", line, directive) |
345 log.debug("%s: %s", line, directive) |
325 |
346 |
326 if directive.directive == 'ORIGIN': |
347 if directive.directive == 'ORIGIN': |
327 directive_origin, = directive.arguments |
348 directive_origin, = directive.arguments |
328 |
349 |
329 log.info("%s: $ORIGIN %s -> %s", file, origin, directive_origin) |
350 log.info("%s: $ORIGIN %s <- %s", line, directive_origin, origin) |
330 |
351 |
331 origin = pvl.dns.labels.join(origin, directive_origin) |
352 origin = pvl.dns.labels.join(origin, directive_origin) |
332 |
353 |
333 elif directive.directive == 'TTL' : |
354 elif directive.directive == 'TTL' : |
334 directive_ttl, = directive.arguments |
355 directive_ttl, = directive.arguments |
335 |
356 |
336 log.info("%s: $TTL %d -> %s", file, ttl, directive_ttl) |
357 log.info("%s: $TTL %s <- %s", line, directive_ttl, ttl) |
337 |
358 |
338 ttl = int(directive_ttl) |
359 ttl = int(directive_ttl) |
339 |
360 |
340 elif directive.directive == 'GENERATE' : |
361 elif directive.directive == 'GENERATE' : |
341 for record in process_generate(line, directive.arguments, |
362 for record in process_generate(line, directive.arguments, |
396 |
417 |
397 # parse ttl/cls/type |
418 # parse ttl/cls/type |
398 _cls = None |
419 _cls = None |
399 |
420 |
400 if parts and parts[0][0].isdigit() : |
421 if parts and parts[0][0].isdigit() : |
401 ttl = int(parts.pop(0)) |
422 ttl = parts.pop(0) |
402 |
423 |
403 if parts and parts[0].upper() in ('IN', 'CH') : |
424 if parts and parts[0].upper() in ('IN', 'CH') : |
404 _cls = parts.pop(0).upper() |
425 _cls = parts.pop(0) |
405 |
426 |
406 # always have type |
427 # always have type |
407 type = parts.pop(0).upper() |
428 type = parts.pop(0) |
408 |
429 |
409 # remaining parts are data |
430 # remaining parts are data |
410 data = parts |
431 data = parts |
411 |
432 |
412 log.debug(" ttl=%r, cls=%r, type=%r, data=%r", ttl, _cls, type, data) |
433 log.debug(" ttl=%r, cls=%r, type=%r, data=%r", ttl, _cls, type, data) |
413 |
434 |
414 return cls(name, ttl, _cls, type, data, line=line, **opts) |
435 # optional subclass for build() |
|
436 cls = ZONE_RECORD_TYPES.get(type.upper(), cls) |
|
437 |
|
438 return cls.build(name, type, *data, ttl=ttl, cls=_cls, line=line, **opts) |
415 |
439 |
416 @classmethod |
440 @classmethod |
417 def build (_cls, name, type, *data, **opts): |
441 def build (_cls, name, type, *data, **opts): |
418 """ |
442 """ |
419 Simple interface to build ZoneRecord from required parts. All optional fields must be given as keyword arguments. |
443 Simple interface to build ZoneRecord from required parts. All optional fields must be given as keyword arguments. |
534 def __repr__ (self) : |
558 def __repr__ (self) : |
535 return '%s(%s)' % (self.__class__.__name__, ', '.join(repr(arg) for arg in ( |
559 return '%s(%s)' % (self.__class__.__name__, ', '.join(repr(arg) for arg in ( |
536 self.name, self.ttl, self.cls, self.type, self.data |
560 self.name, self.ttl, self.cls, self.type, self.data |
537 ))) |
561 ))) |
538 |
562 |
539 class SOA (ZoneRecord) : |
563 class ZoneRecordSOA (ZoneRecord): |
540 @classmethod |
564 """ |
541 def build (cls, line, name, ttl, _cls, type, data, **opts) : |
565 Specialized SOA record. |
542 assert name == '@' |
566 """ |
543 |
567 |
544 return cls(*data, |
568 @classmethod |
545 ttl = ttl, |
569 def build (_cls, name, type, |
546 cls = cls, |
570 master, contact, serial, refresh, retry, expire, nxttl, |
547 line = line, |
571 line=None, |
|
572 **opts |
|
573 ): |
|
574 assert type == 'SOA' |
|
575 |
|
576 if name != '@': |
|
577 raise ZoneLineError(line, "SOA should be @: {name}", name=name) |
|
578 |
|
579 return super(ZoneRecordSOA, _cls).build('@', 'SOA', |
|
580 master, contact, serial, refresh, retry, expire, nxttl, |
548 **opts |
581 **opts |
549 ) |
582 ) |
550 |
583 |
551 def __init__ (self, master, contact, serial, refresh, retry, expire, nxttl, **opts) : |
584 def __init__ (self, name, ttl, cls, type, data, **opts): |
552 super(SOA, self).__init__('@', 'SOA', |
585 super(ZoneRecordSOA, self).__init__(name, ttl, cls, type, data, **opts) |
553 [master, contact, serial, refresh, retry, expire, nxttl], |
586 |
554 **opts |
587 self.master, self.contact, self.serial, self.refresh, self.retry, self.expire, self.nxttl = data |
555 ) |
588 |
556 |
589 ZONE_RECORD_TYPES = { |
557 self.master = master |
590 'SOA': ZoneRecordSOA, |
558 self.contact = contact |
591 } |
559 self.serial = serial |
|
560 self.refresh = refresh |
|
561 self.retry = retry |
|
562 self.expire = expire |
|
563 self.nxttl = nxttl |
|
564 |
|