Skip to content

Commit 6274a16

Browse files
committed
Release 1.7.1
Merge branch develop@5b2e74d2353242d94e4f1906db4dc5d655493f57 into main
2 parents f3bcd26 + 5b2e74d commit 6274a16

File tree

14 files changed

+241
-64
lines changed

14 files changed

+241
-64
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ PVE version. They should be IPv4 or IPv6 addresses. For more information, refer
426426
to the [Cluster Manager][pvecm-network] chapter in the PVE Documentation.
427427

428428
```
429-
# pve_cluster_addr0: "{{ ansible_default_ipv4.address }}"
429+
# pve_cluster_addr0: "{{ defaults to the default interface ipv4 or ipv6 if detected }}"
430430
# pve_cluster_addr1: "another interface's IP address or hostname"
431431
```
432432

defaults/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ pve_ceph_crush_rules: []
3333
pve_cluster_enabled: no
3434
pve_cluster_clustername: "{{ pve_group }}"
3535
pve_manage_hosts_enabled: yes
36-
# pve_cluster_addr0: "{{ ansible_default_ipv4.address }}"
36+
pve_cluster_addr0: "{{ ansible_default_ipv4.address if ansible_default_ipv4.address is defined else ansible_default_ipv6.address if ansible_default_ipv6.address is defined }}"
3737
# pve_cluster_addr1: "{{ ansible_eth1.ipv4.address }}
3838
pve_datacenter_cfg: {}
3939
pve_cluster_ha_groups: []
4040
# additional roles for your cluster (f.e. for monitoring)
41+
pve_pools: []
4142
pve_roles: []
4243
pve_groups: []
4344
pve_users: []

handlers/main.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
name: pveproxy
1111
state: restarted
1212

13-
- name: reload sshd configuration
14-
service:
15-
name: sshd
13+
- name: reload ssh server configuration
14+
ansible.builtin.systemd:
15+
name: ssh.service
1616
state: reloaded
1717

1818
- name: restart watchdog-mux

library/collect_kernel_info.py

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
#!/usr/bin/python
22
import glob
3-
import re
43
import subprocess
54

65
from ansible.module_utils.basic import AnsibleModule
76
from ansible.module_utils._text import to_text
87

8+
99
def main():
1010
module = AnsibleModule(
1111
argument_spec = dict(
@@ -16,52 +16,54 @@ def main():
1616

1717
params = module.params
1818

19-
# Much of the following is reimplemented from /usr/share/grub/grub-mkconfig_lib
20-
kernels = []
21-
# Collect a list of possible installed kernels
22-
for filename in glob.glob("/boot/vmlinuz-*") + glob.glob("/vmlinuz-*") + \
23-
glob.glob("/boot/kernel-*"):
24-
if ".dpkg-" in filename:
25-
continue
26-
if filename.endswith(".rpmsave") or filename.endswith(".rpmnew"):
27-
continue
28-
kernels.append(filename)
19+
# Collect a list of installed kernels
20+
kernels = glob.glob("/lib/modules/*")
2921

22+
# Identify path to the latest kernel
3023
latest_kernel = ""
31-
re_prefix = re.compile("[^-]*-")
32-
re_attributes = re.compile("[._-](pre|rc|test|git|old|trunk)")
3324
for kernel in kernels:
34-
right = re.sub(re_attributes, "~\1", re.sub(re_prefix, '', latest_kernel, count=1))
35-
if not right:
25+
if not latest_kernel:
3626
latest_kernel = kernel
3727
continue
38-
left = re.sub(re_attributes, "~\1", re.sub(re_prefix, '', kernel, count=1))
28+
# These splits remove the path and get the base directory name, which
29+
# should be something like 5.4.78-1-pve, that we can compare
30+
right = latest_kernel.split("/")[-1]
31+
left = kernel.split("/")[-1]
3932
cmp_str = "gt"
40-
if left.endswith(".old") and not right.endswith(".old"):
41-
left = left[:-4]
42-
if right.endswith(".old") and not left.endswith(".old"):
43-
right = right[:-4]
44-
cmp_str = "ge"
4533
if subprocess.call(["dpkg", "--compare-versions", left, cmp_str, right]) == 0:
4634
latest_kernel = kernel
4735

48-
# This will likely output a path that considers the boot partition as /
49-
# e.g. /vmlinuz-4.4.44-1-pve
50-
booted_kernel = to_text(subprocess.check_output(["grep", "-o", "-P", "(?<=BOOT_IMAGE=).*?(?= )", "/proc/cmdline"]).strip())
36+
booted_kernel = "/lib/modules/{}".format(to_text(
37+
subprocess.run(["uname", "-r"], capture_output=True).stdout.strip))
5138

5239
booted_kernel_package = ""
5340
old_kernel_packages = []
54-
5541
if params['lookup_packages']:
5642
for kernel in kernels:
43+
# Identify the currently booted kernel and unused old kernels by
44+
# querying which packages own directories in /lib/modules
45+
try:
46+
sp = subprocess.run(["dpkg-query", "-S", kernel],
47+
check=True, capture_output=True)
48+
except subprocess.CalledProcessError as e:
49+
# Ignore errors about directories not associated with a package
50+
if e.stderr.startswith(b"dpkg-query: no path found matching"):
51+
continue
52+
raise e
5753
if kernel.split("/")[-1] == booted_kernel.split("/")[-1]:
58-
booted_kernel_package = to_text(subprocess.check_output(["dpkg-query", "-S", kernel])).split(":")[0]
54+
booted_kernel_package = to_text(sp.stdout).split(":")[0]
5955
elif kernel != latest_kernel:
60-
old_kernel_packages.append(to_text(subprocess.check_output(["dpkg-query", "-S", kernel])).split(":")[0])
56+
old_kernel_packages.append(to_text(sp.stdout).split(":")[0])
6157

6258
# returns True if we're not booted into the latest kernel
6359
new_kernel_exists = booted_kernel.split("/")[-1] != latest_kernel.split("/")[-1]
64-
module.exit_json(changed=False, new_kernel_exists=new_kernel_exists, old_packages=old_kernel_packages, booted_package=booted_kernel_package)
60+
module.exit_json(
61+
changed=False,
62+
new_kernel_exists=new_kernel_exists,
63+
old_packages=old_kernel_packages,
64+
booted_package=booted_kernel_package
65+
)
66+
6567

6668
if __name__ == '__main__':
6769
main()

library/proxmox_pool.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
ANSIBLE_METADATA = {
5+
'metadata_version': '1.0',
6+
'status': ['stableinterface'],
7+
'supported_by': 'futuriste'
8+
}
9+
10+
DOCUMENTATION = '''
11+
---
12+
module: proxmox_pool
13+
14+
short_description: Manages pools in Proxmox
15+
16+
options:
17+
name:
18+
required: true
19+
aliases: [ "pool", "poolid" ]
20+
description:
21+
- Name of the PVE pool to manage.
22+
state:
23+
required: false
24+
default: "present"
25+
choices: [ "present", "absent" ]
26+
description:
27+
- Specifies whether the pool should exist or not.
28+
comment:
29+
required: false
30+
description:
31+
- Optionally sets the pool's comment in PVE.
32+
33+
author:
34+
- Guiffo Joel (@futuriste)
35+
'''
36+
37+
EXAMPLES = '''
38+
- name: Create Administrators pool
39+
proxmox_pool:
40+
name: Administrators
41+
- name: Create Dev Users pool's
42+
proxmox_pool:
43+
name: pool_dev
44+
comment: Dev Users allowed to access on this pool.
45+
'''
46+
47+
RETURN = '''
48+
updated_fields:
49+
description: Fields that were modified for an existing pool
50+
type: list
51+
pool:
52+
description: Information about the pool fetched from PVE after this task completed.
53+
type: json
54+
'''
55+
56+
from ansible.module_utils.basic import AnsibleModule
57+
from ansible.module_utils._text import to_text
58+
from ansible.module_utils.pvesh import ProxmoxShellError
59+
import ansible.module_utils.pvesh as pvesh
60+
61+
class ProxmoxPool(object):
62+
def __init__(self, module):
63+
self.module = module
64+
self.name = module.params['name']
65+
self.state = module.params['state']
66+
self.comment = module.params['comment']
67+
68+
def lookup(self):
69+
try:
70+
return pvesh.get("pools/{}".format(self.name))
71+
except ProxmoxShellError as e:
72+
self.module.fail_json(msg=e.message, status_code=e.status_code, **result)
73+
74+
def remove_pool(self):
75+
try:
76+
pvesh.delete("pools/{}".format(self.name))
77+
return (True, None)
78+
except ProxmoxShellError as e:
79+
return (False, e.message)
80+
81+
def create_pool(self):
82+
new_pool = {}
83+
if self.comment is not None:
84+
new_pool['comment'] = self.comment
85+
86+
try:
87+
pvesh.create("pools", poolid=self.name, **new_pool)
88+
return (True, None)
89+
except ProxmoxShellError as e:
90+
return (False, e.message)
91+
92+
def modify_pool(self):
93+
lookup = self.lookup()
94+
staged_pool = {}
95+
96+
if self.comment is not None:
97+
staged_pool['comment'] = self.comment
98+
99+
updated_fields = []
100+
error = None
101+
102+
for key in staged_pool:
103+
staged_value = to_text(staged_pool[key]) if isinstance(staged_pool[key], str) else staged_pool[key]
104+
if key not in lookup or staged_value != lookup[key]:
105+
updated_fields.append(key)
106+
107+
if self.module.check_mode:
108+
self.module.exit_json(changed=bool(updated_fields), expected_changes=updated_fields)
109+
110+
if not updated_fields:
111+
# No changes necessary
112+
return (updated_fields, error)
113+
114+
try:
115+
pvesh.set("pools/{}".format(self.name), **staged_pool)
116+
except ProxmoxShellError as e:
117+
error = e.message
118+
119+
return (updated_fields, error)
120+
121+
def main():
122+
# Refer to https://pve.proxmox.com/pve-docs/api-viewer/index.html
123+
module = AnsibleModule(
124+
argument_spec = dict(
125+
name=dict(type='str', required=True, aliases=['pool', 'poolid']),
126+
state=dict(default='present', choices=['present', 'absent'], type='str'),
127+
comment=dict(default=None, type='str'),
128+
),
129+
supports_check_mode=True
130+
)
131+
132+
pool = ProxmoxPool(module)
133+
134+
changed = False
135+
error = None
136+
result = {}
137+
result['name'] = pool.name
138+
result['state'] = pool.state
139+
140+
if pool.state == 'absent':
141+
if pool.lookup() is not None:
142+
if module.check_mode:
143+
module.exit_json(changed=True)
144+
145+
(changed, error) = pool.remove_pool()
146+
147+
if error is not None:
148+
module.fail_json(name=pool.name, msg=error)
149+
elif pool.state == 'present':
150+
if not pool.lookup():
151+
if module.check_mode:
152+
module.exit_json(changed=True)
153+
154+
(changed, error) = pool.create_pool()
155+
else:
156+
# modify pool (note: this function is check mode aware)
157+
(updated_fields, error) = pool.modify_pool()
158+
159+
if updated_fields:
160+
changed = True
161+
result['updated_fields'] = updated_fields
162+
163+
if error is not None:
164+
module.fail_json(name=pool.name, msg=error)
165+
166+
lookup = pool.lookup()
167+
if lookup is not None:
168+
result['pool'] = lookup
169+
170+
result['changed'] = changed
171+
172+
module.exit_json(**result)
173+
174+
if __name__ == '__main__':
175+
main()

module_utils/pvesh.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def run_command(handler, resource, **params):
5757
if handler == "get":
5858
if any(re.match(pattern, stderr[0]) for pattern in [
5959
"^no such user \('.{3,64}?'\)$",
60-
"^(group|role) '[A-Za-z0-9\.\-_]+' does not exist$",
60+
"^(group|role|pool) '[A-Za-z0-9\.\-_]+' does not exist$",
6161
"^domain '[A-Za-z][A-Za-z0-9\.\-_]+' does not exist$"]):
6262
return {u"status": 404, u"message": stderr[0]}
6363

tasks/ceph.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@
3939
- block:
4040
- name: Query for existing Ceph volumes
4141
pve_ceph_volume:
42+
check_mode: no
4243
register: _ceph_volume_data
4344

4445
- name: Generate a list of active OSDs
4546
ansible.builtin.set_fact:
46-
_existing_ceph_osds: "{{ _ceph_volume_data.stdout | from_json | json_query('*[].devices[]') }}"
47+
_existing_ceph_osds: "{{ _ceph_volume_data.stdout | from_json | json_query('*[].devices[]') | default([]) }}"
4748

4849
- name: Generate list of unprovisioned OSDs
4950
ansible.builtin.set_fact:
@@ -126,6 +127,7 @@
126127
- name: List Ceph Pools
127128
command: ceph osd pool ls
128129
changed_when: false
130+
check_mode: no
129131
register: _ceph_pools
130132

131133
- name: Create Ceph Pools
@@ -175,6 +177,7 @@
175177
- name: List Ceph Filesystems
176178
command: ceph fs ls -f json
177179
changed_when: false
180+
check_mode: no
178181
when: "pve_ceph_fs | length > 0"
179182
register: _ceph_fs
180183

@@ -192,6 +195,7 @@
192195
- name: Get Ceph Filesystem pool CRUSH rules
193196
command: 'ceph -f json osd pool get {{ item.0.name }}_{{ item.1 }} crush_rule'
194197
changed_when: false
198+
check_mode: no
195199
when: "pve_ceph_fs | length > 0"
196200
register: _ceph_fs_rule
197201
loop: '{{ pve_ceph_fs | product(["data", "metadata"]) | list }}'
@@ -241,7 +245,7 @@
241245
{{ loop.last | ternary("", ",") -}}
242246
{% endfor %}:/
243247
fstype: 'ceph'
244-
opts: 'name={{ item.name }},secretfile=/etc/ceph/{{ item.name }}.secret,_netdev'
248+
opts: 'name={{ item.name }},secretfile=/etc/ceph/{{ item.name }}.secret,_netdev,fs={{ item.name }}'
245249
state: 'mounted'
246250
when: "item.mountpoint is defined"
247251
loop: '{{ pve_ceph_fs }}'

tasks/kernel_module_cleanup.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
state: absent
66
when: >
77
(pve_zfs_options is not defined) or
8-
(pve_zfs_options is defined and not pve_zfs_options | bool) or
8+
(pve_zfs_options is defined and not pve_zfs_options | length > 0) or
99
(not pve_zfs_enabled | bool)
1010
1111
- name: Disable loading of ZFS module on init

0 commit comments

Comments
 (0)