def delete_zone_domain(args=None, payload=None):
    '''
    Deletion is pretty simple, domains are always unique so we
    we don't need to do any sanity checking to avoid deleting the
    wrong thing.
    '''
    has_changed, has_failed = False, False
    msg, memset_api = None, None

    api_method = 'dns.zone_domain_list'
    _has_failed, _msg, response = memset_api_call(api_key=args['api_key'],
                                                  api_method=api_method)

    domain_exists = check_zone_domain(data=response, domain=args['domain'])

    if domain_exists:
        api_method = 'dns.zone_domain_delete'
        payload['domain'] = args['domain']
        has_failed, msg, response = memset_api_call(api_key=args['api_key'],
                                                    api_method=api_method,
                                                    payload=payload)
        if not has_failed:
            has_changed = True
            memset_api = response.json()
            # unset msg as we don't want to return unnecessary info to the user.
            msg = None

    return (has_failed, has_changed, memset_api, msg)
def poll_reload_status(api_key=None, job_id=None, payload=None):
    '''
    We poll the `job.status` endpoint every 5 seconds up to a
    maximum of 6 times. This is a relatively arbitrary choice of
    timeout, however requests rarely take longer than 15 seconds
    to complete.
    '''
    memset_api, stderr, msg = None, None, None
    payload['id'] = job_id

    api_method = 'job.status'
    _has_failed, _msg, response = memset_api_call(api_key=api_key, api_method=api_method, payload=payload)

    while not response.json()['finished']:
        counter = 0
        while counter < 6:
            sleep(5)
            _has_failed, msg, response = memset_api_call(api_key=api_key, api_method=api_method, payload=payload)
            counter += 1
    if response.json()['error']:
        # the reload job was submitted but polling failed. Don't return this as an overall task failure.
        stderr = "Reload submitted successfully, but the Memset API returned a job error when attempting to poll the reload status."
    else:
        memset_api = response.json()
        msg = None

    return(memset_api, msg, stderr)
def create_zone_domain(args=None,
                       zone_exists=None,
                       zone_id=None,
                       payload=None):
    '''
    At this point we already know whether the containing zone exists,
    so we just need to create the domain (or exit if it already exists).
    '''
    has_changed, has_failed = False, False
    msg = None

    api_method = 'dns.zone_domain_list'
    _has_failed, _msg, response = memset_api_call(api_key=args['api_key'],
                                                  api_method=api_method)

    for zone_domain in response.json():
        if zone_domain['domain'] == args['domain']:
            # zone domain already exists, nothing to change.
            has_changed = False
            break
    else:
        # we need to create the domain
        api_method = 'dns.zone_domain_create'
        payload['domain'] = args['domain']
        payload['zone_id'] = zone_id
        has_failed, msg, response = memset_api_call(api_key=args['api_key'],
                                                    api_method=api_method,
                                                    payload=payload)
        if not has_failed:
            has_changed = True

    return (has_failed, has_changed, msg)
def delete_zone(args=None, zone_exists=None, payload=None):
    '''
    Deletion requires extra sanity checking as the zone cannot be
    deleted if it contains domains or records. Setting force=true
    will override this behaviour.
    '''
    has_changed, has_failed = False, False
    msg, memset_api = None, None

    if zone_exists:
        api_method = 'dns.zone_list'
        _has_failed, _msg, response = memset_api_call(api_key=args['api_key'],
                                                      api_method=api_method,
                                                      payload=payload)
        counter = 0
        for zone in response.json():
            if zone['nickname'] == args['name']:
                counter += 1
        if counter == 1:
            for zone in response.json():
                if zone['nickname'] == args['name']:
                    zone_id = zone['id']
                    domain_count = len(zone['domains'])
                    record_count = len(zone['records'])
            if (domain_count > 0
                    or record_count > 0) and args['force'] is False:
                # we need to fail out if force was not explicitly set.
                stderr = 'Zone contains domains or records and force was not used.'
                has_failed = True
                has_changed = False
                module.fail_json(failed=has_failed,
                                 changed=has_changed,
                                 msg=msg,
                                 stderr=stderr,
                                 rc=1)
            api_method = 'dns.zone_delete'
            payload['id'] = zone_id
            has_failed, msg, response = memset_api_call(
                api_key=args['api_key'],
                api_method=api_method,
                payload=payload)
            if not has_failed:
                has_changed = True
                # return raw JSON from API in named var and then unset msg var so we aren't returning the same thing twice.
                memset_api = msg
                msg = None
        else:
            # zone names are not unique, so we cannot safely delete the requested
            # zone at this time.
            has_failed = True
            has_changed = False
            msg = 'Unable to delete zone as multiple zones with the same name exist.'
    else:
        has_failed, has_changed = False, False

    return (has_failed, has_changed, memset_api, msg)
def create_zone(args=None, zone_exists=None, payload=None):
    '''
    At this point we already know whether the zone exists, so we
    just need to make the API reflect the desired state.
    '''
    has_changed, has_failed = False, False
    msg, memset_api = None, None

    if not zone_exists:
        payload['ttl'] = args['ttl']
        payload['nickname'] = args['name']
        api_method = 'dns.zone_create'
        has_failed, msg, response = memset_api_call(api_key=args['api_key'],
                                                    api_method=api_method,
                                                    payload=payload)
        if not has_failed:
            has_changed = True
    else:
        api_method = 'dns.zone_list'
        _has_failed, _msg, response = memset_api_call(api_key=args['api_key'],
                                                      api_method=api_method)
        for zone in response.json():
            if zone['nickname'] == args['name']:
                break
        if zone['ttl'] != args['ttl']:
            # update the zone if the desired TTL is different.
            payload['id'] = zone['id']
            payload['ttl'] = args['ttl']
            api_method = 'dns.zone_update'
            has_failed, msg, response = memset_api_call(
                api_key=args['api_key'],
                api_method=api_method,
                payload=payload)
            if not has_failed:
                has_changed = True

    # populate return var with zone info.
    api_method = 'dns.zone_list'
    _has_failed, _msg, response = memset_api_call(api_key=args['api_key'],
                                                  api_method=api_method)

    zone_exists, msg, counter, zone_id = get_zone_id(
        zone_name=args['name'], current_zones=response.json())

    if zone_exists:
        payload = dict()
        payload['id'] = zone_id
        api_method = 'dns.zone_info'
        _has_failed, _msg, response = memset_api_call(api_key=args['api_key'],
                                                      api_method=api_method,
                                                      payload=payload)
        memset_api = response.json()

    return (has_failed, has_changed, memset_api, msg)
def create_or_delete_domain(args=None):
    '''
    We need to perform some initial sanity checking and also look
    up required info before handing it off to create or delete.
    '''
    retvals, payload = dict(), dict()
    has_changed, has_failed = False, False
    msg, stderr, memset_api = None, None, None

    # get the zones and check if the relevant zone exists.
    api_method = 'dns.zone_list'
    has_failed, msg, response = memset_api_call(api_key=args['api_key'],
                                                api_method=api_method)

    if has_failed:
        # this is the first time the API is called; incorrect credentials will
        # manifest themselves at this point so we need to ensure the user is
        # informed of the reason.
        retvals['failed'] = has_failed
        retvals['msg'] = msg
        retvals['stderr'] = "API returned an error: {0}".format(
            response.status_code)
        return (retvals)

    zone_exists, msg, counter, zone_id = get_zone_id(
        zone_name=args['zone'], current_zones=response.json())

    if not zone_exists:
        # the zone needs to be unique - this isn't a requirement of Memset's API but it
        # makes sense in the context of this module.
        has_failed = True
        if counter == 0:
            stderr = "DNS zone '{0}' does not exist, cannot create domain.".format(
                args['zone'])
        elif counter > 1:
            stderr = "{0} matches multiple zones, cannot create domain.".format(
                args['zone'])

        retvals['failed'] = has_failed
        retvals['msg'] = stderr
        return (retvals)

    if args['state'] == 'present':
        has_failed, has_changed, msg = create_zone_domain(
            args=args,
            zone_exists=zone_exists,
            zone_id=zone_id,
            payload=payload)

    if args['state'] == 'absent':
        has_failed, has_changed, memset_api, msg = delete_zone_domain(
            args=args, payload=payload)

    retvals['changed'] = has_changed
    retvals['failed'] = has_failed
    for val in ['msg', 'stderr', 'memset_api']:
        if val is not None:
            retvals[val] = eval(val)

    return (retvals)
def delete_zone_record(args=None, records=None, payload=None):
    '''
    Matching records can be cleanly deleted without affecting other
    resource types, so this is pretty simple to achieve.
    '''
    has_changed, has_failed = False, False
    msg, memset_api = None, None

    # if we have any matches, delete them.
    if records:
        for zone_record in records:
            if args['check_mode']:
                has_changed = True
                return (has_changed, has_failed, memset_api, msg)
            payload['id'] = zone_record['id']
            api_method = 'dns.zone_record_delete'
            has_failed, msg, response = memset_api_call(
                api_key=args['api_key'],
                api_method=api_method,
                payload=payload)
            if not has_failed:
                has_changed = True
                memset_api = zone_record
                #  empty msg as we don't want to return a boatload of json to the user.
                msg = None

    return (has_changed, has_failed, memset_api, msg)
Exemple #8
0
def get_facts(args=None):
    '''
    Performs a simple API call and returns a JSON blob.
    '''
    retvals, payload = dict(), dict()
    has_changed, has_failed = False, False
    msg, stderr, memset_api = None, None, None

    payload['name'] = args['name']

    api_method = 'server.info'
    has_failed, msg, response = memset_api_call(api_key=args['api_key'], api_method=api_method, payload=payload)

    if has_failed:
        # this is the first time the API is called; incorrect credentials will
        # manifest themselves at this point so we need to ensure the user is
        # informed of the reason.
        retvals['failed'] = has_failed
        retvals['msg'] = msg
        retvals['stderr'] = "API returned an error: {0}" . format(response.status_code)
        return(retvals)

    # we don't want to return the same thing twice
    msg = None
    memset_api = response.json()

    retvals['changed'] = has_changed
    retvals['failed'] = has_failed
    for val in ['msg', 'memset_api']:
        if val is not None:
            retvals[val] = eval(val)

    return(retvals)
def main():
    global module
    module = AnsibleModule(argument_spec=dict(state=dict(
        default='present', choices=['present', 'absent'], type='str'),
                                              api_key=dict(required=True,
                                                           type='str',
                                                           no_log=True),
                                              domain=dict(required=True,
                                                          aliases=['name'],
                                                          type='str'),
                                              zone=dict(required=True,
                                                        type='str')),
                           supports_check_mode=True)

    # populate the dict with the user-provided vars.
    args = dict()
    for key, arg in module.params.items():
        args[key] = arg
    args['check_mode'] = module.check_mode

    # validate some API-specific limitations.
    api_validation(args=args)

    if module.check_mode:
        retvals = check(args)
    else:
        retvals = create_or_delete_domain(args)

    # we would need to populate the return values with the API's response
    # in several places so it's easier to do it at the end instead.
    if not retvals['failed']:
        if args['state'] == 'present' and not module.check_mode:
            payload = dict()
            payload['domain'] = args['domain']
            api_method = 'dns.zone_domain_info'
            _has_failed, _msg, response = memset_api_call(
                api_key=args['api_key'],
                api_method=api_method,
                payload=payload)
            retvals['memset_api'] = response.json()

    if retvals['failed']:
        module.fail_json(**retvals)
    else:
        module.exit_json(**retvals)
Exemple #10
0
def check(args=None):
    '''
    Support for running with check mode.
    '''
    retvals = dict()

    api_method = 'dns.zone_list'
    has_failed, _msg, response = memset_api_call(api_key=args['api_key'],
                                                 api_method=api_method)

    zone_exists, counter = check_zone(data=response, name=args['name'])

    # set changed to true if the operation would cause a change.
    has_changed = ((zone_exists and args['state'] == 'absent')
                   or (not zone_exists and args['state'] == 'present'))

    retvals['changed'] = has_changed
    retvals['failed'] = has_failed

    return (retvals)
def check(args=None):
    '''
    Support for running with check mode.
    '''
    retvals = dict()
    has_changed = False

    api_method = 'dns.zone_domain_list'
    has_failed, msg, response = memset_api_call(api_key=args['api_key'],
                                                api_method=api_method)

    domain_exists = check_zone_domain(data=response, domain=args['domain'])

    # set changed to true if the operation would cause a change.
    has_changed = ((domain_exists and args['state'] == 'absent')
                   or (not domain_exists and args['state'] == 'present'))

    retvals['changed'] = has_changed
    retvals['failed'] = has_failed

    return (retvals)
Exemple #12
0
def create_or_delete(args=None):
    '''
    We need to perform some initial sanity checking and also look
    up required info before handing it off to create or delete.
    '''
    retvals, payload = dict(), dict()
    has_failed, has_changed = False, False
    msg, memset_api, stderr = None, None, None

    # get the zones and check if the relevant zone exists.
    api_method = 'dns.zone_list'
    _has_failed, _msg, response = memset_api_call(api_key=args['api_key'],
                                                  api_method=api_method)
    if _has_failed:
        # this is the first time the API is called; incorrect credentials will
        # manifest themselves at this point so we need to ensure the user is
        # informed of the reason.
        retvals['failed'] = _has_failed
        retvals['msg'] = _msg

        return (retvals)

    zone_exists, _msg, counter, _zone_id = get_zone_id(
        zone_name=args['name'], current_zones=response.json())

    if args['state'] == 'present':
        has_failed, has_changed, memset_api, msg = create_zone(
            args=args, zone_exists=zone_exists, payload=payload)

    elif args['state'] == 'absent':
        has_failed, has_changed, memset_api, msg = delete_zone(
            args=args, zone_exists=zone_exists, payload=payload)

    retvals['failed'] = has_failed
    retvals['changed'] = has_changed
    for val in ['msg', 'stderr', 'memset_api']:
        if val is not None:
            retvals[val] = eval(val)

    return (retvals)
def reload_dns(args=None):
    '''
    DNS reloads are a single API call and therefore there's not much
    which can go wrong outside of auth errors.
    '''
    retvals, payload = dict(), dict()
    has_changed, has_failed = False, False
    memset_api, msg, stderr = None, None, None

    api_method = 'dns.reload'
    has_failed, msg, response = memset_api_call(api_key=args['api_key'], api_method=api_method)

    if has_failed:
        # this is the first time the API is called; incorrect credentials will
        # manifest themselves at this point so we need to ensure the user is
        # informed of the reason.
        retvals['failed'] = has_failed
        retvals['memset_api'] = response.json()
        retvals['msg'] = msg
        return(retvals)

    # set changed to true if the reload request was accepted.
    has_changed = True
    memset_api = msg
    # empty msg var as we don't want to return the API's json response twice.
    msg = None

    if args['poll']:
        # hand off to the poll function.
        job_id = response.json()['id']
        memset_api, msg, stderr = poll_reload_status(api_key=args['api_key'], job_id=job_id, payload=payload)

    # assemble return variables.
    retvals['failed'] = has_failed
    retvals['changed'] = has_changed
    for val in ['msg', 'stderr', 'memset_api']:
        if val is not None:
            retvals[val] = eval(val)

    return(retvals)
def create_or_delete(args=None):
    '''
    We need to perform some initial sanity checking and also look
    up required info before handing it off to create or delete functions.
    Check mode is integrated into the create or delete functions.
    '''
    has_failed, has_changed = False, False
    msg, memset_api, stderr = None, None, None
    retvals, payload = dict(), dict()

    # get the zones and check if the relevant zone exists.
    api_method = 'dns.zone_list'
    _has_failed, msg, response = memset_api_call(api_key=args['api_key'],
                                                 api_method=api_method)

    if _has_failed:
        # this is the first time the API is called; incorrect credentials will
        # manifest themselves at this point so we need to ensure the user is
        # informed of the reason.
        retvals['failed'] = _has_failed
        retvals['msg'] = msg
        retvals['stderr'] = "API returned an error: {0}".format(
            response.status_code)
        return (retvals)

    zone_exists, _msg, counter, zone_id = get_zone_id(
        zone_name=args['zone'], current_zones=response.json())

    if not zone_exists:
        has_failed = True
        if counter == 0:
            stderr = "DNS zone {0} does not exist.".format(args['zone'])
        elif counter > 1:
            stderr = "{0} matches multiple zones.".format(args['zone'])
        retvals['failed'] = has_failed
        retvals['msg'] = stderr
        retvals['stderr'] = stderr
        return (retvals)

    # get a list of all records ( as we can't limit records by zone)
    api_method = 'dns.zone_record_list'
    _has_failed, _msg, response = memset_api_call(api_key=args['api_key'],
                                                  api_method=api_method)

    # find any matching records
    records = [
        record for record in response.json()
        if record['zone_id'] == zone_id and record['record'] == args['record']
        and record['type'] == args['type']
    ]

    if args['state'] == 'present':
        has_changed, has_failed, memset_api, msg = create_zone_record(
            args=args, zone_id=zone_id, records=records, payload=payload)

    if args['state'] == 'absent':
        has_changed, has_failed, memset_api, msg = delete_zone_record(
            args=args, records=records, payload=payload)

    retvals['changed'] = has_changed
    retvals['failed'] = has_failed
    for val in ['msg', 'stderr', 'memset_api']:
        if val is not None:
            retvals[val] = eval(val)

    return (retvals)
def create_zone_record(args=None, zone_id=None, records=None, payload=None):
    '''
    Sanity checking has already occurred prior to this function being
    called, so we can go ahead and either create or update the record.
    As defaults are defined for all values in the argument_spec, this
    may cause some changes to occur as the defaults are enforced (if
    the user has only configured required variables).
    '''
    has_changed, has_failed = False, False
    msg, memset_api = None, None

    # assemble the new record.
    new_record = dict()
    new_record['zone_id'] = zone_id
    for arg in ['priority', 'address', 'relative', 'record', 'ttl', 'type']:
        new_record[arg] = args[arg]

    # if we have any matches, update them.
    if records:
        for zone_record in records:
            # record exists, add ID to payload.
            new_record['id'] = zone_record['id']
            if zone_record == new_record:
                # nothing to do; record is already correct so we populate
                # the return var with the existing record's details.
                memset_api = zone_record
                return (has_changed, has_failed, memset_api, msg)
            else:
                # merge dicts ensuring we change any updated values
                payload = zone_record.copy()
                payload.update(new_record)
                api_method = 'dns.zone_record_update'
                if args['check_mode']:
                    has_changed = True
                    # return the new record to the user in the returned var.
                    memset_api = new_record
                    return (has_changed, has_failed, memset_api, msg)
                has_failed, msg, response = memset_api_call(
                    api_key=args['api_key'],
                    api_method=api_method,
                    payload=payload)
                if not has_failed:
                    has_changed = True
                    memset_api = new_record
                    # empty msg as we don't want to return a boatload of json to the user.
                    msg = None
    else:
        # no record found, so we need to create it
        api_method = 'dns.zone_record_create'
        payload = new_record
        if args['check_mode']:
            has_changed = True
            # populate the return var with the new record's details.
            memset_api = new_record
            return (has_changed, has_failed, memset_api, msg)
        has_failed, msg, response = memset_api_call(api_key=args['api_key'],
                                                    api_method=api_method,
                                                    payload=payload)
        if not has_failed:
            has_changed = True
            memset_api = new_record
            #  empty msg as we don't want to return a boatload of json to the user.
            msg = None

    return (has_changed, has_failed, memset_api, msg)