def lb_req(url, json_response, response): """ Return a SequenceDispatcher two-tuple that matches a service request to a particular load balancer endpoint (using GET), and returns the given ``response`` as the content in an HTTP 200 ``StubResponse``. """ if isinstance(response, Exception): def handler(i): raise response log_seq = [] else: def handler(i): return (StubResponse(200, {}), response) log_seq = [(Log(mock.ANY, mock.ANY), lambda i: None)] return ( Retry( effect=mock.ANY, should_retry=ShouldDelayAndRetry( can_retry=retry_times(5), next_interval=exponential_backoff_interval(2)) ), nested_sequence([ (service_request( ServiceType.CLOUD_LOAD_BALANCERS, 'GET', url, json_response=json_response).intent, handler) ] + log_seq) )
def _perform_add_event(self, response_sequence): """ Given a sequence of functions that take an intent and returns a response (or raises an exception), perform :func:`add_event` and return the result. """ log = object() eff = add_event(self.event, 'tid', 'ord', log) uid = '00000000-0000-0000-0000-000000000000' svrq = service_request( ServiceType.CLOUD_FEEDS, 'POST', 'autoscale/events', headers={ 'content-type': ['application/vnd.rackspace.atom+json']}, data=self._get_request('INFO', uid, 'tid'), log=log, success_pred=has_code(201), json_response=False) seq = [ (TenantScope(mock.ANY, 'tid'), nested_sequence([ retry_sequence( Retry(effect=svrq, should_retry=ShouldDelayAndRetry( can_retry=mock.ANY, next_interval=exponential_backoff_interval(2))), response_sequence ) ])) ] return perform_sequence(seq, eff)
def node_feed_req(lb_id, node_id, response): """ Return (intent, performer) sequence for getting clb node's feed that wrapped with retry intent. :param lb_id: Lodbalancer ID :param node_id: LB node ID :param response: The response returned when getting CLB node feed. It is either string containing feed or Exception object that will be raised when getting the feed :return: (intent, performer) tuple """ if isinstance(response, Exception): def handler(i): raise response else: def handler(i): return response return ( Retry( effect=mock.ANY, should_retry=ShouldDelayAndRetry( can_retry=retry_times(5), next_interval=exponential_backoff_interval(2)) ), nested_sequence([(("gcnf", lb_id, node_id), handler)]) )
def dispatcher(self, operation, resp): return SequenceDispatcher([ (TenantScope(mock.ANY, "tid"), nested_sequence([ ((operation, pset([("lb_id", "server_id")])), lambda i: resp) ])) ])
def _perform_add_event(self, response_sequence): """ Given a sequence of functions that take an intent and returns a response (or raises an exception), perform :func:`add_event` and return the result. """ log = object() eff = add_event(self.event, 'tid', 'ord', log) uid = '00000000-0000-0000-0000-000000000000' svrq = service_request( ServiceType.CLOUD_FEEDS, 'POST', 'autoscale/events', headers={'content-type': ['application/vnd.rackspace.atom+json']}, data=self._get_request('INFO', uid, 'tid'), log=log, success_pred=has_code(201), json_response=False) seq = [ (TenantScope(mock.ANY, 'tid'), nested_sequence([ retry_sequence( Retry(effect=svrq, should_retry=ShouldDelayAndRetry( can_retry=mock.ANY, next_interval=exponential_backoff_interval(2))), response_sequence) ])) ] return perform_sequence(seq, eff)
def test_throttling(self): """ When the throttler function returns a bracketing function, it's used to throttle the request. """ def throttler(stype, method, tid): if (stype == ServiceType.CLOUD_SERVERS and method == 'get' and tid == 1): return bracket bracket = object() svcreq = service_request( ServiceType.CLOUD_SERVERS, 'GET', 'servers').intent response = stub_pure_response({}, 200) seq = SequenceDispatcher([ (_Throttle(bracket=bracket, effect=mock.ANY), nested_sequence([ (Authenticate(authenticator=self.authenticator, tenant_id=1, log=self.log), lambda i: ('token', fake_service_catalog)), (Request(method='GET', url='http://dfw.openstack/servers', headers=headers('token'), log=self.log), lambda i: response), ])), ]) eff = self._concrete(svcreq, throttler=throttler) with seq.consume(): result = sync_perform(seq, eff) self.assertEqual(result, (response[0], {}))
def test_ignore_disappearing_divergent_flag(self): """ When the divergent flag disappears just as we're starting to converge, the group does not get converged and None is returned as its result. This happens when a concurrent convergence iteration is just finishing up. """ eff = self._converge_all_groups(['00_g1']) def get_bound_sequence(tid, gid): # since this GetStat is going to return None, no more effects will # be run. This is the crux of what we're testing. znode = '/groups/divergent/{}_{}'.format(tid, gid) return [ (GetStat(path=znode), noop), (Log('converge-divergent-flag-disappeared', fields={'znode': znode}), noop)] sequence = [ (ReadReference(ref=self.currently_converging), lambda i: pset()), (Log('converge-all-groups', dict(group_infos=[self.group_infos[0]], currently_converging=[])), noop), (ReadReference(ref=self.recently_converged), lambda i: pmap()), (Func(time.time), lambda i: 100), parallel_sequence([ [(BoundFields(mock.ANY, fields={'tenant_id': '00', 'scaling_group_id': 'g1'}), nested_sequence(get_bound_sequence('00', 'g1')))], ]), ] self.assertEqual(perform_sequence(sequence, eff), [None])
def test_throttling(self): """ When the throttler function returns a bracketing function, it's used to throttle the request. """ def throttler(stype, method, tid): if (stype == ServiceType.CLOUD_SERVERS and method == 'get' and tid == 1): return bracket bracket = object() svcreq = service_request(ServiceType.CLOUD_SERVERS, 'GET', 'servers').intent response = stub_pure_response({}, 200) seq = SequenceDispatcher([ (_Throttle(bracket=bracket, effect=mock.ANY), nested_sequence([ (Authenticate(authenticator=self.authenticator, tenant_id=1, log=self.log), lambda i: ('token', fake_service_catalog)), (Request(method='GET', url='http://dfw.openstack/servers', headers=headers('token'), log=self.log), lambda i: response), ])), ]) eff = self._concrete(svcreq, throttler=throttler) with seq.consume(): result = sync_perform(seq, eff) self.assertEqual(result, (response[0], {}))
def _expect_group_converged(self, tenant_id, group_id): """ Return a SequenceDispatcher two-tuple that matches the usual sequence of intents for converging a single group. """ return ( BoundFields(mock.ANY, dict(tenant_id=tenant_id, scaling_group_id=group_id)), nested_sequence([ (GetStat( path='/groups/divergent/{tenant_id}_{group_id}'.format( tenant_id=tenant_id, group_id=group_id)), lambda i: ZNodeStatStub(version=5)), (TenantScope(mock.ANY, tenant_id), nested_sequence([ (('converge', tenant_id, group_id, 5, 3600), lambda i: 'converged {}!'.format(group_id)), ])), ]))
def _log_sequence(self, intents): uid = uuid.uuid4() exp_uid = str(uid) return SequenceDispatcher([ (Func(uuid.uuid4), lambda i: uid), (BoundFields(effect=mock.ANY, fields={'otter_service': 'converger', 'converger_run_id': exp_uid}), nested_sequence(intents)), ])
def test_active_resumed(self): """ Convergence is triggerred on ACTIVE resumed group """ seq = [ (GetScalingGroupInfo(tenant_id="tid", group_id="gid"), const(("group", self.manifest))), (BoundFields(effect=mock.ANY, fields=dict(tenant_id="tid", scaling_group_id="gid")), nested_sequence([(("tg", "tid", "gid"), noop)])) ] self.assertIsNone( perform_sequence(seq, sh.check_and_trigger("tid", "gid")))
def setUp(self): """ mock dependent functions """ self.connect_cass_servers = patch( self, 'otter.metrics.connect_cass_servers') self.client = mock.Mock(spec=['disconnect']) self.client.disconnect.return_value = succeed(None) self.connect_cass_servers.return_value = self.client self.log = mock_log() self.get_all_metrics = patch(self, 'otter.metrics.get_all_metrics', return_value=succeed("metrics")) self.groups = [ {"tenantId": "t1", "groupId": "g1", "launch_config": '{"type": "launch_server"}'}, {"tenantId": "t1", "groupId": "g2", "launch_config": '{"type": "launch_server"}'}, {"tenantId": "t1", "groupId": "g12", "launch_config": '{"type": "launch_stack"}'}, {"tenantId": "t3", "groupId": "g3", "launch_config": '{"type": "launch_stack"}'}, {"tenantId": "t2", "groupId": "g11", "launch_config": '{"type": "launch_server"}'}] self.lc_groups = {"t1": self.groups[:2], "t2": [self.groups[-1]]} self.add_to_cloud_metrics = patch( self, 'otter.metrics.add_to_cloud_metrics', side_effect=intent_func("atcm")) self.config = {'cassandra': 'c', 'identity': identity_config, 'metrics': {'service': 'ms', 'tenant_id': 'tid', 'region': 'IAD', 'ttl': 200, "last_tenant_fpath": "lpath"}, 'region': 'r', 'cloudServersOpenStack': 'nova', 'cloudLoadBalancers': 'clb', 'cloudOrchestration': 'orch', 'rackconnect': 'rc', "non-convergence-tenants": ["ct"]} self.sequence = SequenceDispatcher([ (GetAllValidGroups(), const(self.groups)), (TenantScope(mock.ANY, "tid"), nested_sequence([ (("atcm", 200, "r", "metrics", 2, self.config, self.log, False), noop) ])) ]) self.get_dispatcher = patch(self, "otter.metrics.get_dispatcher", return_value=self.sequence)
def setUp(self): """ mock dependent functions """ self.connect_cass_servers = patch(self, "otter.metrics.connect_cass_servers") self.client = mock.Mock(spec=["disconnect"]) self.client.disconnect.return_value = succeed(None) self.connect_cass_servers.return_value = self.client self.log = mock_log() self.get_all_metrics = patch(self, "otter.metrics.get_all_metrics", return_value=succeed("metrics")) self.groups = [ {"tenantId": "t1", "groupId": "g1", "launch_config": '{"type": "launch_server"}'}, {"tenantId": "t1", "groupId": "g2", "launch_config": '{"type": "launch_server"}'}, {"tenantId": "t1", "groupId": "g12", "launch_config": '{"type": "launch_stack"}'}, {"tenantId": "t3", "groupId": "g3", "launch_config": '{"type": "launch_stack"}'}, {"tenantId": "t2", "groupId": "g11", "launch_config": '{"type": "launch_server"}'}, ] self.lc_groups = {"t1": self.groups[:2], "t2": [self.groups[-1]]} self.add_to_cloud_metrics = patch(self, "otter.metrics.add_to_cloud_metrics", side_effect=intent_func("atcm")) self.config = { "cassandra": "c", "identity": identity_config, "metrics": {"service": "ms", "tenant_id": "tid", "region": "IAD", "ttl": 200, "last_tenant_fpath": "lpath"}, "region": "r", "cloudServersOpenStack": "nova", "cloudLoadBalancers": "clb", "cloudOrchestration": "orch", "rackconnect": "rc", "non-convergence-tenants": ["ct"], } self.sequence = SequenceDispatcher( [ (GetAllValidGroups(), const(self.groups)), ( TenantScope(mock.ANY, "tid"), nested_sequence([(("atcm", 200, "r", "metrics", 2, self.config, self.log, False), noop)]), ), ] ) self.get_dispatcher = patch(self, "otter.metrics.get_dispatcher", return_value=self.sequence)
def setUp(self): """ mock dependent functions """ self.connect_cass_servers = patch( self, 'otter.metrics.connect_cass_servers') self.client = mock.Mock(spec=['disconnect']) self.client.disconnect.return_value = succeed(None) self.connect_cass_servers.return_value = self.client self.get_todays_scaling_groups = patch( self, 'otter.metrics.get_todays_scaling_groups', side_effect=intent_func("gtsg")) self.log = mock_log() self.metrics = [GroupMetrics('t', 'g1', 3, 2, 0), GroupMetrics('t2', 'g1', 4, 4, 1), GroupMetrics('t2', 'g', 100, 20, 0)] self.get_all_metrics = patch(self, 'otter.metrics.get_all_metrics', return_value=succeed(self.metrics)) self.groups = {"t": "t1group", "t2": "2 groups"} self.add_to_cloud_metrics = patch( self, 'otter.metrics.add_to_cloud_metrics', side_effect=intent_func("atcm")) self.config = {'cassandra': 'c', 'identity': identity_config, 'metrics': {'service': 'ms', 'tenant_id': 'tid', 'region': 'IAD', 'ttl': 200, "last_tenant_fpath": "lpath"}, 'region': 'r', 'cloudServersOpenStack': 'nova', 'cloudLoadBalancers': 'clb', 'rackconnect': 'rc', "convergence-tenants": ["ct"]} self.sequence = SequenceDispatcher([ (("gtsg", ["ct"], "lpath"), const(self.groups)), (TenantScope(mock.ANY, "tid"), nested_sequence([ (("atcm", 200, "r", 107, 26, 1, 2, 3, self.log), noop) ])) ]) self.get_dispatcher = patch(self, "otter.metrics.get_dispatcher", return_value=self.sequence)
def get_seq(self, with_cache=True): exec_seq = [ parallel_sequence([ [(self.gsgi, lambda i: self.gsgi_result)], [(("gacd", self.tenant_id, self.group_id, self.now), lambda i: (self.servers, ()))] ]) ] if with_cache: exec_seq.append( (UpdateServersCache( self.tenant_id, self.group_id, self.now, self.cache), noop) ) return [ (Log("begin-convergence", {}), noop), (Func(datetime.utcnow), lambda i: self.now), (MsgWithTime("gather-convergence-data", mock.ANY), nested_sequence(exec_seq)) ]
def test_first_error_extraction(self): """ If the GetScalingGroupInfo effect fails, its exception is raised directly, without the FirstError wrapper. """ # Perform the GetScalingGroupInfo by raising an exception sequence = [ (Log("begin-convergence", {}), noop), (Func(datetime.utcnow), lambda i: self.now), (MsgWithTime("gather-convergence-data", mock.ANY), nested_sequence([ parallel_sequence([ [(self.gsgi, lambda i: raise_(RuntimeError('foo')))], [("anything", noop)] ]) ])) ] # And make sure that exception isn't wrapped in FirstError. e = self.assertRaises( RuntimeError, perform_sequence, sequence, self._invoke(), test_dispatcher()) self.assertEqual(str(e), 'foo')
def setUp(self): """ mock dependent functions """ self.connect_cass_servers = patch( self, 'otter.metrics.connect_cass_servers') self.client = mock.Mock(spec=['disconnect']) self.client.disconnect.return_value = succeed(None) self.connect_cass_servers.return_value = self.client self.log = mock_log() self.get_all_metrics = patch(self, 'otter.metrics.get_all_metrics', return_value=succeed("metrics")) self.groups = [{ "tenantId": "t1", "groupId": "g1", "launch_config": '{"type": "launch_server"}' }, { "tenantId": "t1", "groupId": "g2", "launch_config": '{"type": "launch_server"}' }, { "tenantId": "t1", "groupId": "g12", "launch_config": '{"type": "launch_stack"}' }, { "tenantId": "t3", "groupId": "g3", "launch_config": '{"type": "launch_stack"}' }, { "tenantId": "t2", "groupId": "g11", "launch_config": '{"type": "launch_server"}' }] self.lc_groups = {"t1": self.groups[:2], "t2": [self.groups[-1]]} self.add_to_cloud_metrics = patch(self, 'otter.metrics.add_to_cloud_metrics', side_effect=intent_func("atcm")) self.config = { 'cassandra': 'c', 'identity': identity_config, 'metrics': { 'service': 'ms', 'tenant_id': 'tid', 'region': 'IAD', 'ttl': 200, "last_tenant_fpath": "lpath" }, 'region': 'r', 'cloudServersOpenStack': 'nova', 'cloudLoadBalancers': 'clb', 'cloudOrchestration': 'orch', 'rackconnect': 'rc', "non-convergence-tenants": ["ct"] } self.sequence = SequenceDispatcher([ (GetAllValidGroups(), const(self.groups)), (TenantScope(mock.ANY, "tid"), nested_sequence([(("atcm", 200, "r", "metrics", 2, self.config, self.log, False), noop)])) ]) self.get_dispatcher = patch(self, "otter.metrics.get_dispatcher", return_value=self.sequence)