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
This commit is contained in:
Toshio Kuratomi 2016-08-18 13:16:06 -07:00 committed by Matt Clay
parent 4f09d6065f
commit 07e79ac683

View file

@ -34,7 +34,8 @@ options:
default: no default: no
get_md5: get_md5:
description: 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 required: false
default: yes default: yes
get_checksum: get_checksum:
@ -45,7 +46,8 @@ options:
version_added: "1.8" version_added: "1.8"
checksum_algorithm: checksum_algorithm:
description: 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 required: false
choices: [ 'sha1', 'sha224', 'sha256', 'sha384', 'sha512' ] choices: [ 'sha1', 'sha224', 'sha256', 'sha384', 'sha512' ]
default: sha1 default: sha1
@ -53,7 +55,8 @@ options:
version_added: "2.0" version_added: "2.0"
mime: mime:
description: 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. - This will add both `mime_type` and 'charset' fields to the return, if possible.
required: false required: false
choices: [ Yes, No ] choices: [ Yes, No ]
@ -265,12 +268,14 @@ stat:
sample: /home/foobar/21102015-1445431274-908472971 sample: /home/foobar/21102015-1445431274-908472971
md5: md5:
description: md5 hash of the path 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 type: string
sample: f88fa92d8cf2eeecf4c0a50ccc96d0c0 sample: f88fa92d8cf2eeecf4c0a50ccc96d0c0
checksum_algorithm: checksum_algorithm:
description: hash of the path 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 type: string
sample: 50ba294cdf28c0d5bcde25708df53346825a429f sample: 50ba294cdf28c0d5bcde25708df53346825a429f
aliases: ['checksum', 'checksum_algo'] aliases: ['checksum', 'checksum_algo']
@ -286,12 +291,16 @@ stat:
sample: www-data sample: www-data
mime_type: mime_type:
description: file magic data or 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 type: string
sample: PDF document, version 1.2 sample: PDF document, version 1.2
charset: charset:
description: file character set or encoding 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 type: string
sample: us-ascii sample: us-ascii
readable: readable:
@ -311,11 +320,87 @@ stat:
sample: False sample: False
''' '''
import os import errno
import sys
from stat import *
import pwd
import grp 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(): def main():
module = AnsibleModule( module = AnsibleModule(
@ -324,7 +409,9 @@ def main():
follow=dict(default='no', type='bool'), follow=dict(default='no', type='bool'),
get_md5=dict(default='yes', type='bool'), get_md5=dict(default='yes', type='bool'),
get_checksum=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']), 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']), mime=dict(default=False, type='bool', aliases=['mime_type', 'mime-type']),
), ),
supports_check_mode=True supports_check_mode=True
@ -332,6 +419,7 @@ def main():
path = module.params.get('path') path = module.params.get('path')
follow = module.params.get('follow') follow = module.params.get('follow')
get_mime = module.params.get('mime')
get_md5 = module.params.get('get_md5') get_md5 = module.params.get('get_md5')
get_checksum = module.params.get('get_checksum') get_checksum = module.params.get('get_checksum')
checksum_algorithm = module.params.get('checksum_algorithm') checksum_algorithm = module.params.get('checksum_algorithm')
@ -344,90 +432,32 @@ def main():
except OSError: except OSError:
e = get_exception() e = get_exception()
if e.errno == errno.ENOENT: if e.errno == errno.ENOENT:
d = { 'exists' : False } output = {'exists': False}
module.exit_json(changed=False, stat=d) 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
# back to ansible if get_mime:
d = { mimetype = 'unknown'
'exists' : True, charset = 'unknown'
'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: try:
rc, out, err = module.run_command(filecmd) rc, out, err = module.run_command(filecmd)
if rc == 0: if rc == 0:
mtype, chset = out.split(':')[1].split(';') mimetype, charset = out.split(':')[1].split(';')
d['mime_type'] = mtype.strip() mimetype = mimetype.strip()
d['charset'] = chset.split('=')[1].strip() charset = charset.split('=')[1].strip()
except: except:
pass 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 module.exit_json(changed=False, stat=output)
from ansible.module_utils.basic import *
if __name__ == '__main__':
main() main()