Coverage for libs/sm/drivers/LVHDSR.py: 47%
1370 statements
« prev ^ index » next coverage.py v7.3.2, created at 2026-07-10 07:59 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2026-07-10 07:59 +0000
1# Copyright (C) Citrix Systems Inc.
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU Lesser General Public License as published
5# by the Free Software Foundation; version 2.1 only.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU Lesser General Public License for more details.
11#
12# You should have received a copy of the GNU Lesser General Public License
13# along with this program; if not, write to the Free Software Foundation, Inc.,
14# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15#
16# LVHDSR: VHD on LVM storage repository
17# matches with drivers/LVHDSR
18#
19import os
20import sys
21import time
22import errno
23import re
24import glob
26from sm import SR
27from sm.SR import deviceCheck
28from sm import VDI
29from sm.core import util
30from sm.core import scsiutil
31from sm.core import xs_errors
32from sm.core.lock import Lock
33from sm import lvutil
34from sm import cleanup
35from sm import lvmcache
36from sm import vhdutil
37from sm import lvhdutil
38from sm import blktap2
39from sm.journaler import Journaler
40from sm.refcounter import RefCounter
41from sm.ipc import IPCFlag
42from sm.lvmanager import LVActivator
43import XenAPI # pylint: disable=import-error
44from sm.srmetadata import ALLOCATION_TAG, NAME_LABEL_TAG, NAME_DESCRIPTION_TAG, \
45 UUID_TAG, IS_A_SNAPSHOT_TAG, SNAPSHOT_OF_TAG, TYPE_TAG, VDI_TYPE_TAG, \
46 READ_ONLY_TAG, MANAGED_TAG, SNAPSHOT_TIME_TAG, METADATA_OF_POOL_TAG, \
47 LVMMetadataHandler, METADATA_OBJECT_TYPE_VDI, \
48 METADATA_OBJECT_TYPE_SR, METADATA_UPDATE_OBJECT_TYPE_TAG
49from sm.metadata import retrieveXMLfromFile, _parseXML
50from xmlrpc.client import DateTime
51from sm.constants import CBTLOG_TAG
52from fairlock import Fairlock
53DEV_MAPPER_ROOT = os.path.join('/dev/mapper', lvhdutil.VG_PREFIX)
55geneology = {}
56CAPABILITIES = ["SR_PROBE", "SR_UPDATE", "SR_TRIM",
57 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", "VDI_MIRROR",
58 "VDI_CLONE", "VDI_SNAPSHOT", "VDI_RESIZE", "ATOMIC_PAUSE",
59 "VDI_RESET_ON_BOOT/2", "VDI_UPDATE", "VDI_CONFIG_CBT",
60 "VDI_ACTIVATE", "VDI_DEACTIVATE"]
62CONFIGURATION = [['device', 'local device path (required) (e.g. /dev/sda3)']]
64DRIVER_INFO = {
65 'name': 'Local VHD on LVM',
66 'description': 'SR plugin which represents disks as VHD disks on ' + \
67 'Logical Volumes within a locally-attached Volume Group',
68 'vendor': 'XenSource Inc',
69 'copyright': '(C) 2008 XenSource Inc',
70 'driver_version': '1.0',
71 'required_api_version': '1.0',
72 'capabilities': CAPABILITIES,
73 'configuration': CONFIGURATION
74 }
76PARAM_VHD = "vhd"
77PARAM_RAW = "raw"
79OPS_EXCLUSIVE = [
80 "sr_create", "sr_delete", "sr_attach", "sr_detach", "sr_scan",
81 "sr_update", "vdi_create", "vdi_delete", "vdi_resize", "vdi_snapshot",
82 "vdi_clone"]
84# Log if snapshot pauses VM for more than this many seconds
85LONG_SNAPTIME = 60
87class LVHDSR(SR.SR):
88 DRIVER_TYPE = 'lvhd'
90 PROVISIONING_TYPES = ["thin", "thick"]
91 PROVISIONING_DEFAULT = "thick"
92 THIN_PLUGIN = "lvhd-thin"
94 PLUGIN_ON_SLAVE = "on-slave"
96 FLAG_USE_VHD = "use_vhd"
97 MDVOLUME_NAME = "MGT"
99 ALLOCATION_QUANTUM = "allocation_quantum"
100 INITIAL_ALLOCATION = "initial_allocation"
102 LOCK_RETRY_INTERVAL = 3
103 LOCK_RETRY_ATTEMPTS = 10
105 TEST_MODE_KEY = "testmode"
106 TEST_MODE_VHD_FAIL_REPARENT_BEGIN = "vhd_fail_reparent_begin"
107 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR = "vhd_fail_reparent_locator"
108 TEST_MODE_VHD_FAIL_REPARENT_END = "vhd_fail_reparent_end"
109 TEST_MODE_VHD_FAIL_RESIZE_BEGIN = "vhd_fail_resize_begin"
110 TEST_MODE_VHD_FAIL_RESIZE_DATA = "vhd_fail_resize_data"
111 TEST_MODE_VHD_FAIL_RESIZE_METADATA = "vhd_fail_resize_metadata"
112 TEST_MODE_VHD_FAIL_RESIZE_END = "vhd_fail_resize_end"
114 ENV_VAR_VHD_TEST = {
115 TEST_MODE_VHD_FAIL_REPARENT_BEGIN:
116 "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN",
117 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR:
118 "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR",
119 TEST_MODE_VHD_FAIL_REPARENT_END:
120 "VHD_UTIL_TEST_FAIL_REPARENT_END",
121 TEST_MODE_VHD_FAIL_RESIZE_BEGIN:
122 "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN",
123 TEST_MODE_VHD_FAIL_RESIZE_DATA:
124 "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED",
125 TEST_MODE_VHD_FAIL_RESIZE_METADATA:
126 "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED",
127 TEST_MODE_VHD_FAIL_RESIZE_END:
128 "VHD_UTIL_TEST_FAIL_RESIZE_END"
129 }
130 testMode = ""
132 legacyMode = True
134 @staticmethod
135 def handles(type):
136 """Returns True if this SR class understands the given dconf string"""
137 # we can pose as LVMSR for compatibility purposes
138 if type == "lvm":
139 return True
140 return type == LVHDSR.DRIVER_TYPE
142 def load(self, sr_uuid):
143 self.ops_exclusive = OPS_EXCLUSIVE
145 self.isMaster = False
146 if 'SRmaster' in self.dconf and self.dconf['SRmaster'] == 'true':
147 self.isMaster = True
149 self.lock = Lock(vhdutil.LOCK_TYPE_SR, self.uuid)
150 self.sr_vditype = SR.DEFAULT_TAP
151 self.uuid = sr_uuid
152 self.vgname = lvhdutil.VG_PREFIX + self.uuid
153 self.path = os.path.join(lvhdutil.VG_LOCATION, self.vgname)
154 self.mdpath = os.path.join(self.path, self.MDVOLUME_NAME)
155 self.provision = self.PROVISIONING_DEFAULT
156 try:
157 self.lvmCache = lvmcache.LVMCache(self.vgname)
158 except:
159 raise xs_errors.XenError('SRUnavailable', \
160 opterr='Failed to initialise the LVMCache')
161 self.lvActivator = LVActivator(self.uuid, self.lvmCache)
162 self.journaler = Journaler(self.lvmCache)
163 if not self.srcmd.params.get("sr_ref"):
164 return # must be a probe call
165 # Test for thick vs thin provisioning conf parameter
166 if 'allocation' in self.dconf: 166 ↛ 167line 166 didn't jump to line 167, because the condition on line 166 was never true
167 if self.dconf['allocation'] in self.PROVISIONING_TYPES:
168 self.provision = self.dconf['allocation']
169 else:
170 raise xs_errors.XenError('InvalidArg', \
171 opterr='Allocation parameter must be one of %s' % self.PROVISIONING_TYPES)
173 self.other_conf = self.session.xenapi.SR.get_other_config(self.sr_ref)
174 if self.other_conf.get(self.TEST_MODE_KEY): 174 ↛ 178line 174 didn't jump to line 178, because the condition on line 174 was never false
175 self.testMode = self.other_conf[self.TEST_MODE_KEY]
176 self._prepareTestMode()
178 self.sm_config = self.session.xenapi.SR.get_sm_config(self.sr_ref)
179 # sm_config flag overrides PBD, if any
180 if self.sm_config.get('allocation') in self.PROVISIONING_TYPES:
181 self.provision = self.sm_config.get('allocation')
183 if self.sm_config.get(self.FLAG_USE_VHD) == "true":
184 self.legacyMode = False
186 if lvutil._checkVG(self.vgname):
187 if self.isMaster and not self.cmd in ["vdi_attach", "vdi_detach", 187 ↛ 190line 187 didn't jump to line 190, because the condition on line 187 was never false
188 "vdi_activate", "vdi_deactivate"]:
189 self._undoAllJournals()
190 if not self.cmd in ["sr_attach", "sr_probe"]:
191 self._checkMetadataVolume()
193 self.mdexists = False
195 # get a VDI -> TYPE map from the storage
196 contains_uuid_regex = \
197 re.compile("^.*[0-9a-f]{8}-(([0-9a-f]{4})-){3}[0-9a-f]{12}.*")
198 self.storageVDIs = {}
200 vdi = None
201 for key in self.lvmCache.lvs.keys(): 201 ↛ 203line 201 didn't jump to line 203, because the loop on line 201 never started
202 # if the lvname has a uuid in it
203 type = None
204 if contains_uuid_regex.search(key) is not None:
205 if key.startswith(lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_VHD]):
206 type = vhdutil.VDI_TYPE_VHD
207 vdi = key[len(lvhdutil.LV_PREFIX[type]):]
208 elif key.startswith(lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_RAW]):
209 type = vhdutil.VDI_TYPE_RAW
210 vdi = key[len(lvhdutil.LV_PREFIX[type]):]
211 else:
212 continue
214 if type is not None:
215 self.storageVDIs[vdi] = type
217 # check if metadata volume exists
218 try:
219 self.mdexists = self.lvmCache.checkLV(self.MDVOLUME_NAME)
220 except:
221 pass
223 def cleanup(self):
224 # we don't need to hold the lock to dec refcounts of activated LVs
225 if not self.lvActivator.deactivateAll(): 225 ↛ 226line 225 didn't jump to line 226, because the condition on line 225 was never true
226 raise util.SMException("failed to deactivate LVs")
228 def updateSRMetadata(self, allocation):
229 try:
230 # Add SR specific SR metadata
231 sr_info = \
232 {ALLOCATION_TAG: allocation,
233 UUID_TAG: self.uuid,
234 NAME_LABEL_TAG: util.to_plain_string(self.session.xenapi.SR.get_name_label(self.sr_ref)),
235 NAME_DESCRIPTION_TAG: util.to_plain_string(self.session.xenapi.SR.get_name_description(self.sr_ref))
236 }
238 vdi_info = {}
239 for vdi in self.session.xenapi.SR.get_VDIs(self.sr_ref):
240 vdi_uuid = self.session.xenapi.VDI.get_uuid(vdi)
241 is_a_snapshot = int(self.session.xenapi.VDI.get_is_a_snapshot(vdi))
243 vdi_type = self.session.xenapi.VDI.get_sm_config(vdi).get('vdi_type')
244 if not vdi_type:
245 raise xs_errors.XenError('MetadataError', opterr=f"Missing `vdi_type` for VDI {vdi_uuid}")
247 # Create the VDI entry in the SR metadata
248 vdi_info[vdi_uuid] = \
249 {
250 UUID_TAG: vdi_uuid,
251 NAME_LABEL_TAG: util.to_plain_string(self.session.xenapi.VDI.get_name_label(vdi)),
252 NAME_DESCRIPTION_TAG: util.to_plain_string(self.session.xenapi.VDI.get_name_description(vdi)),
253 IS_A_SNAPSHOT_TAG: \
254 is_a_snapshot,
255 SNAPSHOT_OF_TAG: \
256 self.session.xenapi.VDI.get_uuid(self.session.xenapi.VDI.get_snapshot_of(vdi)) if bool(is_a_snapshot) else '',
257 SNAPSHOT_TIME_TAG: \
258 self.session.xenapi.VDI.get_snapshot_time(vdi),
259 TYPE_TAG: \
260 self.session.xenapi.VDI.get_type(vdi),
261 VDI_TYPE_TAG: \
262 vdi_type,
263 READ_ONLY_TAG: \
264 int(self.session.xenapi.VDI.get_read_only(vdi)),
265 METADATA_OF_POOL_TAG: \
266 self.session.xenapi.VDI.get_metadata_of_pool(vdi),
267 MANAGED_TAG: \
268 int(self.session.xenapi.VDI.get_managed(vdi))
269 }
270 LVMMetadataHandler(self.mdpath).writeMetadata(sr_info, vdi_info)
272 except Exception as e:
273 raise xs_errors.XenError('MetadataError', \
274 opterr='Error upgrading SR Metadata: %s' % str(e))
276 def syncMetadataAndStorage(self):
277 try:
278 # if a VDI is present in the metadata but not in the storage
279 # then delete it from the metadata
280 vdi_info = LVMMetadataHandler(self.mdpath, False).getMetadata()[1]
281 for vdi in list(vdi_info.keys()):
282 update_map = {}
283 if not vdi_info[vdi][UUID_TAG] in set(self.storageVDIs.keys()): 283 ↛ 290line 283 didn't jump to line 290, because the condition on line 283 was never false
284 # delete this from metadata
285 LVMMetadataHandler(self.mdpath). \
286 deleteVdiFromMetadata(vdi_info[vdi][UUID_TAG])
287 else:
288 # search for this in the metadata, compare types
289 # self.storageVDIs is a map of vdi_uuid to vdi_type
290 if vdi_info[vdi][VDI_TYPE_TAG] != \
291 self.storageVDIs[vdi_info[vdi][UUID_TAG]]:
292 # storage type takes authority
293 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] \
294 = METADATA_OBJECT_TYPE_VDI
295 update_map[UUID_TAG] = vdi_info[vdi][UUID_TAG]
296 update_map[VDI_TYPE_TAG] = \
297 self.storageVDIs[vdi_info[vdi][UUID_TAG]]
298 LVMMetadataHandler(self.mdpath) \
299 .updateMetadata(update_map)
300 else:
301 # This should never happen
302 pass
304 except Exception as e:
305 raise xs_errors.XenError('MetadataError', \
306 opterr='Error synching SR Metadata and storage: %s' % str(e))
308 def syncMetadataAndXapi(self):
309 try:
310 # get metadata
311 (sr_info, vdi_info) = \
312 LVMMetadataHandler(self.mdpath, False).getMetadata()
314 # First synch SR parameters
315 self.update(self.uuid)
317 # Now update the VDI information in the metadata if required
318 for vdi_offset in vdi_info.keys():
319 try:
320 vdi_ref = \
321 self.session.xenapi.VDI.get_by_uuid( \
322 vdi_info[vdi_offset][UUID_TAG])
323 except:
324 # may be the VDI is not in XAPI yet dont bother
325 continue
327 new_name_label = util.to_plain_string(self.session.xenapi.VDI.get_name_label(vdi_ref))
328 new_name_description = util.to_plain_string(self.session.xenapi.VDI.get_name_description(vdi_ref))
330 if vdi_info[vdi_offset][NAME_LABEL_TAG] != new_name_label or \
331 vdi_info[vdi_offset][NAME_DESCRIPTION_TAG] != \
332 new_name_description:
333 update_map = {}
334 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \
335 METADATA_OBJECT_TYPE_VDI
336 update_map[UUID_TAG] = vdi_info[vdi_offset][UUID_TAG]
337 update_map[NAME_LABEL_TAG] = new_name_label
338 update_map[NAME_DESCRIPTION_TAG] = new_name_description
339 LVMMetadataHandler(self.mdpath) \
340 .updateMetadata(update_map)
341 except Exception as e:
342 raise xs_errors.XenError('MetadataError', \
343 opterr='Error synching SR Metadata and XAPI: %s' % str(e))
345 def _checkMetadataVolume(self):
346 util.SMlog("Entering _checkMetadataVolume")
347 self.mdexists = self.lvmCache.checkLV(self.MDVOLUME_NAME)
348 if self.isMaster: 348 ↛ 364line 348 didn't jump to line 364, because the condition on line 348 was never false
349 if self.mdexists and self.cmd == "sr_attach":
350 try:
351 # activate the management volume
352 # will be deactivated at detach time
353 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME)
354 self._synchSmConfigWithMetaData()
355 util.SMlog("Sync SR metadata and the state on the storage.")
356 self.syncMetadataAndStorage()
357 self.syncMetadataAndXapi()
358 except Exception as e:
359 util.SMlog("Exception in _checkMetadataVolume, " \
360 "Error: %s." % str(e))
361 elif not self.mdexists and not self.legacyMode: 361 ↛ 364line 361 didn't jump to line 364, because the condition on line 361 was never false
362 self._introduceMetaDataVolume()
364 if self.mdexists:
365 self.legacyMode = False
367 def _synchSmConfigWithMetaData(self):
368 util.SMlog("Synching sm-config with metadata volume")
370 try:
371 # get SR info from metadata
372 sr_info = {}
373 map = {}
374 sr_info = LVMMetadataHandler(self.mdpath, False).getMetadata()[0]
376 if sr_info == {}: 376 ↛ 377line 376 didn't jump to line 377, because the condition on line 376 was never true
377 raise Exception("Failed to get SR information from metadata.")
379 if "allocation" in sr_info: 379 ↛ 383line 379 didn't jump to line 383, because the condition on line 379 was never false
380 self.provision = sr_info.get("allocation")
381 map['allocation'] = sr_info.get("allocation")
382 else:
383 raise Exception("Allocation key not found in SR metadata. "
384 "SR info found: %s" % sr_info)
386 except Exception as e:
387 raise xs_errors.XenError(
388 'MetadataError',
389 opterr='Error reading SR params from '
390 'metadata Volume: %s' % str(e))
391 try:
392 map[self.FLAG_USE_VHD] = 'true'
393 self.session.xenapi.SR.set_sm_config(self.sr_ref, map)
394 except:
395 raise xs_errors.XenError(
396 'MetadataError',
397 opterr='Error updating sm_config key')
399 def _introduceMetaDataVolume(self):
400 util.SMlog("Creating Metadata volume")
401 try:
402 config = {}
403 self.lvmCache.create(self.MDVOLUME_NAME, 4 * 1024 * 1024)
405 # activate the management volume, will be deactivated at detach time
406 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME)
408 name_label = util.to_plain_string( \
409 self.session.xenapi.SR.get_name_label(self.sr_ref))
410 name_description = util.to_plain_string( \
411 self.session.xenapi.SR.get_name_description(self.sr_ref))
412 config[self.FLAG_USE_VHD] = "true"
413 config['allocation'] = self.provision
414 self.session.xenapi.SR.set_sm_config(self.sr_ref, config)
416 # Add the SR metadata
417 self.updateSRMetadata(self.provision)
418 except Exception as e:
419 raise xs_errors.XenError('MetadataError', \
420 opterr='Error introducing Metadata Volume: %s' % str(e))
422 def _removeMetadataVolume(self):
423 if self.mdexists:
424 try:
425 self.lvmCache.remove(self.MDVOLUME_NAME)
426 except:
427 raise xs_errors.XenError('MetadataError', \
428 opterr='Failed to delete MGT Volume')
430 def _refresh_size(self):
431 """
432 Refreshs the size of the backing device.
433 Return true if all paths/devices agree on the same size.
434 """
435 if hasattr(self, 'SCSIid'): 435 ↛ 437line 435 didn't jump to line 437, because the condition on line 435 was never true
436 # LVHDoHBASR, LVHDoISCSISR
437 return scsiutil.refresh_lun_size_by_SCSIid(getattr(self, 'SCSIid'))
438 else:
439 # LVHDSR
440 devices = self.dconf['device'].split(',')
441 scsiutil.refreshdev(devices)
442 return True
444 def _expand_size(self):
445 """
446 Expands the size of the SR by growing into additional availiable
447 space, if extra space is availiable on the backing device.
448 Needs to be called after a successful call of _refresh_size.
449 """
450 currentvgsize = lvutil._getVGstats(self.vgname)['physical_size']
451 # We are comparing PV- with VG-sizes that are aligned. Need a threshold
452 resizethreshold = 100 * 1024 * 1024 # 100MB
453 devices = self.dconf['device'].split(',')
454 totaldevicesize = 0
455 for device in devices:
456 totaldevicesize = totaldevicesize + scsiutil.getsize(device)
457 if totaldevicesize >= (currentvgsize + resizethreshold):
458 try:
459 if hasattr(self, 'SCSIid'): 459 ↛ 461line 459 didn't jump to line 461, because the condition on line 459 was never true
460 # LVHDoHBASR, LVHDoISCSISR might have slaves
461 scsiutil.refresh_lun_size_by_SCSIid_on_slaves(self.session,
462 getattr(self, 'SCSIid'))
463 util.SMlog("LVHDSR._expand_size for %s will resize the pv." %
464 self.uuid)
465 for pv in lvutil.get_pv_for_vg(self.vgname):
466 lvutil.resizePV(pv)
467 except:
468 util.logException("LVHDSR._expand_size for %s failed to resize"
469 " the PV" % self.uuid)
471 @deviceCheck
472 def create(self, uuid, size):
473 util.SMlog("LVHDSR.create for %s" % self.uuid)
474 if not self.isMaster:
475 util.SMlog('sr_create blocked for non-master')
476 raise xs_errors.XenError('LVMMaster')
478 if lvutil._checkVG(self.vgname):
479 raise xs_errors.XenError('SRExists')
481 # Check none of the devices already in use by other PBDs
482 if util.test_hostPBD_devs(self.session, uuid, self.dconf['device']):
483 raise xs_errors.XenError('SRInUse')
485 # Check serial number entry in SR records
486 for dev in self.dconf['device'].split(','):
487 if util.test_scsiserial(self.session, dev):
488 raise xs_errors.XenError('SRInUse')
490 lvutil.createVG(self.dconf['device'], self.vgname)
492 #Update serial number string
493 scsiutil.add_serial_record(self.session, self.sr_ref, \
494 scsiutil.devlist_to_serialstring(self.dconf['device'].split(',')))
496 # since this is an SR.create turn off legacy mode
497 self.session.xenapi.SR.add_to_sm_config(self.sr_ref, \
498 self.FLAG_USE_VHD, 'true')
500 def delete(self, uuid):
501 util.SMlog("LVHDSR.delete for %s" % self.uuid)
502 if not self.isMaster:
503 raise xs_errors.XenError('LVMMaster')
504 cleanup.gc_force(self.session, self.uuid)
506 success = True
507 for fileName in glob.glob(DEV_MAPPER_ROOT + '*'):
508 if util.extractSRFromDevMapper(fileName) != self.uuid:
509 continue
511 if util.doesFileHaveOpenHandles(fileName):
512 util.SMlog("LVHDSR.delete: The dev mapper entry %s has open " \
513 "handles" % fileName)
514 success = False
515 continue
517 # Now attempt to remove the dev mapper entry
518 if not lvutil.removeDevMapperEntry(fileName, False):
519 success = False
520 continue
522 try:
523 lvname = os.path.basename(fileName.replace('-', '/'). \
524 replace('//', '-'))
525 lpath = os.path.join(self.path, lvname)
526 os.unlink(lpath)
527 except OSError as e:
528 if e.errno != errno.ENOENT:
529 util.SMlog("LVHDSR.delete: failed to remove the symlink for " \
530 "file %s. Error: %s" % (fileName, str(e)))
531 success = False
533 if success:
534 try:
535 if util.pathexists(self.path):
536 os.rmdir(self.path)
537 except Exception as e:
538 util.SMlog("LVHDSR.delete: failed to remove the symlink " \
539 "directory %s. Error: %s" % (self.path, str(e)))
540 success = False
542 self._removeMetadataVolume()
543 self.lvmCache.refresh()
544 if len(lvhdutil.getLVInfo(self.lvmCache)) > 0:
545 raise xs_errors.XenError('SRNotEmpty')
547 if not success:
548 raise Exception("LVHDSR delete failed, please refer to the log " \
549 "for details.")
551 lvutil.removeVG(self.dconf['device'], self.vgname)
552 self._cleanup()
554 def attach(self, uuid):
555 util.SMlog("LVHDSR.attach for %s" % self.uuid)
557 self._cleanup(True) # in case of host crashes, if detach wasn't called
559 if not util.match_uuid(self.uuid) or not lvutil._checkVG(self.vgname): 559 ↛ 560line 559 didn't jump to line 560, because the condition on line 559 was never true
560 raise xs_errors.XenError('SRUnavailable', \
561 opterr='no such volume group: %s' % self.vgname)
563 # Refresh the metadata status
564 self._checkMetadataVolume()
566 refreshsizeok = self._refresh_size()
568 if self.isMaster: 568 ↛ 579line 568 didn't jump to line 579, because the condition on line 568 was never false
569 if refreshsizeok: 569 ↛ 573line 569 didn't jump to line 573, because the condition on line 569 was never false
570 self._expand_size()
572 # Update SCSIid string
573 util.SMlog("Calling devlist_to_serial")
574 scsiutil.add_serial_record(
575 self.session, self.sr_ref,
576 scsiutil.devlist_to_serialstring(self.dconf['device'].split(',')))
578 # Test Legacy Mode Flag and update if VHD volumes exist
579 if self.isMaster and self.legacyMode: 579 ↛ 580line 579 didn't jump to line 580, because the condition on line 579 was never true
580 vdiInfo = lvhdutil.getVDIInfo(self.lvmCache)
581 for uuid, info in vdiInfo.items():
582 if info.vdiType == vhdutil.VDI_TYPE_VHD:
583 self.legacyMode = False
584 map = self.session.xenapi.SR.get_sm_config(self.sr_ref)
585 self._introduceMetaDataVolume()
586 break
588 # Set the block scheduler
589 for dev in self.dconf['device'].split(','):
590 self.block_setscheduler(dev)
592 def detach(self, uuid):
593 util.SMlog("LVHDSR.detach for %s" % self.uuid)
594 cleanup.abort(self.uuid)
596 # Do a best effort cleanup of the dev mapper entries
597 # go through all devmapper entries for this VG
598 success = True
599 for fileName in glob.glob(DEV_MAPPER_ROOT + '*'):
600 if util.extractSRFromDevMapper(fileName) != self.uuid: 600 ↛ 601line 600 didn't jump to line 601, because the condition on line 600 was never true
601 continue
603 with Fairlock('devicemapper'):
604 # check if any file has open handles
605 if util.doesFileHaveOpenHandles(fileName):
606 # if yes, log this and signal failure
607 util.SMlog(
608 f"LVHDSR.detach: The dev mapper entry {fileName} has "
609 "open handles")
610 success = False
611 continue
613 # Now attempt to remove the dev mapper entry
614 if not lvutil.removeDevMapperEntry(fileName, False): 614 ↛ 615line 614 didn't jump to line 615, because the condition on line 614 was never true
615 success = False
616 continue
618 # also remove the symlinks from /dev/VG-XenStorage-SRUUID/*
619 try:
620 lvname = os.path.basename(fileName.replace('-', '/'). \
621 replace('//', '-'))
622 lvname = os.path.join(self.path, lvname)
623 util.force_unlink(lvname)
624 except Exception as e:
625 util.SMlog("LVHDSR.detach: failed to remove the symlink for " \
626 "file %s. Error: %s" % (fileName, str(e)))
627 success = False
629 # now remove the directory where the symlinks are
630 # this should pass as the directory should be empty by now
631 if success:
632 try:
633 if util.pathexists(self.path): 633 ↛ 634line 633 didn't jump to line 634, because the condition on line 633 was never true
634 os.rmdir(self.path)
635 except Exception as e:
636 util.SMlog("LVHDSR.detach: failed to remove the symlink " \
637 "directory %s. Error: %s" % (self.path, str(e)))
638 success = False
640 if not success:
641 raise Exception("SR detach failed, please refer to the log " \
642 "for details.")
644 # Don't delete lock files on the master as it will break the locking
645 # between SM and any GC thread that survives through SR.detach.
646 # However, we should still delete lock files on slaves as it is the
647 # only place to do so.
648 self._cleanup(self.isMaster)
650 def forget_vdi(self, uuid):
651 if not self.legacyMode:
652 LVMMetadataHandler(self.mdpath).deleteVdiFromMetadata(uuid)
653 super(LVHDSR, self).forget_vdi(uuid)
655 def scan(self, uuid):
656 activated_lvs = set()
657 try:
658 util.SMlog("LVHDSR.scan for %s" % self.uuid)
659 if not self.isMaster: 659 ↛ 660line 659 didn't jump to line 660, because the condition on line 659 was never true
660 util.SMlog('sr_scan blocked for non-master')
661 raise xs_errors.XenError('LVMMaster')
663 if self._refresh_size(): 663 ↛ 665line 663 didn't jump to line 665, because the condition on line 663 was never false
664 self._expand_size()
665 self.lvmCache.refresh()
666 cbt_vdis = self.lvmCache.getTagged(CBTLOG_TAG)
667 self._loadvdis()
668 stats = lvutil._getVGstats(self.vgname)
669 self.physical_size = stats['physical_size']
670 self.physical_utilisation = stats['physical_utilisation']
672 # Now check if there are any VDIs in the metadata, which are not in
673 # XAPI
674 if self.mdexists: 674 ↛ 785line 674 didn't jump to line 785, because the condition on line 674 was never false
675 vdiToSnaps = {}
676 # get VDIs from XAPI
677 vdis = self.session.xenapi.SR.get_VDIs(self.sr_ref)
678 vdi_uuids = set([])
679 for vdi in vdis:
680 vdi_uuids.add(self.session.xenapi.VDI.get_uuid(vdi))
682 Dict = LVMMetadataHandler(self.mdpath, False).getMetadata()[1]
684 for vdi in list(Dict.keys()):
685 vdi_uuid = Dict[vdi][UUID_TAG]
686 if bool(int(Dict[vdi][IS_A_SNAPSHOT_TAG])): 686 ↛ 687line 686 didn't jump to line 687, because the condition on line 686 was never true
687 if Dict[vdi][SNAPSHOT_OF_TAG] in vdiToSnaps:
688 vdiToSnaps[Dict[vdi][SNAPSHOT_OF_TAG]].append(vdi_uuid)
689 else:
690 vdiToSnaps[Dict[vdi][SNAPSHOT_OF_TAG]] = [vdi_uuid]
692 if vdi_uuid not in vdi_uuids:
693 util.SMlog("Introduce VDI %s as it is present in " \
694 "metadata and not in XAPI." % vdi_uuid)
695 sm_config = {}
696 sm_config['vdi_type'] = Dict[vdi][VDI_TYPE_TAG]
697 lvname = "%s%s" % \
698 (lvhdutil.LV_PREFIX[sm_config['vdi_type']], vdi_uuid)
699 self.lvActivator.activate(
700 vdi_uuid, lvname, LVActivator.NORMAL)
701 activated_lvs.add(vdi_uuid)
702 lvPath = os.path.join(self.path, lvname)
704 if Dict[vdi][VDI_TYPE_TAG] == vhdutil.VDI_TYPE_RAW: 704 ↛ 705line 704 didn't jump to line 705, because the condition on line 704 was never true
705 size = self.lvmCache.getSize( \
706 lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_RAW] + \
707 vdi_uuid)
708 utilisation = \
709 util.roundup(lvutil.LVM_SIZE_INCREMENT,
710 int(size))
711 else:
712 parent = \
713 vhdutil._getVHDParentNoCheck(lvPath)
715 if parent is not None: 715 ↛ 716line 715 didn't jump to line 716, because the condition on line 715 was never true
716 sm_config['vhd-parent'] = parent[len( \
717 lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_VHD]):]
718 size = vhdutil.getSizeVirt(lvPath)
719 if self.provision == "thin": 719 ↛ 720line 719 didn't jump to line 720
720 utilisation = \
721 util.roundup(lvutil.LVM_SIZE_INCREMENT,
722 vhdutil.calcOverheadEmpty(lvhdutil.MSIZE))
723 else:
724 utilisation = lvhdutil.calcSizeVHDLV(int(size))
726 vdi_ref = self.session.xenapi.VDI.db_introduce(
727 vdi_uuid,
728 Dict[vdi][NAME_LABEL_TAG],
729 Dict[vdi][NAME_DESCRIPTION_TAG],
730 self.sr_ref,
731 Dict[vdi][TYPE_TAG],
732 False,
733 bool(int(Dict[vdi][READ_ONLY_TAG])),
734 {},
735 vdi_uuid,
736 {},
737 sm_config)
739 self.session.xenapi.VDI.set_managed(vdi_ref,
740 bool(int(Dict[vdi][MANAGED_TAG])))
741 self.session.xenapi.VDI.set_virtual_size(vdi_ref,
742 str(size))
743 self.session.xenapi.VDI.set_physical_utilisation( \
744 vdi_ref, str(utilisation))
745 self.session.xenapi.VDI.set_is_a_snapshot( \
746 vdi_ref, bool(int(Dict[vdi][IS_A_SNAPSHOT_TAG])))
747 if bool(int(Dict[vdi][IS_A_SNAPSHOT_TAG])): 747 ↛ 748line 747 didn't jump to line 748, because the condition on line 747 was never true
748 self.session.xenapi.VDI.set_snapshot_time( \
749 vdi_ref, DateTime(Dict[vdi][SNAPSHOT_TIME_TAG]))
750 if Dict[vdi][TYPE_TAG] == 'metadata': 750 ↛ 751line 750 didn't jump to line 751, because the condition on line 750 was never true
751 self.session.xenapi.VDI.set_metadata_of_pool( \
752 vdi_ref, Dict[vdi][METADATA_OF_POOL_TAG])
754 # Update CBT status of disks either just added
755 # or already in XAPI
756 cbt_logname = "%s.%s" % (vdi_uuid, CBTLOG_TAG)
757 if cbt_logname in cbt_vdis: 757 ↛ 758line 757 didn't jump to line 758, because the condition on line 757 was never true
758 vdi_ref = self.session.xenapi.VDI.get_by_uuid(vdi_uuid)
759 self.session.xenapi.VDI.set_cbt_enabled(vdi_ref, True)
760 # For existing VDIs, update local state too
761 # Scan in base class SR updates existing VDIs
762 # again based on local states
763 if vdi_uuid in self.vdis:
764 self.vdis[vdi_uuid].cbt_enabled = True
765 cbt_vdis.remove(cbt_logname)
767 # Now set the snapshot statuses correctly in XAPI
768 for srcvdi in vdiToSnaps.keys(): 768 ↛ 769line 768 didn't jump to line 769, because the loop on line 768 never started
769 try:
770 srcref = self.session.xenapi.VDI.get_by_uuid(srcvdi)
771 except:
772 # the source VDI no longer exists, continue
773 continue
775 for snapvdi in vdiToSnaps[srcvdi]:
776 try:
777 # this might fail in cases where its already set
778 snapref = \
779 self.session.xenapi.VDI.get_by_uuid(snapvdi)
780 self.session.xenapi.VDI.set_snapshot_of(snapref, srcref)
781 except Exception as e:
782 util.SMlog("Setting snapshot failed. " \
783 "Error: %s" % str(e))
785 if cbt_vdis: 785 ↛ 796line 785 didn't jump to line 796, because the condition on line 785 was never false
786 # If we have items remaining in this list,
787 # they are cbt_metadata VDI that XAPI doesn't know about
788 # Add them to self.vdis and they'll get added to the DB
789 for cbt_vdi in cbt_vdis: 789 ↛ 790line 789 didn't jump to line 790, because the loop on line 789 never started
790 cbt_uuid = cbt_vdi.split(".")[0]
791 new_vdi = self.vdi(cbt_uuid)
792 new_vdi.ty = "cbt_metadata"
793 new_vdi.cbt_enabled = True
794 self.vdis[cbt_uuid] = new_vdi
796 super(LVHDSR, self).scan(uuid)
797 self._kickGC()
799 finally:
800 for vdi in activated_lvs:
801 self.lvActivator.deactivate(
802 vdi, LVActivator.NORMAL, False)
804 def update(self, uuid):
805 if not lvutil._checkVG(self.vgname): 805 ↛ 806line 805 didn't jump to line 806, because the condition on line 805 was never true
806 return
807 self._updateStats(uuid, 0)
809 if self.legacyMode: 809 ↛ 810line 809 didn't jump to line 810, because the condition on line 809 was never true
810 return
812 # synch name_label in metadata with XAPI
813 update_map = {}
814 update_map = {METADATA_UPDATE_OBJECT_TYPE_TAG: \
815 METADATA_OBJECT_TYPE_SR,
816 NAME_LABEL_TAG: util.to_plain_string( \
817 self.session.xenapi.SR.get_name_label(self.sr_ref)),
818 NAME_DESCRIPTION_TAG: util.to_plain_string( \
819 self.session.xenapi.SR.get_name_description(self.sr_ref))
820 }
821 LVMMetadataHandler(self.mdpath).updateMetadata(update_map)
823 def _updateStats(self, uuid, virtAllocDelta):
824 valloc = int(self.session.xenapi.SR.get_virtual_allocation(self.sr_ref))
825 self.virtual_allocation = valloc + virtAllocDelta
826 util.SMlog("Setting virtual_allocation of SR %s to %d" %
827 (uuid, self.virtual_allocation))
828 stats = lvutil._getVGstats(self.vgname)
829 self.physical_size = stats['physical_size']
830 self.physical_utilisation = stats['physical_utilisation']
831 self._db_update()
833 @deviceCheck
834 def probe(self):
835 return lvutil.srlist_toxml(
836 lvutil.scan_srlist(lvhdutil.VG_PREFIX, self.dconf['device']),
837 lvhdutil.VG_PREFIX,
838 ('metadata' in self.srcmd.params['sr_sm_config'] and \
839 self.srcmd.params['sr_sm_config']['metadata'] == 'true'))
841 def vdi(self, uuid):
842 return LVHDVDI(self, uuid)
844 def _loadvdis(self):
845 self.virtual_allocation = 0
846 self.vdiInfo = lvhdutil.getVDIInfo(self.lvmCache)
847 self.allVDIs = {}
849 for uuid, info in self.vdiInfo.items():
850 if uuid.startswith(cleanup.SR.TMP_RENAME_PREFIX): 850 ↛ 851line 850 didn't jump to line 851, because the condition on line 850 was never true
851 continue
852 if info.scanError: 852 ↛ 853line 852 didn't jump to line 853, because the condition on line 852 was never true
853 raise xs_errors.XenError('VDIUnavailable', \
854 opterr='Error scanning VDI %s' % uuid)
855 self.vdis[uuid] = self.allVDIs[uuid] = self.vdi(uuid)
856 if not self.vdis[uuid].hidden: 856 ↛ 849line 856 didn't jump to line 849, because the condition on line 856 was never false
857 self.virtual_allocation += self.vdis[uuid].utilisation
859 for uuid, vdi in self.vdis.items():
860 if vdi.parent: 860 ↛ 861line 860 didn't jump to line 861, because the condition on line 860 was never true
861 if vdi.parent in self.vdis:
862 self.vdis[vdi.parent].read_only = True
863 if vdi.parent in geneology:
864 geneology[vdi.parent].append(uuid)
865 else:
866 geneology[vdi.parent] = [uuid]
868 # Now remove all hidden leaf nodes to avoid introducing records that
869 # will be GC'ed
870 for uuid in list(self.vdis.keys()):
871 if uuid not in geneology and self.vdis[uuid].hidden: 871 ↛ 872line 871 didn't jump to line 872, because the condition on line 871 was never true
872 util.SMlog("Scan found hidden leaf (%s), ignoring" % uuid)
873 del self.vdis[uuid]
875 def _ensureSpaceAvailable(self, amount_needed):
876 space_available = lvutil._getVGstats(self.vgname)['freespace']
877 if (space_available < amount_needed): 877 ↛ 878line 877 didn't jump to line 878, because the condition on line 877 was never true
878 util.SMlog("Not enough space! free space: %d, need: %d" % \
879 (space_available, amount_needed))
880 raise xs_errors.XenError('SRNoSpace')
882 def _handleInterruptedCloneOps(self):
883 entries = self.journaler.getAll(LVHDVDI.JRN_CLONE)
884 for uuid, val in entries.items(): 884 ↛ 885line 884 didn't jump to line 885, because the loop on line 884 never started
885 util.fistpoint.activate("LVHDRT_clone_vdi_before_undo_clone", self.uuid)
886 self._handleInterruptedCloneOp(uuid, val)
887 util.fistpoint.activate("LVHDRT_clone_vdi_after_undo_clone", self.uuid)
888 self.journaler.remove(LVHDVDI.JRN_CLONE, uuid)
890 def _handleInterruptedCoalesceLeaf(self):
891 entries = self.journaler.getAll(cleanup.VDI.JRN_LEAF)
892 if len(entries) > 0: 892 ↛ 893line 892 didn't jump to line 893, because the condition on line 892 was never true
893 util.SMlog("*** INTERRUPTED COALESCE-LEAF OP DETECTED ***")
894 cleanup.gc_force(self.session, self.uuid)
895 self.lvmCache.refresh()
897 def _handleInterruptedCloneOp(self, origUuid, jval, forceUndo=False):
898 """Either roll back or finalize the interrupted snapshot/clone
899 operation. Rolling back is unsafe if the leaf VHDs have already been
900 in use and written to. However, it is always safe to roll back while
901 we're still in the context of the failed snapshot operation since the
902 VBD is paused for the duration of the operation"""
903 util.SMlog("*** INTERRUPTED CLONE OP: for %s (%s)" % (origUuid, jval))
904 lvs = lvhdutil.getLVInfo(self.lvmCache)
905 baseUuid, clonUuid = jval.split("_")
907 # is there a "base copy" VDI?
908 if not lvs.get(baseUuid):
909 # no base copy: make sure the original is there
910 if lvs.get(origUuid):
911 util.SMlog("*** INTERRUPTED CLONE OP: nothing to do")
912 return
913 raise util.SMException("base copy %s not present, " \
914 "but no original %s found" % (baseUuid, origUuid))
916 if forceUndo:
917 util.SMlog("Explicit revert")
918 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid)
919 return
921 if not lvs.get(origUuid) or (clonUuid and not lvs.get(clonUuid)):
922 util.SMlog("One or both leaves missing => revert")
923 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid)
924 return
926 vdis = lvhdutil.getVDIInfo(self.lvmCache)
927 if vdis[origUuid].scanError or (clonUuid and vdis[clonUuid].scanError):
928 util.SMlog("One or both leaves invalid => revert")
929 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid)
930 return
932 orig = vdis[origUuid]
933 base = vdis[baseUuid]
934 self.lvActivator.activate(baseUuid, base.lvName, False)
935 self.lvActivator.activate(origUuid, orig.lvName, False)
936 if orig.parentUuid != baseUuid:
937 parent = vdis[orig.parentUuid]
938 self.lvActivator.activate(parent.uuid, parent.lvName, False)
939 origPath = os.path.join(self.path, orig.lvName)
940 if not vhdutil.check(origPath):
941 util.SMlog("Orig VHD invalid => revert")
942 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid)
943 return
945 if clonUuid:
946 clon = vdis[clonUuid]
947 clonPath = os.path.join(self.path, clon.lvName)
948 self.lvActivator.activate(clonUuid, clon.lvName, False)
949 if not vhdutil.check(clonPath):
950 util.SMlog("Clon VHD invalid => revert")
951 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid)
952 return
954 util.SMlog("Snapshot appears valid, will not roll back")
955 self._completeCloneOp(vdis, origUuid, baseUuid, clonUuid)
957 def _undoCloneOp(self, lvs, origUuid, baseUuid, clonUuid):
958 base = lvs[baseUuid]
959 basePath = os.path.join(self.path, base.name)
961 # make the parent RW
962 if base.readonly:
963 self.lvmCache.setReadonly(base.name, False)
965 ns = lvhdutil.NS_PREFIX_LVM + self.uuid
966 origRefcountBinary = RefCounter.check(origUuid, ns)[1]
967 origRefcountNormal = 0
969 # un-hide the parent
970 vhdInfo = None
971 if base.vdiType == vhdutil.VDI_TYPE_VHD:
972 self.lvActivator.activate(baseUuid, base.name, False)
973 origRefcountNormal = 1
974 vhdInfo = vhdutil.getVHDInfo(basePath, lvhdutil.extractUuid, False)
975 if base.vdiType == vhdutil.VDI_TYPE_VHD and vhdInfo.hidden:
976 vhdutil.setHidden(basePath, False)
977 elif base.vdiType == vhdutil.VDI_TYPE_RAW and base.hidden:
978 self.lvmCache.setHidden(base.name, False)
980 # remove the child nodes
981 if clonUuid and lvs.get(clonUuid):
982 if lvs[clonUuid].vdiType != vhdutil.VDI_TYPE_VHD:
983 raise util.SMException("clone %s not VHD" % clonUuid)
984 self.lvmCache.remove(lvs[clonUuid].name)
985 if self.lvActivator.get(clonUuid, False):
986 self.lvActivator.remove(clonUuid, False)
987 if lvs.get(origUuid):
988 self.lvmCache.remove(lvs[origUuid].name)
990 # inflate the parent to fully-allocated size
991 if base.vdiType == vhdutil.VDI_TYPE_VHD:
992 fullSize = lvhdutil.calcSizeVHDLV(vhdInfo.sizeVirt)
993 lvhdutil.inflate(self.journaler, self.uuid, baseUuid, fullSize)
995 # rename back
996 origLV = lvhdutil.LV_PREFIX[base.vdiType] + origUuid
997 self.lvmCache.rename(base.name, origLV)
998 RefCounter.reset(baseUuid, ns)
999 if self.lvActivator.get(baseUuid, False):
1000 self.lvActivator.replace(baseUuid, origUuid, origLV, False)
1001 RefCounter.set(origUuid, origRefcountNormal, origRefcountBinary, ns)
1003 # At this stage, tapdisk and SM vdi will be in paused state. Remove
1004 # flag to facilitate vm deactivate
1005 origVdiRef = self.session.xenapi.VDI.get_by_uuid(origUuid)
1006 self.session.xenapi.VDI.remove_from_sm_config(origVdiRef, 'paused')
1008 # update LVM metadata on slaves
1009 slaves = util.get_slaves_attached_on(self.session, [origUuid])
1010 lvhdutil.lvRefreshOnSlaves(self.session, self.uuid, self.vgname,
1011 origLV, origUuid, slaves)
1013 util.SMlog("*** INTERRUPTED CLONE OP: rollback success")
1015 def _completeCloneOp(self, vdis, origUuid, baseUuid, clonUuid):
1016 """Finalize the interrupted snapshot/clone operation. This must not be
1017 called from the live snapshot op context because we attempt to pause/
1018 unpause the VBD here (the VBD is already paused during snapshot, so it
1019 would cause a deadlock)"""
1020 base = vdis[baseUuid]
1021 clon = None
1022 if clonUuid:
1023 clon = vdis[clonUuid]
1025 cleanup.abort(self.uuid)
1027 # make sure the parent is hidden and read-only
1028 if not base.hidden:
1029 if base.vdiType == vhdutil.VDI_TYPE_RAW:
1030 self.lvmCache.setHidden(base.lvName)
1031 else:
1032 basePath = os.path.join(self.path, base.lvName)
1033 vhdutil.setHidden(basePath)
1034 if not base.lvReadonly:
1035 self.lvmCache.setReadonly(base.lvName, True)
1037 # NB: since this snapshot-preserving call is only invoked outside the
1038 # snapshot op context, we assume the LVM metadata on the involved slave
1039 # has by now been refreshed and do not attempt to do it here
1041 # Update the original record
1042 try:
1043 vdi_ref = self.session.xenapi.VDI.get_by_uuid(origUuid)
1044 sm_config = self.session.xenapi.VDI.get_sm_config(vdi_ref)
1045 type = self.session.xenapi.VDI.get_type(vdi_ref)
1046 sm_config["vdi_type"] = vhdutil.VDI_TYPE_VHD
1047 sm_config['vhd-parent'] = baseUuid
1048 self.session.xenapi.VDI.set_sm_config(vdi_ref, sm_config)
1049 except XenAPI.Failure:
1050 util.SMlog("ERROR updating the orig record")
1052 # introduce the new VDI records
1053 if clonUuid:
1054 try:
1055 clon_vdi = VDI.VDI(self, clonUuid)
1056 clon_vdi.read_only = False
1057 clon_vdi.location = clonUuid
1058 clon_vdi.utilisation = clon.sizeLV
1059 clon_vdi.sm_config = {
1060 "vdi_type": vhdutil.VDI_TYPE_VHD,
1061 "vhd-parent": baseUuid}
1063 if not self.legacyMode:
1064 LVMMetadataHandler(self.mdpath). \
1065 ensureSpaceIsAvailableForVdis(1)
1067 clon_vdi_ref = clon_vdi._db_introduce()
1068 util.SMlog("introduced clon VDI: %s (%s)" % \
1069 (clon_vdi_ref, clonUuid))
1071 vdi_info = {UUID_TAG: clonUuid,
1072 NAME_LABEL_TAG: clon_vdi.label,
1073 NAME_DESCRIPTION_TAG: clon_vdi.description,
1074 IS_A_SNAPSHOT_TAG: 0,
1075 SNAPSHOT_OF_TAG: '',
1076 SNAPSHOT_TIME_TAG: '',
1077 TYPE_TAG: type,
1078 VDI_TYPE_TAG: clon_vdi.sm_config['vdi_type'],
1079 READ_ONLY_TAG: int(clon_vdi.read_only),
1080 MANAGED_TAG: int(clon_vdi.managed),
1081 METADATA_OF_POOL_TAG: ''
1082 }
1084 if not self.legacyMode:
1085 LVMMetadataHandler(self.mdpath).addVdi(vdi_info)
1087 except XenAPI.Failure:
1088 util.SMlog("ERROR introducing the clon record")
1090 try:
1091 base_vdi = VDI.VDI(self, baseUuid) # readonly parent
1092 base_vdi.label = "base copy"
1093 base_vdi.read_only = True
1094 base_vdi.location = baseUuid
1095 base_vdi.size = base.sizeVirt
1096 base_vdi.utilisation = base.sizeLV
1097 base_vdi.managed = False
1098 base_vdi.sm_config = {
1099 "vdi_type": vhdutil.VDI_TYPE_VHD,
1100 "vhd-parent": baseUuid}
1102 if not self.legacyMode:
1103 LVMMetadataHandler(self.mdpath).ensureSpaceIsAvailableForVdis(1)
1105 base_vdi_ref = base_vdi._db_introduce()
1106 util.SMlog("introduced base VDI: %s (%s)" % \
1107 (base_vdi_ref, baseUuid))
1109 vdi_info = {UUID_TAG: baseUuid,
1110 NAME_LABEL_TAG: base_vdi.label,
1111 NAME_DESCRIPTION_TAG: base_vdi.description,
1112 IS_A_SNAPSHOT_TAG: 0,
1113 SNAPSHOT_OF_TAG: '',
1114 SNAPSHOT_TIME_TAG: '',
1115 TYPE_TAG: type,
1116 VDI_TYPE_TAG: base_vdi.sm_config['vdi_type'],
1117 READ_ONLY_TAG: int(base_vdi.read_only),
1118 MANAGED_TAG: int(base_vdi.managed),
1119 METADATA_OF_POOL_TAG: ''
1120 }
1122 if not self.legacyMode:
1123 LVMMetadataHandler(self.mdpath).addVdi(vdi_info)
1124 except XenAPI.Failure:
1125 util.SMlog("ERROR introducing the base record")
1127 util.SMlog("*** INTERRUPTED CLONE OP: complete")
1129 def _undoAllJournals(self):
1130 """Undo all VHD & SM interrupted journaled operations. This call must
1131 be serialized with respect to all operations that create journals"""
1132 # undoing interrupted inflates must be done first, since undoing VHD
1133 # ops might require inflations
1134 self.lock.acquire()
1135 try:
1136 self._undoAllInflateJournals()
1137 self._undoAllVHDJournals()
1138 self._handleInterruptedCloneOps()
1139 self._handleInterruptedCoalesceLeaf()
1140 finally:
1141 self.lock.release()
1142 self.cleanup()
1144 def _undoAllInflateJournals(self):
1145 entries = self.journaler.getAll(lvhdutil.JRN_INFLATE)
1146 if len(entries) == 0:
1147 return
1148 self._loadvdis()
1149 for uuid, val in entries.items():
1150 vdi = self.vdis.get(uuid)
1151 if vdi: 1151 ↛ 1166line 1151 didn't jump to line 1166, because the condition on line 1151 was never false
1152 util.SMlog("Found inflate journal %s, deflating %s to %s" % \
1153 (uuid, vdi.path, val))
1154 if vdi.readonly: 1154 ↛ 1155line 1154 didn't jump to line 1155, because the condition on line 1154 was never true
1155 self.lvmCache.setReadonly(vdi.lvname, False)
1156 self.lvActivator.activate(uuid, vdi.lvname, False)
1157 currSizeLV = self.lvmCache.getSize(vdi.lvname)
1158 util.zeroOut(vdi.path, currSizeLV - vhdutil.VHD_FOOTER_SIZE,
1159 vhdutil.VHD_FOOTER_SIZE)
1160 lvhdutil.deflate(self.lvmCache, vdi.lvname, int(val))
1161 if vdi.readonly: 1161 ↛ 1162line 1161 didn't jump to line 1162, because the condition on line 1161 was never true
1162 self.lvmCache.setReadonly(vdi.lvname, True)
1163 if "true" == self.session.xenapi.SR.get_shared(self.sr_ref): 1163 ↛ 1164line 1163 didn't jump to line 1164, because the condition on line 1163 was never true
1164 lvhdutil.lvRefreshOnAllSlaves(self.session, self.uuid,
1165 self.vgname, vdi.lvname, uuid)
1166 self.journaler.remove(lvhdutil.JRN_INFLATE, uuid)
1167 delattr(self, "vdiInfo")
1168 delattr(self, "allVDIs")
1170 def _undoAllVHDJournals(self):
1171 """check if there are VHD journals in existence and revert them"""
1172 journals = lvhdutil.getAllVHDJournals(self.lvmCache)
1173 if len(journals) == 0: 1173 ↛ 1175line 1173 didn't jump to line 1175, because the condition on line 1173 was never false
1174 return
1175 self._loadvdis()
1176 for uuid, jlvName in journals:
1177 vdi = self.vdis[uuid]
1178 util.SMlog("Found VHD journal %s, reverting %s" % (uuid, vdi.path))
1179 self.lvActivator.activate(uuid, vdi.lvname, False)
1180 self.lvmCache.activateNoRefcount(jlvName)
1181 fullSize = lvhdutil.calcSizeVHDLV(vdi.size)
1182 lvhdutil.inflate(self.journaler, self.uuid, vdi.uuid, fullSize)
1183 try:
1184 jFile = os.path.join(self.path, jlvName)
1185 vhdutil.revert(vdi.path, jFile)
1186 except util.CommandException:
1187 util.logException("VHD journal revert")
1188 vhdutil.check(vdi.path)
1189 util.SMlog("VHD revert failed but VHD ok: removing journal")
1190 # Attempt to reclaim unused space
1191 vhdInfo = vhdutil.getVHDInfo(vdi.path, lvhdutil.extractUuid, False)
1192 NewSize = lvhdutil.calcSizeVHDLV(vhdInfo.sizeVirt)
1193 if NewSize < fullSize:
1194 lvhdutil.deflate(self.lvmCache, vdi.lvname, int(NewSize))
1195 lvhdutil.lvRefreshOnAllSlaves(self.session, self.uuid,
1196 self.vgname, vdi.lvname, uuid)
1197 self.lvmCache.remove(jlvName)
1198 delattr(self, "vdiInfo")
1199 delattr(self, "allVDIs")
1201 def call_on_slave(self, args, host_refs, message: str):
1202 master_ref = util.get_this_host_ref(self.session)
1203 for hostRef in host_refs:
1204 if hostRef == master_ref: 1204 ↛ 1205line 1204 didn't jump to line 1205, because the condition on line 1204 was never true
1205 continue
1206 util.SMlog(f"{message} on slave {hostRef}")
1207 rv = self.session.xenapi.host.call_plugin(
1208 hostRef, self.PLUGIN_ON_SLAVE, "multi", args)
1209 util.SMlog("call-plugin returned: %s" % rv)
1210 if not rv: 1210 ↛ 1211line 1210 didn't jump to line 1211, because the condition on line 1210 was never true
1211 raise Exception('plugin %s failed' % self.PLUGIN_ON_SLAVE)
1213 def _updateSlavesPreClone(self, hostRefs, origOldLV):
1214 args = {"vgName": self.vgname,
1215 "action1": "deactivateNoRefcount",
1216 "lvName1": origOldLV}
1217 message = "Deactivate VDI"
1218 self.call_on_slave(args, hostRefs, message)
1220 def _updateSlavesOnClone(self, hostRefs, origOldLV, origLV,
1221 baseUuid, baseLV):
1222 """We need to reactivate the original LV on each slave (note that the
1223 name for the original LV might change), as well as init the refcount
1224 for the base LV"""
1225 args = {"vgName": self.vgname,
1226 "action1": "refresh",
1227 "lvName1": origLV,
1228 "action2": "activate",
1229 "ns2": lvhdutil.NS_PREFIX_LVM + self.uuid,
1230 "lvName2": baseLV,
1231 "uuid2": baseUuid}
1233 message = f"Updating {origOldLV}, {origLV}, {baseLV}"
1234 self.call_on_slave(args, hostRefs, message)
1236 def _updateSlavesOnCBTClone(self, hostRefs, cbtlog):
1237 """Reactivate and refresh CBT log file on slaves"""
1238 args = {"vgName": self.vgname,
1239 "action1": "deactivateNoRefcount",
1240 "lvName1": cbtlog,
1241 "action2": "refresh",
1242 "lvName2": cbtlog}
1244 message = f"Updating {cbtlog}"
1245 self.call_on_slave(args, hostRefs, message)
1247 def _updateSlavesOnRemove(self, hostRefs, baseUuid, baseLV):
1248 """Tell the slave we deleted the base image"""
1249 args = {"vgName": self.vgname,
1250 "action1": "cleanupLockAndRefcount",
1251 "uuid1": baseUuid,
1252 "ns1": lvhdutil.NS_PREFIX_LVM + self.uuid}
1254 message = f"Cleaning locks for {baseLV}"
1255 self.call_on_slave(args, hostRefs, message)
1257 def _cleanup(self, skipLockCleanup=False):
1258 """delete stale refcounter, flag, and lock files"""
1259 RefCounter.resetAll(lvhdutil.NS_PREFIX_LVM + self.uuid)
1260 IPCFlag(self.uuid).clearAll()
1261 if not skipLockCleanup: 1261 ↛ 1262line 1261 didn't jump to line 1262, because the condition on line 1261 was never true
1262 Lock.cleanupAll(self.uuid)
1263 Lock.cleanupAll(lvhdutil.NS_PREFIX_LVM + self.uuid)
1265 def _prepareTestMode(self):
1266 util.SMlog("Test mode: %s" % self.testMode)
1267 if self.ENV_VAR_VHD_TEST.get(self.testMode): 1267 ↛ 1268line 1267 didn't jump to line 1268, because the condition on line 1267 was never true
1268 os.environ[self.ENV_VAR_VHD_TEST[self.testMode]] = "yes"
1269 util.SMlog("Setting env %s" % self.ENV_VAR_VHD_TEST[self.testMode])
1271 def _kickGC(self):
1272 util.SMlog("Kicking GC")
1273 cleanup.start_gc_service(self.uuid)
1275 def ensureCBTSpace(self):
1276 # Ensure we have space for at least one LV
1277 self._ensureSpaceAvailable(self.journaler.LV_SIZE)
1280class LVHDVDI(VDI.VDI):
1282 JRN_CLONE = "clone" # journal entry type for the clone operation
1284 def load(self, vdi_uuid):
1285 self.lock = self.sr.lock
1286 self.lvActivator = self.sr.lvActivator
1287 self.loaded = False
1288 self.vdi_type = vhdutil.VDI_TYPE_VHD
1289 if self.sr.legacyMode or util.fistpoint.is_active("xenrt_default_vdi_type_legacy"):
1290 self.vdi_type = vhdutil.VDI_TYPE_RAW
1291 self.uuid = vdi_uuid
1292 self.location = self.uuid
1293 self.exists = True
1295 if hasattr(self.sr, "vdiInfo") and self.sr.vdiInfo.get(self.uuid):
1296 self._initFromVDIInfo(self.sr.vdiInfo[self.uuid])
1297 if self.parent: 1297 ↛ 1298line 1297 didn't jump to line 1298, because the condition on line 1297 was never true
1298 self.sm_config_override['vhd-parent'] = self.parent
1299 else:
1300 self.sm_config_override['vhd-parent'] = None
1301 return
1303 # scan() didn't run: determine the type of the VDI manually
1304 if self._determineType():
1305 return
1307 # the VDI must be in the process of being created
1308 self.exists = False
1309 if "vdi_sm_config" in self.sr.srcmd.params and \ 1309 ↛ 1311line 1309 didn't jump to line 1311, because the condition on line 1309 was never true
1310 "type" in self.sr.srcmd.params["vdi_sm_config"]:
1311 type = self.sr.srcmd.params["vdi_sm_config"]["type"]
1312 if type == PARAM_RAW:
1313 self.vdi_type = vhdutil.VDI_TYPE_RAW
1314 elif type == PARAM_VHD:
1315 self.vdi_type = vhdutil.VDI_TYPE_VHD
1316 if self.sr.cmd == 'vdi_create' and self.sr.legacyMode:
1317 raise xs_errors.XenError('VDICreate', \
1318 opterr='Cannot create VHD type disk in legacy mode')
1319 else:
1320 raise xs_errors.XenError('VDICreate', opterr='bad type')
1321 self.lvname = "%s%s" % (lvhdutil.LV_PREFIX[self.vdi_type], vdi_uuid)
1322 self.path = os.path.join(self.sr.path, self.lvname)
1324 def create(self, sr_uuid, vdi_uuid, size):
1325 util.SMlog("LVHDVDI.create for %s" % self.uuid)
1326 if not self.sr.isMaster:
1327 raise xs_errors.XenError('LVMMaster')
1328 if self.exists:
1329 raise xs_errors.XenError('VDIExists')
1331 size = vhdutil.validate_and_round_vhd_size(int(size))
1333 util.SMlog("LVHDVDI.create: type = %s, %s (size=%s)" % \
1334 (self.vdi_type, self.path, size))
1335 lvSize = 0
1336 self.sm_config = self.sr.srcmd.params["vdi_sm_config"]
1337 if self.vdi_type == vhdutil.VDI_TYPE_RAW:
1338 lvSize = util.roundup(lvutil.LVM_SIZE_INCREMENT, int(size))
1339 else:
1340 if self.sr.provision == "thin":
1341 lvSize = util.roundup(lvutil.LVM_SIZE_INCREMENT,
1342 vhdutil.calcOverheadEmpty(lvhdutil.MSIZE))
1343 elif self.sr.provision == "thick":
1344 lvSize = lvhdutil.calcSizeVHDLV(int(size))
1346 self.sr._ensureSpaceAvailable(lvSize)
1348 try:
1349 self.sr.lvmCache.create(self.lvname, lvSize)
1350 if self.vdi_type == vhdutil.VDI_TYPE_RAW:
1351 self.size = self.sr.lvmCache.getSize(self.lvname)
1352 else:
1353 vhdutil.create(self.path, int(size), False, lvhdutil.MSIZE_MB)
1354 self.size = vhdutil.getSizeVirt(self.path)
1355 self.sr.lvmCache.deactivateNoRefcount(self.lvname)
1356 except util.CommandException as e:
1357 util.SMlog("Unable to create VDI")
1358 self.sr.lvmCache.remove(self.lvname)
1359 raise xs_errors.XenError('VDICreate', opterr='error %d' % e.code)
1361 self.utilisation = lvSize
1362 self.sm_config["vdi_type"] = self.vdi_type
1364 if not self.sr.legacyMode:
1365 LVMMetadataHandler(self.sr.mdpath).ensureSpaceIsAvailableForVdis(1)
1367 self.ref = self._db_introduce()
1368 self.sr._updateStats(self.sr.uuid, self.size)
1370 vdi_info = {UUID_TAG: self.uuid,
1371 NAME_LABEL_TAG: util.to_plain_string(self.label),
1372 NAME_DESCRIPTION_TAG: util.to_plain_string(self.description),
1373 IS_A_SNAPSHOT_TAG: 0,
1374 SNAPSHOT_OF_TAG: '',
1375 SNAPSHOT_TIME_TAG: '',
1376 TYPE_TAG: self.ty,
1377 VDI_TYPE_TAG: self.vdi_type,
1378 READ_ONLY_TAG: int(self.read_only),
1379 MANAGED_TAG: int(self.managed),
1380 METADATA_OF_POOL_TAG: ''
1381 }
1383 if not self.sr.legacyMode:
1384 LVMMetadataHandler(self.sr.mdpath).addVdi(vdi_info)
1386 return VDI.VDI.get_params(self)
1388 def delete(self, sr_uuid, vdi_uuid, data_only=False):
1389 util.SMlog("LVHDVDI.delete for %s" % self.uuid)
1390 try:
1391 self._loadThis()
1392 except xs_errors.SRException as e:
1393 # Catch 'VDI doesn't exist' exception
1394 if e.errno == 46:
1395 return super(LVHDVDI, self).delete(sr_uuid, vdi_uuid, data_only)
1396 raise
1398 vdi_ref = self.sr.srcmd.params['vdi_ref']
1399 if not self.session.xenapi.VDI.get_managed(vdi_ref):
1400 raise xs_errors.XenError("VDIDelete", \
1401 opterr="Deleting non-leaf node not permitted")
1403 if not self.hidden:
1404 self._markHidden()
1406 if not data_only:
1407 # Remove from XAPI and delete from MGT
1408 self._db_forget()
1409 else:
1410 # If this is a data_destroy call, don't remove from XAPI db
1411 # Only delete from MGT
1412 if not self.sr.legacyMode:
1413 LVMMetadataHandler(self.sr.mdpath).deleteVdiFromMetadata(self.uuid)
1415 # deactivate here because it might be too late to do it in the "final"
1416 # step: GC might have removed the LV by then
1417 if self.sr.lvActivator.get(self.uuid, False):
1418 self.sr.lvActivator.deactivate(self.uuid, False)
1420 try:
1421 self.sr.lvmCache.remove(self.lvname)
1422 self.sr.lock.cleanup(vdi_uuid, lvhdutil.NS_PREFIX_LVM + sr_uuid)
1423 self.sr.lock.cleanupAll(vdi_uuid)
1424 except xs_errors.SRException as e:
1425 util.SMlog(
1426 "Failed to remove the volume (maybe is leaf coalescing) "
1427 "for %s err:%d" % (self.uuid, e.errno))
1429 self.sr._updateStats(self.sr.uuid, -self.size)
1430 self.sr._kickGC()
1431 return super(LVHDVDI, self).delete(sr_uuid, vdi_uuid, data_only)
1433 def attach(self, sr_uuid, vdi_uuid):
1434 util.SMlog("LVHDVDI.attach for %s" % self.uuid)
1435 if self.sr.journaler.hasJournals(self.uuid):
1436 raise xs_errors.XenError('VDIUnavailable',
1437 opterr='Interrupted operation detected on this VDI, '
1438 'scan SR first to trigger auto-repair')
1440 writable = ('args' not in self.sr.srcmd.params) or \
1441 (self.sr.srcmd.params['args'][0] == "true")
1442 needInflate = True
1443 if self.vdi_type == vhdutil.VDI_TYPE_RAW or not writable:
1444 needInflate = False
1445 else:
1446 self._loadThis()
1447 if self.utilisation >= lvhdutil.calcSizeVHDLV(self.size):
1448 needInflate = False
1450 if needInflate:
1451 try:
1452 self._prepareThin(True)
1453 except:
1454 util.logException("attach")
1455 raise xs_errors.XenError('LVMProvisionAttach')
1457 try:
1458 return self._attach()
1459 finally:
1460 if not self.sr.lvActivator.deactivateAll():
1461 util.SMlog("Failed to deactivate LVs back (%s)" % self.uuid)
1463 def detach(self, sr_uuid, vdi_uuid):
1464 util.SMlog("LVHDVDI.detach for %s" % self.uuid)
1465 self._loadThis()
1466 already_deflated = (self.utilisation < \
1467 lvhdutil.calcSizeVHDLV(self.size))
1468 needDeflate = True
1469 if self.vdi_type == vhdutil.VDI_TYPE_RAW or already_deflated:
1470 needDeflate = False
1471 elif self.sr.provision == "thick":
1472 needDeflate = False
1473 # except for snapshots, which are always deflated
1474 if self.sr.srcmd.cmd != 'vdi_detach_from_config':
1475 vdi_ref = self.sr.srcmd.params['vdi_ref']
1476 snap = self.session.xenapi.VDI.get_is_a_snapshot(vdi_ref)
1477 if snap:
1478 needDeflate = True
1480 if needDeflate:
1481 try:
1482 self._prepareThin(False)
1483 except:
1484 util.logException("_prepareThin")
1485 raise xs_errors.XenError('VDIUnavailable', opterr='deflate')
1487 try:
1488 self._detach()
1489 finally:
1490 if not self.sr.lvActivator.deactivateAll():
1491 raise xs_errors.XenError("SMGeneral", opterr="deactivation")
1493 # We only support offline resize
1494 def resize(self, sr_uuid, vdi_uuid, size):
1495 util.SMlog("LVHDVDI.resize for %s" % self.uuid)
1496 if not self.sr.isMaster:
1497 raise xs_errors.XenError('LVMMaster')
1499 self._loadThis()
1500 if self.hidden:
1501 raise xs_errors.XenError('VDIUnavailable', opterr='hidden VDI')
1503 if size < self.size:
1504 util.SMlog('vdi_resize: shrinking not supported: ' + \
1505 '(current size: %d, new size: %d)' % (self.size, size))
1506 raise xs_errors.XenError('VDISize', opterr='shrinking not allowed')
1508 size = vhdutil.validate_and_round_vhd_size(int(size))
1510 if size == self.size:
1511 return VDI.VDI.get_params(self)
1513 if self.vdi_type == vhdutil.VDI_TYPE_RAW:
1514 lvSizeOld = self.size
1515 lvSizeNew = util.roundup(lvutil.LVM_SIZE_INCREMENT, size)
1516 else:
1517 lvSizeOld = self.utilisation
1518 lvSizeNew = lvhdutil.calcSizeVHDLV(size)
1519 if self.sr.provision == "thin":
1520 # VDI is currently deflated, so keep it deflated
1521 lvSizeNew = lvSizeOld
1522 assert(lvSizeNew >= lvSizeOld)
1523 spaceNeeded = lvSizeNew - lvSizeOld
1524 self.sr._ensureSpaceAvailable(spaceNeeded)
1526 oldSize = self.size
1527 if self.vdi_type == vhdutil.VDI_TYPE_RAW:
1528 self.sr.lvmCache.setSize(self.lvname, lvSizeNew)
1529 self.size = self.sr.lvmCache.getSize(self.lvname)
1530 self.utilisation = self.size
1531 else:
1532 if lvSizeNew != lvSizeOld:
1533 lvhdutil.inflate(self.sr.journaler, self.sr.uuid, self.uuid,
1534 lvSizeNew)
1535 vhdutil.setSizeVirtFast(self.path, size)
1536 self.size = vhdutil.getSizeVirt(self.path)
1537 self.utilisation = self.sr.lvmCache.getSize(self.lvname)
1539 vdi_ref = self.sr.srcmd.params['vdi_ref']
1540 self.session.xenapi.VDI.set_virtual_size(vdi_ref, str(self.size))
1541 self.session.xenapi.VDI.set_physical_utilisation(vdi_ref,
1542 str(self.utilisation))
1543 self.sr._updateStats(self.sr.uuid, self.size - oldSize)
1544 super(LVHDVDI, self).resize_cbt(self.sr.uuid, self.uuid, self.size)
1545 return VDI.VDI.get_params(self)
1547 def clone(self, sr_uuid, vdi_uuid):
1548 return self._do_snapshot(
1549 sr_uuid, vdi_uuid, VDI.SNAPSHOT_DOUBLE, cloneOp=True)
1551 def compose(self, sr_uuid, vdi1, vdi2):
1552 util.SMlog("LVHDSR.compose for %s -> %s" % (vdi2, vdi1))
1553 if self.vdi_type != vhdutil.VDI_TYPE_VHD:
1554 raise xs_errors.XenError('Unimplemented')
1556 parent_uuid = vdi1
1557 parent_lvname = lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_VHD] + parent_uuid
1558 assert(self.sr.lvmCache.checkLV(parent_lvname))
1559 parent_path = os.path.join(self.sr.path, parent_lvname)
1561 self.sr.lvActivator.activate(self.uuid, self.lvname, False)
1562 self.sr.lvActivator.activate(parent_uuid, parent_lvname, False)
1564 vhdutil.setParent(self.path, parent_path, False)
1565 vhdutil.setHidden(parent_path)
1566 self.sr.session.xenapi.VDI.set_managed(self.sr.srcmd.params['args'][0], False)
1568 if not blktap2.VDI.tap_refresh(self.session, self.sr.uuid, self.uuid,
1569 True):
1570 raise util.SMException("failed to refresh VDI %s" % self.uuid)
1572 util.SMlog("Compose done")
1574 def reset_leaf(self, sr_uuid, vdi_uuid):
1575 util.SMlog("LVHDSR.reset_leaf for %s" % vdi_uuid)
1576 if self.vdi_type != vhdutil.VDI_TYPE_VHD:
1577 raise xs_errors.XenError('Unimplemented')
1579 self.sr.lvActivator.activate(self.uuid, self.lvname, False)
1581 # safety check
1582 if not vhdutil.hasParent(self.path):
1583 raise util.SMException("ERROR: VDI %s has no parent, " + \
1584 "will not reset contents" % self.uuid)
1586 vhdutil.killData(self.path)
1588 def _attach(self):
1589 self._chainSetActive(True, True, True)
1590 if not util.pathexists(self.path):
1591 raise xs_errors.XenError('VDIUnavailable', \
1592 opterr='Could not find: %s' % self.path)
1594 if not hasattr(self, 'xenstore_data'):
1595 self.xenstore_data = {}
1597 self.xenstore_data.update(scsiutil.update_XS_SCSIdata(self.uuid, \
1598 scsiutil.gen_synthetic_page_data(self.uuid)))
1600 self.xenstore_data['storage-type'] = 'lvm'
1601 self.xenstore_data['vdi-type'] = self.vdi_type
1603 self.attached = True
1604 self.sr.lvActivator.persist()
1605 return VDI.VDI.attach(self, self.sr.uuid, self.uuid)
1607 def _detach(self):
1608 self._chainSetActive(False, True)
1609 self.attached = False
1611 def _do_snapshot(self, sr_uuid, vdi_uuid, snapType,
1612 cloneOp=False, secondary=None, cbtlog=None):
1613 # If cbt enabled, save file consistency state
1614 if cbtlog is not None:
1615 if blktap2.VDI.tap_status(self.session, vdi_uuid): 1615 ↛ 1616line 1615 didn't jump to line 1616, because the condition on line 1615 was never true
1616 consistency_state = False
1617 else:
1618 consistency_state = True
1619 util.SMlog("Saving log consistency state of %s for vdi: %s" %
1620 (consistency_state, vdi_uuid))
1621 else:
1622 consistency_state = None
1624 pause_time = time.time()
1625 if not blktap2.VDI.tap_pause(self.session, sr_uuid, vdi_uuid): 1625 ↛ 1626line 1625 didn't jump to line 1626, because the condition on line 1625 was never true
1626 raise util.SMException("failed to pause VDI %s" % vdi_uuid)
1628 snapResult = None
1629 try:
1630 snapResult = self._snapshot(snapType, cloneOp, cbtlog, consistency_state)
1631 except Exception as e1:
1632 try:
1633 blktap2.VDI.tap_unpause(self.session, sr_uuid, vdi_uuid,
1634 secondary=None)
1635 except Exception as e2:
1636 util.SMlog('WARNING: failed to clean up failed snapshot: '
1637 '%s (error ignored)' % e2)
1638 raise
1639 self.disable_leaf_on_secondary(vdi_uuid, secondary=secondary)
1640 blktap2.VDI.tap_unpause(self.session, sr_uuid, vdi_uuid, secondary)
1641 unpause_time = time.time()
1642 if (unpause_time - pause_time) > LONG_SNAPTIME: 1642 ↛ 1643line 1642 didn't jump to line 1643, because the condition on line 1642 was never true
1643 util.SMlog('WARNING: snapshot paused VM for %s seconds' %
1644 (unpause_time - pause_time))
1645 return snapResult
1647 def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None):
1648 util.SMlog("LVHDVDI._snapshot for %s (type %s)" % (self.uuid, snapType))
1650 if not self.sr.isMaster: 1650 ↛ 1651line 1650 didn't jump to line 1651, because the condition on line 1650 was never true
1651 raise xs_errors.XenError('LVMMaster')
1652 if self.sr.legacyMode: 1652 ↛ 1653line 1652 didn't jump to line 1653, because the condition on line 1652 was never true
1653 raise xs_errors.XenError('Unimplemented', opterr='In legacy mode')
1655 self._loadThis()
1656 if self.hidden: 1656 ↛ 1657line 1656 didn't jump to line 1657, because the condition on line 1656 was never true
1657 raise xs_errors.XenError('VDISnapshot', opterr='hidden VDI')
1659 self.sm_config = self.session.xenapi.VDI.get_sm_config( \
1660 self.sr.srcmd.params['vdi_ref'])
1661 if "type" in self.sm_config and self.sm_config['type'] == 'raw': 1661 ↛ 1662line 1661 didn't jump to line 1662, because the condition on line 1661 was never true
1662 if not util.fistpoint.is_active("testsm_clone_allow_raw"):
1663 raise xs_errors.XenError('Unimplemented', \
1664 opterr='Raw VDI, snapshot or clone not permitted')
1666 # we must activate the entire VHD chain because the real parent could
1667 # theoretically be anywhere in the chain if all VHDs under it are empty
1668 self._chainSetActive(True, False)
1669 if not util.pathexists(self.path): 1669 ↛ 1670line 1669 didn't jump to line 1670, because the condition on line 1669 was never true
1670 raise xs_errors.XenError('VDIUnavailable', \
1671 opterr='VDI unavailable: %s' % (self.path))
1673 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 1673 ↛ 1681line 1673 didn't jump to line 1681, because the condition on line 1673 was never false
1674 depth = vhdutil.getDepth(self.path)
1675 if depth == -1: 1675 ↛ 1676line 1675 didn't jump to line 1676, because the condition on line 1675 was never true
1676 raise xs_errors.XenError('VDIUnavailable', \
1677 opterr='failed to get VHD depth')
1678 elif depth >= vhdutil.MAX_CHAIN_SIZE: 1678 ↛ 1679line 1678 didn't jump to line 1679, because the condition on line 1678 was never true
1679 raise xs_errors.XenError('SnapshotChainTooLong')
1681 self.issnap = self.session.xenapi.VDI.get_is_a_snapshot( \
1682 self.sr.srcmd.params['vdi_ref'])
1684 fullpr = lvhdutil.calcSizeVHDLV(self.size)
1685 thinpr = util.roundup(lvutil.LVM_SIZE_INCREMENT, \
1686 vhdutil.calcOverheadEmpty(lvhdutil.MSIZE))
1687 lvSizeOrig = thinpr
1688 lvSizeClon = thinpr
1690 hostRefs = []
1691 if self.sr.cmd == "vdi_snapshot":
1692 hostRefs = util.get_hosts_attached_on(self.session, [self.uuid])
1693 if hostRefs: 1693 ↛ 1695line 1693 didn't jump to line 1695, because the condition on line 1693 was never false
1694 lvSizeOrig = fullpr
1695 if self.sr.provision == "thick": 1695 ↛ 1701line 1695 didn't jump to line 1701, because the condition on line 1695 was never false
1696 if not self.issnap:
1697 lvSizeOrig = fullpr
1698 if self.sr.cmd != "vdi_snapshot":
1699 lvSizeClon = fullpr
1701 if (snapType == VDI.SNAPSHOT_SINGLE or 1701 ↛ 1703line 1701 didn't jump to line 1703, because the condition on line 1701 was never true
1702 snapType == VDI.SNAPSHOT_INTERNAL):
1703 lvSizeClon = 0
1705 # the space required must include 2 journal LVs: a clone journal and an
1706 # inflate journal (for the failure handling
1707 size_req = lvSizeOrig + lvSizeClon + 2 * self.sr.journaler.LV_SIZE
1708 lvSizeBase = self.size
1709 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 1709 ↛ 1713line 1709 didn't jump to line 1713, because the condition on line 1709 was never false
1710 lvSizeBase = util.roundup(lvutil.LVM_SIZE_INCREMENT,
1711 vhdutil.getSizePhys(self.path))
1712 size_req -= (self.utilisation - lvSizeBase)
1713 self.sr._ensureSpaceAvailable(size_req)
1715 if hostRefs:
1716 self.sr._updateSlavesPreClone(hostRefs, self.lvname)
1718 baseUuid = util.gen_uuid()
1719 origUuid = self.uuid
1720 clonUuid = ""
1721 if snapType == VDI.SNAPSHOT_DOUBLE: 1721 ↛ 1723line 1721 didn't jump to line 1723, because the condition on line 1721 was never false
1722 clonUuid = util.gen_uuid()
1723 jval = "%s_%s" % (baseUuid, clonUuid)
1724 self.sr.journaler.create(self.JRN_CLONE, origUuid, jval)
1725 util.fistpoint.activate("LVHDRT_clone_vdi_after_create_journal", self.sr.uuid)
1727 try:
1728 # self becomes the "base vdi"
1729 origOldLV = self.lvname
1730 baseLV = lvhdutil.LV_PREFIX[self.vdi_type] + baseUuid
1731 self.sr.lvmCache.rename(self.lvname, baseLV)
1732 self.sr.lvActivator.replace(self.uuid, baseUuid, baseLV, False)
1733 RefCounter.set(baseUuid, 1, 0, lvhdutil.NS_PREFIX_LVM + self.sr.uuid)
1734 self.uuid = baseUuid
1735 self.lvname = baseLV
1736 self.path = os.path.join(self.sr.path, baseLV)
1737 self.label = "base copy"
1738 self.read_only = True
1739 self.location = self.uuid
1740 self.managed = False
1742 # shrink the base copy to the minimum - we do it before creating
1743 # the snapshot volumes to avoid requiring double the space
1744 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 1744 ↛ 1747line 1744 didn't jump to line 1747, because the condition on line 1744 was never false
1745 lvhdutil.deflate(self.sr.lvmCache, self.lvname, lvSizeBase)
1746 self.utilisation = lvSizeBase
1747 util.fistpoint.activate("LVHDRT_clone_vdi_after_shrink_parent", self.sr.uuid)
1749 snapVDI = self._createSnap(origUuid, lvSizeOrig, False)
1750 util.fistpoint.activate("LVHDRT_clone_vdi_after_first_snap", self.sr.uuid)
1751 snapVDI2 = None
1752 if snapType == VDI.SNAPSHOT_DOUBLE: 1752 ↛ 1758line 1752 didn't jump to line 1758, because the condition on line 1752 was never false
1753 snapVDI2 = self._createSnap(clonUuid, lvSizeClon, True)
1754 # If we have CBT enabled on the VDI,
1755 # set CBT status for the new snapshot disk
1756 if cbtlog:
1757 snapVDI2.cbt_enabled = True
1758 util.fistpoint.activate("LVHDRT_clone_vdi_after_second_snap", self.sr.uuid)
1760 # note: it is important to mark the parent hidden only AFTER the
1761 # new VHD children have been created, which are referencing it;
1762 # otherwise we would introduce a race with GC that could reclaim
1763 # the parent before we snapshot it
1764 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 1764 ↛ 1765line 1764 didn't jump to line 1765, because the condition on line 1764 was never true
1765 self.sr.lvmCache.setHidden(self.lvname)
1766 else:
1767 vhdutil.setHidden(self.path)
1768 util.fistpoint.activate("LVHDRT_clone_vdi_after_parent_hidden", self.sr.uuid)
1770 # set the base copy to ReadOnly
1771 self.sr.lvmCache.setReadonly(self.lvname, True)
1772 util.fistpoint.activate("LVHDRT_clone_vdi_after_parent_ro", self.sr.uuid)
1774 if hostRefs:
1775 self.sr._updateSlavesOnClone(hostRefs, origOldLV,
1776 snapVDI.lvname, self.uuid, self.lvname)
1778 # Update cbt files if user created snapshot (SNAPSHOT_DOUBLE)
1779 if snapType == VDI.SNAPSHOT_DOUBLE and cbtlog:
1780 snapVDI._cbt_snapshot(clonUuid, cbt_consistency)
1781 if hostRefs: 1781 ↛ 1795line 1781 didn't jump to line 1795, because the condition on line 1781 was never false
1782 cbtlog_file = self._get_cbt_logname(snapVDI.uuid)
1783 try:
1784 self.sr._updateSlavesOnCBTClone(hostRefs, cbtlog_file)
1785 except:
1786 alert_name = "VDI_CBT_SNAPSHOT_FAILED"
1787 alert_str = ("Creating CBT snapshot for {} failed"
1788 .format(snapVDI.uuid))
1789 snapVDI._disable_cbt_on_error(alert_name, alert_str)
1790 pass
1792 except (util.SMException, XenAPI.Failure) as e:
1793 util.logException("LVHDVDI._snapshot")
1794 self._failClone(origUuid, jval, str(e))
1795 util.fistpoint.activate("LVHDRT_clone_vdi_before_remove_journal", self.sr.uuid)
1797 self.sr.journaler.remove(self.JRN_CLONE, origUuid)
1799 return self._finishSnapshot(snapVDI, snapVDI2, hostRefs, cloneOp, snapType)
1801 def _createSnap(self, snapUuid, snapSizeLV, isNew):
1802 """Snapshot self and return the snapshot VDI object"""
1803 snapLV = lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_VHD] + snapUuid
1804 snapPath = os.path.join(self.sr.path, snapLV)
1805 self.sr.lvmCache.create(snapLV, int(snapSizeLV))
1806 util.fistpoint.activate("LVHDRT_clone_vdi_after_lvcreate", self.sr.uuid)
1807 if isNew:
1808 RefCounter.set(snapUuid, 1, 0, lvhdutil.NS_PREFIX_LVM + self.sr.uuid)
1809 self.sr.lvActivator.add(snapUuid, snapLV, False)
1810 parentRaw = (self.vdi_type == vhdutil.VDI_TYPE_RAW)
1811 vhdutil.snapshot(snapPath, self.path, parentRaw, lvhdutil.MSIZE_MB)
1812 snapParent = vhdutil.getParent(snapPath, lvhdutil.extractUuid)
1814 snapVDI = LVHDVDI(self.sr, snapUuid)
1815 snapVDI.read_only = False
1816 snapVDI.location = snapUuid
1817 snapVDI.size = self.size
1818 snapVDI.utilisation = snapSizeLV
1819 snapVDI.sm_config = dict()
1820 for key, val in self.sm_config.items():
1821 if key not in [ 1821 ↛ 1824line 1821 didn't jump to line 1824, because the condition on line 1821 was never true
1822 "type", "vdi_type", "vhd-parent", "paused", "relinking", "activating"] and \
1823 not key.startswith("host_"):
1824 snapVDI.sm_config[key] = val
1825 snapVDI.sm_config["vdi_type"] = vhdutil.VDI_TYPE_VHD
1826 snapVDI.sm_config["vhd-parent"] = snapParent
1827 snapVDI.lvname = snapLV
1828 return snapVDI
1830 def _finishSnapshot(self, snapVDI, snapVDI2, hostRefs, cloneOp=False, snapType=None):
1831 if snapType is not VDI.SNAPSHOT_INTERNAL: 1831 ↛ 1833line 1831 didn't jump to line 1833, because the condition on line 1831 was never false
1832 self.sr._updateStats(self.sr.uuid, self.size)
1833 basePresent = True
1835 # Verify parent locator field of both children and delete basePath if
1836 # unused
1837 snapParent = snapVDI.sm_config["vhd-parent"]
1838 snap2Parent = ""
1839 if snapVDI2: 1839 ↛ 1841line 1839 didn't jump to line 1841, because the condition on line 1839 was never false
1840 snap2Parent = snapVDI2.sm_config["vhd-parent"]
1841 if snapParent != self.uuid and \ 1841 ↛ 1868line 1841 didn't jump to line 1868, because the condition on line 1841 was never false
1842 (not snapVDI2 or snap2Parent != self.uuid):
1843 util.SMlog("%s != %s != %s => deleting unused base %s" % \
1844 (snapParent, self.uuid, snap2Parent, self.lvname))
1845 RefCounter.put(self.uuid, False, lvhdutil.NS_PREFIX_LVM + self.sr.uuid)
1846 self.sr.lvmCache.remove(self.lvname)
1847 self.sr.lvActivator.remove(self.uuid, False)
1848 if hostRefs:
1849 self.sr._updateSlavesOnRemove(hostRefs, self.uuid, self.lvname)
1850 basePresent = False
1851 else:
1852 # assign the _binary_ refcount of the original VDI to the new base
1853 # VDI (but as the normal refcount, since binary refcounts are only
1854 # for leaf nodes). The normal refcount of the child is not
1855 # transferred to to the base VDI because normal refcounts are
1856 # incremented and decremented individually, and not based on the
1857 # VHD chain (i.e., the child's normal refcount will be decremented
1858 # independently of its parent situation). Add 1 for this clone op.
1859 # Note that we do not need to do protect the refcount operations
1860 # below with per-VDI locking like we do in lvutil because at this
1861 # point we have exclusive access to the VDIs involved. Other SM
1862 # operations are serialized by the Agent or with the SR lock, and
1863 # any coalesce activations are serialized with the SR lock. (The
1864 # coalesce activates the coalesced VDI pair in the beginning, which
1865 # cannot affect the VDIs here because they cannot possibly be
1866 # involved in coalescing at this point, and at the relinkSkip step
1867 # that activates the children, which takes the SR lock.)
1868 ns = lvhdutil.NS_PREFIX_LVM + self.sr.uuid
1869 (cnt, bcnt) = RefCounter.check(snapVDI.uuid, ns)
1870 RefCounter.set(self.uuid, bcnt + 1, 0, ns)
1872 # the "paused" and "host_*" sm-config keys are special and must stay on
1873 # the leaf without being inherited by anyone else
1874 for key in [x for x in self.sm_config.keys() if x == "paused" or x.startswith("host_")]: 1874 ↛ 1875line 1874 didn't jump to line 1875, because the loop on line 1874 never started
1875 snapVDI.sm_config[key] = self.sm_config[key]
1876 del self.sm_config[key]
1878 # Introduce any new VDI records & update the existing one
1879 type = self.session.xenapi.VDI.get_type( \
1880 self.sr.srcmd.params['vdi_ref'])
1881 if snapVDI2: 1881 ↛ 1923line 1881 didn't jump to line 1923, because the condition on line 1881 was never false
1882 LVMMetadataHandler(self.sr.mdpath).ensureSpaceIsAvailableForVdis(1)
1883 vdiRef = snapVDI2._db_introduce()
1884 if cloneOp:
1885 vdi_info = {UUID_TAG: snapVDI2.uuid,
1886 NAME_LABEL_TAG: util.to_plain_string( \
1887 self.session.xenapi.VDI.get_name_label( \
1888 self.sr.srcmd.params['vdi_ref'])),
1889 NAME_DESCRIPTION_TAG: util.to_plain_string( \
1890 self.session.xenapi.VDI.get_name_description(self.sr.srcmd.params['vdi_ref'])),
1891 IS_A_SNAPSHOT_TAG: 0,
1892 SNAPSHOT_OF_TAG: '',
1893 SNAPSHOT_TIME_TAG: '',
1894 TYPE_TAG: type,
1895 VDI_TYPE_TAG: snapVDI2.sm_config['vdi_type'],
1896 READ_ONLY_TAG: 0,
1897 MANAGED_TAG: int(snapVDI2.managed),
1898 METADATA_OF_POOL_TAG: ''
1899 }
1900 else:
1901 util.SMlog("snapshot VDI params: %s" % \
1902 self.session.xenapi.VDI.get_snapshot_time(vdiRef))
1903 vdi_info = {UUID_TAG: snapVDI2.uuid,
1904 NAME_LABEL_TAG: util.to_plain_string( \
1905 self.session.xenapi.VDI.get_name_label( \
1906 self.sr.srcmd.params['vdi_ref'])),
1907 NAME_DESCRIPTION_TAG: util.to_plain_string( \
1908 self.session.xenapi.VDI.get_name_description(self.sr.srcmd.params['vdi_ref'])),
1909 IS_A_SNAPSHOT_TAG: 1,
1910 SNAPSHOT_OF_TAG: snapVDI.uuid,
1911 SNAPSHOT_TIME_TAG: '',
1912 TYPE_TAG: type,
1913 VDI_TYPE_TAG: snapVDI2.sm_config['vdi_type'],
1914 READ_ONLY_TAG: 0,
1915 MANAGED_TAG: int(snapVDI2.managed),
1916 METADATA_OF_POOL_TAG: ''
1917 }
1919 LVMMetadataHandler(self.sr.mdpath).addVdi(vdi_info)
1920 util.SMlog("vdi_clone: introduced 2nd snap VDI: %s (%s)" % \
1921 (vdiRef, snapVDI2.uuid))
1923 if basePresent: 1923 ↛ 1924line 1923 didn't jump to line 1924, because the condition on line 1923 was never true
1924 LVMMetadataHandler(self.sr.mdpath).ensureSpaceIsAvailableForVdis(1)
1925 vdiRef = self._db_introduce()
1926 vdi_info = {UUID_TAG: self.uuid,
1927 NAME_LABEL_TAG: self.label,
1928 NAME_DESCRIPTION_TAG: self.description,
1929 IS_A_SNAPSHOT_TAG: 0,
1930 SNAPSHOT_OF_TAG: '',
1931 SNAPSHOT_TIME_TAG: '',
1932 TYPE_TAG: type,
1933 VDI_TYPE_TAG: self.sm_config['vdi_type'],
1934 READ_ONLY_TAG: 1,
1935 MANAGED_TAG: 0,
1936 METADATA_OF_POOL_TAG: ''
1937 }
1939 LVMMetadataHandler(self.sr.mdpath).addVdi(vdi_info)
1940 util.SMlog("vdi_clone: introduced base VDI: %s (%s)" % \
1941 (vdiRef, self.uuid))
1943 # Update the original record
1944 vdi_ref = self.sr.srcmd.params['vdi_ref']
1945 self.session.xenapi.VDI.set_sm_config(vdi_ref, snapVDI.sm_config)
1946 self.session.xenapi.VDI.set_physical_utilisation(vdi_ref, \
1947 str(snapVDI.utilisation))
1949 # Return the info on the new snap VDI
1950 snap = snapVDI2
1951 if not snap: 1951 ↛ 1952line 1951 didn't jump to line 1952, because the condition on line 1951 was never true
1952 snap = self
1953 if not basePresent:
1954 # a single-snapshot of an empty VDI will be a noop, resulting
1955 # in no new VDIs, so return the existing one. The GC wouldn't
1956 # normally try to single-snapshot an empty VHD of course, but
1957 # if an external snapshot operation manages to sneak in right
1958 # before a snapshot-coalesce phase, we would get here
1959 snap = snapVDI
1960 return snap.get_params()
1962 def _initFromVDIInfo(self, vdiInfo):
1963 self.vdi_type = vdiInfo.vdiType
1964 self.lvname = vdiInfo.lvName
1965 self.size = vdiInfo.sizeVirt
1966 self.utilisation = vdiInfo.sizeLV
1967 self.hidden = vdiInfo.hidden
1968 if self.hidden: 1968 ↛ 1969line 1968 didn't jump to line 1969, because the condition on line 1968 was never true
1969 self.managed = False
1970 self.active = vdiInfo.lvActive
1971 self.readonly = vdiInfo.lvReadonly
1972 self.parent = vdiInfo.parentUuid
1973 self.path = os.path.join(self.sr.path, self.lvname)
1974 if hasattr(self, "sm_config_override"): 1974 ↛ 1977line 1974 didn't jump to line 1977, because the condition on line 1974 was never false
1975 self.sm_config_override["vdi_type"] = self.vdi_type
1976 else:
1977 self.sm_config_override = {'vdi_type': self.vdi_type}
1978 self.loaded = True
1980 def _initFromLVInfo(self, lvInfo):
1981 self.vdi_type = lvInfo.vdiType
1982 self.lvname = lvInfo.name
1983 self.size = lvInfo.size
1984 self.utilisation = lvInfo.size
1985 self.hidden = lvInfo.hidden
1986 self.active = lvInfo.active
1987 self.readonly = lvInfo.readonly
1988 self.parent = ''
1989 self.path = os.path.join(self.sr.path, self.lvname)
1990 if hasattr(self, "sm_config_override"): 1990 ↛ 1993line 1990 didn't jump to line 1993, because the condition on line 1990 was never false
1991 self.sm_config_override["vdi_type"] = self.vdi_type
1992 else:
1993 self.sm_config_override = {'vdi_type': self.vdi_type}
1994 if 'vhd-parent' in self.sm_config_override: 1994 ↛ 1995line 1994 didn't jump to line 1995, because the condition on line 1994 was never true
1995 self.parent = self.sm_config_override['vhd-parent']
1996 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 1996 ↛ 1997line 1996 didn't jump to line 1997, because the condition on line 1996 was never true
1997 self.loaded = True
1999 def _initFromVHDInfo(self, vhdInfo):
2000 self.size = vhdInfo.sizeVirt
2001 if (self.parent == '' or (vhdInfo.parentUuid != '' 2001 ↛ 2004line 2001 didn't jump to line 2004, because the condition on line 2001 was never false
2002 and vhdInfo.parentUuid != self.parent)):
2003 self.parent = vhdInfo.parentUuid
2004 self.hidden = vhdInfo.hidden
2005 self.loaded = True
2007 def _determineType(self):
2008 """Determine whether this is a raw or a VHD VDI"""
2009 if "vdi_ref" in self.sr.srcmd.params:
2010 vdi_ref = self.sr.srcmd.params["vdi_ref"]
2011 sm_config = self.session.xenapi.VDI.get_sm_config(vdi_ref)
2012 if sm_config.get("vdi_type"):
2013 self.vdi_type = sm_config["vdi_type"]
2014 prefix = lvhdutil.LV_PREFIX[self.vdi_type]
2015 self.lvname = "%s%s" % (prefix, self.uuid)
2016 self.path = os.path.join(self.sr.path, self.lvname)
2017 self.sm_config_override = sm_config
2018 return True
2020 # LVM commands can be costly, so check the file directly first in case
2021 # the LV is active
2022 found = False
2023 for t in lvhdutil.VDI_TYPES: 2023 ↛ 2024line 2023 didn't jump to line 2024, because the loop on line 2023 never started
2024 lvname = "%s%s" % (lvhdutil.LV_PREFIX[t], self.uuid)
2025 path = os.path.join(self.sr.path, lvname)
2026 if util.pathexists(path):
2027 if found:
2028 raise xs_errors.XenError('VDILoad',
2029 opterr="multiple VDI's: uuid %s" % self.uuid)
2030 found = True
2031 self.vdi_type = t
2032 self.lvname = lvname
2033 self.path = path
2034 if found: 2034 ↛ 2035line 2034 didn't jump to line 2035, because the condition on line 2034 was never true
2035 return True
2037 # now list all LV's
2038 if not lvutil._checkVG(self.sr.vgname): 2038 ↛ 2040line 2038 didn't jump to line 2040, because the condition on line 2038 was never true
2039 # when doing attach_from_config, the VG won't be there yet
2040 return False
2042 lvs = lvhdutil.getLVInfo(self.sr.lvmCache)
2043 if lvs.get(self.uuid):
2044 self._initFromLVInfo(lvs[self.uuid])
2045 return True
2046 return False
2048 def _loadThis(self):
2049 """Load VDI info for this VDI and activate the LV if it's VHD. We
2050 don't do it in VDI.load() because not all VDI operations need it."""
2051 if self.loaded: 2051 ↛ 2052line 2051 didn't jump to line 2052, because the condition on line 2051 was never true
2052 if self.vdi_type == vhdutil.VDI_TYPE_VHD:
2053 self.sr.lvActivator.activate(self.uuid, self.lvname, False)
2054 return
2055 try:
2056 lvs = lvhdutil.getLVInfo(self.sr.lvmCache, self.lvname)
2057 except util.CommandException as e:
2058 raise xs_errors.XenError('VDIUnavailable',
2059 opterr='%s (LV scan error)' % os.strerror(abs(e.code)))
2060 if not lvs.get(self.uuid): 2060 ↛ 2061line 2060 didn't jump to line 2061, because the condition on line 2060 was never true
2061 raise xs_errors.XenError('VDIUnavailable', opterr='LV not found')
2062 self._initFromLVInfo(lvs[self.uuid])
2063 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 2063 ↛ 2070line 2063 didn't jump to line 2070, because the condition on line 2063 was never false
2064 self.sr.lvActivator.activate(self.uuid, self.lvname, False)
2065 vhdInfo = vhdutil.getVHDInfo(self.path, lvhdutil.extractUuid, False)
2066 if not vhdInfo: 2066 ↛ 2067line 2066 didn't jump to line 2067, because the condition on line 2066 was never true
2067 raise xs_errors.XenError('VDIUnavailable', \
2068 opterr='getVHDInfo failed')
2069 self._initFromVHDInfo(vhdInfo)
2070 self.loaded = True
2072 def _chainSetActive(self, active, binary, persistent=False):
2073 if binary: 2073 ↛ 2074line 2073 didn't jump to line 2074, because the condition on line 2073 was never true
2074 (count, bcount) = RefCounter.checkLocked(self.uuid,
2075 lvhdutil.NS_PREFIX_LVM + self.sr.uuid)
2076 if (active and bcount > 0) or (not active and bcount == 0):
2077 return # this is a redundant activation/deactivation call
2079 vdiList = {self.uuid: self.lvname}
2080 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 2080 ↛ 2083line 2080 didn't jump to line 2083, because the condition on line 2080 was never false
2081 vdiList = vhdutil.getParentChain(self.lvname,
2082 lvhdutil.extractUuid, self.sr.vgname)
2083 for uuid, lvName in vdiList.items(): 2083 ↛ 2084line 2083 didn't jump to line 2084, because the loop on line 2083 never started
2084 binaryParam = binary
2085 if uuid != self.uuid:
2086 binaryParam = False # binary param only applies to leaf nodes
2087 if active:
2088 self.sr.lvActivator.activate(uuid, lvName, binaryParam,
2089 persistent)
2090 else:
2091 # just add the LVs for deactivation in the final (cleanup)
2092 # step. The LVs must not have been activated during the current
2093 # operation
2094 self.sr.lvActivator.add(uuid, lvName, binaryParam)
2096 def _failClone(self, uuid, jval, msg):
2097 try:
2098 self.sr._handleInterruptedCloneOp(uuid, jval, True)
2099 self.sr.journaler.remove(self.JRN_CLONE, uuid)
2100 except Exception as e:
2101 util.SMlog('WARNING: failed to clean up failed snapshot: ' \
2102 ' %s (error ignored)' % e)
2103 raise xs_errors.XenError('VDIClone', opterr=msg)
2105 def _markHidden(self):
2106 if self.vdi_type == vhdutil.VDI_TYPE_RAW:
2107 self.sr.lvmCache.setHidden(self.lvname)
2108 else:
2109 vhdutil.setHidden(self.path)
2110 self.hidden = 1
2112 def _prepareThin(self, attach):
2113 origUtilisation = self.sr.lvmCache.getSize(self.lvname)
2114 if self.sr.isMaster:
2115 # the master can prepare the VDI locally
2116 if attach:
2117 lvhdutil.attachThin(self.sr.journaler, self.sr.uuid, self.uuid)
2118 else:
2119 lvhdutil.detachThin(self.session, self.sr.lvmCache,
2120 self.sr.uuid, self.uuid)
2121 else:
2122 fn = "attach"
2123 if not attach:
2124 fn = "detach"
2125 pools = self.session.xenapi.pool.get_all()
2126 master = self.session.xenapi.pool.get_master(pools[0])
2127 rv = self.session.xenapi.host.call_plugin(
2128 master, self.sr.THIN_PLUGIN, fn,
2129 {"srUuid": self.sr.uuid, "vdiUuid": self.uuid})
2130 util.SMlog("call-plugin returned: %s" % rv)
2131 if not rv:
2132 raise Exception('plugin %s failed' % self.sr.THIN_PLUGIN)
2133 # refresh to pick up the size change on this slave
2134 self.sr.lvmCache.activateNoRefcount(self.lvname, True)
2136 self.utilisation = self.sr.lvmCache.getSize(self.lvname)
2137 if origUtilisation != self.utilisation:
2138 vdi_ref = self.sr.srcmd.params['vdi_ref']
2139 self.session.xenapi.VDI.set_physical_utilisation(vdi_ref,
2140 str(self.utilisation))
2141 stats = lvutil._getVGstats(self.sr.vgname)
2142 sr_utilisation = stats['physical_utilisation']
2143 self.session.xenapi.SR.set_physical_utilisation(self.sr.sr_ref,
2144 str(sr_utilisation))
2146 def update(self, sr_uuid, vdi_uuid):
2147 if self.sr.legacyMode:
2148 return
2150 #Synch the name_label of this VDI on storage with the name_label in XAPI
2151 vdi_ref = self.session.xenapi.VDI.get_by_uuid(self.uuid)
2152 update_map = {}
2153 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \
2154 METADATA_OBJECT_TYPE_VDI
2155 update_map[UUID_TAG] = self.uuid
2156 update_map[NAME_LABEL_TAG] = util.to_plain_string( \
2157 self.session.xenapi.VDI.get_name_label(vdi_ref))
2158 update_map[NAME_DESCRIPTION_TAG] = util.to_plain_string( \
2159 self.session.xenapi.VDI.get_name_description(vdi_ref))
2160 update_map[SNAPSHOT_TIME_TAG] = \
2161 self.session.xenapi.VDI.get_snapshot_time(vdi_ref)
2162 update_map[METADATA_OF_POOL_TAG] = \
2163 self.session.xenapi.VDI.get_metadata_of_pool(vdi_ref)
2164 LVMMetadataHandler(self.sr.mdpath).updateMetadata(update_map)
2166 def _ensure_cbt_space(self):
2167 self.sr.ensureCBTSpace()
2169 def _create_cbt_log(self):
2170 logname = self._get_cbt_logname(self.uuid)
2171 self.sr.lvmCache.create(logname, self.sr.journaler.LV_SIZE, CBTLOG_TAG)
2172 logpath = super(LVHDVDI, self)._create_cbt_log()
2173 self.sr.lvmCache.deactivateNoRefcount(logname)
2174 return logpath
2176 def _delete_cbt_log(self):
2177 logpath = self._get_cbt_logpath(self.uuid)
2178 if self._cbt_log_exists(logpath):
2179 logname = self._get_cbt_logname(self.uuid)
2180 self.sr.lvmCache.remove(logname)
2182 def _rename(self, oldpath, newpath):
2183 oldname = os.path.basename(oldpath)
2184 newname = os.path.basename(newpath)
2185 self.sr.lvmCache.rename(oldname, newname)
2187 def update_slaves_on_cbt_disable(self, cbtlog):
2188 args = {
2189 "vgName": self.sr.vgname,
2190 "action1": "deactivateNoRefcount",
2191 "lvName1": cbtlog
2192 }
2194 host_refs = util.get_hosts_attached_on(self.session, [self.uuid])
2196 message = f"Deactivating {cbtlog}"
2197 self.sr.call_on_slave(args, host_refs, message)
2199 def _activate_cbt_log(self, lv_name):
2200 self.sr.lvmCache.refresh()
2201 if not self.sr.lvmCache.is_active(lv_name): 2201 ↛ 2202line 2201 didn't jump to line 2202, because the condition on line 2201 was never true
2202 try:
2203 self.sr.lvmCache.activateNoRefcount(lv_name)
2204 return True
2205 except Exception as e:
2206 util.SMlog("Exception in _activate_cbt_log, "
2207 "Error: %s." % str(e))
2208 raise
2209 else:
2210 return False
2212 def _deactivate_cbt_log(self, lv_name):
2213 try:
2214 self.sr.lvmCache.deactivateNoRefcount(lv_name)
2215 except Exception as e:
2216 util.SMlog("Exception in _deactivate_cbt_log, Error: %s." % str(e))
2217 raise
2219 def _cbt_log_exists(self, logpath):
2220 return lvutil.exists(logpath)
2222# SR registration at import
2223SR.registerSR(LVHDSR)