mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-06-10 18:15:39 +00:00
When resizefs=true but the logical volume is already at the requested size, run fsadm resize to ensure the filesystem fills the device. Previously resizefs only took effect when the LV size changed
701 lines
25 KiB
Python
701 lines
25 KiB
Python
#!/usr/bin/python
|
|
|
|
# Copyright (c) 2013, Jeroen Hoekx <jeroen.hoekx@dsquare.be>, Alexander Bulimov <lazywolf0@gmail.com>
|
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from __future__ import annotations
|
|
|
|
DOCUMENTATION = r"""
|
|
author:
|
|
- Jeroen Hoekx (@jhoekx)
|
|
- Alexander Bulimov (@abulimov)
|
|
- Raoul Baudach (@unkaputtbar112)
|
|
- Ziga Kern (@zigaSRC)
|
|
module: lvol
|
|
short_description: Configure LVM logical volumes
|
|
description:
|
|
- This module creates, removes or resizes logical volumes.
|
|
extends_documentation_fragment:
|
|
- community.general._attributes
|
|
attributes:
|
|
check_mode:
|
|
support: full
|
|
diff_mode:
|
|
support: none
|
|
options:
|
|
vg:
|
|
type: str
|
|
required: true
|
|
description:
|
|
- The volume group this logical volume is part of.
|
|
lv:
|
|
type: str
|
|
description:
|
|
- The name of the logical volume.
|
|
size:
|
|
type: str
|
|
description:
|
|
- The size of the logical volume, according to lvcreate(8) C(--size), by default in megabytes or optionally with one
|
|
of [bBsSkKmMgGtTpPeE] units; or according to lvcreate(8) C(--extents) as a percentage of [VG|PVS|FREE|ORIGIN]; Float
|
|
values must begin with a digit.
|
|
- When resizing, apart from specifying an absolute size you may, according to lvextend(8)|lvreduce(8) C(--size), specify
|
|
the amount to extend the logical volume with the prefix V(+) or the amount to reduce the logical volume by with prefix
|
|
V(-).
|
|
- Resizing using V(+) or V(-) was not supported prior to community.general 3.0.0.
|
|
- Please note that when using V(+), V(-), or percentage of FREE, the module is B(not idempotent).
|
|
state:
|
|
type: str
|
|
description:
|
|
- Control if the logical volume exists. If V(present) and the volume does not already exist then the O(size) option
|
|
is required.
|
|
choices: [absent, present]
|
|
default: present
|
|
active:
|
|
description:
|
|
- Whether the volume is active and visible to the host.
|
|
type: bool
|
|
default: true
|
|
force:
|
|
description:
|
|
- Shrink or remove operations of volumes requires this switch. Ensures that filesystems never get corrupted/destroyed
|
|
by mistake.
|
|
type: bool
|
|
default: false
|
|
opts:
|
|
type: str
|
|
description:
|
|
- Free-form options to be passed to the lvcreate command.
|
|
snapshot:
|
|
type: str
|
|
description:
|
|
- The name of a snapshot volume to be configured. When creating a snapshot volume, the O(lv) parameter specifies the
|
|
origin volume.
|
|
pvs:
|
|
type: list
|
|
elements: str
|
|
description:
|
|
- List of physical volumes (for example V(/dev/sda, /dev/sdb)).
|
|
thinpool:
|
|
type: str
|
|
description:
|
|
- The thin pool volume name. When you want to create a thin provisioned volume, specify a thin pool volume name.
|
|
shrink:
|
|
description:
|
|
- Shrink if current size is higher than size requested.
|
|
type: bool
|
|
default: true
|
|
resizefs:
|
|
description:
|
|
- Resize the underlying filesystem together with the logical volume.
|
|
- Supported for C(ext2), C(ext3), C(ext4), C(reiserfs) and C(XFS) filesystems. Attempts to resize other filesystem types
|
|
result in failure.
|
|
- When the logical volume already has the requested size, the filesystem will still be resized to fill the logical volume
|
|
if needed.
|
|
type: bool
|
|
default: false
|
|
notes:
|
|
- You must specify lv (when managing the state of logical volumes) or thinpool (when managing a thin provisioned volume).
|
|
"""
|
|
|
|
EXAMPLES = r"""
|
|
- name: Create a logical volume of 512m
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 512
|
|
|
|
- name: Create a logical volume of 512m with disks /dev/sda and /dev/sdb
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 512
|
|
pvs:
|
|
- /dev/sda
|
|
- /dev/sdb
|
|
|
|
- name: Create cache pool logical volume
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: lvcache
|
|
size: 512m
|
|
opts: --type cache-pool
|
|
|
|
- name: Create a logical volume of 512g.
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 512g
|
|
|
|
- name: Create a logical volume the size of all remaining space in the volume group
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 100%FREE
|
|
|
|
- name: Create a logical volume with special options
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 512g
|
|
opts: -r 16
|
|
|
|
- name: Extend the logical volume to 1024m.
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 1024
|
|
|
|
- name: Extend the logical volume to consume all remaining space in the volume group
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: +100%FREE
|
|
|
|
- name: Extend the logical volume by given space
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: +512M
|
|
|
|
- name: Extend the logical volume to take all remaining space of the PVs and resize the underlying filesystem
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 100%PVS
|
|
resizefs: true
|
|
|
|
- name: Resize the logical volume to % of VG
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 80%VG
|
|
force: true
|
|
|
|
- name: Reduce the logical volume to 512m
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 512
|
|
force: true
|
|
|
|
- name: Reduce the logical volume by given space
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: -512M
|
|
force: true
|
|
|
|
- name: Set the logical volume to 512m and do not try to shrink if size is lower than current one
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 512
|
|
shrink: false
|
|
|
|
- name: Remove the logical volume.
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
state: absent
|
|
force: true
|
|
|
|
- name: Create a snapshot volume of the test logical volume.
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
snapshot: snap1
|
|
size: 100m
|
|
|
|
- name: Deactivate a logical volume
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
active: false
|
|
|
|
- name: Create a deactivated logical volume
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
size: 512g
|
|
active: false
|
|
|
|
- name: Create a thin pool of 512g
|
|
community.general.lvol:
|
|
vg: firefly
|
|
thinpool: testpool
|
|
size: 512g
|
|
|
|
- name: Create a thin volume of 128g
|
|
community.general.lvol:
|
|
vg: firefly
|
|
lv: test
|
|
thinpool: testpool
|
|
size: 128g
|
|
"""
|
|
|
|
import re
|
|
import shlex
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible_collections.community.general.plugins.module_utils._lvm import (
|
|
lvchange_runner,
|
|
lvcreate_runner,
|
|
lvextend_runner,
|
|
lvreduce_runner,
|
|
lvremove_runner,
|
|
lvs_runner,
|
|
vgs_runner,
|
|
)
|
|
|
|
|
|
def mkversion(major, minor, patch):
|
|
return (1000 * 1000 * int(major)) + (1000 * int(minor)) + int(patch)
|
|
|
|
|
|
def parse_lvs(data):
|
|
lvs = []
|
|
for line in data.splitlines():
|
|
parts = line.strip().split(";")
|
|
lvs.append(
|
|
{
|
|
"name": parts[0].replace("[", "").replace("]", ""),
|
|
"size": float(parts[1]),
|
|
"active": (parts[2][4] == "a"),
|
|
"thinpool": (parts[2][0] == "t"),
|
|
"thinvol": (parts[2][0] == "V"),
|
|
}
|
|
)
|
|
return lvs
|
|
|
|
|
|
def parse_vgs(data):
|
|
vgs = []
|
|
for line in data.splitlines():
|
|
parts = line.strip().split(";")
|
|
vgs.append({"name": parts[0], "size": float(parts[1]), "free": float(parts[2]), "ext_size": float(parts[3])})
|
|
return vgs
|
|
|
|
|
|
def get_lvm_version(module):
|
|
ver_cmd = module.get_bin_path("lvm", required=True)
|
|
rc, out, err = module.run_command([ver_cmd, "version"])
|
|
if rc != 0:
|
|
return None
|
|
m = re.search(r"LVM version:\s+(\d+)\.(\d+)\.(\d+)", out)
|
|
if not m:
|
|
return None
|
|
return mkversion(m.group(1), m.group(2), m.group(3))
|
|
|
|
|
|
def _run_resize(module, is_extend, lvextend, lvreduce, size_opt, size_value, resizefs, lv_name, pvs):
|
|
size_key = f"size_{size_opt}"
|
|
if is_extend:
|
|
with lvextend(f"test resizefs {size_key} lv pvs") as ctx:
|
|
return ctx.run(test=module.check_mode, resizefs=resizefs, **{size_key: size_value}, lv=[lv_name], pvs=pvs)
|
|
else:
|
|
with lvreduce(f"test force resizefs {size_key} lv pvs") as ctx:
|
|
return ctx.run(
|
|
test=module.check_mode, force=True, resizefs=resizefs, **{size_key: size_value}, lv=[lv_name], pvs=pvs
|
|
)
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
vg=dict(type="str", required=True),
|
|
lv=dict(type="str"),
|
|
size=dict(type="str"),
|
|
opts=dict(type="str"),
|
|
state=dict(type="str", default="present", choices=["absent", "present"]),
|
|
force=dict(type="bool", default=False),
|
|
shrink=dict(type="bool", default=True),
|
|
active=dict(type="bool", default=True),
|
|
snapshot=dict(type="str"),
|
|
pvs=dict(type="list", elements="str"),
|
|
resizefs=dict(type="bool", default=False),
|
|
thinpool=dict(type="str"),
|
|
),
|
|
supports_check_mode=True,
|
|
required_one_of=(["lv", "thinpool"],),
|
|
)
|
|
|
|
# Determine if the "--yes" option should be used
|
|
version_found = get_lvm_version(module)
|
|
if version_found is None:
|
|
module.fail_json(msg="Failed to get LVM version number")
|
|
version_yesopt = mkversion(2, 2, 99) # First LVM with the "--yes" option
|
|
use_yes = version_found >= version_yesopt
|
|
|
|
vg = module.params["vg"]
|
|
lv = module.params["lv"]
|
|
size = module.params["size"]
|
|
opts = shlex.split(module.params["opts"] or "")
|
|
state = module.params["state"]
|
|
force = module.boolean(module.params["force"])
|
|
shrink = module.boolean(module.params["shrink"])
|
|
active = module.boolean(module.params["active"])
|
|
resizefs = module.boolean(module.params["resizefs"])
|
|
thinpool = module.params["thinpool"]
|
|
size_opt = "L"
|
|
size_unit = "m"
|
|
size_operator = None
|
|
snapshot = module.params["snapshot"]
|
|
pvs = module.params["pvs"] or []
|
|
|
|
if size:
|
|
# LVEXTEND(8)/LVREDUCE(8) -l, -L options: Check for relative value for resizing
|
|
if size.startswith("+"):
|
|
size_operator = "+"
|
|
size = size[1:]
|
|
elif size.startswith("-"):
|
|
size_operator = "-"
|
|
size = size[1:]
|
|
# LVCREATE(8) does not support [+-]
|
|
|
|
# LVCREATE(8)/LVEXTEND(8)/LVREDUCE(8) -l --extents option with percentage
|
|
if "%" in size:
|
|
size_parts = size.split("%", 1)
|
|
size_percent = int(size_parts[0])
|
|
if size_percent > 100:
|
|
module.fail_json(msg="Size percentage cannot be larger than 100%")
|
|
size_whole = size_parts[1]
|
|
if size_whole == "ORIGIN" and snapshot is None:
|
|
module.fail_json(msg="Percentage of ORIGIN supported only for snapshot volumes")
|
|
elif size_whole not in ["VG", "PVS", "FREE", "ORIGIN"]:
|
|
module.fail_json(msg="Specify extents as a percentage of VG|PVS|FREE|ORIGIN")
|
|
size_opt = "l"
|
|
size_unit = ""
|
|
|
|
# LVCREATE(8)/LVEXTEND(8)/LVREDUCE(8) -L --size option unit
|
|
if "%" not in size:
|
|
if size[-1].lower() in "bskmgtpe":
|
|
size_unit = size[-1]
|
|
size = size[0:-1]
|
|
|
|
try:
|
|
float(size)
|
|
if not size[0].isdigit():
|
|
raise ValueError()
|
|
except ValueError:
|
|
module.fail_json(msg=f"Bad size specification of '{size}'")
|
|
|
|
# when no unit, megabytes by default
|
|
if size_opt == "l":
|
|
unit = "m"
|
|
else:
|
|
unit = size_unit
|
|
|
|
vgs = vgs_runner(module)
|
|
lvs = lvs_runner(module)
|
|
lvcreate = lvcreate_runner(module)
|
|
lvremove = lvremove_runner(module)
|
|
lvextend = lvextend_runner(module)
|
|
lvreduce = lvreduce_runner(module)
|
|
lvchange = lvchange_runner(module)
|
|
|
|
# Get information on volume group requested
|
|
with vgs("noheadings nosuffix units separator fields vg") as ctx:
|
|
rc, current_vgs, err = ctx.run(units=unit.lower(), separator=";", fields="vg_name,size,free,vg_extent_size")
|
|
|
|
if rc != 0:
|
|
if state == "absent":
|
|
module.exit_json(changed=False, stdout=f"Volume group {vg} does not exist.")
|
|
else:
|
|
module.fail_json(msg=f"Volume group {vg} does not exist.", rc=rc, err=err)
|
|
|
|
vg_data = parse_vgs(current_vgs)
|
|
this_vg = vg_data[0]
|
|
|
|
# Get information on logical volume requested
|
|
with lvs("all noheadings nosuffix units separator fields vg") as ctx:
|
|
rc, current_lvs, err = ctx.run(units=unit.lower(), separator=";", fields="lv_name,size,lv_attr")
|
|
|
|
if rc != 0:
|
|
if state == "absent":
|
|
module.exit_json(changed=False, stdout=f"Volume group {vg} does not exist.")
|
|
else:
|
|
module.fail_json(msg=f"Volume group {vg} does not exist.", rc=rc, err=err)
|
|
|
|
changed = False
|
|
|
|
lv_data = parse_lvs(current_lvs)
|
|
|
|
if snapshot:
|
|
# Check snapshot pre-conditions
|
|
for test_lv in lv_data:
|
|
if test_lv["name"] == lv or test_lv["name"] == thinpool:
|
|
if not test_lv["thinpool"] and not thinpool:
|
|
break
|
|
else:
|
|
module.fail_json(msg="Snapshots of thin pool LVs are not supported.")
|
|
else:
|
|
module.fail_json(msg=f"Snapshot origin LV {lv} does not exist in volume group {vg}.")
|
|
check_lv = snapshot
|
|
elif thinpool:
|
|
if lv:
|
|
# Check thin volume pre-conditions
|
|
for test_lv in lv_data:
|
|
if test_lv["name"] == thinpool:
|
|
break
|
|
else:
|
|
module.fail_json(msg=f"Thin pool LV {thinpool} does not exist in volume group {vg}.")
|
|
check_lv = lv
|
|
else:
|
|
check_lv = thinpool
|
|
else:
|
|
check_lv = lv
|
|
|
|
for test_lv in lv_data:
|
|
if test_lv["name"] in (check_lv, check_lv.rsplit("/", 1)[-1]):
|
|
this_lv = test_lv
|
|
break
|
|
else:
|
|
this_lv = None
|
|
|
|
msg = ""
|
|
if this_lv is None:
|
|
if state == "present":
|
|
if size_operator is not None:
|
|
if size_operator == "-" or (size_whole not in ["VG", "PVS", "FREE", "ORIGIN", None]):
|
|
module.fail_json(msg=f"Bad size specification of '{size_operator}{size}' for creating LV")
|
|
# Require size argument except for snapshot of thin volumes
|
|
if (lv or thinpool) and not size:
|
|
for test_lv in lv_data:
|
|
if test_lv["name"] == lv and test_lv["thinvol"] and snapshot:
|
|
break
|
|
else:
|
|
module.fail_json(msg="No size given.")
|
|
|
|
# create LV
|
|
size_value = f"{size}{size_unit}"
|
|
if snapshot is not None:
|
|
if size_opt == "l":
|
|
with lvcreate("test yes size_l is_snapshot lv opts vg") as ctx:
|
|
rc, dummy, err = ctx.run(
|
|
test=module.check_mode,
|
|
yes=use_yes,
|
|
size_l=size_value if size else None,
|
|
is_snapshot=True,
|
|
lv=snapshot,
|
|
opts=opts,
|
|
vg=[f"{vg}/{lv}"],
|
|
)
|
|
else:
|
|
with lvcreate("test yes size_L is_snapshot lv opts vg") as ctx:
|
|
rc, dummy, err = ctx.run(
|
|
test=module.check_mode,
|
|
yes=use_yes,
|
|
size_L=size_value if size else None,
|
|
is_snapshot=True,
|
|
lv=snapshot,
|
|
opts=opts,
|
|
vg=[f"{vg}/{lv}"],
|
|
)
|
|
elif thinpool:
|
|
if lv:
|
|
if size_opt == "l":
|
|
module.fail_json(changed=False, msg="Thin volume sizing with percentage not supported.")
|
|
with lvcreate("test yes lv size_V opts thin vg") as ctx:
|
|
rc, dummy, err = ctx.run(
|
|
test=module.check_mode,
|
|
yes=use_yes,
|
|
lv=lv,
|
|
size_V=size_value,
|
|
opts=opts,
|
|
vg=[f"{vg}/{thinpool}"],
|
|
)
|
|
else:
|
|
if size_opt == "l":
|
|
with lvcreate("test yes size_l opts thin vg pvs") as ctx:
|
|
rc, dummy, err = ctx.run(
|
|
test=module.check_mode,
|
|
yes=use_yes,
|
|
size_l=size_value,
|
|
opts=opts,
|
|
vg=[f"{vg}/{thinpool}"],
|
|
pvs=pvs,
|
|
)
|
|
else:
|
|
with lvcreate("test yes size_L opts thin vg pvs") as ctx:
|
|
rc, dummy, err = ctx.run(
|
|
test=module.check_mode,
|
|
yes=use_yes,
|
|
size_L=size_value,
|
|
opts=opts,
|
|
vg=[f"{vg}/{thinpool}"],
|
|
pvs=pvs,
|
|
)
|
|
else:
|
|
if size_opt == "l":
|
|
with lvcreate("test yes lv size_l opts vg pvs") as ctx:
|
|
rc, dummy, err = ctx.run(
|
|
test=module.check_mode,
|
|
yes=use_yes,
|
|
lv=lv,
|
|
size_l=size_value,
|
|
opts=opts,
|
|
pvs=pvs,
|
|
)
|
|
else:
|
|
with lvcreate("test yes lv size_L opts vg pvs") as ctx:
|
|
rc, dummy, err = ctx.run(
|
|
test=module.check_mode,
|
|
yes=use_yes,
|
|
lv=lv,
|
|
size_L=size_value,
|
|
opts=opts,
|
|
pvs=pvs,
|
|
)
|
|
if rc == 0:
|
|
changed = True
|
|
else:
|
|
module.fail_json(msg=f"Creating logical volume '{lv}' failed", rc=rc, err=err)
|
|
else:
|
|
if state == "absent":
|
|
# remove LV
|
|
if not force:
|
|
module.fail_json(msg=f"Sorry, no removal of logical volume {this_lv['name']} without force=true.")
|
|
with lvremove("test force lv") as ctx:
|
|
rc, dummy, err = ctx.run(test=module.check_mode, force=True, lv=[f"{vg}/{this_lv['name']}"])
|
|
if rc == 0:
|
|
module.exit_json(changed=True)
|
|
else:
|
|
module.fail_json(msg=f"Failed to remove logical volume {lv}", rc=rc, err=err)
|
|
|
|
elif not size:
|
|
pass
|
|
|
|
elif size_opt == "l":
|
|
# Resize LV based on % value
|
|
tool = None
|
|
size_free = this_vg["free"]
|
|
if size_whole == "VG" or size_whole == "PVS":
|
|
size_requested = size_percent * this_vg["size"] / 100
|
|
else: # size_whole == 'FREE':
|
|
size_requested = size_percent * this_vg["free"] / 100
|
|
|
|
if size_operator == "+":
|
|
size_requested += this_lv["size"]
|
|
elif size_operator == "-":
|
|
size_requested = this_lv["size"] - size_requested
|
|
|
|
# According to latest documentation (LVM2-2.03.11) all tools round down
|
|
size_requested -= size_requested % this_vg["ext_size"]
|
|
|
|
if this_lv["size"] < size_requested:
|
|
if (size_free > 0) and (size_free >= (size_requested - this_lv["size"])):
|
|
tool = "extend"
|
|
else:
|
|
module.fail_json(
|
|
msg=(
|
|
f"Logical Volume {this_lv['name']} could not be extended. Not enough free space left "
|
|
f"({size_requested - this_lv['size']}{unit} required / {size_free}{unit} available)"
|
|
)
|
|
)
|
|
elif shrink and this_lv["size"] > size_requested + this_vg["ext_size"]: # more than an extent too large
|
|
if size_requested < 1:
|
|
module.fail_json(msg=f"Sorry, no shrinking of {this_lv['name']} to 0 permitted.")
|
|
elif not force:
|
|
module.fail_json(msg=f"Sorry, no shrinking of {this_lv['name']} without force=true")
|
|
else:
|
|
tool = "reduce"
|
|
|
|
if tool:
|
|
size_value = f"{size_operator or ''}{size}{size_unit}"
|
|
lv_name = f"{vg}/{this_lv['name']}"
|
|
rc, out, err = _run_resize(
|
|
module, tool == "extend", lvextend, lvreduce, size_opt, size_value, resizefs, lv_name, pvs
|
|
)
|
|
if "Reached maximum COW size" in out:
|
|
module.fail_json(msg=f"Unable to resize {lv} to {size}{size_unit}", rc=rc, err=err, out=out)
|
|
elif rc == 0:
|
|
changed = True
|
|
msg = f"Volume {this_lv['name']} resized to {size_requested}{unit}"
|
|
elif "matches existing size" in err or "matches existing size" in out:
|
|
module.exit_json(changed=False, vg=vg, lv=this_lv["name"], size=this_lv["size"])
|
|
elif "not larger than existing size" in err or "not larger than existing size" in out:
|
|
module.exit_json(
|
|
changed=False,
|
|
vg=vg,
|
|
lv=this_lv["name"],
|
|
size=this_lv["size"],
|
|
msg="Original size is larger than requested size",
|
|
err=err,
|
|
)
|
|
else:
|
|
module.fail_json(msg=f"Unable to resize {lv} to {size}{size_unit}", rc=rc, err=err)
|
|
|
|
else:
|
|
# resize LV based on absolute values
|
|
tool = None
|
|
if float(size) > this_lv["size"] or size_operator == "+":
|
|
tool = "extend"
|
|
elif shrink and float(size) < this_lv["size"] or size_operator == "-":
|
|
if float(size) == 0:
|
|
module.fail_json(msg=f"Sorry, no shrinking of {this_lv['name']} to 0 permitted.")
|
|
if not force:
|
|
module.fail_json(msg=f"Sorry, no shrinking of {this_lv['name']} without force=true.")
|
|
else:
|
|
tool = "reduce"
|
|
|
|
if tool:
|
|
size_value = f"{size_operator or ''}{size}{size_unit}"
|
|
lv_name = f"{vg}/{this_lv['name']}"
|
|
rc, out, err = _run_resize(
|
|
module, tool == "extend", lvextend, lvreduce, size_opt, size_value, resizefs, lv_name, pvs
|
|
)
|
|
if "Reached maximum COW size" in out:
|
|
module.fail_json(msg=f"Unable to resize {lv} to {size}{size_unit}", rc=rc, err=err, out=out)
|
|
elif rc == 0:
|
|
changed = True
|
|
elif "matches existing size" in err or "matches existing size" in out:
|
|
module.exit_json(changed=False, vg=vg, lv=this_lv["name"], size=this_lv["size"])
|
|
elif "not larger than existing size" in err or "not larger than existing size" in out:
|
|
module.exit_json(
|
|
changed=False,
|
|
vg=vg,
|
|
lv=this_lv["name"],
|
|
size=this_lv["size"],
|
|
msg="Original size is larger than requested size",
|
|
err=err,
|
|
)
|
|
else:
|
|
module.fail_json(msg=f"Unable to resize {lv} to {size}{size_unit}", rc=rc, err=err)
|
|
|
|
if resizefs and not changed and size:
|
|
lv_path = f"/dev/{vg}/{this_lv['name']}"
|
|
if module.check_mode:
|
|
changed = True
|
|
else:
|
|
fsadm = module.get_bin_path("fsadm", required=True)
|
|
rc, out, err = module.run_command([fsadm, "resize", lv_path])
|
|
if rc == 0:
|
|
changed = True
|
|
else:
|
|
module.fail_json(msg=f"Failed to resize filesystem on {lv_path}", rc=rc, err=err, out=out)
|
|
|
|
if this_lv is not None:
|
|
if active:
|
|
with lvchange("active lv") as ctx:
|
|
rc, dummy, err = ctx.run(active=True, lv=[f"{vg}/{this_lv['name']}"])
|
|
if rc == 0:
|
|
module.exit_json(
|
|
changed=(not this_lv["active"]) or changed, vg=vg, lv=this_lv["name"], size=this_lv["size"]
|
|
)
|
|
else:
|
|
module.fail_json(msg=f"Failed to activate logical volume {lv}", rc=rc, err=err)
|
|
else:
|
|
with lvchange("active lv") as ctx:
|
|
rc, dummy, err = ctx.run(active=False, lv=[f"{vg}/{this_lv['name']}"])
|
|
if rc == 0:
|
|
module.exit_json(changed=this_lv["active"] or changed, vg=vg, lv=this_lv["name"], size=this_lv["size"])
|
|
else:
|
|
module.fail_json(msg=f"Failed to deactivate logical volume {lv}", rc=rc, err=err)
|
|
|
|
module.exit_json(changed=changed, msg=msg)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|