text: # 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__ = [
      'HostVirtualDNSDriver'
  ]
  
  try:
      import simplejson as json
  except Exception:
      import json
  
  from libcloud.utils.py3 import httplib
  from libcloud.utils.misc import merge_valid_keys, get_new_obj
  from libcloud.common.hostvirtual import HostVirtualResponse
  from libcloud.common.hostvirtual import HostVirtualConnection
  from libcloud.compute.drivers.hostvirtual import API_ROOT
  from libcloud.dns.types import Provider, RecordType
  from libcloud.dns.types import ZoneDoesNotExistError, RecordDoesNotExistError
  from libcloud.dns.base import DNSDriver, Zone, Record
  
  VALID_RECORD_EXTRA_PARAMS = ['prio', 'ttl']
  
  
  class HostVirtualDNSResponse(HostVirtualResponse):
      def parse_error(self):
          context = self.connection.context
          status = int(self.status)
  
          if status == httplib.NOT_FOUND:
              if context['resource'] == 'zone':
                  raise ZoneDoesNotExistError(
                      value=self.parse_body()['error']['message'],
                      driver=self, zone_id=context['id'])
              elif context['resource'] == 'record':
                  raise RecordDoesNotExistError(
                      value=self.parse_body()['error']['message'],
                      driver=self, record_id=context['id'])
  
          super(HostVirtualDNSResponse, self).parse_error()
          return self.body
  
  
  class HostVirtualDNSConnection(HostVirtualConnection):
      responseCls = HostVirtualDNSResponse
  
  
  class HostVirtualDNSDriver(DNSDriver):
      type = Provider.HOSTVIRTUAL
      name = 'Host Virtual DNS'
      website = 'https://www.hostvirtual.com/'
      connectionCls = HostVirtualDNSConnection
  
      RECORD_TYPE_MAP = {
          RecordType.A: 'A',
          RecordType.AAAA: 'AAAA',
          RecordType.CNAME: 'CNAME',
          RecordType.MX: 'MX',
          RecordType.NS: 'SPF',
          RecordType.SRV: 'SRV',
          RecordType.TXT: 'TXT',
      }
  
      def __init__(self, key, secure=True, host=None, port=None):
          super(HostVirtualDNSDriver, self).__init__(key=key, secure=secure,
                                                     host=host, port=port)
  
      def _to_zones(self, items):
          zones = []
          for item in items:
              zones.append(self._to_zone(item))
          return zones
  
      def _to_zone(self, item):
          extra = {}
          if 'records' in item:
              extra['records'] = item['records']
          if item['type'] == 'NATIVE':
              item['type'] = 'master'
          zone = Zone(id=item['id'], domain=item['name'],
                      type=item['type'], ttl=item['ttl'],
                      driver=self, extra=extra)
          return zone
  
      def _to_records(self, items, zone=None):
          records = []
  
          for item in items:
              records.append(self._to_record(item=item, zone=zone))
          return records
  
      def _to_record(self, item, zone=None):
          extra = {'ttl': item['ttl']}
          type = self._string_to_record_type(item['type'])
          name = item['name'][:-len(zone.domain) - 1]
          record = Record(id=item['id'], name=name,
                          type=type, data=item['content'],
                          zone=zone, driver=self, ttl=item['ttl'],
                          extra=extra)
          return record
  
      def list_zones(self):
          result = self.connection.request(
              API_ROOT + '/dns/zones/').object
          zones = self._to_zones(result)
          return zones
  
      def list_records(self, zone):
          params = {'id': zone.id}
          self.connection.set_context({'resource': 'zone', 'id': zone.id})
          try:
              result = self.connection.request(
                  API_ROOT + '/dns/records/', params=params).object
          except ZoneDoesNotExistError as e:
              if 'Not Found: No Records Found' in e.value:
                  return []
              raise e
          records = self._to_records(items=result, zone=zone)
          return records
  
      def get_zone(self, zone_id):
          params = {'id': zone_id}
          self.connection.set_context({'resource': 'zone', 'id': zone_id})
          result = self.connection.request(
              API_ROOT + '/dns/zone/', params=params).object
          if 'id' not in result:
              raise ZoneDoesNotExistError(value='', driver=self, zone_id=zone_id)
          zone = self._to_zone(result)
          return zone
  
      def get_record(self, zone_id, record_id):
          zone = self.get_zone(zone_id=zone_id)
          params = {'id': record_id}
          self.connection.set_context({'resource': 'record', 'id': record_id})
          result = self.connection.request(
              API_ROOT + '/dns/record/', params=params).object
          if 'id' not in result:
              raise RecordDoesNotExistError(value='',
                                            driver=self, record_id=record_id)
          record = self._to_record(item=result, zone=zone)
          return record
  
      def delete_zone(self, zone):
          params = {'id': zone.id}
          self.connection.set_context({'resource': 'zone', 'id': zone.id})
          result = self.connection.request(
              API_ROOT + '/dns/zone/', params=params, method='DELETE').object
          return bool(result)
  
      def delete_record(self, record):
          params = {'id': record.id}
          self.connection.set_context({'resource': 'record', 'id': record.id})
          result = self.connection.request(
              API_ROOT + '/dns/record/', params=params, method='DELETE').object
  
          return bool(result)
  
      def create_zone(self, domain, type='NATIVE', ttl=None, extra=None):
          if type == 'master':
              type = 'NATIVE'
          elif type == 'slave':
              type = 'SLAVE'
          params = {'name': domain, 'type': type, 'ttl': ttl}
          result = self.connection.request(
              API_ROOT + '/dns/zone/',
              data=json.dumps(params), method='POST').object
          extra = {
              'soa': result['soa'],
              'ns': result['ns']
          }
          zone = Zone(id=result['id'], domain=domain,
                      type=type, ttl=ttl, extra=extra, driver=self)
          return zone
  
      def update_zone(self, zone, domain=None, type=None, ttl=None, extra=None):
          params = {'id': zone.id}
          if domain:
              params['name'] = domain
          if type:
              params['type'] = type
          self.connection.set_context({'resource': 'zone', 'id': zone.id})
          self.connection.request(API_ROOT + '/dns/zone/',
                                  data=json.dumps(params), method='PUT').object
          updated_zone = get_new_obj(
              obj=zone, klass=Zone,
              attributes={
                  'domain': domain,
                  'type': type,
                  'ttl': ttl,
                  'extra': extra
              })
          return updated_zone
  
      def create_record(self, name, zone, type, data, extra=None):
          params = {
              'name': name,
              'type': self.RECORD_TYPE_MAP[type],
              'domain_id': zone.id,
              'content': data
          }
          merged = merge_valid_keys(
              params=params,
              valid_keys=VALID_RECORD_EXTRA_PARAMS,
              extra=extra
          )
          self.connection.set_context({'resource': 'zone', 'id': zone.id})
          result = self.connection.request(
              API_ROOT + '/dns/record/',
              data=json.dumps(params), method='POST').object
          record = Record(id=result['id'], name=name,
                          type=type, data=data,
                          extra=merged, zone=zone,
                          ttl=merged.get('ttl', None),
                          driver=self)
          return record
  
      def update_record(self, record, name=None, type=None,
                        data=None, extra=None):
          params = {
              'domain_id': record.zone.id,
              'record_id': record.id
          }
          if name:
              params['name'] = name
          if data:
              params['content'] = data
          if type is not None:
              params['type'] = self.RECORD_TYPE_MAP[type]
              merged = merge_valid_keys(
                  params=params,
                  valid_keys=VALID_RECORD_EXTRA_PARAMS,
                  extra=extra
              )
          self.connection.set_context({'resource': 'record', 'id': record.id})
          self.connection.request(API_ROOT + '/dns/record/',
                                  data=json.dumps(params), method='PUT').object
          updated_record = get_new_obj(
              obj=record, klass=Record, attributes={
                  'name': name, 'data': data,
                  'type': type,
                  'extra': merged
              })
          return updated_record
  