text: #
  #    Licensed 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.
  
  from heat.common import exception
  from heat.common.i18n import _
  from heat.engine import attributes
  from heat.engine import constraints
  from heat.engine import properties
  from heat.engine import resource
  from heat.engine import support
  
  
  class DesignateZone(resource.Resource):
      """Heat Template Resource for Designate Zone.
  
      Designate provides DNS-as-a-Service services for OpenStack. So, zone, part
      of domain is a realm with an identification string, unique in DNS.
      """
  
      support_status = support.SupportStatus(
          version='8.0.0')
  
      PROPERTIES = (
          NAME, TTL, DESCRIPTION, EMAIL, TYPE, MASTERS
      ) = (
          'name', 'ttl', 'description', 'email', 'type', 'masters'
      )
  
      ATTRIBUTES = (
          SERIAL,
      ) = (
          'serial',
      )
  
      TYPES = (
          PRIMARY, SECONDARY
      ) = (
          'PRIMARY', 'SECONDARY'
      )
  
      properties_schema = {
          # Based on RFC 1035, length of name is set to max of 255
          NAME: properties.Schema(
              properties.Schema.STRING,
              _('DNS Name for the zone.'),
              required=True,
              constraints=[constraints.Length(max=255)]
          ),
          # Based on RFC 1035, range for ttl is set to 1 to signed 32 bit number
          TTL: properties.Schema(
              properties.Schema.INTEGER,
              _('Time To Live (Seconds) for the zone.'),
              update_allowed=True,
              constraints=[constraints.Range(min=1,
                                             max=2147483647)]
          ),
          # designate mandates to the max length of 160 for description
          DESCRIPTION: properties.Schema(
              properties.Schema.STRING,
              _('Description of zone.'),
              update_allowed=True,
              constraints=[constraints.Length(max=160)]
          ),
          EMAIL: properties.Schema(
              properties.Schema.STRING,
              _('E-mail for the zone. Used in SOA records for the zone. '
                'It is required for PRIMARY Type, otherwise ignored.'),
              update_allowed=True,
          ),
          TYPE: properties.Schema(
              properties.Schema.STRING,
              _('Type of zone. PRIMARY is controlled by Designate, SECONDARY '
                'zones are slaved from another DNS Server.'),
              default=PRIMARY,
              constraints=[constraints.AllowedValues(
                  allowed=TYPES)]
          ),
          MASTERS: properties.Schema(
              properties.Schema.LIST,
              _('The servers to slave from to get DNS information and is '
                'mandatory for zone type SECONDARY, otherwise ignored.'),
              update_allowed=True
          )
      }
  
      attributes_schema = {
          SERIAL: attributes.Schema(
              _("DNS zone serial number."),
              type=attributes.Schema.STRING
          ),
      }
  
      default_client_name = 'designate'
  
      entity = 'zones'
  
      def validate(self):
          super(DesignateZone, self).validate()
  
          def raise_invalid_exception(zone_type, prp):
              if self.properties.get(self.TYPE) == zone_type:
                  if not self.properties.get(prp):
                      msg = _('Property %(prp)s is required for zone type '
                              '%(zone_type)s') % {
                          "prp": prp,
                          "zone_type": zone_type
                      }
                      raise exception.StackValidationFailed(message=msg)
  
          raise_invalid_exception(self.PRIMARY, self.EMAIL)
          raise_invalid_exception(self.SECONDARY, self.MASTERS)
  
      def handle_create(self):
          args = dict((k, v) for k, v in self.properties.items() if v)
          args['type_'] = args.pop(self.TYPE)
  
          zone = self.client().zones.create(**args)
  
          self.resource_id_set(zone['id'])
  
      def _check_status_complete(self):
          zone = self.client().zones.get(self.resource_id)
  
          if zone['status'] == 'ERROR':
              raise exception.ResourceInError(
                  resource_status=zone['status'],
                  status_reason=_('Error in zone'))
  
          return zone['status'] != 'PENDING'
  
      def check_create_complete(self, handler_data=None):
          return self._check_status_complete()
  
      def handle_update(self, json_snippet, tmpl_diff, prop_diff):
          args = dict()
  
          for prp in (self.EMAIL, self.TTL, self.DESCRIPTION, self.MASTERS):
              if prop_diff.get(prp):
                  args[prp] = prop_diff.get(prp)
  
          if len(args.keys()) > 0:
              self.client().zones.update(self.resource_id, args)
  
      def check_update_complete(self, handler_data=None):
          return self._check_status_complete()
  
      def _resolve_attribute(self, name):
          if self.resource_id is None:
              return
          if name == self.SERIAL:
              zone = self.client().zones.get(self.resource_id)
              return zone[name]
  
      def check_delete_complete(self, handler_data=None):
          if handler_data:
              with self.client_plugin().ignore_not_found:
                  return self._check_status_complete()
  
          return True
  
  
  def resource_mapping():
      return {
          'OS::Designate::Zone': DesignateZone
      }
  