import logging.config
import re
import copy
-import json
import os
import ipaddress
import socket
-import getpass
-import time
# Third party modules
-import requests
import psutil
# Own modules
from fb_tools.common import pp
from fb_pdnstools.zone import PowerDNSZone
-from fb_pdnstools.record import PowerDnsSOAData
+from fb_pdnstools.server import PowerDNSServer
+from fb_pdnstools.errors import PDNSApiNotFoundError
+from fb_pdnstools.errors import PDNSApiValidationError
from .cfg_app import PpCfgAppError, PpConfigApplication
-__version__ = '0.7.0'
+from .xlate import XLATOR
+
+__version__ = '0.7.1'
LOG = logging.getLogger(__name__)
_LIBRARY_NAME = "pp-pdns-api-client"
+_ = XLATOR.gettext
+
# =============================================================================
class PpPDNSAppError(PpCfgAppError):
pass
-# =============================================================================
-class PDNSApiError(PpPDNSAppError):
- """Base class for more complex exceptions"""
- def __init__(self, resp, content, uri=None):
- self.resp = resp
- self.content = content
- self.uri = uri
-
-
-# =============================================================================
-class PDNSApiNotAuthorizedError(PDNSApiError):
- """The authorization information provided is not correct"""
- pass
-
-
-# =============================================================================
-class PDNSApiNotFoundError(PDNSApiError):
- """The ProfitBricks entity was not found"""
- pass
-
-
-# =============================================================================
-class PDNSApiValidationError(PDNSApiError):
- """The HTTP data provided is not valid"""
- pass
-
-
-# =============================================================================
-class PDNSApiRateLimitExceededError(PDNSApiError):
- """The number of requests sent have exceeded the allowed API rate limit"""
- pass
-
-
-# =============================================================================
-class PDNSApiRequestError(PDNSApiError):
- """Base error for request failures"""
- pass
-
-
-# =============================================================================
-class PDNSApiTimeoutError(PDNSApiRequestError):
- """Raised when a request does not finish in the given time span."""
- pass
-
-
# =============================================================================
class PpPDNSApplication(PpConfigApplication):
"""
self.local_addresses = []
+ self.pdns = None
+
self._environment = 'global'
if environment != 'global':
self.environment = environment
for stem in cfg_stems:
s = str(stem).strip()
if not s:
- msg = "Invalid configuration stem {!r} given.".format(stem)
+ msg = _("Invalid configuration stem {!r} given.").format(stem)
raise PpPDNSAppError(msg)
stems.append(s)
else:
s = str(cfg_stems).strip()
if not s:
- msg = "Invalid configuration stem {!r} given.".format(cfg_stems)
+ msg = _("Invalid configuration stem {!r} given.").format(cfg_stems)
raise PpPDNSAppError(msg)
stems.append(s)
else:
@api_key.setter
def api_key(self, value):
if value is None or str(value).strip() == '':
- raise PpPDNSAppError("Invalid API key {!r} given.".format(value))
+ raise PpPDNSAppError(_("Invalid API key {!r} given.").format(value))
self._api_key = str(value).strip()
# -----------------------------------------------------------
@api_host.setter
def api_host(self, value):
if value is None or str(value).strip() == '':
- raise PpPDNSAppError("Invalid API host {!r} given.".format(value))
+ raise PpPDNSAppError(_("Invalid API host {!r} given.").format(value))
self._api_host = str(value).strip().lower()
# -----------------------------------------------------------
def api_port(self, value):
v = int(value)
if v < 1:
- raise PpPDNSAppError("Invalid API port {!r} given.".format(value))
+ raise PpPDNSAppError(_("Invalid API port {!r} given.").format(value))
self._api_port = v
# -----------------------------------------------------------
@api_servername.setter
def api_servername(self, value):
if value is None or str(value).strip() == '':
- raise PpPDNSAppError("Invalid API server name {!r} given.".format(value))
+ raise PpPDNSAppError(_("Invalid API server name {!r} given.").format(value))
self._api_servername = str(value).strip()
# -----------------------------------------------------------
@user_agent.setter
def user_agent(self, value):
if value is None or str(value).strip() == '':
- raise PpPDNSAppError("Invalid user agent {!r} given.".format(value))
+ raise PpPDNSAppError(_("Invalid user agent {!r} given.").format(value))
self._user_agent = str(value).strip()
# -----------------------------------------------------------
def timeout(self, value):
v = int(value)
if v < 1:
- raise PpPDNSAppError("Invalid timeout {!r} given.".format(value))
+ raise PpPDNSAppError(_("Invalid timeout {!r} given.").format(value))
self._timeout = v
# -----------------------------------------------------------
@environment.setter
def environment(self, value):
if value is None:
- raise PpPDNSAppError("Invalid environment None given.")
+ raise PpPDNSAppError(_("Invalid environment {!r} given.").format(None))
v = str(value).strip().lower()
if v not in self.api_keys.keys():
- raise PpPDNSAppError("Invalid environment {!r} given.".format(value))
+ raise PpPDNSAppError(_("Invalid environment {!r} given.").format(value))
self._environment = v
self._api_host = self.api_hosts[v]
self._api_key = self.api_keys[v]
super(PpPDNSApplication, self).init_arg_parser()
- pdns_group = self.arg_parser.add_argument_group('PowerDNS API options')
+ pdns_group = self.arg_parser.add_argument_group(_('PowerDNS API options'))
env_group = pdns_group.add_mutually_exclusive_group()
envs = []
env_group.add_argument(
'-E', '--env', '--environment',
- metavar="ENVIRONMENT", choices=envs, dest="env",
- help=(
+ metavar=_("ENVIRONMENT"), choices=envs, dest="env",
+ help=_(
"Select, which PowerDNS environment to use. "
- "Valid values: {v}, default: {d!r}.".format(
- v=', '.join(map(lambda x: repr(x), envs)),
- d='global'))
+ "Valid values: {v}, default: {d!r}.").format(
+ v=', '.join(map(lambda x: repr(x), envs)), d='global')
)
env_group.add_argument(
'-G', '--global',
action='store_true', dest="env_global",
- help=("Using the 'global' PowerDNS environment."),
+ help=_("Using the {!r} PowerDNS environment.").format('global'),
)
env_group.add_argument(
'-L', '--local',
action='store_true', dest="env_local",
- help=("Using the 'local' PowerDNS environment."),
+ help=_("Using the {!r} PowerDNS environment.").format('local'),
)
env_group.add_argument(
'-P', '--public',
action='store_true', dest="env_public",
- help=("Using the 'public' PowerDNS environment."),
+ help=_("Using the {!r} PowerDNS environment.").format('public'),
)
pdns_group.add_argument(
'-p', '--port',
- metavar="PORT", type=int, dest='api_port', default=self.default_api_port,
- help=("Which port to connect to PowerDNS API, default: {}.".format(
- self.default_api_port)),
+ metavar=_("PORT"), type=int, dest='api_port', default=self.default_api_port,
+ help=_("Which port to connect to PowerDNS API, default: {}.").format(
+ self.default_api_port),
)
pdns_group.add_argument(
'-t', '--timeout',
- metavar="SECS", type=int, dest='timeout', default=self.default_timeout,
- help=("The timeout in seconds to request the PowerDNS API, default: {}.".format(
- self.default_timeout)),
+ metavar=_("SECS"), type=int, dest='timeout', default=self.default_timeout,
+ help=_("The timeout in seconds to request the PowerDNS API, default: {}.").format(
+ self.default_timeout),
)
# -------------------------------------------------------------------------
for section_name in self.cfg.keys():
if self.verbose > 3:
- LOG.debug("Checking config section {!r} ...".format(section_name))
+ LOG.debug(_("Checking config section {!r} ...").format(section_name))
section = self.cfg[section_name]
def set_cfg_api_options(self, section, section_name):
if self.verbose > 2:
- LOG.debug("Evaluating config section {n!r}:\n{s}".format(
- n=section_name, s=pp(section)))
+ LOG.debug(
+ _("Evaluating config section {!r}:").format(section_name) + '\n' + pp(section))
if 'environment' in section:
v = section['environment'].strip().lower()
if v not in self.api_hosts:
- LOG.error("Wrong environment {!r} found in configuration.".format(
+ LOG.error(_("Wrong environment {!r} found in configuration.").format(
section['environment']))
self.config_has_errors = True
else:
port = int(section['port'])
if port <= 0 or port > 2**16:
raise ValueError(
- "a port must be greater than 0 and less than {}.".format(2**16))
+ _("A port must be greater than 0 and less than {}.").format(2**16))
except (TypeError, ValueError) as e:
- LOG.error("Wrong port number {!r} in configuration section {!r}: {}".format(
- section['port'], section_name, e))
+ LOG.error(_("Wrong port number {p!r} in configuration section {s!r}: {e}").format(
+ p=section['port'], s=section_name, e=e))
self.config_has_errors = True
else:
self.api_port = port
path = section[key].strip()
if not path:
- msg = "No path given for{} [{}]/{} in configuration.".format(
- d, section_name, key)
+ msg = _("No path given for{d} [{s}]/{k} in configuration.").format(
+ d=d, s=section_name, k=key)
LOG.error(msg)
self.config_has_errors = True
return
if absolute and not os.path.isabs(path):
- msg = "Path {!r} for{} [{}]/{} in configuration must be an absolute path.".format(
- path, d, section_name, key)
+ msg = _(
+ "Path {p!r} for{d} [{s}]/{k} in configuration must be an absolute "
+ "path.").format(p=path, d=d, s=section_name, k=key)
LOG.error(msg)
self.config_has_errors = True
return
setattr(self, class_prop, path)
+ # -------------------------------------------------------------------------
+ def post_init(self):
+ """
+ Method to execute before calling run(). Here could be done some
+ finishing actions after reading in commandline parameters,
+ configuration a.s.o.
+
+ This method could be overwritten by descendant classes, these
+ methhods should allways include a call to post_init() of the
+ parent class.
+
+ """
+
+ if self.verbose > 1:
+ LOG.debug(_("Executing {} ...").format('post_init()'))
+
+ self.pdns = PowerDNSServer(
+ appname=self.appname, verbose=self.verbose, base_dir=self.base_dir,
+ master_server=self.api_host, port=self.api_port,
+ key=self.api_key, use_https=False,
+ simulate=self.simulate, force=self.force, initialized=False,
+ )
+ self.pdns.initialized = True
+
+ self.initialized = True
+
# -------------------------------------------------------------------------
def pre_run(self):
"""
"""
if self.verbose > 1:
- LOG.debug("executing pre_run() ...")
+ LOG.debug(_("Executing {} ...").format('pre_run()'))
- LOG.debug("Setting Loglevel of the requests module to WARNING")
+ LOG.debug(_("Setting Loglevel of the requests module to {}.").format('WARNING'))
logging.getLogger("requests").setLevel(logging.WARNING)
super(PpPDNSApplication, self).pre_run()
MUST be overwritten by descendant classes.
"""
- LOG.debug("Executing nothing ...")
+ LOG.debug(_("Executing nothing ..."))
# -------------------------------------------------------------------------
def post_run(self):
"""
if self.verbose > 1:
- LOG.debug("executing post_run() ...")
+ LOG.debug(_("Executing {} ...").format('post_run()'))
# -------------------------------------------------------------------------
def get_api_server_version(self):
- path = "/servers/{}".format(self.api_servername)
- try:
- json_response = self.perform_request(path)
- except (PDNSApiNotFoundError, PDNSApiValidationError):
- LOG.error("Could not found server info.")
- return None
- if self.verbose > 2:
- LOG.debug("Got a response:\n{}".format(pp(json_response)))
+ if not self.pdns:
+ raise PpPDNSAppError(_("The PDNS server object does not exists."))
+ if not self.pdns.initialized:
+ raise PpPDNSAppError(_("The PDNS server object is not initialized."))
- if 'version' in json_response:
- self._api_server_version = json_response['version']
- LOG.info("PowerDNS server version {!r}.".format(self.api_server_version))
- return self.api_server_version
- LOG.error("Did not found version info in server info:\n{}".format(pp(json_response)))
- return None
+ return self.pdns.get_api_server_version()
# -------------------------------------------------------------------------
def _build_url(self, path):
def perform_request(self, path, method='GET', data=None, headers=None, may_simulate=False):
"""Performing the underlying API request."""
- if headers is None:
- headers = dict()
- headers['X-API-Key'] = self.api_key
-
- url = self._build_url(path)
- if self.verbose > 1:
- LOG.debug("Request method: {!r}".format(method))
- if data and self.verbose > 2:
- data_out = "{!r}".format(data)
- try:
- data_out = json.loads(data)
- except ValueError:
- pass
- else:
- data_out = pp(data_out)
- LOG.debug("Data:\n{}".format(data_out))
- LOG.debug("RAW data:\n{}".format(data))
-
- headers.update({'User-Agent': self.user_agent})
- headers.update({'Content-Type': 'application/json'})
- if self.verbose > 1:
- LOG.debug("Headers:\n%s", pp(headers))
-
- if may_simulate and self.simulate:
- LOG.debug("Simulation mode, Request will not be sent.")
- return ''
+ if not self.pdns:
+ raise PpPDNSAppError(_("The PDNS server object does not exists."))
+ if not self.pdns.initialized:
+ raise PpPDNSAppError(_("The PDNS server object is not initialized."))
- session = requests.Session()
- response = session.request(method, url, data=data, headers=headers, timeout=self.timeout)
-
- try:
- if not response.ok:
- LOG.debug
- err = response.json()
- code = response.status_code
- msg = err['error']
- if response.status_code == 401:
- raise PDNSApiNotAuthorizedError(code, msg, url)
- if response.status_code == 404:
- raise PDNSApiNotFoundError(code, msg, url)
- if response.status_code == 422:
- raise PDNSApiValidationError(code, msg, url)
- if response.status_code == 429:
- raise PDNSApiRateLimitExceededError(code, msg, url)
- else:
- raise PDNSApiError(code, msg, url)
-
- except ValueError:
- raise PpPDNSAppError('Failed to parse the response', response.text)
-
- if self.verbose > 3:
- LOG.debug("RAW response: {!r}.".format(response.text))
- if not response.text:
- return ''
-
- json_response = response.json()
-
- if 'location' in response.headers:
- json_response['requestId'] = self._request_id(response.headers)
-
- return json_response
+ return self.pdns.perform_request(
+ path, method=method, data=data, headers=headers, may_simulate=may_simulate)
# -------------------------------------------------------------------------
def get_api_zones(self):
- LOG.debug("Trying to get all zones from PDNS API ...")
-
- path = "/servers/{}/zones".format(self.api_servername)
- json_response = self.perform_request(path)
- if self.verbose > 3:
- LOG.debug("Got a response:\n{}".format(pp(json_response)))
-
- zone_list = []
-
- for data in json_response:
- zone = PowerDNSZone.init_from_dict(
- data, appname=self.appname, verbose=self.verbose, base_dir=self.base_dir)
- zone_list.append(zone)
- if self.verbose > 2:
- print("{!r}".format(zone))
-
- if self.verbose > 1:
- LOG.debug("Found {} zones.".format(len(zone_list)))
+ if not self.pdns:
+ raise PpPDNSAppError(_("The PDNS server object does not exists."))
+ if not self.pdns.initialized:
+ raise PpPDNSAppError(_("The PDNS server object is not initialized."))
- return zone_list
+ return self.pdns.get_api_zones()
# -------------------------------------------------------------------------
def get_api_zone(self, zone_name):
+ if not self.pdns:
+ raise PpPDNSAppError(_("The PDNS server object does not exists."))
+ if not self.pdns.initialized:
+ raise PpPDNSAppError(_("The PDNS server object is not initialized."))
+
zone_unicode = zone_name
json_response = None
zout = "{!r}".format(zone_name)
if 'xn--' in zone_name:
zone_unicode = zone_name.encode('idna').decode('idna')
zout = "{!r} ({})".format(zone_name, zone_unicode)
- LOG.debug("Trying to get complete information about zone {!r} ...".format(zone_name))
+ LOG.debug(_("Trying to get complete information about zone {!r} ...").format(zone_name))
- path = "/servers/{}/zones/{}".format(self.api_servername, zone_name)
+ path = "/servers/{}/zones/{}".format(self.pdns.api_servername, zone_name)
try:
json_response = self.perform_request(path)
except (PDNSApiNotFoundError, PDNSApiValidationError):
- LOG.error("The given zone {} was not found.".format(zout))
+ LOG.error(_("The given zone {} was not found.").format(zout))
return None
if self.verbose > 2:
- LOG.debug("Got a response:\n{}".format(pp(json_response)))
+ LOG.debug(_("Got a response:") + '\n' + pp(json_response))
zone = PowerDNSZone.init_from_dict(
json_response, appname=self.appname, verbose=self.verbose, base_dir=self.base_dir)
if self.verbose > 2:
- LOG.debug("Zone object:\n{}".format(pp(zone.as_dict())))
+ LOG.debug(_("Zone object:") + '\n' + pp(zone.as_dict()))
return zone
- # -------------------------------------------------------------------------
- def patch_zone(self, zone, payload):
-
- if self.verbose > 1:
- LOG.debug("Patching zone {!r} ...".format(zone.name))
-
- path = "/servers/{}/zones/{}".format(self.api_servername, zone.name)
- return self.perform_request(path, 'PATCH', json.dumps(payload), may_simulate=True)
-
- # -------------------------------------------------------------------------
- def update_soa(self, zone, new_soa, comment=None, ttl=None):
-
- if not isinstance(new_soa, PowerDnsSOAData):
- msg = "New SOA must by of type PowerDnsSOAData, given {t}: {s!r}".format(
- t=new_soa.__class__.__name__, s=new_soa)
- raise TypeError(msg)
-
- if ttl:
- ttl = int(ttl)
- else:
- cur_soa_rrset = zone.get_soa_rrset()
- ttl = cur_soa_rrset.ttl
-
- if comment is not None:
- comment = str(comment).strip()
- if comment == '':
- comment = None
-
- rrset = {
- 'name': zone.name,
- 'type': 'SOA',
- 'ttl': ttl,
- 'changetype': 'REPLACE',
- 'records': [],
- 'comments': [],
- }
-
-# if comment:
-# comment_rec = {
-# 'content': comment,
-# 'account': getpass.getuser(),
-# 'modified_at': int(time.time() + 0.5),
-# }
-# rrset['comments'] = [comment_rec]
-
- record = {
- 'content': new_soa.data,
- 'disabled': False,
- 'name': zone.name,
- 'set-ptr': False,
- 'type': 'SOA',
- }
- rrset['records'].append(record)
- payload = {"rrsets": [rrset]}
-
- if self.verbose > 1:
- LOG.debug("Setting new SOA {s!r} for zone {z!r}, TTL {t} ...".format(
- s=new_soa.data, z=zone.name, t=ttl))
-
- self.patch_zone(zone, payload)
-
- # -------------------------------------------------------------------------
- def increase_serial(self, zone_name, comment=None):
-
- zone = self.get_api_zone(zone_name)
- if not zone:
- raise PpPDNSAppError("Did not found zone for {!r}.".format(zone_name))
-
- LOG.info("Increasing serial in SOA of zone {!r} ....".format(zone_name))
-
- api_host_address = None
- for addr_info in socket.getaddrinfo(self.api_host, 53, family=socket.AF_INET):
- api_host_address = addr_info[4][0]
- break
-
- api_soa = zone.get_soa()
- if not api_soa:
- raise PpPDNSAppError("Could not find SOA for zone {!r}.".format(zone_name))
- if self.verbose > 2:
- LOG.debug("Got SOA for zone {z!r} by API:\n{s}".format(
- z=zone_name, s=api_soa))
-
- dns_soa = zone.get_soa_by_dns(api_host_address)
- if self.verbose > 2:
- LOG.debug("Got SOA for zone {z!r} from DNS by {h!r}:\n{s}".format(
- h=self.api_host, z=zone_name, s=dns_soa))
-
- new_serial = zone.get_new_serial(dns_soa.serial)
- LOG.debug("Got new serial number for zone {z!r}: {s}.".format(
- z=zone_name, s=new_serial))
-
- api_soa.serial = new_serial
- return self.update_soa(zone, api_soa, comment)
-
- # -------------------------------------------------------------------------
- def set_nameservers(
- self, zone, new_nameservers, for_zone=None, comment=None, new_ttl=None,
- do_serial=True, do_notify=True):
-
- current_nameservers = zone.get_zone_nameservers(for_zone=for_zone)
- if for_zone:
- LOG.debug("Current nameservers of {f!r} in zone {z!r}:\n{ns}".format(
- f=for_zone, z=zone.name, ns=pp(current_nameservers)))
- else:
- LOG.debug("Current nameservers of zone {z!r}:\n{ns}".format(
- z=zone.name, ns=pp(current_nameservers)))
-
- ns2remove = []
- ns2add = []
-
- for ns in current_nameservers:
- if ns not in new_nameservers:
- ns2remove.append(ns)
- for ns in new_nameservers:
- if ns not in current_nameservers:
- ns2add.append(ns)
-
- if not ns2remove and not ns2add:
- if for_zone:
- msg = "Subzone {f!r} has already the expected nameservers in zone {z!r}."
- else:
- msg = "Zone {z!r} has already the expected nameservers."
- LOG.info(msg.format(f=for_zone, z=zone.name))
- return False
-
- LOG.debug("Nameservers to remove from zone {z!r}:\n{ns}".format(
- z=zone.name, ns=pp(ns2remove)))
- LOG.debug("Nameservers to add to zone {z!r}:\n{ns}".format(
- z=zone.name, ns=pp(ns2add)))
-
- ns_ttl = None
- if not new_ttl:
- cur_rrset = zone.get_ns_rrset(for_zone=for_zone)
- if cur_rrset:
- ns_ttl = cur_rrset.ttl
- else:
- soa = zone.get_soa()
- ns_ttl = soa.ttl
- del soa
- else:
- ns_ttl = int(new_ttl)
- if ns_ttl <= 0:
- ns_ttl = 3600
- LOG.debug("TTL for NS records: {}.".format(ns_ttl))
-
- rrset_name = zone.name.lower()
- if for_zone:
- rrset_name = for_zone.lower()
-
- records = []
- for ns in new_nameservers:
- record = {
- "name": rrset_name,
- "type": "NS",
- "content": ns,
- "disabled": False,
- "set-ptr": False,
- }
- records.append(record)
- rrset = {
- "name": rrset_name,
- "type": "NS",
- "ttl": ns_ttl,
- "changetype": "REPLACE",
- "records": records,
- }
-
- if comment:
- comment_rec = {
- 'content': comment,
- 'account': getpass.getuser(),
- 'modified_at': int(time.time() + 0.5),
- }
- rrset['comments'] = [comment_rec]
-
- payload = {"rrsets": [rrset]}
-
- self.patch_zone(zone, payload)
-
- if do_serial:
- self.increase_serial(zone.name)
-
- if do_notify:
- self.notify_zone(zone)
-
- return True
-
- # -------------------------------------------------------------------------
- def notify_zone(self, zone):
-
- LOG.info("Notifying slaves of zone {!r} ...".format(zone.name))
-
- path = "/servers/{}/zones/{}/notify".format(self.api_servername, zone.name)
- return self.perform_request(path, 'PUT', '', may_simulate=True)
+# # -------------------------------------------------------------------------
+# def patch_zone(self, zone, payload):
+#
+# return zone.patch(payload)
+#
+# # -------------------------------------------------------------------------
+# def update_soa(self, zone, new_soa, comment=None, ttl=None):
+#
+# return zone.update_soa(new_soa=new_soa, comment=comment, ttl=ttl)
+#
+# # -------------------------------------------------------------------------
+# def set_nameservers(
+# self, zone, new_nameservers, for_zone=None, comment=None, new_ttl=None,
+# do_serial=True, do_notify=True):
+#
+# current_nameservers = zone.get_zone_nameservers(for_zone=for_zone)
+# if for_zone:
+# LOG.debug("Current nameservers of {f!r} in zone {z!r}:\n{ns}".format(
+# f=for_zone, z=zone.name, ns=pp(current_nameservers)))
+# else:
+# LOG.debug("Current nameservers of zone {z!r}:\n{ns}".format(
+# z=zone.name, ns=pp(current_nameservers)))
+#
+# ns2remove = []
+# ns2add = []
+#
+# for ns in current_nameservers:
+# if ns not in new_nameservers:
+# ns2remove.append(ns)
+# for ns in new_nameservers:
+# if ns not in current_nameservers:
+# ns2add.append(ns)
+#
+# if not ns2remove and not ns2add:
+# if for_zone:
+# msg = "Subzone {f!r} has already the expected nameservers in zone {z!r}."
+# else:
+# msg = "Zone {z!r} has already the expected nameservers."
+# LOG.info(msg.format(f=for_zone, z=zone.name))
+# return False
+#
+# LOG.debug("Nameservers to remove from zone {z!r}:\n{ns}".format(
+# z=zone.name, ns=pp(ns2remove)))
+# LOG.debug("Nameservers to add to zone {z!r}:\n{ns}".format(
+# z=zone.name, ns=pp(ns2add)))
+#
+# ns_ttl = None
+# if not new_ttl:
+# cur_rrset = zone.get_ns_rrset(for_zone=for_zone)
+# if cur_rrset:
+# ns_ttl = cur_rrset.ttl
+# else:
+# soa = zone.get_soa()
+# ns_ttl = soa.ttl
+# del soa
+# else:
+# ns_ttl = int(new_ttl)
+# if ns_ttl <= 0:
+# ns_ttl = 3600
+# LOG.debug("TTL for NS records: {}.".format(ns_ttl))
+#
+# rrset_name = zone.name.lower()
+# if for_zone:
+# rrset_name = for_zone.lower()
+#
+# records = []
+# for ns in new_nameservers:
+# record = {
+# "name": rrset_name,
+# "type": "NS",
+# "content": ns,
+# "disabled": False,
+# "set-ptr": False,
+# }
+# records.append(record)
+# rrset = {
+# "name": rrset_name,
+# "type": "NS",
+# "ttl": ns_ttl,
+# "changetype": "REPLACE",
+# "records": records,
+# }
+#
+# if comment:
+# comment_rec = {
+# 'content': comment,
+# 'account': getpass.getuser(),
+# 'modified_at': int(time.time() + 0.5),
+# }
+# rrset['comments'] = [comment_rec]
+#
+# payload = {"rrsets": [rrset]}
+#
+# self.patch_zone(zone, payload)
+#
+# if do_serial:
+# zone.increase_serial()
+#
+# if do_notify:
+# self.notify_zone(zone)
+#
+# return True
+#
+# # -------------------------------------------------------------------------
+# def notify_zone(self, zone):
+#
+# LOG.info("Notifying slaves of zone {!r} ...".format(zone.name))
+#
+# path = "/servers/{}/zones/{}/notify".format(self.api_servername, zone.name)
+# return self.perform_request(path, 'PUT', '', may_simulate=True)
# =============================================================================