forked from wefixit-AT/oVirtBackup
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vmtools.py
298 lines (283 loc) · 13.5 KB
/
vmtools.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
import logging
import time
import sys
import datetime
import re
import ovirtsdk4.types as types
logger = logging.getLogger()
class VMTools:
"""
Class which holds static methods which are used more than once
"""
@staticmethod
def wait_for_snapshot_operation(api, vm, config, comment):
"""
Wait for a snapshot operation to be finished
:param api: ovirtsdk api
:param vm: Virtual machine object
:param config: Configuration
:param comment: This comment will be used for debugging output
"""
vm_service = api.system_service().vms_service().vm_service(vm.id)
snaps_service = vm_service.snapshots_service()
snapshots = snaps_service.list()
for i in snapshots:
if i.description == config.get_snapshot_description():
snap_service = snaps_service.snapshot_service(i.id)
while True:
try:
snap = snap_service.get()
except:
break
if snap.snapshot_status == types.SnapshotStatus.OK:
break
logger.debug("Snapshot operation(%s) in progress ...", comment)
logger.debug("Snapshot id=%s status=%s", snap.id, snap.snapshot_status)
time.sleep(config.get_timeout())
@staticmethod
def delete_snapshots(api, vm, config, vm_name):
"""
Deletes a backup snapshot
:param api: ovirtsdk api
:param vm: Virtual machine object
:param config: Configuration
:param vm_name: Virtual machine object
"""
logger.debug("Search backup snapshots matching Description=\"%s\"", config.get_snapshot_description())
vm_service = api.system_service().vms_service().vm_service(vm.id)
snaps_service = vm_service.snapshots_service()
snapshots = snaps_service.list()
done = False
if snapshots:
for i in snapshots:
if i.description == config.get_snapshot_description():
logger.debug("Found backup snapshot to delete. Description: %s, Created on: %s", i.description,
i.date)
try:
while True:
try:
if not config.get_dry_run():
snaps_service.snapshot_service(i.id).remove()
logger.info("Snapshot deletion started ...")
VMTools.wait_for_snapshot_operation(api, vm, config, "deletion")
done = True
break
except Exception as e:
if "status: 409" in str(e):
logger.debug("Got 409 wait for operation to be finished, DEBUG: %s", e)
time.sleep(config.get_timeout())
continue
else:
logger.info(" !!! Found another exception for VM: %s", vm_name)
logger.info(" DEBUG: %s", e)
sys.exit(1)
except Exception as e:
logger.info(" !!! Can't delete snapshot for VM: %s", vm_name)
logger.info(" Description: %s, Created on: %s", i.get_description(), i.get_date())
logger.info(" DEBUG: %s", e)
sys.exit(1)
if done:
logger.info("Snapshots deleted")
@staticmethod
def delete_vm(api, config, vm_name):
"""
Delets a vm which was created during backup
:param api: ovirtsdk api
:param config: Configuration
:param vm_name: Virtual machine object
"""
global global_vm
done = False
try:
vms_service = api.system_service().vms_service()
vm_search_regexp = ("name=%s%s_*" % (vm_name, config.get_vm_middle()))
for global_vm in vms_service.list(search=vm_search_regexp):
logger.info("Delete cloned VM (%s) started ..." % global_vm.name)
if not config.get_dry_run():
vm_service = vms_service.vm_service(global_vm.id)
if vm_service is None:
logger.warning(
"The VM (%s) doesn't exist anymore, "
"skipping deletion ...", global_vm.name
)
done = True
continue
global_vm.delete_protected = False
vm_service.update(global_vm)
while True:
try:
vm_service.remove()
break
except:
logger.debug("Wait for previous clone operation to complete (VM %s status is %s)...",
global_vm.name, global_vm.status)
time.sleep(config.get_timeout())
while True:
try:
vm_service.get()
except:
break
logger.debug("Deletion of cloned VM (%s) in progress ..." % global_vm.name)
time.sleep(config.get_timeout())
done = True
except Exception as e:
logger.info("!!! Can't delete cloned VM (%s)", global_vm.name)
raise e
if done:
logger.info("Cloned VM (%s) deleted", global_vm.name)
@staticmethod
def wait_for_vm_operation(api, config, comment, vm_name):
"""
Wait for a vm operation to be finished
:param api: ovirtsdk api
:param vm_name: Virtual machine object
:param config: Configuration
:param comment: This comment will be used for debugging output
"""
# not used
composed_vm_name = "%s%s%s" % (
vm_name, config.get_vm_middle(), config.get_vm_suffix()
)
while True:
vm = api.system_service().vms_service().list(search='name=%s' % composed_vm_name)
if len(vm) == 0:
logger.warning(
"The VM (%s) doesn't exist anymore, "
"leaving waiting loop ...", composed_vm_name
)
break
vm = vm[0]
if vm.status == types.VmStatus.DOWN:
break
logger.debug(
"%s in progress (VM %s status is '%s') ...",
comment, composed_vm_name, vm.status,
)
time.sleep(config.get_timeout())
@staticmethod
def delete_old_backups(api, config, vm_name):
"""
Delete old backups from the export domain
:param api: ovirtsdk api
:param config: Configuration
:param vm_name: Virtual machine object
"""
vm_search_regexp = r'^' + vm_name + config.get_vm_middle() + '*'
logger.debug("Looking for old backup to delete matching %s and older than %s days...", vm_search_regexp,
config.get_backup_keep_count())
sds_service = api.system_service().storage_domains_service()
export_sd = sds_service.list(search='name=%s' % config.get_export_domain())[0]
vms_service = sds_service.storage_domain_service(export_sd.id).vms_service()
# missing list(search'name=... on storage_domain_service().vms_service().list()
exported_vms = vms_service.list()
exported_vms = [i for i in exported_vms if re.match(vm_search_regexp, i.name)]
# not really needed to sort
logger.info("Found %s old backup images in export_domain.", len(exported_vms))
exported_vms.sort(key=lambda x: x.creation_time)
for i in exported_vms:
datetimeStart = datetime.datetime.combine(
(datetime.date.today() - datetime.timedelta(config.get_backup_keep_count())),
datetime.datetime.min.time())
timestampStart = time.mktime(datetimeStart.timetuple())
datetime_creation = i.creation_time
datetime_creation = datetime_creation.replace(hour=0, minute=0, second=0)
timestamp_creation = time.mktime(datetime_creation.timetuple())
if timestamp_creation < timestampStart:
logger.info("Backup deletion (by date) started for backup: %s", i.name)
if not config.get_dry_run():
vms_service.vm_service(id=i.id).remove()
while True:
try:
vms_service.vm_service(id=i.id).get()
logger.debug("Delete old backup (%s) in progress ...", i.name)
time.sleep(config.get_timeout())
except:
logger.info("Backup deletion complete for backup: %s", i.name)
break
@staticmethod
def delete_old_backups_by_number(api, config, vm_name):
"""
Delete old backups from the export domain by number of requested
:param api: ovirtsdk api
:param config: Configuration
:param vm_name: Virtual machine object
"""
vm_search_regexp = r'^' + vm_name + config.get_vm_middle() + '*'
logger.debug("Looking for old backup to delete matching %s, keeping max %s images...", vm_search_regexp,
config.get_backup_keep_count_by_number())
sds_service = api.system_service().storage_domains_service()
export_sd = sds_service.list(search='name=%s' % config.get_export_domain())[0]
vms_service = sds_service.storage_domain_service(export_sd.id).vms_service()
# missing list(search'name=... on storage_domain_service().vms_service().list()
exported_vms = vms_service.list()
exported_vms = [i for i in exported_vms if re.match(vm_search_regexp, i.name)]
exported_vms.sort(key=lambda x: x.creation_time)
logger.info("Found %s old backup images in export_domain.", len(exported_vms))
while len(exported_vms) > config.get_backup_keep_count_by_number():
i = exported_vms.pop(0)
logger.info("Backup deletion (by number) started for backup: %s", i.name)
if not config.get_dry_run():
vms_service.vm_service(id=i.id).remove()
while True:
try:
vms_service.vm_service(id=i.id).get()
logger.debug("Delete old backup (%s) in progress ...", i.name)
time.sleep(config.get_timeout())
except:
logger.info("Backup deletion complete for backup: %s", i.name)
break
@staticmethod
def check_free_space(api, config, vm):
"""
Check if the summarized size of all VM disks is available on the storagedomain
:param api: ovirtsdk api
:param config: Configuration
to avoid running out of space
:param vm: object
"""
sd = api.system_service().storage_domains_service().list(search='name=%s' % config.get_storage_domain())[0]
vm_service = api.system_service().vms_service().vm_service(vm.id)
disk_attachments = vm_service.disk_attachments_service().list()
vm_size = 0
for disk_attachment in disk_attachments:
disk_id = disk_attachment.disk.id
if disk_id in config.get_disks_id_exclude():
logger.info('excluded disk: ', disk_id)
continue
disk = api.system_service().disks_service().disk_service(disk_id).get()
# For safety reason "vm.actual_size" is not used
if disk.provisioned_size is not None:
vm_size += disk.provisioned_size
logger.info("VM SIZE: %s GiB", vm_size /1024/1024/1024)
storage_space_threshold = 0
if config.get_storage_space_threshold() > 0:
storage_space_threshold = config.get_storage_space_threshold()
vm_size *= (1 + storage_space_threshold)
if (sd.available - vm_size) <= 0:
raise Exception(
"!!! The is not enough free storage on the storage domain '%s' available to backup the VM '%s', need at least %s GiB" % (
config.get_storage_domain(), vm.name, vm_size/1024/1024/1024))
@staticmethod
def check_storage_domain_status(api, data_center, storage_domain):
"""
Check the state of the export domain
:param api: ovirt api module
:param data_center: data center name where the storage domain attached
:param storage_domain: storage domain name
:return: True if 'active'
:raises: Exception if storage domain is not 'active'
"""
dcs_service = api.system_service().data_centers_service()
dc = dcs_service.list(search='name=%s' % data_center)[0]
dc_service = dcs_service.data_center_service(dc.id)
sds_service = dc_service.storage_domains_service()
sd = sds_service.list(search='name=%s' % storage_domain)[0]
info_msg = (
"The storage domain {0} is in state {1}".format(
storage_domain, sd.status
)
)
if sd.status == types.StorageDomainStatus.ACTIVE:
logger.info(info_msg)
return True
raise Exception(info_msg)