Ensure a hosted zone exists with the given attributes. Note that most
    things cannot be modified once a zone is created - it must be deleted and
    re-spun to update these attributes:

    - private_zone (AWS API limitation).
    - comment (the appropriate call exists in the AWS API and in boto3, but has
      not, as of this writing, been added to boto2).
    - vpc_id (same story - we really need to rewrite this module with boto3)
    - vpc_name (really just a pointer to vpc_id anyway).
    - vpc_region (again, supported in boto3 but not boto2).

    If you need the ability to update these attributes, please use the newer
    boto3_route53 module instead.

    name
        The name of the state definition.

    domain_name
        The name of the domain. This must be fully-qualified, terminating with a period.  This is
        the name you have registered with your domain registrar.  It is also the name you will
        delegate from your registrar to the Amazon Route 53 delegation servers returned in response
        to this request.  Defaults to the value of name if not provided.

    private_zone
        Set True if creating a private hosted zone.

    caller_ref
        A unique string that identifies the request and that allows create_hosted_zone() calls to be
        retried without the risk of executing the operation twice.  This helps ensure idempotency
        across state calls, but can cause issues if a zone is deleted and then an attempt is made
        to recreate it with the same caller_ref.  If not provided, a unique UUID will be generated
        at each state run, which avoids the risk of the above (transient) error.  This option is
        generally not needed.  Maximum length of 128.

    comment
        Any comments you want to include about the hosted zone.

    vpc_id
        When creating a private hosted zone, either the VPC ID or VPC Name to associate with is
        required.  Exclusive with vpe_name.  Ignored when creating a non-private zone.

    vpc_name
        When creating a private hosted zone, either the VPC ID or VPC Name to associate with is
        required.  Exclusive with vpe_id.  Ignored when creating a non-private zone.

    vpc_region
        When creating a private hosted zone, the region of the associated VPC is required.  If not
        provided, an effort will be made to determine it from vpc_id or vpc_name, where possible.
        If this fails, you'll need to provide an explicit value for this option.  Ignored when
        creating a non-private zone.

def hosted_zone_present(name, domain_name=None, private_zone=False, caller_ref=None, comment='',
                        vpc_id=None, vpc_name=None, vpc_region=None, region=None, key=None,
                        keyid=None, profile=None):
    '''
    Ensure a hosted zone exists with the given attributes. Note that most
    things cannot be modified once a zone is created - it must be deleted and
    re-spun to update these attributes:

    - private_zone (AWS API limitation).
    - comment (the appropriate call exists in the AWS API and in boto3, but has
      not, as of this writing, been added to boto2).
    - vpc_id (same story - we really need to rewrite this module with boto3)
    - vpc_name (really just a pointer to vpc_id anyway).
    - vpc_region (again, supported in boto3 but not boto2).

    If you need the ability to update these attributes, please use the newer
    boto3_route53 module instead.

    name
        The name of the state definition.

    domain_name
        The name of the domain. This must be fully-qualified, terminating with a period.  This is
        the name you have registered with your domain registrar.  It is also the name you will
        delegate from your registrar to the Amazon Route 53 delegation servers returned in response
        to this request.  Defaults to the value of name if not provided.

    private_zone
        Set True if creating a private hosted zone.

    caller_ref
        A unique string that identifies the request and that allows create_hosted_zone() calls to be
        retried without the risk of executing the operation twice.  This helps ensure idempotency
        across state calls, but can cause issues if a zone is deleted and then an attempt is made
        to recreate it with the same caller_ref.  If not provided, a unique UUID will be generated
        at each state run, which avoids the risk of the above (transient) error.  This option is
        generally not needed.  Maximum length of 128.

    comment
        Any comments you want to include about the hosted zone.

    vpc_id
        When creating a private hosted zone, either the VPC ID or VPC Name to associate with is
        required.  Exclusive with vpe_name.  Ignored when creating a non-private zone.

    vpc_name
        When creating a private hosted zone, either the VPC ID or VPC Name to associate with is
        required.  Exclusive with vpe_id.  Ignored when creating a non-private zone.

    vpc_region
        When creating a private hosted zone, the region of the associated VPC is required.  If not
        provided, an effort will be made to determine it from vpc_id or vpc_name, where possible.
        If this fails, you'll need to provide an explicit value for this option.  Ignored when
        creating a non-private zone.
    '''
    domain_name = domain_name if domain_name else name

    ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}

    # First translaste vpc_name into a vpc_id if possible
    if private_zone:
        if not salt.utils.data.exactly_one((vpc_name, vpc_id)):
            raise SaltInvocationError('Either vpc_name or vpc_id is required when creating a '
                                      'private zone.')
        vpcs = __salt__['boto_vpc.describe_vpcs'](
                vpc_id=vpc_id, name=vpc_name, region=region, key=key,
                keyid=keyid, profile=profile).get('vpcs', [])
        if vpc_region and vpcs:
            vpcs = [v for v in vpcs if v['region'] == vpc_region]
        if not vpcs:
            msg = 'Private zone requested but a VPC matching given criteria not found.'
            log.error(msg)
            ret['comment'] = msg
            ret['result'] = False
            return ret
        if len(vpcs) > 1:
            log.error(
                'Private zone requested but multiple VPCs matching given '
                'criteria found: %s', [v['id'] for v in vpcs]
            )
            return None
        vpc = vpcs[0]
        if vpc_name:
            vpc_id = vpc['id']
        if not vpc_region:
            vpc_region = vpc['region']

    # Next, see if it (or they) exist at all, anywhere?
    deets = __salt__['boto_route53.describe_hosted_zones'](
          domain_name=domain_name, region=region, key=key, keyid=keyid,
          profile=profile)

    create = False
    if not deets:
        create = True
    else:  # Something exists - now does it match our criteria?
        if (salt.utils.json.loads(deets['HostedZone']['Config']['PrivateZone']) !=
                private_zone):
            create = True
        else:
            if private_zone:
                for d in deets.get('VPCs', {}):
                    if (d['VPCId'] == vpc_id
                            and d['VPCRegion'] == vpc_region):
                        create = False
                        break
                    else:
                        create = True
        if not create:
            ret['comment'] = 'Hostd Zone {0} already in desired state'.format(
                    domain_name)
        else:
            # Until we get modifies in place with boto3, the best option is to
            # attempt creation and let route53 tell us if we're stepping on
            # toes.  We can't just fail, because some scenarios (think split
            # horizon DNS) require zones with identical names but different
            # settings...
            log.info('A Hosted Zone with name %s already exists, but with '
                     'different settings.  Will attempt to create the one '
                     'requested on the assumption this is what is desired.  '
                     'This may fail...', domain_name)

    if create:
        if caller_ref is None:
            caller_ref = six.text_type(uuid.uuid4())
        if __opts__['test']:
            ret['comment'] = 'Route53 Hosted Zone {0} set to be added.'.format(
                    domain_name)
            ret['result'] = None
            return ret
        res = __salt__['boto_route53.create_hosted_zone'](domain_name=domain_name,
                caller_ref=caller_ref, comment=comment, private_zone=private_zone,
                vpc_id=vpc_id, vpc_region=vpc_region, region=region, key=key,
                keyid=keyid, profile=profile)
        if res:
            msg = 'Hosted Zone {0} successfully created'.format(domain_name)
            log.info(msg)
            ret['comment'] = msg
            ret['changes']['old'] = None
            ret['changes']['new'] = res
        else:
            ret['comment'] = 'Creating Hosted Zone {0} failed'.format(
                    domain_name)
            ret['result'] = False

    return ret