Skip to content

Commit 5dc0830

Browse files
authored
Merge pull request #235 from lae/develop
Release 1.8.0
2 parents 6274a16 + 33685a8 commit 5dc0830

File tree

11 files changed

+158
-42
lines changed

11 files changed

+158
-42
lines changed

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,14 @@ Copy the following playbook to a file like `install_proxmox.yml`:
4949
become: True
5050
roles:
5151
- role: geerlingguy.ntp
52+
vars:
5253
ntp_manage_config: true
5354
ntp_servers:
5455
- clock.sjc.he.net,
5556
- clock.fmt.he.net,
5657
- clock.nyc.he.net
5758
- role: lae.proxmox
59+
vars:
5860
- pve_group: all
5961
- pve_reboot_on_kernel_update: true
6062

@@ -543,8 +545,8 @@ Refer to `library/proxmox_role.py` [link][user-module] and
543545

544546
You can use this role to manage storage within Proxmox VE (both in
545547
single server deployments and cluster deployments). For now, the only supported
546-
types are `dir`, `rbd`, `nfs`, `cephfs`, `lvm`,`lvmthin`, and `zfspool`.
547-
Here are some examples.
548+
types are `dir`, `rbd`, `nfs`, `cephfs`, `lvm`,`lvmthin`, `zfspool`, `btrfs`,
549+
and `pbs`. Here are some examples.
548550

549551
```
550552
pve_storages:
@@ -587,13 +589,28 @@ pve_storages:
587589
- 10.0.0.1
588590
- 10.0.0.2
589591
- 10.0.0.3
592+
- name: pbs1
593+
type: pbs
594+
content: [ "backup" ]
595+
server: 192.168.122.2
596+
username: user@pbs
597+
password: PBSPassword1
598+
datastore: main
590599
- name: zfs1
591600
type: zfspool
592601
content: [ "images", "rootdir" ]
593602
pool: rpool/data
594603
sparse: true
604+
- name: btrfs1
605+
type: btrfs
606+
content: [ "images", "rootdir" ]
607+
nodes: [ "lab-node01.local", "lab-node02.local" ]
608+
path: /mnt/proxmox_storage
609+
is_mountpoint: true
595610
```
596611

612+
Refer to https://pve.proxmox.com/pve-docs/api-viewer/index.html for more information.
613+
597614
Currently the `zfspool` type can be used only for `images` and `rootdir` contents.
598615
If you want to store the other content types on a ZFS volume, you need to specify
599616
them with type `dir`, path `/<POOL>/<VOLUME>` and add an entry in

defaults/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pve_zfs_enabled: no
1818
# pve_zfs_zed_email: "email address for zfs events"
1919
pve_zfs_create_volumes: []
2020
pve_ceph_enabled: false
21-
pve_ceph_repository_line: "deb http://download.proxmox.com/debian/{% if ansible_distribution_release == 'buster' %}ceph-nautilus buster{% else %}ceph-pacific bullseye{% endif %} main"
21+
pve_ceph_repository_line: "deb http://download.proxmox.com/debian/{% if ansible_distribution_release == 'buster' %}ceph-nautilus buster{% else %}ceph-quincy bullseye{% endif %} main"
2222
pve_ceph_network: "{{ (ansible_default_ipv4.network +'/'+ ansible_default_ipv4.netmask) | ipaddr('net') }}"
2323
pve_ceph_nodes: "{{ pve_group }}"
2424
pve_ceph_mon_group: "{{ pve_group }}"

files/proxmox-release-bookworm.gpg

1.16 KB
Binary file not shown.

library/collect_kernel_info.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ def main():
3434
latest_kernel = kernel
3535

3636
booted_kernel = "/lib/modules/{}".format(to_text(
37-
subprocess.run(["uname", "-r"], capture_output=True).stdout.strip))
37+
subprocess.run(["uname", "-r"], capture_output=True).stdout).strip())
3838

39-
booted_kernel_package = ""
39+
booted_kernel_packages = ""
4040
old_kernel_packages = []
4141
if params['lookup_packages']:
4242
for kernel in kernels:
@@ -50,18 +50,19 @@ def main():
5050
if e.stderr.startswith(b"dpkg-query: no path found matching"):
5151
continue
5252
raise e
53+
pkgs = to_text(sp.stdout).split(":")[0].split(", ")
5354
if kernel.split("/")[-1] == booted_kernel.split("/")[-1]:
54-
booted_kernel_package = to_text(sp.stdout).split(":")[0]
55+
booted_kernel_packages = pkgs
5556
elif kernel != latest_kernel:
56-
old_kernel_packages.append(to_text(sp.stdout).split(":")[0])
57+
old_kernel_packages.extend(pkgs)
5758

5859
# returns True if we're not booted into the latest kernel
5960
new_kernel_exists = booted_kernel.split("/")[-1] != latest_kernel.split("/")[-1]
6061
module.exit_json(
6162
changed=False,
6263
new_kernel_exists=new_kernel_exists,
6364
old_packages=old_kernel_packages,
64-
booted_package=booted_kernel_package
65+
booted_packages=booted_kernel_packages
6566
)
6667

6768

library/proxmox_role.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,4 @@ def main():
186186
module.exit_json(**result)
187187

188188
if __name__ == '__main__':
189-
main()
189+
main()

library/proxmox_storage.py

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
type:
2323
required: true
2424
aliases: [ "storagetype" ]
25-
choices: [ "dir", "nfs", "rbd", "lvm", "lvmthin", "cephfs", "zfspool" ]
25+
choices: [ "dir", "nfs", "rbd", "lvm", "lvmthin", "cephfs", "zfspool", "btrfs" ]
2626
description:
2727
- Type of storage, must be supported by Proxmox.
2828
disable:
@@ -98,6 +98,11 @@
9898
required: false
9999
description:
100100
- Use ZFS thin-provisioning.
101+
is_mountpoint:
102+
required: false
103+
description:
104+
- Specifies whether or not the given path is an externally managed
105+
mountpoint.
101106
102107
author:
103108
- Fabien Brachere (@fbrachere)
@@ -154,6 +159,17 @@
154159
- 10.0.0.1
155160
- 10.0.0.2
156161
- 10.0.0.3
162+
- name: Create a Proxmox Backup Server storage type
163+
proxmox_storage:
164+
name: pbs1
165+
type: pbs
166+
content: [ "backup" ]
167+
server: 192.168.122.2
168+
username: user@pbs
169+
password: PBSPassword1
170+
datastore: main
171+
fingerprint: f2:fb:85:76:d2:2a:c4:96:5c:6e:d8:71:37:36:06:17:09:55:f7:04:e3:74:bb:aa:9e:26:85:92:63:c8:b9:23
172+
encryption_key: autogen
157173
- name: Create a ZFS storage type
158174
proxmox_storage:
159175
name: zfs1
@@ -170,16 +186,26 @@
170186
from ansible.module_utils._text import to_text
171187
from ansible.module_utils.pvesh import ProxmoxShellError
172188
import ansible.module_utils.pvesh as pvesh
189+
import re
190+
import json
191+
from json import JSONDecodeError, loads as parse_json
192+
173193

174194
class ProxmoxStorage(object):
175195
def __init__(self, module):
176196
self.module = module
177197
self.name = module.params['name']
178198
self.state = module.params['state']
179-
self.type = module.params['type']
199+
# Globally applicable PVE API arguments
180200
self.disable = module.params['disable']
181201
self.content = module.params['content']
182202
self.nodes = module.params['nodes']
203+
self.type = module.params['type']
204+
# Remaining PVE API arguments (depending on type) past this point
205+
self.datastore = module.params['datastore']
206+
self.encryption_key = module.params['encryption_key']
207+
self.fingerprint = module.params['fingerprint']
208+
self.password = module.params['password']
183209
self.path = module.params['path']
184210
self.pool = module.params['pool']
185211
self.monhost = module.params['monhost']
@@ -192,7 +218,26 @@ def __init__(self, module):
192218
self.vgname = module.params['vgname']
193219
self.thinpool = module.params['thinpool']
194220
self.sparse = module.params['sparse']
195-
221+
self.is_mountpoint = module.params['is_mountpoint']
222+
223+
# Validate the parameters given to us
224+
fingerprint_re = re.compile('^([A-Fa-f0-9]{2}:){31}[A-Fa-f0-9]{2}$')
225+
if self.fingerprint is not None and not fingerprint_re.match(self.fingerprint):
226+
self.module.fail_json(msg=(f"fingerprint must be of the format, "
227+
f"{fingerprint_re.pattern}."))
228+
229+
if self.type == 'pbs':
230+
if self.content != ['backup']:
231+
self.module.fail_json(msg="PBS storage type only supports the "
232+
"'backup' content type.")
233+
try:
234+
if self.encryption_key not in ["autogen", None]:
235+
parse_json(self.encryption_key)
236+
except JSONDecodeError:
237+
self.module.fail_json(msg=("encryption_key needs to be valid "
238+
"JSON or set to 'autogen'."))
239+
240+
# Attempt to retrieve current/live storage definitions
196241
try:
197242
self.existing_storages = pvesh.get("storage")
198243
except ProxmoxShellError as e:
@@ -202,9 +247,9 @@ def lookup(self):
202247
for item in self.existing_storages:
203248
if item['storage'] == self.name:
204249
# pvesh doesn't return the disable param value if it's false,
205-
# so we set it to False.
250+
# so we set it to 0, which is what PVE would normally use.
206251
if item.get('disable') is None:
207-
item['disable'] = False
252+
item['disable'] = 0
208253
return item
209254
return None
210255

@@ -218,13 +263,25 @@ def prepare_storage_args(self):
218263
args = {}
219264

220265
args['type'] = self.type
221-
args['content'] = ','.join(self.content)
266+
if self.content is not None and len(self.content) > 0:
267+
args['content'] = ','.join(self.content)
268+
else:
269+
# PVE uses "none" to represent when no content types are selected
270+
args['content'] = 'none'
222271
if self.nodes is not None:
223272
args['nodes'] = ','.join(self.nodes)
224273
if self.disable is not None:
225-
args['disable'] = self.disable
226-
else:
227-
args['disable'] = False
274+
args['disable'] = 1 if self.disable else 0
275+
if self.datastore is not None:
276+
args['datastore'] = self.datastore
277+
if self.encryption_key is not None:
278+
args['encryption-key'] = self.encryption_key
279+
if self.fingerprint is not None:
280+
args['fingerprint'] = self.fingerprint
281+
if self.master_pubkey is not None:
282+
args['master-pubkey'] = self.master_pubkey
283+
if self.password is not None:
284+
args['password'] = self.password
228285
if self.path is not None:
229286
args['path'] = self.path
230287
if self.pool is not None:
@@ -234,7 +291,7 @@ def prepare_storage_args(self):
234291
if self.username is not None:
235292
args['username'] = self.username
236293
if self.krbd is not None:
237-
args['krbd'] = self.krbd
294+
args['krbd'] = 1 if self.krbd else 0
238295
if self.maxfiles is not None:
239296
args['maxfiles'] = self.maxfiles
240297
if self.server is not None:
@@ -248,7 +305,9 @@ def prepare_storage_args(self):
248305
if self.thinpool is not None:
249306
args['thinpool'] = self.thinpool
250307
if self.sparse is not None:
251-
args['sparse'] = self.sparse
308+
args['sparse'] = 1 if self.sparse else 0
309+
if self.is_mountpoint is not None:
310+
args['is_mountpoint'] = 1 if self.is_mountpoint else 0
252311

253312
if self.maxfiles is not None and 'backup' not in self.content:
254313
self.module.fail_json(msg="maxfiles is not allowed when there is no 'backup' in content")
@@ -275,7 +334,8 @@ def modify_storage(self):
275334

276335
for key in new_storage:
277336
if key == 'content':
278-
if set(self.content) != set(lookup.get('content', '').split(',')):
337+
if set(new_storage['content'].split(',')) \
338+
!= set(lookup.get('content', '').split(',')):
279339
updated_fields.append(key)
280340
staged_storage[key] = new_storage[key]
281341
elif key == 'monhost':
@@ -318,13 +378,20 @@ def main():
318378
# Refer to https://pve.proxmox.com/pve-docs/api-viewer/index.html
319379
module_args = dict(
320380
name=dict(type='str', required=True, aliases=['storage', 'storageid']),
381+
state=dict(default='present', choices=['present', 'absent'], type='str'),
382+
# Globally applicable PVE API arguments
321383
content=dict(type='list', required=True, aliases=['storagetype']),
384+
disable=dict(required=False, type='bool', default=False),
322385
nodes=dict(type='list', required=False, default=None),
323386
type=dict(default=None, type='str', required=True,
324387
choices=["dir", "nfs", "rbd", "lvm", "lvmthin", "cephfs",
325-
"zfspool"]),
326-
disable=dict(required=False, type='bool', default=False),
327-
state=dict(default='present', choices=['present', 'absent'], type='str'),
388+
"zfspool", "btrfs", "pbs"]),
389+
# Remaining PVE API arguments (depending on type) past this point
390+
datastore=dict(default=None, type='str', required=False),
391+
encryption_key=dict(default=None, type='str', required=False),
392+
fingerprint=dict(default=None, type='str', required=False),
393+
master_pubkey=dict(default=None, type='str', required=False),
394+
password=dict(default=None, type='str', required=False),
328395
path=dict(default=None, required=False, type='str'),
329396
pool=dict(default=None, type='str', required=False),
330397
monhost=dict(default=None, type='list', required=False),
@@ -337,6 +404,7 @@ def main():
337404
vgname=dict(default=None, type='str', required=False),
338405
thinpool=dict(default=None, type='str', required=False),
339406
sparse=dict(default=None, type='bool', required=False),
407+
is_mountpoint=dict(default=None, type='bool', required=False),
340408
)
341409

342410
module = AnsibleModule(
@@ -350,7 +418,12 @@ def main():
350418
["type", "lvm", ["vgname", "content"]],
351419
["type", "lvmthin", ["vgname", "thinpool", "content"]],
352420
["type", "zfspool", ["pool", "content"]],
353-
]
421+
["type", "btrfs", ["path", "content"]],
422+
["type", "pbs", ["server", "username", "password", "datastore"]]
423+
],
424+
required_by={
425+
"master_pubkey": "encryption_key"
426+
}
354427
)
355428
storage = ProxmoxStorage(module)
356429

meta/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ galaxy_info:
1313
versions:
1414
- buster
1515
- bullseye
16+
- bookworm
1617

1718
galaxy_tags:
1819
- proxmox

0 commit comments

Comments
 (0)