diff --git a/docsite/rst/developing_modules_python3.rst b/docsite/rst/developing_modules_python3.rst index 612a4ef606..b4e7baeafb 100644 --- a/docsite/rst/developing_modules_python3.rst +++ b/docsite/rst/developing_modules_python3.rst @@ -131,6 +131,29 @@ modules should create their octals like this:: # Can't use 0755 on Python-3 and can't use 0o755 on Python-2.4 EXECUTABLE_PERMS = int('0755', 8) +Outputting octal numbers may also need to be changed. In python2 we often did +this to return file permissions:: + + mode = int('0775', 8) + result['mode'] = oct(mode) + +This would give the user ``result['mode'] == '0755'`` in their playbook. In +python3, :func:`oct` returns the format with the lowercase ``o`` in it like: +``result['mode'] == '0o755'``. If a user had a conditional in their playbook +or was using the mode in a template the new format might break things. We +need to return the old form of mode for backwards compatibility. You can do +it like this:: + + mode = int('0775', 8) + result['mode'] = '0%03o' % mode + +You should use this wherever backwards compatibility is a concern or you are +dealing with file permissions. (With file permissions a user may be feeding +the mode into another program or to another module which doesn't understand +the python syntax for octal numbers. ``[zero][digit][digit][digit]`` is +understood by most everything and therefore the right way to express octals in +these cisrcumstances. + Bundled six ----------- diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index e8014d1c33..62f9e8d7d7 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -1013,10 +1013,10 @@ class AnsibleModule(object): if diff is not None: if 'before' not in diff: diff['before'] = {} - diff['before']['mode'] = oct(prev_mode) + diff['before']['mode'] = '0%03o' % prev_mode if 'after' not in diff: diff['after'] = {} - diff['after']['mode'] = oct(mode) + diff['after']['mode'] = '0%03o' % mode if self.check_mode: return True diff --git a/lib/ansible/plugins/lookup/filetree.py b/lib/ansible/plugins/lookup/filetree.py index d0cbe298fc..f4d96af876 100644 --- a/lib/ansible/plugins/lookup/filetree.py +++ b/lib/ansible/plugins/lookup/filetree.py @@ -23,6 +23,8 @@ import grp import stat from ansible.plugins.lookup import LookupBase +from ansible.utils.unicode import to_str + from __main__ import display warning = display.warning @@ -33,25 +35,15 @@ try: except ImportError: pass -def _to_filesystem_str(path): - '''Returns filesystem path as a str, if it wasn't already. - - Used in selinux interactions because it cannot accept unicode - instances, and specifying complex args in a playbook leaves - you with unicode instances. This method currently assumes - that your filesystem encoding is UTF-8. - - ''' - if isinstance(path, unicode): - path = path.encode("utf-8") - return path # If selinux fails to find a default, return an array of None def selinux_context(path): context = [None, None, None, None] if HAVE_SELINUX and selinux.is_selinux_enabled(): try: - ret = selinux.lgetfilecon_raw(_to_filesystem_str(path)) + # note: the selinux module uses byte strings on python2 and text + # strings on python3 + ret = selinux.lgetfilecon_raw(to_str(path)) except OSError: return context if ret[0] != -1: @@ -60,6 +52,7 @@ def selinux_context(path): context = ret[1].split(':', 3) return context + def file_props(root, path): ''' Returns dictionary with file properties, or return None on failure ''' abspath = os.path.join(root, path) @@ -94,7 +87,7 @@ def file_props(root, path): ret['group'] = grp.getgrgid(st.st_gid).gr_name except KeyError: ret['group'] = st.st_gid - ret['mode'] = str(oct(stat.S_IMODE(st.st_mode))) + ret['mode'] = '0%03o' % (stat.S_IMODE(st.st_mode)) ret['size'] = st.st_size ret['mtime'] = st.st_mtime ret['ctime'] = st.st_ctime @@ -129,4 +122,4 @@ class LookupModule(LookupBase): if props is not None: ret.append(props) - return ret \ No newline at end of file + return ret