Example #1
0
def file_exist(module):
    """
    Check if a USS file or directory already exists
    Return exist_result to indicate if a USS file or directory with same name already exists.
    :param AnsibleModule module: the ansible module
    """
    exist_result = dict(type=None)
    # create session
    session = get_connect_session(module)
    target = module.params['file_path'].strip()
    if not target.startswith('/'):
        target = '/' + target
    exist_result['session'] = session
    exist_result['target'] = target
    # setup file parent path and file name
    f_path = target[:target.rfind('/')]
    f_name = target[target.rfind('/') + 1:]
    if f_path.startswith('/'):
        f_path = f_path[1:]
    module.params['f_path'] = f_path
    module.params['f_name'] = f_name
    # step1 - check if the target USS file or directory with same name already exists
    res_list = call_file_api(module, session, 'list')
    if res_list.status_code == 200:
        res_content = json.loads(res_list.content)
        if 'returnedRows' in res_content and res_content['returnedRows'] > 0:
            exist_result['file_properties'] = res_content['items'][0]
            if res_content['items'][0]['mode'].startswith('d'):
                exist_result['type'] = 'directory'
            else:
                exist_result['type'] = 'file'
            # list tag of the target USS file or directory
            request_body = dict()
            request_body['request'] = 'chtag'
            request_body['action'] = 'list'
            request_headers = dict()
            request_headers['Content-Type'] = 'application/json'
            res_operate = call_file_api(module, session, 'operate',
                                        request_headers,
                                        json.dumps(request_body))
            if res_operate.status_code == 200:
                res_content = json.loads(res_operate.content)
                if 'stdout' in res_content:
                    exist_result['file_properties']['tag'] = res_content[
                        'stdout'][0]
    else:
        res_json = res_list.json()
        if (res_list.status_code == 404 and 'category' in res_json
                and 'rc' in res_json and 'reason' in res_json
                and res_json['category'] == 1 and res_json['rc'] == 4
                and res_json['reason'] == 8):
            # parent path not found
            exist_result['type'] = 'error'
    return exist_result
Example #2
0
def delete_file(module, session, target, exist):
    """
    Delete a USS file or directory
    Return the message to indicate whether the USS file or directory is deleted successfully.
    :param AnsibleModule module: the ansible module
    :param Session session: the current connection session
    :param str target: the USS file or directory to be deleted
    :param bool exist: whether the USS file or directory to be deleted exists or not
    """
    delete_result = dict(
        changed=False,
        message='',
    )
    # step1 - exit if the USS file or directory to be deleted does not exist
    if not exist:
        delete_result[
            'message'] = 'The file or directory ' + target + ' does not exist.'
        module.exit_json(**delete_result)
    # step2 - setup request headers
    request_headers = dict()
    request_headers['X-IBM-Option'] = 'recursive'
    # step3 - delete the USS file or directory
    res_delete = call_file_api(module, session, 'delete', request_headers)
    if res_delete.status_code == 204:
        delete_result['changed'] = True
        delete_result[
            'message'] = 'The file or directory ' + target + ' is deleted successfully.'
        module.exit_json(**delete_result)
    else:
        module.fail_json(
            msg='Failed to delete ' + target + ' ---- Http request error: ' +
            str(res_delete.status_code) + ': ' + str(res_delete.json()))
Example #3
0
def create_file(module, session, target):
    """
    Create a USS file or directory
    Return the message to indicate whether the USS file or directory is created successfully.
    :param AnsibleModule module: the ansible module
    :param Session session: the current connection session
    :param str target: the USS file or directory to be created
    """
    create_result = dict(
        changed=False,
        message='',
    )
    # step1 - setup request args
    module.params['f_create_type'] = module.params['file_state']
    if module.params['file_mode'] is not None:
        module.params['f_create_mode'] = format_mode(
            module, module.params['file_mode']['mode'], False)
    else:
        module.params['f_create_mode'] = 'rwxr-xr-x'
    # step2 - setup request headers
    request_headers = dict()
    request_headers['Content-Type'] = 'application/json'
    # step3 - create the USS file or directory
    res_create = call_file_api(module, session, 'create', request_headers)
    if res_create.status_code == 201:
        create_result['changed'] = True
        create_result['message'] = 'The ' + module.params[
            'file_state'] + ' ' + target + ' is created successfully.'
        operate_result = operate_file(module, session, target, None)
        if operate_result['message'] != '':
            create_result['message'] += ' ' + operate_result['message']
        if 'file_properties' in operate_result:
            create_result['file_properties'] = operate_result[
                'file_properties']
        if 'errors' not in operate_result:
            module.exit_json(**create_result)
        else:
            create_result['errors'] = operate_result['errors']
            module.fail_json(msg=str(create_result))
    else:
        module.fail_json(
            msg='Failed to create the ' + module.params['file_state'] + ' ' +
            target + ' ---- Http request error: ' +
            str(res_create.status_code) + ': ' + str(res_create.json()))
Example #4
0
def fetch_file(module):
    """
    Fetch USS file from z/OS
    Return the message to indicate whether the USS file is successfully fetched.
    Return file_content of the retrieved contents.
    Return file_matched_content of the matched contents if file_search is specified.
    Return file_matched_range of the range of the matched contents if file_search is specified.
    Return file_checksum of the checksum if file_search and file_range are not specified.
    :param AnsibleModule module: the ansible module
    """
    fetch_result = dict(
        changed=False,
        message='',
    )
    # create session
    session = get_connect_session(module)
    file = module.params['file_src'].strip()
    path = module.params['file_dest'].strip()
    host = module.params['zmf_host'].strip()
    fetch_src = file
    if not fetch_src.startswith('/'):
        fetch_src = '/' + fetch_src
    # setup file parent path and file name
    f_path = fetch_src[:fetch_src.rfind('/')]
    f_name = fetch_src[fetch_src.rfind('/') + 1:]
    if f_path.startswith('/'):
        f_path = f_path[1:]
    module.params['f_path'] = f_path
    module.params['f_name'] = f_name
    # step1 - combine request headers
    request_headers = dict()
    if module.params['file_data_type'] == 'text' and module.params[
            'file_encoding'] is not None:
        request_headers[
            'X-IBM-Data-Type'] = 'text;fileEncoding=' + module.params[
                'file_encoding']['from']
        request_headers[
            'Content-Type'] = 'text/plain;charset=' + module.params[
                'file_encoding']['to']
    else:
        request_headers['X-IBM-Data-Type'] = module.params['file_data_type']
    if module.params['file_range'] is not None:
        if 'end' in module.params['file_range']:
            end = module.params['file_range']['end']
        else:
            end = 0
        if 'start' in module.params['file_range']:
            range = str(module.params['file_range']['start']) + '-' + str(end)
        else:
            range = '-' + str(end)
        if module.params['file_data_type'] == 'text':
            request_headers['X-IBM-Record-Range'] = range
        else:
            request_headers['Range'] = 'bytes=' + range
    if module.params['file_checksum'] is not None and module.params[
            'file_checksum'].strip() != '':
        request_headers['If-None-Match'] = module.params[
            'file_checksum'].strip()
    # step2 - fetch USS file
    res_fetch = call_file_api(module, session, 'fetch', request_headers)
    res_cd = res_fetch.status_code
    if res_cd != 200 and res_cd != 206 and res_cd != 416 and res_cd != 304:
        # handle response error
        res_error = res_fetch.json()
        if ('category' in res_error and res_error['category'] == 6
                and 'rc' in res_error and res_error['rc'] == 8
                and 'reason' in res_error and res_error['reason'] == 1046):
            # not fail - no conntents returned in the range of records (500)
            fetch_result[
                'message'] = 'The USS file ' + fetch_src + ' is not fetched since no contents is returned in the specified range.'
            module.exit_json(**fetch_result)
        else:
            # fail - return JSON error report
            module.fail_json(msg='Failed to fetch the USS file ' + fetch_src +
                             ' ---- Http request error: ' + str(res_cd) +
                             ': ' + str(res_error))
    else:
        # handle response
        res_hd = res_fetch.headers
        if 'Etag' in res_hd:
            fetch_result['file_checksum'] = res_hd['Etag']
        if 'X-IBM-Record-Range' in res_hd:
            fetch_result['file_matched_range'] = res_hd['X-IBM-Record-Range']
            fetch_result['file_matched_content'] = []
            if 'Content-Length' in res_hd and int(
                    res_hd['Content-Length']) == 0:
                # no matched conntents with the specified search keyword (200)
                fetch_result[
                    'message'] = 'The USS file ' + fetch_src + ' is not fetched since no matched contents is found with the specified search keyword.'
                module.exit_json(**fetch_result)
        if res_cd == 304:
            # file not changed when file_checksum is specified (304)
            fetch_result[
                'message'] = 'The USS file ' + fetch_src + ' is not fetched since it is not changed.'
        elif res_cd == 416:
            # no conntents returned in the range of bytes (416)
            fetch_result[
                'message'] = 'The USS file ' + fetch_src + ' is not fetched since no contents is returned in the specified range.'
        else:
            # save the returned conntents to local (200/206)
            if file.startswith('/'):
                file = file[1:]
            if not path.endswith('/'):
                path += '/'
            if module.params['file_flat'] is False:
                path += host + '/' + file[0:file.rfind('/') + 1]
            file = file[file.rfind('/') + 1:]
            try:
                if not os.path.exists(path):
                    os.makedirs(path, 0o755)
                else:
                    os.chmod(path, 0o755)
            except OSError as ex:
                module.fail_json(msg='Failed to fetch the USS file ' +
                                 fetch_src + ' ---- OS error: ' + str(ex))
            if res_cd == 206:
                # binary contents returned in the specified range of bytes (206)
                f_write = open(path + file + '.range', 'wb')
                f_write.write(res_fetch.content)
                f_write.close()
                fetch_result['message'] = 'A range of bytes in the USS file ' + fetch_src + ' is fetched successfully and saved in: ' \
                    + path + file + '.range'
            elif 'file_matched_range' in fetch_result:
                # matched text contents returned with the specified search keyword (200)
                f_write = open(path + file + '.search', 'w')
                f_write.write(res_fetch.text)
                f_write.close()
                fetch_result['file_matched_content'] = res_fetch.text.split(
                    '\n')
                fetch_result['message'] = 'The matched contents in the USS file ' + fetch_src + ' is fetched successfully and saved in: ' \
                    + path + file + '.search'
            elif 'file_checksum' not in fetch_result:
                # text contents returned in the specified range of records (200)
                f_write = open(path + file + '.range', 'w')
                f_write.write(res_fetch.text)
                f_write.close()
                fetch_result['file_content'] = res_fetch.text.split('\n')
                fetch_result['message'] = 'A range of records in the USS file ' + fetch_src + ' is fetched successfully and saved in: ' \
                    + path + file + '.range'
            else:
                # all contents returned (200)
                if res_hd['Content-Type'].find('text/plain') > -1:
                    # text contents returned
                    f_write = open(path + file, 'w')
                    f_write.write(res_fetch.text)
                    f_write.close()
                    fetch_result['file_content'] = res_fetch.text.split('\n')
                else:
                    # binary contents returned
                    f_write = open(path + file, 'wb')
                    f_write.write(res_fetch.content)
                    f_write.close()
                fetch_result[
                    'message'] = 'The USS file ' + fetch_src + ' is fetched successfully and saved in: ' + path + file
        module.exit_json(**fetch_result)
Example #5
0
def copy_file(module):
    """
    Copy data to a USS file
    Return the message to indicate whether the USS file is successfully copied.
    Return file_checksum of the checksum of the USS file.
    :param AnsibleModule module: the ansible module
    """
    copy_result = dict(
        changed=False,
        message='',
    )
    # create session
    session = get_connect_session(module)
    copy_dest = module.params['file_dest'].strip()
    if not copy_dest.startswith('/'):
        copy_dest = '/' + copy_dest
    copy_src = None
    if module.params['file_src'] is not None and module.params[
            'file_src'].strip() != '':
        copy_src = module.params['file_src'].strip()
    # setup file parent path and file name
    f_path = copy_dest[:copy_dest.rfind('/')]
    f_name = copy_dest[copy_dest.rfind('/') + 1:]
    if f_path.startswith('/'):
        f_path = f_path[1:]
    module.params['f_path'] = f_path
    module.params['f_name'] = f_name
    # step1 - check if the target USS file exists when file_force=false
    if module.params['file_force'] is False:
        res_list = call_file_api(module, session, 'list')
        if res_list.status_code == 200:
            res_content = json.loads(res_list.content)
            if 'returnedRows' in res_content and res_content[
                    'returnedRows'] > 0:
                # not fail - no data is copied since the target USS file exists
                copy_result[
                    'message'] = 'No data is copied since the target USS file ' + copy_dest + ' already exists and file_force is set to False.'
                module.exit_json(**copy_result)
    # step2 - read file_src or file_content
    f_read = None
    request_body = None
    if module.params['file_data_type'] != 'text':
        try:
            f_read = open(copy_src, 'rb')
            request_body = f_read.read()
        except OSError as ex:
            module.fail_json(
                msg='Failed to copy data to the target USS file ' + copy_dest +
                ' ---- OS error: ' + str(ex))
    else:
        if module.params['file_content'] is not None and module.params[
                'file_content'].strip() != '':
            request_body = module.params['file_content']
        else:
            try:
                f_read = open(copy_src, 'r')
                request_body = f_read.read()
            except OSError as ex:
                module.fail_json(
                    msg='Failed to copy data to the target USS file ' +
                    copy_dest + ' ---- OS error: ' + str(ex))
    if f_read is not None:
        f_read.close()
    # step3 - combine request headers
    request_headers = dict()
    request_headers['X-IBM-Data-Type'] = module.params['file_data_type']
    request_headers['Content-Type'] = 'text/plain'
    if module.params['file_data_type'] == 'text':
        if module.params['file_diff'] is True:
            request_headers['Content-Type'] = 'application/x-ibm-diff-e'
        if module.params['file_encoding'] is not None:
            request_headers[
                'X-IBM-Data-Type'] += ';fileEncoding=' + module.params[
                    'file_encoding']['to']
            request_headers['Content-Type'] += ';charset=' + module.params[
                'file_encoding']['from']
        if module.params['file_crlf'] is True:
            request_headers['X-IBM-Data-Type'] += ';crlf=true'
    if module.params['file_checksum'] is not None and module.params[
            'file_checksum'].strip() != '':
        request_headers['If-Match'] = module.params['file_checksum'].strip()
    # step4 - copy data to the target USS file
    res_copy = call_file_api(module, session, 'copy', request_headers,
                             request_body)
    res_cd = res_copy.status_code
    if res_cd != 201 and res_cd != 204:
        # handle response error
        if res_cd == 412:
            # fail - file has been modified when file_checksum is specified (412)
            module.fail_json(
                msg='Failed to copy data to the target USS file ' + copy_dest +
                ' ---- the target USS file has been modified and its checksum is: '
                + res_copy.headers['Etag'])
        else:
            # fail - return JSON error report
            res_error = res_copy.json()
            module.fail_json(
                msg='Failed to copy data to the target USS file ' + copy_dest +
                ' ---- Http request error: ' + str(res_cd) + ': ' +
                str(res_error))
    else:
        # handle response
        copy_result['changed'] = True
        copy_result['file_checksum'] = res_copy.headers['Etag']
        if res_cd == 201:
            # success - a new USS file is created (201)
            copy_result[
                'message'] = 'The target USS file ' + copy_dest + ' is created and updated successfully.'
        else:
            # success - an existing USS file is updated (204)
            copy_result[
                'message'] = 'The target USS file ' + copy_dest + ' is updated successfully.'
        module.exit_json(**copy_result)
Example #6
0
def operate_file_action(module, session, action, target):
    """
    Operate on a USS file or directory
    Return operate_result_action to indicate whether each operation for the USS file or directory is successfully.
    :param AnsibleModule module: the ansible module
    :param Session session: the current connection session
    :param str action: the operation
    :param str target: the USS file or directory to be operated
    """
    operate_result_action = dict(updated=False, error='')
    old_f_path = module.params['f_path']
    old_f_name = module.params['f_name']
    # step1 - setup request args
    request_body = dict()
    request_body['request'] = action
    if action == 'chmod':
        # chmod
        request_body['mode'] = format_mode(module,
                                           module.params['file_mode']['mode'],
                                           True)
        if 'recursive' in module.params['file_mode']:
            request_body['recursive'] = module.params['file_mode']['recursive']
    elif action == 'chown':
        # chown
        request_body['owner'] = module.params['file_owner']['owner']
        if 'group' in module.params['file_owner']:
            request_body['group'] = module.params['file_owner']['group']
        if 'recursive' in module.params['file_owner']:
            request_body['recursive'] = module.params['file_owner'][
                'recursive']
    elif action == 'chtag':
        # chtag
        if module.params['file_tag']['tag'] == 'absent':
            request_body['action'] = 'remove'
        else:
            request_body['action'] = 'set'
            request_body['type'] = module.params['file_tag']['tag']
        if 'codeset' in module.params['file_tag']:
            request_body['codeset'] = module.params['file_tag']['codeset']
        if 'recursive' in module.params['file_tag']:
            request_body['recursive'] = module.params['file_tag']['recursive']
    elif action == 'move':
        # rename
        new = module.params['file_new_name'].strip()
        if not new.startswith('/'):
            new = '/' + new
        # setup file parent path and file name
        f_path = new[:new.rfind('/')]
        f_name = new[new.rfind('/') + 1:]
        if f_path.startswith('/'):
            f_path = f_path[1:]
        module.params['f_path'] = f_path
        module.params['f_name'] = f_name
        request_body['from'] = target
    # step2 - setup request headers
    request_headers = dict()
    request_headers['Content-Type'] = 'application/json'
    # step3 - operate on the USS file or directory
    res_operate = call_file_api(module, session, 'operate', request_headers,
                                json.dumps(request_body))
    if res_operate.status_code == 200:
        operate_result_action['updated'] = True
        if action == 'move':
            operate_result_action['new'] = new
    else:
        # reset file parent path and file name if rename is failed
        if action == 'move':
            module.params['f_path'] = old_f_path
            module.params['f_name'] = old_f_name
        try:
            res_content = res_operate.json()
        except Exception:
            res_content = res_operate.content
        operate_result_action['error'] = ' ---- Http request error: ' + str(
            res_operate.status_code) + ': ' + str(res_content)
    return operate_result_action
Example #7
0
def operate_file(module, session, target, old_properties):
    """
    Operate on a USS file or directory
    Return the message to indicate whether the USS file or directory is updated successfully.
    Return the file_properties of the updated USS file or directory.
    :param AnsibleModule module: the ansible module
    :param Session session: the current connection session
    :param str target: the USS file or directory to be operated
    :param dict old_properties: the old properties of the USS file or directory
    """
    operate_result = dict(
        changed=False,
        message='',
    )
    need_update = False
    need_rename = False
    new = ''
    # step1 - operate on the USS file or directory
    if old_properties is not None and module.params['file_mode'] is not None:
        need_update = True
        chmod = operate_file_action(module, session, 'chmod', target)
        if chmod['updated'] is True:
            operate_result['changed'] = True
        else:
            if 'errors' not in operate_result:
                operate_result['errors'] = []
            operate_result['errors'].append('Failed to change mode for the ' +
                                            module.params['file_state'] + ' ' +
                                            target + chmod['error'])
    if module.params['file_owner'] is not None:
        need_update = True
        chown = operate_file_action(module, session, 'chown', target)
        if chown['updated'] is True:
            operate_result['changed'] = True
        else:
            if 'errors' not in operate_result:
                operate_result['errors'] = []
            operate_result['errors'].append('Failed to change owner for the ' +
                                            module.params['file_state'] + ' ' +
                                            target + chown['error'])
    if module.params['file_tag'] is not None:
        need_update = True
        chtag = operate_file_action(module, session, 'chtag', target)
        if chtag['updated'] is True:
            operate_result['changed'] = True
        else:
            if 'errors' not in operate_result:
                operate_result['errors'] = []
            operate_result['errors'].append('Failed to change tag for the ' +
                                            module.params['file_state'] + ' ' +
                                            target + chtag['error'])
    if module.params['file_new_name'] is not None and module.params[
            'file_new_name'].strip() != '':
        need_rename = True
        rename = operate_file_action(module, session, 'move', target)
        if rename['updated'] is True:
            operate_result['changed'] = True
            new = rename['new']
        else:
            if 'errors' not in operate_result:
                operate_result['errors'] = []
            operate_result['errors'].append('Failed to rename the ' +
                                            module.params['file_state'] + ' ' +
                                            target + rename['error'])
    # step2 - return the roperties of the USS file or directory
    if old_properties is not None and (need_update is not True
                                       and need_rename is not True):
        operate_result['file_properties'] = old_properties
        operate_result['message'] = 'The ' + module.params[
            'file_state'] + ' ' + target + ' already exists.'
        module.exit_json(**operate_result)
    else:
        res_list = call_file_api(module, session, 'list')
        if res_list.status_code == 200:
            res_content = json.loads(res_list.content)
            if 'returnedRows' in res_content and res_content[
                    'returnedRows'] > 0:
                operate_result['file_properties'] = res_content['items'][0]
                # list tag of the target USS file or directory
                request_body = dict()
                request_body['request'] = 'chtag'
                request_body['action'] = 'list'
                request_headers = dict()
                request_headers['Content-Type'] = 'application/json'
                res_operate = call_file_api(module, session, 'operate',
                                            request_headers,
                                            json.dumps(request_body))
                if res_operate.status_code == 200:
                    res_content = json.loads(res_operate.content)
                    if 'stdout' in res_content:
                        operate_result['file_properties']['tag'] = res_content[
                            'stdout'][0]
        if need_update is True:
            if 'errors' not in operate_result:
                operate_result['message'] = 'The ' + module.params[
                    'file_state'] + ' ' + target + ' is updated successfully. '
            else:
                operate_result[
                    'message'] = 'Failed to update the ' + module.params[
                        'file_state'] + ' ' + target + '. '
        if need_rename is True:
            if new != '':
                operate_result['message'] += 'The ' + module.params[
                    'file_state'] + ' ' + target + ' is successfully renamed to ' + new + '.'
            else:
                operate_result[
                    'message'] += 'Failed to rename the ' + module.params[
                        'file_state'] + ' ' + target + '.'
        if old_properties is None:
            return operate_result
        else:
            if 'errors' not in operate_result:
                module.exit_json(**operate_result)
            else:
                module.fail_json(msg=str(operate_result))