def __reparent_children(opq, callbacks, journal_entries): """ Reparent the children of a node after it has been coalesced """ for child in journal_entries: child_path = callbacks.volumeGetPath(opq, str(child.id)) with callbacks.db_context(opq) as db: child_volume = db.get_volume_by_id(child.id) # Find all leaves having child as an ancestor leaves = [] log.debug('Find active leaves of {}'.format(child.id)) find_active_leaves(child_volume, db, leaves) image_utils = ImageFormat.get_format( child_volume.image_type).image_utils # reparent child to grandparent log.debug("Reparenting {} to {}".format(child.id, child.new_parent_id)) with callbacks.db_context(opq) as db: db.update_volume_parent(child.id, child.new_parent_id) new_parent_path = callbacks.volumeGetPath(opq, str(child.new_parent_id)) image_utils.set_parent(GC, child_path, new_parent_path) db.remove_journal_entry(child.id) # Add leaves to database if leaves: # Refresh all leaves having child as an ancestor leaves_to_refresh = db.add_refresh_entries( child.id, child.parent_id, child.new_parent_id, leaves) log.debug("Children {}: leaves: {} will be refreshed".format( child.id, [str(x) for x in leaves_to_refresh]))
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 refresh_live_cow_chain(vdi, refresh, callbacks, opq): """ Refresh the datapath for the vdi """ assert vdi.active_on vdi_meta_path = callbacks.get_data_metadata_path(opq, vdi.uuid) image_utils = ImageFormat.get_format(vdi.image_type).image_utils image_utils.refresh_datapath_coalesce( GC, vdi_meta_path, callbacks.volumeGetPath(opq, str(refresh.old_parent)), callbacks.volumeGetPath(opq, str(refresh.new_parent)))
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 __leaf_coalesce(leaf, parent, opq, callbacks): """ Perform leaf volume coalesce. Must be called from inside a global SR lock. """ leaf_volume = leaf.volume parent_volume = parent.volume log.debug('leaf_coalesce key={}, parent={}'.format(leaf_volume.id, parent_volume.id)) leaf_path = callbacks.volumeGetPath(opq, str(leaf_volume.id)) parent_path = callbacks.volumeGetPath(opq, str(parent_volume.id)) leaf_psize = os.path.getsize(leaf_path) try: with callbacks.db_context(opq) as db: vdi = db.get_vdi_for_volume(leaf_volume.id) image_utils = ImageFormat.get_format(vdi.image_type).image_utils vdi_meta_path = callbacks.get_data_metadata_path(opq, vdi.uuid) if leaf_psize < _LEAF_COALESCE_MAX_SIZE: log.debug("Running leaf-coalesce on {}".format(leaf_volume.id)) image_utils.coalesce(GC, leaf_path, parent_path) if vdi.active_on: image_utils.pause_datapath(GC, vdi_meta_path) with callbacks.db_context(opq) as db: db.update_vdi_volume_id(vdi.uuid, leaf_volume.parent_id) if vdi.active_on: image_utils.unpause_datapath(GC, vdi_meta_path, parent_path) with callbacks.db_context(opq) as db: db.delete_volume(leaf_volume.id) callbacks.volumeDestroy(opq, str(leaf_volume.id)) else: # If the leaf is larger than the maximum size allowed for # a live leaf coalesce to happen, snapshot it and let # non_leaf_coalesce() take care of it. log.debug("Snapshot {} and let non-leaf-coalesce handle it".format( leaf_volume.id)) with callbacks.db_context(opq) as db: new_leaf_volume = db.insert_child_volume( leaf_volume.id, leaf_volume.vsize) new_leaf_path = callbacks.volumeCreate(opq, str(new_leaf_volume.id), leaf_volume.vsize) image_utils.online_snapshot(GC, new_leaf_path, leaf_path, False) db.update_vdi_volume_id(vdi.uuid, new_leaf_volume.id) if vdi.active_on: image_utils.refresh_datapath_clone(GC, vdi_meta_path, new_leaf_path) finally: callbacks.volumeUnlock(opq, leaf.lock) callbacks.volumeUnlock(opq, parent.lock)