/opt/imh-python/lib/python3.9/site-packages/libcloud/dns/drivers
NameSizeModeActions
__pycache__/-0755rm
auroradns.py221810644editdlrm
buddyns.py46940644editdlrm
cloudflare.py163530644editdlrm
digitalocean.py104320644editdlrm
dnsimple.py97300644editdlrm
dnspod.py117240644editdlrm
dummy.py77890644editdlrm
durabledns.py262080644editdlrm
gandi.py91840644editdlrm
gandi_live.py182960644editdlrm
godaddy.py159800644editdlrm
google.py126240644editdlrm
hostvirtual.py93710644editdlrm
linode.py103510644editdlrm
liquidweb.py129070644editdlrm
luadns.py96050644editdlrm
nfsn.py71630644editdlrm
nsone.py120650644editdlrm
onapp.py106840644editdlrm
pointdns.py284210644editdlrm
powerdns.py196540644editdlrm
rackspace.py245980644editdlrm
rcodezero.py188860644editdlrm
route53.py212250644editdlrm
softlayer.py74800644editdlrm
vultr.py119850644editdlrm
worldwidedns.py189420644editdlrm
zerigo.py182640644editdlrm
zonomi.py111720644editdlrm
__init__.py00644editdlrm
Edit: /opt/imh-python/lib/python3.9/site-packages/libcloud/dns/drivers/zerigo.py (18264B)
# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. __all__ = [ 'ZerigoDNSDriver' ] import copy import base64 from libcloud.utils.py3 import httplib from libcloud.utils.py3 import b from libcloud.utils.py3 import ET from libcloud.utils.misc import merge_valid_keys, get_new_obj from libcloud.utils.xml import findtext, findall from libcloud.common.base import XmlResponse, ConnectionUserAndKey from libcloud.common.types import InvalidCredsError, LibcloudError from libcloud.common.types import MalformedResponseError from libcloud.dns.types import Provider, RecordType from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError from libcloud.dns.base import DNSDriver, Zone, Record API_HOST = 'ns.zerigo.com' API_VERSION = '1.1' API_ROOT = '/api/%s/' % (API_VERSION) VALID_ZONE_EXTRA_PARAMS = ['notes', 'tag-list', 'ns1', 'slave-nameservers'] VALID_RECORD_EXTRA_PARAMS = ['notes', 'ttl', 'priority'] # Number of items per page (maximum limit is 1000) ITEMS_PER_PAGE = 100 class ZerigoError(LibcloudError): def __init__(self, code, errors): self.code = code self.errors = errors or [] def __str__(self): return 'Errors: %s' % (', '.join(self.errors)) def __repr__(self): return ('' % ( self.code, len(self.errors))) class ZerigoDNSResponse(XmlResponse): def success(self): return self.status in [httplib.OK, httplib.CREATED, httplib.ACCEPTED] def parse_error(self): status = int(self.status) if status == 401: if not self.body: raise InvalidCredsError(str(self.status) + ': ' + self.error) else: raise InvalidCredsError(self.body) elif status == 404: context = self.connection.context if context['resource'] == 'zone': raise ZoneDoesNotExistError(value='', driver=self, zone_id=context['id']) elif context['resource'] == 'record': raise RecordDoesNotExistError(value='', driver=self, record_id=context['id']) elif status != 503: try: body = ET.XML(self.body) except Exception: raise MalformedResponseError('Failed to parse XML', body=self.body) errors = [] for error in findall(element=body, xpath='error'): errors.append(error.text) raise ZerigoError(code=status, errors=errors) return self.body class ZerigoDNSConnection(ConnectionUserAndKey): host = API_HOST secure = True responseCls = ZerigoDNSResponse def add_default_headers(self, headers): auth_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key))) headers['Authorization'] = 'Basic %s' % (auth_b64.decode('utf-8')) return headers def request(self, action, params=None, data='', headers=None, method='GET'): if not headers: headers = {} if not params: params = {} if method in ("POST", "PUT"): headers = {'Content-Type': 'application/xml; charset=UTF-8'} return super(ZerigoDNSConnection, self).request(action=action, params=params, data=data, method=method, headers=headers) class ZerigoDNSDriver(DNSDriver): type = Provider.ZERIGO name = 'Zerigo DNS' website = 'http://www.zerigo.com/' connectionCls = ZerigoDNSConnection RECORD_TYPE_MAP = { RecordType.A: 'A', RecordType.AAAA: 'AAAA', RecordType.CNAME: 'CNAME', RecordType.GEO: 'GEO', RecordType.MX: 'MX', RecordType.NAPTR: 'NAPTR', RecordType.NS: 'NS', RecordType.PTR: 'PTR', RecordType.REDIRECT: 'REDIRECT', RecordType.SPF: 'SPF', RecordType.SRV: 'SRV', RecordType.TXT: 'TXT', RecordType.URL: 'URL', } def iterate_zones(self): return self._get_more('zones') def iterate_records(self, zone): return self._get_more('records', zone=zone) def get_zone(self, zone_id): path = API_ROOT + 'zones/%s.xml' % (zone_id) self.connection.set_context({'resource': 'zone', 'id': zone_id}) data = self.connection.request(path).object zone = self._to_zone(elem=data) return zone def get_record(self, zone_id, record_id): zone = self.get_zone(zone_id=zone_id) self.connection.set_context({'resource': 'record', 'id': record_id}) path = API_ROOT + 'hosts/%s.xml' % (record_id) data = self.connection.request(path).object record = self._to_record(elem=data, zone=zone) return record def create_zone(self, domain, type='master', ttl=None, extra=None): """ Create a new zone. Provider API docs: https://www.zerigo.com/docs/apis/dns/1.1/zones/create @inherits: :class:`DNSDriver.create_zone` """ path = API_ROOT + 'zones.xml' zone_elem = self._to_zone_elem(domain=domain, type=type, ttl=ttl, extra=extra) data = self.connection.request(action=path, data=ET.tostring(zone_elem), method='POST').object zone = self._to_zone(elem=data) return zone def update_zone(self, zone, domain=None, type=None, ttl=None, extra=None): """ Update an existing zone. Provider API docs: https://www.zerigo.com/docs/apis/dns/1.1/zones/update @inherits: :class:`DNSDriver.update_zone` """ if domain: raise LibcloudError('Domain cannot be changed', driver=self) path = API_ROOT + 'zones/%s.xml' % (zone.id) zone_elem = self._to_zone_elem(domain=domain, type=type, ttl=ttl, extra=extra) response = self.connection.request(action=path, data=ET.tostring(zone_elem), method='PUT') assert response.status == httplib.OK merged = merge_valid_keys(params=copy.deepcopy(zone.extra), valid_keys=VALID_ZONE_EXTRA_PARAMS, extra=extra) updated_zone = get_new_obj(obj=zone, klass=Zone, attributes={'type': type, 'ttl': ttl, 'extra': merged}) return updated_zone def create_record(self, name, zone, type, data, extra=None): """ Create a new record. Provider API docs: https://www.zerigo.com/docs/apis/dns/1.1/hosts/create @inherits: :class:`DNSDriver.create_record` """ path = API_ROOT + 'zones/%s/hosts.xml' % (zone.id) record_elem = self._to_record_elem(name=name, type=type, data=data, extra=extra) response = self.connection.request(action=path, data=ET.tostring(record_elem), method='POST') assert response.status == httplib.CREATED record = self._to_record(elem=response.object, zone=zone) return record def update_record(self, record, name=None, type=None, data=None, extra=None): path = API_ROOT + 'hosts/%s.xml' % (record.id) record_elem = self._to_record_elem(name=name, type=type, data=data, extra=extra) response = self.connection.request(action=path, data=ET.tostring(record_elem), method='PUT') assert response.status == httplib.OK merged = merge_valid_keys(params=copy.deepcopy(record.extra), valid_keys=VALID_RECORD_EXTRA_PARAMS, extra=extra) updated_record = get_new_obj(obj=record, klass=Record, attributes={'type': type, 'data': data, 'extra': merged}) return updated_record def delete_zone(self, zone): path = API_ROOT + 'zones/%s.xml' % (zone.id) self.connection.set_context({'resource': 'zone', 'id': zone.id}) response = self.connection.request(action=path, method='DELETE') return response.status == httplib.OK def delete_record(self, record): path = API_ROOT + 'hosts/%s.xml' % (record.id) self.connection.set_context({'resource': 'record', 'id': record.id}) response = self.connection.request(action=path, method='DELETE') return response.status == httplib.OK def ex_get_zone_by_domain(self, domain): """ Retrieve a zone object by the domain name. :param domain: The domain which should be used :type domain: ``str`` :rtype: :class:`Zone` """ path = API_ROOT + 'zones/%s.xml' % (domain) self.connection.set_context({'resource': 'zone', 'id': domain}) data = self.connection.request(path).object zone = self._to_zone(elem=data) return zone def ex_force_slave_axfr(self, zone): """ Force a zone transfer. :param zone: Zone which should be used. :type zone: :class:`Zone` :rtype: :class:`Zone` """ path = API_ROOT + 'zones/%s/force_slave_axfr.xml' % (zone.id) self.connection.set_context({'resource': 'zone', 'id': zone.id}) response = self.connection.request(path, method='POST') assert response.status == httplib.ACCEPTED return zone def _to_zone_elem(self, domain=None, type=None, ttl=None, extra=None): zone_elem = ET.Element('zone', {}) if domain: domain_elem = ET.SubElement(zone_elem, 'domain') domain_elem.text = domain if type: ns_type_elem = ET.SubElement(zone_elem, 'ns-type') if type == 'master': ns_type_elem.text = 'pri_sec' elif type == 'slave': if not extra or 'ns1' not in extra: raise LibcloudError('ns1 extra attribute is required ' + 'when zone type is slave', driver=self) ns_type_elem.text = 'sec' ns1_elem = ET.SubElement(zone_elem, 'ns1') ns1_elem.text = extra['ns1'] elif type == 'std_master': # TODO: Each driver should provide supported zone types # Slave name servers are elsewhere if not extra or 'slave-nameservers' not in extra: raise LibcloudError('slave-nameservers extra ' + 'attribute is required whenzone ' + 'type is std_master', driver=self) ns_type_elem.text = 'pri' slave_nameservers_elem = ET.SubElement(zone_elem, 'slave-nameservers') slave_nameservers_elem.text = extra['slave-nameservers'] if ttl: default_ttl_elem = ET.SubElement(zone_elem, 'default-ttl') default_ttl_elem.text = str(ttl) if extra and 'tag-list' in extra: tags = extra['tag-list'] tags_elem = ET.SubElement(zone_elem, 'tag-list') tags_elem.text = ' '.join(tags) return zone_elem def _to_record_elem(self, name=None, type=None, data=None, extra=None): record_elem = ET.Element('host', {}) if name: name_elem = ET.SubElement(record_elem, 'hostname') name_elem.text = name if type is not None: type_elem = ET.SubElement(record_elem, 'host-type') type_elem.text = self.RECORD_TYPE_MAP[type] if data: data_elem = ET.SubElement(record_elem, 'data') data_elem.text = data if extra: if 'ttl' in extra: ttl_elem = ET.SubElement(record_elem, 'ttl', {'type': 'integer'}) ttl_elem.text = str(extra['ttl']) if 'priority' in extra: # Only MX and SRV records support priority priority_elem = ET.SubElement(record_elem, 'priority', {'type': 'integer'}) priority_elem.text = str(extra['priority']) if 'notes' in extra: notes_elem = ET.SubElement(record_elem, 'notes') notes_elem.text = extra['notes'] return record_elem def _to_zones(self, elem): zones = [] for item in findall(element=elem, xpath='zone'): zone = self._to_zone(elem=item) zones.append(zone) return zones def _to_zone(self, elem): id = findtext(element=elem, xpath='id') domain = findtext(element=elem, xpath='domain') type = findtext(element=elem, xpath='ns-type') type = 'master' if type.find('pri') == 0 else 'slave' ttl = findtext(element=elem, xpath='default-ttl') hostmaster = findtext(element=elem, xpath='hostmaster') custom_ns = findtext(element=elem, xpath='custom-ns') custom_nameservers = findtext(element=elem, xpath='custom-nameservers') notes = findtext(element=elem, xpath='notes') nx_ttl = findtext(element=elem, xpath='nx-ttl') slave_nameservers = findtext(element=elem, xpath='slave-nameservers') tags = findtext(element=elem, xpath='tag-list') tags = tags.split(' ') if tags else [] extra = {'hostmaster': hostmaster, 'custom-ns': custom_ns, 'custom-nameservers': custom_nameservers, 'notes': notes, 'nx-ttl': nx_ttl, 'slave-nameservers': slave_nameservers, 'tags': tags} zone = Zone(id=str(id), domain=domain, type=type, ttl=int(ttl), driver=self, extra=extra) return zone def _to_records(self, elem, zone): records = [] for item in findall(element=elem, xpath='host'): record = self._to_record(elem=item, zone=zone) records.append(record) return records def _to_record(self, elem, zone): id = findtext(element=elem, xpath='id') name = findtext(element=elem, xpath='hostname') type = findtext(element=elem, xpath='host-type') type = self._string_to_record_type(type) data = findtext(element=elem, xpath='data') notes = findtext(element=elem, xpath='notes', no_text_value=None) state = findtext(element=elem, xpath='state', no_text_value=None) fqdn = findtext(element=elem, xpath='fqdn', no_text_value=None) priority = findtext(element=elem, xpath='priority', no_text_value=None) ttl = findtext(element=elem, xpath='ttl', no_text_value=None) if not name: name = None if ttl: ttl = int(ttl) extra = {'notes': notes, 'state': state, 'fqdn': fqdn, 'priority': priority, 'ttl': ttl} record = Record(id=id, name=name, type=type, data=data, zone=zone, driver=self, ttl=ttl, extra=extra) return record def _get_more(self, rtype, **kwargs): exhausted = False last_key = None while not exhausted: items, last_key, exhausted = self._get_data(rtype, last_key, **kwargs) for item in items: yield item def _get_data(self, rtype, last_key, **kwargs): # Note: last_key in this case really is a "last_page". # TODO: Update base driver and change last_key to something more # generic - e.g. marker params = {} params['per_page'] = ITEMS_PER_PAGE params['page'] = last_key + 1 if last_key else 1 if rtype == 'zones': path = API_ROOT + 'zones.xml' response = self.connection.request(path) transform_func = self._to_zones elif rtype == 'records': zone = kwargs['zone'] path = API_ROOT + 'zones/%s/hosts.xml' % (zone.id) self.connection.set_context({'resource': 'zone', 'id': zone.id}) response = self.connection.request(path, params=params) transform_func = self._to_records exhausted = False result_count = int(response.headers.get('x-query-count', 0)) if (params['page'] * ITEMS_PER_PAGE) >= result_count: exhausted = True if response.status == httplib.OK: items = transform_func(elem=response.object, **kwargs) return items, params['page'], exhausted else: return [], None, True