def test_cage(self): """Test basic DSE functionality.""" cage = congress.dse.d6cage.d6Cage() # so that we exit once test finishes; all other threads are forced # to be daemons cage.daemon = True cage.start() cage.loadModule("TestDriver", helper.data_module_path("test_driver.py")) cage.createservice(name="test1", moduleName="TestDriver", args={'poll_time': 0}) cage.createservice(name="test2", moduleName="TestDriver", args={'poll_time': 0}) test1 = cage.service_object('test1') test2 = cage.service_object('test2') test1.subscribe('test2', 'p', callback=test1.receive_msg) test2.publish('p', 42) helper.pause() # give other threads chance to run # logging.debug("d6cage:: dataPath = {}; inbox = {}".format( # policy.runtime.iterstr(list(cage.dataPath.queue)), # policy.runtime.iterstr(list(cage.inbox.queue)))) # logging.debug("test1:: dataPath = {}; inbox = {}".format( # policy.runtime.iterstr(list(test1.dataPath.queue)), # policy.runtime.iterstr(list(test1.inbox.queue)))) # logging.debug("test2:: dataPath = {}; inbox = {}".format( # policy.runtime.iterstr(list(test2.dataPath.queue)), # policy.runtime.iterstr(list(test2.inbox.queue)))) self.assertTrue(test1.msg.body, 42)
def test_rule_api_model(self): """Test the rule api model. Same as test_multiple except we use the api interface instead of the DSE interface. """ api = self.api cage = self.cage engine = self.engine # Insert formula (which creates neutron services) net_formula = create_networkXnetwork_group('p') LOG.debug("Sending formula: {}".format(str(net_formula))) engine.debug_mode() context = {'policy_id': engine.DEFAULT_THEORY} (id1, rule) = api['rule'].add_item( {'rule': str(net_formula)}, {}, context=context) # Poll neutron = cage.service_object('neutron') neutron2 = cage.service_object('neutron2') neutron.poll() neutron2.poll() helper.pause() # Insert a second formula other_formula = compile.parse1('q(x,y) :- p(x,y)') (id2, rule) = api['rule'].add_item( {'rule': str(other_formula)}, {}, context=context) helper.pause() # give time for messages/creation of services ans1 = ('p("240ff9df-df35-43ae-9df5-27fae87f2492", ' ' "240ff9df-df35-43ae-9df5-27fae87f2492") ') ans2 = ('q("240ff9df-df35-43ae-9df5-27fae87f2492", ' ' "240ff9df-df35-43ae-9df5-27fae87f2492") ') e = helper.db_equal(engine.select('p(x,y)'), ans1) self.assertTrue(e, "Insert rule-api 1") e = helper.db_equal(engine.select('q(x,y)'), ans2) self.assertTrue(e, "Insert rule-api 2") # Get formula ruleobj = api['rule'].get_item(id1, {}, context=context) self.assertTrue(e, net_formula == compile.parse1(ruleobj['rule'])) # Get all formulas ds = api['rule'].get_items({}, context=context)['results'] self.assertEqual(len(ds), 2) ids = set([x['id'] for x in ds]) rules = set([compile.parse1(x['rule']) for x in ds]) self.assertEqual(ids, set([id1, id2])) self.assertEqual(rules, set([net_formula, other_formula])) # Delete formula api['rule'].delete_item(id1, {}, context=context) # Get all formulas ds = api['rule'].get_items({}, context=context)['results'] self.assertEqual(len(ds), 1) ids = sorted([x['id'] for x in ds]) self.assertEqual(ids, sorted([id2]))
def test_policy_subscriptions(self): """Test that policy engine subscriptions adjust to policy changes.""" engine = self.engine api = self.api cage = self.cage # Send formula helper.pause() formula = compile.parse1("p(y) :- neutron:networks(y)") LOG.debug("Sending formula: {}".format(str(formula))) api['rule'].publish('policy-update', [runtime.Event(formula)]) helper.pause() # give time for messages/creation of services # check we have the proper subscriptions self.assertTrue('neutron' in cage.services) neutron = cage.service_object('neutron') self.check_subscriptions(engine, [('neutron', 'networks')]) self.check_subscribers(neutron, [(engine.name, 'networks')])
def test_policy_tables(self): """Test basic DSE functionality with policy engine and the API.""" cage = congress.dse.d6cage.d6Cage() cage.loadModule( "TestDriver", helper.data_module_path("../tests/datasources/test_driver.py")) cage.loadModule("TestPolicy", helper.policy_module_path()) cage.createservice(name="data", moduleName="TestDriver", args=helper.datasource_openstack_args()) # using regular testdriver as API for now cage.createservice(name="api", moduleName="TestDriver", args=helper.datasource_openstack_args()) cage.createservice(name="policy", moduleName="TestPolicy", args={ 'd6cage': cage, 'rootdir': '' }) data = cage.services['data']['object'] api = cage.services['api']['object'] policy = cage.services['policy']['object'] policy.create_policy('data') policy.set_schema('data', compile.Schema({'q': (1, )})) policy.subscribe('api', 'policy-update', callback=policy.receive_policy_update) # simulate API call for insertion of policy statements formula = policy.parse1('p(x) :- data:q(x)') api.publish('policy-update', [runtime.Event(formula)]) helper.retry_check_nonempty_last_policy_change(policy) # simulate data source publishing to q formula = policy.parse1('q(1)') data.publish('q', [runtime.Event(formula)]) helper.retry_check_db_equal(policy, 'data:q(x)', 'data:q(1)') # check that policy did the right thing with data e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy insert') # check that publishing into 'p' does not work formula = policy.parse1('p(3)') data.publish('p', [runtime.Event(formula)]) # can't actually check that the update for p does not arrive # so instead wait a bit and check helper.pause() e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy non-insert')
def setUp(self): """Setup tests that use multiple mock neutron instances.""" super(TestCongress, self).setUp() # create neutron mock and tell cage to use that mock # https://code.google.com/p/pymox/wiki/MoxDocumentation mock_factory = mox.Mox() neutron_mock = mock_factory.CreateMock( neutronclient.v2_0.client.Client) neutron_mock2 = mock_factory.CreateMock( neutronclient.v2_0.client.Client) override = {} override['neutron'] = {'client': neutron_mock, 'poll_time': 0} override['neutron2'] = {'client': neutron_mock2, 'poll_time': 0} override['nova'] = {'poll_time': 0} cage = harness.create(helper.root_path(), self.state_path(), helper.datasource_config_path(), override) engine = cage.service_object('engine') api = {'policy': cage.service_object('api-policy'), 'rule': cage.service_object('api-rule'), 'table': cage.service_object('api-table'), 'row': cage.service_object('api-row'), 'datasource': cage.service_object('api-datasource')} # initialize neutron_mocks network1 = test_neutron.network_response port_response = test_neutron.port_response router_response = test_neutron.router_response sg_group_response = test_neutron.security_group_response neutron_mock.list_networks().InAnyOrder().AndReturn(network1) neutron_mock.list_ports().InAnyOrder().AndReturn(port_response) neutron_mock.list_routers().InAnyOrder().AndReturn(router_response) neutron_mock.list_security_groups().InAnyOrder().AndReturn( sg_group_response) neutron_mock2.list_networks().InAnyOrder().AndReturn(network1) neutron_mock2.list_ports().InAnyOrder().AndReturn(port_response) neutron_mock2.list_routers().InAnyOrder().AndReturn(router_response) neutron_mock2.list_security_groups().InAnyOrder().AndReturn( sg_group_response) mock_factory.ReplayAll() helper.pause() self.cage = cage self.engine = engine self.api = api
def test_communication(self): """Test the module's ability to be loaded into the DSE by checking its ability to communicate on the message bus. """ cage = congress.dse.d6cage.d6Cage() # so that we exit once test finishes; all other threads are forced # to be daemons cage.daemon = True cage.start() # Create modules. # Turn off polling so we don't need to deal with real data. args = helper.datasource_openstack_args() args['poll_time'] = 0 cage.loadModule("NovaDriver", helper.data_module_path("nova_driver.py")) cage.loadModule("PolicyDriver", helper.policy_module_path()) cage.createservice(name="policy", moduleName="PolicyDriver", args={'d6cage': cage, 'rootdir': helper.data_module_path('')}) cage.createservice(name="nova", moduleName="NovaDriver", args=args) # Check that data gets sent from nova to policy as expected nova = cage.service_object('nova') policy = cage.service_object('policy') policy.subscribe('nova', 'server', callback=policy.receive_data) # publishing is slightly convoluted b/c deltas are computed # automatically. (Not just convenient--useful so that DSE # properly handles the initial state problem.) # Need to set nova.state and nova.prior_state and then publish # anything. # publish server(1), server(2), server(3) helper.pause() nova.prior_state = {} nova.state['server'] = set([(1,), (2,), (3,)]) nova.publish('server', None) helper.pause() e = helper.db_equal( policy.select('nova:server(x)'), 'nova:server(1) nova:server(2) nova:server(3)') self.assertTrue(e, 'Nova insertion 1') # publish server(1), server(4), server(5) helper.pause() nova.prior_state['server'] = nova.state['server'] nova.state['server'] = set([(1,), (4,), (5,)]) nova.publish('server', None) helper.pause() e = helper.db_equal( policy.select('nova:server(x)'), 'nova:server(1) nova:server(4) nova:server(5)') self.assertTrue(e, 'Nova insertion 2')
def test_policy(self): """Test basic DSE functionality with policy engine.""" cage = congress.dse.d6cage.d6Cage() # so that we exit once test finishes; all other threads are forced # to be daemons cage.daemon = True cage.start() cage.loadModule("TestDriver", helper.data_module_path("test_driver.py")) cage.loadModule("TestPolicy", helper.policy_module_path()) cage.createservice(name="data", moduleName="TestDriver") cage.createservice(name="policy", moduleName="TestPolicy") data = cage.services['data']['object'] policy = cage.services['policy']['object'] policy.subscribe('data', 'p', callback=policy.receive_msg) data.publish('p', 42) helper.pause() # give other threads chance to run self.assertTrue(policy.msg.body, 42)
def test_policy_tables(self): """Test basic DSE functionality with policy engine and the API.""" cage = congress.dse.d6cage.d6Cage() cage.loadModule("TestDriver", helper.data_module_path( "../tests/datasources/test_driver.py")) cage.loadModule("TestPolicy", helper.policy_module_path()) cage.createservice(name="data", moduleName="TestDriver", args=helper.datasource_openstack_args()) # using regular testdriver as API for now cage.createservice(name="api", moduleName="TestDriver", args=helper.datasource_openstack_args()) cage.createservice(name="policy", moduleName="TestPolicy", args={'d6cage': cage, 'rootdir': '', 'log_actions_only': True}) data = cage.services['data']['object'] api = cage.services['api']['object'] policy = cage.services['policy']['object'] policy.create_policy('data') policy.set_schema('data', compile.Schema({'q': (1,)})) policy.subscribe('api', 'policy-update', callback=policy.receive_policy_update) # simulate API call for insertion of policy statements formula = policy.parse1('p(x) :- data:q(x)') api.publish('policy-update', [compile.Event(formula)]) helper.retry_check_nonempty_last_policy_change(policy) # simulate data source publishing to q formula = policy.parse1('q(1)') data.publish('q', [compile.Event(formula)]) helper.retry_check_db_equal(policy, 'data:q(x)', 'data:q(1)') # check that policy did the right thing with data e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy insert') # check that publishing into 'p' does not work formula = policy.parse1('p(3)') data.publish('p', [compile.Event(formula)]) # can't actually check that the update for p does not arrive # so instead wait a bit and check helper.pause() e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy non-insert')
def test_policy_initialization(self): """Test subscribing before polling. The common case.""" cage = self.info['cage'] policy = cage.service_object('policy') neutron = cage.service_object('neutron') datalog1 = self.info['datalog1'] fake_networks = self.info['fake_networks'] # add garbage to policy for formula in fake_networks: policy.insert(formula) # subscribe policy.subscribe('neutron', 'networks', callback=policy.receive_data) helper.pause() # so that subscription messages are processed # poll 1 neutron.poll() helper.pause() # so that data updates are processed e = helper.db_equal(policy.select('p(x)'), datalog1) self.assertTrue(e, 'Neutron insertion 1')
def test_policy_data(self): """Test policy properly inserts data and processes it normally.""" cage = congress.dse.d6cage.d6Cage() # so that we exit once test finishes; all other threads are forced # to be daemons cage.daemon = True cage.start() cage.loadModule("TestDriver", helper.data_module_path("test_driver.py")) cage.loadModule("TestPolicy", helper.policy_module_path()) cage.createservice(name="data", moduleName="TestDriver") cage.createservice(name="policy", moduleName="TestPolicy") data = cage.services['data']['object'] policy = cage.services['policy']['object'] policy.subscribe('data', 'p', callback=policy.receive_data) formula = compile.parse1('p(1)') # sending a single Insert. (Default for Event is Insert.) data.publish('p', [runtime.Event(formula)]) helper.pause() # give other threads chance to run e = helper.db_equal(policy.select('data:p(x)'), 'data:p(1)') self.assertTrue(e, 'Single insert')
def test_multiple(self): """Test polling and publishing of multiple neutron instances.""" api = self.api cage = self.cage engine = self.engine # Send formula formula = create_networkXnetwork_group('p') api['rule'].publish('policy-update', [runtime.Event(formula)]) helper.pause() # give time for messages/creation of services # poll datasources neutron = cage.service_object('neutron') neutron2 = cage.service_object('neutron2') neutron.poll() neutron2.poll() helper.pause() # check answer ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492", ' ' "240ff9df-df35-43ae-9df5-27fae87f2492") ') e = helper.db_equal(engine.select('p(x,y)'), ans) self.assertTrue(e, "Multiple neutron datasources")
def test_poll_subscribe(self): """Test polling before subscribing.""" cage = self.info['cage'] policy = cage.service_object('policy') neutron = cage.service_object('neutron') datalog1 = self.info['datalog1'] datalog2 = self.info['datalog2'] fake_networks = self.info['fake_networks'] # add garbage to policy for formula in fake_networks: policy.insert(formula) # poll 1 and then subscribe; should still see first result neutron.poll() helper.pause() # so that data is sent policy.subscribe('neutron', 'networks', callback=policy.receive_data) helper.pause() # so that data updates are processed e = helper.db_equal(policy.select('p(x)'), datalog1) self.assertTrue(e, 'Neutron insertion 1') # poll 2 neutron.poll() helper.pause() e = helper.db_equal(policy.select('p(x)'), datalog2) self.assertTrue(e, 'Neutron insertion 2')
def test_double_poll_subscribe(self): """Test double polling before subscribing.""" cage = self.info['cage'] policy = cage.service_object('policy') neutron = cage.service_object('neutron') datalog2 = self.info['datalog2'] # poll twice and then subscribe: should see 2nd result neutron.poll() helper.pause() neutron.poll() helper.pause() policy.subscribe('neutron', 'networks', callback=policy.receive_data) helper.pause() # so that messages are processed e = helper.db_equal(policy.select('p(x)'), datalog2) self.assertTrue(e, 'Neutron insertion 2')
def test_neutron(self): """Test polling and publishing of neutron updates.""" engine = self.engine api = self.api cage = self.cage helper.pause() # Send formula formula = test_neutron.create_network_group('p') LOG.debug("Sending formula: {}".format(str(formula))) api['rule'].publish('policy-update', [runtime.Event(formula)]) helper.pause() # give time for messages/creation of services LOG.debug("All services: " + str(cage.services.keys())) neutron = cage.service_object('neutron') neutron.poll() helper.pause() ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492") ') e = helper.db_equal(engine.select('p(x)'), ans) self.assertTrue(e, "Neutron datasource")
def test_policy_tables(self): """Test basic DSE functionality with policy engine and the API.""" cage = congress.dse.d6cage.d6Cage() # so that we exit once test finishes; all other threads are forced # to be daemons cage.daemon = True cage.start() cage.loadModule("TestDriver", helper.data_module_path("test_driver.py")) cage.loadModule("TestPolicy", helper.policy_module_path()) cage.createservice(name="data", moduleName="TestDriver", args=helper.datasource_openstack_args()) # using regular testdriver as API for now cage.createservice(name="api", moduleName="TestDriver", args=helper.datasource_openstack_args()) cage.createservice(name="policy", moduleName="TestPolicy", args={'d6cage': cage, 'rootdir': ''}) data = cage.services['data']['object'] api = cage.services['api']['object'] policy = cage.services['policy']['object'] policy.subscribe('api', 'policy-update', callback=policy.receive_policy_update) # simulate API call for insertion of policy statements formula = compile.parse1('p(x) :- data:q(x)') api.publish('policy-update', [runtime.Event(formula)]) helper.pause() # simulate data source publishing to q formula = compile.parse1('q(1)') data.publish('q', [runtime.Event(formula)]) helper.pause() # give other threads chance to run # check that policy did the right thing with data e = helper.db_equal(policy.select('data:q(x)'), 'data:q(1)') self.assertTrue(e, 'Policy insert 1') e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy insert 2') #check that publishing into 'p' does not work formula = compile.parse1('p(3)') data.publish('p', [runtime.Event(formula)]) helper.pause() e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy noninsert')
def test_subscribe_poll(self): """Test subscribing before polling. The common case.""" cage = self.info['cage'] policy = cage.service_object('policy') neutron = cage.service_object('neutron') datalog1 = self.info['datalog1'] datalog2 = self.info['datalog2'] # subscribe policy.subscribe('neutron', 'networks', callback=policy.receive_data) helper.pause() # so that subscription messages are processed # poll 1 neutron.poll() helper.pause(3) # so that data updates are processed e = helper.db_equal(policy.select('p(x)'), datalog1) self.assertTrue(e, 'Neutron insertion 1: ' + str(policy.content())) # poll 2 neutron.poll() helper.pause() e = helper.db_equal(policy.select('p(x)'), datalog2) self.assertTrue(e, 'Neutron insertion 2')
def test_policy_recovery(self): """Test policy crashing and recovering (sort of).""" cage = self.info['cage'] policy = cage.service_object('policy') neutron = cage.service_object('neutron') datalog1 = self.info['datalog1'] # get initial data policy.subscribe('neutron', 'networks', callback=policy.receive_data) helper.pause() neutron.poll() helper.pause() e = helper.db_equal(policy.select('p(x)'), datalog1) self.assertTrue(e, 'Neutron insertion 1') # clear out policy's neutron:networks data (to simulate crashing) policy.initialize(['neutron:networks'], []) # subscribe again (without unsubscribing) policy.subscribe('neutron', 'networks', callback=policy.receive_data) helper.pause() # should get same data e = helper.db_equal(policy.select('p(x)'), datalog1) self.assertTrue(e, 'Neutron insertion 1')