Repository: ansible/ansible-modules-core

Issue:
Mount module does not accept spaces in mount point path
##### Issue Type:

Bug Report
##### Ansible Version:

ansible 1.9 (devel 27d2539fcd) last updated 2014/12/16 17:34:16 (GMT +200)
  lib/ansible/modules/core: (detached HEAD 08c5cc06c6) last updated 2014/12/16 17:34:20 (GMT +200)
  lib/ansible/modules/extras: (detached HEAD 317654dba5) last updated 2014/12/16 17:34:24 (GMT +200)
  v2/ansible/modules/core: (detached HEAD cb69744bce) last updated 2014/12/16 17:34:30 (GMT +200)
  v2/ansible/modules/extras: (detached HEAD 8a4f07eecd) last updated 2014/12/16 17:34:34 (GMT +200)
  configured module search path = None
##### Environment:

Control host and remote host both Ubuntu 14.04
##### Summary:

I'm trying to have a volume mounted inside VMare Workstation Server's sharde vm path, which contains spaces : `/var/lib/vmware/Shared VMs/`
##### Steps To Reproduce:

The offending task is: 

```
- name: Le système de fichiers doit être monté dans le dossier VMWare
  mount: name="/var/lib/vmware/Shared VMs/{{ inventory_hostname }}"
         src=/dev/drbd/by-res/{{ inventory_hostname }}
         passno=2
         fstype=ext4
         state=mounted
         opts=noauto
  delegate_to: "{{ master }}"
```
##### Expected Results:

Ansible should accept spaces and take the necessary steps to escape them in the updated fstab.
The usual way to represent spaces in paths in fstab is to replace them with `\040`.
##### Actual Results:

```
<vmhost9> ESTABLISH CONNECTION FOR USER: root
<vmhost9> REMOTE_MODULE mount name="/var/lib/vmware/Shared VMs/vmls" src=/dev/drbd/by-res/vmls passno=2 fstype=ext4 state=mounted opts=noauto
<vmhost9> EXEC ssh -C -vvv -o ControlMaster=auto -o ControlPersist=60s -o ForwardAgent=yes -o ControlPath="/home/ls/.ansible/cp/ansible-ssh-%h-%p-%r" -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 vmhost9 /bin/sh -c 'LANG=fr LC_CTYPE=fr /usr/bin/python'
failed: [vmls -> vmhost9] => {"failed": true}
msg: Error mounting /var/lib/vmware/Shared VMs/vmls: [mntent]: line 13 in /etc/fstab is bad
mount: can't find /var/lib/vmware/Shared VMs/vmls in /etc/fstab or /etc/mtab
```

Contents of fstab on delegate host:

```
/dev/drbd/by-res/vmls /var/lib/vmware/Shared VMs/vmls ext4 noauto 0 2
```



Discussion:

Resolving patch:
diff --git a/system/mount.py b/system/mount.py
--- a/system/mount.py
+++ b/system/mount.py
@@ -102,6 +102,10 @@ def write_fstab(lines, dest):
     fs_w.flush()
     fs_w.close()
 
+def _escape_fstab(v):
+    """ escape space (040), ampersand (046) and backslash (134) which are invalid in fstab fields """
+    return v.replace('\\', '\\134').replace(' ', '\\040').replace('&', '\\046')
+
 def set_mount(**kwargs):
     """ set/change a mount point location in fstab """
 
@@ -119,6 +123,7 @@ def set_mount(**kwargs):
     to_write = []
     exists = False
     changed = False
+    escaped_args = dict([(k, _escape_fstab(v)) for k, v in args.iteritems()])
     for line in open(args['fstab'], 'r').readlines():
         if not line.strip():
             to_write.append(line)
@@ -135,16 +140,16 @@ def set_mount(**kwargs):
         ld = {}
         ld['src'], ld['name'], ld['fstype'], ld['opts'], ld['dump'], ld['passno']  = line.split()
 
-        if ld['name'] != args['name']:
+        if ld['name'] != escaped_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]:
+            if ld[t] != escaped_args[t]:
                 changed = True
-                ld[t] = args[t]
+                ld[t] = escaped_args[t]
 
         if changed:
             to_write.append(new_line % ld)
@@ -175,6 +180,7 @@ def unset_mount(**kwargs):
 
     to_write = []
     changed = False
+    escaped_name = _escape_fstab(args['name'])
     for line in open(args['fstab'], 'r').readlines():
         if not line.strip():
             to_write.append(line)
@@ -191,7 +197,7 @@ def unset_mount(**kwargs):
         ld = {}
         ld['src'], ld['name'], ld['fstype'], ld['opts'], ld['dump'], ld['passno']  = line.split()
 
-        if ld['name'] != args['name']:
+        if ld['name'] != escaped_name:
             to_write.append(line)
             continue
 
@@ -260,8 +266,6 @@ def main():
         args['passno'] = module.params['passno']
     if module.params['opts'] is not None:
         args['opts'] = module.params['opts']
-        if ' ' in args['opts']:
-            module.fail_json(msg="unexpected space in 'opts' parameter")
     if module.params['dump'] is not None:
         args['dump'] = module.params['dump']
     if module.params['fstab'] is not None:


Test patch: