File attributes (#18213)

* added attributes to base file params

* dont change attributes when none

* fixed test to deal with new attributes
This commit is contained in:
Brian Coca 2016-11-07 15:48:04 -05:00 committed by GitHub
parent 7a33c14782
commit a0f27d552c
3 changed files with 100 additions and 1 deletions

View file

@ -34,6 +34,29 @@ BOOLEANS = BOOLEANS_TRUE + BOOLEANS_FALSE
SIZE_RANGES = { 'Y': 1<<80, 'Z': 1<<70, 'E': 1<<60, 'P': 1<<50, 'T': 1<<40, 'G': 1<<30, 'M': 1<<20, 'K': 1<<10, 'B': 1 }
FILE_ATTRIBUTES = {
'A': 'noatime',
'a': 'append',
'c': 'compressed',
'C': 'nocow',
'd': 'nodump',
'D': 'dirsync',
'e': 'extents',
'E': 'encrypted',
'h': 'blocksize',
'i': 'immutable',
'I': 'indexed',
'j': 'journalled',
'N': 'inline',
's': 'zero',
'S': 'synchronous',
't': 'notail',
'T': 'blockroot',
'u': 'undelete',
'X': 'compressedraw',
'Z': 'compresseddirty',
}
# ansible modules can be written in any language. To simplify
# development of Python modules, the functions available here can
# be used to do many common tasks
@ -203,6 +226,7 @@ FILE_COMMON_ARGUMENTS=dict(
delimiter = dict(), # used by assemble
directory_mode = dict(), # used by copy
unsafe_writes = dict(type='bool'), # should be available to any module using atomic_move
attributes = dict(aliases=['attr']),
)
PASSWD_ARG_RE = re.compile(r'^[-]{0,2}pass[-]?(word|wd)?')
@ -618,6 +642,19 @@ def _lenient_lowercase(lst):
lowered.append(value)
return lowered
def format_attributes(attributes):
attribute_list = []
for attr in attributes:
if attr in FILE_ATTRIBUTES:
attribute_list.append(FILE_ATTRIBUTES[attr])
return attribute_list
def get_flags_from_attributes(attributes):
flags = []
for key,attr in FILE_ATTRIBUTES.iteritems():
if attr in attributes:
flags.append(key)
return ''.join(flags)
class AnsibleFallbackNotFound(Exception):
pass
@ -759,10 +796,11 @@ class AnsibleModule(object):
if i is not None and secontext[i] == '_default':
secontext[i] = default_secontext[i]
attributes = params.get('attributes', None)
return dict(
path=path, mode=mode, owner=owner, group=group,
seuser=seuser, serole=serole, setype=setype,
selevel=selevel, secontext=secontext,
selevel=selevel, secontext=secontext, attributes=attributes,
)
@ -1058,6 +1096,56 @@ class AnsibleModule(object):
changed = True
return changed
def set_attributes_if_different(self, path, attributes, changed, diff=None):
if attributes is None:
return changed
b_path = to_bytes(path, errors='surrogate_or_strict')
b_path = os.path.expanduser(os.path.expandvars(b_path))
existing = self.get_file_attributes(b_path)
if existing.get('attr_flags','') != attributes:
attrcmd = self.get_bin_path('chattr')
if attrcmd:
attrcmd = [attrcmd, '=%s' % attributes, b_path]
changed = True
if diff is not None:
if 'before' not in diff:
diff['before'] = {}
diff['before']['attributes'] = existing.get('attr_flags')
if 'after' not in diff:
diff['after'] = {}
diff['after']['attributes'] = attributes
if not self.check_mode:
try:
rc, out, err = self.run_command(attrcmd)
if rc != 0 or err:
raise Exception("Error while setting attributes: %s" % (out + err))
except:
e = get_exception()
self.fail_json(path=path, msg='chattr failed', details=str(e))
return changed
def get_file_attributes(self, path):
output = {}
attrcmd = self.get_bin_path('lsattr', False)
if attrcmd:
attrcmd = [attrcmd, '-vd', path]
try:
rc, out, err = self.run_command(attrcmd)
if rc == 0:
res = out.split(' ')[0:2]
output['attr_flags'] = res[1].replace('-','').strip()
output['version'] = res[0].strip()
output['attributes'] = format_attributes(output['attr_flags'])
except:
pass
return output
def _symbolic_mode_to_octal(self, path_stat, symbolic_mode):
new_mode = stat.S_IMODE(path_stat.st_mode)
@ -1167,6 +1255,9 @@ class AnsibleModule(object):
changed = self.set_mode_if_different(
file_args['path'], file_args['mode'], changed, diff
)
changed = self.set_attributes_if_different(
file_args['path'], file_args['attributes'], changed, diff
)
return changed
def set_directory_attributes_if_different(self, file_args, changed, diff=None):

View file

@ -70,4 +70,11 @@ options:
required: false
default: false
version_added: "2.2"
attributes:
description:
- Attributes of the file or directory should be. To get supported flags look at the man page for I(chattr) on the taget system.
required: false
default: None
aliases: ['attr']
version_added: "2.3"
"""

View file

@ -422,6 +422,7 @@ class TestModuleUtilsBasic(ModuleTestCase):
final_params.update(dict(
path = '/path/to/real_file',
secontext=['unconfined_u', 'object_r', 'default_t', 's0'],
attributes=None,
))
# with the proper params specified, the returned dictionary should represent