We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
lvg: undocumented support of physical volume creation
##### Issue Type:

Documentation Report
##### Plugin Name:

lvg
##### Ansible Version:

```
# ansible --version
ansible 1.9.4
  configured module search path = None
```
##### Summary:

lvg module seems to take care of physical volume creation, on top of volume group creation. However, it doesn't appear to be documented.

I noticed I could just use

```
- name: Create LVM volume group
  lvg:
    pvs: /dev/vdc
    state: present
    vg: direct-lvm
```

instead of:

```
- name: Create LVM physical volume
  command: pvcreate -y /dev/vdc

- name: Create LVM volume group
  lvg:
    pvs: /dev/vdc
    state: present
    vg: direct-lvm
```

which isn't idempotent since executing pvcreate a second time will fail.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `system/lvg.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 
4 # (c) 2013, Alexander Bulimov <lazywolf0@gmail.com>
5 # based on lvol module by Jeroen Hoekx <jeroen.hoekx@dsquare.be>
6 #
7 # This file is part of Ansible
8 #
9 # Ansible is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # Ansible is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
21 
22 DOCUMENTATION = '''
23 ---
24 author: "Alexander Bulimov (@abulimov)"
25 module: lvg
26 short_description: Configure LVM volume groups
27 description:
28   - This module creates, removes or resizes volume groups.
29 version_added: "1.1"
30 options:
31   vg:
32     description:
33     - The name of the volume group.
34     required: true
35   pvs:
36     description:
37     - List of comma-separated devices to use as physical devices in this volume group. Required when creating or resizing volume group.
38     required: false
39   pesize:
40     description:
41     - The size of the physical extent in megabytes. Must be a power of 2.
42     default: 4
43     required: false
44   vg_options:
45     description:
46     - Additional options to pass to C(vgcreate) when creating the volume group.
47     default: null
48     required: false
49     version_added: "1.6"
50   state:
51     choices: [ "present", "absent" ]
52     default: present
53     description:
54     - Control if the volume group exists.
55     required: false
56   force:
57     choices: [ "yes", "no" ]
58     default: "no"
59     description:
60     - If yes, allows to remove volume group with logical volumes.
61     required: false
62 notes:
63   - module does not modify PE size for already present volume group
64 '''
65 
66 EXAMPLES = '''
67 # Create a volume group on top of /dev/sda1 with physical extent size = 32MB.
68 - lvg:  vg=vg.services pvs=/dev/sda1 pesize=32
69 
70 # Create or resize a volume group on top of /dev/sdb1 and /dev/sdc5.
71 # If, for example, we already have VG vg.services on top of /dev/sdb1,
72 # this VG will be extended by /dev/sdc5.  Or if vg.services was created on
73 # top of /dev/sda5, we first extend it with /dev/sdb1 and /dev/sdc5,
74 # and then reduce by /dev/sda5.
75 - lvg: vg=vg.services pvs=/dev/sdb1,/dev/sdc5
76 
77 # Remove a volume group with name vg.services.
78 - lvg: vg=vg.services state=absent
79 '''
80 
81 def parse_vgs(data):
82     vgs = []
83     for line in data.splitlines():
84         parts = line.strip().split(';')
85         vgs.append({
86             'name': parts[0],
87             'pv_count': int(parts[1]),
88             'lv_count': int(parts[2]),
89         })
90     return vgs
91 
92 def find_mapper_device_name(module, dm_device):
93         dmsetup_cmd = module.get_bin_path('dmsetup', True)
94         mapper_prefix = '/dev/mapper/'
95         rc, dm_name, err = module.run_command("%s info -C --noheadings -o name %s" % (dmsetup_cmd, dm_device))
96         if rc != 0:
97             module.fail_json(msg="Failed executing dmsetup command.", rc=rc, err=err)
98         mapper_device = mapper_prefix + dm_name.rstrip()
99         return mapper_device
100 
101 def parse_pvs(module, data):
102     pvs = []
103     dm_prefix = '/dev/dm-'
104     for line in data.splitlines():
105         parts = line.strip().split(';')
106         if parts[0].startswith(dm_prefix):
107             parts[0] = find_mapper_device_name(module, parts[0])
108         pvs.append({
109             'name': parts[0],
110             'vg_name': parts[1],
111         })
112     return pvs
113 
114 def main():
115     module = AnsibleModule(
116         argument_spec = dict(
117             vg=dict(required=True),
118             pvs=dict(type='list'),
119             pesize=dict(type='int', default=4),
120             vg_options=dict(default=''),
121             state=dict(choices=["absent", "present"], default='present'),
122             force=dict(type='bool', default='no'),
123         ),
124         supports_check_mode=True,
125     )
126 
127     vg = module.params['vg']
128     state = module.params['state']
129     force = module.boolean(module.params['force'])
130     pesize = module.params['pesize']
131     vgoptions = module.params['vg_options'].split()
132 
133     if module.params['pvs']:
134         dev_list = module.params['pvs']
135     elif state == 'present':
136         module.fail_json(msg="No physical volumes given.")
137 
138     # LVM always uses real paths not symlinks so replace symlinks with actual path
139     for idx, dev in enumerate(dev_list):
140         dev_list[idx] = os.path.realpath(dev)
141 
142     if state=='present':
143         ### check given devices
144         for test_dev in dev_list:
145             if not os.path.exists(test_dev):
146                 module.fail_json(msg="Device %s not found."%test_dev)
147 
148         ### get pv list
149         pvs_cmd = module.get_bin_path('pvs', True)
150         rc,current_pvs,err = module.run_command("%s --noheadings -o pv_name,vg_name --separator ';'" % pvs_cmd)
151         if rc != 0:
152             module.fail_json(msg="Failed executing pvs command.",rc=rc, err=err)
153 
154         ### check pv for devices
155         pvs = parse_pvs(module, current_pvs)
156         used_pvs = [ pv for pv in pvs if pv['name'] in dev_list and pv['vg_name'] and pv['vg_name'] != vg ]
157         if used_pvs:
158             module.fail_json(msg="Device %s is already in %s volume group."%(used_pvs[0]['name'],used_pvs[0]['vg_name']))
159 
160     vgs_cmd = module.get_bin_path('vgs', True)
161     rc,current_vgs,err = module.run_command("%s --noheadings -o vg_name,pv_count,lv_count --separator ';'" % vgs_cmd)
162 
163     if rc != 0:
164         module.fail_json(msg="Failed executing vgs command.",rc=rc, err=err)
165 
166     changed = False
167 
168     vgs = parse_vgs(current_vgs)
169 
170     for test_vg in vgs:
171         if test_vg['name'] == vg:
172             this_vg = test_vg
173             break
174     else:
175         this_vg = None
176 
177     if this_vg is None:
178         if state == 'present':
179             ### create VG
180             if module.check_mode:
181                 changed = True
182             else:
183                 ### create PV
184                 pvcreate_cmd = module.get_bin_path('pvcreate', True)
185                 for current_dev in dev_list:
186                     rc,_,err = module.run_command("%s -f %s" % (pvcreate_cmd,current_dev))
187                     if rc == 0:
188                         changed = True
189                     else:
190                         module.fail_json(msg="Creating physical volume '%s' failed" % current_dev, rc=rc, err=err)
191                 vgcreate_cmd = module.get_bin_path('vgcreate')
192                 rc,_,err = module.run_command([vgcreate_cmd] + vgoptions + ['-s', str(pesize), vg] + dev_list)
193                 if rc == 0:
194                     changed = True
195                 else:
196                     module.fail_json(msg="Creating volume group '%s' failed"%vg, rc=rc, err=err)
197     else:
198         if state == 'absent':
199             if module.check_mode:
200                 module.exit_json(changed=True)
201             else:
202                 if this_vg['lv_count'] == 0 or force:
203                     ### remove VG
204                     vgremove_cmd = module.get_bin_path('vgremove', True)
205                     rc,_,err = module.run_command("%s --force %s" % (vgremove_cmd, vg))
206                     if rc == 0:
207                         module.exit_json(changed=True)
208                     else:
209                         module.fail_json(msg="Failed to remove volume group %s"%(vg),rc=rc, err=err)
210                 else:
211                     module.fail_json(msg="Refuse to remove non-empty volume group %s without force=yes"%(vg))
212 
213         ### resize VG
214         current_devs = [ os.path.realpath(pv['name']) for pv in pvs if pv['vg_name'] == vg ]
215         devs_to_remove = list(set(current_devs) - set(dev_list))
216         devs_to_add = list(set(dev_list) - set(current_devs))
217 
218         if devs_to_add or devs_to_remove:
219             if module.check_mode:
220                 changed = True
221             else:
222                 if devs_to_add:
223                     devs_to_add_string = ' '.join(devs_to_add)
224                     ### create PV
225                     pvcreate_cmd = module.get_bin_path('pvcreate', True)
226                     for current_dev in devs_to_add:
227                         rc,_,err = module.run_command("%s -f %s" % (pvcreate_cmd, current_dev))
228                         if rc == 0:
229                             changed = True
230                         else:
231                             module.fail_json(msg="Creating physical volume '%s' failed"%current_dev, rc=rc, err=err)
232                     ### add PV to our VG
233                     vgextend_cmd = module.get_bin_path('vgextend', True)
234                     rc,_,err = module.run_command("%s %s %s" % (vgextend_cmd, vg, devs_to_add_string))
235                     if rc == 0:
236                         changed = True
237                     else:
238                         module.fail_json(msg="Unable to extend %s by %s."%(vg, devs_to_add_string),rc=rc,err=err)
239 
240                 ### remove some PV from our VG
241                 if devs_to_remove:
242                     devs_to_remove_string = ' '.join(devs_to_remove)
243                     vgreduce_cmd = module.get_bin_path('vgreduce', True)
244                     rc,_,err = module.run_command("%s --force %s %s" % (vgreduce_cmd, vg, devs_to_remove_string))
245                     if rc == 0:
246                         changed = True
247                     else:
248                         module.fail_json(msg="Unable to reduce %s by %s."%(vg, devs_to_remove_string),rc=rc,err=err)
249 
250     module.exit_json(changed=changed)
251 
252 # import module snippets
253 from ansible.module_utils.basic import *
254 main()
255
```
--- 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/system/lvg.py b/system/lvg.py
--- a/system/lvg.py
+++ b/system/lvg.py
@@ -35,6 +35,7 @@
   pvs:
     description:
     - List of comma-separated devices to use as physical devices in this volume group. Required when creating or resizing volume group.
+    - The module will take care of running pvcreate if needed. 
     required: false
   pesize:
     description:

{"golden_diff": "diff --git a/system/lvg.py b/system/lvg.py\n--- a/system/lvg.py\n+++ b/system/lvg.py\n@@ -35,6 +35,7 @@\n   pvs:\n     description:\n     - List of comma-separated devices to use as physical devices in this volume group. Required when creating or resizing volume group.\n+    - The module will take care of running pvcreate if needed. \n     required: false\n   pesize:\n     description:\n", "issue": "lvg: undocumented support of physical volume creation\n##### Issue Type:\n\nDocumentation Report\n##### Plugin Name:\n\nlvg\n##### Ansible Version:\n\n```\n# ansible --version\nansible 1.9.4\n  configured module search path = None\n```\n##### Summary:\n\nlvg module seems to take care of physical volume creation, on top of volume group creation. However, it doesn't appear to be documented.\n\nI noticed I could just use\n\n```\n- name: Create LVM volume group\n  lvg:\n    pvs: /dev/vdc\n    state: present\n    vg: direct-lvm\n```\n\ninstead of:\n\n```\n- name: Create LVM physical volume\n  command: pvcreate -y /dev/vdc\n\n- name: Create LVM volume group\n  lvg:\n    pvs: /dev/vdc\n    state: present\n    vg: direct-lvm\n```\n\nwhich isn't idempotent since executing pvcreate a second time will fail.\n\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# (c) 2013, Alexander Bulimov <lazywolf0@gmail.com>\n# based on lvol module by Jeroen Hoekx <jeroen.hoekx@dsquare.be>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n\nDOCUMENTATION = '''\n---\nauthor: \"Alexander Bulimov (@abulimov)\"\nmodule: lvg\nshort_description: Configure LVM volume groups\ndescription:\n  - This module creates, removes or resizes volume groups.\nversion_added: \"1.1\"\noptions:\n  vg:\n    description:\n    - The name of the volume group.\n    required: true\n  pvs:\n    description:\n    - List of comma-separated devices to use as physical devices in this volume group. Required when creating or resizing volume group.\n    required: false\n  pesize:\n    description:\n    - The size of the physical extent in megabytes. Must be a power of 2.\n    default: 4\n    required: false\n  vg_options:\n    description:\n    - Additional options to pass to C(vgcreate) when creating the volume group.\n    default: null\n    required: false\n    version_added: \"1.6\"\n  state:\n    choices: [ \"present\", \"absent\" ]\n    default: present\n    description:\n    - Control if the volume group exists.\n    required: false\n  force:\n    choices: [ \"yes\", \"no\" ]\n    default: \"no\"\n    description:\n    - If yes, allows to remove volume group with logical volumes.\n    required: false\nnotes:\n  - module does not modify PE size for already present volume group\n'''\n\nEXAMPLES = '''\n# Create a volume group on top of /dev/sda1 with physical extent size = 32MB.\n- lvg:  vg=vg.services pvs=/dev/sda1 pesize=32\n\n# Create or resize a volume group on top of /dev/sdb1 and /dev/sdc5.\n# If, for example, we already have VG vg.services on top of /dev/sdb1,\n# this VG will be extended by /dev/sdc5.  Or if vg.services was created on\n# top of /dev/sda5, we first extend it with /dev/sdb1 and /dev/sdc5,\n# and then reduce by /dev/sda5.\n- lvg: vg=vg.services pvs=/dev/sdb1,/dev/sdc5\n\n# Remove a volume group with name vg.services.\n- lvg: vg=vg.services state=absent\n'''\n\ndef parse_vgs(data):\n    vgs = []\n    for line in data.splitlines():\n        parts = line.strip().split(';')\n        vgs.append({\n            'name': parts[0],\n            'pv_count': int(parts[1]),\n            'lv_count': int(parts[2]),\n        })\n    return vgs\n\ndef find_mapper_device_name(module, dm_device):\n        dmsetup_cmd = module.get_bin_path('dmsetup', True)\n        mapper_prefix = '/dev/mapper/'\n        rc, dm_name, err = module.run_command(\"%s info -C --noheadings -o name %s\" % (dmsetup_cmd, dm_device))\n        if rc != 0:\n            module.fail_json(msg=\"Failed executing dmsetup command.\", rc=rc, err=err)\n        mapper_device = mapper_prefix + dm_name.rstrip()\n        return mapper_device\n\ndef parse_pvs(module, data):\n    pvs = []\n    dm_prefix = '/dev/dm-'\n    for line in data.splitlines():\n        parts = line.strip().split(';')\n        if parts[0].startswith(dm_prefix):\n            parts[0] = find_mapper_device_name(module, parts[0])\n        pvs.append({\n            'name': parts[0],\n            'vg_name': parts[1],\n        })\n    return pvs\n\ndef main():\n    module = AnsibleModule(\n        argument_spec = dict(\n            vg=dict(required=True),\n            pvs=dict(type='list'),\n            pesize=dict(type='int', default=4),\n            vg_options=dict(default=''),\n            state=dict(choices=[\"absent\", \"present\"], default='present'),\n            force=dict(type='bool', default='no'),\n        ),\n        supports_check_mode=True,\n    )\n\n    vg = module.params['vg']\n    state = module.params['state']\n    force = module.boolean(module.params['force'])\n    pesize = module.params['pesize']\n    vgoptions = module.params['vg_options'].split()\n\n    if module.params['pvs']:\n        dev_list = module.params['pvs']\n    elif state == 'present':\n        module.fail_json(msg=\"No physical volumes given.\")\n\n    # LVM always uses real paths not symlinks so replace symlinks with actual path\n    for idx, dev in enumerate(dev_list):\n        dev_list[idx] = os.path.realpath(dev)\n\n    if state=='present':\n        ### check given devices\n        for test_dev in dev_list:\n            if not os.path.exists(test_dev):\n                module.fail_json(msg=\"Device %s not found.\"%test_dev)\n\n        ### get pv list\n        pvs_cmd = module.get_bin_path('pvs', True)\n        rc,current_pvs,err = module.run_command(\"%s --noheadings -o pv_name,vg_name --separator ';'\" % pvs_cmd)\n        if rc != 0:\n            module.fail_json(msg=\"Failed executing pvs command.\",rc=rc, err=err)\n\n        ### check pv for devices\n        pvs = parse_pvs(module, current_pvs)\n        used_pvs = [ pv for pv in pvs if pv['name'] in dev_list and pv['vg_name'] and pv['vg_name'] != vg ]\n        if used_pvs:\n            module.fail_json(msg=\"Device %s is already in %s volume group.\"%(used_pvs[0]['name'],used_pvs[0]['vg_name']))\n\n    vgs_cmd = module.get_bin_path('vgs', True)\n    rc,current_vgs,err = module.run_command(\"%s --noheadings -o vg_name,pv_count,lv_count --separator ';'\" % vgs_cmd)\n\n    if rc != 0:\n        module.fail_json(msg=\"Failed executing vgs command.\",rc=rc, err=err)\n\n    changed = False\n\n    vgs = parse_vgs(current_vgs)\n\n    for test_vg in vgs:\n        if test_vg['name'] == vg:\n            this_vg = test_vg\n            break\n    else:\n        this_vg = None\n\n    if this_vg is None:\n        if state == 'present':\n            ### create VG\n            if module.check_mode:\n                changed = True\n            else:\n                ### create PV\n                pvcreate_cmd = module.get_bin_path('pvcreate', True)\n                for current_dev in dev_list:\n                    rc,_,err = module.run_command(\"%s -f %s\" % (pvcreate_cmd,current_dev))\n                    if rc == 0:\n                        changed = True\n                    else:\n                        module.fail_json(msg=\"Creating physical volume '%s' failed\" % current_dev, rc=rc, err=err)\n                vgcreate_cmd = module.get_bin_path('vgcreate')\n                rc,_,err = module.run_command([vgcreate_cmd] + vgoptions + ['-s', str(pesize), vg] + dev_list)\n                if rc == 0:\n                    changed = True\n                else:\n                    module.fail_json(msg=\"Creating volume group '%s' failed\"%vg, rc=rc, err=err)\n    else:\n        if state == 'absent':\n            if module.check_mode:\n                module.exit_json(changed=True)\n            else:\n                if this_vg['lv_count'] == 0 or force:\n                    ### remove VG\n                    vgremove_cmd = module.get_bin_path('vgremove', True)\n                    rc,_,err = module.run_command(\"%s --force %s\" % (vgremove_cmd, vg))\n                    if rc == 0:\n                        module.exit_json(changed=True)\n                    else:\n                        module.fail_json(msg=\"Failed to remove volume group %s\"%(vg),rc=rc, err=err)\n                else:\n                    module.fail_json(msg=\"Refuse to remove non-empty volume group %s without force=yes\"%(vg))\n\n        ### resize VG\n        current_devs = [ os.path.realpath(pv['name']) for pv in pvs if pv['vg_name'] == vg ]\n        devs_to_remove = list(set(current_devs) - set(dev_list))\n        devs_to_add = list(set(dev_list) - set(current_devs))\n\n        if devs_to_add or devs_to_remove:\n            if module.check_mode:\n                changed = True\n            else:\n                if devs_to_add:\n                    devs_to_add_string = ' '.join(devs_to_add)\n                    ### create PV\n                    pvcreate_cmd = module.get_bin_path('pvcreate', True)\n                    for current_dev in devs_to_add:\n                        rc,_,err = module.run_command(\"%s -f %s\" % (pvcreate_cmd, current_dev))\n                        if rc == 0:\n                            changed = True\n                        else:\n                            module.fail_json(msg=\"Creating physical volume '%s' failed\"%current_dev, rc=rc, err=err)\n                    ### add PV to our VG\n                    vgextend_cmd = module.get_bin_path('vgextend', True)\n                    rc,_,err = module.run_command(\"%s %s %s\" % (vgextend_cmd, vg, devs_to_add_string))\n                    if rc == 0:\n                        changed = True\n                    else:\n                        module.fail_json(msg=\"Unable to extend %s by %s.\"%(vg, devs_to_add_string),rc=rc,err=err)\n\n                ### remove some PV from our VG\n                if devs_to_remove:\n                    devs_to_remove_string = ' '.join(devs_to_remove)\n                    vgreduce_cmd = module.get_bin_path('vgreduce', True)\n                    rc,_,err = module.run_command(\"%s --force %s %s\" % (vgreduce_cmd, vg, devs_to_remove_string))\n                    if rc == 0:\n                        changed = True\n                    else:\n                        module.fail_json(msg=\"Unable to reduce %s by %s.\"%(vg, devs_to_remove_string),rc=rc,err=err)\n\n    module.exit_json(changed=changed)\n\n# import module snippets\nfrom ansible.module_utils.basic import *\nmain()\n", "path": "system/lvg.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# (c) 2013, Alexander Bulimov <lazywolf0@gmail.com>\n# based on lvol module by Jeroen Hoekx <jeroen.hoekx@dsquare.be>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n\nDOCUMENTATION = '''\n---\nauthor: \"Alexander Bulimov (@abulimov)\"\nmodule: lvg\nshort_description: Configure LVM volume groups\ndescription:\n  - This module creates, removes or resizes volume groups.\nversion_added: \"1.1\"\noptions:\n  vg:\n    description:\n    - The name of the volume group.\n    required: true\n  pvs:\n    description:\n    - List of comma-separated devices to use as physical devices in this volume group. Required when creating or resizing volume group.\n    - The module will take care of running pvcreate if needed. \n    required: false\n  pesize:\n    description:\n    - The size of the physical extent in megabytes. Must be a power of 2.\n    default: 4\n    required: false\n  vg_options:\n    description:\n    - Additional options to pass to C(vgcreate) when creating the volume group.\n    default: null\n    required: false\n    version_added: \"1.6\"\n  state:\n    choices: [ \"present\", \"absent\" ]\n    default: present\n    description:\n    - Control if the volume group exists.\n    required: false\n  force:\n    choices: [ \"yes\", \"no\" ]\n    default: \"no\"\n    description:\n    - If yes, allows to remove volume group with logical volumes.\n    required: false\nnotes:\n  - module does not modify PE size for already present volume group\n'''\n\nEXAMPLES = '''\n# Create a volume group on top of /dev/sda1 with physical extent size = 32MB.\n- lvg:  vg=vg.services pvs=/dev/sda1 pesize=32\n\n# Create or resize a volume group on top of /dev/sdb1 and /dev/sdc5.\n# If, for example, we already have VG vg.services on top of /dev/sdb1,\n# this VG will be extended by /dev/sdc5.  Or if vg.services was created on\n# top of /dev/sda5, we first extend it with /dev/sdb1 and /dev/sdc5,\n# and then reduce by /dev/sda5.\n- lvg: vg=vg.services pvs=/dev/sdb1,/dev/sdc5\n\n# Remove a volume group with name vg.services.\n- lvg: vg=vg.services state=absent\n'''\n\ndef parse_vgs(data):\n    vgs = []\n    for line in data.splitlines():\n        parts = line.strip().split(';')\n        vgs.append({\n            'name': parts[0],\n            'pv_count': int(parts[1]),\n            'lv_count': int(parts[2]),\n        })\n    return vgs\n\ndef find_mapper_device_name(module, dm_device):\n        dmsetup_cmd = module.get_bin_path('dmsetup', True)\n        mapper_prefix = '/dev/mapper/'\n        rc, dm_name, err = module.run_command(\"%s info -C --noheadings -o name %s\" % (dmsetup_cmd, dm_device))\n        if rc != 0:\n            module.fail_json(msg=\"Failed executing dmsetup command.\", rc=rc, err=err)\n        mapper_device = mapper_prefix + dm_name.rstrip()\n        return mapper_device\n\ndef parse_pvs(module, data):\n    pvs = []\n    dm_prefix = '/dev/dm-'\n    for line in data.splitlines():\n        parts = line.strip().split(';')\n        if parts[0].startswith(dm_prefix):\n            parts[0] = find_mapper_device_name(module, parts[0])\n        pvs.append({\n            'name': parts[0],\n            'vg_name': parts[1],\n        })\n    return pvs\n\ndef main():\n    module = AnsibleModule(\n        argument_spec = dict(\n            vg=dict(required=True),\n            pvs=dict(type='list'),\n            pesize=dict(type='int', default=4),\n            vg_options=dict(default=''),\n            state=dict(choices=[\"absent\", \"present\"], default='present'),\n            force=dict(type='bool', default='no'),\n        ),\n        supports_check_mode=True,\n    )\n\n    vg = module.params['vg']\n    state = module.params['state']\n    force = module.boolean(module.params['force'])\n    pesize = module.params['pesize']\n    vgoptions = module.params['vg_options'].split()\n\n    if module.params['pvs']:\n        dev_list = module.params['pvs']\n    elif state == 'present':\n        module.fail_json(msg=\"No physical volumes given.\")\n\n    # LVM always uses real paths not symlinks so replace symlinks with actual path\n    for idx, dev in enumerate(dev_list):\n        dev_list[idx] = os.path.realpath(dev)\n\n    if state=='present':\n        ### check given devices\n        for test_dev in dev_list:\n            if not os.path.exists(test_dev):\n                module.fail_json(msg=\"Device %s not found.\"%test_dev)\n\n        ### get pv list\n        pvs_cmd = module.get_bin_path('pvs', True)\n        rc,current_pvs,err = module.run_command(\"%s --noheadings -o pv_name,vg_name --separator ';'\" % pvs_cmd)\n        if rc != 0:\n            module.fail_json(msg=\"Failed executing pvs command.\",rc=rc, err=err)\n\n        ### check pv for devices\n        pvs = parse_pvs(module, current_pvs)\n        used_pvs = [ pv for pv in pvs if pv['name'] in dev_list and pv['vg_name'] and pv['vg_name'] != vg ]\n        if used_pvs:\n            module.fail_json(msg=\"Device %s is already in %s volume group.\"%(used_pvs[0]['name'],used_pvs[0]['vg_name']))\n\n    vgs_cmd = module.get_bin_path('vgs', True)\n    rc,current_vgs,err = module.run_command(\"%s --noheadings -o vg_name,pv_count,lv_count --separator ';'\" % vgs_cmd)\n\n    if rc != 0:\n        module.fail_json(msg=\"Failed executing vgs command.\",rc=rc, err=err)\n\n    changed = False\n\n    vgs = parse_vgs(current_vgs)\n\n    for test_vg in vgs:\n        if test_vg['name'] == vg:\n            this_vg = test_vg\n            break\n    else:\n        this_vg = None\n\n    if this_vg is None:\n        if state == 'present':\n            ### create VG\n            if module.check_mode:\n                changed = True\n            else:\n                ### create PV\n                pvcreate_cmd = module.get_bin_path('pvcreate', True)\n                for current_dev in dev_list:\n                    rc,_,err = module.run_command(\"%s -f %s\" % (pvcreate_cmd,current_dev))\n                    if rc == 0:\n                        changed = True\n                    else:\n                        module.fail_json(msg=\"Creating physical volume '%s' failed\" % current_dev, rc=rc, err=err)\n                vgcreate_cmd = module.get_bin_path('vgcreate')\n                rc,_,err = module.run_command([vgcreate_cmd] + vgoptions + ['-s', str(pesize), vg] + dev_list)\n                if rc == 0:\n                    changed = True\n                else:\n                    module.fail_json(msg=\"Creating volume group '%s' failed\"%vg, rc=rc, err=err)\n    else:\n        if state == 'absent':\n            if module.check_mode:\n                module.exit_json(changed=True)\n            else:\n                if this_vg['lv_count'] == 0 or force:\n                    ### remove VG\n                    vgremove_cmd = module.get_bin_path('vgremove', True)\n                    rc,_,err = module.run_command(\"%s --force %s\" % (vgremove_cmd, vg))\n                    if rc == 0:\n                        module.exit_json(changed=True)\n                    else:\n                        module.fail_json(msg=\"Failed to remove volume group %s\"%(vg),rc=rc, err=err)\n                else:\n                    module.fail_json(msg=\"Refuse to remove non-empty volume group %s without force=yes\"%(vg))\n\n        ### resize VG\n        current_devs = [ os.path.realpath(pv['name']) for pv in pvs if pv['vg_name'] == vg ]\n        devs_to_remove = list(set(current_devs) - set(dev_list))\n        devs_to_add = list(set(dev_list) - set(current_devs))\n\n        if devs_to_add or devs_to_remove:\n            if module.check_mode:\n                changed = True\n            else:\n                if devs_to_add:\n                    devs_to_add_string = ' '.join(devs_to_add)\n                    ### create PV\n                    pvcreate_cmd = module.get_bin_path('pvcreate', True)\n                    for current_dev in devs_to_add:\n                        rc,_,err = module.run_command(\"%s -f %s\" % (pvcreate_cmd, current_dev))\n                        if rc == 0:\n                            changed = True\n                        else:\n                            module.fail_json(msg=\"Creating physical volume '%s' failed\"%current_dev, rc=rc, err=err)\n                    ### add PV to our VG\n                    vgextend_cmd = module.get_bin_path('vgextend', True)\n                    rc,_,err = module.run_command(\"%s %s %s\" % (vgextend_cmd, vg, devs_to_add_string))\n                    if rc == 0:\n                        changed = True\n                    else:\n                        module.fail_json(msg=\"Unable to extend %s by %s.\"%(vg, devs_to_add_string),rc=rc,err=err)\n\n                ### remove some PV from our VG\n                if devs_to_remove:\n                    devs_to_remove_string = ' '.join(devs_to_remove)\n                    vgreduce_cmd = module.get_bin_path('vgreduce', True)\n                    rc,_,err = module.run_command(\"%s --force %s %s\" % (vgreduce_cmd, vg, devs_to_remove_string))\n                    if rc == 0:\n                        changed = True\n                    else:\n                        module.fail_json(msg=\"Unable to reduce %s by %s.\"%(vg, devs_to_remove_string),rc=rc,err=err)\n\n    module.exit_json(changed=changed)\n\n# import module snippets\nfrom ansible.module_utils.basic import *\nmain()\n", "path": "system/lvg.py"}]}