Migrate harness to DSE2
Pull API models off the bus and give them an argument to the bus so they can still RPC. Necessary since API models are part of 1 service on the bus in DSE2 (and never needed to be on the bus for DSE1). Introduce new creation process that bootstraps system using DSE2. Change-Id: Ic91e2f7918970766fad8e2aff4a0ef88fb5777e2
This commit is contained in:
parent
d7f122db21
commit
2354bf4c58
@ -29,12 +29,6 @@ def d6service(name, keys, inbox, datapath, args):
|
|||||||
|
|
||||||
class ActionsModel(base.APIModel):
|
class ActionsModel(base.APIModel):
|
||||||
"""Model for handling API requests about Actions."""
|
"""Model for handling API requests about Actions."""
|
||||||
def __init__(self, name, keys='', inbox=None, dataPath=None,
|
|
||||||
policy_engine=None, datasource_mgr=None):
|
|
||||||
super(ActionsModel, self).__init__(name, keys, inbox=inbox,
|
|
||||||
dataPath=dataPath,
|
|
||||||
policy_engine=policy_engine,
|
|
||||||
datasource_mgr=datasource_mgr)
|
|
||||||
|
|
||||||
def get_items(self, params, context=None):
|
def get_items(self, params, context=None):
|
||||||
"""Retrieve items from this model.
|
"""Retrieve items from this model.
|
||||||
|
@ -24,9 +24,11 @@ import webob
|
|||||||
import webob.dec
|
import webob.dec
|
||||||
|
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
|
from congress.dse2 import data_service
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
API_SERVICE_NAME = '__api'
|
||||||
|
|
||||||
|
|
||||||
class ApiApplication(object):
|
class ApiApplication(object):
|
||||||
@ -64,7 +66,7 @@ class ApiApplication(object):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class ResourceManager(object):
|
class ResourceManager(data_service.DataService):
|
||||||
"""A container for REST API resources.
|
"""A container for REST API resources.
|
||||||
|
|
||||||
This container is meant to be called from one or more wsgi servers/ports.
|
This container is meant to be called from one or more wsgi servers/ports.
|
||||||
@ -75,6 +77,7 @@ class ResourceManager(object):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.handlers = []
|
self.handlers = []
|
||||||
|
super(ResourceManager, self).__init__(API_SERVICE_NAME)
|
||||||
|
|
||||||
def register_handler(self, handler, search_index=None):
|
def register_handler(self, handler, search_index=None):
|
||||||
"""Register a new resource handler.
|
"""Register a new resource handler.
|
||||||
|
@ -19,27 +19,24 @@ from __future__ import absolute_import
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
# Use new deepsix when appropriate
|
|
||||||
if getattr(cfg.CONF, 'distributed_architecture', None):
|
|
||||||
from congress.dse2 import deepsix2 as deepsix
|
|
||||||
else:
|
|
||||||
from congress.dse import deepsix
|
|
||||||
from congress import exception
|
from congress import exception
|
||||||
|
|
||||||
|
|
||||||
class APIModel(deepsix.deepSix):
|
class APIModel(object):
|
||||||
"""Base Class for handling API requests."""
|
"""Base Class for handling API requests."""
|
||||||
|
|
||||||
def __init__(self, name, keys='', inbox=None, dataPath=None,
|
def __init__(self, name, keys='', inbox=None, dataPath=None,
|
||||||
policy_engine=None, datasource_mgr=None):
|
policy_engine=None, datasource_mgr=None, bus=None):
|
||||||
super(APIModel, self).__init__(name, keys, inbox=inbox,
|
# super(APIModel, self).__init__(name, keys, inbox=inbox,
|
||||||
dataPath=dataPath)
|
# dataPath=dataPath)
|
||||||
self.engine = policy_engine
|
self.engine = policy_engine
|
||||||
self.datasource_mgr = datasource_mgr
|
self.datasource_mgr = datasource_mgr
|
||||||
|
self.bus = bus
|
||||||
|
self.name = name
|
||||||
|
|
||||||
def invoke_rpc(self, caller, name, kwargs):
|
def invoke_rpc(self, caller, name, kwargs):
|
||||||
if getattr(cfg.CONF, 'distributed_architecture', None):
|
if getattr(cfg.CONF, 'distributed_architecture', None):
|
||||||
return self.rpc(caller, name, kwargs)
|
return self.bus.rpc(caller, name, kwargs)
|
||||||
else:
|
else:
|
||||||
func = getattr(caller, name, None)
|
func = getattr(caller, name, None)
|
||||||
if func:
|
if func:
|
||||||
|
@ -39,11 +39,13 @@ def d6service(name, keys, inbox, datapath, args):
|
|||||||
class DatasourceModel(base.APIModel):
|
class DatasourceModel(base.APIModel):
|
||||||
"""Model for handling API requests about Datasources."""
|
"""Model for handling API requests about Datasources."""
|
||||||
def __init__(self, name, keys='', inbox=None, dataPath=None,
|
def __init__(self, name, keys='', inbox=None, dataPath=None,
|
||||||
policy_engine=None, datasource_mgr=None, synchronizer=None):
|
policy_engine=None, datasource_mgr=None, bus=None,
|
||||||
|
synchronizer=None):
|
||||||
super(DatasourceModel, self).__init__(name, keys, inbox=inbox,
|
super(DatasourceModel, self).__init__(name, keys, inbox=inbox,
|
||||||
dataPath=dataPath,
|
dataPath=dataPath,
|
||||||
policy_engine=policy_engine,
|
policy_engine=policy_engine,
|
||||||
datasource_mgr=datasource_mgr)
|
datasource_mgr=datasource_mgr,
|
||||||
|
bus=bus)
|
||||||
self.synchronizer = synchronizer
|
self.synchronizer = synchronizer
|
||||||
self.dist_arch = getattr(cfg.CONF, 'distributed_architecture', False)
|
self.dist_arch = getattr(cfg.CONF, 'distributed_architecture', False)
|
||||||
|
|
||||||
@ -60,7 +62,7 @@ class DatasourceModel(base.APIModel):
|
|||||||
dict will also be rendered for the user.
|
dict will also be rendered for the user.
|
||||||
"""
|
"""
|
||||||
if self.dist_arch:
|
if self.dist_arch:
|
||||||
self.datasource_mgr = self
|
self.datasource_mgr = self.bus
|
||||||
|
|
||||||
results = self.datasource_mgr.get_datasources(filter_secret=True)
|
results = self.datasource_mgr.get_datasources(filter_secret=True)
|
||||||
|
|
||||||
@ -91,7 +93,7 @@ class DatasourceModel(base.APIModel):
|
|||||||
obj = None
|
obj = None
|
||||||
try:
|
try:
|
||||||
if self.dist_arch:
|
if self.dist_arch:
|
||||||
obj = self.add_datasource(item=item)
|
obj = self.bus.add_datasource(item=item)
|
||||||
# Get the schema for the datasource using service_id
|
# Get the schema for the datasource using service_id
|
||||||
schema = self.invoke_rpc(obj['name'], 'get_datasource_schema',
|
schema = self.invoke_rpc(obj['name'], 'get_datasource_schema',
|
||||||
{'source_id': obj['name']})
|
{'source_id': obj['name']})
|
||||||
@ -117,11 +119,11 @@ class DatasourceModel(base.APIModel):
|
|||||||
ds_id = context.get('ds_id')
|
ds_id = context.get('ds_id')
|
||||||
try:
|
try:
|
||||||
if self.dist_arch:
|
if self.dist_arch:
|
||||||
datasource = self.get_datasource(ds_id)
|
datasource = self.bus.get_datasource(ds_id)
|
||||||
args = {'name': datasource['name'],
|
args = {'name': datasource['name'],
|
||||||
'disallow_dangling_refs': True}
|
'disallow_dangling_refs': True}
|
||||||
self.invoke_rpc(self.engine, 'delete_policy', args)
|
self.invoke_rpc(self.engine, 'delete_policy', args)
|
||||||
self.delete_datasource(datasource)
|
self.bus.delete_datasource(datasource)
|
||||||
else:
|
else:
|
||||||
self.datasource_mgr.delete_datasource(ds_id)
|
self.datasource_mgr.delete_datasource(ds_id)
|
||||||
except (exception.DatasourceNotFound,
|
except (exception.DatasourceNotFound,
|
||||||
|
@ -34,12 +34,6 @@ def d6service(name, keys, inbox, datapath, args):
|
|||||||
|
|
||||||
class RowModel(base.APIModel):
|
class RowModel(base.APIModel):
|
||||||
"""Model for handling API requests about Rows."""
|
"""Model for handling API requests about Rows."""
|
||||||
def __init__(self, name, keys='', inbox=None, dataPath=None,
|
|
||||||
policy_engine=None, datasource_mgr=None):
|
|
||||||
super(RowModel, self).__init__(name, keys, inbox=inbox,
|
|
||||||
dataPath=dataPath,
|
|
||||||
policy_engine=policy_engine,
|
|
||||||
datasource_mgr=datasource_mgr)
|
|
||||||
|
|
||||||
# TODO(thinrichs): No rows have IDs right now. Maybe eventually
|
# TODO(thinrichs): No rows have IDs right now. Maybe eventually
|
||||||
# could make ID the hash of the row, but then might as well
|
# could make ID the hash of the row, but then might as well
|
||||||
@ -76,7 +70,6 @@ class RowModel(base.APIModel):
|
|||||||
# Get the caller, it should be either policy or datasource
|
# Get the caller, it should be either policy or datasource
|
||||||
caller, source_id = api_utils.get_id_from_context(
|
caller, source_id = api_utils.get_id_from_context(
|
||||||
context, self.datasource_mgr, self.engine)
|
context, self.datasource_mgr, self.engine)
|
||||||
|
|
||||||
table_id = context['table_id']
|
table_id = context['table_id']
|
||||||
try:
|
try:
|
||||||
args = {'table_id': table_id, 'source_id': source_id,
|
args = {'table_id': table_id, 'source_id': source_id,
|
||||||
|
@ -33,11 +33,6 @@ def d6service(name, keys, inbox, datapath, args):
|
|||||||
|
|
||||||
class SchemaModel(base.APIModel):
|
class SchemaModel(base.APIModel):
|
||||||
"""Model for handling API requests about Schemas."""
|
"""Model for handling API requests about Schemas."""
|
||||||
def __init__(self, name, keys, inbox=None, dataPath=None,
|
|
||||||
datasource_mgr=None):
|
|
||||||
super(SchemaModel, self).__init__(name, keys, inbox=inbox,
|
|
||||||
dataPath=dataPath,
|
|
||||||
datasource_mgr=datasource_mgr)
|
|
||||||
|
|
||||||
def get_item(self, id_, params, context=None):
|
def get_item(self, id_, params, context=None):
|
||||||
"""Retrieve item with id id_ from model.
|
"""Retrieve item with id id_ from model.
|
||||||
|
@ -34,12 +34,6 @@ def d6service(name, keys, inbox, datapath, args):
|
|||||||
|
|
||||||
class StatusModel(base.APIModel):
|
class StatusModel(base.APIModel):
|
||||||
"""Model for handling API requests about Statuses."""
|
"""Model for handling API requests about Statuses."""
|
||||||
def __init__(self, name, keys='', inbox=None, dataPath=None,
|
|
||||||
policy_engine=None, datasource_mgr=None):
|
|
||||||
super(StatusModel, self).__init__(name, keys, inbox=inbox,
|
|
||||||
dataPath=dataPath,
|
|
||||||
policy_engine=policy_engine,
|
|
||||||
datasource_mgr=datasource_mgr)
|
|
||||||
|
|
||||||
def get_item(self, id_, params, context=None):
|
def get_item(self, id_, params, context=None):
|
||||||
"""Retrieve item with id id_ from model.
|
"""Retrieve item with id id_ from model.
|
||||||
|
@ -20,8 +20,8 @@ from __future__ import absolute_import
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from congress.api import api_utils
|
from congress.api import api_utils
|
||||||
|
from congress.api import base
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress.dse import deepsix
|
|
||||||
from congress import exception
|
from congress import exception
|
||||||
|
|
||||||
|
|
||||||
@ -33,12 +33,12 @@ def d6service(name, keys, inbox, datapath, args):
|
|||||||
dataPath=datapath, **args)
|
dataPath=datapath, **args)
|
||||||
|
|
||||||
|
|
||||||
class DatasourceDriverModel(deepsix.deepSix):
|
class DatasourceDriverModel(base.APIModel):
|
||||||
"""Model for handling API requests about DatasourceDriver."""
|
"""Model for handling API requests about DatasourceDriver."""
|
||||||
def __init__(self, name, keys, inbox=None, dataPath=None,
|
def __init__(self, name, keys='', inbox=None, dataPath=None,
|
||||||
datasource_mgr=None):
|
datasource_mgr=None, bus=None):
|
||||||
super(DatasourceDriverModel, self).__init__(name, keys, inbox=inbox,
|
super(DatasourceDriverModel, self).__init__(name, keys, inbox=inbox,
|
||||||
dataPath=dataPath)
|
dataPath=dataPath, bus=bus)
|
||||||
self.datasource_mgr = datasource_mgr
|
self.datasource_mgr = datasource_mgr
|
||||||
|
|
||||||
def rpc(self, caller, name, *args, **kwargs):
|
def rpc(self, caller, name, *args, **kwargs):
|
||||||
|
@ -33,12 +33,6 @@ def d6service(name, keys, inbox, datapath, args):
|
|||||||
|
|
||||||
class TableModel(base.APIModel):
|
class TableModel(base.APIModel):
|
||||||
"""Model for handling API requests about Tables."""
|
"""Model for handling API requests about Tables."""
|
||||||
def __init__(self, name, keys='', inbox=None, dataPath=None,
|
|
||||||
policy_engine=None, datasource_mgr=None):
|
|
||||||
super(TableModel, self).__init__(name, keys, inbox=inbox,
|
|
||||||
dataPath=dataPath,
|
|
||||||
policy_engine=policy_engine,
|
|
||||||
datasource_mgr=datasource_mgr)
|
|
||||||
|
|
||||||
def get_item(self, id_, params, context=None):
|
def get_item(self, id_, params, context=None):
|
||||||
"""Retrieve item with id id_ from model.
|
"""Retrieve item with id id_ from model.
|
||||||
|
@ -26,13 +26,28 @@ import sys
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from congress.api import action_model
|
||||||
|
from congress.api import application
|
||||||
|
from congress.api import datasource_model
|
||||||
|
from congress.api import policy_model
|
||||||
|
from congress.api import router
|
||||||
|
from congress.api import row_model
|
||||||
|
from congress.api import rule_model
|
||||||
|
from congress.api import schema_model
|
||||||
|
from congress.api import status_model
|
||||||
|
from congress.api.system import driver_model
|
||||||
|
from congress.api import table_model
|
||||||
from congress.datalog import base
|
from congress.datalog import base
|
||||||
from congress.dse import d6cage
|
from congress.dse import d6cage
|
||||||
|
from congress.dse2 import dse_node
|
||||||
from congress import exception
|
from congress import exception
|
||||||
from congress.managers import datasource as datasource_manager
|
from congress.managers import datasource as datasource_manager
|
||||||
|
from congress.policy_engines.agnostic import Dse2Runtime
|
||||||
|
from congress.tests import helper
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
ENGINE_SERVICE_NAME = 'engine'
|
||||||
|
|
||||||
|
|
||||||
def create(rootdir, config_override=None):
|
def create(rootdir, config_override=None):
|
||||||
@ -252,6 +267,183 @@ def create(rootdir, config_override=None):
|
|||||||
return cage
|
return cage
|
||||||
|
|
||||||
|
|
||||||
|
def create2(config_override=None, node=None):
|
||||||
|
"""Get Congress up and running when src is installed in rootdir.
|
||||||
|
|
||||||
|
i.e. ROOTDIR=/path/to/congress/congress.
|
||||||
|
CONFIG_OVERRIDE is a dictionary of dictionaries with configuration
|
||||||
|
values that overrides those provided in CONFIG_FILE. The top-level
|
||||||
|
dictionary has keys for the CONFIG_FILE sections, and the second-level
|
||||||
|
dictionaries store values for that section.
|
||||||
|
|
||||||
|
:param node is a DseNode
|
||||||
|
"""
|
||||||
|
LOG.debug("Starting Congress with config_override=%s",
|
||||||
|
config_override)
|
||||||
|
|
||||||
|
# create services
|
||||||
|
services = {}
|
||||||
|
services[ENGINE_SERVICE_NAME] = create_policy_engine()
|
||||||
|
services['api'], services['api_service'] = create_api(
|
||||||
|
services[ENGINE_SERVICE_NAME])
|
||||||
|
services['datasources'] = create_datasources(services[ENGINE_SERVICE_NAME])
|
||||||
|
|
||||||
|
# create message bus and attach services
|
||||||
|
if node:
|
||||||
|
bus = node
|
||||||
|
else:
|
||||||
|
messaging_config = helper.generate_messaging_config()
|
||||||
|
bus = dse_node.DseNode(messaging_config, "root", [])
|
||||||
|
bus.config = config_override or {}
|
||||||
|
bus.register_service(services[ENGINE_SERVICE_NAME])
|
||||||
|
for ds in services['datasources']:
|
||||||
|
bus.register_service(ds)
|
||||||
|
bus.register_service(services['api_service'])
|
||||||
|
|
||||||
|
# TODO(dse2): Need this?
|
||||||
|
# initialize_policy_engine(services[ENGINE_SERVICE_NAME], services['api'])
|
||||||
|
|
||||||
|
# TODO(dse2): Figure out what to do about the synchronizer
|
||||||
|
# # Start datasource synchronizer after explicitly starting the
|
||||||
|
# # datasources, because the explicit call to create a datasource
|
||||||
|
# # will crash if the synchronizer creates the datasource first.
|
||||||
|
# synchronizer_path = os.path.join(src_path, "synchronizer.py")
|
||||||
|
# LOG.info("main::start() synchronizer: %s", synchronizer_path)
|
||||||
|
# cage.loadModule("Synchronizer", synchronizer_path)
|
||||||
|
# cage.createservice(
|
||||||
|
# name="synchronizer",
|
||||||
|
# moduleName="Synchronizer",
|
||||||
|
# description="DB synchronizer instance",
|
||||||
|
# args={'poll_time': cfg.CONF.datasource_sync_period})
|
||||||
|
# synchronizer = cage.service_object('synchronizer')
|
||||||
|
# engine.set_synchronizer(synchronizer)
|
||||||
|
|
||||||
|
return services
|
||||||
|
|
||||||
|
|
||||||
|
def create_api(policy_engine):
|
||||||
|
"""Return service that encapsulates api logic for DSE2."""
|
||||||
|
# ResourceManager inherits from DataService
|
||||||
|
api_resource_mgr = application.ResourceManager()
|
||||||
|
models = create_api_models(policy_engine, api_resource_mgr)
|
||||||
|
router.APIRouterV1(api_resource_mgr, models)
|
||||||
|
return models, api_resource_mgr
|
||||||
|
|
||||||
|
|
||||||
|
def create_api_models(policy_engine, bus):
|
||||||
|
"""Create all the API models and return as a dictionary for DSE2."""
|
||||||
|
policy_engine = policy_engine.name
|
||||||
|
datasource_mgr = None
|
||||||
|
res = {}
|
||||||
|
res['api-policy'] = policy_model.PolicyModel(
|
||||||
|
'api-policy', policy_engine=policy_engine, bus=bus)
|
||||||
|
res['api-rule'] = rule_model.RuleModel(
|
||||||
|
'api-rule', policy_engine=policy_engine, bus=bus)
|
||||||
|
res['api-row'] = row_model.RowModel(
|
||||||
|
'api-row', policy_engine=policy_engine,
|
||||||
|
datasource_mgr=datasource_mgr, bus=bus)
|
||||||
|
# TODO(dse2): migrate this to DSE2 and then reenable
|
||||||
|
res['api-datasource'] = datasource_model.DatasourceModel(
|
||||||
|
'api-datasource', policy_engine=policy_engine, bus=bus)
|
||||||
|
res['api-schema'] = schema_model.SchemaModel(
|
||||||
|
'api-schema', datasource_mgr=datasource_mgr, bus=bus)
|
||||||
|
res['api-table'] = table_model.TableModel(
|
||||||
|
'api-table', policy_engine=policy_engine,
|
||||||
|
datasource_mgr=datasource_mgr, bus=bus)
|
||||||
|
res['api-status'] = status_model.StatusModel(
|
||||||
|
'api-status', policy_engine=policy_engine,
|
||||||
|
datasource_mgr=datasource_mgr, bus=bus)
|
||||||
|
res['api-action'] = action_model.ActionsModel(
|
||||||
|
'api-action', policy_engine=policy_engine,
|
||||||
|
datasource_mgr=datasource_mgr, bus=bus)
|
||||||
|
res['api-system'] = driver_model.DatasourceDriverModel(
|
||||||
|
'api-system', datasource_mgr=datasource_mgr, bus=bus)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def create_policy_engine():
|
||||||
|
"""Create policy engine and initialize it using the api models."""
|
||||||
|
engine = Dse2Runtime(ENGINE_SERVICE_NAME)
|
||||||
|
engine.initialize_table_subscriptions()
|
||||||
|
engine.debug_mode() # should take this out for production
|
||||||
|
return engine
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_policy_engine(engine, api):
|
||||||
|
"""Initialize the policy engine using the API."""
|
||||||
|
|
||||||
|
# Load policies from database
|
||||||
|
engine.persistent_load_policies()
|
||||||
|
|
||||||
|
# TODO(dse2): check that we can move this here, now that we
|
||||||
|
# have flexible schema handling. If so, remove following
|
||||||
|
# comment.
|
||||||
|
# Insert rules. Needs to be done after datasources are loaded
|
||||||
|
# so that we can compile away column references at read time.
|
||||||
|
# If datasources loaded after this, we don't have schemas.
|
||||||
|
engine.persistent_load_rules()
|
||||||
|
|
||||||
|
# if this is the first time we are running Congress, need
|
||||||
|
# to create the default theories (which cannot be deleted)
|
||||||
|
api_policy = api['api-policy']
|
||||||
|
|
||||||
|
engine.DEFAULT_THEORY = 'classification'
|
||||||
|
engine.builtin_policy_names.add(engine.DEFAULT_THEORY)
|
||||||
|
try:
|
||||||
|
api_policy.add_item({'name': engine.DEFAULT_THEORY,
|
||||||
|
'description': 'default policy'}, {})
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
engine.ACTION_THEORY = 'action'
|
||||||
|
engine.builtin_policy_names.add(engine.ACTION_THEORY)
|
||||||
|
try:
|
||||||
|
api_policy.add_item({'kind': base.ACTION_POLICY_TYPE,
|
||||||
|
'name': engine.ACTION_THEORY,
|
||||||
|
'description': 'default action policy'},
|
||||||
|
{})
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# TODO(dse2): delete this subscription and the associated tests.
|
||||||
|
# Don't want 2 paths for updating policy.
|
||||||
|
engine.subscribe('api-rule', 'policy-update',
|
||||||
|
callback=engine.receive_policy_update)
|
||||||
|
|
||||||
|
|
||||||
|
def create_datasources(engine):
|
||||||
|
"""Create datasource services, modify engine, and return datasources."""
|
||||||
|
# TODO(dse2): port this code to DSE2. There were never any tests for
|
||||||
|
# this code, so write those too. In particular, should be able to
|
||||||
|
# remove the datasourceManager entirely.
|
||||||
|
datasource_mgr = datasource_manager.DataSourceManager()
|
||||||
|
datasources = []
|
||||||
|
for datasource in datasource_mgr.get_datasources():
|
||||||
|
if not datasource['enabled']:
|
||||||
|
LOG.info("module %s not enabled, skip loading", datasource['name'])
|
||||||
|
continue
|
||||||
|
driver_info = datasource_mgr.get_driver_info(datasource['driver'])
|
||||||
|
engine.create_policy(datasource['name'],
|
||||||
|
kind=base.DATASOURCE_POLICY_TYPE)
|
||||||
|
try:
|
||||||
|
ds = datasource_mgr.createservice(
|
||||||
|
name=datasource['name'],
|
||||||
|
moduleName=driver_info['module'],
|
||||||
|
args=datasource['config'],
|
||||||
|
module_driver=True,
|
||||||
|
type_='datasource_driver',
|
||||||
|
id_=datasource['id'])
|
||||||
|
datasources.append(ds)
|
||||||
|
except datasource_mgr.DataServiceError:
|
||||||
|
# FIXME(arosen): If createservice raises congress-server
|
||||||
|
# dies here. So we catch this exception so the server does
|
||||||
|
# not die. We need to refactor the dse code so it just
|
||||||
|
# keeps retrying the driver gracefully...
|
||||||
|
continue
|
||||||
|
engine.set_schema(ds.name, ds.get_schema())
|
||||||
|
return datasources
|
||||||
|
|
||||||
|
|
||||||
def load_data_service(service_name, config, cage, rootdir, id_):
|
def load_data_service(service_name, config, cage, rootdir, id_):
|
||||||
"""Load service.
|
"""Load service.
|
||||||
|
|
||||||
|
@ -492,7 +492,7 @@ class Runtime (object):
|
|||||||
def initialize_datasource(self, name, schema):
|
def initialize_datasource(self, name, schema):
|
||||||
"""Initializes datasource by creating policy and setting schema. """
|
"""Initializes datasource by creating policy and setting schema. """
|
||||||
try:
|
try:
|
||||||
self.create_policy(name, kind=base.DATABASE_POLICY_TYPE)
|
self.create_policy(name, kind=base.DATASOURCE_POLICY_TYPE)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise exception.DatasourceNameInUse(value=name)
|
raise exception.DatasourceNameInUse(value=name)
|
||||||
try:
|
try:
|
||||||
|
@ -55,12 +55,16 @@ def congress_app_factory(global_conf, **local_conf):
|
|||||||
# replated with new API model creation method. If All API models can
|
# replated with new API model creation method. If All API models can
|
||||||
# be generated without any argument, we don't need to make dict here
|
# be generated without any argument, we don't need to make dict here
|
||||||
# and API process instantiate all API model in APIRouterV1().
|
# and API process instantiate all API model in APIRouterV1().
|
||||||
cage = harness.create(root_path, data_path)
|
if getattr(cfg.CONF, "distributed_architecture", False):
|
||||||
api_process_dict = dict([[name, service_obj['object']]
|
services = harness.create2(root_path, data_path)
|
||||||
for name, service_obj
|
return application.ApiApplication(services['api_service'])
|
||||||
in cage.getservices().items()
|
else:
|
||||||
if 'object' in service_obj])
|
cage = harness.create(root_path, data_path)
|
||||||
|
api_process_dict = dict([[name, service_obj['object']]
|
||||||
|
for name, service_obj
|
||||||
|
in cage.getservices().items()
|
||||||
|
if 'object' in service_obj])
|
||||||
|
|
||||||
api_resource_mgr = application.ResourceManager()
|
api_resource_mgr = application.ResourceManager()
|
||||||
router.APIRouterV1(api_resource_mgr, api_process_dict)
|
router.APIRouterV1(api_resource_mgr, api_process_dict)
|
||||||
return application.ApiApplication(api_resource_mgr)
|
return application.ApiApplication(api_resource_mgr)
|
||||||
|
@ -237,8 +237,8 @@ class TestDsePerformance(testbase.SqlTestCase):
|
|||||||
policy = self.engine.DEFAULT_THEORY
|
policy = self.engine.DEFAULT_THEORY
|
||||||
formula = compile.parse1(
|
formula = compile.parse1(
|
||||||
'q(1) :- data:p(1, 2.3, "foo", "bar", 1, %s)' % ('a'*100 + '1'))
|
'q(1) :- data:p(1, 2.3, "foo", "bar", 1, %s)' % ('a'*100 + '1'))
|
||||||
self.api['rule'].publish(
|
self.engine.process_policy_update(
|
||||||
'policy-update', [compile.Event(formula, target=policy)])
|
[compile.Event(formula, target=policy)])
|
||||||
|
|
||||||
# Poll data and wait til it arrives at engine
|
# Poll data and wait til it arrives at engine
|
||||||
driver.poll()
|
driver.poll()
|
||||||
@ -283,8 +283,8 @@ class TestDsePerformance(testbase.SqlTestCase):
|
|||||||
formula = compile.parse1(
|
formula = compile.parse1(
|
||||||
'q(1) :- data:p(1, 2.3, "foo", "bar", 1, %s)' % ('a'*100 + '1'))
|
'q(1) :- data:p(1, 2.3, "foo", "bar", 1, %s)' % ('a'*100 + '1'))
|
||||||
LOG.info("publishing rule")
|
LOG.info("publishing rule")
|
||||||
self.api['rule'].publish(
|
self.engine.process_policy_update(
|
||||||
'policy-update', [compile.Event(formula, target=policy)])
|
[compile.Event(formula, target=policy)])
|
||||||
|
|
||||||
# Poll data and wait til it arrives at engine
|
# Poll data and wait til it arrives at engine
|
||||||
driver.poll()
|
driver.poll()
|
||||||
|
@ -35,7 +35,6 @@ from oslo_log import log as logging
|
|||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress.common import config
|
from congress.common import config
|
||||||
from congress.datalog import base as datalog_base
|
from congress.datalog import base as datalog_base
|
||||||
from congress.datalog import compile
|
|
||||||
from congress import harness
|
from congress import harness
|
||||||
from congress.tests import base
|
from congress.tests import base
|
||||||
import congress.tests.datasources.test_neutron_driver as test_neutron
|
import congress.tests.datasources.test_neutron_driver as test_neutron
|
||||||
@ -162,15 +161,6 @@ class TestCongress(base.SqlTestCase):
|
|||||||
args = ['--config-file', helper.etcdir('congress.conf.test')]
|
args = ['--config-file', helper.etcdir('congress.conf.test')]
|
||||||
config.init(args)
|
config.init(args)
|
||||||
|
|
||||||
def test_startup(self):
|
|
||||||
"""Test that everything is properly loaded at startup."""
|
|
||||||
engine = self.engine
|
|
||||||
api = self.api
|
|
||||||
helper.retry_check_subscriptions(
|
|
||||||
engine, [(api['rule'].name, 'policy-update')])
|
|
||||||
helper.retry_check_subscribers(
|
|
||||||
api['rule'], [(engine.name, 'policy-update')])
|
|
||||||
|
|
||||||
def test_synchronize_policy_no_erratic_change(self):
|
def test_synchronize_policy_no_erratic_change(self):
|
||||||
"""Test that synchronize_policies does not changes init state"""
|
"""Test that synchronize_policies does not changes init state"""
|
||||||
with mock.patch.object(self.engine, 'delete_policy') as d:
|
with mock.patch.object(self.engine, 'delete_policy') as d:
|
||||||
@ -180,65 +170,6 @@ class TestCongress(base.SqlTestCase):
|
|||||||
d.assert_not_called()
|
d.assert_not_called()
|
||||||
c.assert_not_called()
|
c.assert_not_called()
|
||||||
|
|
||||||
def test_policy_subscriptions(self):
|
|
||||||
"""Test that policy engine subscriptions adjust to policy changes."""
|
|
||||||
engine = self.engine
|
|
||||||
api = self.api
|
|
||||||
cage = self.cage
|
|
||||||
policy = engine.DEFAULT_THEORY
|
|
||||||
|
|
||||||
# Send formula
|
|
||||||
formula = test_neutron.create_network_group('p')
|
|
||||||
LOG.debug("Sending formula: %s", formula)
|
|
||||||
api['rule'].publish(
|
|
||||||
'policy-update', [compile.Event(formula, target=policy)])
|
|
||||||
# check we have the proper subscriptions
|
|
||||||
self.assertTrue('neutron' in cage.services)
|
|
||||||
neutron = cage.service_object('neutron')
|
|
||||||
helper.retry_check_subscriptions(engine, [('neutron', 'networks')])
|
|
||||||
helper.retry_check_subscribers(neutron, [(engine.name, 'networks')])
|
|
||||||
|
|
||||||
def test_neutron(self):
|
|
||||||
"""Test polling and publishing of neutron updates."""
|
|
||||||
engine = self.engine
|
|
||||||
api = self.api
|
|
||||||
cage = self.cage
|
|
||||||
policy = engine.DEFAULT_THEORY
|
|
||||||
|
|
||||||
# Send formula
|
|
||||||
formula = test_neutron.create_network_group('p')
|
|
||||||
LOG.debug("Sending formula: %s", formula)
|
|
||||||
api['rule'].publish(
|
|
||||||
'policy-update', [compile.Event(formula, target=policy)])
|
|
||||||
helper.retry_check_nonempty_last_policy_change(engine)
|
|
||||||
LOG.debug("All services: %s", cage.services.keys())
|
|
||||||
neutron = cage.service_object('neutron')
|
|
||||||
neutron.poll()
|
|
||||||
ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492") ')
|
|
||||||
helper.retry_check_db_equal(engine, 'p(x)', ans, target=policy)
|
|
||||||
|
|
||||||
def test_multiple(self):
|
|
||||||
"""Test polling and publishing of multiple neutron instances."""
|
|
||||||
api = self.api
|
|
||||||
cage = self.cage
|
|
||||||
engine = self.engine
|
|
||||||
policy = engine.DEFAULT_THEORY
|
|
||||||
|
|
||||||
# Send formula
|
|
||||||
formula = test_neutron.create_networkXnetwork_group('p')
|
|
||||||
api['rule'].publish(
|
|
||||||
'policy-update', [compile.Event(formula, target=policy)])
|
|
||||||
helper.retry_check_nonempty_last_policy_change(engine)
|
|
||||||
# poll datasources
|
|
||||||
neutron = cage.service_object('neutron')
|
|
||||||
neutron2 = cage.service_object('neutron2')
|
|
||||||
neutron.poll()
|
|
||||||
neutron2.poll()
|
|
||||||
# check answer
|
|
||||||
ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492", '
|
|
||||||
' "240ff9df-df35-43ae-9df5-27fae87f2492") ')
|
|
||||||
helper.retry_check_db_equal(engine, 'p(x,y)', ans, target=policy)
|
|
||||||
|
|
||||||
def test_datasource_api_model(self):
|
def test_datasource_api_model(self):
|
||||||
"""Test the datasource api model.
|
"""Test the datasource api model.
|
||||||
|
|
||||||
|
@ -15,12 +15,18 @@
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
from congress.policy_engines.agnostic import Dse2Runtime
|
from congress import harness
|
||||||
from congress.tests import fake_datasource
|
from congress.tests import fake_datasource
|
||||||
from congress.tests import helper
|
from congress.tests import helper
|
||||||
|
|
||||||
|
|
||||||
def setup_config(services=[]):
|
def setup_config(with_fake_datasource=True):
|
||||||
|
"""Setup DseNode for testing.
|
||||||
|
|
||||||
|
:param services is an array of DataServices
|
||||||
|
:param api is a dictionary mapping api name to API model instance
|
||||||
|
"""
|
||||||
|
|
||||||
cfg.CONF.set_override('distributed_architecture', True)
|
cfg.CONF.set_override('distributed_architecture', True)
|
||||||
# Load the fake driver.
|
# Load the fake driver.
|
||||||
cfg.CONF.set_override(
|
cfg.CONF.set_override(
|
||||||
@ -28,13 +34,23 @@ def setup_config(services=[]):
|
|||||||
['congress.tests.fake_datasource.FakeDataSource'])
|
['congress.tests.fake_datasource.FakeDataSource'])
|
||||||
|
|
||||||
node = helper.make_dsenode_new_partition("testnode")
|
node = helper.make_dsenode_new_partition("testnode")
|
||||||
engine = Dse2Runtime('engine')
|
services = harness.create2(node=node)
|
||||||
data = fake_datasource.FakeDataSource('data')
|
|
||||||
|
|
||||||
node.register_service(engine)
|
# Always register engine and fake datasource
|
||||||
node.register_service(data)
|
# engine = Dse2Runtime('engine')
|
||||||
|
# node.register_service(engine)
|
||||||
|
data = None
|
||||||
|
if with_fake_datasource:
|
||||||
|
data = fake_datasource.FakeDataSource('data')
|
||||||
|
node.register_service(data)
|
||||||
|
|
||||||
for service in services:
|
# Register provided apis (and no others)
|
||||||
node.register_service(service)
|
# (ResourceManager inherits from DataService)
|
||||||
|
# api_map = {a.name: a for a in api}
|
||||||
|
# api_resource_mgr = application.ResourceManager()
|
||||||
|
# router.APIRouterV1(api_resource_mgr, api)
|
||||||
|
# node.register_service(api_resource_mgr)
|
||||||
|
|
||||||
return {'node': node, 'engine': engine, 'data': data}
|
engine = services[harness.ENGINE_SERVICE_NAME]
|
||||||
|
api = services['api']
|
||||||
|
return {'node': node, 'engine': engine, 'data': data, 'api': api}
|
||||||
|
@ -21,7 +21,6 @@ from __future__ import absolute_import
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
cfg.CONF.distributed_architecture = True
|
cfg.CONF.distributed_architecture = True
|
||||||
|
|
||||||
from congress.api import action_model
|
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress.tests import base
|
from congress.tests import base
|
||||||
from congress.tests2.api import base as api_base
|
from congress.tests2.api import base as api_base
|
||||||
@ -30,9 +29,8 @@ from congress.tests2.api import base as api_base
|
|||||||
class TestActionModel(base.SqlTestCase):
|
class TestActionModel(base.SqlTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestActionModel, self).setUp()
|
super(TestActionModel, self).setUp()
|
||||||
self.action_model = action_model.ActionsModel(
|
services = api_base.setup_config()
|
||||||
'api-action', policy_engine='engine')
|
self.action_model = services['api']['api-action']
|
||||||
services = api_base.setup_config([self.action_model])
|
|
||||||
self.datasource = services['data']
|
self.datasource = services['data']
|
||||||
|
|
||||||
def test_get_datasource_actions(self):
|
def test_get_datasource_actions(self):
|
||||||
|
@ -20,7 +20,6 @@ from __future__ import absolute_import
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
cfg.CONF.distributed_architecture = True
|
cfg.CONF.distributed_architecture = True
|
||||||
|
|
||||||
from congress.api import datasource_model
|
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress import exception
|
from congress import exception
|
||||||
from congress.tests import base
|
from congress.tests import base
|
||||||
@ -30,12 +29,11 @@ from congress.tests2.api import base as api_base
|
|||||||
class TestDatasourceModel(base.SqlTestCase):
|
class TestDatasourceModel(base.SqlTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestDatasourceModel, self).setUp()
|
super(TestDatasourceModel, self).setUp()
|
||||||
self.datasource_model = datasource_model.DatasourceModel(
|
services = api_base.setup_config()
|
||||||
'test_datasource', policy_engine='engine')
|
self.datasource_model = services['api']['api-datasource']
|
||||||
self.config = api_base.setup_config([self.datasource_model])
|
self.data = services['data']
|
||||||
self.data = self.config['data']
|
self.node = services['node']
|
||||||
self.node = self.config['node']
|
self.engine = services['engine']
|
||||||
self.engine = self.config['engine']
|
|
||||||
self.datasource = self._get_datasource_request()
|
self.datasource = self._get_datasource_request()
|
||||||
self.node.add_datasource(self.datasource)
|
self.node.add_datasource(self.datasource)
|
||||||
|
|
||||||
|
@ -25,8 +25,6 @@ import mock
|
|||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
from congress.api import error_codes
|
from congress.api import error_codes
|
||||||
from congress.api import policy_model
|
|
||||||
from congress.api import rule_model
|
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress.tests import base
|
from congress.tests import base
|
||||||
from congress.tests import helper
|
from congress.tests import helper
|
||||||
@ -37,11 +35,9 @@ class TestPolicyModel(base.SqlTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestPolicyModel, self).setUp()
|
super(TestPolicyModel, self).setUp()
|
||||||
|
|
||||||
self.policy_model = policy_model.PolicyModel('api-policy',
|
services = api_base.setup_config()
|
||||||
policy_engine='engine')
|
self.policy_model = services['api']['api-policy']
|
||||||
self.rule_api = rule_model.RuleModel('api-rule',
|
self.rule_api = services['api']['api-rule']
|
||||||
policy_engine='engine')
|
|
||||||
services = api_base.setup_config([self.policy_model, self.rule_api])
|
|
||||||
self.node = services['node']
|
self.node = services['node']
|
||||||
self.engine = services['engine']
|
self.engine = services['engine']
|
||||||
self.initial_policies = set(self.engine.policy_names())
|
self.initial_policies = set(self.engine.policy_names())
|
||||||
|
@ -20,9 +20,6 @@ from __future__ import absolute_import
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
cfg.CONF.distributed_architecture = True
|
cfg.CONF.distributed_architecture = True
|
||||||
|
|
||||||
from congress.api import policy_model
|
|
||||||
from congress.api import row_model
|
|
||||||
from congress.api import rule_model
|
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress.tests import base
|
from congress.tests import base
|
||||||
from congress.tests2.api import base as api_base
|
from congress.tests2.api import base as api_base
|
||||||
@ -32,15 +29,12 @@ class TestRowModel(base.SqlTestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestRowModel, self).setUp()
|
super(TestRowModel, self).setUp()
|
||||||
self.policy_model = policy_model.PolicyModel(
|
services = api_base.setup_config()
|
||||||
'api-policy', policy_engine='engine')
|
self.policy_model = services['api']['api-policy']
|
||||||
self.rule_model = rule_model.RuleModel('api-rule',
|
self.rule_model = services['api']['api-rule']
|
||||||
policy_engine='engine')
|
self.row_model = services['api']['api-row']
|
||||||
self.row_model = row_model.RowModel('api-row', policy_engine='engine')
|
self.node = services['node']
|
||||||
result = api_base.setup_config([self.policy_model, self.rule_model,
|
self.data = services['data']
|
||||||
self.row_model])
|
|
||||||
self.node = result['node']
|
|
||||||
self.data = result['data']
|
|
||||||
|
|
||||||
def test_get_items_datasource_row(self):
|
def test_get_items_datasource_row(self):
|
||||||
# adjust datasource to have required value
|
# adjust datasource to have required value
|
||||||
|
@ -21,7 +21,6 @@ import mock
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
cfg.CONF.distributed_architecture = True
|
cfg.CONF.distributed_architecture = True
|
||||||
|
|
||||||
from congress.api import policy_model
|
|
||||||
from congress.api import rule_model
|
from congress.api import rule_model
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress.tests import base
|
from congress.tests import base
|
||||||
@ -32,12 +31,11 @@ class TestRuleModel(base.SqlTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestRuleModel, self).setUp()
|
super(TestRuleModel, self).setUp()
|
||||||
|
|
||||||
self.rule_model = rule_model.RuleModel('api-rule',
|
services = api_base.setup_config()
|
||||||
policy_engine='engine')
|
self.policy_model = services['api']['api-policy']
|
||||||
self.policy_model = policy_model.PolicyModel('api-policy',
|
self.rule_model = services['api']['api-rule']
|
||||||
policy_engine='engine')
|
self.node = services['node']
|
||||||
result = api_base.setup_config([self.policy_model, self.rule_model])
|
|
||||||
self.node = result['node']
|
|
||||||
self.policy_model.add_item({'name': 'classification'}, {})
|
self.policy_model.add_item({'name': 'classification'}, {})
|
||||||
self.action_policy = self._add_action_policy()
|
self.action_policy = self._add_action_policy()
|
||||||
self.context = {'policy_id': self.action_policy["name"]}
|
self.context = {'policy_id': self.action_policy["name"]}
|
||||||
|
@ -21,7 +21,6 @@ from oslo_config import cfg
|
|||||||
cfg.CONF.distributed_architecture = True
|
cfg.CONF.distributed_architecture = True
|
||||||
|
|
||||||
from congress.api import api_utils
|
from congress.api import api_utils
|
||||||
from congress.api import schema_model
|
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress.tests import base
|
from congress.tests import base
|
||||||
from congress.tests2.api import base as api_base
|
from congress.tests2.api import base as api_base
|
||||||
@ -30,9 +29,9 @@ from congress.tests2.api import base as api_base
|
|||||||
class TestSchemaModel(base.TestCase):
|
class TestSchemaModel(base.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestSchemaModel, self).setUp()
|
super(TestSchemaModel, self).setUp()
|
||||||
self.schema_model = schema_model.SchemaModel("test_schema", {})
|
services = api_base.setup_config()
|
||||||
self.config = api_base.setup_config([self.schema_model])
|
self.schema_model = services['api']['api-schema']
|
||||||
self.data = self.config['data']
|
self.data = services['data']
|
||||||
|
|
||||||
def test_get_item_all_table(self):
|
def test_get_item_all_table(self):
|
||||||
context = {'ds_id': self.data.service_id}
|
context = {'ds_id': self.data.service_id}
|
||||||
|
@ -22,9 +22,6 @@ import uuid
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
cfg.CONF.distributed_architecture = True
|
cfg.CONF.distributed_architecture = True
|
||||||
|
|
||||||
from congress.api import policy_model
|
|
||||||
from congress.api import rule_model
|
|
||||||
from congress.api import status_model
|
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress.tests import base
|
from congress.tests import base
|
||||||
from congress.tests2.api import base as api_base
|
from congress.tests2.api import base as api_base
|
||||||
@ -33,16 +30,12 @@ from congress.tests2.api import base as api_base
|
|||||||
class TestStatusModel(base.SqlTestCase):
|
class TestStatusModel(base.SqlTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestStatusModel, self).setUp()
|
super(TestStatusModel, self).setUp()
|
||||||
self.policy_model = policy_model.PolicyModel('api-policy',
|
services = api_base.setup_config()
|
||||||
policy_engine='engine')
|
self.policy_model = services['api']['api-policy']
|
||||||
self.rule_model = rule_model.RuleModel('api-rule',
|
self.rule_model = services['api']['api-rule']
|
||||||
policy_engine='engine')
|
self.status_model = services['api']['api-status']
|
||||||
self.status_model = status_model.StatusModel('api-status',
|
self.node = services['node']
|
||||||
policy_engine='engine')
|
self.datasource = services['data']
|
||||||
result = api_base.setup_config([self.policy_model, self.rule_model,
|
|
||||||
self.status_model])
|
|
||||||
self.node = result['node']
|
|
||||||
self.datasource = result['data']
|
|
||||||
|
|
||||||
def test_get_datasource_status(self):
|
def test_get_datasource_status(self):
|
||||||
context = {'ds_id': self.datasource.service_id}
|
context = {'ds_id': self.datasource.service_id}
|
||||||
|
@ -20,9 +20,6 @@ from __future__ import absolute_import
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
cfg.CONF.distributed_architecture = True
|
cfg.CONF.distributed_architecture = True
|
||||||
|
|
||||||
from congress.api import policy_model
|
|
||||||
from congress.api import rule_model
|
|
||||||
from congress.api import table_model
|
|
||||||
from congress.api import webservice
|
from congress.api import webservice
|
||||||
from congress.tests import base
|
from congress.tests import base
|
||||||
from congress.tests2.api import base as api_base
|
from congress.tests2.api import base as api_base
|
||||||
@ -31,22 +28,13 @@ from congress.tests2.api import base as api_base
|
|||||||
class TestTableModel(base.SqlTestCase):
|
class TestTableModel(base.SqlTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestTableModel, self).setUp()
|
super(TestTableModel, self).setUp()
|
||||||
# Here we load the fake driver
|
services = api_base.setup_config()
|
||||||
cfg.CONF.set_override(
|
self.policy_model = services['api']['api-policy']
|
||||||
'drivers',
|
self.table_model = services['api']['api-table']
|
||||||
['congress.tests.fake_datasource.FakeDataSource'])
|
self.api_rule = services['api']['api-rule']
|
||||||
|
self.node = services['node']
|
||||||
self.table_model = table_model.TableModel('api-table',
|
self.engine = services['engine']
|
||||||
policy_engine='engine')
|
self.data = services['data']
|
||||||
self.api_rule = rule_model.RuleModel('api-rule',
|
|
||||||
policy_engine='engine')
|
|
||||||
self.policy_model = policy_model.PolicyModel('api-policy',
|
|
||||||
policy_engine='engine')
|
|
||||||
result = api_base.setup_config([self.table_model, self.api_rule,
|
|
||||||
self.policy_model])
|
|
||||||
self.node = result['node']
|
|
||||||
self.engine = result['engine']
|
|
||||||
self.data = result['data']
|
|
||||||
# create test policy
|
# create test policy
|
||||||
self._create_test_policy()
|
self._create_test_policy()
|
||||||
|
|
||||||
|
698
congress/tests2/test_congress.py
Normal file
698
congress/tests2/test_congress.py
Normal file
@ -0,0 +1,698 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014 VMware, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
test_congress
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Tests for `congress` module.
|
||||||
|
"""
|
||||||
|
from __future__ import print_function
|
||||||
|
from __future__ import division
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
cfg.CONF.distributed_architecture = True
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from congress.common import config
|
||||||
|
from congress import harness
|
||||||
|
from congress.tests import base
|
||||||
|
from congress.tests import helper
|
||||||
|
from congress.tests2.api import base as api_base
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCongress(base.SqlTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Setup tests that use multiple mock neutron instances."""
|
||||||
|
super(TestCongress, self).setUp()
|
||||||
|
self.services = api_base.setup_config(with_fake_datasource=False)
|
||||||
|
self.api = self.services['api']
|
||||||
|
self.node = self.services['node']
|
||||||
|
|
||||||
|
def setup_config(self):
|
||||||
|
args = ['--config-file', helper.etcdir('congress.conf.test')]
|
||||||
|
config.init(args)
|
||||||
|
|
||||||
|
def test_startup(self):
|
||||||
|
self.assertIsNotNone(self.services['api'])
|
||||||
|
self.assertIsNotNone(self.services[harness.ENGINE_SERVICE_NAME])
|
||||||
|
self.assertIsNotNone(self.services[harness.ENGINE_SERVICE_NAME].node)
|
||||||
|
|
||||||
|
def test_policy(self):
|
||||||
|
self.create_policy('alpha')
|
||||||
|
self.insert_rule('q(1, 2) :- true', 'alpha')
|
||||||
|
self.insert_rule('q(2, 3) :- true', 'alpha')
|
||||||
|
helper.retry_check_function_return_value(
|
||||||
|
lambda: self.query('q', 'alpha'),
|
||||||
|
{'results': [{'data': (1, 2)}, {'data': (2, 3)}]})
|
||||||
|
|
||||||
|
def test_policy_datasource(self):
|
||||||
|
self.create_policy('alpha')
|
||||||
|
self.create_fake_datasource('fake')
|
||||||
|
data = self.node.service_object('fake')
|
||||||
|
data.state = {'fake_table': set([(1, 2)])}
|
||||||
|
|
||||||
|
data.poll()
|
||||||
|
self.insert_rule('q(x) :- fake:fake_table(x,y)', 'alpha')
|
||||||
|
helper.retry_check_function_return_value(
|
||||||
|
lambda: self.query('q', 'alpha'), {'results': [{'data': (1,)}]})
|
||||||
|
|
||||||
|
# TODO(dse2): enable rules to be inserted before data created.
|
||||||
|
# Maybe just have subscription handle errors gracefull when
|
||||||
|
# asking for a snapshot and return [].
|
||||||
|
# self.insert_rule('p(x) :- fake:fake_table(x)', 'alpha')
|
||||||
|
|
||||||
|
def create_policy(self, name):
|
||||||
|
self.api['api-policy'].add_item({'name': name}, {})
|
||||||
|
|
||||||
|
def insert_rule(self, rule, policy):
|
||||||
|
context = {'policy_id': policy}
|
||||||
|
return self.api['api-rule'].add_item(
|
||||||
|
{'rule': rule}, {}, context=context)
|
||||||
|
|
||||||
|
def create_fake_datasource(self, name):
|
||||||
|
item = {'name': name,
|
||||||
|
'driver': 'fake_datasource',
|
||||||
|
'description': 'hello world!',
|
||||||
|
'enabled': True,
|
||||||
|
'type': None,
|
||||||
|
'config': {'auth_url': 'foo',
|
||||||
|
'username': 'armax',
|
||||||
|
'password': '<hidden>',
|
||||||
|
'tenant_name': 'armax'}}
|
||||||
|
|
||||||
|
return self.api['api-datasource'].add_item(item, params={})
|
||||||
|
|
||||||
|
def query(self, tablename, policyname):
|
||||||
|
context = {'policy_id': policyname,
|
||||||
|
'table_id': tablename}
|
||||||
|
return self.api['api-row'].get_items({}, context)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(dse2): port this test
|
||||||
|
# class TestCongress(base.SqlTestCase):
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# config_override = {'neutron2': {'username': 'demo', 'tenant_name':
|
||||||
|
# 'demo', 'password': 'password',
|
||||||
|
# 'auth_url':
|
||||||
|
# 'http://127.0.0.1:5000/v2.0',
|
||||||
|
# 'module':
|
||||||
|
# 'datasources/neutron_driver.py'},
|
||||||
|
# 'nova': {'username': 'demo',
|
||||||
|
# 'tenant_name': 'demo',
|
||||||
|
# 'password': 'password',
|
||||||
|
# 'auth_url': 'http://127.0.0.1:5000/v2.0',
|
||||||
|
# 'module': 'datasources/nova_driver.py'},
|
||||||
|
# 'neutron': {'username': 'demo',
|
||||||
|
# 'tenant_name': 'demo',
|
||||||
|
# 'password': 'password',
|
||||||
|
# 'auth_url':
|
||||||
|
# 'http://127.0.0.1:5000/v2.0',
|
||||||
|
# 'module':
|
||||||
|
# 'datasources/neutron_driver.py'}}
|
||||||
|
|
||||||
|
# cage = harness.create2(helper.root_path(), config_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'),
|
||||||
|
# 'status': cage.service_object('api-status'),
|
||||||
|
# 'schema': cage.service_object('api-schema')}
|
||||||
|
|
||||||
|
# config = {'username': 'demo',
|
||||||
|
# 'auth_url': 'http://127.0.0.1:5000/v2.0',
|
||||||
|
# 'tenant_name': 'demo',
|
||||||
|
# 'password': 'password',
|
||||||
|
# 'module': 'datasources/neutron_driver.py',
|
||||||
|
# 'poll_time': 0}
|
||||||
|
|
||||||
|
# engine.create_policy('neutron')
|
||||||
|
# engine.create_policy('neutron2')
|
||||||
|
# engine.create_policy('nova')
|
||||||
|
# harness.load_data_service(
|
||||||
|
# 'neutron', config, cage,
|
||||||
|
# os.path.join(helper.root_path(), "congress"), 1)
|
||||||
|
# service = cage.service_object('neutron')
|
||||||
|
# engine.set_schema('neutron', service.get_schema())
|
||||||
|
# harness.load_data_service(
|
||||||
|
# 'neutron2', config, cage,
|
||||||
|
# os.path.join(helper.root_path(), "congress"), 2)
|
||||||
|
|
||||||
|
# engine.set_schema('neutron2', service.get_schema())
|
||||||
|
# config['module'] = 'datasources/nova_driver.py'
|
||||||
|
# harness.load_data_service(
|
||||||
|
# 'nova', config, cage,
|
||||||
|
# os.path.join(helper.root_path(), "congress"), 3)
|
||||||
|
# engine.set_schema('nova', service.get_schema())
|
||||||
|
|
||||||
|
# cage.service_object('neutron').neutron = neutron_mock
|
||||||
|
# cage.service_object('neutron2').neutron = neutron_mock2
|
||||||
|
# # delete all policies that aren't builtin, so we have clean slate
|
||||||
|
# names = set(engine.policy_names()) - engine.builtin_policy_names
|
||||||
|
# for name in names:
|
||||||
|
# try:
|
||||||
|
# api['policy'].delete_item(name, {})
|
||||||
|
# except KeyError:
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# # Turn off schema checking
|
||||||
|
# engine.module_schema = None
|
||||||
|
|
||||||
|
# # 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()
|
||||||
|
|
||||||
|
# self.cage = cage
|
||||||
|
# self.engine = engine
|
||||||
|
# self.api = api
|
||||||
|
|
||||||
|
# def setup_config(self):
|
||||||
|
# args = ['--config-file', helper.etcdir('congress.conf.test')]
|
||||||
|
# config.init(args)
|
||||||
|
|
||||||
|
# def test_startup(self):
|
||||||
|
# """Test that everything is properly loaded at startup."""
|
||||||
|
# engine = self.engine
|
||||||
|
# api = self.api
|
||||||
|
# helper.retry_check_subscriptions(
|
||||||
|
# engine, [(api['rule'].name, 'policy-update')])
|
||||||
|
# helper.retry_check_subscribers(
|
||||||
|
# api['rule'], [(engine.name, 'policy-update')])
|
||||||
|
|
||||||
|
# def test_policy_subscriptions(self):
|
||||||
|
# """Test that policy engine subscriptions adjust to policy changes."""
|
||||||
|
# engine = self.engine
|
||||||
|
# api = self.api
|
||||||
|
# cage = self.cage
|
||||||
|
# policy = engine.DEFAULT_THEORY
|
||||||
|
|
||||||
|
# # Send formula
|
||||||
|
# formula = test_neutron.create_network_group('p')
|
||||||
|
# LOG.debug("Sending formula: %s", formula)
|
||||||
|
# api['rule'].publish(
|
||||||
|
# 'policy-update', [compile.Event(formula, target=policy)])
|
||||||
|
# # check we have the proper subscriptions
|
||||||
|
# self.assertTrue('neutron' in cage.services)
|
||||||
|
# neutron = cage.service_object('neutron')
|
||||||
|
# helper.retry_check_subscriptions(engine, [('neutron', 'networks')])
|
||||||
|
# helper.retry_check_subscribers(neutron, [(engine.name, 'networks')])
|
||||||
|
|
||||||
|
# def test_neutron(self):
|
||||||
|
# """Test polling and publishing of neutron updates."""
|
||||||
|
# engine = self.engine
|
||||||
|
# api = self.api
|
||||||
|
# cage = self.cage
|
||||||
|
# policy = engine.DEFAULT_THEORY
|
||||||
|
|
||||||
|
# # Send formula
|
||||||
|
# formula = test_neutron.create_network_group('p')
|
||||||
|
# LOG.debug("Sending formula: %s", formula)
|
||||||
|
# api['rule'].publish(
|
||||||
|
# 'policy-update', [compile.Event(formula, target=policy)])
|
||||||
|
# helper.retry_check_nonempty_last_policy_change(engine)
|
||||||
|
# LOG.debug("All services: %s", cage.services.keys())
|
||||||
|
# neutron = cage.service_object('neutron')
|
||||||
|
# neutron.poll()
|
||||||
|
# ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492") ')
|
||||||
|
# helper.retry_check_db_equal(engine, 'p(x)', ans, target=policy)
|
||||||
|
|
||||||
|
# def test_multiple(self):
|
||||||
|
# """Test polling and publishing of multiple neutron instances."""
|
||||||
|
# api = self.api
|
||||||
|
# cage = self.cage
|
||||||
|
# engine = self.engine
|
||||||
|
# policy = engine.DEFAULT_THEORY
|
||||||
|
|
||||||
|
# # Send formula
|
||||||
|
# formula = test_neutron.create_networkXnetwork_group('p')
|
||||||
|
# api['rule'].publish(
|
||||||
|
# 'policy-update', [compile.Event(formula, target=policy)])
|
||||||
|
# helper.retry_check_nonempty_last_policy_change(engine)
|
||||||
|
# # poll datasources
|
||||||
|
# neutron = cage.service_object('neutron')
|
||||||
|
# neutron2 = cage.service_object('neutron2')
|
||||||
|
# neutron.poll()
|
||||||
|
# neutron2.poll()
|
||||||
|
# # check answer
|
||||||
|
# ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492", '
|
||||||
|
# ' "240ff9df-df35-43ae-9df5-27fae87f2492") ')
|
||||||
|
# helper.retry_check_db_equal(engine, 'p(x,y)', ans, target=policy)
|
||||||
|
|
||||||
|
# def test_datasource_api_model(self):
|
||||||
|
# """Test the datasource api model.
|
||||||
|
|
||||||
|
# Same as test_multiple except we use the api interface
|
||||||
|
# instead of the DSE interface.
|
||||||
|
# """
|
||||||
|
# self.skipTest("Move to test/api/api_model and use fake driver...")
|
||||||
|
# # FIXME(arosen): we should break out these tests into
|
||||||
|
# # congress/tests/api/test_datasource.py
|
||||||
|
# with mock.patch(
|
||||||
|
# "congress.managers.datasource.DataSourceDriverManager."
|
||||||
|
# "get_datasource_drivers_info") as get_info:
|
||||||
|
# get_info.return_value = [{'datasource_driver': 'neutron'},
|
||||||
|
# {'datasource_driver': 'neutron2'},
|
||||||
|
# {'datasource_driver': 'nova'}]
|
||||||
|
# api = self.api
|
||||||
|
# engine = self.engine
|
||||||
|
# # Insert formula (which creates neutron services)
|
||||||
|
# net_formula = test_neutron.create_networkXnetwork_group('p')
|
||||||
|
# LOG.debug("Sending formula: %s", net_formula)
|
||||||
|
# context = {'policy_id': engine.DEFAULT_THEORY}
|
||||||
|
# api['rule'].add_item(
|
||||||
|
# {'rule': str(net_formula)}, {}, context=context)
|
||||||
|
# datasources = api['datasource'].get_items({})['results']
|
||||||
|
# datasources = [d['datasource_driver'] for d in datasources]
|
||||||
|
# self.assertEqual(set(datasources),
|
||||||
|
# set(['neutron', 'neutron2', 'nova']))
|
||||||
|
|
||||||
|
# def test_row_api_model(self):
|
||||||
|
# """Test the row api model."""
|
||||||
|
# self.skipTest("Move to test/api/test_row_api_model..")
|
||||||
|
# api = self.api
|
||||||
|
# engine = self.engine
|
||||||
|
# # add some rules defining tables
|
||||||
|
# context = {'policy_id': engine.DEFAULT_THEORY}
|
||||||
|
# api['rule'].add_item(
|
||||||
|
# {'rule': 'p(x) :- q(x)'},
|
||||||
|
# {}, context=context)
|
||||||
|
# api['rule'].add_item(
|
||||||
|
# {'rule': 'p(x) :- r(x)'},
|
||||||
|
# {}, context=context)
|
||||||
|
# api['rule'].add_item(
|
||||||
|
# {'rule': 'q(x) :- r(x)'},
|
||||||
|
# {}, context=context)
|
||||||
|
# api['rule'].add_item(
|
||||||
|
# {'rule': 'r(1) :- true'},
|
||||||
|
# {}, context=context)
|
||||||
|
|
||||||
|
# # without tracing
|
||||||
|
# context['table_id'] = 'p'
|
||||||
|
# ans = api['row'].get_items({}, context=context)
|
||||||
|
# s = frozenset([tuple(x['data']) for x in ans['results']])
|
||||||
|
# t = frozenset([(1,)])
|
||||||
|
# self.assertEqual(s, t, "Rows without tracing")
|
||||||
|
# self.assertTrue('trace' not in ans, "Rows should have no Trace")
|
||||||
|
# self.assertEqual(len(ans['results']), 1) # no duplicates
|
||||||
|
|
||||||
|
# # with tracing
|
||||||
|
# ans = api['row'].get_items({'trace': 'true'}, context=context)
|
||||||
|
# s = frozenset([tuple(x['data']) for x in ans['results']])
|
||||||
|
# t = frozenset([(1,)])
|
||||||
|
# self.assertEqual(s, t, "Rows with tracing")
|
||||||
|
# self.assertTrue('trace' in ans, "Rows should have trace")
|
||||||
|
# self.assertEqual(len(ans['trace'].split('\n')), 16)
|
||||||
|
|
||||||
|
# # unknown policy table
|
||||||
|
# context = {'policy_id': engine.DEFAULT_THEORY,
|
||||||
|
# 'table_id': 'unktable'}
|
||||||
|
# ans = api['row'].get_items({}, context=context)
|
||||||
|
# self.assertEqual(len(ans['results']), 0)
|
||||||
|
|
||||||
|
# # unknown policy
|
||||||
|
# context = {'policy_id': 'unkpolicy', 'table_id': 'unktable'}
|
||||||
|
# ans = api['row'].get_items({}, context=context)
|
||||||
|
# self.assertEqual(len(ans['results']), 0)
|
||||||
|
|
||||||
|
# # unknown datasource table
|
||||||
|
# context = {'ds_id': 'neutron', 'table_id': 'unktable'}
|
||||||
|
# ans = api['row'].get_items({}, context=context)
|
||||||
|
# self.assertEqual(len(ans['results']), 0)
|
||||||
|
|
||||||
|
# # unknown datasource
|
||||||
|
# context = {'ds_id': 'unkds', 'table_id': 'unktable'}
|
||||||
|
# ans = api['row'].get_items({}, context=context)
|
||||||
|
# self.assertEqual(len(ans['results']), 0)
|
||||||
|
|
||||||
|
# def test_policy_api_model_execute(self):
|
||||||
|
# def _execute_api(client, action, action_args):
|
||||||
|
# LOG.info("_execute_api called on %s and %s", action, action_args)
|
||||||
|
# positional_args = action_args['positional']
|
||||||
|
# named_args = action_args['named']
|
||||||
|
# method = reduce(getattr, action.split('.'), client)
|
||||||
|
# method(*positional_args, **named_args)
|
||||||
|
|
||||||
|
# class NovaClient(object):
|
||||||
|
# def __init__(self, testkey):
|
||||||
|
# self.testkey = testkey
|
||||||
|
|
||||||
|
# def _get_testkey(self):
|
||||||
|
# return self.testkey
|
||||||
|
|
||||||
|
# def disconnectNetwork(self, arg1, arg2, arg3):
|
||||||
|
# self.testkey = "arg1=%s arg2=%s arg3=%s" % (arg1, arg2, arg3)
|
||||||
|
|
||||||
|
# nova_client = NovaClient("testing")
|
||||||
|
# nova = self.cage.service_object('nova')
|
||||||
|
# nova._execute_api = _execute_api
|
||||||
|
# nova.nova_client = nova_client
|
||||||
|
|
||||||
|
# api = self.api
|
||||||
|
# body = {'name': 'nova:disconnectNetwork',
|
||||||
|
# 'args': {'positional': ['value1', 'value2'],
|
||||||
|
# 'named': {'arg3': 'value3'}}}
|
||||||
|
|
||||||
|
# request = helper.FakeRequest(body)
|
||||||
|
# result = api['policy'].execute_action({}, {}, request)
|
||||||
|
# self.assertEqual(result, {})
|
||||||
|
|
||||||
|
# expected_result = "arg1=value1 arg2=value2 arg3=value3"
|
||||||
|
# f = nova.nova_client._get_testkey
|
||||||
|
# helper.retry_check_function_return_value(f, expected_result)
|
||||||
|
|
||||||
|
# def test_rule_insert_delete(self):
|
||||||
|
# self.api['policy'].add_item({'name': 'alice'}, {})
|
||||||
|
# context = {'policy_id': 'alice'}
|
||||||
|
# (id1, _) = self.api['rule'].add_item(
|
||||||
|
# {'rule': 'p(x) :- plus(y, 1, x), q(y)'}, {}, context=context)
|
||||||
|
# ds = self.api['rule'].get_items({}, context)['results']
|
||||||
|
# self.assertEqual(len(ds), 1)
|
||||||
|
# self.api['rule'].delete_item(id1, {}, context)
|
||||||
|
# ds = self.engine.policy_object('alice').content()
|
||||||
|
# self.assertEqual(len(ds), 0)
|
||||||
|
|
||||||
|
# # TODO(thinrichs): Clean up this file. In particular, make it possible
|
||||||
|
# # to group all of the policy-execute tests into their own class.
|
||||||
|
# # Execute[...] tests
|
||||||
|
# def test_policy_execute(self):
|
||||||
|
# class NovaClient(object):
|
||||||
|
# def __init__(self, testkey):
|
||||||
|
# self.testkey = testkey
|
||||||
|
|
||||||
|
# def disconnectNetwork(self, arg1):
|
||||||
|
# LOG.info("disconnectNetwork called on %s", arg1)
|
||||||
|
# self.testkey = "arg1=%s" % arg1
|
||||||
|
|
||||||
|
# nova_client = NovaClient(None)
|
||||||
|
# nova = self.cage.service_object('nova')
|
||||||
|
# nova.nova_client = nova_client
|
||||||
|
|
||||||
|
# # insert rule and data
|
||||||
|
# self.api['policy'].add_item({'name': 'alice'}, {})
|
||||||
|
# (id1, _) = self.api['rule'].add_item(
|
||||||
|
# {'rule': 'execute[nova:disconnectNetwork(x)] :- q(x)'}, {},
|
||||||
|
# context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 0)
|
||||||
|
# (id2, _) = self.api['rule'].add_item(
|
||||||
|
# {'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 1)
|
||||||
|
# ans = "arg1=1"
|
||||||
|
# f = lambda: nova.nova_client.testkey
|
||||||
|
# helper.retry_check_function_return_value(f, ans)
|
||||||
|
|
||||||
|
# # insert more data
|
||||||
|
# self.api['rule'].add_item(
|
||||||
|
# {'rule': 'q(2)'}, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 2)
|
||||||
|
# ans = "arg1=2"
|
||||||
|
# f = lambda: nova.nova_client.testkey
|
||||||
|
# helper.retry_check_function_return_value(f, ans)
|
||||||
|
|
||||||
|
# # insert irrelevant data
|
||||||
|
# self.api['rule'].add_item(
|
||||||
|
# {'rule': 'r(3)'}, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 2)
|
||||||
|
|
||||||
|
# # delete relevant data
|
||||||
|
# self.api['rule'].delete_item(
|
||||||
|
# id2, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 2)
|
||||||
|
|
||||||
|
# # delete policy rule
|
||||||
|
# self.api['rule'].delete_item(
|
||||||
|
# id1, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 2)
|
||||||
|
|
||||||
|
# def test_policy_execute_data_first(self):
|
||||||
|
# class NovaClient(object):
|
||||||
|
# def __init__(self, testkey):
|
||||||
|
# self.testkey = testkey
|
||||||
|
|
||||||
|
# def disconnectNetwork(self, arg1):
|
||||||
|
# LOG.info("disconnectNetwork called on %s", arg1)
|
||||||
|
# self.testkey = "arg1=%s" % arg1
|
||||||
|
|
||||||
|
# nova_client = NovaClient(None)
|
||||||
|
# nova = self.cage.service_object('nova')
|
||||||
|
# nova.nova_client = nova_client
|
||||||
|
|
||||||
|
# # insert rule and data
|
||||||
|
# self.api['policy'].add_item({'name': 'alice'}, {})
|
||||||
|
# self.api['rule'].add_item(
|
||||||
|
# {'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 0)
|
||||||
|
# self.api['rule'].add_item(
|
||||||
|
# {'rule': 'execute[nova:disconnectNetwork(x)] :- q(x)'}, {},
|
||||||
|
# context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 1)
|
||||||
|
# ans = "arg1=1"
|
||||||
|
# f = lambda: nova.nova_client.testkey
|
||||||
|
# helper.retry_check_function_return_value(f, ans)
|
||||||
|
|
||||||
|
# def test_policy_execute_dotted(self):
|
||||||
|
# class NovaClient(object):
|
||||||
|
# def __init__(self, testkey):
|
||||||
|
# self.testkey = testkey
|
||||||
|
# self.servers = ServersClass()
|
||||||
|
|
||||||
|
# class ServersClass(object):
|
||||||
|
# def __init__(self):
|
||||||
|
# self.ServerManager = ServerManagerClass()
|
||||||
|
|
||||||
|
# class ServerManagerClass(object):
|
||||||
|
# def __init__(self):
|
||||||
|
# self.testkey = None
|
||||||
|
|
||||||
|
# def pause(self, id_):
|
||||||
|
# self.testkey = "arg1=%s" % id_
|
||||||
|
|
||||||
|
# nova_client = NovaClient(None)
|
||||||
|
# nova = self.cage.service_object('nova')
|
||||||
|
# nova.nova_client = nova_client
|
||||||
|
|
||||||
|
# self.api['policy'].add_item({'name': 'alice'}, {})
|
||||||
|
# self.api['rule'].add_item(
|
||||||
|
# {'rule': 'execute[nova:servers.ServerManager.pause(x)] :- q(x)'},
|
||||||
|
# {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 0)
|
||||||
|
# self.api['rule'].add_item(
|
||||||
|
# {'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 1)
|
||||||
|
# ans = "arg1=1"
|
||||||
|
# f = lambda: nova.nova_client.servers.ServerManager.testkey
|
||||||
|
# helper.retry_check_function_return_value(f, ans)
|
||||||
|
|
||||||
|
# def test_policy_execute_no_args(self):
|
||||||
|
# class NovaClient(object):
|
||||||
|
# def __init__(self, testkey):
|
||||||
|
# self.testkey = testkey
|
||||||
|
|
||||||
|
# def disconnectNetwork(self):
|
||||||
|
# LOG.info("disconnectNetwork called")
|
||||||
|
# self.testkey = "noargs"
|
||||||
|
|
||||||
|
# nova_client = NovaClient(None)
|
||||||
|
# nova = self.cage.service_object('nova')
|
||||||
|
# nova.nova_client = nova_client
|
||||||
|
|
||||||
|
# # Note: this probably isn't the behavior we really want.
|
||||||
|
# # But at least we have a test documenting that behavior.
|
||||||
|
|
||||||
|
# # insert rule and data
|
||||||
|
# self.api['policy'].add_item({'name': 'alice'}, {})
|
||||||
|
# (id1, rule1) = self.api['rule'].add_item(
|
||||||
|
# {'rule': 'execute[nova:disconnectNetwork()] :- q(x)'}, {},
|
||||||
|
# context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 0)
|
||||||
|
# (id2, rule2) = self.api['rule'].add_item(
|
||||||
|
# {'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 1)
|
||||||
|
# ans = "noargs"
|
||||||
|
# f = lambda: nova.nova_client.testkey
|
||||||
|
# helper.retry_check_function_return_value(f, ans)
|
||||||
|
|
||||||
|
# # insert more data (which DOES NOT cause an execution)
|
||||||
|
# (id3, rule3) = self.api['rule'].add_item(
|
||||||
|
# {'rule': 'q(2)'}, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 1)
|
||||||
|
|
||||||
|
# # delete all data
|
||||||
|
# self.api['rule'].delete_item(
|
||||||
|
# id2, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 1)
|
||||||
|
|
||||||
|
# self.api['rule'].delete_item(
|
||||||
|
# id3, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 1)
|
||||||
|
|
||||||
|
# # insert data (which now DOES cause an execution)
|
||||||
|
# (id4, rule3) = self.api['rule'].add_item(
|
||||||
|
# {'rule': 'q(3)'}, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 2)
|
||||||
|
# ans = "noargs"
|
||||||
|
# f = lambda: nova.nova_client.testkey
|
||||||
|
# helper.retry_check_function_return_value(f, ans)
|
||||||
|
|
||||||
|
# # delete policy rule
|
||||||
|
# self.api['rule'].delete_item(
|
||||||
|
# id1, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 2)
|
||||||
|
|
||||||
|
# def test_datasource_request_refresh(self):
|
||||||
|
# # Remember that neutron does not poll automatically here, which
|
||||||
|
# # is why this test actually testing request_refresh
|
||||||
|
# neutron = self.cage.service_object('neutron')
|
||||||
|
# LOG.info("neutron.state: %s", neutron.state)
|
||||||
|
# self.assertEqual(len(neutron.state['ports']), 0)
|
||||||
|
# # TODO(thinrichs): Seems we can't test the datasource API at all.
|
||||||
|
# # api['datasource'].request_refresh_action(
|
||||||
|
# # {}, context, helper.FakeRequest({}))
|
||||||
|
# neutron.request_refresh()
|
||||||
|
# f = lambda: len(neutron.state['ports'])
|
||||||
|
# helper.retry_check_function_return_value_not_eq(f, 0)
|
||||||
|
|
||||||
|
# def test_neutron_policy_execute(self):
|
||||||
|
# class NeutronClient(object):
|
||||||
|
# def __init__(self, testkey):
|
||||||
|
# self.testkey = testkey
|
||||||
|
|
||||||
|
# def disconnectNetwork(self, arg1):
|
||||||
|
# LOG.info("disconnectNetwork called on %s", arg1)
|
||||||
|
# self.testkey = "arg1=%s" % arg1
|
||||||
|
|
||||||
|
# neutron_client = NeutronClient(None)
|
||||||
|
# neutron = self.cage.service_object('neutron')
|
||||||
|
# neutron.neutron = neutron_client
|
||||||
|
|
||||||
|
# # insert rule and data
|
||||||
|
# self.api['policy'].add_item({'name': 'alice'}, {})
|
||||||
|
# (id1, _) = self.api['rule'].add_item(
|
||||||
|
# {'rule': 'execute[neutron:disconnectNetwork(x)] :- q(x)'}, {},
|
||||||
|
# context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 0)
|
||||||
|
# (id2, _) = self.api['rule'].add_item(
|
||||||
|
# {'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
|
||||||
|
# self.assertEqual(len(self.engine.logger.messages), 1)
|
||||||
|
# ans = "arg1=1"
|
||||||
|
# f = lambda: neutron.neutron.testkey
|
||||||
|
# helper.retry_check_function_return_value(f, ans)
|
||||||
|
|
||||||
|
# def test_datasource_api_model_execute(self):
|
||||||
|
# def _execute_api(client, action, action_args):
|
||||||
|
# positional_args = action_args.get('positional', [])
|
||||||
|
# named_args = action_args.get('named', {})
|
||||||
|
# method = reduce(getattr, action.split('.'), client)
|
||||||
|
# method(*positional_args, **named_args)
|
||||||
|
|
||||||
|
# class NovaClient(object):
|
||||||
|
# def __init__(self, testkey):
|
||||||
|
# self.testkey = testkey
|
||||||
|
|
||||||
|
# def _get_testkey(self):
|
||||||
|
# return self.testkey
|
||||||
|
|
||||||
|
# def disconnect(self, arg1, arg2, arg3):
|
||||||
|
# self.testkey = "arg1=%s arg2=%s arg3=%s" % (arg1, arg2, arg3)
|
||||||
|
|
||||||
|
# def disconnect_all(self):
|
||||||
|
# self.testkey = "action_has_no_args"
|
||||||
|
|
||||||
|
# nova_client = NovaClient("testing")
|
||||||
|
# nova = self.cage.service_object('nova')
|
||||||
|
# nova._execute_api = _execute_api
|
||||||
|
# nova.nova_client = nova_client
|
||||||
|
|
||||||
|
# execute_action = self.api['datasource'].execute_action
|
||||||
|
|
||||||
|
# # Positive test: valid body args, ds_id
|
||||||
|
# context = {'ds_id': 'nova'}
|
||||||
|
# body = {'name': 'disconnect',
|
||||||
|
# 'args': {'positional': ['value1', 'value2'],
|
||||||
|
# 'named': {'arg3': 'value3'}}}
|
||||||
|
# request = helper.FakeRequest(body)
|
||||||
|
# result = execute_action({}, context, request)
|
||||||
|
# self.assertEqual(result, {})
|
||||||
|
# expected_result = "arg1=value1 arg2=value2 arg3=value3"
|
||||||
|
# f = nova.nova_client._get_testkey
|
||||||
|
# helper.retry_check_function_return_value(f, expected_result)
|
||||||
|
|
||||||
|
# # Positive test: no body args
|
||||||
|
# context = {'ds_id': 'nova'}
|
||||||
|
# body = {'name': 'disconnect_all'}
|
||||||
|
# request = helper.FakeRequest(body)
|
||||||
|
# result = execute_action({}, context, request)
|
||||||
|
# self.assertEqual(result, {})
|
||||||
|
# expected_result = "action_has_no_args"
|
||||||
|
# f = nova.nova_client._get_testkey
|
||||||
|
# helper.retry_check_function_return_value(f, expected_result)
|
||||||
|
|
||||||
|
# # Negative test: invalid ds_id
|
||||||
|
# context = {'ds_id': 'unknown_ds'}
|
||||||
|
# self.assertRaises(webservice.DataModelException, execute_action,
|
||||||
|
# {}, context, request)
|
||||||
|
|
||||||
|
# # Negative test: no ds_id
|
||||||
|
# context = {}
|
||||||
|
# self.assertRaises(webservice.DataModelException, execute_action,
|
||||||
|
# {}, context, request)
|
||||||
|
|
||||||
|
# # Negative test: empty body
|
||||||
|
# context = {'ds_id': 'nova'}
|
||||||
|
# bad_request = helper.FakeRequest({})
|
||||||
|
# self.assertRaises(webservice.DataModelException, execute_action,
|
||||||
|
# {}, context, bad_request)
|
||||||
|
|
||||||
|
# # Negative test: no body name/action
|
||||||
|
# context = {'ds_id': 'nova'}
|
||||||
|
# body = {'args': {'positional': ['value1', 'value2'],
|
||||||
|
# 'named': {'arg3': 'value3'}}}
|
||||||
|
# bad_request = helper.FakeRequest(body)
|
||||||
|
# self.assertRaises(webservice.DataModelException, execute_action,
|
||||||
|
# {}, context, bad_request)
|
Loading…
Reference in New Issue
Block a user