def _can_disk_snapshot_storage_merge( self, node: tree.StorageNode) -> (bool, int): if node.is_root and len(node.children) > 1: return False, 0 # 不支持:此时如果合并,那么快照树会分裂为两棵树 if node.is_leaf: return False, 0 # 不支持:当前节点为叶子,应该走删除逻辑,而非回收逻辑 storage_obj = node.storage if storage_obj.status != m.SnapshotStorage.STATUS_RECYCLING: return False, 0 parent_storage_obj = self._get_parent_storage_obj_by_node(node) if parent_storage_obj and parent_storage_obj in ( m.SnapshotStorage.STATUS_CREATING, m.SnapshotStorage.STATUS_WRITING, m.SnapshotStorage.STATUS_HASHING, m.SnapshotStorage.STATUS_ABNORMAL): return False, 0 # 不支持:父快照存储正在生成中 if rt.PathInMount.is_in_not_mount(storage_obj.image_path): return False if storage_obj.is_cdp: if node.is_root: return False, 0 # 不支持:如果cdp在根节点中 if self._is_child_depend_with_timestamp(node): return False, 0 # 不支持:cdp文件的中间有依赖的情况 if srm.get_srm().is_storage_writing(parent_storage_obj.image_path): return False, 0 # 不支持:父快照存储正在写入中 """可回收""" return True, self.TYPE_CDP # else is_qcow if node.storage.file_level_deduplication: return False, 0 # 不支持:带有文件级去重 if self._is_children_in_other_file(node): if node.is_root: return False, 0 # 不支持:如果该节点为根节点 if parent_storage_obj and parent_storage_obj.is_cdp: return False, 0 # 不支持:父快照是CDP文件 if parent_storage_obj.disk_bytes != node.storage.disk_bytes: return False, 0 # 不支持:父快照是CDP文件 if self._is_multi_snapshot_in_the_qcow(node): return False, 0 # 不支持:有其他快照点在该qcow文件中 if srm.get_srm().is_storage_writing(parent_storage_obj.image_path): return False, 0 # 不支持:父快照存储正在写入中 """可回收""" return True, self.TYPE_QCOW_MOVE_DATA # else not is_children_in_other_file if srm.get_srm().is_storage_writing(node.storage.image_path): return False, 0 # 不支持:快照存储所在文件正在写入中 """可回收""" return True, self.TYPE_QCOW_REMOVE
def _query_parent_storage_and_chain( self) -> (storage.Storage, chain.StorageChainForRW, str): if self.is_root_node: parent_storage = None tree_ident = self._query_tree_ident_from_children() depend_nodes = list() else: parent_storage = self._query_parent_storage() tree_ident = parent_storage.tree_ident storage_tree = tree.generate(tree_ident) depend_nodes = storage_tree.fetch_nodes_to_root(self.parent_ident) rw_chain = chain.StorageChainForRW(srm.get_srm(), self.caller_name, self.parent_timestamp) for node in depend_nodes: if node.storage.status == m.SnapshotStorage.STATUS_CREATING: raise exc.generate_exception_and_logger( r'快照存储链无效,存在创建中的快照存储', f'invalid storage chain, {node.storage}', 0) if node.storage.status == m.SnapshotStorage.STATUS_ABNORMAL: raise exc.generate_exception_and_logger( r'快照存储链无效,存在异常的快照存储', f'invalid storage chain, {node.storage}', 0) rw_chain.insert_tail(node.storage) return parent_storage, rw_chain, tree_ident
def __init__(self, storage_obj: m.SnapshotStorage, call_name: str): super(DeleteWork, self).__init__() assert storage_obj.status in ( m.SnapshotStorage.STATUS_RECYCLING, m.SnapshotStorage.STATUS_ABNORMAL, ) self.duplicated = False self.w_chain = chain.StorageChainForWrite( srm.get_srm(), call_name).insert_tail(storage_obj)
def _create_write_chain(self, storage_tree, call_name) -> chain.StorageChainForWrite: write_chain = chain.StorageChainForWrite(srm.get_srm(), call_name) depend_nodes = storage_tree.fetch_nodes_to_root( self.parent_storage.ident) for node in depend_nodes: write_chain.insert_tail(node.storage) write_chain.insert_tail(self.new_storage.storage_obj) return write_chain
def execute(self) -> pool.Handle: """ 1. 更新数据库记录,新建立的存储对象指向父节点 * 父快照为qcow类型文件时,支持父快照还在日志表中 CreateInJournal; 也就是,创建文件的时序与日志的时序不一致 需要在日志表中记录当前节点,支持创建父快照时,调整当前节点的父 * 其余情况,其父快照必须已经在存储对象表中 2. 调用底层模块创建存储文件 * 如果创建突然中断,那么数据库记录会保持为 Creating 状态,下次启动时进行状态修正 * 如果创建失败,那么标记为 Abnormal 状态,后台回收线程异步进行状态修正 3. 更新数据库记录,将建立的存储对象标记为 Writing 状态 :remark: 1. 仅仅支持单次的创建时序与依赖关系顺序不一致 也就是不支持依赖链上有超过一个 CreateInJournal 类型的节点,且这些节点在未来某个时刻会真实创建; 上层逻辑应杜绝出现此类状况 参考 CreateQcowStorage """ handle: pool.Handle = pool.generate_handle(self._handle, True, self.raw_flag) new_snapshot: storage.Storage = None try: with deal_handle_when_excption(handle): with lm.get_journal_locker( self.trace_msg), lm.get_storage_locker( self.trace_msg), s.transaction(): parent_storage = self._query_parent_storage() new_snapshot = self._create_new_storage(parent_storage) handle.storage_chain = (chain.StorageChainForWrite( srm.get_srm(), self.caller_name).insert_tail( new_snapshot.storage_obj).acquire()) with handle.locker: handle.raw_handle, handle.ice_endpoint = ( action.DiskSnapshotAction.create_cdp_snapshot( handle.storage_chain.last_storage_item, handle.raw_flag)) with lm.get_storage_locker(self.trace_msg), s.transaction(): new_snapshot.update_status( m.SnapshotStorage.STATUS_WRITING) return handle except Exception as e: self._set_storage_abnormal_when_except(new_snapshot) raise e
def generate_qcow(parent_storage_obj: m.SnapshotStorage, folder, new_disk_bytes): if parent_storage_obj is None: return ImagePathGenerator.generate_new_qcow(folder) """ 需要创建新的文件的情况 1. 如果与父的disk_bytes不同, 2. 如果与父的存储目录路径不同, 3. 如果父文件不是qcow, 4. 如果父文件正在创建或写入中, 其他情况 1. 复用父快照文件 """ if (new_disk_bytes != parent_storage_obj.disk_bytes or parent_storage_obj.type != m.SnapshotStorage.TYPE_QCOW or folder != os.path.split(parent_storage_obj.image_path)[0] or srm.get_srm().is_storage_writing( parent_storage_obj.image_path)): return ImagePathGenerator.generate_new_qcow(folder) else: return parent_storage_obj.image_path
def _can_disk_snapshot_storage_delete(node: tree.StorageNode) -> bool: storage_obj: m.SnapshotStorage = node.storage if storage_obj.status != m.SnapshotStorage.STATUS_RECYCLING: return False ref_manager = srm.get_srm() if ref_manager.is_storage_using(storage_obj.ident): return False if rt.PathInMount.is_in_not_mount(storage_obj.image_path): return False if storage_obj.is_qcow and ref_manager.is_storage_writing( storage_obj.image_path): return False for child_node in node.children: if child_node.storage_obj.storage_status != m.SnapshotStorage.STATUS_RECYCLING: return False return True
def _generate_chain(self, depend_nodes): r_chain = chain.StorageChainForRead(srm.get_srm(), self.caller_name, self.timestamp) for node in depend_nodes: r_chain.insert_tail(node.storage) return r_chain
def __init__(self, storage_obj: m.SnapshotStorage, call_name: str): call_name += f' DeleteQcowSnapshotWork {storage_obj.ident}' super(DeleteQcowSnapshotWork, self).__init__(storage_obj, call_name) assert storage_obj.is_qcow assert not srm.get_srm().is_storage_writing(storage_obj.image_path)