def test_complex_circular_rev(self): d = dependencies.Dependencies([('last', 'e1'), ('last', 'mid1'), ('last', 'mid2'), ('mid1', 'e2'), ('mid1', 'mid3'), ('mid2', 'mid3'), ('mid3', 'e3'), ('e3', 'mid1')]) self.assertRaises(dependencies.CircularDependencyException, list, reversed(d))
def test_roots(self): d = dependencies.Dependencies([('last1', 'mid'), ('last2', 'mid'), ('mid', 'first1'), ('mid', 'first2')]) leaves = sorted(list(d.roots())) self.assertEqual(['last1', 'last2'], leaves)
def test_circular_rev(self): d = dependencies.Dependencies([('first', 'second'), ('second', 'third'), ('third', 'first')]) self.assertRaises(dependencies.CircularDependencyException, list, reversed(d))
def test_noexist_partial(self): d = dependencies.Dependencies([('foo', 'bar')]) def get(i): return d[i] self.assertRaises(KeyError, get, 'baz')
def _get_dependencies(resources): '''Return the dependency graph for a list of resources''' deps = dependencies.Dependencies() for resource in resources: resource.add_dependencies(deps) return deps
def test_simple_partial(self): d = dependencies.Dependencies([('last', 'middle'), ('middle', 'first')]) p = d['middle'] order = list(iter(p)) self.assertEqual(2, len(order)) for n in ('last', 'middle'): self.assertIn(n, order, "'%s' not found in dependency order" % n) self.assertGreater(order.index('last'), order.index('middle'))
def test_simple_multilevel_partial(self): d = dependencies.Dependencies([('last', 'middle'), ('middle', 'target'), ('target', 'first')]) p = d['target'] order = list(iter(p)) self.assertEqual(3, len(order)) for n in ('last', 'middle', 'target'): self.assertIn(n, order, "'%s' not found in dependency order" % n)
def test_complex_partial(self): d = dependencies.Dependencies([('last', 'e1'), ('last', 'mid1'), ('last', 'mid2'), ('mid1', 'e2'), ('mid1', 'mid3'), ('mid2', 'mid3'), ('mid3', 'e3')]) p = d['mid3'] order = list(iter(p)) self.assertEqual(4, len(order)) for n in ('last', 'mid1', 'mid2', 'mid3'): self.assertIn(n, order, "'%s' not found in dependency order" % n)
def setUp(self): super(ResourceDependenciesTest, self).setUp() utils.setup_dummy_db() resource._register_class('GenericResourceType', generic_rsrc.GenericResource) resource._register_class('ResourceWithPropsType', generic_rsrc.ResourceWithProps) self.deps = dependencies.Dependencies()
def _dep_test(self, *edges): dummy = DummyTask(getattr(self, 'steps', 3)) deps = dependencies.Dependencies(edges) tg = scheduler.DependencyTaskGroup(deps, dummy) self.mox.StubOutWithMock(dummy, 'do_step') yield dummy self.mox.ReplayAll() scheduler.TaskRunner(tg)(wait_time=None) self.mox.VerifyAll()
def _dep_test(self, func, checkorder, deps): nodes = set.union(*[set(e) for e in deps]) d = dependencies.Dependencies(deps) order = list(func(d)) for n in nodes: self.assertIn(n, order, '"%s" is not in the sequence' % n) self.assertEqual(1, order.count(n)) self.assertEqual(len(nodes), len(order)) for l, f in deps: checkorder(order.index(f), order.index(l))
def test_delete_retrigger_check_resource_new_traversal_updates_rsrc( self, mock_cru, mock_crc, mock_pcr, mock_csc): # mock dependencies to indicate a rsrc with id 2 has an update # in latest traversal self.stack._convg_deps = dependencies.Dependencies([ [(1, False), (1, True)], [(2, False), (2, True)]]) # simulate rsrc 2 completing its delete for old traversal # and calling rcr self.cr.retrigger_check_resource(self.ctx, 2, self.stack) # Ensure that pcr was called with proper delete traversal mock_pcr.assert_called_once_with(self.ctx, mock.ANY, 2, self.stack.current_traversal, mock.ANY, (2, True), None, True, None)
def _dep_test(self, *edges): dummy = DummyTask(getattr(self, 'steps', 3)) deps = dependencies.Dependencies(edges) tg = scheduler.DependencyTaskGroup( deps, dummy, aggregate_exceptions=self.aggregate_exceptions, reverse=self.reverse_order) self.m.StubOutWithMock(dummy, 'do_step') yield dummy self.m.ReplayAll() scheduler.TaskRunner(tg)(wait_time=None)
def test_required_by(self): d = dependencies.Dependencies([('last', 'e1'), ('last', 'mid1'), ('last', 'mid2'), ('mid1', 'e2'), ('mid1', 'mid3'), ('mid2', 'mid3'), ('mid3', 'e3')]) self.assertEqual(0, len(list(d.required_by('last')))) required_by = list(d.required_by('mid3')) self.assertEqual(2, len(required_by)) for n in ('mid1', 'mid2'): self.assertIn(n, required_by, "'%s' not found in required_by" % n) required_by = list(d.required_by('e2')) self.assertEqual(1, len(required_by)) self.assertIn('mid1', required_by, "'%s' not found in required_by" % n) self.assertRaises(KeyError, d.required_by, 'foo')
def dependencies(self): ''' Return a Dependencies object representing the dependencies between update operations to move from an existing stack definition to a new one. ''' existing_deps = self.existing_stack.dependencies new_deps = self.new_stack.dependencies def edges(): # Create/update the new stack's resources in create order for e in new_deps.graph().edges(): yield e # Destroy/cleanup the old stack's resources in delete order for e in existing_deps.graph(reverse=True).edges(): yield e # Don't cleanup old resources until after they have been replaced for name, res in six.iteritems(self.existing_stack): if name in self.new_stack: yield (res, self.new_stack[name]) return dependencies.Dependencies(edges())
def _dep_test(self, *edges): dummy = DummyTask() class TaskMaker(object): def __init__(self, name): self.name = name def __repr__(self): return 'Dummy task "%s"' % self.name def __call__(self, *args, **kwargs): return dummy(self.name, *args, **kwargs) deps = dependencies.Dependencies(edges) tg = scheduler.DependencyTaskGroup(deps, TaskMaker) self.mox.StubOutWithMock(dummy, 'do_step') yield dummy self.mox.ReplayAll() scheduler.TaskRunner(tg)(wait_time=None) self.mox.VerifyAll()
def test_circular_deps(self): d = dependencies.Dependencies([('first', 'second'), ('second', 'third'), ('third', 'first')]) self.assertRaises(dependencies.CircularDependencyException, scheduler.DependencyTaskGroup, d)
def test_disjoint(self): d = dependencies.Dependencies([('1', None), ('2', None)]) l = list(iter(d)) self.assertEqual(2, len(l)) self.assertIn('1', l) self.assertIn('2', l)
def test_single_node(self): d = dependencies.Dependencies([('only', None)]) l = list(iter(d)) self.assertEqual(1, len(l)) self.assertEqual('only', l[0])
def test_repr(self): dp = dependencies.Dependencies([('1', None), ('2', '3'), ('2', '4')]) s = "Dependencies([('1', None), ('2', '3'), ('2', '4')])" self.assertEqual(s, repr(dp))
def test_edges(self): input_edges = [('1', None), ('2', '3'), ('2', '4')] dp = dependencies.Dependencies(input_edges) self.assertEqual(set(input_edges), set(dp.graph().edges()))
def test_noexist_partial(self): d = dependencies.Dependencies([('foo', 'bar')]) get = lambda i: d[i] self.assertRaises(KeyError, get, 'baz')
def check_resource(self, cnxt, resource_id, current_traversal, data, is_update): ''' Process a node in the dependency graph. The node may be associated with either an update or a cleanup of its associated resource. ''' try: rsrc, stack = resource.Resource.load(cnxt, resource_id, data) except exception.NotFound: return tmpl = stack.t if current_traversal != rsrc.stack.current_traversal: LOG.debug('[%s] Traversal cancelled; stopping.', current_traversal) return current_deps = ([tuple(i), (tuple(j) if j is not None else None)] for i, j in rsrc.stack.current_deps['edges']) deps = dependencies.Dependencies(edges=current_deps) graph = deps.graph() if is_update: if (rsrc.replaced_by is not None and rsrc.current_template_id != tmpl.id): return try: check_resource_update(rsrc, tmpl.id, data) except resource.UpdateReplace: # NOTE(sirushtim): Implemented by spec # convergence-resource-replacement. rsrc.make_replacement() return except resource.UpdateInProgress: return input_data = construct_input_data(rsrc) else: try: check_resource_cleanup(rsrc, tmpl.id, data) except resource.UpdateInProgress: return graph_key = (rsrc.id, is_update) if graph_key not in graph and rsrc.replaces is not None: # If we are a replacement, impersonate the replaced resource for # the purposes of calculating whether subsequent resources are # ready, since everybody has to work from the same version of the # graph. Our real resource ID is sent in the input_data, so the # dependencies will get updated to point to this resource in time # for the next traversal. graph_key = (rsrc.replaces, is_update) try: for req, fwd in deps.required_by(graph_key): propagate_check_resource(cnxt, self._rpc_client, req, current_traversal, set(graph[(req, fwd)]), graph_key, input_data if fwd else rsrc.id, fwd) check_stack_complete(cnxt, rsrc.stack, current_traversal, rsrc.id, graph, is_update) except sync_point.SyncPointNotFound: # NOTE(sirushtim): Implemented by spec # convergence-concurrent-workflow pass
def _compute_dependencies(self, stack): current_deps = ([tuple(i), (tuple(j) if j is not None else None)] for i, j in stack.current_deps['edges']) return dependencies.Dependencies(edges=current_deps)
def test_self_ref(self): d = dependencies.Dependencies([('node', 'node')]) self.assertRaises(dependencies.CircularDependencyException, list, iter(d))
def test_single_partial(self): d = dependencies.Dependencies([('last', 'first')]) p = d['last'] l = list(iter(p)) self.assertEqual(1, len(l)) self.assertEqual('last', l[0])