1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Allow update of proxmox container configuration (#7540)

* add update paramater to proxmox module

* add changelog fragment

* revert formatting changes

* make update idempotent

* fix lints

---------

Co-authored-by: Eric Trombly <etrombly@iomaxis.com>
This commit is contained in:
Eric Trombly 2023-12-01 00:33:02 -06:00 committed by GitHub
parent af01b462d5
commit cf7a58f627
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 134 additions and 5 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- proxmox - adds ``update`` parameter, allowing update of an already existing containers configuration (https://github.com/ansible-collections/community.general/pull/7540).

View file

@ -132,6 +132,12 @@ options:
- timeout for operations
type: int
default: 30
update:
description:
- If V(true), the container will be updated with new values.
type: bool
default: false
version_added: 8.1.0
force:
description:
- Forcing operations.
@ -384,6 +390,16 @@ EXAMPLES = r'''
hostname: clone.example.org
storage: local
- name: Update container configuration
community.general.proxmox:
vmid: 100
node: uk-mc02
api_user: root@pam
api_password: 1q2w3e
api_host: node1
netif: '{"net0":"name=eth0,gw=192.168.0.1,ip=192.168.0.3/24,bridge=vmbr0"}'
update: true
- name: Start container
community.general.proxmox:
vmid: 100
@ -478,6 +494,80 @@ class ProxmoxLxcAnsible(ProxmoxAnsible):
config = getattr(proxmox_node, VZ_TYPE)(vmid).config.get()
return config.get('template', False)
def update_config(self, vmid, node, disk, cpus, memory, swap, **kwargs):
if VZ_TYPE != "lxc":
self.module.fail_json(
changed=False,
msg="Updating configuration is only supported for LXC enabled proxmox clusters.",
)
# Version limited features
minimum_version = {"tags": "6.1", "timezone": "6.3"}
proxmox_node = self.proxmox_api.nodes(node)
pve_version = self.version()
# Fail on unsupported features
for option, version in minimum_version.items():
if pve_version < LooseVersion(version) and option in kwargs:
self.module.fail_json(
changed=False,
msg="Feature {option} is only supported in PVE {version}+, and you're using PVE {pve_version}".format(
option=option, version=version, pve_version=pve_version
),
)
# Remove all empty kwarg entries
kwargs = dict((k, v) for k, v in kwargs.items() if v is not None)
if cpus is not None:
kwargs["cpulimit"] = cpus
if disk is not None:
kwargs["rootfs"] = disk
if memory is not None:
kwargs["memory"] = memory
if swap is not None:
kwargs["swap"] = swap
if "netif" in kwargs:
kwargs.update(kwargs["netif"])
del kwargs["netif"]
if "mounts" in kwargs:
kwargs.update(kwargs["mounts"])
del kwargs["mounts"]
# LXC tags are expected to be valid and presented as a comma/semi-colon delimited string
if "tags" in kwargs:
re_tag = re.compile(r"^[a-z0-9_][a-z0-9_\-\+\.]*$")
for tag in kwargs["tags"]:
if not re_tag.match(tag):
self.module.fail_json(msg="%s is not a valid tag" % tag)
kwargs["tags"] = ",".join(kwargs["tags"])
# fetch the current config
current_config = getattr(proxmox_node, VZ_TYPE)(vmid).config.get()
# compare the requested config against the current
update_config = False
for (arg, value) in kwargs.items():
# some values are lists, the order isn't always the same, so split them and compare by key
if isinstance(value, str):
current_values = current_config[arg].split(",")
requested_values = value.split(",")
for new_value in requested_values:
if new_value not in current_values:
update_config = True
break
# if it's not a list (or string) just compare the current value
else:
# some types don't match with the API, so forcing to string for comparison
if str(value) != str(current_config[arg]):
update_config = True
break
if update_config:
getattr(proxmox_node, VZ_TYPE)(vmid).config.put(vmid=vmid, node=node, **kwargs)
else:
self.module.exit_json(changed=False, msg="Container config is already up to date")
def create_instance(self, vmid, node, disk, storage, cpus, memory, swap, timeout, clone, **kwargs):
# Version limited features
@ -658,6 +748,7 @@ def main():
nameserver=dict(),
searchdomain=dict(),
timeout=dict(type='int', default=30),
update=dict(type='bool', default=False),
force=dict(type='bool', default=False),
purge=dict(type='bool', default=False),
state=dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted', 'template']),
@ -678,14 +769,15 @@ def main():
argument_spec=module_args,
required_if=[
('state', 'present', ['node', 'hostname']),
('state', 'present', ('clone', 'ostemplate'), True), # Require one of clone and ostemplate. Together with mutually_exclusive this ensures that we
# Require one of clone, ostemplate, or update. Together with mutually_exclusive this ensures that we
# either clone a container or create a new one from a template file.
('state', 'present', ('clone', 'ostemplate', 'update'), True),
],
required_together=[
('api_token_id', 'api_token_secret')
],
required_one_of=[('api_password', 'api_token_id')],
mutually_exclusive=[('clone', 'ostemplate')], # Creating a new container is done either by cloning an existing one, or based on a template.
mutually_exclusive=[('clone', 'ostemplate', 'update')], # Creating a new container is done either by cloning an existing one, or based on a template.
)
proxmox = ProxmoxLxcAnsible(module)
@ -733,8 +825,43 @@ def main():
# Create a new container
if state == 'present' and clone is None:
try:
if proxmox.get_vm(vmid, ignore_missing=True) and not module.params['force']:
module.exit_json(changed=False, vmid=vmid, msg="VM with vmid = %s is already exists" % vmid)
if proxmox.get_vm(vmid, ignore_missing=True):
if module.params["update"]:
try:
proxmox.update_config(vmid, node, disk, cpus, memory, swap,
cores=module.params["cores"],
hostname=module.params["hostname"],
netif=module.params["netif"],
mounts=module.params["mounts"],
ip_address=module.params["ip_address"],
onboot=ansible_to_proxmox_bool(module.params["onboot"]),
cpuunits=module.params["cpuunits"],
nameserver=module.params["nameserver"],
searchdomain=module.params["searchdomain"],
features=",".join(module.params["features"])
if module.params["features"] is not None
else None,
description=module.params["description"],
hookscript=module.params["hookscript"],
timezone=module.params["timezone"],
tags=module.params["tags"])
module.exit_json(
changed=True,
vmid=vmid,
msg="Configured VM %s" % (vmid),
)
except Exception as e:
module.fail_json(
vmid=vmid,
msg="Configuration of %s VM %s failed with exception: %s"
% (VZ_TYPE, vmid, e),
)
if not module.params["force"]:
module.exit_json(
changed=False,
vmid=vmid,
msg="VM with vmid = %s is already exists" % vmid,
)
# If no vmid was passed, there cannot be another VM named 'hostname'
if (not module.params['vmid'] and
proxmox.get_vmid(hostname, ignore_missing=True) and