mount 9.06 KB
Newer Older
1
#!/usr/bin/python
2
# -*- coding: utf-8 -*-
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

# (c) 2012, Red Hat, inc
# Written by Seth Vidal
# based on the mount modules from salt and puppet
#
# 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/>.

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
DOCUMENTATION = '''
---
module: mount
short_description: Control active and configured mount points
description:
     - This module controls active and configured mount points in C(/etc/fstab).
version_added: "0.6"
options:
  name:
    description:
      - "path to the mount point, eg: C(/mnt/files)"
    required: true
    default: null
    aliases: []
  src:
    description:
      - device to be mounted on I(name).
    required: true
    default: null
  fstype:
    description:
      - file-system type
    required: true
    default: null
  opts:
    description:
      - mount options (see fstab(8))
    required: false
    default: null
  dump:
    description:
      - dump (see fstab(8))
    required: false
    default: null
  passno:
    description:
      - passno (see fstab(8))
    required: false
    default: null
  state:
    description:
      - If C(mounted) or C(unmounted), the device will be actively mounted or unmounted
        as well as just configured in I(fstab). C(absent) and C(present) only deal with
66 67 68
        I(fstab). C(mounted) will also automatically create the mount point
        directory if it doesn't exist. If C(absent) changes anything, it will
        remove the mount point directory.
69 70 71
    required: true
    choices: [ "present", "absent", "mounted", "unmounted" ]
    default: null
72
     
73 74 75 76
notes: []
requirements: []
author: Seth Vidal
'''
77 78 79 80 81 82 83 84 85 86
EXAMPLES = '''
# Mount DVD read-only
- mount: name=/mnt/dvd src=/dev/sr0 fstype=iso9660 opts=ro state=present

# Mount up device by label
- mount: name=/srv/disk src='LABEL=SOME_LABEL' state=present

# Mount up device by UUID
- mount: name=/home src='UUID=b3e48f45-f933-4c8e-a700-22a159ec9077' opts=noatime state=present
'''
87

88 89 90 91 92 93 94 95 96 97 98

def write_fstab(lines, dest):

    fs_w = open(dest, 'w')
    for l in lines:
        fs_w.write(l)

    fs_w.flush()
    fs_w.close()

def set_mount(**kwargs):
99 100
    """ set/change a mount point location in fstab """

101
    # kwargs: name, src, fstype, opts, dump, passno, state, fstab=/etc/fstab
102 103 104 105 106 107
    args = dict(
        opts   = 'defaults',
        dump   = '0',
        passno = '0',
        fstab  = '/etc/fstab'
    )
108 109
    args.update(kwargs)

110
    new_line = '%(src)s %(name)s %(fstype)s %(opts)s %(dump)s %(passno)s\n'
111 112 113 114 115 116

    to_write = []
    exists = False
    changed = False
    for line in open(args['fstab'], 'r').readlines():
        if not line.strip():
117 118
            to_write.append(line)
            continue
119 120 121 122 123 124 125 126
        if line.strip().startswith('#'):
            to_write.append(line)
            continue
        if len(line.split()) != 6:
            # not sure what this is or why it is here
            # but it is not our fault so leave it be
            to_write.append(line)
            continue
127

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
        ld = {}
        ld['src'], ld['name'], ld['fstype'], ld['opts'], ld['dump'], ld['passno']  = line.split()

        if ld['name'] != args['name']:
            to_write.append(line)
            continue

        # it exists - now see if what we have is different
        exists = True
        for t in ('src', 'fstype','opts', 'dump', 'passno'):
            if ld[t] != args[t]:
                changed = True
                ld[t] = args[t]

        if changed:
143
            to_write.append(new_line % ld)
144
        else:
145
            to_write.append(line)
146

147 148 149
    if not exists:
        to_write.append(new_line % args)
        changed = True
150

151 152 153 154
    if changed:
        write_fstab(to_write, args['fstab'])

    return (args['name'], changed)
155

156 157

def unset_mount(**kwargs):
158 159
    """ remove a mount point from fstab """

160
    # kwargs: name, src, fstype, opts, dump, passno, state, fstab=/etc/fstab
161 162 163 164 165 166
    args = dict(
        opts   = 'default',
        dump   = '0',
        passno = '0',
        fstab  = '/etc/fstab'
    )
167 168 169 170 171 172
    args.update(kwargs)

    to_write = []
    changed = False
    for line in open(args['fstab'], 'r').readlines():
        if not line.strip():
173 174
            to_write.append(line)
            continue
175 176 177 178 179 180 181 182
        if line.strip().startswith('#'):
            to_write.append(line)
            continue
        if len(line.split()) != 6:
            # not sure what this is or why it is here
            # but it is not our fault so leave it be
            to_write.append(line)
            continue
183

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
        ld = {}
        ld['src'], ld['name'], ld['fstype'], ld['opts'], ld['dump'], ld['passno']  = line.split()

        if ld['name'] != args['name']:
            to_write.append(line)
            continue

        # if we got here we found a match - continue and mark changed
        changed = True

    if changed:
        write_fstab(to_write, args['fstab'])

    return (args['name'], changed)

199

200
def mount(module, **kwargs):
201
    """ mount up a path or remount if needed """
202
    mount_bin = module.get_bin_path('mount')
203

204 205
    name = kwargs['name']
    if os.path.ismount(name):
206
        cmd = [ mount_bin , '-o', 'remount', name ]
207
    else:
208
        cmd = [ mount_bin, name ]
209

210 211
    rc, out, err = module.run_command(cmd)
    if rc == 0:
212 213
        return 0, ''
    else:
214
        return rc, out+err
215

216
def umount(module, **kwargs):
217 218
    """ unmount a path """

219
    umount_bin = module.get_bin_path('umount')
220
    name = kwargs['name']
221
    cmd = [umount_bin, name]
222

223 224
    rc, out, err = module.run_command(cmd)
    if rc == 0:
225 226
        return 0, ''
    else:
227
        return rc, out+err
228 229

def main():
230

231 232 233 234 235 236 237 238 239 240 241 242
    module = AnsibleModule(
        argument_spec = dict(
            state  = dict(required=True, choices=['present', 'absent', 'mounted', 'unmounted']),
            name   = dict(required=True),
            opts   = dict(default=None),
            passno = dict(default=None),
            dump   = dict(default=None),
            src    = dict(required=True),
            fstype = dict(required=True),
            fstab  = dict(default=None)
        )
    )
243

244

245 246 247 248 249 250 251 252 253 254 255
    changed = False
    rc = 0
    args = {
        'name': module.params['name'],
        'src': module.params['src'],
        'fstype': module.params['fstype']
    }
    if module.params['passno'] is not None:
        args['passno'] = module.params['passno']
    if module.params['opts'] is not None:
        args['opts'] = module.params['opts']
256 257
        if ' ' in args['opts']:
            module.fail_json(msg="unexpected space in 'opts' parameter")
258 259 260 261
    if module.params['dump'] is not None:
        args['dump'] = module.params['dump']
    if module.params['fstab'] is not None:
        args['fstab'] = module.params['fstab']
262

263 264 265 266
    # absent == remove from fstab and unmounted
    # unmounted == do not change fstab state, but unmount
    # present == add to fstab, do not change mount state
    # mounted == add to fstab if not there and make sure it is mounted, if it has changed in fstab then remount it
267

268 269 270 271 272 273
    state = module.params['state']
    name  = module.params['name']
    if state == 'absent':
        name, changed = unset_mount(**args)
        if changed:
            if os.path.ismount(name):
274
                res,msg  = umount(module, **args)
275
                if res:
276
                    module.fail_json(msg="Error unmounting %s: %s" % (name, msg))
277

278 279 280 281
            if os.path.exists(name):
                try:
                    os.rmdir(name)
                except (OSError, IOError), e:
282
                    module.fail_json(msg="Error rmdir %s: %s" % (name, str(e)))
283

284
        module.exit_json(changed=changed, **args)
285

286
    if state == 'unmounted':
287
        if os.path.ismount(name):
288
            res,msg  = umount(module, **args)
289
            if res:
290
                module.fail_json(msg="Error unmounting %s: %s" % (name, msg))
291
            changed = True
292

293
        module.exit_json(changed=changed, **args)
294

295 296 297 298 299 300 301
    if state in ['mounted', 'present']:
        name, changed = set_mount(**args)
        if state == 'mounted':
            if not os.path.exists(name):
                try:
                    os.makedirs(name)
                except (OSError, IOError), e:
302
                    module.fail_json(msg="Error making dir %s: %s" % (name, str(e)))
303

304 305 306
            res = 0
            if os.path.ismount(name):
                if changed:
307
                    res,msg = mount(module, **args)
308 309
            else:
                changed = True
310
                res,msg = mount(module, **args)
311

312
            if res:
313
                module.fail_json(msg="Error mounting %s: %s" % (name, msg))
314 315


316
        module.exit_json(changed=changed, **args)
317

318 319
    module.fail_json(msg='Unexpected position reached')
    sys.exit(0)
320

321
# import module snippets
322
from ansible.module_utils.basic import *
323
main()