def test_detect_cyclic_graph(self): """Test that cyclic graphs are detected""" test_matrix = ( ({ "A": ["B"], "B": ["C"], "C": [], }, False), ({ "A": [], "B": ["C"], "C": [], }, False), ({ "A": ["C"], "B": ["C"], "C": ["A"], }, True), ({ "A": ["B"], "B": ["C"], "C": ["A"], }, True), ) for g, expected_result in test_matrix: if expected_result: self.assertRaises( graph.FoundLoopError, graph.build_graph, g.keys(), g.__getitem__, ) else: graph.build_graph(g.keys(), g.__getitem__)
def test_diamond_graph(self): def test_node_children(testnode): return testnode.children TestNode = namedtuple('TestNode', ['id', 'children']) # A # / \ # B C # \ / # D test_diamond_left = TestNode('D', ()) test_diamond_right = TestNode('D', ()) print('id left: ', id(test_diamond_left)) print('id right:', id(test_diamond_right)) test_left = TestNode('B', (test_diamond_left, )) test_right = TestNode('C', (test_diamond_right, )) test_root = TestNode('A', (test_left, test_right, )) test_nodes = {test_root, } result_graph = graph.build_graph(test_nodes, test_node_children) test_root_node = result_graph[0] self.assertEqual(len(test_root_node.child_nodes), 2) test_left_node = test_root_node.child_nodes[0] test_right_node = test_root_node.child_nodes[1] self.assertEqual(id(test_left_node.child_nodes[0]), id(test_right_node.child_nodes[0]))
def test_resource_graph_walker_listener_plan(self): expected_calls = [ ("on_resource_start", 'A', True), ("on_resource_start", 'C', True), ("on_resource_start", 'D', True), ("on_resource_end", 'D'), ("on_resource_start", 'E', True), ("on_resource_end", 'E'), ("on_resource_end", 'C'), ("on_resource_end", 'A'), ("on_resource_start", 'B', True), ("on_resource_start", 'C', False), ("on_resource_start", 'D', False), ("on_resource_end", 'D'), ("on_resource_start", 'E', False), ("on_resource_end", 'E'), ("on_resource_end", 'C'), ("on_resource_end", 'B'), ] fake_cntxt = "fake_cntxt" fake_protection_plugin = FakeProtectionPlugin(expected_calls) fake_context = ResourceGraphContext( fake_cntxt, plugin_map={"fake_plugin": fake_protection_plugin}) listener = ResourceGraphWalkerListener(fake_context) walker = graph.GraphWalker() walker.register_listener(listener) walker.walk_graph(graph.build_graph(plan_resources, resource_map.__getitem__)) self.assertEqual(len(listener.context.status_getters), 5)
def test_diamond_graph(self): def test_node_children(testnode): return testnode.children TestNode = namedtuple('TestNode', ['id', 'children']) # A # / \ # B C # \ / # D test_diamond_left = TestNode('D', ()) test_diamond_right = TestNode('D', ()) print('id left: ', id(test_diamond_left)) print('id right:', id(test_diamond_right)) test_left = TestNode('B', (test_diamond_left, )) test_right = TestNode('C', (test_diamond_right, )) test_root = TestNode('A', ( test_left, test_right, )) test_nodes = { test_root, } result_graph = graph.build_graph(test_nodes, test_node_children) test_root_node = result_graph[0] self.assertEqual(len(test_root_node.child_nodes), 2) test_left_node = test_root_node.child_nodes[0] test_right_node = test_root_node.child_nodes[1] self.assertEqual(id(test_left_node.child_nodes[0]), id(test_right_node.child_nodes[0]))
def test_resource_graph(self): bank = bank_plugin.Bank(_InMemoryBankPlugin()) bank_lease = _InMemoryLeasePlugin() bank_section = bank_plugin.BankSection(bank, "/checkpoints") owner_id = bank.get_owner_id() plan = fake_protection_plan() cp = checkpoint.Checkpoint.create_in_section(bank_section=bank_section, bank_lease=bank_lease, owner_id=owner_id, plan=plan) resource_graph = graph.build_graph([A, B, C, D], resource_map.__getitem__) cp.resource_graph = resource_graph cp.commit() checkpoint_data = { "version": checkpoint.Checkpoint.VERSION, "id": cp.id, "status": "protecting", "owner_id": owner_id, "protection_plan": { "id": plan.get("id"), "name": plan.get("name"), "resources": plan.get("resources") }, "resource_graph": graph.serialize_resource_graph(resource_graph) } self.assertEqual( checkpoint_data, bank._plugin.get_object( "/checkpoints/%s/%s" % (checkpoint_data["id"], checkpoint._INDEX_FILE_NAME))) self.assertEqual(len(resource_graph), len(cp.resource_graph)) for start_node in resource_graph: self.assertEqual(True, start_node in cp.resource_graph)
def test_resource_graph_walker_listener_plan(self): expected_calls = [ ("on_resource_start", 'A', True), ("on_resource_start", 'C', True), ("on_resource_start", 'D', True), ("on_resource_end", 'D'), ("on_resource_start", 'E', True), ("on_resource_end", 'E'), ("on_resource_end", 'C'), ("on_resource_end", 'A'), ("on_resource_start", 'B', True), ("on_resource_start", 'C', False), ("on_resource_start", 'D', False), ("on_resource_end", 'D'), ("on_resource_start", 'E', False), ("on_resource_end", 'E'), ("on_resource_end", 'C'), ("on_resource_end", 'B'), ] fake_cntxt = "fake_cntxt" fake_protection_plugin = FakeProtectionPlugin(expected_calls) fake_context = ResourceGraphContext( fake_cntxt, plugin_map={"fake_plugin": fake_protection_plugin}) listener = ResourceGraphWalkerListener(fake_context) walker = graph.GraphWalker() walker.register_listener(listener) walker.walk_graph( graph.build_graph(plan_resources, resource_map.__getitem__)) self.assertEqual(len(listener.context.status_getters), 5)
def build_graph(self, context, resources): def fetch_dependent_resources_context(resource): return self.fetch_dependent_resources(context, resource) return build_graph( start_nodes=resources, get_child_nodes_func=fetch_dependent_resources_context, )
def test_graph_pack_unpack(self): test_base = { "A1": ["B1", "B2"], "B1": ["C1", "C2"], "B2": ["C3", "C2"], "C1": [], "C2": [], "C3": [], } test_graph = graph.build_graph(test_base.keys(), test_base.__getitem__) packed_graph = graph.pack_graph(test_graph) unpacked_graph = graph.unpack_graph(packed_graph) self.assertEqual(test_graph, unpacked_graph)
def test_build_protect_task_flow(self, mock_build_graph): pr = ProviderRegistry() self.assertEqual(len(pr.providers), 1) plugable_provider = pr.providers["fake_id1"] cntxt = "fake_cntxt" plan = fake_protection_plan() workflow_engine = FakeWorkflowEngine() operation = constants.OPERATION_PROTECT ctx = {"context": cntxt, "plan": plan, "workflow_engine": workflow_engine, "operation_type": operation, } expected_calls = [ ("on_resource_start", 'A', True), ("on_resource_start", 'C', True), ("on_resource_start", 'D', True), ("on_resource_end", 'D'), ("on_resource_start", 'E', True), ("on_resource_end", 'E'), ("on_resource_end", 'C'), ("on_resource_end", 'A'), ("on_resource_start", 'B', True), ("on_resource_start", 'C', False), ("on_resource_start", 'D', False), ("on_resource_end", 'D'), ("on_resource_start", 'E', False), ("on_resource_end", 'E'), ("on_resource_end", 'C'), ("on_resource_end", 'B'), ] fake_registry = FakeProtectableRegistry() plugable_provider.protectable_registry = fake_registry fake_registry.build_graph = mock.MagicMock() resource_graph = build_graph(plan_resources, resource_map.__getitem__) mock_build_graph.return_value = resource_graph fake_protection_plugin = FakeProtectionPlugin(expected_calls) plugable_provider._plugin_map = { "fake_plugin": fake_protection_plugin } result = plugable_provider.build_task_flow(ctx) self.assertEqual(len(result["status_getters"]), 5) self.assertEqual(len(result["task_flow"]), 5)
def test_build_protect_task_flow(self, mock_build_graph): pr = ProviderRegistry() self.assertEqual(len(pr.providers), 1) plugable_provider = pr.providers["fake_id1"] cntxt = "fake_cntxt" plan = fake_protection_plan() workflow_engine = FakeWorkflowEngine() operation = constants.OPERATION_PROTECT ctx = { "context": cntxt, "plan": plan, "workflow_engine": workflow_engine, "operation_type": operation, } expected_calls = [ ("on_resource_start", 'A', True), ("on_resource_start", 'C', True), ("on_resource_start", 'D', True), ("on_resource_end", 'D'), ("on_resource_start", 'E', True), ("on_resource_end", 'E'), ("on_resource_end", 'C'), ("on_resource_end", 'A'), ("on_resource_start", 'B', True), ("on_resource_start", 'C', False), ("on_resource_start", 'D', False), ("on_resource_end", 'D'), ("on_resource_start", 'E', False), ("on_resource_end", 'E'), ("on_resource_end", 'C'), ("on_resource_end", 'B'), ] fake_registry = FakeProtectableRegistry() plugable_provider.protectable_registry = fake_registry fake_registry.build_graph = mock.MagicMock() resource_graph = build_graph(plan_resources, resource_map.__getitem__) mock_build_graph.return_value = resource_graph fake_protection_plugin = FakeProtectionPlugin(expected_calls) plugable_provider._plugin_map = {"fake_plugin": fake_protection_plugin} result = plugable_provider.build_task_flow(ctx) self.assertEqual(len(result["status_getters"]), 5) self.assertEqual(len(result["task_flow"]), 5)
def test_graph_deserialize_unordered_adjacency(self): test_base = { "A1": ["B1", "B2"], "B1": ["C1", "C2"], "B2": ["C3", "C2"], "C1": [], "C2": [], "C3": [], } test_graph = graph.build_graph(test_base.keys(), test_base.__getitem__) packed_graph = graph.pack_graph(test_graph) reversed_adjacency = tuple(reversed(packed_graph.adjacency)) packed_graph = graph.PackedGraph(packed_graph.nodes, reversed_adjacency) with self.assertRaisesRegex(exception.InvalidInput, "adjacency list"): graph.unpack_graph(packed_graph)
def test_pack_unpack_graph_with_isolated_node(self): test_base = { "A1": ["B1", "B2"], "B1": ["C1", "C2"], "B2": ["C3", "C2"], "C1": [], "C2": [], "C3": [], "C4": [] } test_graph = graph.build_graph(test_base.keys(), test_base.__getitem__) packed_graph = graph.pack_graph(test_graph) unpacked_graph = graph.unpack_graph(packed_graph) self.assertEqual(len(test_graph), len(unpacked_graph)) for start_node in test_graph: self.assertEqual(True, start_node in unpacked_graph)
def test_graph_serialize_deserialize(self): Format = namedtuple('Format', ['loads', 'dumps']) formats = [ Format(jsonutils.loads, jsonutils.dumps), Format(msgpackutils.loads, msgpackutils.dumps), ] test_base = { "A1": ["B1", "B2"], "B1": ["C1", "C2"], "B2": ["C3", "C2"], "C1": [], "C2": [], "C3": [], } test_graph = graph.build_graph(test_base.keys(), test_base.__getitem__) for fmt in formats: serialized = fmt.dumps(graph.pack_graph(test_graph)) unserialized = graph.unpack_graph(fmt.loads(serialized)) self.assertEqual(test_graph, unserialized)
def test_resource_graph(self): bank = bank_plugin.Bank(_InMemoryBankPlugin()) bank_lease = _InMemoryLeasePlugin() bank_section = bank_plugin.BankSection(bank, "/checkpoints") owner_id = bank.get_owner_id() plan = fake_protection_plan() cp = checkpoint.Checkpoint.create_in_section(bank_section=bank_section, bank_lease=bank_lease, owner_id=owner_id, plan=plan) resource_graph = graph.build_graph([A, B, C, D], resource_map.__getitem__) cp.resource_graph = resource_graph cp.commit() checkpoint_data = { "version": checkpoint.Checkpoint.VERSION, "id": cp.id, "status": "protecting", "owner_id": owner_id, "protection_plan": { "id": plan.get("id"), "name": plan.get("name"), "resources": plan.get("resources") }, "resource_graph": graph.serialize_resource_graph( resource_graph) } self.assertEqual( checkpoint_data, bank._plugin.get_object( "/checkpoints/%s/%s" % (checkpoint_data["id"], checkpoint._INDEX_FILE_NAME) ) ) self.assertEqual(len(resource_graph), len(cp.resource_graph)) for start_node in resource_graph: self.assertEqual(True, start_node in cp.resource_graph)
def test_resource_graph(self): bank = bank_plugin.Bank(_InMemoryBankPlugin()) bank_lease = _InMemoryLeasePlugin() bank_section = bank_plugin.BankSection(bank, "/checkpoints") owner_id = bank.get_owner_id() plan = fake_protection_plan() checkpoint = Checkpoint.create_in_section(bank_section=bank_section, bank_lease=bank_lease, owner_id=owner_id, plan=plan) resource_graph = graph.build_graph([A, B, C, D], resource_map.__getitem__) checkpoint.resource_graph = resource_graph checkpoint.commit() checkpoint_data = { "version": Checkpoint.VERSION, "id": checkpoint.id, "status": "protecting", "owner_id": owner_id, "protection_plan": { "id": plan.get("id"), "name": plan.get("name"), "resources": plan.get("resources") }, "resource_graph": graph.pack_graph(resource_graph) } self.assertEqual( checkpoint_data, bank._plugin.get_object( "/checkpoints%s" % checkpoint._index_file_path ) ) self.assertEqual( graph.unpack_graph(graph.pack_graph(resource_graph)), checkpoint.resource_graph)
def test_source_set(self): """Test that the source set only contains sources""" test_matrix = ( ({ "A": ["B"], "B": ["C"], "C": [], }, {"A"}), ({ "A": [], "B": ["C"], "C": [], }, {"A", "B"}), ({ "A": ["C"], "B": ["C"], "C": [], }, {"A", "B"}), ) for g, expected_result in test_matrix: result = graph.build_graph(g.keys(), g.__getitem__) self.assertEqual({node.value for node in result}, expected_result)
A = Resource(id='A', type='fake', name='fake') B = Resource(id='B', type='fake', name='fake') C = Resource(id='C', type='fake', name='fake') D = Resource(id='D', type='fake', name='fake') E = Resource(id='E', type='fake', name='fake') resource_map = { A: [C], B: [C], C: [D, E], D: [], E: [], } resource_graph = build_graph([A, B, C, D], resource_map.__getitem__) def fake_protection_plan(): protection_plan = { 'id': 'fake_id', 'is_enabled': True, 'name': 'fake_protection_plan', 'comments': '', 'revision': 0, 'resources': [{
def build_graph(self, context, resources): return build_graph( start_nodes=resources, get_child_nodes_func=self.fetch_dependent_resources, )
def test_graph_walker(self): test_matrix = ( ({ 'A': ['B'], 'B': ['C'], 'C': [], }, ( ("on_node_enter", 'A', False), ("on_node_enter", 'B', False), ("on_node_enter", 'C', False), ("on_node_exit", 'C'), ("on_node_exit", 'B'), ("on_node_exit", 'A'), )), ({ 'A': ['C'], 'B': ['C'], 'C': [], }, ( ("on_node_enter", 'A', False), ("on_node_enter", 'C', False), ("on_node_exit", 'C'), ("on_node_exit", 'A'), ("on_node_enter", 'B', False), ("on_node_enter", 'C', True), ("on_node_exit", 'C'), ("on_node_exit", 'B'), )), ({ 'A': ['C'], 'B': ['C'], 'C': ['D', 'E'], 'D': [], 'E': [], }, ( ("on_node_enter", 'A', False), ("on_node_enter", 'C', False), ("on_node_enter", 'D', False), ("on_node_exit", 'D'), ("on_node_enter", 'E', False), ("on_node_exit", 'E'), ("on_node_exit", 'C'), ("on_node_exit", 'A'), ("on_node_enter", 'B', False), ("on_node_enter", 'C', True), ("on_node_enter", 'D', True), ("on_node_exit", 'D'), ("on_node_enter", 'E', True), ("on_node_exit", 'E'), ("on_node_exit", 'C'), ("on_node_exit", 'B'), )), ) for g, expected_calls in test_matrix: listener = _TestGraphWalkerListener(expected_calls, self) walker = graph.GraphWalker() walker.register_listener(listener) keys = list(g.keys()) keys.sort() walker.walk_graph(graph.build_graph(keys, g.__getitem__))
A = Resource(id='A', type='fake', name='fake') B = Resource(id='B', type='fake', name='fake') C = Resource(id='C', type='fake', name='fake') D = Resource(id='D', type='fake', name='fake') E = Resource(id='E', type='fake', name='fake') resource_map = { A: [C], B: [C], C: [D, E], D: [], E: [], } resource_graph = build_graph([A, B, C, D], resource_map.__getitem__) def fake_protection_plan(): protection_plan = {'id': 'fake_id', 'is_enabled': True, 'name': 'fake_protection_plan', 'comments': '', 'revision': 0, 'resources': [ {"id": "A", "type": "fake", "name": "fake"}, {"id": "B", "type": "fake", "name": "fake"}, {"id": "C", "type": "fake", "name": "fake"}, {"id": "D", "type": "fake", "name": "fake"}], 'protection_provider': None, 'parameters': {},