Repository: ansible-collections/community.general

Issue:
parted: unexpected partition table wipe
<!--- 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 -->
If the disk label is unspecified and actual label is different from msdos, then parted module wipes the partition table.

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
parted

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.9.9
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/ei-grad/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.8/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.3 (default, May 17 2020, 18:15:42) [GCC 10.1.0]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below

```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->


##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
Execute the following task on disk with gpt label:
<!--- Paste example playbooks or commands between quotes below -->
```yaml
  - name: create partition for lvm on each nvme
    parted:
      device: "/dev/nvme1n1"
      number: 3
      part_start: 197257216s
      part_end: 100%
      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 -->
New partition is created.

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
The partition table is wiped.
<!--- Paste verbatim command output between quotes -->
```paste below
failed: [myhostname] (item=/dev/nvme1n1) => changed=false 
  ansible_loop_var: item
  err: |-
    Error: Partition(s) 2 on /dev/nvme1n1 have been written, but we have been unable to inform the kernel of the change, probably because it/they are in use.  As a result, the old partition(s) will remain in use.  You should reboot now before making further changes.
  item: /dev/nvme1n1
  msg: 'Error while running parted script: /sbin/parted -s -m -a optimal /dev/nvme1n1 -- unit KiB mklabel msdos mkpart primary 197257216s 100%'

```

parted: unexpected partition table wipe
<!--- 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 -->
If the disk label is unspecified and actual label is different from msdos, then parted module wipes the partition table.

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
parted

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.9.9
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/ei-grad/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.8/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.3 (default, May 17 2020, 18:15:42) [GCC 10.1.0]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below

```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->


##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
Execute the following task on disk with gpt label:
<!--- Paste example playbooks or commands between quotes below -->
```yaml
  - name: create partition for lvm on each nvme
    parted:
      device: "/dev/nvme1n1"
      number: 3
      part_start: 197257216s
      part_end: 100%
      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 -->
New partition is created.

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
The partition table is wiped.
<!--- Paste verbatim command output between quotes -->
```paste below
failed: [myhostname] (item=/dev/nvme1n1) => changed=false 
  ansible_loop_var: item
  err: |-
    Error: Partition(s) 2 on /dev/nvme1n1 have been written, but we have been unable to inform the kernel of the change, probably because it/they are in use.  As a result, the old partition(s) will remain in use.  You should reboot now before making further changes.
  item: /dev/nvme1n1
  msg: 'Error while running parted script: /sbin/parted -s -m -a optimal /dev/nvme1n1 -- unit KiB mklabel msdos mkpart primary 197257216s 100%'

```

parted: unexpected partition table wipe
<!--- 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 -->
If the disk label is unspecified and actual label is different from msdos, then parted module wipes the partition table.

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
parted

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.9.9
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/ei-grad/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.8/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.3 (default, May 17 2020, 18:15:42) [GCC 10.1.0]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below

```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->


##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
Execute the following task on disk with gpt label:
<!--- Paste example playbooks or commands between quotes below -->
```yaml
  - name: create partition for lvm on each nvme
    parted:
      device: "/dev/nvme1n1"
      number: 3
      part_start: 197257216s
      part_end: 100%
      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 -->
New partition is created.

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
The partition table is wiped.
<!--- Paste verbatim command output between quotes -->
```paste below
failed: [myhostname] (item=/dev/nvme1n1) => changed=false 
  ansible_loop_var: item
  err: |-
    Error: Partition(s) 2 on /dev/nvme1n1 have been written, but we have been unable to inform the kernel of the change, probably because it/they are in use.  As a result, the old partition(s) will remain in use.  You should reboot now before making further changes.
  item: /dev/nvme1n1
  msg: 'Error while running parted script: /sbin/parted -s -m -a optimal /dev/nvme1n1 -- unit KiB mklabel msdos mkpart primary 197257216s 100%'

```



Discussion:
Files identified in the description:
* [`plugins/modules/system/parted.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/system/parted.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @ColOfAbRiX @MorrisA @bcoca @d-little @flynn1973 @gforster @kairoaraujo @marvin-sinister @mator @molekuul @ramooncamacho @wtcross
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I'm sorry for loss of your partitions (I hope you haven't lost any important data!)

The parted module always inserts mklabel command when there's no label or current label is different than specified, see https://github.com/ansible-collections/community.general/blob/f16a93f976e0379b4d405f2bd9031d8eb95fa367/plugins/modules/system/parted.py#L615

You didn't specify any label type, but 'msdos' is default value for this parameter -- hence the confusion. This is expected, but documentation ("Creates a new disk label) may suggest module takes action only if there is no partition label and leaves current label when unspecified. 

I suggest documentation change (PR will come soon), something like: 'Disk label type to be created. If device contains different label, any previous partitions will be lost'.

I use parted for GPT partitions exclusively, so I've added the following code:

```yaml
 - name: get partiton info
   parted:
      device: /dev/{{device_name}}
      unit: "{{part.unit}}"
   register: parted_call
  
 - name: check GPT label
   fail: msg="GPT table required"
   when: parted_call.disk.table not in ['gpt', 'unknown']
```

Note: behaviour expected by @ei-grad (that is: don't change existing label type) would require dropping default setting for label parameter, which would break backward compatibility. It also may be achieved by reference to registered partition info like: `label: {{parted_call.disk.table}}`.
There's another side of this issue report: parted program is unable to complete script generated by parted module when partition table is used by kernel. In this issue: only 'mklabel msdos' step was executed by parted and actual partition creation was not executed. I proposed possible workaround for this behaviour in #252.
@ei-grad  or anyone else: please reopen this issue for a while, I found out documentation fix is not enough, PR is coming.
Files identified in the description:
* [`plugins/modules/system/parted.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/system/parted.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @ColOfAbRiX @MorrisA @bcoca @d-little @flynn1973 @gforster @kairoaraujo @marvin-sinister @mator @molekuul @ramooncamacho @wtcross
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I'm sorry for loss of your partitions (I hope you haven't lost any important data!)

The parted module always inserts mklabel command when there's no label or current label is different than specified, see https://github.com/ansible-collections/community.general/blob/f16a93f976e0379b4d405f2bd9031d8eb95fa367/plugins/modules/system/parted.py#L615

You didn't specify any label type, but 'msdos' is default value for this parameter -- hence the confusion. This is expected, but documentation ("Creates a new disk label) may suggest module takes action only if there is no partition label and leaves current label when unspecified. 

I suggest documentation change (PR will come soon), something like: 'Disk label type to be created. If device contains different label, any previous partitions will be lost'.

I use parted for GPT partitions exclusively, so I've added the following code:

```yaml
 - name: get partiton info
   parted:
      device: /dev/{{device_name}}
      unit: "{{part.unit}}"
   register: parted_call
  
 - name: check GPT label
   fail: msg="GPT table required"
   when: parted_call.disk.table not in ['gpt', 'unknown']
```

Note: behaviour expected by @ei-grad (that is: don't change existing label type) would require dropping default setting for label parameter, which would break backward compatibility. It also may be achieved by reference to registered partition info like: `label: {{parted_call.disk.table}}`.
There's another side of this issue report: parted program is unable to complete script generated by parted module when partition table is used by kernel. In this issue: only 'mklabel msdos' step was executed by parted and actual partition creation was not executed. I proposed possible workaround for this behaviour in #252.
@ei-grad  or anyone else: please reopen this issue for a while, I found out documentation fix is not enough, PR is coming.
Files identified in the description:
* [`plugins/modules/system/parted.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/system/parted.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @ColOfAbRiX @MorrisA @bcoca @d-little @flynn1973 @gforster @kairoaraujo @marvin-sinister @mator @molekuul @ramooncamacho @wtcross
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I'm sorry for loss of your partitions (I hope you haven't lost any important data!)

The parted module always inserts mklabel command when there's no label or current label is different than specified, see https://github.com/ansible-collections/community.general/blob/f16a93f976e0379b4d405f2bd9031d8eb95fa367/plugins/modules/system/parted.py#L615

You didn't specify any label type, but 'msdos' is default value for this parameter -- hence the confusion. This is expected, but documentation ("Creates a new disk label) may suggest module takes action only if there is no partition label and leaves current label when unspecified. 

I suggest documentation change (PR will come soon), something like: 'Disk label type to be created. If device contains different label, any previous partitions will be lost'.

I use parted for GPT partitions exclusively, so I've added the following code:

```yaml
 - name: get partiton info
   parted:
      device: /dev/{{device_name}}
      unit: "{{part.unit}}"
   register: parted_call
  
 - name: check GPT label
   fail: msg="GPT table required"
   when: parted_call.disk.table not in ['gpt', 'unknown']
```

Note: behaviour expected by @ei-grad (that is: don't change existing label type) would require dropping default setting for label parameter, which would break backward compatibility. It also may be achieved by reference to registered partition info like: `label: {{parted_call.disk.table}}`.
There's another side of this issue report: parted program is unable to complete script generated by parted module when partition table is used by kernel. In this issue: only 'mklabel msdos' step was executed by parted and actual partition creation was not executed. I proposed possible workaround for this behaviour in #252.
@ei-grad  or anyone else: please reopen this issue for a while, I found out documentation fix is not enough, PR is coming.

Resolving patch:
diff --git a/plugins/modules/system/parted.py b/plugins/modules/system/parted.py
--- a/plugins/modules/system/parted.py
+++ b/plugins/modules/system/parted.py
@@ -634,11 +634,12 @@ def main():
     if state == 'present':
 
         # Assign label if required
-        if current_device['generic'].get('table', None) != label:
+        mklabel_needed = current_device['generic'].get('table', None) != label
+        if mklabel_needed:
             script += "mklabel %s " % label
 
         # Create partition if required
-        if part_type and not part_exists(current_parts, 'num', number):
+        if part_type and (mklabel_needed or not part_exists(current_parts, 'num', number)):
             script += "mkpart %s %s%s %s " % (
                 part_type,
                 '%s ' % fs_type if fs_type is not None else '',


Test patch:
diff --git a/tests/unit/plugins/modules/system/test_parted.py b/tests/unit/plugins/modules/system/test_parted.py
--- a/tests/unit/plugins/modules/system/test_parted.py
+++ b/tests/unit/plugins/modules/system/test_parted.py
@@ -262,6 +262,19 @@ def test_create_label_gpt(self):
         with patch('ansible_collections.community.general.plugins.modules.system.parted.get_device_info', return_value=parted_dict2):
             self.execute_module(changed=True, script='unit KiB mklabel gpt mkpart primary 0% 100% unit KiB name 1 \'"lvmpartition"\' set 1 lvm on')
 
+    def test_change_label_gpt(self):
+        # When partitions already exists and label is changed, mkpart should be called even when partition already exists,
+        # because new empty label will be created anyway
+        set_module_args({
+            'device': '/dev/sdb',
+            'number': 1,
+            'state': 'present',
+            'label': 'gpt',
+            '_ansible_check_mode': True,
+        })
+        with patch('ansible_collections.community.general.plugins.modules.system.parted.get_device_info', return_value=parted_dict1):
+            self.execute_module(changed=True, script='unit KiB mklabel gpt mkpart primary 0% 100%')
+
     def test_check_mode_unchanged(self):
         # Test that get_device_info result is checked in check mode too
         # No change on partition 1