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_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_resource_graph(self): bank = bank_plugin.Bank(_InMemoryBankPlugin()) bank_lease = _InMemoryLeasePlugin() checkpoints_section = bank_plugin.BankSection(bank, "/checkpoints") indices_section = bank_plugin.BankSection(bank, "/indices") owner_id = bank.get_owner_id() plan = fake_protection_plan() cp = checkpoint.Checkpoint.create_in_section( checkpoints_section=checkpoints_section, indices_section=indices_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 = cp._md_cache 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.assertIn(start_node, cp.resource_graph)
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(2, len(test_root_node.child_nodes)) 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() checkpoints_section = bank_plugin.BankSection(bank, "/checkpoints") indices_section = bank_plugin.BankSection(bank, "/indices") owner_id = bank.get_owner_id() plan = fake_protection_plan() cp = checkpoint.Checkpoint.create_in_section( checkpoints_section=checkpoints_section, indices_section=indices_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 = cp._md_cache 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.assertIn(start_node, 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 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 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_serialize(self): resource_a = resource.Resource('server', 0, 'a', {'name': 'a'}) resource_b = resource.Resource('volume', 1, 'b', {'name': 'b'}) test_base = {resource_a: [resource_b], resource_b: []} test_graph = graph.build_graph(test_base.keys(), test_base.__getitem__) self.assertIn(graph.serialize_resource_graph(test_graph), [ '[{"0x1": ["server", 0, "a", {"name": "a"}], ' '"0x0": ["volume", 1, "b", {"name": "b"}]}, ' '[["0x1", ["0x0"]]]]', '[{"0x0": ["volume", 1, "b", {"name": "b"}], ' '"0x1": ["server", 0, "a", {"name": "a"}]}, ' '[["0x1", ["0x0"]]]]' ])
def setUp(self): super(ResourceFlowTest, self).setUp() self.resource_graph = { parent: [child], child: [grandchild], grandchild: [], } self.provider = fakes.FakeProvider() self.test_graph = graph.build_graph([parent], self.resource_graph.__getitem__) self.taskflow_engine = TaskFlowEngine()
def setUp(self): super(ResourceFlowTest, self).setUp() self.resource_graph = { parent: [child], child: [grandchild], grandchild: [], } self.provider = fakes.FakeProvider() self.test_graph = graph.build_graph([parent], self.resource_graph.__getitem__) self.taskflow_engine = TaskFlowEngine()
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_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_pack_unpack_graph(self): test_base = { "A1": ["B1", "B2", "B3", "B4"], "B1": [], "B2": [], "B3": ["B1"], "B4": ["B2"], } 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.assertIn(start_node, unpacked_graph)
def test_pack_unpack_graph(self): test_base = { "A1": ["B1", "B2", "B3", "B4"], "B1": [], "B2": [], "B3": ["B1"], "B4": ["B2"], } 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.assertIn(start_node, unpacked_graph)
def build_graph(self, context, resources): def fetch_dependent_resources_context(resource): dependent_resources = self.fetch_dependent_resources( context, resource) # The extra_info field of the resource is a dict # The dict can not be handled by build_graph, it will throw a # error. TypeError: unhashable type: 'dict' return [Resource(type=dependent_resource.type, id=dependent_resource.id, name=dependent_resource.name, extra_info=None) for dependent_resource in dependent_resources] return build_graph( start_nodes=resources, get_child_nodes_func=fetch_dependent_resources_context, )
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_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_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_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_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)
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(expected_result, {node.value for node in 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': [{
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': {},
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__))
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__))