17
17
DATE_HUMAN_FORMAT = False
18
18
DATE_TRUENAS_FORMAT = False
19
19
INCLUDE_VM_STATE = False
20
+ CHECK_FREE_SPACE = False
20
21
21
22
# Name of the currently running node
22
23
NODE_NAME = socket .gethostname ().split ('.' )[0 ]
@@ -112,6 +113,49 @@ def get_zfs_volume(proxmox_fs: str, virtualization: str) -> str:
112
113
return zfsvol
113
114
114
115
116
+ def fetch_storage_details () -> dict :
117
+ if not CHECK_FREE_SPACE :
118
+ return {}
119
+
120
+ cmd = [
121
+ 'pvesh' , 'get' ,
122
+ '/nodes/{0}/storage' .format (NODE_NAME ),
123
+ '--content' , 'rootdir' ,
124
+ '--enabled' , '1' ,
125
+ '--output-format' , 'json' ,
126
+ ]
127
+ run = run_command (cmd )
128
+ if not run ['status' ]:
129
+ raise SystemExit (run ['message' ])
130
+
131
+ try :
132
+ storages = json .loads (run ['message' ])
133
+ except json .JSONDecodeError as e :
134
+ raise SystemExit ('Error decoding JSON: {0}' .format (e ))
135
+
136
+ result = {}
137
+ for storage in storages :
138
+ storage_name = storage .get ('storage' )
139
+ cmd = [
140
+ 'pvesh' , 'get' ,
141
+ '/nodes/{0}/storage/{1}/content' .format (NODE_NAME , storage_name ),
142
+ '--output-format' , 'json'
143
+ ]
144
+ run = run_command (cmd )
145
+ if not run ['status' ]:
146
+ raise SystemExit (run ['message' ])
147
+
148
+ try :
149
+ content = json .loads (run ['message' ])
150
+ except json .JSONDecodeError as e :
151
+ raise SystemExit ('Error decoding JSON: {0}' .format (e ))
152
+
153
+ vmids = sorted ({int (item ['vmid' ]) for item in content if 'vmid' in item })
154
+ result [storage_name ] = {'used_fraction' : round (storage .get ('used_fraction' ), 2 ), 'vmids' : vmids }
155
+
156
+ return result
157
+
158
+
115
159
def get_vmids (exclude : list ) -> dict :
116
160
run = run_command (['cat' , '/etc/pve/.vmlist' ])
117
161
if not run ['status' ]:
@@ -124,6 +168,7 @@ def get_vmids(exclude: list) -> dict:
124
168
125
169
# Capture non-excluded, local VMs by type (vm vs container)
126
170
result = {}
171
+ storages = fetch_storage_details ()
127
172
for vmid , vm in json_data ['ids' ].items ():
128
173
if vm ['node' ] == NODE_NAME and vmid not in exclude :
129
174
if vm ['type' ] == 'lxc' :
@@ -139,6 +184,10 @@ def get_vmids(exclude: list) -> dict:
139
184
if ONLY_ON_RUNNING and vm_is_stopped (vmid , virtualization ):
140
185
continue
141
186
187
+ if any (info ['used_fraction' ] >= 0.99 and int (vmid ) in info ['vmids' ] for info in storages .values ()):
188
+ print (f'VM { vmid } - Storage usage is above 99%, skipping...' ) if not MUTE else None
189
+ continue
190
+
142
191
result [vmid ] = virtualization
143
192
144
193
return result
@@ -315,12 +364,14 @@ def main():
315
364
parser .add_argument ('--sudo' , action = 'store_true' , help = 'Launch commands through sudo.' )
316
365
parser .add_argument ('--force' , action = 'store_true' ,
317
366
help = 'Force removal from config file, even if disk snapshot deletion fails.' )
367
+ parser .add_argument ('--check-free-space' , action = 'store_true' ,
368
+ help = 'Check if there is enough free space on the storage before creating a snapshot.' )
318
369
argp = parser .parse_args ()
319
370
320
371
if not argp .vmid and not argp .tags and not argp .exclude_tags :
321
372
parser .error ('At least one of --vmid or --tags or --exclude-tags is required.' )
322
373
323
- global MUTE , FORCE , DRY_RUN , USE_SUDO , ONLY_ON_RUNNING , INCLUDE_VM_STATE , DATE_ISO_FORMAT , DATE_HUMAN_FORMAT , DATE_TRUENAS_FORMAT
374
+ global MUTE , FORCE , DRY_RUN , USE_SUDO , ONLY_ON_RUNNING , INCLUDE_VM_STATE , DATE_ISO_FORMAT , DATE_HUMAN_FORMAT , DATE_TRUENAS_FORMAT , CHECK_FREE_SPACE
324
375
MUTE = argp .mute
325
376
FORCE = argp .force
326
377
DRY_RUN = argp .dryrun
@@ -330,6 +381,7 @@ def main():
330
381
DATE_HUMAN_FORMAT = argp .date_human_format
331
382
DATE_TRUENAS_FORMAT = argp .date_truenas_format
332
383
INCLUDE_VM_STATE = argp .includevmstate
384
+ CHECK_FREE_SPACE = argp .check_free_space
333
385
334
386
picked_vmid = get_filtered_vmids (vmids = argp .vmid , exclude = argp .exclude , tags = argp .tags ,
335
387
exclude_tags = argp .exclude_tags )
0 commit comments