class VolumeOperations(object): def __init__(self): self.MetadataHandler = MetadataHandler() def create(self, dbg, uri, size): raise NotImplementedError('Override in VolumeOperations specifc class') def destroy(self, dbg, uri): raise NotImplementedError('Override in VolumeOperations specifc class') def resize(self, dbg, uri, size): raise NotImplementedError('Override in VolumeOperations specifc class') def swap(self, dbg, uri1, uri2): log.debug("%s: xcpng.volume.VolumeOperations.swap: uri1: %s uri2: %s" % (dbg, uri1, uri2)) volume1_meta = self.MetadataHandler.get_vdi_meta(dbg, uri1) volume2_meta = self.MetadataHandler.get_vdi_meta(dbg, uri2) log.debug("%s: xcpng.volume.VolumeOperations.swap: before image_uuid1: %s image_uudi2: %s" % (dbg, volume1_meta[IMAGE_UUID_TAG], volume2_meta[IMAGE_UUID_TAG])) image1_uuid = volume1_meta[IMAGE_UUID_TAG] image2_uuid = volume2_meta[IMAGE_UUID_TAG] volume1_meta = {IMAGE_UUID_TAG: image2_uuid} volume2_meta = {IMAGE_UUID_TAG: image1_uuid} log.debug("%s: xcpng.volume.VolumeOperations.swap: after image_uuid1: %s image_uudi2: %s" % (dbg, volume1_meta[IMAGE_UUID_TAG], volume2_meta[IMAGE_UUID_TAG])) self.MetadataHandler.update_vdi_meta(dbg, uri1, volume1_meta) self.MetadataHandler.update_vdi_meta(dbg, uri2, volume2_meta) def get_phisical_utilization(self, dbg, uri): raise NotImplementedError('Override in VolumeOperations specifc class') def roundup_size(self, dbg, size): raise NotImplementedError('Override in VolumeOperations specifc class')
class Volume(object): def __init__(self): self.MetadataHandler = MetadataHandler() self.VolOpsHendler = _VolumeOperations_() self.Datapathes = DATAPATHES for k, v in DATAPATHES.iteritems(): self.Datapathes[k] = v() def _create(self, dbg, sr, name, description, size, sharable, volume_meta): raise NotImplementedError('Override in Volume specifc class') def create(self, dbg, sr, name, description, size, sharable): log.debug("%s: xcpng.volume.Volume.create: SR: %s Name: %s Description: %s Size: %s, Sharable: %s" % (dbg, sr, name, description, size, sharable)) vdi_uuid = str(uuid.uuid4()) image_uuid = str(uuid.uuid4()) vdi_uri = "%s/%s" % (sr, vdi_uuid) volume_meta = { KEY_TAG: vdi_uuid, VDI_UUID_TAG: vdi_uuid, IMAGE_UUID_TAG: image_uuid, TYPE_TAG: get_vdi_type_by_uri(dbg, vdi_uri), NAME_TAG: name, DESCRIPTION_TAG: description, READ_WRITE_TAG: True, VIRTUAL_SIZE_TAG: size, PHYSICAL_UTILISATION_TAG: 0, URI_TAG: [vdi_uri], SHARABLE_TAG: sharable, CUSTOM_KEYS_TAG: {} } try: self.MetadataHandler.update_vdi_meta(dbg, vdi_uri, volume_meta) volume_meta = self._create(dbg, sr, name, description, size, sharable, volume_meta) except Exception as e: log.error("%s: xcpng.volume.Volume.create: Failed to create volume: key %s: SR: %s" % (dbg, vdi_uuid, sr)) try: self.destroy(dbg, sr, vdi_uuid) self.MetadataHandler.remove_vdi_meta(dbg, vdi_uri) except: pass raise Exception(e) return volume_meta def set(self, dbg, sr, key, k, v): log.debug("%s: xcpng.volume.Volume.set: SR: %s Key: %s Custom_key: %s Value: %s" % (dbg, sr, key, k, v)) uri = "%s/%s" % (sr, key) volume_meta = {CUSTOM_KEYS_TAG: {}} try: volume_meta[CUSTOM_KEYS_TAG][k] = v self.MetadataHandler.update_vdi_meta(dbg, uri, volume_meta) except Exception as e: log.error("%s: xcpng.volume.Volume.set: Failed to set volume param: key %s: SR: %s" % (dbg, key, sr)) raise Exception(e) def unset(self, dbg, sr, key, k): log.debug("%s: xcpng.volume.Volume.unset: SR: %s Key: %s Custom_key: %s" % (dbg, sr, key, k)) uri = "%s/%s" % (sr, key) volume_meta = {CUSTOM_KEYS_TAG: {}} try: volume_meta[CUSTOM_KEYS_TAG][k] = None self.MetadataHandler.update_vdi_meta(dbg, uri, volume_meta) except Exception as e: log.error("%s: xcpng.volume.Volume.set: Failed to unset volume param: key %s: SR: %s" % (dbg, key, sr)) raise Exception(e) def _stat(self, dbg, sr, key, volume_meta): # Override in Volume specific class return volume_meta def stat(self, dbg, sr, key): log.debug("%s: xcpng.volume.Volume.stat: SR: %s Key: %s" % (dbg, sr, key)) uri = "%s/%s" % (sr, key) try: volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) volume_meta[PHYSICAL_UTILISATION_TAG] = self.VolOpsHendler.get_phisical_utilization(dbg, uri) log.debug("%s: xcpng.volume.Volume.stat: SR: %s Key: %s Metadata: %s" % (dbg, sr, key, volume_meta)) return self._stat(dbg, sr, key, volume_meta) except Exception as e: log.error("%s: xcpng.volume.Volume.stat: Failed to get volume stat: key %s: SR: %s" % (dbg, key, sr)) raise Exception(e) def _destroy(self, dbg, sr, key): # Override in Volume specifc class pass def destroy(self, dbg, sr, key): log.debug("%s: xcpng.volume.Volume.destroy: SR: %s Key: %s" % (dbg, sr, key)) uri = "%s/%s" % (sr, key) try: self._destroy(dbg, sr, key) self.VolOpsHendler.destroy(dbg, uri) self.MetadataHandler.remove_vdi_meta(dbg, uri) except Exception as e: log.error("%s: xcpng.volume.Volume.destroy: Failed to destroy volume: key %s: SR: %s" % (dbg, key, sr)) raise Exception(e) def set_description(self, dbg, sr, key, new_description): log.debug("%s: xcpng.volume.Volume.set_description: SR: %s Key: %s New_description: %s" % (dbg, sr, key, new_description)) uri = "%s/%s" % (sr, key) volume_meta = { 'description': new_description, } try: self.MetadataHandler.update_vdi_meta(dbg, uri, volume_meta) except Exception as e: log.error("%s: xcpng.volume.Volume.set: Failed to set volume description: key %s: SR: %s" % (dbg, key, sr)) raise Exception(e) def set_name(self, dbg, sr, key, new_name): log.debug("%s: xcpng.volume.Volume.set_name: SR: %s Key: %s New_name: %s" % (dbg, sr, key, new_name)) uri = "%s/%s" % (sr, key) volume_meta = { 'name': new_name, } try: self.MetadataHandler.update_vdi_meta(dbg, uri, volume_meta) except Exception as e: log.error("%s: xcpng.volume.Volume.set: Failed to set volume name: key %s: SR: %s" % (dbg, key, sr)) raise Exception(e) def _resize(self, dbg, sr, key, new_size): raise NotImplementedError('Override in Volume specifc class') def resize(self, dbg, sr, key, new_size): log.debug("%s: xcpng.volume.Volume.resize: SR: %s Key: %s New_size: %s" % (dbg, sr, key, new_size)) uri = "%s/%s" % (sr, key) volume_meta = { 'virtual_size': new_size, } try: self._resize(dbg, sr, key, new_size) self.MetadataHandler.update_vdi_meta(dbg, uri, volume_meta) except Exception as e: log.error("%s: xcpng.volume.Volume.set: Failed to resize volume: key %s: SR: %s" % (dbg, key, sr)) raise Exception(e) def _clone(self, dbg, sr, key, mode, volume_meta): raise NotImplementedError('Override in Volume specifc class') def clone(self, dbg, sr, key, mode): log.debug("%s: xcpng.volume.Volume.clone: SR: %s Key: %s Mode: %s" % (dbg, sr, key, mode)) orig_uri = "%s/%s" % (sr, key) try: orig_meta = self.MetadataHandler.get_vdi_meta(dbg, orig_uri) if SNAPSHOT_OF_TAG in orig_meta[CUSTOM_KEYS_TAG]: base_uri = orig_meta[PARENT_URI_TAG][0] base_meta = self.MetadataHandler.get_vdi_meta(dbg, base_uri) else: base_meta = deepcopy(orig_meta) if ACTIVE_ON_TAG in base_meta: current_host = get_current_host_uuid() if base_meta[ACTIVE_ON_TAG] != current_host: log.debug("%s: librbd.Volume.clone: SR: %s Key: %s Can not snapshot on %s as VDI already active on %s" % (dbg, sr, base_meta[VDI_UUID_TAG], current_host, base_meta[ACTIVE_ON_TAG])) raise Activated_on_another_host(base_meta[ACTIVE_ON_TAG]) return self._clone(dbg, sr, key, mode, base_meta) except Exception as e: log.error("%s: xcpng.volume.Volume.set: Failed to clone volume: key %s: SR: %s" % (dbg, key, sr)) raise Exception(e) def _commit(self, dbg, sr, child, parent): raise NotImplementedError('Coalesce is not supported') def _set_parent(self, dbg, sr, child, parent): raise NotImplementedError('Coalesce is not supported') def commit(self, dbg, sr, child, parent): self._commit(dbg, sr, child, parent) def set_parent(self, dbg, sr, child, parent): self._set_parent(dbg, sr, child, parent) def coalesce(self, dbg, sr, key): uri = "%s/%s" % (sr, key) try: volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) children = self.MetadataHandler.find_vdi_children(dbg, uri) self._commit(dbg, sr, uri, volume_meta[PARENT_URI_TAG]) for child in children: self._set_parent(dbg, sr, child[URI_TAG], volume_meta[PARENT_URI_TAG]) meta = {PARENT_URI_TAG: volume_meta[PARENT_URI_TAG]} self.MetadataHandler.update_vdi_meta(dbg, child[URI_TAG], meta) self.destroy(dbg, sr, key) self.MetadataHandler.remove_vdi_meta(dbg, uri) except Exception as e: log.error("%s: xcpng.volume.Volume.set: Failed to coalesce volume with parent: key %s: SR: %s" % (dbg, key, sr)) raise Exception(e)
class DatapathOperations(object): def __init__(self): self.MetadataHandler = MetadataHandler() self.blkdev = None def gen_vol_path(self, dbg, uri): volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) return "%s/%s/%s" % (SR_PATH_PREFIX, get_sr_uuid_by_uri( dbg, uri), volume_meta[IMAGE_UUID_TAG]) def gen_vol_uri(self, dbg, uri): return "file:%s" % self.gen_vol_path(dbg, uri) def map_vol(self, dbg, uri, chained=False): if self.blkdev: log.debug( "%s: xcpng.datapath.DatapathOperations.map_vol: uri: %s" % (dbg, uri)) _blkdev_ = self.blkdev try: volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) if chained is True: if PARENT_URI_TAG in volume_meta: self.map_vol(dbg, volume_meta[PARENT_URI_TAG][0], chained) if REF_COUNT_TAG in volume_meta: new_meta = {} new_meta[REF_COUNT_TAG] = volume_meta[REF_COUNT_TAG] + 1 self.MetadataHandler.update_vdi_meta(dbg, uri, new_meta) else: new_meta = {} new_meta[REF_COUNT_TAG] = 1 call(dbg, ['ln', '-s', _blkdev_, self.gen_vol_path(dbg, uri)]) self.MetadataHandler.update_vdi_meta(dbg, uri, new_meta) except Exception as e: log.error( "%s: xcpng.datapath.DatapathOperations.map_vol: Failed to map volume: uri: %s device: %s" % (dbg, uri, _blkdev_)) raise Exception(e) def unmap_vol(self, dbg, uri, chained=False): path = self.gen_vol_path(dbg, uri) if exists(path): log.debug( "%s: xcpng.datapath.DatapathOperations.unmap_vol: uri: %s" % (dbg, uri)) try: volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) if REF_COUNT_TAG in volume_meta: new_meta = {} if volume_meta[REF_COUNT_TAG] == 1: new_meta[REF_COUNT_TAG] = None call(dbg, ['unlink', path]) else: new_meta[ REF_COUNT_TAG] = volume_meta[REF_COUNT_TAG] - 1 self.MetadataHandler.update_vdi_meta(dbg, uri, new_meta) if chained: if PARENT_URI_TAG in volume_meta: self.unmap_vol(dbg, volume_meta[PARENT_URI_TAG][0], chained) except Exception as e: log.error( "%s: xcpng.datapath.DatapathOperations.unmap_vol: Failed to unmap volume: uri: %s" % (dbg, uri)) raise Exception(e)
class Datapath(object): def __init__(self): self.MetadataHandler = MetadataHandler() self.DatapathOpsHandler = _DatapathOperations_() def _relink(self, dbg, uri, child, parent, domain): raise NotImplementedError('Override in Datapath specifc class') def relink(self, dbg, uri, child, parent, domain): log.debug( "%s: xcpng.datapath.Datapath.relink: uri: %s child: %s parent: %s domain: %s" % (dbg, uri, child, parent, domain)) try: self._relink(dbg, uri, child, parent, domain) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.relink: Failed to relink: uri: %s" % (dbg, uri)) raise Exception(e) def _commit(self, dbg, uri, child, parent, domain): raise NotImplementedError('Override in Datapath specifc class') def commit(self, dbg, uri, child, parent, domain): log.debug( "%s: xcpng.datapath.Datapath.commit: uri: %s child: %s parent: %s domain: %s" % (dbg, uri, child, parent, domain)) try: self._commit(dbg, uri, child, parent, domain) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.commit: Failed to commit: uri: %s" % (dbg, uri)) raise Exception(e) def _open(self, dbg, uri, persistent): raise NotImplementedError('Override in Datapath specifc class') def open(self, dbg, uri, persistent): log.debug("%s: xcpng.datapath.Datapath.open: uri: %s persistent: %s" % (dbg, uri, persistent)) try: image_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) if NON_PERSISTENT_TAG in image_meta: vdi_non_persistent = image_meta[NON_PERSISTENT_TAG] else: vdi_non_persistent = False if persistent: log.debug( "%s: xcpng.Datapath.open: uri: %s will be marked as persistent" % (dbg, uri)) if vdi_non_persistent: # unmark as non-peristent image_meta = { NON_PERSISTENT_TAG: None, } self.MetadataHandler.update_vdi_meta(dbg, uri, image_meta) # on detach remove special snapshot to rollback to elif vdi_non_persistent: log.debug( "%s: xcpng.Datapath.open: uri: %s already marked as non-persistent" % (dbg, uri)) else: log.debug( "%s: xcpng.Datapath.open: uri: %s will be marked as non-persistent" % (dbg, uri)) # mark as non-peristent image_meta = { NON_PERSISTENT_TAG: True, } self.MetadataHandler.update_vdi_meta(dbg, uri, image_meta) # on attach create special snapshot to rollback to on detach self._open(dbg, uri, persistent) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.open: Failed to open datapath for volume: uri: %s" % (dbg, uri)) raise Exception(e) def _close(self, dbg, uri): raise NotImplementedError('Override in Datapath specifc class') def close(self, dbg, uri): log.debug("%s: xcpng.datapath.Datapath.close: uri: %s" % (dbg, uri)) try: image_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) if NON_PERSISTENT_TAG in image_meta: vdi_non_persistent = image_meta[NON_PERSISTENT_TAG] else: vdi_non_persistent = False log.debug( "%s: xcpng.Datapath.close: uri: %s will be marked as persistent" % (dbg, uri)) if vdi_non_persistent: # unmark as non-peristent image_meta = { NON_PERSISTENT_TAG: None, } self.MetadataHandler.update_vdi_meta(dbg, uri, image_meta) self._close(dbg, uri) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.close: Failed to close datapath for volume: uri: %s" % (dbg, uri)) raise Exception(e) def _attach(self, dbg, uri, domain): raise NotImplementedError('Override in Datapath specifc class') def attach(self, dbg, uri, domain): log.debug("%s: xcpng.datapath.Datapath.attach: uri: %s domain: %s" % (dbg, uri, domain)) try: self.DatapathOpsHandler.map_vol(dbg, uri, chained=True) if platform.linux_distribution()[1] == '7.5.0': protocol, params = self._attach(dbg, uri, domain) return { 'domain_uuid': '0', 'implementation': [protocol, params] } elif platform.linux_distribution( )[1] == '7.6.0' or platform.linux_distribution()[1] == '8.0.0': return {'implementations': self._attach(dbg, uri, domain)} except Exception as e: log.error( "%s: xcpng.datapath.Datapath.attach: Failed to attach datapath for volume: uri: %s" % (dbg, uri)) try: self.DatapathOpsHandler.unmap_vol(dbg, uri, chained=True) except: pass raise Exception(e) def _detach(self, dbg, uri, domain): raise NotImplementedError('Override in Datapath specifc class') def detach(self, dbg, uri, domain): log.debug("%s: xcpng.datapath.Datapath.detach: uri: %s domain: %s" % (dbg, uri, domain)) try: self.DatapathOpsHandler.unmap_vol(dbg, uri, chained=True) self._detach(dbg, uri, domain) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.detach: Failed to detach datapath for volume: uri: %s" % (dbg, uri)) raise Exception(e) def _activate(self, dbg, uri, domain): raise NotImplementedError('Override in Datapath specifc class') def activate(self, dbg, uri, domain): log.debug("%s: xcpng.datapath.Datapath.activate: uri: %s domain: %s" % (dbg, uri, domain)) # TODO: Check that VDI is not active on other host try: self._activate(dbg, uri, domain) image_meta = {ACTIVE_ON_TAG: get_current_host_uuid()} self.MetadataHandler.update_vdi_meta(dbg, uri, image_meta) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.activate: Failed to activate datapath for volume: uri: %s" % (dbg, uri)) try: self._deactivate(dbg, uri, domain) except: pass raise Exception(e) def _deactivate(self, dbg, uri, domain): raise NotImplementedError('Override in Datapath specifc class') def deactivate(self, dbg, uri, domain): log.debug( "%s: xcpng.datapath.Datapath.deactivate: uri: %s domain: %s" % (dbg, uri, domain)) try: self._deactivate(dbg, uri, domain) image_meta = {ACTIVE_ON_TAG: None} self.MetadataHandler.update_vdi_meta(dbg, uri, image_meta) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.deactivate: Failed to deactivate datapath for volume: uri: %s" % (dbg, uri)) raise Exception(e) def _suspend(self, dbg, uri, domain): raise NotImplementedError('Override in Datapath specifc class') def suspend(self, dbg, uri, domain): log.debug("%s: xcpng.datapath.Datapath.suspend: uri: %s domain: %s" % (dbg, uri, domain)) try: self._suspend(dbg, uri, domain) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.suspend: Failed to suspend datapath for volume: uri: %s" % (dbg, uri)) raise Exception(e) def _resume(self, dbg, uri, domain): raise NotImplementedError('Override in Datapath specifc class') def resume(self, dbg, uri, domain): log.debug("%s: xcpng.datapath.Datapath.resume: uri: %s domain: %s" % (dbg, uri, domain)) try: self._resume(dbg, uri, domain) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.resume: Failed to resume datapath for volume: uri: %s" % (dbg, uri)) raise Exception(e) def _snapshot(self, dbg, base_uri, snap_uri, domain): raise NotImplementedError('Override in Datapath specifc class') def snapshot(self, dbg, base_uri, snap_uri, domain): log.debug( "%s: xcpng.Datapath.snapshot: base_uri: %s snap_uri: %s domain: %s" % (dbg, base_uri, snap_uri, domain)) try: self._snapshot(dbg, base_uri, snap_uri, domain) except Exception as e: log.error( "%s: xcpng.datapath.Datapath.snapshot: Failed to set backing file for live volume: uri: %s" % (dbg, snap_uri)) raise Exception(e)