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:
parent
7a33c14782
commit
a0f27d552c
3 changed files with 100 additions and 1 deletions
|
@ -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):
|
||||
|
|
|
@ -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"
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue