Skip to content

Commit 1bc49ef

Browse files
authored
Merge pull request #282 from BendingBender/prune_backups_shared_storage_args
Implement `shared` and `prune_backups` options for `proxmox_storage` module
2 parents 7754460 + 280eaf6 commit 1bc49ef

File tree

1 file changed

+155
-16
lines changed

1 file changed

+155
-16
lines changed

library/proxmox_storage.py

Lines changed: 155 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,24 @@
1717
name:
1818
required: true
1919
aliases: [ "storage", "storageid" ]
20+
type: str
2021
description:
2122
- Name of the storage.
2223
type:
2324
required: true
2425
aliases: [ "storagetype" ]
26+
type: str
2527
choices: [ "dir", "nfs", "rbd", "lvm", "lvmthin", "cephfs", "zfspool", "btrfs" ]
2628
description:
2729
- Type of storage, must be supported by Proxmox.
2830
disable:
2931
required: false
32+
type: bool
33+
default: false
3034
description: Disable the storage.
3135
state:
3236
required: false
37+
type: str
3338
default: "present"
3439
choices: [ "present", "absent" ]
3540
description:
@@ -38,87 +43,144 @@
3843
required: true
3944
aliases: [ "storagecontent" ]
4045
type: list
46+
elements: str
4147
choices: [ "images", "rootdir", "vztmpl", "backup", "iso", "snippets" ]
4248
description:
43-
- Contents supported by the storage, not all storage
44-
types support all content types.
49+
- Contents supported by the storage, not all storage types support all content types.
4550
nodes:
4651
required: false
4752
type: list
53+
elements: str
4854
description:
4955
- List of cluster node names where this storage is usable.
56+
shared:
57+
required: false
58+
type: bool
59+
description:
60+
- Indicate that this is a single storage with the same contents on all nodes (or all listed in the O(nodes) option).
61+
- It will not make the contents of a local storage automatically accessible to other nodes, it just marks an already shared storage as such!
5062
path:
5163
required: false
64+
type: str
5265
description:
5366
- File system path.
5467
pool:
5568
required: false
69+
type: str
5670
description:
5771
- Ceph/ZFS pool name.
5872
monhost:
5973
required: false
6074
type: list
75+
elements: str
6176
description:
6277
- Monitor addresses of the ceph cluster.
6378
username:
6479
required: false
80+
type: str
6581
description:
6682
- User name (RBD) who access to ceph cluster.
6783
krbd:
6884
required: false
69-
default: 0
85+
type: bool
86+
default: false
7087
description:
7188
- Always access rbd through krbd kernel module.
7289
maxfiles:
7390
required: false
91+
type: int
7492
default: 0
7593
description:
7694
- Maximal number of backup files per VM. 0 for unlimited.
95+
- Deprecated, use O(prune_backups) instead. Replace either by C(keep-last) or, in case C(maxfiles) was C(0) for unlimited retention, by C(keep-all).
96+
prune_backups:
97+
required: false
98+
type: list
99+
elements: dict
100+
description:
101+
- Specifies how to prune backups.
102+
- The retention options are processed in the order given. Each option only covers backups within its time period. The next option does not take care of already covered backups. It will only consider older backups.
103+
suboptions:
104+
option:
105+
required: true
106+
choices:
107+
- keep-all
108+
- keep-last
109+
- keep-hourly
110+
- keep-daily
111+
- keep-weekly
112+
- keep-monthly
113+
- keep-yearly
114+
description:
115+
- The retention option to use.
116+
- "C(keep-all): Keep all backups. This option is mutually exclusive with the other options."
117+
- "C(keep-last): Keep the last n backups."
118+
- "C(keep-hourly): Keep backups for the last n hours. If there is more than one backup for a single hour, only the latest is kept."
119+
- "C(keep-daily): Keep backups for the last n days. If there is more than one backup for a single day, only the latest is kept."
120+
- "C(keep-weekly): Keep backups for the last n weeks. If there is more than one backup for a single week, only the latest is kept. Weeks start on Monday and end on Sunday. The software uses the ISO week date-system and handles weeks at the end of the year correctly."
121+
- "C(keep-monthly): Keep backups for the last n months. If there is more than one backup for a single month, only the latest is kept."
122+
- "C(keep-yearly): Keep backups for the last n years. If there is more than one backup for a single year, only the latest is kept."
123+
value:
124+
required: true
125+
description:
126+
- The number of backups to keep.
127+
- For C(keep-all) option, this value must be a C(bool). For all other options, this value must be an C(int).
77128
export:
78129
required: false
130+
type: str
79131
description:
80132
- NFS export path
81133
server:
82134
required: false
135+
type: str
83136
description:
84137
- Server IP or DNS name.
85138
options:
86139
required: false
140+
type: str
87141
description:
88142
- NFS mount options.
89143
vgname:
90144
required: false
145+
type: str
91146
description:
92147
- LVM volume group name. This must point to an existing volume group.
93148
thinpool:
94149
required: false
150+
type: str
95151
description:
96152
- The name of the LVM thin pool.
97153
sparse:
98154
required: false
155+
type: bool
99156
description:
100157
- Use ZFS thin-provisioning.
101158
is_mountpoint:
102159
required: false
160+
type: bool
103161
description:
104162
- Specifies whether or not the given path is an externally managed
105163
mountpoint.
106164
namespace:
107165
required: false
166+
type: str
108167
description:
109168
- Specifies the Namespace that should be used on PBS
110169
share:
111170
required: false
171+
type: str
112172
description:
113-
- Specifies the CIFS-Share to use
173+
- Specifies the CIFS share to use
114174
subdir:
115175
required: false
116-
- specifies the folder in the share dir to use for proxmox
117-
(useful to seperate proxmox content from other content)
176+
type: str
177+
description:
178+
- Specifies the folder in the share dir to use for proxmox (useful to separate proxmox content from other content)
118179
domain:
119180
required: false
120-
- Specifies Realm to use for NTLM/LDAPS Authentification if using
121-
an AD-Enabled share
181+
type: str
182+
description:
183+
- Specifies Realm to use for NTLM/LDAPS authentication if using an AD-enabled share
122184
123185
author:
124186
- Fabien Brachere (@fbrachere)
@@ -131,7 +193,9 @@
131193
type: dir
132194
path: /mydir
133195
content: [ "images", "iso", "backup" ]
134-
maxfiles: 3
196+
prune_backups:
197+
- option: keep-all
198+
value: 1
135199
- name: Create an RBD storage type
136200
proxmox_storage:
137201
name: ceph1
@@ -170,7 +234,7 @@
170234
name: cephfs1
171235
type: cephfs
172236
content: [ "snippets", "vztmpl", "iso" ]
173-
nodes: [ "proxmox1", "proxmox2"]
237+
nodes: [ "proxmox1", "proxmox2" ]
174238
monhost:
175239
- 10.0.0.1
176240
- 10.0.0.2
@@ -228,6 +292,7 @@ def __init__(self, module):
228292
self.disable = module.params['disable']
229293
self.content = module.params['content']
230294
self.nodes = module.params['nodes']
295+
self.shared = module.params['shared']
231296
self.type = module.params['type']
232297
# Remaining PVE API arguments (depending on type) past this point
233298
self.datastore = module.params['datastore']
@@ -241,6 +306,7 @@ def __init__(self, module):
241306
self.username = module.params['username']
242307
self.krbd = module.params['krbd']
243308
self.maxfiles = module.params['maxfiles']
309+
self.prune_backups = module.params['prune_backups']
244310
self.server = module.params['server']
245311
self.export = module.params['export']
246312
self.options = module.params['options']
@@ -306,6 +372,8 @@ def prepare_storage_args(self):
306372
args['content'] = 'none'
307373
if self.nodes is not None:
308374
args['nodes'] = ','.join(self.nodes)
375+
if self.shared is not None:
376+
args['shared'] = 1 if self.shared else 0
309377
if self.disable is not None:
310378
args['disable'] = 1 if self.disable else 0
311379
if self.datastore is not None:
@@ -355,13 +423,59 @@ def prepare_storage_args(self):
355423
if self.share is not None:
356424
args['share'] = self.share
357425
# end cifs
358-
if self.maxfiles is not None and 'backup' not in self.content:
359-
self.module.fail_json(msg="maxfiles is not allowed when there is no 'backup' in content")
426+
if self.maxfiles is not None:
427+
self.module.warn("'maxfiles' parameter is deprecated, use 'prune_backups' parameter instead")
428+
if 'backup' not in self.content:
429+
self.module.fail_json(
430+
msg="'maxfiles' parameter is not allowed when there is no 'backup' in 'content' parameter"
431+
)
432+
if self.prune_backups is not None:
433+
# order is important for prune_backups, hence we accept a list of options instead of a dict
434+
keep_all_entry, other_entries = self.validate_storage_prune_backups_option()
435+
436+
# the format for the prune-backups argument is (see https://pve.proxmox.com/pve-docs/api-viewer/index.html#/storage/{storage}):
437+
# [keep-all=<1|0>][,keep-daily=<N>][,keep-hourly=<N>][,keep-last=<N>][,keep-monthly=<N>][,keep-weekly=<N>][,keep-yearly=<N>]
438+
args['prune-backups'] = (
439+
# keep-all is mutually exclusive with the other options, we checked that earlier
440+
# example: "keep-all=1"
441+
'keep-all={}'.format(1 if keep_all_entry['value'] else 0)
442+
if keep_all_entry
443+
# example: "keep-last=3,keep-hourly=6"
444+
else ",".join(
445+
map(lambda cfg: '{}={}'.format(cfg['option'], cfg['value']), other_entries)
446+
)
447+
)
360448
if self.krbd is not None and self.type != 'rbd':
361449
self.module.fail_json(msg="krbd is only allowed with 'rbd' storage type")
362450

363451
return args
364452

453+
def validate_storage_prune_backups_option(self):
454+
if 'backup' not in self.content:
455+
self.module.fail_json(
456+
msg="'prune_backups' parameter is not allowed when there is no 'backup' in 'content' parameter"
457+
)
458+
459+
if len(self.prune_backups) != len(set(cfg['option'] for cfg in self.prune_backups)):
460+
self.module.fail_json(msg="'prune_backups' parameter has duplicate entries")
461+
462+
keep_all_entries = [cfg for cfg in self.prune_backups if cfg['option'] == 'keep-all']
463+
keep_all_entry = keep_all_entries[0] if len(keep_all_entries) > 0 else None
464+
other_entries = [cfg for cfg in self.prune_backups if cfg['option'] != 'keep-all']
465+
if keep_all_entry and len(other_entries) > 0:
466+
self.module.fail_json(
467+
msg="'keep-all' is mutually exclusive with other options in 'prune_backups' parameter"
468+
)
469+
470+
if keep_all_entry and type(keep_all_entry['value']) is not bool:
471+
self.module.fail_json(msg="value of 'keep-all' option must be a boolean in 'prune_backups' parameter")
472+
if any(type(cfg['value']) is not int for cfg in other_entries):
473+
self.module.fail_json(
474+
msg="all values except for the 'keep-all' option must be integers in 'prune_backups' parameter"
475+
)
476+
477+
return keep_all_entry, other_entries
478+
365479
def create_storage(self):
366480
new_storage = self.prepare_storage_args()
367481
try:
@@ -429,21 +543,43 @@ def main():
429543
content=dict(type='list', required=True, aliases=['storagetype']),
430544
disable=dict(required=False, type='bool', default=False),
431545
nodes=dict(type='list', required=False, default=None),
546+
shared=dict(type='bool', required=False, default=None),
432547
type=dict(default=None, type='str', required=True,
433548
choices=["dir", "nfs", "rbd", "lvm", "lvmthin", "cephfs",
434549
"zfspool", "btrfs", "pbs", "cifs"]),
435550
# Remaining PVE API arguments (depending on type) past this point
436551
datastore=dict(default=None, type='str', required=False),
437-
encryption_key=dict(default=None, type='str', required=False),
552+
encryption_key=dict(default=None, type='str', required=False, no_log=True),
438553
fingerprint=dict(default=None, type='str', required=False),
439554
master_pubkey=dict(default=None, type='str', required=False),
440-
password=dict(default=None, type='str', required=False),
555+
password=dict(default=None, type='str', required=False, no_log=True),
441556
path=dict(default=None, required=False, type='str'),
442557
pool=dict(default=None, type='str', required=False),
443558
monhost=dict(default=None, type='list', required=False),
444559
username=dict(default=None, type='str', required=False),
445560
krbd=dict(default=None, type='bool', required=False),
446561
maxfiles=dict(default=None, type='int', required=False),
562+
prune_backups=dict(
563+
default=None,
564+
type='list',
565+
elements='dict',
566+
required=False,
567+
options=dict(
568+
option=dict(
569+
required=True,
570+
choices=[
571+
'keep-all',
572+
'keep-last',
573+
'keep-hourly',
574+
'keep-daily',
575+
'keep-weekly',
576+
'keep-monthly',
577+
'keep-yearly',
578+
],
579+
),
580+
value=dict(required=True, type='raw'),
581+
),
582+
),
447583
export=dict(default=None, type='str', required=False),
448584
server=dict(default=None, type='str', required=False),
449585
options=dict(default=None, type='str', required=False),
@@ -474,7 +610,10 @@ def main():
474610
],
475611
required_by={
476612
"master_pubkey": "encryption_key"
477-
}
613+
},
614+
mutually_exclusive=[
615+
["maxfiles", "prune_backups"],
616+
],
478617
)
479618
storage = ProxmoxStorage(module)
480619

@@ -499,7 +638,7 @@ def main():
499638
error = storage.create_storage()
500639
else:
501640
# modify storage (check mode is ok)
502-
(updated_fields,error) = storage.modify_storage()
641+
(updated_fields, error) = storage.modify_storage()
503642

504643
if updated_fields:
505644
result['changed'] = True

0 commit comments

Comments
 (0)