Coverage for drivers/VDI : 62%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
#!/usr/bin/python # # Copyright (C) Citrix Systems Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; version 2.1 only. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # VDI: Base class for virtual disk instances #
size = 0 if type == 'vhd': size_mb = virtualsize / (1024 * 1024) #Footer + footer copy + header + possible CoW parent locator fields size = 3 * 1024
# BAT 4 Bytes per block segment size += (size_mb / 2) * 4 size = util.roundup(512, size)
# BATMAP 1 bit per block segment size += (size_mb / 2) / 8 size = util.roundup(4096, size)
# Segment bitmaps + Page align offsets size += (size_mb / 2) * 4096
return size
"""Virtual Disk Instance descriptor.
Attributes: uuid: string, globally unique VDI identifier conforming to OSF DEC 1.1 label: string, user-generated tag string for identifyng the VDI description: string, longer user generated description string size: int, virtual size in bytes of this VDI utilisation: int, actual size in Bytes of data on disk that is utilised. For non-sparse disks, utilisation == size vdi_type: string, disk type, e.g. raw file, partition parent: VDI object, parent backing VDI if this disk is a CoW instance shareable: boolean, does this disk support multiple writer instances? e.g. shared OCFS disk attached: boolean, whether VDI is attached read_only: boolean, whether disk is read-only. """ # Don't set either the UUID or location to None- no good can # ever come of this. else: # We assume that children class initializors calling without # uuid will set these attributes themselves somewhere. They # are VDIs whose physical paths/locations have no direct # connections with their UUID strings (e.g. ISOSR, udevSR, # SHMSR). So we avoid overwriting these attributes here. pass
# deliberately not initialised self.sm_config so that it is # ommitted from the XML output
def from_uuid(session, vdi_uuid):
_VDI = session.xenapi.VDI vdi_ref = _VDI.get_by_uuid(vdi_uuid) sr_ref = _VDI.get_SR(vdi_ref)
_SR = session.xenapi.SR sr_uuid = _SR.get_uuid(sr_ref)
sr = SR.SR.from_uuid(session, sr_uuid)
sr.srcmd.params['vdi_ref'] = vdi_ref return sr.vdi(vdi_uuid)
"""Create a VDI of size <Size> MB on the given SR.
This operation IS NOT idempotent and will fail if the UUID already exists or if there is insufficient space. The vdi must be explicitly attached via the attach() command following creation. The actual disk size created may be larger than the requested size if the substrate requires a size in multiples of a certain extent size. The SR must be queried for the exact size. """ raise xs_errors.XenError('Unimplemented')
"""Query and update the configuration of a particular VDI.
Given an SR and VDI UUID, this operation returns summary statistics on the named VDI. Note the XenAPI VDI object will exist when this call is made. """ # no-op unless individual backends implement it return
"""Explicitly introduce a particular VDI.
Given an SR and VDI UUID and a disk location (passed in via the <conf> XML), this operation verifies the existence of the underylying disk object and then creates the XenAPI VDI object. """ raise xs_errors.XenError('Unimplemented')
"""Initiate local access to the VDI. Initialises any device state required to access the VDI.
This operation IS idempotent and should succeed if the VDI can be attached or if the VDI is already attached.
Returns: string, local device path. """ struct = { 'params': self.path, 'xenstore_data': (self.xenstore_data or {})} return xmlrpclib.dumps((struct,), "", True)
"""Remove local access to the VDI. Destroys any device state initialised via the vdi.attach() command.
This operation is idempotent. """ raise xs_errors.XenError('Unimplemented')
"""Create a mutable instance of the referenced VDI.
This operation is not idempotent and will fail if the UUID already exists or if there is insufficient space. The SRC VDI must be in a detached state and deactivated. Upon successful creation of the clone, the clone VDI must be explicitly attached via vdi.attach(). If the driver does not support cloning this operation should raise SRUnsupportedOperation.
Arguments: Raises: SRUnsupportedOperation """ raise xs_errors.XenError('Unimplemented')
"""Resize the given VDI which may have active VBDs, which have been paused for the duration of this call.""" raise xs_errors.XenError('Unimplemented')
"""Generate the XML config required to activate a VDI for use when XAPI is not running. Activation is handled by the vdi_attach_from_config() SMAPI call. """ raise xs_errors.XenError('Unimplemented')
"""Layer the updates from [vdi2] onto [vdi1], calling the result [vdi2].
Raises: SRUnsupportedOperation """ raise xs_errors.XenError('Unimplemented')
"""Activate a VDI based on the config passed in on the CLI. For use when XAPI is not running. The config is generated by the Activation is handled by the vdi_generate_config() SMAPI call. """ raise xs_errors.XenError('Unimplemented')
cloneOp=False, secondary=None, cbtlog=None): raise xs_errors.XenError('Unimplemented')
raise xs_errors.XenError('Unimplemented')
raise xs_errors.XenError('Unimplemented')
"""Check if CBT log file exists
Must be implemented by all classes inheriting from base VDI class """ raise xs_errors.XenError('Unimplemented')
"""Resize the given VDI to size <size> MB. Size can be any valid disk size greater than [or smaller than] the current value.
This operation IS idempotent and should succeed if the VDI can be resized to the specified value or if the VDI is already the specified size. The actual disk size created may be larger than the requested size if the substrate requires a size in multiples of a certain extent size. The SR must be queried for the exact size. This operation does not modify the contents on the disk such as the filesystem. Responsibility for resizing the FS is left to the VM administrator. [Reducing the size of the disk is a very dangerous operation and should be conducted very carefully.] Disk contents should always be backed up in advance. """ raise xs_errors.XenError('Unimplemented')
"""Resize the given VDI to size <size> MB. Size can be any valid disk size greater than [or smaller than] the current value.
This operation IS idempotent and should succeed if the VDI can be resized to the specified value or if the VDI is already the specified size. The actual disk size created may be larger than the requested size if the substrate requires a size in multiples of a certain extent size. The SR must be queried for the exact size. This operation does not modify the contents on the disk such as the filesystem. Responsibility for resizing the FS is left to the VM administrator. [Reducing the size of the disk is a very dangerous operation and should be conducted very carefully.] Disk contents should always be backed up in advance. """ % vdi_uuid)
"""Delete this VDI.
This operation IS idempotent and should succeed if the VDI exists and can be deleted or if the VDI does not exist. It is the responsibility of the higher-level management tool to ensure that the detach() operation has been explicitly called prior to deletion, otherwise the delete() will fail if the disk is still attached. """
logpath)
parent_path, child_uuid)
child_path, parent_uuid) # Coalesce contents of bitmap with child's bitmap # Check if child bitmap is currently attached cbtutil.get_cbt_consistency, child_path) sr_uuid, child_uuid): raise util.SMException("failed to pause VDI %s") logpath, child_path) # If there is an exception in coalescing, # CBT log file is not deleted and pointers are reset # to what they were " restoring to previous state") parent_path, vdi_uuid) child_path, vdi_uuid) finally: finally: # Unpause tapdisk if it wasn't originally paused child_uuid) finally:
"""Save an immutable copy of the referenced VDI.
This operation IS NOT idempotent and will fail if the UUID already exists or if there is insufficient space. The vdi must be explicitly attached via the vdi_attach() command following creation. If the driver does not support snapshotting this operation should raise SRUnsupportedOperation
Arguments: Raises: SRUnsupportedOperation """ # logically, "snapshot" should mean SNAPSHOT_SINGLE and "clone" should # mean "SNAPSHOT_DOUBLE", but in practice we have to do SNAPSHOT_DOUBLE # in both cases, unless driver_params overrides it snapType = SNAPSHOT_SINGLE snapType = SNAPSHOT_INTERNAL
cbtlog = self._get_cbt_logpath(self.uuid) else: secondary=secondary, cbtlog=cbtlog)
"""Activate VDI - called pre tapdisk open""" # Disk is being attached in RO mode, # don't attach metadata log file return None
# Activate CBT log file, if required finally:
# Check and update consistency logpath) " for disk %s." % vdi_uuid)
logpath, False)
"""Deactivate VDI - called post tapdisk close"""
# Finally deactivate log file finally:
""" Returns: XMLRPC response containing a single struct with fields 'location' and 'uuid' """ struct = { 'location': self.location, 'uuid': self.uuid } return xmlrpclib.dumps((struct,), "", True)
"""Post-init hook"""
uuid = util.default(self, "uuid", lambda: util.gen_uuid()) sm_config = util.default(self, "sm_config", lambda: {}) if self.sr.srcmd.params.has_key("vdi_sm_config"): for key in SM_CONFIG_PASS_THROUGH_FIELDS: val = self.sr.srcmd.params["vdi_sm_config"].get(key) if val: sm_config[key] = val ty = util.default(self, "ty", lambda: "user") is_a_snapshot = util.default(self, "is_a_snapshot", lambda: False) metadata_of_pool = util.default(self, "metadata_of_pool", lambda: "OpaqueRef:NULL") snapshot_time = util.default(self, "snapshot_time", lambda: "19700101T00:00:00Z") snapshot_of = util.default(self, "snapshot_of", lambda: "OpaqueRef:NULL") cbt_enabled = util.default(self, "cbt_enabled", lambda: False) vdi = self.sr.session.xenapi.VDI.db_introduce(uuid, self.label, self.description, self.sr.sr_ref, ty, self.shareable, self.read_only, {}, self.location, {}, sm_config, self.managed, str(self.size), str(self.utilisation), metadata_of_pool, is_a_snapshot, xmlrpclib.DateTime(snapshot_time), snapshot_of, cbt_enabled) return vdi
self.sr.forget_vdi(self.uuid)
for key, val in self.sm_config_override.iteritems(): if val == sm_config.get(key): continue if val: util.SMlog("_override_sm_config: %s: %s -> %s" % \ (key, sm_config.get(key), val)) sm_config[key] = val elif sm_config.has_key(key): util.SMlog("_override_sm_config: del %s" % key) del sm_config[key]
import cleanup current_sm_config = self.sr.session.xenapi.VDI.get_sm_config(ref) for key, val in sm_config.iteritems(): if key.startswith("host_") or \ key in ["paused", cleanup.VDI.DB_VHD_BLOCKS]: continue if sm_config.get(key) != current_sm_config.get(key): util.SMlog("_db_update_sm_config: %s sm-config:%s %s->%s" % \ (self.uuid, key, current_sm_config.get(key), val)) self.sr.session.xenapi.VDI.remove_from_sm_config(ref, key) self.sr.session.xenapi.VDI.add_to_sm_config(ref, key, val)
for key in current_sm_config.keys(): if key.startswith("host_") or \ key in ["paused", cleanup.VDI.DB_VHD_BLOCKS] or \ key in self.sm_config_keep: continue if not sm_config.get(key): util.SMlog("_db_update_sm_config: %s del sm-config:%s" % \ (self.uuid, key)) self.sr.session.xenapi.VDI.remove_from_sm_config(ref, key)
vdi = self.sr.session.xenapi.VDI.get_by_uuid(self.uuid) self.sr.session.xenapi.VDI.set_virtual_size(vdi, str(self.size)) self.sr.session.xenapi.VDI.set_physical_utilisation(vdi, str(self.utilisation)) self.sr.session.xenapi.VDI.set_read_only(vdi, self.read_only) sm_config = util.default(self, "sm_config", lambda: {}) self._override_sm_config(sm_config) self._db_update_sm_config(vdi, sm_config) self.sr.session.xenapi.VDI.set_cbt_enabled(vdi, self._get_blocktracking_status())
"""Returns true if this VDI is in sync with the supplied XenAPI record""" if self.location <> util.to_plain_string(x['location']): util.SMlog("location %s <> %s" % (self.location, x['location'])) return False if self.read_only <> x['read_only']: util.SMlog("read_only %s <> %s" % (self.read_only, x['read_only'])) return False if str(self.size) <> x['virtual_size']: util.SMlog("virtual_size %s <> %s" % (self.size, x['virtual_size'])) return False if str(self.utilisation) <> x['physical_utilisation']: util.SMlog("utilisation %s <> %s" % (self.utilisation, x['physical_utilisation'])) return False sm_config = util.default(self, "sm_config", lambda: {}) if set(sm_config.keys()) <> set(x['sm_config'].keys()): util.SMlog("sm_config %s <> %s" % (repr(sm_config), repr(x['sm_config']))) return False for k in sm_config.keys(): if sm_config[k] <> x['sm_config'][k]: util.SMlog("sm_config %s <> %s" % (repr(sm_config), repr(x['sm_config']))) return False if self.cbt_enabled != x['cbt_enabled']: util.SMlog("cbt_enabled %s <> %s" % ( self.cbt_enabled, x['cbt_enabled'])) return False return True
"""Function for configuring blocktracking"""
# Check if raw VDI or snapshot self.session.xenapi.VDI.get_is_a_snapshot(vdi_ref): opterr='Raw VDI or snapshot not permitted')
# Check if already enabled
# Save disk state before pause
# Check available space # Set consistency % self.uuid) logpath, False) opterr=str(error)) else: # Find parent of leaf metadata file, if any, # and nullify its successor cbtutil.get_cbt_parent, logpath) parent_path, uuid.UUID(int=0)) finally: finally:
"""Delete the data associated with a CBT enabled snapshot
Can only be called for a snapshot VDI on a VHD chain that has had CBT enabled on it at some point. The latter is enforced by upper layers """
vdi_ref = self.sr.srcmd.params['vdi_ref'] if not self.session.xenapi.VDI.get_is_a_snapshot(vdi_ref): raise xs_errors.XenError('VDIType', opterr='Only allowed for snapshot VDIs')
self.delete(sr_uuid, vdi_uuid, data_only=True)
""" List all changed blocks """
"Source and target VDI are same")
# Check 1: Check if CBT is enabled on VDIs and they are related self._get_blocktracking_status(vdi_to)):
# Starting at log file after "vdi_from", traverse the CBT chain # through child pointers until one of the following is true # * We've reached destination VDI # * We've reached end of CBT chain originating at "vdi_from" # Check if we have reached end of CBT chain logpath) # VDIs are not part of the same metadata chain break else:
cbtutil.get_cbt_size, logpath) % (curr_vdi, curr_vdi_size)) cbtutil.get_cbt_bitmap, logpath))
# This should ideally never happen but fail call to calculate # changed blocks instead of returning corrupt data util.SMlog("Size of bitmap %d is less than expected size %d" % (len(curr_bitmap), expected_bitmap_len)) raise xs_errors.XenError('CBTMetadataInconsistent', "Inconsistent bitmaps")
# Rule out error conditions # 1) New VDI size < original VDI size # 2) New bitmap size < original bitmap size # 3) new VDI size > original VDI size but new bitmap # is not bigger len(curr_bitmap) < len(merged_bitmap) or (curr_vdi_size > vdi_size and len(curr_bitmap) <= len(merged_bitmap))): # Return error: Failure to calculate changed blocks "inconsistent bitmap sizes") "Inconsistent bitmaps")
# Check if disk has been resized # Extend merged_bitmap to match size of curr_bitmap
# At this point bitmap sizes should be same curr_vdi_size == vdi_size): # This is unusual. Log it but calculate merged # bitmap by truncating new bitmap "in chain without change in size" % curr_vdi)
else:
# Check if we have reached "vdi_to"
# TODO: Check 2: If both VDIs still exist, # find common ancestor and find difference
# TODO: VDIs are unrelated # return fully populated bitmap size of to VDI
"Source and target VDI are unrelated")
""" CBT snapshot"""
# Rename vdi vdi.cbtlog to snapshot.cbtlog # and mark it consistent snap_logpath, True)
#TODO: Make parent detection logic better. Ideally, get_cbt_parent # should return None if the parent is set to a UUID made of all 0s. # In this case, we don't know the difference between whether it is a # NULL UUID or the parent file is missing. See cbtutil for why we can't # do this cbtutil.get_cbt_parent, snap_logpath) parent_path, snapshot_uuid) # Ensure enough space for metadata file # Create new vdi.cbtlog # Set previous vdi node consistency status self._cbt_op(self.uuid, cbtutil.set_cbt_consistency, vdi_logpath, consistency_state) # Set relationship pointers # Save the child of the VDI just snapshotted snap_logpath) vdi_logpath, snapshot_uuid) # Set child of new vdi to existing child of snapshotted VDI vdi_logpath, curr_child_uuid) snap_logpath, self.uuid) % self.uuid)
""" Get blocktracking status """ if not uuid: uuid = self.uuid if self.vdi_type == vhdutil.VDI_TYPE_RAW: return False elif 'VDI_CONFIG_CBT' not in util.sr_get_capability(self.sr.uuid): return False logpath = self._get_cbt_logpath(uuid) return self._cbt_log_exists(logpath)
""" Set blocktracking status""" vdi_config = self.session.xenapi.VDI.get_other_config(vdi_ref) if "cbt_enabled" in vdi_config: self.session.xenapi.VDI.remove_from_other_config( vdi_ref, "cbt_enabled")
self.session.xenapi.VDI.add_to_other_config( vdi_ref, "cbt_enabled", enable)
""" Ensure enough CBT space """
""" Get CBT logname """
""" Get CBT logpath """
""" Create CBT log """ #cbtutil.create_cbt_log(logpath, size) except: pass finally:
"""Activate CBT log file
SR specific Implementation required for VDIs on block-based SRs. No-op otherwise """ pass
"""Deactivate CBT log file
SR specific Implementation required for VDIs on block-based SRs. No-op otherwise """ pass
# Lock cbtlog operations
self._deactivate_cbt_log(logname) finally:
alert_prio_warning, alert_obj, alert_uuid, alert_str) |