We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ZFS Snapshot fails on root zvol
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Also test if the latest release and devel branch are affected too -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Explain the problem briefly below -->
When trying to create a ZFS snapshot of the root zvol, using: 
```yaml
zfs:
  name: "hobraid2@daily_{{ ansible_date_time.date }}"
  state: present
```
it fails with error `"cannot open 'hobraid2@daily_2020-09-18': invalid character '@' in pool name"`
##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
zfs
##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.9.13
  config file = /mnt/hob-raid/docker/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.7.6 (default, Dec 19 2019, 22:52:49) [GCC 9.2.1 20190827 (Red Hat 9.2.1-1)]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below
DEFAULT_HOST_LIST(/mnt/hob-raid/docker/ansible/ansible.cfg) = ['/mnt/hob-raid/docker/ansible/inventory.yml']
HOST_KEY_CHECKING(/mnt/hob-raid/docker/ansible/ansible.cfg) = False
```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
Fedora 31

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
1 - Create A root zpool, in my case hobraid2
```
# zpool list
NAME       SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
hobraid2  3.62T  1.33T  2.30T        -         -     3%    36%  1.00x    ONLINE  -
```
2 - attempt to use the zfs module in ansible to create a snapshot, e.g:
```yaml
zfs:
  name: "hobraid2@daily_{{ ansible_date_time.date }}"
  state: present
```
<!--- Paste example playbooks or commands between quotes below -->
```yaml
zfs:
  name: "hobraid2@daily_{{ ansible_date_time.date }}"
  state: present
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
The snapshot is created without error

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
`"cannot open 'hobraid2@daily_2020-09-18': invalid character '@' in pool name"`
<!--- Paste verbatim command output between quotes -->
```paste below
fatal: [hob-ssv]: FAILED! => {
    "changed": false,
    "cmd": "/usr/sbin/zpool get version ''",
    "invocation": {
        "module_args": {
            "extra_zfs_properties": {},
            "name": "hobraid2@daily_2020-09-18",
            "origin": null,
            "state": "present"
        }
    },
    "msg": "cannot open 'hobraid2@daily_2020-09-18':********@' in pool name",
    "rc": 1,
    "stderr": "cannot open 'hobraid2@daily_2020-09-18': invalid character '@' in pool name\n",
    "stderr_lines": [
        "cannot open 'hobraid2@daily_2020-09-18': invalid character '@' in pool name"
    ],
    "stdout": "",
    "stdout_lines": []
}
```

It would appear that this line in the ZFS module gets the pool name by separating it out using split('/'), however when running commands on the root zvol, there is no /, causing the full name to be passed into zpool version generating the error.

https://github.com/ansible-collections/community.general/blob/51121e54d02e663fa26599cbebb667bec666ce37/plugins/modules/storage/zfs/zfs.py#L93

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `plugins/modules/storage/zfs/zfs.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 
4 # Copyright: (c) 2013, Johan Wiren <johan.wiren.se@gmail.com>
5 # Copyright: (c) 2017, Ansible Project
6 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
7 
8 from __future__ import absolute_import, division, print_function
9 __metaclass__ = type
10 
11 DOCUMENTATION = '''
12 ---
13 module: zfs
14 short_description: Manage zfs
15 description:
16   - Manages ZFS file systems, volumes, clones and snapshots
17 options:
18   name:
19     description:
20       - File system, snapshot or volume name e.g. C(rpool/myfs).
21     required: true
22   state:
23     description:
24       - Whether to create (C(present)), or remove (C(absent)) a
25         file system, snapshot or volume. All parents/children
26         will be created/destroyed as needed to reach the desired state.
27     choices: [ absent, present ]
28     required: true
29   origin:
30     description:
31       - Snapshot from which to create a clone.
32   extra_zfs_properties:
33     description:
34       - A dictionary of zfs properties to be set.
35       - See the zfs(8) man page for more information.
36 author:
37 - Johan Wiren (@johanwiren)
38 '''
39 
40 EXAMPLES = '''
41 - name: Create a new file system called myfs in pool rpool with the setuid property turned off
42   community.general.zfs:
43     name: rpool/myfs
44     state: present
45     extra_zfs_properties:
46       setuid: off
47 
48 - name: Create a new volume called myvol in pool rpool.
49   community.general.zfs:
50     name: rpool/myvol
51     state: present
52     extra_zfs_properties:
53       volsize: 10M
54 
55 - name: Create a snapshot of rpool/myfs file system.
56   community.general.zfs:
57     name: rpool/myfs@mysnapshot
58     state: present
59 
60 - name: Create a new file system called myfs2 with snapdir enabled
61   community.general.zfs:
62     name: rpool/myfs2
63     state: present
64     extra_zfs_properties:
65       snapdir: enabled
66 
67 - name: Create a new file system by cloning a snapshot
68   community.general.zfs:
69     name: rpool/cloned_fs
70     state: present
71     origin: rpool/myfs@mysnapshot
72 
73 - name: Destroy a filesystem
74   community.general.zfs:
75     name: rpool/myfs
76     state: absent
77 '''
78 
79 import os
80 
81 from ansible.module_utils.basic import AnsibleModule
82 
83 
84 class Zfs(object):
85 
86     def __init__(self, module, name, properties):
87         self.module = module
88         self.name = name
89         self.properties = properties
90         self.changed = False
91         self.zfs_cmd = module.get_bin_path('zfs', True)
92         self.zpool_cmd = module.get_bin_path('zpool', True)
93         self.pool = name.split('/')[0]
94         self.is_solaris = os.uname()[0] == 'SunOS'
95         self.is_openzfs = self.check_openzfs()
96         self.enhanced_sharing = self.check_enhanced_sharing()
97 
98     def check_openzfs(self):
99         cmd = [self.zpool_cmd]
100         cmd.extend(['get', 'version'])
101         cmd.append(self.pool)
102         (rc, out, err) = self.module.run_command(cmd, check_rc=True)
103         version = out.splitlines()[-1].split()[2]
104         if version == '-':
105             return True
106         if int(version) == 5000:
107             return True
108         return False
109 
110     def check_enhanced_sharing(self):
111         if self.is_solaris and not self.is_openzfs:
112             cmd = [self.zpool_cmd]
113             cmd.extend(['get', 'version'])
114             cmd.append(self.pool)
115             (rc, out, err) = self.module.run_command(cmd, check_rc=True)
116             version = out.splitlines()[-1].split()[2]
117             if int(version) >= 34:
118                 return True
119         return False
120 
121     def exists(self):
122         cmd = [self.zfs_cmd, 'list', '-t', 'all', self.name]
123         (rc, out, err) = self.module.run_command(' '.join(cmd))
124         if rc == 0:
125             return True
126         else:
127             return False
128 
129     def create(self):
130         if self.module.check_mode:
131             self.changed = True
132             return
133         properties = self.properties
134         origin = self.module.params.get('origin', None)
135         cmd = [self.zfs_cmd]
136 
137         if "@" in self.name:
138             action = 'snapshot'
139         elif origin:
140             action = 'clone'
141         else:
142             action = 'create'
143 
144         cmd.append(action)
145 
146         if action in ['create', 'clone']:
147             cmd += ['-p']
148 
149         if properties:
150             for prop, value in properties.items():
151                 if prop == 'volsize':
152                     cmd += ['-V', value]
153                 elif prop == 'volblocksize':
154                     cmd += ['-b', value]
155                 else:
156                     cmd += ['-o', '%s="%s"' % (prop, value)]
157         if origin and action == 'clone':
158             cmd.append(origin)
159         cmd.append(self.name)
160         (rc, out, err) = self.module.run_command(' '.join(cmd))
161         if rc == 0:
162             self.changed = True
163         else:
164             self.module.fail_json(msg=err)
165 
166     def destroy(self):
167         if self.module.check_mode:
168             self.changed = True
169             return
170         cmd = [self.zfs_cmd, 'destroy', '-R', self.name]
171         (rc, out, err) = self.module.run_command(' '.join(cmd))
172         if rc == 0:
173             self.changed = True
174         else:
175             self.module.fail_json(msg=err)
176 
177     def set_property(self, prop, value):
178         if self.module.check_mode:
179             self.changed = True
180             return
181         cmd = [self.zfs_cmd, 'set', prop + '=' + str(value), self.name]
182         (rc, out, err) = self.module.run_command(cmd)
183         if rc == 0:
184             self.changed = True
185         else:
186             self.module.fail_json(msg=err)
187 
188     def set_properties_if_changed(self):
189         current_properties = self.get_current_properties()
190         for prop, value in self.properties.items():
191             if current_properties.get(prop, None) != value:
192                 self.set_property(prop, value)
193 
194     def get_current_properties(self):
195         cmd = [self.zfs_cmd, 'get', '-H']
196         if self.enhanced_sharing:
197             cmd += ['-e']
198         cmd += ['all', self.name]
199         rc, out, err = self.module.run_command(" ".join(cmd))
200         properties = dict()
201         for prop, value, source in [l.split('\t')[1:4] for l in out.splitlines()]:
202             if source == 'local':
203                 properties[prop] = value
204         # Add alias for enhanced sharing properties
205         if self.enhanced_sharing:
206             properties['sharenfs'] = properties.get('share.nfs', None)
207             properties['sharesmb'] = properties.get('share.smb', None)
208         return properties
209 
210 
211 def main():
212 
213     module = AnsibleModule(
214         argument_spec=dict(
215             name=dict(type='str', required=True),
216             state=dict(type='str', required=True, choices=['absent', 'present']),
217             origin=dict(type='str', default=None),
218             extra_zfs_properties=dict(type='dict', default={}),
219         ),
220         supports_check_mode=True,
221     )
222 
223     state = module.params.get('state')
224     name = module.params.get('name')
225 
226     if module.params.get('origin') and '@' in name:
227         module.fail_json(msg='cannot specify origin when operating on a snapshot')
228 
229     # Reverse the boolification of zfs properties
230     for prop, value in module.params['extra_zfs_properties'].items():
231         if isinstance(value, bool):
232             if value is True:
233                 module.params['extra_zfs_properties'][prop] = 'on'
234             else:
235                 module.params['extra_zfs_properties'][prop] = 'off'
236         else:
237             module.params['extra_zfs_properties'][prop] = value
238 
239     result = dict(
240         name=name,
241         state=state,
242     )
243 
244     zfs = Zfs(module, name, module.params['extra_zfs_properties'])
245 
246     if state == 'present':
247         if zfs.exists():
248             zfs.set_properties_if_changed()
249         else:
250             zfs.create()
251 
252     elif state == 'absent':
253         if zfs.exists():
254             zfs.destroy()
255 
256     result.update(zfs.properties)
257     result['changed'] = zfs.changed
258     module.exit_json(**result)
259 
260 
261 if __name__ == '__main__':
262     main()
263
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/plugins/modules/storage/zfs/zfs.py b/plugins/modules/storage/zfs/zfs.py
--- a/plugins/modules/storage/zfs/zfs.py
+++ b/plugins/modules/storage/zfs/zfs.py
@@ -90,7 +90,7 @@
         self.changed = False
         self.zfs_cmd = module.get_bin_path('zfs', True)
         self.zpool_cmd = module.get_bin_path('zpool', True)
-        self.pool = name.split('/')[0]
+        self.pool = name.split('/')[0].split('@')[0]
         self.is_solaris = os.uname()[0] == 'SunOS'
         self.is_openzfs = self.check_openzfs()
         self.enhanced_sharing = self.check_enhanced_sharing()

{"golden_diff": "diff --git a/plugins/modules/storage/zfs/zfs.py b/plugins/modules/storage/zfs/zfs.py\n--- a/plugins/modules/storage/zfs/zfs.py\n+++ b/plugins/modules/storage/zfs/zfs.py\n@@ -90,7 +90,7 @@\n         self.changed = False\n         self.zfs_cmd = module.get_bin_path('zfs', True)\n         self.zpool_cmd = module.get_bin_path('zpool', True)\n-        self.pool = name.split('/')[0]\n+        self.pool = name.split('/')[0].split('@')[0]\n         self.is_solaris = os.uname()[0] == 'SunOS'\n         self.is_openzfs = self.check_openzfs()\n         self.enhanced_sharing = self.check_enhanced_sharing()\n", "issue": "ZFS Snapshot fails on root zvol\n<!--- Verify first that your issue is not already reported on GitHub -->\r\n<!--- Also test if the latest release and devel branch are affected too -->\r\n<!--- Complete *all* sections as described, this form is processed automatically -->\r\n\r\n##### SUMMARY\r\n<!--- Explain the problem briefly below -->\r\nWhen trying to create a ZFS snapshot of the root zvol, using: \r\n```yaml\r\nzfs:\r\n  name: \"hobraid2@daily_{{ ansible_date_time.date }}\"\r\n  state: present\r\n```\r\nit fails with error `\"cannot open 'hobraid2@daily_2020-09-18': invalid character '@' in pool name\"`\r\n##### ISSUE TYPE\r\n- Bug Report\r\n\r\n##### COMPONENT NAME\r\n<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->\r\nzfs\r\n##### ANSIBLE VERSION\r\n<!--- Paste verbatim output from \"ansible --version\" between quotes -->\r\n```paste below\r\nansible 2.9.13\r\n  config file = /mnt/hob-raid/docker/ansible/ansible.cfg\r\n  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /usr/lib/python3.7/site-packages/ansible\r\n  executable location = /usr/bin/ansible\r\n  python version = 3.7.6 (default, Dec 19 2019, 22:52:49) [GCC 9.2.1 20190827 (Red Hat 9.2.1-1)]\r\n```\r\n\r\n##### CONFIGURATION\r\n<!--- Paste verbatim output from \"ansible-config dump --only-changed\" between quotes -->\r\n```paste below\r\nDEFAULT_HOST_LIST(/mnt/hob-raid/docker/ansible/ansible.cfg) = ['/mnt/hob-raid/docker/ansible/inventory.yml']\r\nHOST_KEY_CHECKING(/mnt/hob-raid/docker/ansible/ansible.cfg) = False\r\n```\r\n\r\n##### OS / ENVIRONMENT\r\n<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->\r\nFedora 31\r\n\r\n##### STEPS TO REPRODUCE\r\n<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->\r\n1 - Create A root zpool, in my case hobraid2\r\n```\r\n# zpool list\r\nNAME       SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT\r\nhobraid2  3.62T  1.33T  2.30T        -         -     3%    36%  1.00x    ONLINE  -\r\n```\r\n2 - attempt to use the zfs module in ansible to create a snapshot, e.g:\r\n```yaml\r\nzfs:\r\n  name: \"hobraid2@daily_{{ ansible_date_time.date }}\"\r\n  state: present\r\n```\r\n<!--- Paste example playbooks or commands between quotes below -->\r\n```yaml\r\nzfs:\r\n  name: \"hobraid2@daily_{{ ansible_date_time.date }}\"\r\n  state: present\r\n```\r\n\r\n<!--- HINT: You can paste gist.github.com links for larger files -->\r\n\r\n##### EXPECTED RESULTS\r\n<!--- Describe what you expected to happen when running the steps above -->\r\nThe snapshot is created without error\r\n\r\n##### ACTUAL RESULTS\r\n<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->\r\n`\"cannot open 'hobraid2@daily_2020-09-18': invalid character '@' in pool name\"`\r\n<!--- Paste verbatim command output between quotes -->\r\n```paste below\r\nfatal: [hob-ssv]: FAILED! => {\r\n    \"changed\": false,\r\n    \"cmd\": \"/usr/sbin/zpool get version ''\",\r\n    \"invocation\": {\r\n        \"module_args\": {\r\n            \"extra_zfs_properties\": {},\r\n            \"name\": \"hobraid2@daily_2020-09-18\",\r\n            \"origin\": null,\r\n            \"state\": \"present\"\r\n        }\r\n    },\r\n    \"msg\": \"cannot open 'hobraid2@daily_2020-09-18':********@' in pool name\",\r\n    \"rc\": 1,\r\n    \"stderr\": \"cannot open 'hobraid2@daily_2020-09-18': invalid character '@' in pool name\\n\",\r\n    \"stderr_lines\": [\r\n        \"cannot open 'hobraid2@daily_2020-09-18': invalid character '@' in pool name\"\r\n    ],\r\n    \"stdout\": \"\",\r\n    \"stdout_lines\": []\r\n}\r\n```\r\n\r\nIt would appear that this line in the ZFS module gets the pool name by separating it out using split('/'), however when running commands on the root zvol, there is no /, causing the full name to be passed into zpool version generating the error.\r\n\r\nhttps://github.com/ansible-collections/community.general/blob/51121e54d02e663fa26599cbebb667bec666ce37/plugins/modules/storage/zfs/zfs.py#L93\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# Copyright: (c) 2013, Johan Wiren <johan.wiren.se@gmail.com>\n# Copyright: (c) 2017, Ansible Project\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\nDOCUMENTATION = '''\n---\nmodule: zfs\nshort_description: Manage zfs\ndescription:\n  - Manages ZFS file systems, volumes, clones and snapshots\noptions:\n  name:\n    description:\n      - File system, snapshot or volume name e.g. C(rpool/myfs).\n    required: true\n  state:\n    description:\n      - Whether to create (C(present)), or remove (C(absent)) a\n        file system, snapshot or volume. All parents/children\n        will be created/destroyed as needed to reach the desired state.\n    choices: [ absent, present ]\n    required: true\n  origin:\n    description:\n      - Snapshot from which to create a clone.\n  extra_zfs_properties:\n    description:\n      - A dictionary of zfs properties to be set.\n      - See the zfs(8) man page for more information.\nauthor:\n- Johan Wiren (@johanwiren)\n'''\n\nEXAMPLES = '''\n- name: Create a new file system called myfs in pool rpool with the setuid property turned off\n  community.general.zfs:\n    name: rpool/myfs\n    state: present\n    extra_zfs_properties:\n      setuid: off\n\n- name: Create a new volume called myvol in pool rpool.\n  community.general.zfs:\n    name: rpool/myvol\n    state: present\n    extra_zfs_properties:\n      volsize: 10M\n\n- name: Create a snapshot of rpool/myfs file system.\n  community.general.zfs:\n    name: rpool/myfs@mysnapshot\n    state: present\n\n- name: Create a new file system called myfs2 with snapdir enabled\n  community.general.zfs:\n    name: rpool/myfs2\n    state: present\n    extra_zfs_properties:\n      snapdir: enabled\n\n- name: Create a new file system by cloning a snapshot\n  community.general.zfs:\n    name: rpool/cloned_fs\n    state: present\n    origin: rpool/myfs@mysnapshot\n\n- name: Destroy a filesystem\n  community.general.zfs:\n    name: rpool/myfs\n    state: absent\n'''\n\nimport os\n\nfrom ansible.module_utils.basic import AnsibleModule\n\n\nclass Zfs(object):\n\n    def __init__(self, module, name, properties):\n        self.module = module\n        self.name = name\n        self.properties = properties\n        self.changed = False\n        self.zfs_cmd = module.get_bin_path('zfs', True)\n        self.zpool_cmd = module.get_bin_path('zpool', True)\n        self.pool = name.split('/')[0]\n        self.is_solaris = os.uname()[0] == 'SunOS'\n        self.is_openzfs = self.check_openzfs()\n        self.enhanced_sharing = self.check_enhanced_sharing()\n\n    def check_openzfs(self):\n        cmd = [self.zpool_cmd]\n        cmd.extend(['get', 'version'])\n        cmd.append(self.pool)\n        (rc, out, err) = self.module.run_command(cmd, check_rc=True)\n        version = out.splitlines()[-1].split()[2]\n        if version == '-':\n            return True\n        if int(version) == 5000:\n            return True\n        return False\n\n    def check_enhanced_sharing(self):\n        if self.is_solaris and not self.is_openzfs:\n            cmd = [self.zpool_cmd]\n            cmd.extend(['get', 'version'])\n            cmd.append(self.pool)\n            (rc, out, err) = self.module.run_command(cmd, check_rc=True)\n            version = out.splitlines()[-1].split()[2]\n            if int(version) >= 34:\n                return True\n        return False\n\n    def exists(self):\n        cmd = [self.zfs_cmd, 'list', '-t', 'all', self.name]\n        (rc, out, err) = self.module.run_command(' '.join(cmd))\n        if rc == 0:\n            return True\n        else:\n            return False\n\n    def create(self):\n        if self.module.check_mode:\n            self.changed = True\n            return\n        properties = self.properties\n        origin = self.module.params.get('origin', None)\n        cmd = [self.zfs_cmd]\n\n        if \"@\" in self.name:\n            action = 'snapshot'\n        elif origin:\n            action = 'clone'\n        else:\n            action = 'create'\n\n        cmd.append(action)\n\n        if action in ['create', 'clone']:\n            cmd += ['-p']\n\n        if properties:\n            for prop, value in properties.items():\n                if prop == 'volsize':\n                    cmd += ['-V', value]\n                elif prop == 'volblocksize':\n                    cmd += ['-b', value]\n                else:\n                    cmd += ['-o', '%s=\"%s\"' % (prop, value)]\n        if origin and action == 'clone':\n            cmd.append(origin)\n        cmd.append(self.name)\n        (rc, out, err) = self.module.run_command(' '.join(cmd))\n        if rc == 0:\n            self.changed = True\n        else:\n            self.module.fail_json(msg=err)\n\n    def destroy(self):\n        if self.module.check_mode:\n            self.changed = True\n            return\n        cmd = [self.zfs_cmd, 'destroy', '-R', self.name]\n        (rc, out, err) = self.module.run_command(' '.join(cmd))\n        if rc == 0:\n            self.changed = True\n        else:\n            self.module.fail_json(msg=err)\n\n    def set_property(self, prop, value):\n        if self.module.check_mode:\n            self.changed = True\n            return\n        cmd = [self.zfs_cmd, 'set', prop + '=' + str(value), self.name]\n        (rc, out, err) = self.module.run_command(cmd)\n        if rc == 0:\n            self.changed = True\n        else:\n            self.module.fail_json(msg=err)\n\n    def set_properties_if_changed(self):\n        current_properties = self.get_current_properties()\n        for prop, value in self.properties.items():\n            if current_properties.get(prop, None) != value:\n                self.set_property(prop, value)\n\n    def get_current_properties(self):\n        cmd = [self.zfs_cmd, 'get', '-H']\n        if self.enhanced_sharing:\n            cmd += ['-e']\n        cmd += ['all', self.name]\n        rc, out, err = self.module.run_command(\" \".join(cmd))\n        properties = dict()\n        for prop, value, source in [l.split('\\t')[1:4] for l in out.splitlines()]:\n            if source == 'local':\n                properties[prop] = value\n        # Add alias for enhanced sharing properties\n        if self.enhanced_sharing:\n            properties['sharenfs'] = properties.get('share.nfs', None)\n            properties['sharesmb'] = properties.get('share.smb', None)\n        return properties\n\n\ndef main():\n\n    module = AnsibleModule(\n        argument_spec=dict(\n            name=dict(type='str', required=True),\n            state=dict(type='str', required=True, choices=['absent', 'present']),\n            origin=dict(type='str', default=None),\n            extra_zfs_properties=dict(type='dict', default={}),\n        ),\n        supports_check_mode=True,\n    )\n\n    state = module.params.get('state')\n    name = module.params.get('name')\n\n    if module.params.get('origin') and '@' in name:\n        module.fail_json(msg='cannot specify origin when operating on a snapshot')\n\n    # Reverse the boolification of zfs properties\n    for prop, value in module.params['extra_zfs_properties'].items():\n        if isinstance(value, bool):\n            if value is True:\n                module.params['extra_zfs_properties'][prop] = 'on'\n            else:\n                module.params['extra_zfs_properties'][prop] = 'off'\n        else:\n            module.params['extra_zfs_properties'][prop] = value\n\n    result = dict(\n        name=name,\n        state=state,\n    )\n\n    zfs = Zfs(module, name, module.params['extra_zfs_properties'])\n\n    if state == 'present':\n        if zfs.exists():\n            zfs.set_properties_if_changed()\n        else:\n            zfs.create()\n\n    elif state == 'absent':\n        if zfs.exists():\n            zfs.destroy()\n\n    result.update(zfs.properties)\n    result['changed'] = zfs.changed\n    module.exit_json(**result)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/storage/zfs/zfs.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# Copyright: (c) 2013, Johan Wiren <johan.wiren.se@gmail.com>\n# Copyright: (c) 2017, Ansible Project\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\nDOCUMENTATION = '''\n---\nmodule: zfs\nshort_description: Manage zfs\ndescription:\n  - Manages ZFS file systems, volumes, clones and snapshots\noptions:\n  name:\n    description:\n      - File system, snapshot or volume name e.g. C(rpool/myfs).\n    required: true\n  state:\n    description:\n      - Whether to create (C(present)), or remove (C(absent)) a\n        file system, snapshot or volume. All parents/children\n        will be created/destroyed as needed to reach the desired state.\n    choices: [ absent, present ]\n    required: true\n  origin:\n    description:\n      - Snapshot from which to create a clone.\n  extra_zfs_properties:\n    description:\n      - A dictionary of zfs properties to be set.\n      - See the zfs(8) man page for more information.\nauthor:\n- Johan Wiren (@johanwiren)\n'''\n\nEXAMPLES = '''\n- name: Create a new file system called myfs in pool rpool with the setuid property turned off\n  community.general.zfs:\n    name: rpool/myfs\n    state: present\n    extra_zfs_properties:\n      setuid: off\n\n- name: Create a new volume called myvol in pool rpool.\n  community.general.zfs:\n    name: rpool/myvol\n    state: present\n    extra_zfs_properties:\n      volsize: 10M\n\n- name: Create a snapshot of rpool/myfs file system.\n  community.general.zfs:\n    name: rpool/myfs@mysnapshot\n    state: present\n\n- name: Create a new file system called myfs2 with snapdir enabled\n  community.general.zfs:\n    name: rpool/myfs2\n    state: present\n    extra_zfs_properties:\n      snapdir: enabled\n\n- name: Create a new file system by cloning a snapshot\n  community.general.zfs:\n    name: rpool/cloned_fs\n    state: present\n    origin: rpool/myfs@mysnapshot\n\n- name: Destroy a filesystem\n  community.general.zfs:\n    name: rpool/myfs\n    state: absent\n'''\n\nimport os\n\nfrom ansible.module_utils.basic import AnsibleModule\n\n\nclass Zfs(object):\n\n    def __init__(self, module, name, properties):\n        self.module = module\n        self.name = name\n        self.properties = properties\n        self.changed = False\n        self.zfs_cmd = module.get_bin_path('zfs', True)\n        self.zpool_cmd = module.get_bin_path('zpool', True)\n        self.pool = name.split('/')[0].split('@')[0]\n        self.is_solaris = os.uname()[0] == 'SunOS'\n        self.is_openzfs = self.check_openzfs()\n        self.enhanced_sharing = self.check_enhanced_sharing()\n\n    def check_openzfs(self):\n        cmd = [self.zpool_cmd]\n        cmd.extend(['get', 'version'])\n        cmd.append(self.pool)\n        (rc, out, err) = self.module.run_command(cmd, check_rc=True)\n        version = out.splitlines()[-1].split()[2]\n        if version == '-':\n            return True\n        if int(version) == 5000:\n            return True\n        return False\n\n    def check_enhanced_sharing(self):\n        if self.is_solaris and not self.is_openzfs:\n            cmd = [self.zpool_cmd]\n            cmd.extend(['get', 'version'])\n            cmd.append(self.pool)\n            (rc, out, err) = self.module.run_command(cmd, check_rc=True)\n            version = out.splitlines()[-1].split()[2]\n            if int(version) >= 34:\n                return True\n        return False\n\n    def exists(self):\n        cmd = [self.zfs_cmd, 'list', '-t', 'all', self.name]\n        (rc, out, err) = self.module.run_command(' '.join(cmd))\n        if rc == 0:\n            return True\n        else:\n            return False\n\n    def create(self):\n        if self.module.check_mode:\n            self.changed = True\n            return\n        properties = self.properties\n        origin = self.module.params.get('origin', None)\n        cmd = [self.zfs_cmd]\n\n        if \"@\" in self.name:\n            action = 'snapshot'\n        elif origin:\n            action = 'clone'\n        else:\n            action = 'create'\n\n        cmd.append(action)\n\n        if action in ['create', 'clone']:\n            cmd += ['-p']\n\n        if properties:\n            for prop, value in properties.items():\n                if prop == 'volsize':\n                    cmd += ['-V', value]\n                elif prop == 'volblocksize':\n                    cmd += ['-b', value]\n                else:\n                    cmd += ['-o', '%s=\"%s\"' % (prop, value)]\n        if origin and action == 'clone':\n            cmd.append(origin)\n        cmd.append(self.name)\n        (rc, out, err) = self.module.run_command(' '.join(cmd))\n        if rc == 0:\n            self.changed = True\n        else:\n            self.module.fail_json(msg=err)\n\n    def destroy(self):\n        if self.module.check_mode:\n            self.changed = True\n            return\n        cmd = [self.zfs_cmd, 'destroy', '-R', self.name]\n        (rc, out, err) = self.module.run_command(' '.join(cmd))\n        if rc == 0:\n            self.changed = True\n        else:\n            self.module.fail_json(msg=err)\n\n    def set_property(self, prop, value):\n        if self.module.check_mode:\n            self.changed = True\n            return\n        cmd = [self.zfs_cmd, 'set', prop + '=' + str(value), self.name]\n        (rc, out, err) = self.module.run_command(cmd)\n        if rc == 0:\n            self.changed = True\n        else:\n            self.module.fail_json(msg=err)\n\n    def set_properties_if_changed(self):\n        current_properties = self.get_current_properties()\n        for prop, value in self.properties.items():\n            if current_properties.get(prop, None) != value:\n                self.set_property(prop, value)\n\n    def get_current_properties(self):\n        cmd = [self.zfs_cmd, 'get', '-H']\n        if self.enhanced_sharing:\n            cmd += ['-e']\n        cmd += ['all', self.name]\n        rc, out, err = self.module.run_command(\" \".join(cmd))\n        properties = dict()\n        for prop, value, source in [l.split('\\t')[1:4] for l in out.splitlines()]:\n            if source == 'local':\n                properties[prop] = value\n        # Add alias for enhanced sharing properties\n        if self.enhanced_sharing:\n            properties['sharenfs'] = properties.get('share.nfs', None)\n            properties['sharesmb'] = properties.get('share.smb', None)\n        return properties\n\n\ndef main():\n\n    module = AnsibleModule(\n        argument_spec=dict(\n            name=dict(type='str', required=True),\n            state=dict(type='str', required=True, choices=['absent', 'present']),\n            origin=dict(type='str', default=None),\n            extra_zfs_properties=dict(type='dict', default={}),\n        ),\n        supports_check_mode=True,\n    )\n\n    state = module.params.get('state')\n    name = module.params.get('name')\n\n    if module.params.get('origin') and '@' in name:\n        module.fail_json(msg='cannot specify origin when operating on a snapshot')\n\n    # Reverse the boolification of zfs properties\n    for prop, value in module.params['extra_zfs_properties'].items():\n        if isinstance(value, bool):\n            if value is True:\n                module.params['extra_zfs_properties'][prop] = 'on'\n            else:\n                module.params['extra_zfs_properties'][prop] = 'off'\n        else:\n            module.params['extra_zfs_properties'][prop] = value\n\n    result = dict(\n        name=name,\n        state=state,\n    )\n\n    zfs = Zfs(module, name, module.params['extra_zfs_properties'])\n\n    if state == 'present':\n        if zfs.exists():\n            zfs.set_properties_if_changed()\n        else:\n            zfs.create()\n\n    elif state == 'absent':\n        if zfs.exists():\n            zfs.destroy()\n\n    result.update(zfs.properties)\n    result['changed'] = zfs.changed\n    module.exit_json(**result)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/storage/zfs/zfs.py"}]}