library/file
be55145a
 #!/usr/bin/python
7e9e2901
 # -*- coding: utf-8 -*-
be55145a
 
 # (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
 #
 # This file is part of Ansible
 #
 # Ansible is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 #
 # Ansible is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
 
 import shutil
 import stat
 import grp
 import pwd
1e5d34ba
 try:
     import selinux
     HAVE_SELINUX=True
 except ImportError:
     HAVE_SELINUX=False
be55145a
 
60e04102
 DOCUMENTATION = '''
 ---
 module: file
 short_description: Sets attributes of files
 description: 
      - Sets attributes of files, symlinks, and directories, or removes
        files/symlinks/directories. Many other modules support the same options as
068ef0e9
        the M(file) module - including M(copy), M(template), and M(assemble).
60e04102
 options:
8dadf8a9
   path:
83f277cf
     description:
caf003c8
       - defines the file being managed, unless when used with C(state=link), and then sets the destination to create a symbolic link to using I(src)
83f277cf
     required: true
     default: []
     aliases: []
   state:
     description:
e620fed7
       - If C(directory), all immediate subdirectories will be created if they
         do not exist. If C(file), the file will NOT be created if it does not
         exist, see the M(copy) or M(template) module if you want that behavior.
         If C(link), the symbolic link will be created or changed. If C(absent),
         directories will be recursively deleted, and files or symlinks will be
         unlinked.
83f277cf
     required: false
     default: file
     choices: [ file, link, directory, absent ]
   mode:
e620fed7
     required: false
     default: null
     choices: []
     description:
caf003c8
       - mode the file or directory should be, such as 0644 as would be fed to I(chmod)
e620fed7
   owner:
     required: false
     default: null
     choices: []
     description:
       - name of the user that should own the file/directory, as would be fed to I(chown)
   group:
     required: false
     default: null
     choices: []
     description:
       - name of the group that should own the file/directory, as would be fed to I(chown)
   src:
     required: false
     default: null
     choices: []
     description:
       - path of the file to link to (applies only to C(state=link)).
   seuser:
     required: false
     default: null
     choices: []
     description:
       - user part of SELinux file context. Will default to system policy, if
         applicable. If set to C(_default), it will use the C(user) portion of the
caf003c8
         policy if available
e620fed7
   serole:
     required: false
     default: null
     choices: []
     description:
       - role part of SELinux file context, C(_default) feature works as for I(seuser).
   setype:
     required: false
     default: null
     choices: []
     description:
       - type part of SELinux file context, C(_default) feature works as for I(seuser).
   selevel:
     required: false
     default: "s0"
     choices: []
     description:
       - level part of the SELinux file context. This is the MLS/MCS attribute,
         sometimes known as the C(range). C(_default) feature works as for
         I(seuser).
   context:
     required: false
     default: null
     choices: [ "default" ]
83f277cf
     description:
e620fed7
       - accepts only C(default) as value. This will restore a file's SELinux context
         in the policy. Does nothing if no default value is available.
60e04102
 examples:
feab57e2
    - code: "file: path=/etc/foo.conf owner=foo group=foo mode=0644"
60e04102
      description: Example from Ansible Playbooks
feab57e2
    - code: "file: src=/file/to/link/to dest=/path/to/symlink owner=foo group=foo state=link"
60e04102
 notes:
     - See also M(copy), M(template), M(assemble)
 requirements: [ ]
e620fed7
 author: Michael DeHaan
60e04102
 '''
 
ce5f3dd1
 def main():
 
477ca2ed
     # FIXME: pass this around, should not use global
ce5f3dd1
     global module
477ca2ed
 
ed14312a
     module = AnsibleModule(
         check_invalid_arguments = False,
ce5f3dd1
         argument_spec = dict(
             state = dict(choices=['file','directory','link','absent'], default='file'),
             path  = dict(aliases=['dest', 'name'], required=True),
cbc12f0d
         ),
         add_file_common_args=True
ce5f3dd1
     )
 
     params = module.params
     state  = params['state']
     path   = os.path.expanduser(params['path'])
c5d2f6b0
 
     # source is both the source of a symlink or an informational passing of the src for a template module
     # or copy module, even if this module never uses it, it is needed to key off some things
 
cbc12f0d
     src = params.get('src', None)
ce5f3dd1
     if src:
         src = os.path.expanduser(src)
 
702469f7
     if src is not None and os.path.isdir(path) and state != "link":
c5d2f6b0
         params['path'] = path = os.path.join(path, os.path.basename(src))
 
cbc12f0d
     file_args = module.load_file_common_arguments(params)
ce5f3dd1
 
     if state == 'link' and (src is None or path is None):
cbc12f0d
         module.fail_json(msg='src and dest are required for "link" state')
ce5f3dd1
     elif path is None:
cbc12f0d
         module.fail_json(msg='path is required')
ce5f3dd1
 
     changed = False
 
     prev_state = 'absent'
c5d2f6b0
 
ce5f3dd1
     if os.path.lexists(path):
         if os.path.islink(path):
             prev_state = 'link'
9d4f70f0
         elif os.path.isdir(path):
ce5f3dd1
             prev_state = 'directory'
9d4f70f0
         else:
             prev_state = 'file'
be55145a
 
ce5f3dd1
     if prev_state != 'absent' and state == 'absent':
         try:
             if prev_state == 'directory':
                 if os.path.islink(path):
                     os.unlink(path)
                 else:
cbc12f0d
                     try:
                         shutil.rmtree(path, ignore_errors=False)
                     except:
                         module.exit_json(msg="rmtree failed")
ce5f3dd1
             else:
                 os.unlink(path)
         except Exception, e:
cbc12f0d
             module.fail_json(path=path, msg=str(e))
         module.exit_json(path=path, changed=True)
be55145a
 
75837041
     if prev_state != 'absent' and prev_state != state:
cbc12f0d
         module.fail_json(path=path, msg='refusing to convert between %s and %s for %s' % (prev_state, state, src))
be55145a
 
ce5f3dd1
     if prev_state == 'absent' and state == 'absent':
cbc12f0d
         module.exit_json(path=path, changed=False)
be55145a
 
ce5f3dd1
     if state == 'file':
faed4b5a
 
eefe66f1
         if prev_state != 'file':
cbc12f0d
             module.fail_json(path=path, msg='file (%s) does not exist, use copy or template module to create' % path) 
be55145a
 
cbc12f0d
         changed = module.set_file_attributes_if_different(file_args, changed)
         module.exit_json(path=path, changed=changed)
be55145a
 
ce5f3dd1
     elif state == 'directory':
be55145a
 
ce5f3dd1
         if prev_state == 'absent':
             os.makedirs(path)
             changed = True
faed4b5a
 
cbc12f0d
         changed = module.set_directory_attributes_if_different(file_args, changed)
         module.exit_json(path=path, changed=changed)
be55145a
 
ce5f3dd1
     elif state == 'link':
faed4b5a
 
ce5f3dd1
         if os.path.isabs(src):
             abs_src = src
         else:
9e934acf
             module.fail_json(msg="absolute paths are required")
ce5f3dd1
         if not os.path.exists(abs_src):
cbc12f0d
             module.fail_json(path=path, src=src, msg='src file does not exist')
faed4b5a
 
ce5f3dd1
         if prev_state == 'absent':
             os.symlink(src, path)
             changed = True
         elif prev_state == 'link':
             old_src = os.readlink(path)
             if not os.path.isabs(old_src):
                 old_src = os.path.join(os.path.dirname(path), old_src)
             if old_src != src:
                 os.unlink(path)
                 os.symlink(src, path)
eefe66f1
                 changed = True
ce5f3dd1
         else:
cbc12f0d
             module.fail_json(dest=path, src=src, msg='unexpected position reached')
ce5f3dd1
 
         # set modes owners and context as needed
cbc12f0d
 
         file_args = module.load_file_common_arguments(module.params)
         changed = module.set_context_if_different(path, file_args['secontext'], changed)
         changed = module.set_owner_if_different(path, file_args['owner'], changed)
         changed = module.set_group_if_different(path, file_args['group'], changed)
         changed = module.set_mode_if_different(path, file_args['mode'], changed)
24f61f15
 
ce5f3dd1
         module.exit_json(dest=path, src=src, changed=changed)
24f61f15
 
cbc12f0d
     module.fail_json(path=path, msg='unexpected position reached')
24f61f15
 
ce5f3dd1
 # this is magic, see lib/ansible/module_common.py
 #<<INCLUDE_ANSIBLE_MODULE_COMMON>>
0a3ebdb6
 main()
be55145a