Ensure the Route53 record is present.

    name
        Name of the record.

    value
        Value of the record.  As a special case, you can pass in:
            `private:<Name tag>` to have the function autodetermine the private IP
            `public:<Name tag>` to have the function autodetermine the public IP

    zone
        The zone to create the record in.

    record_type
        The record type (A, NS, MX, TXT, etc.)

    ttl
        The time to live for the record.

    identifier
        The unique identifier to use for this record.

    region
        The region to connect to.

    key
        Secret key to be used.

    keyid
        Access key to be used.

    profile
        A dict with region, key and keyid, or a pillar key (string) that contains a dict
        with region, key and keyid.

    wait_for_sync
        Wait for an INSYNC change status from Route53 before returning success.

    split_dns
        Route53 supports parallel public and private DNS zones with the same name.

    private_zone
        If using split_dns, specify if this is the private zone.

def present(name, value, zone, record_type, ttl=None, identifier=None, region=None, key=None,
            keyid=None, profile=None, wait_for_sync=True, split_dns=False, private_zone=False):
    '''
    Ensure the Route53 record is present.

    name
        Name of the record.

    value
        Value of the record.  As a special case, you can pass in:
            `private:<Name tag>` to have the function autodetermine the private IP
            `public:<Name tag>` to have the function autodetermine the public IP

    zone
        The zone to create the record in.

    record_type
        The record type (A, NS, MX, TXT, etc.)

    ttl
        The time to live for the record.

    identifier
        The unique identifier to use for this record.

    region
        The region to connect to.

    key
        Secret key to be used.

    keyid
        Access key to be used.

    profile
        A dict with region, key and keyid, or a pillar key (string) that contains a dict
        with region, key and keyid.

    wait_for_sync
        Wait for an INSYNC change status from Route53 before returning success.

    split_dns
        Route53 supports parallel public and private DNS zones with the same name.

    private_zone
        If using split_dns, specify if this is the private zone.
    '''
    ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}

    # If a list is passed in for value, change it to a comma-separated string
    # So it will work with subsequent boto module calls and string functions
    if isinstance(value, list):
        value = ','.join(value)
    elif value.startswith('private:') or value.startswith('public:'):
        name_tag = value.split(':', 1)[1]
        in_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped')
        r = __salt__['boto_ec2.find_instances'](name=name_tag,
                                                return_objs=True,
                                                in_states=in_states,
                                                profile=profile)
        if not r:
            ret['comment'] = 'Error: instance with Name tag {0} not found'.format(name_tag)
            ret['result'] = False
            return ret
        if len(r) > 1:
            ret['comment'] = 'Error: Name tag {0} matched more than one instance'.format(name_tag)
            ret['result'] = False
            return ret
        instance = r[0]
        private_ip = getattr(instance, 'private_ip_address', None)
        public_ip = getattr(instance, 'ip_address', None)
        if value.startswith('private:'):
            value = private_ip
            log.info('Found private IP %s for instance %s', private_ip, name_tag)
        else:
            if public_ip is None:
                ret['comment'] = 'Error: No Public IP assigned to instance with Name {0}'.format(name_tag)
                ret['result'] = False
                return ret
            value = public_ip
            log.info('Found public IP %s for instance %s', public_ip, name_tag)

    try:
        record = __salt__['boto_route53.get_record'](name, zone, record_type,
                                                     False, region, key, keyid,
                                                     profile, split_dns,
                                                     private_zone, identifier)
    except SaltInvocationError as err:
        ret['comment'] = 'Error: {0}'.format(err)
        ret['result'] = False
        return ret

    if isinstance(record, dict) and not record:
        if __opts__['test']:
            ret['comment'] = 'Route53 record {0} set to be added.'.format(name)
            ret['result'] = None
            return ret
        added = __salt__['boto_route53.add_record'](name, value, zone,
                                                    record_type, identifier,
                                                    ttl, region, key, keyid,
                                                    profile, wait_for_sync,
                                                    split_dns, private_zone)
        if added:
            ret['changes']['old'] = None
            ret['changes']['new'] = {'name': name,
                                     'value': value,
                                     'record_type': record_type,
                                     'ttl': ttl,
                                     'identifier': identifier}
            ret['comment'] = 'Added {0} Route53 record.'.format(name)
        else:
            ret['result'] = False
            ret['comment'] = 'Failed to add {0} Route53 record.'.format(name)
            return ret
    elif record:
        need_to_update = False
        # Values can be a comma separated list and some values will end with a
        # period (even if we set it without one). To easily check this we need
        # to split and check with the period stripped from the input and what's
        # in route53.
        # TODO: figure out if this will cause us problems with some records.
        _values = [x.rstrip('.') for x in value.split(',')]
        _r_values = [x.rstrip('.') for x in record['value'].split(',')]
        _values.sort()
        _r_values.sort()
        if _values != _r_values:
            need_to_update = True
        if identifier and identifier != record['identifier']:
            need_to_update = True
        if ttl and six.text_type(ttl) != six.text_type(record['ttl']):
            need_to_update = True
        if need_to_update:
            if __opts__['test']:
                ret['comment'] = 'Route53 record {0} set to be updated.'.format(name)
                ret['result'] = None
                return ret
            updated = __salt__['boto_route53.update_record'](name, value, zone,
                                                             record_type,
                                                             identifier, ttl,
                                                             region, key,
                                                             keyid, profile,
                                                             wait_for_sync,
                                                             split_dns,
                                                             private_zone)
            if updated:
                ret['changes']['old'] = record
                ret['changes']['new'] = {'name': name,
                                         'value': value,
                                         'record_type': record_type,
                                         'ttl': ttl,
                                         'identifier': identifier}
                ret['comment'] = 'Updated {0} Route53 record.'.format(name)
            else:
                ret['result'] = False
                ret['comment'] = 'Failed to update {0} Route53 record.'.format(name)
        else:
            ret['comment'] = '{0} exists.'.format(name)
    return ret