From 07e79ac68319fd2bfbf400c6245aef618369d795 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Thu, 18 Aug 2016 13:16:06 -0700 Subject: [PATCH] Refactor stat.py so that it would be more friendly for unittests. (#4462) * Refactor stat.py so that it would be more friendly for unittests. * PEP8ify since we're moving most of the lines in the file and there's no open PRs --- lib/ansible/modules/files/stat.py | 220 +++++++++++++++++------------- 1 file changed, 125 insertions(+), 95 deletions(-) diff --git a/lib/ansible/modules/files/stat.py b/lib/ansible/modules/files/stat.py index facdd8fb544..c92d3da66a7 100644 --- a/lib/ansible/modules/files/stat.py +++ b/lib/ansible/modules/files/stat.py @@ -34,7 +34,8 @@ options: default: no get_md5: description: - - Whether to return the md5 sum of the file. Will return None if we're unable to use md5 (Common for FIPS-140 compliant systems) + - Whether to return the md5 sum of the file. Will return None if we're + unable to use md5 (Common for FIPS-140 compliant systems) required: false default: yes get_checksum: @@ -45,7 +46,8 @@ options: version_added: "1.8" checksum_algorithm: description: - - Algorithm to determine checksum of file. Will throw an error if the host is unable to use specified algorithm. + - Algorithm to determine checksum of file. Will throw an error if the + host is unable to use specified algorithm. required: false choices: [ 'sha1', 'sha224', 'sha256', 'sha384', 'sha512' ] default: sha1 @@ -53,7 +55,8 @@ options: version_added: "2.0" mime: description: - - Use file magic and return data about the nature of the file. this uses the 'file' utility found on most Linux/Unix systems. + - Use file magic and return data about the nature of the file. this uses + the 'file' utility found on most Linux/Unix systems. - This will add both `mime_type` and 'charset' fields to the return, if possible. required: false choices: [ Yes, No ] @@ -265,12 +268,14 @@ stat: sample: /home/foobar/21102015-1445431274-908472971 md5: description: md5 hash of the path - returned: success, path exists and user can read stats and path supports hashing and md5 is supported + returned: success, path exists and user can read stats and path + supports hashing and md5 is supported type: string sample: f88fa92d8cf2eeecf4c0a50ccc96d0c0 checksum_algorithm: description: hash of the path - returned: success, path exists, user can read stats, path supports hashing and supplied checksum algorithm is available + returned: success, path exists, user can read stats, path supports + hashing and supplied checksum algorithm is available type: string sample: 50ba294cdf28c0d5bcde25708df53346825a429f aliases: ['checksum', 'checksum_algo'] @@ -286,12 +291,16 @@ stat: sample: www-data mime_type: description: file magic data or mime-type - returned: success, path exists and user can read stats and installed python supports it and the `mime` option was true, will return 'unknown' on error. + returned: success, path exists and user can read stats and + installed python supports it and the `mime` option was true, will + return 'unknown' on error. type: string sample: PDF document, version 1.2 charset: description: file character set or encoding - returned: success, path exists and user can read stats and installed python supports it and the `mime` option was true, will return 'unknown' on error. + returned: success, path exists and user can read stats and + installed python supports it and the `mime` option was true, will + return 'unknown' on error. type: string sample: us-ascii readable: @@ -311,27 +320,106 @@ stat: sample: False ''' -import os -import sys -from stat import * -import pwd +import errno import grp +import os +import pwd +import stat + +# import module snippets +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.pycompat24 import get_exception + + +def format_output(module, path, st, follow, get_md5, get_checksum, + checksum_algorithm, mimetype=None, charset=None): + mode = st.st_mode + + # back to ansible + output = dict( + exists=True, + path=path, + mode="%04o" % stat.S_IMODE(mode), + isdir=stat.S_ISDIR(mode), + ischr=stat.S_ISCHR(mode), + isblk=stat.S_ISBLK(mode), + isreg=stat.S_ISREG(mode), + isfifo=stat.S_ISFIFO(mode), + islnk=stat.S_ISLNK(mode), + issock=stat.S_ISSOCK(mode), + uid=st.st_uid, + gid=st.st_gid, + size=st.st_size, + inode=st.st_ino, + dev=st.st_dev, + nlink=st.st_nlink, + atime=st.st_atime, + mtime=st.st_mtime, + ctime=st.st_ctime, + wusr=bool(mode & stat.S_IWUSR), + rusr=bool(mode & stat.S_IRUSR), + xusr=bool(mode & stat.S_IXUSR), + wgrp=bool(mode & stat.S_IWGRP), + rgrp=bool(mode & stat.S_IRGRP), + xgrp=bool(mode & stat.S_IXGRP), + woth=bool(mode & stat.S_IWOTH), + roth=bool(mode & stat.S_IROTH), + xoth=bool(mode & stat.S_IXOTH), + isuid=bool(mode & stat.S_ISUID), + isgid=bool(mode & stat.S_ISGID), + readable=os.access(path, os.R_OK), + writeable=os.access(path, os.W_OK), + excutable=os.access(path, os.X_OK), + ) + + if stat.S_ISLNK(mode): + output['lnk_source'] = os.path.realpath(path) + + if stat.S_ISREG(mode) and get_md5 and os.access(path, os.R_OK): + # Will fail on FIPS-140 compliant systems + try: + output['md5'] = module.md5(path) + except ValueError: + output['md5'] = None + + if stat.S_ISREG(mode) and get_checksum and os.access(path, os.R_OK): + output['checksum'] = module.digest_from_file(path, checksum_algorithm) + + try: + pw = pwd.getpwuid(st.st_uid) + + output['pw_name'] = pw.pw_name + + grp_info = grp.getgrgid(st.st_gid) + output['gr_name'] = grp_info.gr_name + except: + pass + + if not (mimetype is None and charset is None): + output['mime_type'] = mimetype + output['charset'] = charset + + return output + def main(): module = AnsibleModule( - argument_spec = dict( - path = dict(required=True, type='path'), - follow = dict(default='no', type='bool'), - get_md5 = dict(default='yes', type='bool'), - get_checksum = dict(default='yes', type='bool'), - checksum_algorithm = dict(default='sha1', type='str', choices=['sha1', 'sha224', 'sha256', 'sha384', 'sha512'], aliases=['checksum_algo', 'checksum']), - mime = dict(default=False, type='bool', aliases=['mime_type', 'mime-type']), + argument_spec=dict( + path=dict(required=True, type='path'), + follow=dict(default='no', type='bool'), + get_md5=dict(default='yes', type='bool'), + get_checksum=dict(default='yes', type='bool'), + checksum_algorithm=dict(default='sha1', type='str', + choices=['sha1', 'sha224', 'sha256', 'sha384', 'sha512'], + aliases=['checksum_algo', 'checksum']), + mime=dict(default=False, type='bool', aliases=['mime_type', 'mime-type']), ), - supports_check_mode = True + supports_check_mode=True ) path = module.params.get('path') follow = module.params.get('follow') + get_mime = module.params.get('mime') get_md5 = module.params.get('get_md5') get_checksum = module.params.get('get_checksum') checksum_algorithm = module.params.get('checksum_algorithm') @@ -344,90 +432,32 @@ def main(): except OSError: e = get_exception() if e.errno == errno.ENOENT: - d = { 'exists' : False } - module.exit_json(changed=False, stat=d) + output = {'exists': False} + module.exit_json(changed=False, stat=output) - module.fail_json(msg = e.strerror) + module.fail_json(msg=e.strerror) - mode = st.st_mode + mimetype = None + charset = None + if get_mime: + mimetype = 'unknown' + charset = 'unknown' - # back to ansible - d = { - 'exists' : True, - 'path' : path, - 'mode' : "%04o" % S_IMODE(mode), - 'isdir' : S_ISDIR(mode), - 'ischr' : S_ISCHR(mode), - 'isblk' : S_ISBLK(mode), - 'isreg' : S_ISREG(mode), - 'isfifo' : S_ISFIFO(mode), - 'islnk' : S_ISLNK(mode), - 'issock' : S_ISSOCK(mode), - 'uid' : st.st_uid, - 'gid' : st.st_gid, - 'size' : st.st_size, - 'inode' : st.st_ino, - 'dev' : st.st_dev, - 'nlink' : st.st_nlink, - 'atime' : st.st_atime, - 'mtime' : st.st_mtime, - 'ctime' : st.st_ctime, - 'wusr' : bool(mode & stat.S_IWUSR), - 'rusr' : bool(mode & stat.S_IRUSR), - 'xusr' : bool(mode & stat.S_IXUSR), - 'wgrp' : bool(mode & stat.S_IWGRP), - 'rgrp' : bool(mode & stat.S_IRGRP), - 'xgrp' : bool(mode & stat.S_IXGRP), - 'woth' : bool(mode & stat.S_IWOTH), - 'roth' : bool(mode & stat.S_IROTH), - 'xoth' : bool(mode & stat.S_IXOTH), - 'isuid' : bool(mode & stat.S_ISUID), - 'isgid' : bool(mode & stat.S_ISGID), - 'readable' : os.access(path, os.R_OK), - 'writeable' : os.access(path, os.W_OK), - 'excutable' : os.access(path, os.X_OK), - } - - if S_ISLNK(mode): - d['lnk_source'] = os.path.realpath(path) - - if S_ISREG(mode) and get_md5 and os.access(path,os.R_OK): - # Will fail on FIPS-140 compliant systems - try: - d['md5'] = module.md5(path) - except ValueError: - d['md5'] = None - - if S_ISREG(mode) and get_checksum and os.access(path,os.R_OK): - d['checksum'] = module.digest_from_file(path, checksum_algorithm) - - try: - pw = pwd.getpwuid(st.st_uid) - - d['pw_name'] = pw.pw_name - - grp_info = grp.getgrgid(st.st_gid) - d['gr_name'] = grp_info.gr_name - except: - pass - - if module.params.get('mime'): - d['mime_type'] = 'unknown' - d['charset'] = 'unknown' - - filecmd = [module.get_bin_path('file', True),'-i', path] + filecmd = [module.get_bin_path('file', True), '-i', path] try: rc, out, err = module.run_command(filecmd) if rc == 0: - mtype, chset = out.split(':')[1].split(';') - d['mime_type'] = mtype.strip() - d['charset'] = chset.split('=')[1].strip() + mimetype, charset = out.split(':')[1].split(';') + mimetype = mimetype.strip() + charset = charset.split('=')[1].strip() except: pass - module.exit_json(changed=False, stat=d) + output = format_output(module, path, st, follow, get_md5, get_checksum, + checksum_algorithm, mimetype=mimetype, + charset=charset) -# import module snippets -from ansible.module_utils.basic import * + module.exit_json(changed=False, stat=output) -main() +if __name__ == '__main__': + main()