def non_leaf_coalesce(node, parent, uri, callbacks): """ Perform non-leaf (mid tree) volume coalesce """ node_volume = node.volume parent_volume = parent.volume log.debug("non_leaf_coalesce key={}, parent={}".format( node_volume.id, parent_volume.id)) with VolumeContext(callbacks, uri, 'w') as opq: node_path = callbacks.volumeGetPath(opq, str(node_volume.id)) parent_path = callbacks.volumeGetPath(opq, str(parent_volume.id)) log.debug("Running cow-coalesce on {}".format(node_volume.id)) image_utils = ImageFormat.get_format( node_volume.image_type).image_utils image_utils.coalesce(GC, node_path, parent_path) with PollLock(opq, 'gl', callbacks, PRIO_GC): with callbacks.db_context(opq) as db: # reparent all of the children to this node's parent children = db.get_children(node_volume.id) journal_entries = db.add_journal_entries( node_volume.id, parent_volume.id, children) __reparent_children(opq, callbacks, journal_entries) callbacks.volumeUnlock(opq, node.lock) callbacks.volumeUnlock(opq, parent.lock)
def stop(): with VolumeContext(callbacks, uri, 'w') as opq: path = os.path.join(util.var_run_prefix(), 'sr', callbacks.getUniqueIdentifier(opq), name + '_task.pickle') with open(path) as f: process = pickle.load(f) process.kill() process.wait() os.unlink(path)
def create(self, dbg, sr, name, description, size, sharable): devices = util.get_sr_metadata(dbg, 'file://' + sr)['devices'] devices = map(lambda x: os.path.normpath(x), devices) with VolumeContext(self.callbacks, sr, 'w') as opq: image_type = ImageFormat.IMAGE_RAW image_format = ImageFormat.get_format(image_type) vdi_uuid = str(uuid.uuid4()) with PollLock(opq, 'gl', self.callbacks, 0.5): with self.callbacks.db_context(opq) as db: # List all used devices. used_devices = map( lambda x: os.path.realpath( self.callbacks.volumeGetPath(opq, str(x.id))), db.get_all_volumes()) # Find first free device with the best size. free_device = None psize = sys.maxint for device in devices: if os.path.realpath(device) not in used_devices: device_size = util.get_physical_file_size(device) if device_size >= size and device_size < psize: free_device = device psize = device_size if not free_device: # TODO: Maybe find a better exception. raise ValueError('No free device found in config') volume = db.insert_new_volume(psize, image_type) db.insert_vdi(name, description, vdi_uuid, volume.id, sharable) volume_path = self.callbacks.volumeGetPath( opq, str(volume.id)) os.symlink(free_device, volume_path) vdi_uri = self.callbacks.getVolumeUriPrefix(opq) + vdi_uuid return { 'key': vdi_uuid, 'uuid': vdi_uuid, 'name': name, 'description': description, 'read_write': True, 'virtual_size': psize, 'physical_utilisation': psize, 'uri': [image_format.uri_prefix + vdi_uri], 'sharable': sharable, 'keys': {} }
def start_task(dbg_msg, uri, callbacks, name, args): with VolumeContext(callbacks, uri, 'w') as opq: task = subprocess.Popen(args) log.debug(dbg_msg) path = os.path.join(util.var_run_prefix(), 'sr', callbacks.getUniqueIdentifier(opq)) try: os.makedirs(path) except OSError: pass path = os.path.join(path, name + '_task.pickle') with open(path, 'w+') as f: pickle.dump(task, f)
def remove_garbage_volumes(uri, callbacks): """ Find any unreferenced, garbage COW nodes and remove """ with VolumeContext(callbacks, uri, 'w') as opq: with PollLock(opq, 'gl', callbacks, PRIO_GC): with callbacks.db_context(opq) as db: garbage = db.get_garbage_volumes() if len(garbage) > 0: for volume in garbage: callbacks.volumeDestroy(opq, str(volume.id)) with callbacks.db_context(opq) as db: db.delete_volume(volume.id)
def _find_best_non_leaf_coalesceable(uri, callbacks): """ Find the next pair of COW nodes to be coalesced """ with VolumeContext(callbacks, uri, 'w') as opq: ret = (None, None) with PollLock(opq, 'gl', callbacks, PRIO_GC): with callbacks.db_context(opq) as db: nodes = __find_non_leaf_coalesceable(db) for node in nodes: ret = __lock_node_pair(node, opq, db, callbacks) if ret != (None, None): break return ret
def _find_best_leaf_coalesceable(this_host, uri, callbacks): """ Find the next pair of COW nodes to be leaf coalesced """ with VolumeContext(callbacks, uri, 'w') as opq: with PollLock(opq, 'gl', callbacks, PRIO_GC): with callbacks.db_context(opq) as db: nodes = __find_leaf_coalesceable(this_host, db) for node in nodes: # Temp: no leaf on qcow2 for now if node.image_type == ImageFormat.IMAGE_QCOW2: continue with callbacks.db_context(opq) as db: leaf, parent = __lock_node_pair(node, opq, db, callbacks) if (leaf, parent) != (None, None): __leaf_coalesce(leaf, parent, opq, callbacks) return True return False
def start_gc(dbg, sr_type, uri): # Ensure trash directory exists before starting GC. callbacks = util.get_sr_callbacks(sr_type) with VolumeContext(callbacks, uri, 'w') as opq: callbacks.create_trash_dir(opq) if gc_is_enabled(uri, callbacks): # Get the command to run, need to replace pyc with py as __file__ # will be the byte compiled file. dbg_msg = "{}: Starting GC sr_type={} uri={}".format( dbg, sr_type, uri) args = [ os.path.abspath(re.sub("pyc$", "py", __file__)), sr_type, uri ] start_task(dbg_msg, uri, callbacks, "gc", args) else: log.debug('GC is disabled, cannot start it') start_background_tasks(dbg, sr_type, uri, callbacks)
def recover_journal(uri, this_host, callbacks): """ Complete recover operations started in a different instance """ with VolumeContext(callbacks, uri, 'w') as opq: # Take the global SR lock, the coaleasce reparenting happens within # this lock, so if we can get it and if there are any pending # operations then a different process crashed or was aborted and we # need to complete the outstanding operations with PollLock(opq, 'gl', callbacks, PRIO_GC): with callbacks.db_context(opq) as db: # Get the journalled reparent operations journal_entries = db.get_journal_entries() __reparent_children(opq, callbacks, journal_entries) # Now refresh any leaves with callbacks.db_context(opq) as db: refresh_entries = db.get_refresh_entries(this_host) __refresh_leaf_vdis(opq, callbacks, refresh_entries)
def gc_is_enabled(uri, callbacks): with VolumeContext(callbacks, uri, 'w') as opq: return not os.path.exists( os.path.join('/var/lib/sr', callbacks.getUniqueIdentifier(opq), 'gc_disabled'))