Disable old DSE code

- Move tests2 to tests
- Disabled a few tests that require porting to new DSE
  and marked with TODO(dse2)
- Removed all the distributed_architecture checks
- Fixed api_driver_model and upgraded tests to dist_arch

Still need to remove dse code and deal with fallout
Still need to remove dead code from some api models
But this patchset is large enough, so save rest for
future work.

Change-Id: Ia7688b86186e321cfd349ebae1f2ec6c9068616c
This commit is contained in:
Tim Hinrichs 2016-07-13 16:46:18 -07:00
parent cc1bfd0c70
commit fd6600cda1
60 changed files with 748 additions and 3946 deletions

View File

@ -16,7 +16,6 @@ from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from oslo_config import cfg
from oslo_log import log as logging
from congress.api import webservice
@ -36,10 +35,9 @@ def create_table_dict(tablename, schema):
# Note(thread-safety): blocking function
def get_id_from_context(context, datasource_mgr=None, policy_engine=None):
if cfg.CONF.distributed_architecture:
# Note(thread-safety): blocking call
datasource_mgr = db_datasources.get_datasource_name(
context.get('ds_id'))
# Note(thread-safety): blocking call
datasource_mgr = db_datasources.get_datasource_name(
context.get('ds_id'))
if 'ds_id' in context:
return datasource_mgr, context.get('ds_id')

View File

@ -19,15 +19,13 @@ from __future__ import absolute_import
from oslo_config import cfg
from congress import exception
class APIModel(object):
"""Base Class for handling API requests."""
def __init__(self, name, keys='', inbox=None, dataPath=None,
policy_engine=None, datasource_mgr=None, bus=None):
self.dist_arch = getattr(cfg.CONF, 'distributed_architecture', False)
self.dist_arch = True
self.engine = policy_engine
if self.dist_arch:
self.engine = 'engine'
@ -38,14 +36,7 @@ class APIModel(object):
# Note(thread-safety): blocking function
def invoke_rpc(self, caller, name, kwargs, timeout=None):
if self.dist_arch:
local = (caller is self.engine and
self.bus.node.service_object(self.engine) is not None)
return self.bus.rpc(
caller, name, kwargs, timeout=timeout, local=local)
else:
func = getattr(caller, name, None)
if func:
return func(**kwargs)
raise exception.CongressException('method: %s is not defined in %s'
% (name, caller.__name__))

View File

@ -19,7 +19,6 @@ from __future__ import absolute_import
import json
from oslo_config import cfg
from oslo_log import log as logging
from congress.api import api_utils
@ -46,7 +45,7 @@ class DatasourceModel(base.APIModel):
datasource_mgr=datasource_mgr,
bus=bus)
self.synchronizer = synchronizer
self.dist_arch = getattr(cfg.CONF, 'distributed_architecture', False)
self.dist_arch = True
# Note(thread-safety): blocking function
def get_items(self, params, context=None):
@ -61,11 +60,9 @@ class DatasourceModel(base.APIModel):
a list of items in the model. Additional keys set in the
dict will also be rendered for the user.
"""
if self.dist_arch:
self.datasource_mgr = self.bus
# Note(thread-safety): blocking call
results = self.datasource_mgr.get_datasources(filter_secret=True)
results = self.bus.get_datasources(filter_secret=True)
# Check that running datasources match the datasources in the
# database since this is going to tell the client about those
@ -95,13 +92,9 @@ class DatasourceModel(base.APIModel):
"""
obj = None
try:
if self.dist_arch:
# Note(thread-safety): blocking call
obj = self.bus.add_datasource(item=item)
# Let PE synchronizer take care of creating the policy.
else:
# Note(thread-safety): blocking call
obj = self.datasource_mgr.add_datasource(item=item)
# Note(thread-safety): blocking call
obj = self.bus.add_datasource(item=item)
# Let PE synchronizer take care of creating the policy.
except (exception.BadConfig,
exception.DatasourceNameInUse,
exception.DriverNotFound,

View File

@ -35,15 +35,6 @@ def d6service(name, keys, inbox, datapath, args):
class DatasourceDriverModel(base.APIModel):
"""Model for handling API requests about DatasourceDriver."""
def __init__(self, name, keys='', inbox=None, dataPath=None,
datasource_mgr=None, bus=None):
super(DatasourceDriverModel, self).__init__(name, keys, inbox=inbox,
dataPath=dataPath, bus=bus)
self.datasource_mgr = datasource_mgr
def rpc(self, caller, name, *args, **kwargs):
f = getattr(caller, name)
return f(*args, **kwargs)
def get_items(self, params, context=None):
"""Get items in model.
@ -57,10 +48,10 @@ class DatasourceDriverModel(base.APIModel):
a list of items in the model. Additional keys set in the
dict will also be rendered for the user.
"""
drivers = self.rpc(self.datasource_mgr, 'get_drivers_info')
drivers = self.bus.get_drivers_info()
fields = ['id', 'description']
results = [self.datasource_mgr.make_datasource_dict(
driver, fields=fields)
results = [self.bus.make_datasource_dict(
drivers[driver], fields=fields)
for driver in drivers]
return {"results": results}
@ -78,10 +69,8 @@ class DatasourceDriverModel(base.APIModel):
"""
datasource = context.get('driver_id')
try:
schema = self.rpc(self.datasource_mgr, 'get_driver_schema',
datasource)
driver = self.rpc(self.datasource_mgr, 'get_driver_info',
datasource)
driver = self.bus.get_driver_info(datasource)
schema = self.bus.get_driver_schema(datasource)
except exception.DriverNotFound as e:
raise webservice.DataModelException(e.code, str(e),
http_status_code=e.code)

View File

@ -72,7 +72,7 @@ core_opts = [
cfg.BoolOpt('enable_execute_action', default=True,
help="Sets the flag to False if you don't want the congress "
"to execute actions."),
cfg.BoolOpt('distributed_architecture', default=False,
cfg.BoolOpt('distributed_architecture', default=True,
help="The flag to use congress new distributed architecture."
"Don't set it to True in L release since the new architecture "
"is under implementation."),

View File

@ -103,12 +103,11 @@ class APIServer(service.ServiceBase):
# store API, policy-engine, datasource flags; for use in start()
self.flags = kwargs
if cfg.CONF.distributed_architecture:
# TODO(masa): To support Active-Active HA with DseNode on any
# driver of oslo.messaging, make sure to use same partition_id
# among multi DseNodes sharing same message topic namespace.
self.node = dse_node.DseNode(cfg.CONF, self.name, [],
partition_id=bus_id)
# TODO(masa): To support Active-Active HA with DseNode on any
# driver of oslo.messaging, make sure to use same partition_id
# among multi DseNodes sharing same message topic namespace.
self.node = dse_node.DseNode(cfg.CONF, self.name, [],
partition_id=bus_id)
def start(self, key=None, backlog=128):
"""Run a WSGI server with the given application."""

View File

@ -17,13 +17,7 @@ from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
# Use new deepsix when appropriate
from oslo_config import cfg
if (hasattr(cfg.CONF, 'distributed_architecture')
and cfg.CONF.distributed_architecture):
from congress.dse2 import deepsix2 as deepsix
else:
from congress.dse import deepsix
from congress.dse2 import deepsix2 as deepsix
from functools import cmp_to_key
from functools import reduce
@ -1123,11 +1117,6 @@ class DataSourceDriver(deepsix.deepSix):
def request_refresh(self):
raise NotImplementedError('request_refresh() is not implemented.')
@utils.removed_in_dse2
def cleanup(self):
"""Cleanup this object in preparation for elimination."""
pass
def get_status(self):
d = {}
d['last_updated'] = str(self.last_updated_time)
@ -1242,12 +1231,7 @@ class PollingDataSourceDriver(DataSourceDriver):
else:
poll_time = 10
# a number of tests rely on polling being disabled if there's no inbox
# provided to the deepSix base class so clamp to zero here in that case
if cfg.CONF.distributed_architecture:
self.poll_time = poll_time
else:
self.poll_time = poll_time if inbox is not None else 0
self.poll_time = poll_time
self.refresh_request_queue = eventlet.Queue(maxsize=1)
self.worker_greenthread = None
@ -1286,12 +1270,6 @@ class PollingDataSourceDriver(DataSourceDriver):
self.worker_greenthread = None
self.log_info("killed worker thread")
@utils.removed_in_dse2
def cleanup(self):
"""Delete worker thread if created."""
self.stop_polling_thread()
super(PollingDataSourceDriver, self).cleanup()
def get_last_updated_time(self):
return self.last_updated_time

View File

@ -39,11 +39,6 @@ import requests
from congress.datasources import constants
from congress.datasources import datasource_driver
if (hasattr(cfg.CONF, 'distributed_architecture')
and cfg.CONF.distributed_architecture):
pass
else:
from congress.managers.datasource import DataSourceManager as ds_mgr
LOG = logging.getLogger(__name__)
@ -581,10 +576,7 @@ class PlexxiDriver(datasource_driver.PollingDataSourceDriver,
VMs that have the same name in the Plexxi table and the Nova Table.
"""
try:
if cfg.CONF.distributed_architecture:
datasources = self.node.get_datasources()
else:
datasources = ds_mgr.get_datasources()
datasources = self.node.get_datasources()
for datasource in datasources:
if datasource['driver'] == 'nova':
repeated_name_rule = ('{"rule": "RepeatedName' +

View File

@ -208,6 +208,26 @@ class DataService(object):
def delete_datasource(self, datasource):
return self.node.delete_datasource(datasource)
# Will be removed once the reference of node exists in api
# Note(thread-safety): blocking function
def get_drivers_info(self, *args):
return self.node.get_drivers_info(*args)
# Will be removed once the reference of node exists in api
# Note(thread-safety): blocking function
def get_driver_info(self, *args):
return self.node.get_driver_info(*args)
# Will be removed once the reference of node exists in api
# Note(thread-safety): blocking function
def get_driver_schema(self, *args):
return self.node.get_driver_schema(*args)
# Will be removed once the reference of node exists in api
# Note(thread-safety): blocking function
def make_datasource_dict(self, *args, **kwargs):
return self.node.make_datasource_dict(*args, **kwargs)
# Note(thread-safety): blocking function
def publish(self, table, data, use_snapshot=True):
if self.always_snapshot:

View File

@ -505,6 +505,15 @@ class DseNode(object):
raise exception.DriverNotFound(id=driver)
return driver
def get_drivers_info(self):
return self.loaded_drivers
def get_driver_schema(self, drivername):
driver = self.get_driver_info(drivername)
# Note(thread-safety): blocking call?
obj = importutils.import_class(driver['module'])
return obj.get_schema()
# Datasource CRUD. Maybe belongs in a subclass of DseNode?
# Note(thread-safety): blocking function
def get_datasource(cls, id_):

View File

@ -99,14 +99,19 @@ class CongressException(Exception):
else:
# at least get the core message out
message = self.msg_fmt
super(CongressException, self).__init__(message)
# e.message appears in 50 different places, but only works in Python2
# TODO(thinrichs): replace all e.message with str(message) and then
# remove the following 2 lines
if not hasattr(self, "message"):
self.message = self.args[0]
def format_message(self):
# NOTE(mrodden): use the first argument to the python Exception object
# which should be our full CongressException message, (see __init__)
return self.args[0]
# FIXME(thinrichs): Get rid of the ones below and instead create exception
# classes to represent the parts of the code that generated the exception,
# e.g. datasources versus policy compiler versus policy runtime.
@ -180,7 +185,7 @@ class IncompleteSchemaException(CongressException):
pass
class DataServiceError (Exception):
class DataServiceError(Exception):
pass

View File

@ -24,6 +24,7 @@ import re
import sys
from oslo_config import cfg
from oslo_log import log as logging
from congress.api import action_model
@ -37,15 +38,8 @@ 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.db import datasources as db_datasources
from congress.dse import d6cage
from congress import exception
if (hasattr(cfg.CONF, 'distributed_architecture')
and cfg.CONF.distributed_architecture):
pass
else:
from congress.managers import datasource as datasource_manager
from congress.policy_engines.agnostic import Dse2Runtime
@ -53,223 +47,6 @@ LOG = logging.getLogger(__name__)
ENGINE_SERVICE_NAME = 'engine'
def create(rootdir, config_override=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.
"""
LOG.debug("Starting Congress with rootdir=%s, config_override=%s",
rootdir, config_override)
# create message bus
cage = d6cage.d6Cage()
# read in datasource configurations
cage.config = config_override or {}
# path to congress source dir
src_path = os.path.join(rootdir, "congress")
datasource_mgr = datasource_manager.DataSourceManager()
datasource_mgr.validate_configured_drivers()
# add policy engine
engine_path = os.path.join(src_path, "policy_engines/agnostic.py")
LOG.info("main::start() engine_path: %s", engine_path)
cage.loadModule("PolicyEngine", engine_path)
cage.createservice(
name="engine",
moduleName="PolicyEngine",
description="Policy Engine (DseRuntime instance)",
args={'d6cage': cage, 'rootdir': src_path,
'log_actions_only': cfg.CONF.enable_execute_action})
engine = cage.service_object('engine')
engine.initialize_table_subscriptions()
engine.debug_mode() # should take this out for production
# add policy api
api_path = os.path.join(src_path, "api/policy_model.py")
LOG.info("main::start() api_path: %s", api_path)
cage.loadModule("API-policy", api_path)
cage.createservice(
name="api-policy",
moduleName="API-policy",
description="API-policy DSE instance",
args={'policy_engine': engine})
# add rule api
api_path = os.path.join(src_path, "api/rule_model.py")
LOG.info("main::start() api_path: %s", api_path)
cage.loadModule("API-rule", api_path)
cage.createservice(
name="api-rule",
moduleName="API-rule",
description="API-rule DSE instance",
args={'policy_engine': engine})
# add table api
api_path = os.path.join(src_path, "api/table_model.py")
LOG.info("main::start() api_path: %s", api_path)
cage.loadModule("API-table", api_path)
cage.createservice(
name="api-table",
moduleName="API-table",
description="API-table DSE instance",
args={'policy_engine': engine,
'datasource_mgr': datasource_mgr})
# add row api
api_path = os.path.join(src_path, "api/row_model.py")
LOG.info("main::start() api_path: %s", api_path)
cage.loadModule("API-row", api_path)
cage.createservice(
name="api-row",
moduleName="API-row",
description="API-row DSE instance",
args={'policy_engine': engine,
'datasource_mgr': datasource_mgr})
# add status api
api_path = os.path.join(src_path, "api/status_model.py")
LOG.info("main::start() api_path: %s", api_path)
cage.loadModule("API-status", api_path)
cage.createservice(
name="api-status",
moduleName="API-status",
description="API-status DSE instance",
args={'policy_engine': engine,
'datasource_mgr': datasource_mgr})
# add action api
api_path = os.path.join(src_path, "api/action_model.py")
LOG.info("main::start() api_path: %s", api_path)
cage.loadModule("API-action", api_path)
cage.createservice(
name="api-action",
moduleName="API-action",
description="API-action DSE instance",
args={'policy_engine': engine,
'datasource_mgr': datasource_mgr})
# add schema api
api_path = os.path.join(src_path, "api/schema_model.py")
LOG.info("main::start() api_path: %s", api_path)
cage.loadModule("API-schema", api_path)
cage.createservice(
name="api-schema",
moduleName="API-schema",
description="API-schema DSE instance",
args={'datasource_mgr': datasource_mgr})
# add path for system/datasource-drivers
api_path = os.path.join(src_path, "api/system/driver_model.py")
LOG.info("main::start() api_path: %s", api_path)
cage.loadModule("API-system", api_path)
cage.createservice(
name="api-system",
moduleName="API-system",
description="API-system DSE instance",
args={'datasource_mgr': datasource_mgr})
# Load policies from database
engine.persistent_load_policies()
# if this is the first time we are running Congress, need
# to create the default theories (which cannot be deleted)
api_policy = cage.service_object('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
# have policy-engine subscribe to api calls
# TODO(thinrichs): either have API publish everything to DSE bus and
# have policy engine subscribe to all those messages
# OR have API interact with individual components directly
# and change all tests so that the policy engine does not need to be
# subscribed to 'policy-update'
engine.subscribe('api-rule', 'policy-update',
callback=engine.receive_policy_update)
# spin up all the configured services, if we have configured them
drivers = datasource_mgr.get_datasources()
# Setup cage.config as it previously done when it was loaded
# from disk. FIXME(arosen) later!
for driver in drivers:
if not driver['enabled']:
LOG.info("module %s not enabled, skip loading", driver['name'])
continue
driver_info = datasource_mgr.get_driver_info(driver['driver'])
engine.create_policy(driver['name'], kind=base.DATASOURCE_POLICY_TYPE)
try:
cage.createservice(name=driver['name'],
moduleName=driver_info['module'],
args=driver['config'],
module_driver=True,
type_='datasource_driver',
id_=driver['id'])
except d6cage.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
service = cage.service_object(driver['name'])
engine.set_schema(driver['name'], service.get_schema())
# 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()
# 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)
# add datasource api
api_path = os.path.join(src_path, "api/datasource_model.py")
LOG.info("main::start() api_path: %s", api_path)
cage.loadModule("API-datasource", api_path)
cage.createservice(
name="api-datasource",
moduleName="API-datasource",
description="API-datasource DSE instance",
args={'policy_engine': engine,
'datasource_mgr': datasource_mgr,
'synchronizer': synchronizer})
return cage
def create2(node, policy_engine=True, datasources=True, api=True):
"""Get Congress up.

View File

@ -1,480 +0,0 @@
# Copyright (c) 2014 OpenStack Foundation
#
# 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.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import json
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_log import log as logging
from oslo_utils import importutils
from oslo_utils import uuidutils
import six
from congress.datalog import base
from congress.datasources import constants
from congress.db import api as db
from congress.db import datasources as datasources_db
from congress.dse import d6cage
from congress import exception
import sys
import traceback
LOG = logging.getLogger(__name__)
class DataServiceError (Exception):
pass
class DataSourceManager(object):
loaded_drivers = {}
dseNode = None
@classmethod
def set_dseNode(cls, dseNode):
cls.dseNode = dseNode
# Note(thread-safety): blocking function
@classmethod
def add_datasource(cls, item, deleted=False, update_db=True):
req = cls.make_datasource_dict(item)
# check the request has valid infomation
cls.validate_create_datasource(req)
# If update_db is True, new_id will get a new value from the db.
new_id = req['id']
LOG.debug("adding datasource %s", req['name'])
if update_db:
LOG.debug("updating db")
try:
# Note(thread-safety): blocking call
datasource = datasources_db.add_datasource(
id_=req['id'],
name=req['name'],
driver=req['driver'],
config=req['config'],
description=req['description'],
enabled=req['enabled'])
new_id = datasource['id']
req['id'] = new_id
except db_exc.DBDuplicateEntry:
raise exception.DatasourceNameInUse(value=req['name'])
try:
# Note(thread-safety): blocking call
cls._add_datasource_service(req, item)
except exception.DatasourceNameInUse:
LOG.info('the datasource service is created by synchronizer.')
except Exception:
if update_db:
# Note(thread-safety): blocking call
datasources_db.delete_datasource(new_id)
raise
new_item = dict(item)
new_item['id'] = new_id
return cls.make_datasource_dict(new_item)
# Ensuring only one thread can create datasource service by
# in-process level lock
# Note(thread-safety): blocking function
@classmethod
@lockutils.synchronized('create_ds_service')
def _add_datasource_service(cls, new_ds, original_req):
driver_info = cls.get_driver_info(new_ds['driver'])
cage = cls.dseNode or d6cage.d6Cage()
engine = cage.service_object('engine')
try:
LOG.debug("creating policy %s", new_ds['name'])
engine.create_policy(new_ds['name'],
kind=base.DATASOURCE_POLICY_TYPE)
except KeyError:
raise exception.DatasourceNameInUse(value=new_ds['name'])
try:
if cls.dseNode:
cls.createservice(name=new_ds['name'],
moduleName=driver_info['module'],
args=original_req['config'],
module_driver=True,
type_='datasource_driver',
id_=new_ds['id'])
else:
if not cage.service_object(new_ds['name']):
cage.createservice(name=new_ds['name'],
moduleName=driver_info['module'],
args=original_req['config'],
module_driver=True,
type_='datasource_driver',
id_=new_ds['id'])
service = cage.service_object(new_ds['name'])
engine.set_schema(new_ds['name'], service.get_schema())
except Exception:
engine.delete_policy(new_ds['name'])
raise exception.DatasourceCreationError(value=new_ds['name'])
# Note(thread-safety): blocking function?
@classmethod
def validate_configured_drivers(cls):
"""load all configured drivers and check no name conflict"""
result = {}
for driver_path in cfg.CONF.drivers:
obj = importutils.import_class(driver_path)
driver = obj.get_datasource_info()
if driver['id'] in result:
raise exception.BadConfig(_("There is a driver loaded already"
"with the driver name of %s") %
driver['id'])
driver['module'] = driver_path
result[driver['id']] = driver
cls.loaded_drivers = result
@classmethod
def make_datasource_dict(cls, req, fields=None):
result = {'id': req.get('id') or uuidutils.generate_uuid(),
'name': req.get('name'),
'driver': req.get('driver'),
'description': req.get('description'),
'type': None,
'enabled': req.get('enabled', True)}
# NOTE(arosen): we store the config as a string in the db so
# here we serialize it back when returning it.
if isinstance(req.get('config'), six.string_types):
result['config'] = json.loads(req['config'])
else:
result['config'] = req.get('config')
return cls._fields(result, fields)
@classmethod
def _fields(cls, resource, fields):
if fields:
return dict(((key, item) for key, item in resource.items()
if key in fields))
return resource
# Note(thread-safety): blocking function
@classmethod
def get_datasources(cls, filter_secret=False):
"""Return the created datasources.
This returns what datasources the database contains, not the
datasources that this server instance is running.
"""
results = []
# Note(thread-safety): blocking call
for datasouce_driver in datasources_db.get_datasources():
result = cls.make_datasource_dict(datasouce_driver)
if filter_secret:
# secret field may be not provided while creating datasource
try:
hides = cls.get_driver_info(result['driver'])['secret']
for hide_field in hides:
result['config'][hide_field] = "<hidden>"
except KeyError:
pass
results.append(result)
return results
# Note(thread-safety): blocking function
@classmethod
def get_datasource(cls, id_):
"""Return the created datasource."""
# Note(thread-safety): blocking call
result = datasources_db.get_datasource(id_)
if not result:
raise exception.DatasourceNotFound(id=id_)
return cls.make_datasource_dict(result)
@classmethod
def get_driver_info(cls, driver):
driver = cls.loaded_drivers.get(driver)
if not driver:
raise exception.DriverNotFound(id=driver)
return driver
# Note(thread-safety): blocking function?
@classmethod
def get_driver_schema(cls, datasource_id):
driver = cls.get_driver_info(datasource_id)
# Note(thread-safety): blocking call?
obj = importutils.import_class(driver['module'])
return obj.get_schema()
# Note(thread-safety): blocking function
@classmethod
def get_datasource_schema(cls, source_id):
# Note(thread-safety): blocking call
datasource = datasources_db.get_datasource(source_id)
if not datasource:
raise exception.DatasourceNotFound(id=source_id)
driver = cls.get_driver_info(datasource.driver)
if driver:
# NOTE(arosen): raises if not found
driver = cls.get_driver_info(
driver['id'])
# Note(thread-safety): blocking call?
obj = importutils.import_class(driver['module'])
return obj.get_schema()
# Note(thread-safety): blocking function
@classmethod
def load_module_object(cls, datasource_id_or_name):
# Note(thread-safety): blocking call
datasource = datasources_db.get_datasource(datasource_id_or_name)
# Ideally speaking, it should change datasource_db.get_datasource() to
# be able to retrieve datasource info from db at once. The datasource
# table and the method, however, will be removed in the new
# architecture, so it use this way. Supporting both name and id is
# a backward compatibility.
if not datasource:
# Note(thread-safety): blocking call
datasource = (datasources_db.
get_datasource_by_name(datasource_id_or_name))
if not datasource:
return None
driver = cls.get_driver_info(datasource.driver)
# Note(thread-safety): blocking call?
obj = importutils.import_class(driver['module'])
return obj
# Note(thread-safety): blocking function
@classmethod
def get_row_data(cls, table_id, source_id, **kwargs):
# Note(thread-safety): blocking call
datasource = cls.get_datasource(source_id)
# FIXME(thread-safety):
# by the time greenthread resumes, the
# returned datasource name could refer to a totally different
# datasource, causing the rest of this code to unintentionally
# operate on a different datasource
# Fix: check UUID of datasource before operating.
# Abort if mismatch
cage = cls.dseNode or d6cage.d6Cage()
datasource_obj = cage.service_object(datasource['name'])
return datasource_obj.get_row_data(table_id)
# Note(thread-safety): blocking function
@classmethod
def update_entire_data(cls, table_id, source_id, objs):
# Note(thread-safety): blocking call
datasource = cls.get_datasource(source_id)
# FIXME(thread-safety):
# by the time greenthread resumes, the
# returned datasource name could refer to a totally different
# datasource, causing the rest of this code to unintentionally
# operate on a different datasource
# Fix: check UUID of datasource before operating.
# Abort if mismatch
cage = d6cage.d6Cage()
datasource_obj = cage.service_object(datasource['name'])
return datasource_obj.update_entire_data(table_id, objs)
# Note(thread-safety): blocking function
@classmethod
def get_tablename(cls, source_id, table_id):
# Note(thread-safety): blocking call
obj = cls.load_module_object(source_id)
if obj:
return obj.get_tablename(table_id)
else:
return None
# Note(thread-safety): blocking function
@classmethod
def get_tablenames(cls, source_id):
'''The method to get datasource tablename.'''
# In the new architecture, table model would call datasource_driver's
# get_tablenames() directly using RPC
# Note(thread-safety): blocking call
obj = cls.load_module_object(source_id)
if obj:
return obj.get_tablenames()
else:
return None
# Note(thread-safety): blocking function
# FIXME(thread-safety): if synchronizer interrupts after deletion from db
# but before unregister_service, an unintended error results.
# FIX: make sure unregister succeeds even if service had already been
# urregistered.
# Note(thread-safety): watch out for potentially bad interactions when
# instances of delete_datasource and add_datasource interrupt one another
@classmethod
def delete_datasource(cls, datasource_id, update_db=True):
# Note(thread-safety): blocking call
datasource = cls.get_datasource(datasource_id)
session = db.get_session()
with session.begin(subtransactions=True):
cage = cls.dseNode or d6cage.d6Cage()
engine = cage.service_object('engine')
try:
engine.delete_policy(datasource['name'],
disallow_dangling_refs=True)
except exception.DanglingReference as e:
raise e
except KeyError:
raise exception.DatasourceNotFound(id=datasource_id)
if update_db:
# Note(thread-safety): blocking call
result = datasources_db.delete_datasource(
datasource_id, session)
if not result:
raise exception.DatasourceNotFound(id=datasource_id)
if cls.dseNode:
# Note(thread-safety): blocking call
cls.dseNode.unregister_service(
cls.dseNode.service_object(datasource['name']))
else:
cage.deleteservice(datasource['name'])
@classmethod
def get_status(cls, source_id=None, params=None):
cage = d6cage.d6Cage()
driver = cage.getservice(id_=source_id, type_='datasource_driver')
if not driver:
raise exception.NotFound('Could not find datasource %s' %
source_id)
return driver['object'].get_status()
@classmethod
def get_actions(cls, source_id=None):
cage = d6cage.d6Cage()
driver = cage.getservice(id_=source_id, type_='datasource_driver')
if not driver:
raise exception.NotFound('Could not find datasource %s' %
source_id)
return driver['object'].get_actions()
@classmethod
def get_drivers_info(cls):
return [driver for driver in cls.loaded_drivers.values()]
@classmethod
def validate_create_datasource(cls, req):
driver = req['driver']
config = req['config'] or {}
for loaded_driver in cls.loaded_drivers.values():
if loaded_driver['id'] == driver:
specified_options = set(config.keys())
valid_options = set(loaded_driver['config'].keys())
# Check that all the specified options passed in are
# valid configuration options that the driver exposes.
invalid_options = specified_options - valid_options
if invalid_options:
raise exception.InvalidDriverOption(
invalid_options=invalid_options)
# check that all the required options are passed in
required_options = set(
[k for k, v in loaded_driver['config'].items()
if v == constants.REQUIRED])
missing_options = required_options - specified_options
if missing_options:
missing_options = ', '.join(missing_options)
raise exception.MissingRequiredConfigOptions(
missing_options=missing_options)
return loaded_driver
# If we get here no datasource driver match was found.
raise exception.InvalidDriver(driver=req)
# Note(thread-safety): blocking function
@classmethod
def request_refresh(cls, source_id):
# Note(thread-safety): blocking call
datasource = cls.get_datasource(source_id)
# FIXME(thread-safety):
# by the time greenthread resumes, the
# returned datasource name could refer to a totally different
# datasource, causing the rest of this code to unintentionally
# operate on a different datasource
# Fix: check UUID of datasource before operating.
# Abort if mismatch
cage = cls.dseNode or d6cage.d6Cage()
datasource = cage.service_object(datasource['name'])
# Note(thread-safety): blocking call
datasource.request_refresh()
# Note(thread-safety): blocking function
@classmethod
def createservice(
cls,
name="",
keys="",
description="",
moduleName="",
args={},
module_driver=False,
type_=None,
id_=None):
# copied from d6cage. It's not clear where this code should reside LT
# self.log_info("creating service %s with module %s and args %s",
# name, moduleName, strutils.mask_password(args, "****"))
# FIXME(arosen) This will be refactored out in the next patchset
# this is only done because existing imports from d6service
# instead of the module.
if module_driver:
congress_expected_module_path = ""
for entry in range(len(moduleName.split(".")) - 1):
congress_expected_module_path += (
moduleName.split(".")[entry] + ".")
congress_expected_module_path = congress_expected_module_path[:-1]
module = importutils.import_module(congress_expected_module_path)
if not module_driver and moduleName not in sys.modules:
# self.log_error(
# "error loading service %s: module %s does not exist",
# name,
# moduleName)
raise exception.DataServiceError(
"error loading service %s: module %s does not exist" %
(name, moduleName))
# if not module_driver and name in self.services:
# self.log_error("error loading service '%s': name already in use",
# name)
# raise DataServiceError(
# "error loading service '%s': name already in use"
# % name)
if not module_driver:
module = sys.modules[moduleName]
try:
svcObject = module.d6service(name, keys, None, None,
args)
# Note(thread-safety): blocking call
cls.dseNode.register_service(svcObject)
except Exception:
# self.log_error(
# "Error loading service '%s' of module '%s':: \n%s",
# name, module, traceback.format_exc())
raise exception.DataServiceError(
"Error loading service '%s' of module '%s':: \n%s"
% (name, module, traceback.format_exc()))

View File

@ -21,11 +21,8 @@ import eventlet
from futurist import periodics
# Use new deepsix when appropriate
from oslo_config import cfg
if (hasattr(cfg.CONF, 'distributed_architecture')
and cfg.CONF.distributed_architecture):
from congress.dse2 import deepsix2 as deepsix
else:
from congress.dse import deepsix
from congress.dse2 import deepsix2 as deepsix
from oslo_log import log as logging
from oslo_utils import uuidutils
@ -1927,9 +1924,8 @@ class DseRuntime (Runtime, deepsix.deepSix):
# TODO(dse2): checks needed that all literals are facts
# TODO(dse2): should we support modals and other non-fact literals?
if getattr(cfg.CONF, 'distributed_architecture', False):
# convert literals to rows
newdata = [lit.argument_names() for lit in newdata]
# convert literals to rows for dse2
newdata = [lit.argument_names() for lit in newdata]
self.publish(policySubData.dataindex, newdata)
def get_snapshot(self, table_name):

View File

@ -17,13 +17,10 @@ from __future__ import absolute_import
import functools
import sys
from oslo_config import cfg
from oslo_log import log as logging
from congress.api import application
from congress.api import router
from congress import harness
from congress import utils
LOG = logging.getLogger(__name__)
@ -43,28 +40,10 @@ def fail_gracefully(f):
@fail_gracefully
def congress_app_factory(global_conf, **local_conf):
if getattr(cfg.CONF, "distributed_architecture", False):
# global_conf only accepts an iteratable value as its dict value
services = harness.create2(
node=global_conf['node'][0], # value must be iterables
policy_engine=global_conf['flags']['policy_engine'],
api=global_conf['flags']['api'],
datasources=global_conf['flags']['datasources'])
return application.ApiApplication(services['api_service'])
else:
if cfg.CONF.root_path:
root_path = cfg.CONF.root_path
else:
root_path = utils.get_root_path()
data_path = cfg.CONF.datasource_file
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()
router.APIRouterV1(api_resource_mgr, api_process_dict)
return application.ApiApplication(api_resource_mgr)
# global_conf only accepts an iteratable value as its dict value
services = harness.create2(
node=global_conf['node'][0], # value must be iterables
policy_engine=global_conf['flags']['policy_engine'],
api=global_conf['flags']['api'],
datasources=global_conf['flags']['datasources'])
return application.ApiApplication(services['api_service'])

View File

@ -33,7 +33,6 @@ from congress.datalog import compile
from congress.db import db_policy_rules
from congress.dse import d6cage
from congress.dse import deepsix
from congress.managers import datasource as datasource_manager
LOG = logging.getLogger(__name__)
@ -55,7 +54,7 @@ class Synchronizer(deepsix.deepSix):
self.last_poll_time = None
self.last_update = None
self.datasource_mgr = datasource_manager.DataSourceManager()
self.datasource_mgr = self.node
self.poller_greenthread = eventlet.spawn(self.poll_loop, poll_time)
# unfortunately LifoQueue(maxsize=1) blocks writers if the queue is
# full (or raises Full exception for non-blocking writers) so we use

View File

@ -16,6 +16,7 @@ from futurist import periodics
import mock
from oslo_config import cfg
from congress.common import config
from congress import harness
from congress.tests import fake_datasource
from congress.tests import helper
@ -28,7 +29,7 @@ def setup_config(with_fake_datasource=True, node_id='testnode',
:param services is an array of DataServices
:param api is a dictionary mapping api name to API model instance
"""
config.set_config_defaults()
cfg.CONF.set_override('distributed_architecture', True)
# Load the fake driver.
cfg.CONF.set_override(

View File

@ -17,46 +17,20 @@ from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from oslo_config import cfg
from congress.tests import base
from congress.api import action_model
from congress.api import webservice
from congress import harness
from congress.managers import datasource as datasource_manager
from congress.tests import helper
from congress.tests.api import base as api_base
from congress.tests import base
class TestActionModel(base.SqlTestCase):
def setUp(self):
super(TestActionModel, self).setUp()
# Here we load the fake driver
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
# NOTE(arosen): this set of tests, tests to deeply. We don't have
# any tests currently testing cage. Once we do we should mock out
# cage so we don't have to create one here.
self.cage = harness.create(helper.root_path())
self.datasource_mgr = datasource_manager.DataSourceManager
self.datasource_mgr.validate_configured_drivers()
req = {'driver': 'fake_datasource',
'name': 'fake_datasource'}
req['config'] = {'auth_url': 'foo',
'username': 'foo',
'password': 'password',
'tenant_name': 'foo'}
self.datasource = self.datasource_mgr.add_datasource(req)
engine = self.cage.service_object('engine')
self.action_model = action_model.ActionsModel(
"action_schema", {}, policy_engine=engine,
datasource_mgr=self.datasource_mgr)
services = api_base.setup_config()
self.action_model = services['api']['api-action']
self.datasource = services['data']
def test_get_datasource_actions(self):
context = {'ds_id': self.datasource['id']}
context = {'ds_id': self.datasource.service_id}
actions = self.action_model.get_items({}, context=context)
expected_ret = {'results': [{'name': 'fake_act',
'args': [{'name': 'server_id',

View File

@ -22,7 +22,7 @@ from congress.api import webservice
from congress.tests import base
class TestAPIUtils(base.TestCase):
class TestAPIUtils(base.SqlTestCase):
def setUp(self):
super(TestAPIUtils, self).setUp()
@ -39,7 +39,7 @@ class TestAPIUtils(base.TestCase):
def test_get_id_from_context_ds_id(self):
context = {'ds_id': 'datasource id'}
expected = ('datasource-mgr', 'datasource id')
expected = ('datasource id', 'datasource id')
result = api_utils.get_id_from_context(context,
'datasource-mgr',
'policy-engine')

View File

@ -18,6 +18,7 @@ from __future__ import division
from __future__ import absolute_import
import mock
from six.moves import reduce
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
@ -25,9 +26,9 @@ cfg.CONF.distributed_architecture = True
from congress.api import webservice
from congress.datasources import nova_driver
from congress import exception
from congress.tests.api import base as api_base
from congress.tests import base
from congress.tests import helper
from congress.tests2.api import base as api_base
class TestDatasourceModel(base.SqlTestCase):

View File

@ -17,38 +17,28 @@ from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from oslo_config import cfg
from congress.api.system import driver_model
from congress.api import webservice
from congress import harness
from congress.managers import datasource as datasource_manager
from congress.tests.api import base as api_base
from congress.tests import base
from congress.tests import helper
class TestDriverModel(base.SqlTestCase):
def setUp(self):
super(TestDriverModel, self).setUp()
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
services = api_base.setup_config()
self.node = services['node']
self.cage = harness.create(helper.root_path())
self.datasource_mgr = datasource_manager.DataSourceManager
self.datasource_mgr.validate_configured_drivers()
self.node.add_datasource(self._get_datasource_request())
self.driver_model = services['api']['api-system']
def _get_datasource_request(self):
req = {'driver': 'fake_datasource',
'name': 'fake_datasource'}
req['config'] = {'auth_url': 'foo',
'username': 'foo',
'password': 'password',
'tenant_name': 'foo'}
self.datasource = self.datasource_mgr.add_datasource(req)
self.api_system = self.cage.service_object('api-system')
self.driver_model = (
driver_model.DatasourceDriverModel(
"driver-model", {}, datasource_mgr=self.datasource_mgr)
)
return req
def tearDown(self):
super(TestDriverModel, self).tearDown()

View File

@ -17,15 +17,16 @@ from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import mock
# set test to run as distributed arch
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
import mock
from oslo_utils import uuidutils
from congress.api import error_codes
from congress.api import policy_model
from congress.api import webservice
from congress.dse2 import dse_node
from congress import harness
from congress.tests.api import base as api_base
from congress.tests import base
from congress.tests import helper
@ -33,44 +34,15 @@ from congress.tests import helper
class TestPolicyModel(base.SqlTestCase):
def setUp(self):
super(TestPolicyModel, self).setUp()
# Here we load the fake driver
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
self.engine, self.rule_api, self.policy_model = self.create_services()
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.rule_api = services['api']['api-rule']
self.node = services['node']
self.engine = services['engine']
self.initial_policies = set(self.engine.policy_names())
self._add_test_policy()
def create_services(self):
if cfg.CONF.distributed_architecture:
messaging_config = helper.generate_messaging_config()
# TODO(masa): following initializing DseNode will be just a fake
# until API model and Policy Engine support rpc. After these
# support rpc, pub/sub and so on, replaced with each class.
engine = dse_node.DseNode(messaging_config, 'engine', [])
rule_api = dse_node.DseNode(messaging_config, 'api-rule', [])
policy_api = dse_node.DseNode(messaging_config,
'policy_model', [])
for n in (engine, rule_api, policy_api):
n.start()
else:
cage = harness.create(helper.root_path())
engine = cage.service_object('engine')
rule_api = cage.service_object('api-rule')
policy_api = policy_model.PolicyModel("policy_model", {},
policy_engine=engine)
return engine, rule_api, policy_api
def tearDown(self):
super(TestPolicyModel, self).tearDown()
if cfg.CONF.distributed_architecture:
for n in (self.engine, self.rule_api, self.policy_model):
n.stop()
n.wait()
def _add_test_policy(self):
test_policy = {
"name": "test-policy",
@ -78,7 +50,6 @@ class TestPolicyModel(base.SqlTestCase):
"kind": "nonrecursive",
"abbreviation": "abbr"
}
test_policy_id, obj = self.policy_model.add_item(test_policy, {})
test_policy["id"] = test_policy_id
test_policy["owner_id"] = obj["owner_id"]
@ -89,7 +60,6 @@ class TestPolicyModel(base.SqlTestCase):
"kind": "nonrecursive",
"abbreviation": "abbr2"
}
test_policy_id, obj = self.policy_model.add_item(test_policy2, {})
test_policy2["id"] = test_policy_id
test_policy2["owner_id"] = obj["owner_id"]
@ -97,6 +67,9 @@ class TestPolicyModel(base.SqlTestCase):
self.policy = test_policy
self.policy2 = test_policy2
action_policy = self.policy_model.get_item('action', {})
self.action_policy = action_policy
def test_in_mem_and_db_policies(self):
ret = self.policy_model.get_items({})
db = [p['name'] for p in ret['results']]
@ -198,7 +171,7 @@ class TestPolicyModel(base.SqlTestCase):
def test_simulate_action(self):
context = {
'policy_id': self.engine.ACTION_THEORY
'policy_id': self.action_policy['name']
}
action_rule1 = {
'rule': 'action("q")',
@ -211,7 +184,7 @@ class TestPolicyModel(base.SqlTestCase):
request_body = {
'query': 'p(x)',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
@ -226,7 +199,7 @@ class TestPolicyModel(base.SqlTestCase):
def test_simulate_with_delta(self):
context = {
'policy_id': self.engine.ACTION_THEORY
'policy_id': self.action_policy['name']
}
action_rule1 = {
'rule': 'action("q")',
@ -239,7 +212,7 @@ class TestPolicyModel(base.SqlTestCase):
request_body = {
'query': 'p(x)',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
@ -257,7 +230,7 @@ class TestPolicyModel(base.SqlTestCase):
def test_simulate_with_trace(self):
context = {
'policy_id': self.engine.ACTION_THEORY
'policy_id': self.action_policy['name']
}
action_rule1 = {
'rule': 'action("q")',
@ -270,7 +243,7 @@ class TestPolicyModel(base.SqlTestCase):
request_body = {
'query': 'p(x)',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
@ -292,7 +265,7 @@ class TestPolicyModel(base.SqlTestCase):
def test_simulate_with_delta_and_trace(self):
context = {
'policy_id': self.engine.ACTION_THEORY
'policy_id': self.action_policy['name']
}
action_rule1 = {
'rule': 'action("q")',
@ -305,7 +278,7 @@ class TestPolicyModel(base.SqlTestCase):
request_body = {
'query': 'p(x)',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
@ -332,7 +305,7 @@ class TestPolicyModel(base.SqlTestCase):
}
request_body = {
'query': 'p(x)',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
@ -343,7 +316,7 @@ class TestPolicyModel(base.SqlTestCase):
def test_simulate_invalid_sequence(self):
context = {
'policy_id': self.engine.ACTION_THEORY
'policy_id': self.action_policy['name']
}
action_rule = {
'rule': 'w(x):-z(x)',
@ -352,7 +325,7 @@ class TestPolicyModel(base.SqlTestCase):
request_body = {
'query': 'w(x)',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'z(1)'
}
request = helper.FakeRequest(request_body)
@ -370,25 +343,25 @@ class TestPolicyModel(base.SqlTestCase):
self.assertIn(emsg, str(e))
context = {
'policy_id': self.engine.ACTION_THEORY
'policy_id': self.action_policy['name']
}
# Missing query
body = {'action_policy': self.engine.ACTION_THEORY,
body = {'action_policy': self.action_policy['name'],
'sequence': 'q(1)'}
check_err({}, context, helper.FakeRequest(body),
'Simulate requires parameters')
# Invalid query
body = {'query': 'p(x',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'}
check_err({}, context, helper.FakeRequest(body),
'Parse failure')
# Multiple querys
body = {'query': 'p(x) q(x)',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'}
check_err({}, context, helper.FakeRequest(body),
'more than 1 rule')
@ -401,20 +374,20 @@ class TestPolicyModel(base.SqlTestCase):
# Missing sequence
body = {'query': 'p(x)',
'action_policy': self.engine.ACTION_THEORY}
'action_policy': self.action_policy['name']}
check_err({}, context, helper.FakeRequest(body),
'Simulate requires parameters')
# Syntactically invalid sequence
body = {'query': 'p(x)',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'q(1'}
check_err({}, context, helper.FakeRequest(body),
'Parse failure')
# Semantically invalid sequence
body = {'query': 'p(x)',
'action_policy': self.engine.ACTION_THEORY,
'action_policy': self.action_policy['name'],
'sequence': 'r(1)'} # r is not an action
check_err({}, context, helper.FakeRequest(body),
'non-action, non-update')

View File

@ -17,58 +17,35 @@ from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import mock
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.api import row_model
from congress.api import webservice
from congress import harness
from congress.managers import datasource as datasource_manager
from congress.tests.api import base as api_base
from congress.tests import base
from congress.tests import helper
class TestRowModel(base.SqlTestCase):
def setUp(self):
super(TestRowModel, self).setUp()
# Here we load the fake driver
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.rule_model = services['api']['api-rule']
self.row_model = services['api']['api-row']
self.node = services['node']
self.data = services['data']
self.cage = harness.create(helper.root_path())
self.datasource_mgr = datasource_manager.DataSourceManager
self.datasource_mgr.validate_configured_drivers()
req = {'driver': 'fake_datasource',
'name': 'fake_datasource'}
req['config'] = {'auth_url': 'foo',
'username': 'foo',
'password': 'password',
'tenant_name': 'foo'}
self.datasource_mgr.add_datasource(req)
self.datasource = self.cage.getservice(name='fake_datasource',
type_='datasource_driver')
self.engine = self.cage.service_object('engine')
self.api_rule = self.cage.service_object('api-rule')
self.policy_model = self.cage.service_object('api-policy')
self.row_model = row_model.RowModel(
"row_model", {},
policy_engine=self.engine,
datasource_mgr=self.datasource_mgr)
def test_get_items_datasource_row(self):
# adjust datasource to have required value
row = ('data1', 'data2')
self.data.state['fake_table'] = set([row])
def tearDown(self):
super(TestRowModel, self).tearDown()
@mock.patch.object(datasource_manager.DataSourceManager,
'get_row_data')
def test_get_items_datasource_row(self, row_mock):
context = {'ds_id': self.datasource['id'],
# check result
context = {'ds_id': self.data.service_id,
'table_id': 'fake_table'}
data = [{'data': ('data1', 'data2')}]
row_mock.return_value = data
data = [{'data': row}]
expected_ret = {'results': data}
ret = self.row_model.get_items({}, context)
self.assertEqual(expected_ret, ret)
@ -79,7 +56,7 @@ class TestRowModel(base.SqlTestCase):
self.row_model.get_items, {}, context)
def test_get_items_invalid_ds_table_name(self):
context = {'ds_id': self.datasource['id'],
context = {'ds_id': self.data.service_id,
'table_id': 'invalid-table'}
self.assertRaises(webservice.DataModelException,
self.row_model.get_items, {}, context)
@ -92,8 +69,8 @@ class TestRowModel(base.SqlTestCase):
# insert rules
context = {'policy_id': policyname,
'table_id': 'p'}
self.api_rule.add_item({'rule': 'p("x"):- true'}, {},
context=context)
self.rule_model.add_item({'rule': 'p("x"):- true'}, {},
context=context)
# check results
row = ('x',)
@ -101,6 +78,14 @@ class TestRowModel(base.SqlTestCase):
ret = self.row_model.get_items({}, context)
self.assertEqual({'results': data}, ret)
# Enable trace and check
ret = self.row_model.get_items({'trace': 'true'}, context=context)
s = frozenset([tuple(x['data']) for x in ret['results']])
t = frozenset([('x',)])
self.assertEqual(s, t, "Rows with tracing")
self.assertTrue('trace' in ret, "Rows should have trace")
self.assertEqual(len(ret['trace'].split('\n')), 9)
def test_get_items_invalid_policy_name(self):
context = {'policy_id': 'invalid-policy',
'table_id': 'p'}
@ -109,14 +94,18 @@ class TestRowModel(base.SqlTestCase):
self.row_model.get_items, {}, context)
def test_get_items_invalid_policy_table_name(self):
context = {'policy_id': self.engine.DEFAULT_THEORY,
# create policy
policyname = 'test-policy'
self.policy_model.add_item({"name": policyname}, {})
context = {'policy_id': policyname,
'table_id': 'invalid-table'}
self.assertRaises(webservice.DataModelException,
self.row_model.get_items, {}, context)
def test_update_items(self):
context = {'ds_id': self.datasource['id'],
context = {'ds_id': self.data.service_id,
'table_id': 'fake_table'}
objs = [
{"id": 'id-1', "name": 'name-1'},
@ -125,14 +114,14 @@ class TestRowModel(base.SqlTestCase):
expected_state = (('id-1', 'name-1'), ('id-2', 'name-2'))
self.row_model.update_items(objs, {}, context=context)
table_row = self.datasource['object'].state['fake_table']
table_row = self.data.state['fake_table']
self.assertEqual(len(expected_state), len(table_row))
for row in expected_state:
self.assertTrue(row in table_row)
def test_update_items_invalid_table(self):
context = {'ds_id': self.datasource['id'],
context = {'ds_id': self.data.service_id,
'table_id': 'invalid-table'}
objs = [
{"id": 'id-1', "name": 'name-1'},

View File

@ -17,35 +17,29 @@ from __future__ import division
from __future__ import absolute_import
import mock
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.api import rule_model
from congress.api import webservice
from congress.datalog import base as datalogbase
from congress.datalog import compile
from congress import harness
from congress.tests.api import base as api_base
from congress.tests import base
from congress.tests import helper
class TestRuleModel(base.SqlTestCase):
def setUp(self):
super(TestRuleModel, self).setUp()
# Here we load the fake driver
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
self.cage = harness.create(helper.root_path())
self.engine = self.cage.service_object('engine')
self.rule_model = rule_model.RuleModel("rule_model", {},
policy_engine=self.engine)
self.context = {'policy_id': 'action'}
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.rule_model = services['api']['api-rule']
self.node = services['node']
self.action_policy = self.policy_model.get_item('action', {})
self.context = {'policy_id': self.action_policy["name"]}
self._add_test_rule()
def tearDown(self):
super(TestRuleModel, self).tearDown()
def _add_test_rule(self):
test_rule1 = {
"rule": "p(x) :- q(x)",
@ -75,28 +69,34 @@ class TestRuleModel(base.SqlTestCase):
self.rule_model.add_item,
test_rule, {})
def test_add_rule_with_colrefs(self):
engine = self.engine
engine.create_policy('beta', kind=datalogbase.DATASOURCE_POLICY_TYPE)
engine.set_schema(
'beta', compile.Schema({'q': ("name", "status", "year")}))
# insert/retrieve rule with column references
# just testing that no errors are thrown--correctness tested elsewhere
# Assuming that api-models are pass-throughs to functionality
(id1, _) = self.rule_model.add_item(
{'rule': 'p(x) :- beta:q(name=x)'},
{}, context=self.context)
self.rule_model.get_item(id1, {}, context=self.context)
# TODO(dse2): Fix this test; it must create a 'beta' service on the dse
# so that when it subscribes, the snapshot can be returned.
# Or fix the subscribe() implementation so that we can subscribe before
# the service has been created.
# def test_add_rule_with_colrefs(self):
# engine = self.engine
# engine.create_policy('beta', kind=datalogbase.DATASOURCE_POLICY_TYPE)
# engine.set_schema(
# 'beta', compile.Schema({'q': ("name", "status", "year")}))
# # insert/retrieve rule with column references
# # just testing that no errors are thrown--correctness elsewhere
# # Assuming that api-models are pass-throughs to functionality
# (id1, _) = self.rule_model.add_item(
# {'rule': 'p(x) :- beta:q(name=x)'},
# {}, context=self.context)
# self.rule_model.get_item(id1, {}, context=self.context)
def test_add_rule_with_bad_colrefs(self):
engine = self.engine
engine.create_policy('beta') # not datasource policy
# exception because col refs over non-datasource policy
self.assertRaises(
webservice.DataModelException,
self.rule_model.add_item,
{'rule': 'p(x) :- beta:q(name=x)'},
{}, context=self.context)
# def test_add_rule_with_bad_colrefs(self):
# engine = self.engine
# engine.create_policy('beta') # not datasource policy
# # insert/retrieve rule with column references
# # just testing that no errors are thrown--correctness elsewhere
# # Assuming that api-models are pass-throughs to functionality
# self.assertRaises(
# webservice.DataModelException,
# self.rule_model.add_item,
# {'rule': 'p(x) :- beta:q(name=x)'},
# {}, context=self.context)
def test_get_items(self):
ret = self.rule_model.get_items({}, context=self.context)
@ -134,19 +134,21 @@ class TestRuleModel(base.SqlTestCase):
error messages.
"""
# lexer error
with self.assertRaisesRegexp(
with self.assertRaisesRegex(
webservice.DataModelException,
"Lex failure"):
self.rule_model.add_item({'rule': 'p#'}, {}, context=self.context)
self.rule_model.add_item({'rule': 'p#'}, {},
context=self.context)
# parser error
with self.assertRaisesRegexp(
with self.assertRaisesRegex(
webservice.DataModelException,
"Parse failure"):
self.rule_model.add_item({'rule': 'p('}, {}, context=self.context)
self.rule_model.add_item({'rule': 'p('}, {},
context=self.context)
# single-rule error: safety in the head
with self.assertRaisesRegexp(
with self.assertRaisesRegex(
webservice.DataModelException,
"Variable x found in head but not in body"):
# TODO(ramineni):check for action
@ -157,17 +159,17 @@ class TestRuleModel(base.SqlTestCase):
# multi-rule error: recursion through negation
self.rule_model.add_item({'rule': 'p(x) :- q(x), not r(x)'}, {},
context=self.context)
with self.assertRaisesRegexp(
with self.assertRaisesRegex(
webservice.DataModelException,
"Rules are recursive"):
self.rule_model.add_item({'rule': 'r(x) :- q(x), not p(x)'}, {},
context=self.context)
self.rule_model.add_item({'rule': 'p(x) :- q(x)'}, {},
self.rule_model.add_item({'rule': 'p1(x) :- q1(x)'}, {},
context=self.context)
# duplicate rules
with self.assertRaisesRegexp(
with self.assertRaisesRegex(
webservice.DataModelException,
"Rule already exists"):
self.rule_model.add_item({'rule': 'p(x) :- q(x)'}, {},
self.rule_model.add_item({'rule': 'p1(x) :- q1(x)'}, {},
context=self.context)

View File

@ -17,76 +17,53 @@ from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import mock
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.api import api_utils
from congress.api import schema_model
from congress.api import webservice
from congress import exception
from congress.managers import datasource as datasource_manager
from congress.tests.api import base as api_base
from congress.tests import base
from congress.tests import fake_datasource
class TestSchemaModel(base.TestCase):
def setUp(self):
super(TestSchemaModel, self).setUp()
# Here we load the fake driver and test the schema functions with it.
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
ds_mgr = datasource_manager.DataSourceManager()
self.schema_model = schema_model.SchemaModel("test_schema", {},
datasource_mgr=ds_mgr)
services = api_base.setup_config()
self.schema_model = services['api']['api-schema']
self.data = services['data']
def test_get_item_all_table(self):
context = {'ds_id': 'fake_datasource'}
schema = fake_datasource.FakeDataSource.get_schema()
context = {'ds_id': self.data.service_id}
schema = self.data.get_schema()
fake_tables = {'tables':
[api_utils.create_table_dict(
table_, schema) for table_ in schema]}
with mock.patch.object(self.schema_model.datasource_mgr,
"get_datasource_schema",
return_value=schema):
tables = self.schema_model.get_item(None, {}, context=context)
self.assertEqual(fake_tables, tables)
tables = self.schema_model.get_item(None, {}, context=context)
self.assertEqual(fake_tables, tables)
def test_get_item_table(self):
context = {'ds_id': 'fake_datasource', 'table_id': 'fake_table'}
fake_schema = fake_datasource.FakeDataSource.get_schema()
context = {'ds_id': self.data.service_id, 'table_id': 'fake_table'}
fake_schema = self.data.get_schema()
fake_table = api_utils.create_table_dict(
"fake_table", fake_schema)
table = self.schema_model.get_item(None, {}, context=context)
self.assertEqual(fake_table, table)
with mock.patch.object(self.schema_model.datasource_mgr,
"get_datasource_schema",
return_value=fake_schema):
table = self.schema_model.get_item(None, {}, context=context)
self.assertEqual(fake_table, table)
def test_get_invalid_datasource_table(self):
context = {'ds_id': self.data.service_id, 'table_id': 'invalid_table'}
try:
self.schema_model.get_item(None, {}, context=context)
except webservice.DataModelException as e:
self.assertEqual(404, e.error_code)
else:
raise Exception("Should not get here")
def test_get_invalid_datasource(self):
context = {'ds_id': 'invalid'}
with mock.patch.object(
self.schema_model.datasource_mgr,
"get_datasource_schema",
side_effect=exception.DatasourceNotFound('invalid')
):
try:
self.schema_model.get_item(None, {}, context=context)
except webservice.DataModelException as e:
self.assertEqual(404, e.error_code)
else:
raise Exception("Should not get here")
def test_get_invalid_datasource_table(self):
context = {'ds_id': 'fake_datasource', 'table_id': 'invalid_table'}
fake_schema = fake_datasource.FakeDataSource.get_schema()
with mock.patch.object(self.schema_model.datasource_mgr,
"get_datasource_schema",
return_value=fake_schema):
try:
self.schema_model.get_item(None, {}, context=context)
except webservice.DataModelException as e:
self.assertEqual(404, e.error_code)
else:
raise Exception("Should not get here")
try:
self.schema_model.get_item(None, {}, context=context)
except webservice.DataModelException as e:
self.assertEqual(404, e.error_code)
else:
raise Exception("Should not get here")

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015 OpenStack Foundation
# Copyright (c) 2016 NTT
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -20,44 +20,25 @@ from __future__ import absolute_import
import uuid
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.api import status_model as st_model
from congress.api import webservice
from congress import harness
from congress.managers import datasource as datasource_manager
from congress.tests.api import base as api_base
from congress.tests import base
from congress.tests import helper
class TestStatusModel(base.SqlTestCase):
def setUp(self):
super(TestStatusModel, self).setUp()
# Here we load the fake driver
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
# NOTE(arosen): this set of tests, tests to deeply. We don't have
# any tests currently testing cage. Once we do we should mock out
# cage so we don't have to create one here.
self.cage = harness.create(helper.root_path())
self.ds_mgr = datasource_manager.DataSourceManager
self.ds_mgr.validate_configured_drivers()
req = {'driver': 'fake_datasource',
'name': 'fake_datasource'}
req['config'] = {'auth_url': 'foo',
'username': 'foo',
'password': 'password',
'tenant_name': 'foo'}
self.datasource = self.ds_mgr.add_datasource(req)
engine = self.cage.service_object('engine')
self.status_model = st_model.StatusModel("status_schema", {},
policy_engine=engine,
datasource_mgr=self.ds_mgr)
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.rule_model = services['api']['api-rule']
self.status_model = services['api']['api-status']
self.node = services['node']
self.datasource = services['data']
def test_get_datasource_status(self):
context = {'ds_id': self.datasource['id']}
context = {'ds_id': self.datasource.service_id}
status = self.status_model.get_item(None, {}, context=context)
expected_status_keys = ['last_updated', 'subscriptions',
'last_error', 'subscribers',
@ -71,8 +52,7 @@ class TestStatusModel(base.SqlTestCase):
context=context)
def test_policy_id_status(self):
policy_model = self.cage.getservice(name='api-policy')['object']
result = policy_model.add_item({'name': 'test_policy'}, {})
result = self.policy_model.add_item({'name': 'test_policy'}, {})
context = {'policy_id': result[0]}
status = self.status_model.get_item(None, {}, context=context)
@ -93,15 +73,13 @@ class TestStatusModel(base.SqlTestCase):
context=context)
def test_rule_status_policy_id(self):
policy_model = self.cage.getservice(name='api-policy')['object']
result = policy_model.add_item({'name': 'test_policy'}, {})
result = self.policy_model.add_item({'name': 'test_policy'}, {})
policy_id = result[0]
policy_name = result[1]['name']
rule_model = self.cage.getservice(name='api-rule')['object']
result = rule_model.add_item({'name': 'test_rule',
'rule': 'p(x) :- q(x)'}, {},
context={'policy_id': 'test_policy'})
result = self.rule_model.add_item({'name': 'test_rule',
'rule': 'p(x) :- q(x)'}, {},
context={'policy_id': 'test_policy'})
context = {'policy_id': policy_id, 'rule_id': result[0]}
status = self.status_model.get_item(None, {}, context=context)
@ -121,8 +99,7 @@ class TestStatusModel(base.SqlTestCase):
self.assertEqual(expected_status, status)
def test_rule_status_invalid_rule_policy_id(self):
policy_model = self.cage.getservice(name='api-policy')['object']
result = policy_model.add_item({'name': 'test_policy'}, {})
result = self.policy_model.add_item({'name': 'test_policy'}, {})
policy_id = result[0]
invalid_rule = uuid.uuid4()

View File

@ -18,54 +18,39 @@ from __future__ import division
from __future__ import absolute_import
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.api import table_model
from congress import harness
from congress.managers import datasource as datasource_manager
from congress.api import webservice
from congress.tests.api import base as api_base
from congress.tests import base
from congress.tests import helper
class TestTableModel(base.SqlTestCase):
def setUp(self):
super(TestTableModel, self).setUp()
# Here we load the fake driver
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.table_model = services['api']['api-table']
self.api_rule = services['api']['api-rule']
self.node = services['node']
self.engine = services['engine']
self.data = services['data']
# create test policy
self._create_test_policy()
# NOTE(masa): this set of tests, tests to deeply. We don't have
# any tests currently testing cage. Once we do we should mock out
# cage so we don't have to create one here.
self.cage = harness.create(helper.root_path())
self.ds_mgr = datasource_manager.DataSourceManager
self.ds_mgr.validate_configured_drivers()
req = {'driver': 'fake_datasource',
'name': 'fake_datasource'}
req['config'] = {'auth_url': 'foo',
'username': 'foo',
'password': 'password',
'tenant_name': 'foo'}
self.datasource = self.ds_mgr.add_datasource(req)
self.engine = self.cage.service_object('engine')
self.api_rule = self.cage.service_object('api-rule')
self.table_model = table_model.TableModel("table_model", {},
policy_engine=self.engine,
datasource_mgr=self.ds_mgr)
def tearDown(self):
super(TestTableModel, self).tearDown()
def _create_test_policy(self):
# create policy
self.policy_model.add_item({"name": 'test-policy'}, {})
def test_get_datasource_table_with_id(self):
context = {'ds_id': self.datasource['id'],
context = {'ds_id': self.data.service_id,
'table_id': 'fake_table'}
expected_ret = {'id': 'fake_table'}
ret = self.table_model.get_item('fake_table', {}, context)
self.assertEqual(expected_ret, ret)
def test_get_datasource_table_with_name(self):
context = {'ds_id': self.datasource['name'],
context = {'ds_id': self.data.service_id,
'table_id': 'fake_table'}
expected_ret = {'id': 'fake_table'}
ret = self.table_model.get_item('fake_table', {}, context)
@ -74,20 +59,19 @@ class TestTableModel(base.SqlTestCase):
def test_get_invalid_datasource(self):
context = {'ds_id': 'invalid-id',
'table_id': 'fake_table'}
expected_ret = None
ret = self.table_model.get_item('fake_table', {}, context)
self.assertEqual(expected_ret, ret)
self.assertRaises(webservice.DataModelException,
self.table_model.get_item, 'fake_table',
{}, context)
def test_get_invalid_datasource_table(self):
context = {'ds_id': self.datasource['id'],
context = {'ds_id': self.data.service_id,
'table_id': 'invalid-table'}
expected_ret = None
ret = self.table_model.get_item('invalid-table', {}, context)
self.assertEqual(expected_ret, ret)
def test_get_policy_table(self):
context = {'policy_id': self.engine.DEFAULT_THEORY,
context = {'policy_id': 'test-policy',
'table_id': 'p'}
expected_ret = {'id': 'p'}
@ -98,7 +82,7 @@ class TestTableModel(base.SqlTestCase):
self.assertEqual(expected_ret, ret)
def test_get_invalid_policy(self):
context = {'policy_id': self.engine.DEFAULT_THEORY,
context = {'policy_id': 'test-policy',
'table_id': 'fake-table'}
invalid_context = {'policy_id': 'invalid-policy',
'table_id': 'fake-table'}
@ -107,26 +91,26 @@ class TestTableModel(base.SqlTestCase):
self.api_rule.add_item({'rule': 'p(x) :- q(x)'}, {}, context=context)
self.api_rule.add_item({'rule': 'q(x) :- r(x)'}, {}, context=context)
ret = self.table_model.get_item(self.engine.DEFAULT_THEORY,
ret = self.table_model.get_item('test-policy',
{}, invalid_context)
self.assertEqual(expected_ret, ret)
def test_get_invalid_policy_table(self):
context = {'policy_id': self.engine.DEFAULT_THEORY,
context = {'policy_id': 'test-policy',
'table_id': 'fake-table'}
invalid_context = {'policy_id': self.engine.DEFAULT_THEORY,
invalid_context = {'policy_id': 'test-policy',
'table_id': 'invalid-name'}
expected_ret = None
self.api_rule.add_item({'rule': 'p(x) :- q(x)'}, {}, context=context)
self.api_rule.add_item({'rule': 'q(x) :- r(x)'}, {}, context=context)
ret = self.table_model.get_item(self.engine.DEFAULT_THEORY, {},
ret = self.table_model.get_item('test-policy', {},
invalid_context)
self.assertEqual(expected_ret, ret)
def test_get_items_datasource_table(self):
context = {'ds_id': self.datasource['id']}
context = {'ds_id': self.data.service_id}
expected_ret = {'results': [{'id': 'fake_table'}]}
ret = self.table_model.get_items({}, context)
@ -136,14 +120,14 @@ class TestTableModel(base.SqlTestCase):
context = {'ds_id': 'invalid-id',
'table_id': 'fake-table'}
ret = self.table_model.get_items({}, context)
self.assertIsNone(ret)
self.assertRaises(webservice.DataModelException,
self.table_model.get_items, {}, context)
def _get_id_list_from_return(self, result):
return [r['id'] for r in result['results']]
def test_get_items_policy_table(self):
context = {'policy_id': self.engine.DEFAULT_THEORY}
context = {'policy_id': 'test-policy'}
expected_ret = {'results': [{'id': x} for x in ['q', 'p', 'r']]}
self.api_rule.add_item({'rule': 'p(x) :- q(x)'}, {}, context=context)
@ -154,7 +138,7 @@ class TestTableModel(base.SqlTestCase):
set(self._get_id_list_from_return(ret)))
def test_get_items_invalid_policy(self):
context = {'policy_id': self.engine.DEFAULT_THEORY}
context = {'policy_id': 'test-policy'}
invalid_context = {'policy_id': 'invalid-policy'}
expected_ret = None

View File

@ -50,14 +50,24 @@ class PerformanceTestDriver(datasource_driver.PollingDataSourceDriver):
{'fieldname': 'field6', 'translator': value_trans})}
def __init__(self, name='', keys='', inbox=None, datapath=None, args=None):
if args is None:
args = self._empty_openstack_credentials()
# if args is None:
# args = self._empty_openstack_credentials()
super(PerformanceTestDriver, self).__init__(
name, keys, inbox, datapath, args)
self.client_data = None
self.register_translator(PerformanceTestDriver.p_translator)
self._init_end_start_poll()
@staticmethod
def get_datasource_info():
result = {}
result['id'] = 'performance'
result['description'] = 'Datasource driver used for perf tests'
# result['config'] = ds_utils.get_openstack_required_config()
# result['config']['api_version'] = constants.OPTIONAL
result['secret'] = ['password']
return result
def update_from_datasource(self):
if self.client_data is not None:
self.state = {}

View File

@ -1683,12 +1683,48 @@ class TestDatasourceDriver(base.TestCase):
expected_ret.remove(row)
self.assertEqual([], expected_ret)
# Old version
# class TestPollingDataSourceDriver(base.TestCase):
# class TestDriver(datasource_driver.PollingDataSourceDriver):
# def __init__(self):
# super(TestPollingDataSourceDriver.TestDriver, self).__init__(
# '', '', None, None, None)
# self._init_end_start_poll()
# def setUp(self):
# super(TestPollingDataSourceDriver, self).setUp()
# @mock.patch.object(eventlet, 'spawn')
# def test_init_consistence(self, mock_spawn):
# test_driver = TestPollingDataSourceDriver.TestDriver()
# mock_spawn.assert_called_once_with(test_driver.poll_loop,
# test_driver.poll_time)
# self.assertTrue(test_driver.initialized)
# self.assertIsNotNone(test_driver.worker_greenthread)
# @mock.patch.object(eventlet.greenthread, 'kill')
# @mock.patch.object(eventlet, 'spawn')
# def test_cleanup(self, mock_spawn, mock_kill):
# dummy_thread = dict()
# mock_spawn.return_value = dummy_thread
# test_driver = TestPollingDataSourceDriver.TestDriver()
# self.assertEqual(test_driver.worker_greenthread, dummy_thread)
# test_driver.cleanup()
# mock_kill.assert_called_once_with(dummy_thread)
# self.assertIsNone(test_driver.worker_greenthread)
class TestPollingDataSourceDriver(base.TestCase):
class TestDriver(datasource_driver.PollingDataSourceDriver):
def __init__(self):
super(TestPollingDataSourceDriver.TestDriver, self).__init__(
'', '', None, None, None)
self.node = 'node'
self._rpc_server = mock.MagicMock()
self._init_end_start_poll()
def setUp(self):
@ -1697,6 +1733,9 @@ class TestPollingDataSourceDriver(base.TestCase):
@mock.patch.object(eventlet, 'spawn')
def test_init_consistence(self, mock_spawn):
test_driver = TestPollingDataSourceDriver.TestDriver()
mock_spawn.assert_not_called()
self.assertIsNone(test_driver.worker_greenthread)
test_driver.start()
mock_spawn.assert_called_once_with(test_driver.poll_loop,
test_driver.poll_time)
self.assertTrue(test_driver.initialized)
@ -1709,10 +1748,11 @@ class TestPollingDataSourceDriver(base.TestCase):
mock_spawn.return_value = dummy_thread
test_driver = TestPollingDataSourceDriver.TestDriver()
test_driver.start()
self.assertEqual(test_driver.worker_greenthread, dummy_thread)
test_driver.cleanup()
test_driver.stop()
mock_kill.assert_called_once_with(dummy_thread)
self.assertIsNone(test_driver.worker_greenthread)
@ -1761,10 +1801,6 @@ class TestPushedDriver(base.TestCase):
test_driver.state['test_translator'])
self.assertEqual(expected_state, test_driver.state['test_translator'])
def test_ensure_cleanup_pushdriver(self):
test_driver = TestPushedDriver.TestDriver()
test_driver.cleanup()
class TestExecutionDriver(base.TestCase):

View File

@ -378,102 +378,107 @@ class TestDataSourceDriver(base.TestCase):
self.assertLess(before_time, last_updated)
self.assertLess(last_updated, datetime.datetime.now())
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']
# TODO(dse2): port using generic test driver instead of Neutron
# 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.retry_check_subscribers(neutron, [(policy.name, 'networks')])
# # subscribe
# policy.subscribe('neutron', 'networks', callback=policy.receive_data)
# helper.retry_check_subscribers(neutron, [(policy.name, 'networks')])
# poll 1
neutron.poll()
helper.retry_check_db_equal(policy, 'p(x)', datalog1)
# # poll 1
# neutron.poll()
# helper.retry_check_db_equal(policy, 'p(x)', datalog1)
# poll 2
neutron.poll()
helper.retry_check_db_equal(policy, 'p(x)', datalog2)
# # poll 2
# neutron.poll()
# helper.retry_check_db_equal(policy, 'p(x)', datalog2)
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']
# TODO(dse2): port using generic test driver instead of Neutron
# 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)
# # add garbage to policy
# for formula in fake_networks:
# policy.insert(formula)
# subscribe
policy.subscribe('neutron', 'networks', callback=policy.receive_data)
helper.retry_check_subscribers(neutron, [(policy.name, 'networks')])
# # subscribe
# policy.subscribe('neutron', 'networks', callback=policy.receive_data)
# helper.retry_check_subscribers(neutron, [(policy.name, 'networks')])
# poll 1
neutron.poll()
helper.retry_check_db_equal(policy, 'p(x)', datalog1)
# # poll 1
# neutron.poll()
# helper.retry_check_db_equal(policy, 'p(x)', datalog1)
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']
# TODO(dse2): port using generic test driver instead of Neutron
# 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)
# # 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.retry_check_number_of_updates(neutron, 1)
policy.subscribe('neutron', 'networks', callback=policy.receive_data)
helper.retry_check_db_equal(policy, 'p(x)', datalog1)
# # poll 1 and then subscribe; should still see first result
# neutron.poll()
# helper.retry_check_number_of_updates(neutron, 1)
# policy.subscribe('neutron', 'networks', callback=policy.receive_data)
# helper.retry_check_db_equal(policy, 'p(x)', datalog1)
# poll 2
neutron.poll()
helper.retry_check_db_equal(policy, 'p(x)', datalog2)
# # poll 2
# neutron.poll()
# helper.retry_check_db_equal(policy, 'p(x)', datalog2)
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']
# TODO(dse2): port using generic test driver instead of Neutron
# 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.retry_check_number_of_updates(neutron, 1)
neutron.poll()
helper.retry_check_number_of_updates(neutron, 2)
policy.subscribe('neutron', 'networks', callback=policy.receive_data)
helper.retry_check_db_equal(policy, 'p(x)', datalog2)
# # poll twice and then subscribe: should see 2nd result
# neutron.poll()
# helper.retry_check_number_of_updates(neutron, 1)
# neutron.poll()
# helper.retry_check_number_of_updates(neutron, 2)
# policy.subscribe('neutron', 'networks', callback=policy.receive_data)
# helper.retry_check_db_equal(policy, 'p(x)', datalog2)
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']
# TODO(dse2): port using generic test driver instead of Neutron
# 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.retry_check_subscribers(neutron, [(policy.name, 'networks')])
neutron.poll()
helper.retry_check_db_equal(policy, 'p(x)', datalog1)
# # get initial data
# policy.subscribe('neutron', 'networks', callback=policy.receive_data)
# helper.retry_check_subscribers(neutron, [(policy.name, 'networks')])
# neutron.poll()
# helper.retry_check_db_equal(policy, 'p(x)', datalog1)
# clear out policy's neutron:networks data (to simulate crashing)
policy.initialize_tables(['neutron:networks'], [])
# subscribe again (without unsubscribing)
policy.subscribe('neutron', 'networks', callback=policy.receive_data)
helper.retry_check_db_equal(policy, 'p(x)', datalog1)
# # clear out policy's neutron:networks data (to simulate crashing)
# policy.initialize_tables(['neutron:networks'], [])
# # subscribe again (without unsubscribing)
# policy.subscribe('neutron', 'networks', callback=policy.receive_data)
# helper.retry_check_db_equal(policy, 'p(x)', datalog1)
def create_network_group(tablename, full_neutron_tablename=None):

View File

@ -20,9 +20,7 @@ from __future__ import absolute_import
import mock
import novaclient
from congress.datalog import compile
from congress.datasources import nova_driver
from congress.dse import d6cage
from congress import exception
from congress.tests import base
from congress.tests.datasources import fakes
@ -224,58 +222,59 @@ class TestNovaDriver(base.TestCase):
for az in az_tuples:
map(self.assertEqual, expected_ret[az[0]], az)
def test_communication(self):
"""Test for communication.
# TODO(dse2): port or not. Unclear why we're doing this with Nova.
# def test_communication(self):
# """Test for communication.
Test the module's ability to be loaded into the DSE
by checking its ability to communicate on the message bus.
"""
cage = d6cage.d6Cage()
# Test the module's ability to be loaded into the DSE
# by checking its ability to communicate on the message bus.
# """
# cage = d6cage.d6Cage()
# 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(''),
'log_actions_only': True})
cage.createservice(name="nova", moduleName="NovaDriver", args=args)
# # 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(''),
# 'log_actions_only': True})
# 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.debug_mode()
policy.create_policy('nova')
policy.set_schema('nova', compile.Schema({'server': (1,)}))
policy.subscribe('nova', 'server',
callback=policy.receive_data)
# # Check that data gets sent from nova to policy as expected
# nova = cage.service_object('nova')
# policy = cage.service_object('policy')
# policy.debug_mode()
# policy.create_policy('nova')
# policy.set_schema('nova', compile.Schema({'server': (1,)}))
# 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.
# # 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.retry_check_subscribers(nova, [(policy.name, 'server')])
nova.prior_state = {}
nova.state['server'] = set([(1,), (2,), (3,)])
nova.publish('server', None)
helper.retry_check_db_equal(
policy, 'nova:server(x)',
'nova:server(1) nova:server(2) nova:server(3)')
# # publish server(1), server(2), server(3)
# helper.retry_check_subscribers(nova, [(policy.name, 'server')])
# nova.prior_state = {}
# nova.state['server'] = set([(1,), (2,), (3,)])
# nova.publish('server', None)
# helper.retry_check_db_equal(
# policy, 'nova:server(x)',
# 'nova:server(1) nova:server(2) nova:server(3)')
# publish server(1), server(4), server(5)
nova.prior_state['server'] = nova.state['server']
nova.state['server'] = set([(1,), (4,), (5,)])
nova.publish('server', None)
helper.retry_check_db_equal(
policy, 'nova:server(x)',
'nova:server(1) nova:server(4) nova:server(5)')
# # publish server(1), server(4), server(5)
# nova.prior_state['server'] = nova.state['server']
# nova.state['server'] = set([(1,), (4,), (5,)])
# nova.publish('server', None)
# helper.retry_check_db_equal(
# policy, 'nova:server(x)',
# 'nova:server(1) nova:server(4) nova:server(5)')
# TODO(thinrichs): test that Nova's polling functionality
# works properly. Or perhaps could bundle this into the

View File

@ -1,249 +0,0 @@
# Copyright (c) 2014 OpenStack Foundation
#
# 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.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from oslo_config import cfg
from congress import exception
from congress import harness
from congress.managers import datasource as datasource_manager
from congress.tests import base
from congress.tests import fake_datasource
from congress.tests import helper
class TestDataSourceManager(base.SqlTestCase):
def setUp(self):
super(TestDataSourceManager, self).setUp()
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
self.datasource_mgr = datasource_manager.DataSourceManager
self.datasource_mgr.validate_configured_drivers()
self.cage = harness.create(helper.root_path())
def _get_datasource_request(self):
return {'id': 'asdf',
'name': 'aaron',
'driver': '',
'description': 'hello world!',
'enabled': True,
'type': None,
'config': {}}
def test_make_datasource_dict(self):
req = self._get_datasource_request()
result = self.datasource_mgr.make_datasource_dict(req)
self.assertEqual(req, result)
def test_validate_create_datasource_invalid_driver(self):
req = self._get_datasource_request()
self.assertRaises(exception.InvalidDriver,
self.datasource_mgr.validate_create_datasource,
req)
def test_validate_create_datasource_invalid_config_invalid_options(self):
req = self._get_datasource_request()
req['driver'] = 'invalid_datasource'
self.assertRaises(exception.InvalidDriver,
self.datasource_mgr.validate_create_datasource,
req)
def test_validate_create_datasource_missing_config_options(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
# This is still missing some required options
req['config'] = {'auth_url': '1234'}
self.assertRaises(exception.MissingRequiredConfigOptions,
self.datasource_mgr.validate_create_datasource,
req)
def test_add_datasource(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
result = self.datasource_mgr.add_datasource(req)
for key, value in req.items():
self.assertEqual(value, result[key])
# TODO(thinrichs): test that ensure the DB, the policy engine,
# and the datasource manager are all in sync
def test_get_datasouce(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
result = self.datasource_mgr.add_datasource(req)
result = self.datasource_mgr.get_datasource(result['id'])
for key, value in req.items():
self.assertEqual(value, result[key])
def test_get_datasources(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['name'] = 'datasource1'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
self.datasource_mgr.add_datasource(req)
req['name'] = 'datasource2'
self.datasource_mgr.add_datasource(req)
result = self.datasource_mgr.get_datasources()
req['name'] = 'datasource1'
for key, value in req.items():
self.assertEqual(value, result[0][key])
req['name'] = 'datasource2'
for key, value in req.items():
self.assertEqual(value, result[1][key])
def test_get_datasources_hide_secret(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['name'] = 'datasource1'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
self.datasource_mgr.add_datasource(req)
req['name'] = 'datasource2'
self.datasource_mgr.add_datasource(req)
# Value will be set as <hidden>
req['config']['password'] = "<hidden>"
result = self.datasource_mgr.get_datasources(filter_secret=True)
req['name'] = 'datasource1'
for key, value in req.items():
self.assertEqual(value, result[0][key])
req['name'] = 'datasource2'
for key, value in req.items():
self.assertEqual(value, result[1][key])
def test_create_datasource_duplicate_name(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['name'] = 'datasource1'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
self.datasource_mgr.add_datasource(req)
self.assertRaises(exception.DatasourceNameInUse,
self.datasource_mgr.add_datasource, req)
def test_delete_datasource(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
result = self.datasource_mgr.add_datasource(req)
self.datasource_mgr.delete_datasource(result['id'])
self.assertRaises(exception.DatasourceNotFound,
self.datasource_mgr.get_datasource,
result['id'])
engine = self.cage.service_object('engine')
self.assertRaises(exception.PolicyRuntimeException,
engine.assert_policy_exists, req['name'])
# TODO(thinrichs): test that we've actually removed
# the row from the DB
def test_delete_datasource_error(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
result = self.datasource_mgr.add_datasource(req)
engine = self.cage.service_object('engine')
engine.create_policy('alice')
engine.insert('p(x) :- %s:q(x)' % req['name'], 'alice')
self.assertRaises(exception.DanglingReference,
self.datasource_mgr.delete_datasource,
result['id'])
def test_delete_invalid_datasource(self):
self.assertRaises(exception.DatasourceNotFound,
self.datasource_mgr.delete_datasource,
"does_not_exist")
def test_get_driver_schema(self):
schema = self.datasource_mgr.get_driver_schema(
'fake_datasource')
self.assertEqual(
schema,
fake_datasource.FakeDataSource.get_schema())
def test_get_datasouce_schema_driver_not_found(self):
self.assertRaises(exception.DatasourceNotFound,
self.datasource_mgr.get_datasource_schema,
"does_not_exist")
def test_duplicate_driver_name_raises(self):
# Load the driver twice
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource',
'congress.tests.fake_datasource.FakeDataSource'])
self.datasource_mgr = datasource_manager.DataSourceManager
self.assertRaises(exception.BadConfig,
self.datasource_mgr.validate_configured_drivers)
def test_datasource_spawn_datasource_poll(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
result = self.datasource_mgr.add_datasource(req)
self.datasource_mgr.request_refresh(result['id'])
# TODO(thinrichs): test that the driver actually polls
def test_datasource_spawn_datasource_poll_not_found(self):
self.assertRaises(exception.DatasourceNotFound,
self.datasource_mgr.request_refresh,
"does_not_exist")

View File

@ -21,8 +21,6 @@ import eventlet
from oslo_log import log as logging
from congress.datalog import arithmetic_solvers
from congress.dse import d6cage
from congress import harness
from congress.policy_engines import vm_placement
from congress.tests import base
from congress.tests import helper
@ -69,21 +67,24 @@ class TestSetPolicy(base.TestCase):
def setUp(self):
# create DSE and add vm-placement engine and fake datasource
super(TestSetPolicy, self).setUp()
self.cage = d6cage.d6Cage()
config = {'vmplace':
{'module': "congress/policy_engines/vm_placement.py"},
'fake':
{'poll_time': 0,
'module': "congress/tests/fake_datasource.py"}}
self.cage = helper.make_dsenode_new_partition("perf")
kwds = {}
kwds['name'] = 'fake'
kwds['args'] = helper.datasource_openstack_args()
self.fake = self.cage.create_service(
"congress.tests.fake_datasource.FakeDataSource", kwds)
self.fake.poll_time = 0
self.cage.register_service(self.fake)
harness.load_data_service("vmplace", config['vmplace'],
self.cage, helper.root_path(), 1)
harness.load_data_service("fake", config['fake'],
self.cage, helper.root_path(), 2)
self.vmplace = self.cage.service_object('vmplace')
kwds = {}
kwds['name'] = 'vmplace'
kwds['args'] = helper.datasource_openstack_args()
self.vmplace = self.cage.create_service(
"congress.policy_engines.vm_placement.ComputePlacementEngine",
kwds)
self.vmplace.debug_mode()
self.fake = self.cage.service_object('fake')
self.vmplace.poll_time = 0
self.cage.register_service(self.vmplace)
def test_set_policy_subscriptions(self):
self.vmplace.set_policy('p(x) :- fake:q(x)')

View File

@ -18,15 +18,15 @@ import mock
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.tests.policy_engines.test_agnostic import TestRuntime
from congress.tests2.api import base as api_base
from congress.tests.api import base as api_base
from congress.policy_engines import agnostic
from congress.tests import base
from congress.tests import helper
import sys
class TestDse2Runtime(TestRuntime):
class TestDse2Runtime(base.SqlTestCase):
def setUp(self):
super(TestDse2Runtime, self).setUp()
@ -73,7 +73,7 @@ class TestDse2Runtime(TestRuntime):
self.assertEqual([('nova', 'services')], subscriptions)
class TestAgnostic(TestRuntime):
class TestAgnostic(base.TestCase):
def test_receive_data_no_sequence_num(self):
'''Test receiving data without sequence numbers'''
run = agnostic.Dse2Runtime('engine')

View File

@ -17,6 +17,7 @@ from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from oslo_config import cfg
from oslo_log import log as logging
import retrying
@ -191,7 +192,12 @@ class TestDsePerformance(testbase.SqlTestCase):
def setUp(self):
super(TestDsePerformance, self).setUp()
self.cage = harness.create(helper.root_path(), config_override={})
cfg.CONF.set_override(
'drivers',
[('congress.tests.datasources.performance_datasource_driver'
'.PerformanceTestDriver')])
self.cage = helper.make_dsenode_new_partition("perf")
harness.create2(self.cage)
self.api = {'policy': self.cage.service_object('api-policy'),
'rule': self.cage.service_object('api-rule'),
'table': self.cage.service_object('api-table'),
@ -216,15 +222,12 @@ class TestDsePerformance(testbase.SqlTestCase):
"""
MAX_TUPLES = 700
# install datasource driver we can control
self.cage.loadModule(
"TestDriver",
helper.data_module_path(
"../tests/datasources/test_driver.py"))
self.cage.createservice(
name="data",
moduleName="TestDriver",
args=helper.datasource_openstack_args())
driver = self.cage.service_object('data')
kwds = {}
kwds['name'] = 'data'
kwds['args'] = helper.datasource_openstack_args()
kwds['driver'] = 'performance'
driver = self.cage.create_datasource_service(kwds)
self.cage.register_service(driver)
driver.poll_time = 0
self.engine.create_policy('data')
@ -256,15 +259,12 @@ class TestDsePerformance(testbase.SqlTestCase):
"""
MAX_TUPLES = 700
# install datasource driver we can control
self.cage.loadModule(
"PerformanceTestDriver",
helper.data_module_path(
"../tests/datasources/performance_datasource_driver.py"))
self.cage.createservice(
name="data",
moduleName="PerformanceTestDriver",
args=helper.datasource_openstack_args())
driver = self.cage.service_object('data')
kwds = {}
kwds['name'] = 'data'
kwds['args'] = helper.datasource_openstack_args()
kwds['driver'] = 'performance'
driver = self.cage.create_datasource_service(kwds)
self.cage.register_service(driver)
driver.poll_time = 0
self.engine.create_policy('data')

View File

@ -23,287 +23,236 @@ Tests for `congress` module.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from functools import reduce
import os
import mock
from mox3 import mox
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
import neutronclient.v2_0
from oslo_log import log as logging
from congress.api import webservice
from congress.common import config
from congress.datalog import base as datalog_base
from congress.datasources import neutronv2_driver
from congress.datasources import nova_driver
from congress import harness
from congress.tests.api import base as api_base
from congress.tests import base
import congress.tests.datasources.test_neutron_driver as test_neutron
from congress.tests.datasources import test_neutron_driver as test_neutron
from congress.tests import helper
LOG = logging.getLogger(__name__)
class TestCongress(base.SqlTestCase):
class BaseTestPolicyCongress(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)
super(BaseTestPolicyCongress, self).setUp()
self.services = api_base.setup_config(with_fake_datasource=False)
self.api = self.services['api']
self.node = self.services['node']
self.engine = self.services['engine']
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'}}
self.neutronv2 = self._create_neutron_mock('neutron')
cage = harness.create(helper.root_path(), config_override)
# Disable synchronizer because the this test creates
# datasources without also inserting them into the database.
# The synchronizer would delete these datasources.
cage.service_object('synchronizer').set_poll_time(0)
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}
# FIXME(arosen): remove all this code
# monkey patch
engine.create_policy('neutron',
kind=datalog_base.DATASOURCE_POLICY_TYPE)
engine.create_policy('neutron2',
kind=datalog_base.DATASOURCE_POLICY_TYPE)
engine.create_policy('nova',
kind=datalog_base.DATASOURCE_POLICY_TYPE)
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
def _create_neutron_mock(self, name):
# Register Neutron service
args = helper.datasource_openstack_args()
neutronv2 = neutronv2_driver.NeutronV2Driver(name, args=args)
self.node.register_service(neutronv2)
neutron_mock = mock.MagicMock(spec=neutronclient.v2_0.client.Client)
neutronv2.neutron = neutron_mock
# 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()
neutron_mock.list_networks.return_value = network1
neutron_mock.list_ports.return_value = port_response
neutron_mock.list_routers.return_value = router_response
neutron_mock.list_security_groups.return_value = sg_group_response
return neutronv2
self.cage = cage
self.engine = engine
self.api = api
class TestCongress(BaseTestPolicyCongress):
def setUp(self):
"""Setup tests that use multiple mock neutron instances."""
super(TestCongress, self).setUp()
def setup_config(self):
args = ['--config-file', helper.etcdir('congress.conf.test')]
config.init(args)
def test_synchronize_policy_no_erratic_change(self):
"""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, 'create_policy') as c:
self.engine.synchronizer.synchronize_policies()
# TODO(ekcs): How can we show args used if erratic call made?
d.assert_not_called()
c.assert_not_called()
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_datasource_api_model(self):
"""Test the datasource api model.
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: sorted(self.query('q', 'alpha')['results'],
key=lambda x: x['data']),
sorted([{'data': (1, 2)}, {'data': (2, 3)}],
key=lambda x: x['data']))
helper.retry_check_function_return_value(
lambda: list(self.query('q', 'alpha').keys()),
['results'])
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_policy_datasource(self):
self.create_policy('alpha')
self.create_fake_datasource('fake')
self.engine.synchronize_policies()
data = self.node.service_object('fake')
data.state = {'fake_table': set([(1, 2)])}
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)
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,)}]})
# 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
# 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')
# 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)
def create_policy(self, name):
self.api['api-policy'].add_item({'name': name}, {})
# 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)
def insert_rule(self, rule, policy):
context = {'policy_id': policy}
return self.api['api-rule'].add_item(
{'rule': rule}, {}, context=context)
# unknown policy
context = {'policy_id': 'unkpolicy', 'table_id': 'unktable'}
ans = api['row'].get_items({}, context=context)
self.assertEqual(len(ans['results']), 0)
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'}}
# unknown datasource table
context = {'ds_id': 'neutron', 'table_id': 'unktable'}
ans = api['row'].get_items({}, context=context)
self.assertEqual(len(ans['results']), 0)
return self.api['api-datasource'].add_item(item, params={})
# 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 query(self, tablename, policyname):
context = {'policy_id': policyname,
'table_id': tablename}
return self.api['api-row'].get_items({}, context)
def test_rule_insert_delete(self):
self.api['policy'].add_item({'name': 'alice'}, {})
self.api['api-policy'].add_item({'name': 'alice'}, {})
context = {'policy_id': 'alice'}
(id1, _) = self.api['rule'].add_item(
(id1, _) = self.api['api-rule'].add_item(
{'rule': 'p(x) :- plus(y, 1, x), q(y)'}, {}, context=context)
ds = self.api['rule'].get_items({}, context)['results']
ds = self.api['api-rule'].get_items({}, context)['results']
self.assertEqual(len(ds), 1)
self.api['rule'].delete_item(id1, {}, context)
self.api['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_datasource_request_refresh(self):
# neutron polls automatically here, which is why register_service
# starts its service.
neutron = self.neutronv2
neutron.stop()
self.assertEqual(neutron.refresh_request_queue.qsize(), 0)
neutron.request_refresh()
self.assertEqual(neutron.refresh_request_queue.qsize(), 1)
neutron.start()
neutron.request_refresh()
f = lambda: neutron.refresh_request_queue.qsize()
helper.retry_check_function_return_value(f, 0)
def test_datasource_poll(self):
neutron = self.neutronv2
neutron.stop()
neutron._translate_ports({'ports': []})
self.assertEqual(len(neutron.state['ports']), 0)
neutron.start()
f = lambda: len(neutron.state['ports'])
helper.retry_check_function_return_value_not_eq(f, 0)
class APILocalRouting(BaseTestPolicyCongress):
def setUp(self):
super(APILocalRouting, self).setUp()
# set up second API+PE node
self.services = api_base.setup_config(
with_fake_datasource=False, node_id='testnode2',
same_partition_as_node=self.node)
self.api2 = self.services['api']
self.node2 = self.services['node']
self.engine2 = self.services['engine']
self.data = self.services['data']
# add different data to two PE instances
# going directly to agnostic not via API to make sure faulty API
# routing (subject of the test) would not affect test accuracy
self.engine.create_policy('policy')
self.engine2.create_policy('policy')
self.engine.insert('p(1) :- NOT q()', 'policy')
# self.engine1.insert('p(1)', 'policy')
self.engine2.insert('p(2) :- NOT q()', 'policy')
self.engine2.insert('p(3) :- NOT q()', 'policy')
def test_intranode_pe_routing(self):
for i in range(0, 5): # run multiple times (non-determinism)
result = self.api['api-row'].get_items(
{}, {'policy_id': 'policy', 'table_id': 'p'})
self.assertEqual(len(result['results']), 1)
result = self.api2['api-row'].get_items(
{}, {'policy_id': 'policy', 'table_id': 'p'})
self.assertEqual(len(result['results']), 2)
def test_non_PE_service_reachable(self):
# intranode
result = self.api['api-row'].get_items(
{}, {'ds_id': 'neutron', 'table_id': 'ports'})
self.assertEqual(len(result['results']), 1)
# internode
result = self.api2['api-row'].get_items(
{}, {'ds_id': 'neutron', 'table_id': 'ports'})
self.assertEqual(len(result['results']), 1)
def test_internode_pe_routing(self):
'''test reach internode PE when intranode PE not available'''
self.node.unregister_service('engine')
result = self.api['api-row'].get_items(
{}, {'policy_id': 'policy', 'table_id': 'p'})
self.assertEqual(len(result['results']), 2)
result = self.api2['api-row'].get_items(
{}, {'policy_id': 'policy', 'table_id': 'p'})
self.assertEqual(len(result['results']), 2)
class TestPolicyExecute(BaseTestPolicyCongress):
def setUp(self):
super(TestPolicyExecute, self).setUp()
self.nova = self._register_test_datasource('nova')
def _register_test_datasource(self, name):
args = helper.datasource_openstack_args()
if name == 'nova':
ds = nova_driver.NovaDriver('nova', args=args)
if name == 'neutron':
ds = neutronv2_driver.NeutronV2Driver('neutron', args=args)
self.node.register_service(ds)
ds.update_from_datasource = mock.MagicMock()
return ds
def test_policy_execute(self):
class NovaClient(object):
def __init__(self, testkey):
@ -313,17 +262,17 @@ class TestCongress(base.SqlTestCase):
LOG.info("disconnectNetwork called on %s", arg1)
self.testkey = "arg1=%s" % arg1
nova_client = NovaClient(None)
nova = self.cage.service_object('nova')
nova_client = NovaClient("testing")
nova = self.nova
nova.nova_client = nova_client
# insert rule and data
self.api['policy'].add_item({'name': 'alice'}, {})
(id1, _) = self.api['rule'].add_item(
self.api['api-policy'].add_item({'name': 'alice'}, {})
(id1, _) = self.api['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(
(id2, _) = self.api['api-rule'].add_item(
{'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 1)
ans = "arg1=1"
@ -331,7 +280,7 @@ class TestCongress(base.SqlTestCase):
helper.retry_check_function_return_value(f, ans)
# insert more data
self.api['rule'].add_item(
self.api['api-rule'].add_item(
{'rule': 'q(2)'}, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 2)
ans = "arg1=2"
@ -339,17 +288,17 @@ class TestCongress(base.SqlTestCase):
helper.retry_check_function_return_value(f, ans)
# insert irrelevant data
self.api['rule'].add_item(
self.api['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(
self.api['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(
self.api['api-rule'].delete_item(
id1, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 2)
@ -363,15 +312,15 @@ class TestCongress(base.SqlTestCase):
self.testkey = "arg1=%s" % arg1
nova_client = NovaClient(None)
nova = self.cage.service_object('nova')
nova = self.nova
nova.nova_client = nova_client
# insert rule and data
self.api['policy'].add_item({'name': 'alice'}, {})
self.api['rule'].add_item(
self.api['api-policy'].add_item({'name': 'alice'}, {})
self.api['api-rule'].add_item(
{'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 0)
self.api['rule'].add_item(
self.api['api-rule'].add_item(
{'rule': 'execute[nova:disconnectNetwork(x)] :- q(x)'}, {},
context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 1)
@ -397,15 +346,15 @@ class TestCongress(base.SqlTestCase):
self.testkey = "arg1=%s" % id_
nova_client = NovaClient(None)
nova = self.cage.service_object('nova')
nova = self.nova
nova.nova_client = nova_client
self.api['policy'].add_item({'name': 'alice'}, {})
self.api['rule'].add_item(
self.api['api-policy'].add_item({'name': 'alice'}, {})
self.api['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(
self.api['api-rule'].add_item(
{'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 1)
ans = "arg1=1"
@ -422,19 +371,19 @@ class TestCongress(base.SqlTestCase):
self.testkey = "noargs"
nova_client = NovaClient(None)
nova = self.cage.service_object('nova')
nova = self.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(
self.api['api-policy'].add_item({'name': 'alice'}, {})
(id1, rule1) = self.api['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(
(id2, rule2) = self.api['api-rule'].add_item(
{'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 1)
ans = "noargs"
@ -442,21 +391,21 @@ class TestCongress(base.SqlTestCase):
helper.retry_check_function_return_value(f, ans)
# insert more data (which DOES NOT cause an execution)
(id3, rule3) = self.api['rule'].add_item(
(id3, rule3) = self.api['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(
self.api['api-rule'].delete_item(
id2, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 1)
self.api['rule'].delete_item(
self.api['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(
(id4, rule3) = self.api['api-rule'].add_item(
{'rule': 'q(3)'}, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 2)
ans = "noargs"
@ -464,23 +413,10 @@ class TestCongress(base.SqlTestCase):
helper.retry_check_function_return_value(f, ans)
# delete policy rule
self.api['rule'].delete_item(
self.api['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):
@ -491,91 +427,41 @@ class TestCongress(base.SqlTestCase):
self.testkey = "arg1=%s" % arg1
neutron_client = NeutronClient(None)
neutron = self.cage.service_object('neutron')
neutron = self.neutronv2
neutron.neutron = neutron_client
# insert rule and data
self.api['policy'].add_item({'name': 'alice'}, {})
(id1, _) = self.api['rule'].add_item(
self.api['api-policy'].add_item({'name': 'alice'}, {})
(id1, _) = self.api['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(
(id2, _) = self.api['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)
def test_neutron_policy_poll_and_subscriptions(self):
"""Test polling and publishing of neutron updates."""
policy = self.engine.DEFAULT_THEORY
neutron2 = self._create_neutron_mock('neutron2')
self.engine.initialize_datasource('neutron',
self.neutronv2.get_schema())
self.engine.initialize_datasource('neutron2',
self.neutronv2.get_schema())
str_rule = ('p(x0, y0) :- neutron:networks(x0, x1, x2, x3, x4, x5), '
'neutron2:networks(y0, y1, y2, y3, y4, y5)')
rule = {'rule': str_rule, 'name': 'testrule1', 'comment': 'test'}
self.api['api-rule'].add_item(rule, {}, context={'policy_id': policy})
# Test policy subscriptions
subscriptions = self.engine.subscription_list()
self.assertEqual(sorted([('neutron', 'networks'),
('neutron2', 'networks')]), sorted(subscriptions))
# Test multiple instances
self.neutronv2.poll()
neutron2.poll()
ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492", '
' "240ff9df-df35-43ae-9df5-27fae87f2492") ')
helper.retry_check_db_equal(self.engine, 'p(x, y)', ans, target=policy)

View File

@ -1,48 +0,0 @@
# Copyright (c) 2015 Intel, 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.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
# set test to run as distributed arch
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.api import webservice
from congress.tests import base
from congress.tests2.api import base as api_base
class TestActionModel(base.SqlTestCase):
def setUp(self):
super(TestActionModel, self).setUp()
services = api_base.setup_config()
self.action_model = services['api']['api-action']
self.datasource = services['data']
def test_get_datasource_actions(self):
context = {'ds_id': self.datasource.service_id}
actions = self.action_model.get_items({}, context=context)
expected_ret = {'results': [{'name': 'fake_act',
'args': [{'name': 'server_id',
'description': 'server to act'}],
'description': 'fake action'}]}
self.assertEqual(expected_ret, actions)
def test_get_invalid_datasource_action(self):
context = {'ds_id': 'invalid_id'}
self.assertRaises(webservice.DataModelException,
self.action_model.get_items, {}, context=context)

View File

@ -1,428 +0,0 @@
# Copyright (c) 2015 OpenStack Foundation
#
# 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.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
# set test to run as distributed arch
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
import mock
from oslo_utils import uuidutils
from congress.api import error_codes
from congress.api import webservice
from congress.tests import base
from congress.tests import helper
from congress.tests2.api import base as api_base
class TestPolicyModel(base.SqlTestCase):
def setUp(self):
super(TestPolicyModel, self).setUp()
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.rule_api = services['api']['api-rule']
self.node = services['node']
self.engine = services['engine']
self.initial_policies = set(self.engine.policy_names())
self._add_test_policy()
def _add_test_policy(self):
test_policy = {
"name": "test-policy",
"description": "test policy description",
"kind": "nonrecursive",
"abbreviation": "abbr"
}
test_policy_id, obj = self.policy_model.add_item(test_policy, {})
test_policy["id"] = test_policy_id
test_policy["owner_id"] = obj["owner_id"]
test_policy2 = {
"name": "test-policy2",
"description": "test policy2 description",
"kind": "nonrecursive",
"abbreviation": "abbr2"
}
test_policy_id, obj = self.policy_model.add_item(test_policy2, {})
test_policy2["id"] = test_policy_id
test_policy2["owner_id"] = obj["owner_id"]
self.policy = test_policy
self.policy2 = test_policy2
action_policy = self.policy_model.get_item('action', {})
self.action_policy = action_policy
def test_in_mem_and_db_policies(self):
ret = self.policy_model.get_items({})
db = [p['name'] for p in ret['results']]
mem = self.engine.policy_names()
new_memory = set(mem) - self.initial_policies
new_db = set(db) - self.initial_policies
self.assertEqual(new_memory, new_db)
def test_get_items(self):
ret = self.policy_model.get_items({})
self.assertTrue(all(p in ret['results']
for p in [self.policy, self.policy2]))
def test_get_item(self):
expected_ret = self.policy
ret = self.policy_model.get_item(self.policy["id"], {})
self.assertEqual(expected_ret, ret)
def test_get_invalid_item(self):
self.assertRaises(KeyError, self.policy_model.get_item,
'invalid-id', {})
@mock.patch('oslo_utils.uuidutils.generate_uuid')
def test_add_item(self, patched_gen_uuid):
test = {
"name": "test",
"description": "test description",
"kind": "nonrecursive",
"abbreviation": "abbr"
}
patched_gen_uuid.return_value = 'uuid'
uuidutils.generate_uuid = mock.Mock()
uuidutils.generate_uuid.return_value = 'uuid'
expected_ret1 = 'uuid'
expected_ret2 = {
'id': 'uuid',
'name': test['name'],
'owner_id': 'user',
'description': test['description'],
'abbreviation': test['abbreviation'],
'kind': test['kind']
}
policy_id, policy_obj = self.policy_model.add_item(test, {})
self.assertEqual(expected_ret1, policy_id)
self.assertEqual(expected_ret2, policy_obj)
def test_add_item_with_id(self):
test = {
"name": "test",
"description": "test description",
"kind": "nonrecursive",
"abbreviation": "abbr"
}
self.assertRaises(webservice.DataModelException,
self.policy_model.add_item, test, {}, 'id')
def test_add_item_without_name(self):
test = {
"description": "test description",
"kind": "nonrecursive",
"abbreviation": "abbr"
}
self.assertRaises(webservice.DataModelException,
self.policy_model.add_item, test, {})
def test_add_item_with_long_abbreviation(self):
test = {
"name": "test",
"description": "test description",
"kind": "nonrecursive",
"abbreviation": "123456"
}
try:
self.policy_model.add_item(test, {})
self.fail("DataModelException should been raised.")
except webservice.DataModelException as e:
error_key = 'policy_abbreviation_error'
self.assertEqual(error_codes.get_num(error_key), e.error_code)
self.assertEqual(error_codes.get_desc(error_key), e.description)
self.assertEqual(error_codes.get_http(error_key),
e.http_status_code)
def test_delete_item(self):
expected_ret = self.policy
policy_id = self.policy['id']
ret = self.policy_model.delete_item(policy_id, {})
self.assertEqual(expected_ret, ret)
self.assertRaises(KeyError, self.policy_model.get_item,
self.policy['id'], {})
# check that deleting the policy also deletes the rules
self.assertRaises(webservice.DataModelException,
self.rule_api.get_items,
{}, {'policy_id': policy_id})
def test_simulate_action(self):
context = {
'policy_id': self.action_policy['name']
}
action_rule1 = {
'rule': 'action("q")',
}
action_rule2 = {
'rule': 'p+(x):- q(x)'
}
self.rule_api.add_item(action_rule1, {}, context=context)
self.rule_api.add_item(action_rule2, {}, context=context)
request_body = {
'query': 'p(x)',
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
expected_ret = {
'result': [
"p(1)"
]
}
ret = self.policy_model.simulate_action({}, context, request)
self.assertEqual(expected_ret, ret)
def test_simulate_with_delta(self):
context = {
'policy_id': self.action_policy['name']
}
action_rule1 = {
'rule': 'action("q")',
}
action_rule2 = {
'rule': 'p+(x):- q(x)'
}
self.rule_api.add_item(action_rule1, {}, context=context)
self.rule_api.add_item(action_rule2, {}, context=context)
request_body = {
'query': 'p(x)',
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
params = {
'delta': 'true'
}
expected_ret = {
'result': [
"p+(1)"
]
}
ret = self.policy_model.simulate_action(params, context, request)
self.assertEqual(expected_ret, ret)
def test_simulate_with_trace(self):
context = {
'policy_id': self.action_policy['name']
}
action_rule1 = {
'rule': 'action("q")',
}
action_rule2 = {
'rule': 'p+(x):- q(x)'
}
self.rule_api.add_item(action_rule1, {}, context=context)
self.rule_api.add_item(action_rule2, {}, context=context)
request_body = {
'query': 'p(x)',
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
params = {
'trace': 'true'
}
expected_ret = {
'result': [
"p(1)"
],
'trace': "trace strings"
}
ret = self.policy_model.simulate_action(params, context, request)
# check response's keys equal expected_ret's key
self.assertTrue(all(key in expected_ret.keys() for key in ret.keys()))
self.assertEqual(expected_ret['result'], ret['result'])
self.assertTrue(len(ret['trace']) > 10)
def test_simulate_with_delta_and_trace(self):
context = {
'policy_id': self.action_policy['name']
}
action_rule1 = {
'rule': 'action("q")',
}
action_rule2 = {
'rule': 'p+(x):- q(x)'
}
self.rule_api.add_item(action_rule1, {}, context=context)
self.rule_api.add_item(action_rule2, {}, context=context)
request_body = {
'query': 'p(x)',
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
params = {
'trace': 'true',
'delta': 'true'
}
expected_ret = {
'result': [
"p+(1)"
],
'trace': "trace strings"
}
ret = self.policy_model.simulate_action(params, context, request)
# check response's keys equal expected_ret's key
self.assertTrue(all(key in expected_ret.keys() for key in ret.keys()))
self.assertEqual(expected_ret['result'], ret['result'])
self.assertTrue(len(ret['trace']) > 10)
def test_simulate_invalid_policy(self):
context = {
'policy_id': 'invalid-policy'
}
request_body = {
'query': 'p(x)',
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'
}
request = helper.FakeRequest(request_body)
self.assertRaises(webservice.DataModelException,
self.policy_model.simulate_action,
{}, context, request)
def test_simulate_invalid_sequence(self):
context = {
'policy_id': self.action_policy['name']
}
action_rule = {
'rule': 'w(x):-z(x)',
}
self.rule_api.add_item(action_rule, {}, context=context)
request_body = {
'query': 'w(x)',
'action_policy': self.action_policy['name'],
'sequence': 'z(1)'
}
request = helper.FakeRequest(request_body)
self.assertRaises(webservice.DataModelException,
self.policy_model.simulate_action,
{}, context, request)
def test_simulate_policy_errors(self):
def check_err(params, context, request, emsg):
try:
self.policy_model.simulate_action(params, context, request)
self.assertFail()
except webservice.DataModelException as e:
self.assertIn(emsg, str(e))
context = {
'policy_id': self.action_policy['name']
}
# Missing query
body = {'action_policy': self.action_policy['name'],
'sequence': 'q(1)'}
check_err({}, context, helper.FakeRequest(body),
'Simulate requires parameters')
# Invalid query
body = {'query': 'p(x',
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'}
check_err({}, context, helper.FakeRequest(body),
'Parse failure')
# Multiple querys
body = {'query': 'p(x) q(x)',
'action_policy': self.action_policy['name'],
'sequence': 'q(1)'}
check_err({}, context, helper.FakeRequest(body),
'more than 1 rule')
# Missing action_policy
body = {'query': 'p(x)',
'sequence': 'q(1)'}
check_err({}, context, helper.FakeRequest(body),
'Simulate requires parameters')
# Missing sequence
body = {'query': 'p(x)',
'action_policy': self.action_policy['name']}
check_err({}, context, helper.FakeRequest(body),
'Simulate requires parameters')
# Syntactically invalid sequence
body = {'query': 'p(x)',
'action_policy': self.action_policy['name'],
'sequence': 'q(1'}
check_err({}, context, helper.FakeRequest(body),
'Parse failure')
# Semantically invalid sequence
body = {'query': 'p(x)',
'action_policy': self.action_policy['name'],
'sequence': 'r(1)'} # r is not an action
check_err({}, context, helper.FakeRequest(body),
'non-action, non-update')
def test_policy_api_model_error(self):
"""Test the policy api model."""
# add policy without name
self.assertRaises(webservice.DataModelException,
self.policy_model.add_item, {}, {})
# add policy with bad ID
self.assertRaises(webservice.DataModelException,
self.policy_model.add_item, {'name': '7*7'}, {})
self.assertRaises(webservice.DataModelException,
self.policy_model.add_item,
{'name': 'p(x) :- q(x)'}, {})
# add policy with invalid 'kind'
self.assertRaises(webservice.DataModelException,
self.policy_model.add_item,
{'kind': 'nonexistent', 'name': 'alice'}, {})
# add existing policy
self.policy_model.add_item({'name': 'Test1'}, {})
self.assertRaises(KeyError, self.policy_model.add_item,
{'name': 'Test1'}, {})
# delete non-existent policy
self.assertRaises(KeyError, self.policy_model.delete_item,
'noexist', {})
# delete system-maintained policy
policies = self.policy_model.get_items({})['results']
class_policy = [p for p in policies if p['name'] == 'classification']
class_policy = class_policy[0]
self.assertRaises(KeyError, self.policy_model.delete_item,
class_policy['id'], {})

View File

@ -1,131 +0,0 @@
# Copyright (c) 2015 OpenStack Foundation
#
# 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.
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 congress.api import webservice
from congress.tests import base
from congress.tests2.api import base as api_base
class TestRowModel(base.SqlTestCase):
def setUp(self):
super(TestRowModel, self).setUp()
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.rule_model = services['api']['api-rule']
self.row_model = services['api']['api-row']
self.node = services['node']
self.data = services['data']
def test_get_items_datasource_row(self):
# adjust datasource to have required value
row = ('data1', 'data2')
self.data.state['fake_table'] = set([row])
# check result
context = {'ds_id': self.data.service_id,
'table_id': 'fake_table'}
data = [{'data': row}]
expected_ret = {'results': data}
ret = self.row_model.get_items({}, context)
self.assertEqual(expected_ret, ret)
def test_get_items_invalid_ds_name(self):
context = {'ds_id': 'invalid-ds',
'table_id': 'fake-table'}
self.assertRaises(webservice.DataModelException,
self.row_model.get_items, {}, context)
def test_get_items_invalid_ds_table_name(self):
context = {'ds_id': self.data.service_id,
'table_id': 'invalid-table'}
self.assertRaises(webservice.DataModelException,
self.row_model.get_items, {}, context)
def test_get_items_policy_row(self):
# create policy
policyname = 'test-policy'
self.policy_model.add_item({"name": policyname}, {})
# insert rules
context = {'policy_id': policyname,
'table_id': 'p'}
self.rule_model.add_item({'rule': 'p("x"):- true'}, {},
context=context)
# check results
row = ('x',)
data = [{'data': row}]
ret = self.row_model.get_items({}, context)
self.assertEqual({'results': data}, ret)
# Enable trace and check
ret = self.row_model.get_items({'trace': 'true'}, context=context)
s = frozenset([tuple(x['data']) for x in ret['results']])
t = frozenset([('x',)])
self.assertEqual(s, t, "Rows with tracing")
self.assertTrue('trace' in ret, "Rows should have trace")
self.assertEqual(len(ret['trace'].split('\n')), 9)
def test_get_items_invalid_policy_name(self):
context = {'policy_id': 'invalid-policy',
'table_id': 'p'}
self.assertRaises(webservice.DataModelException,
self.row_model.get_items, {}, context)
def test_get_items_invalid_policy_table_name(self):
# create policy
policyname = 'test-policy'
self.policy_model.add_item({"name": policyname}, {})
context = {'policy_id': policyname,
'table_id': 'invalid-table'}
self.assertRaises(webservice.DataModelException,
self.row_model.get_items, {}, context)
def test_update_items(self):
context = {'ds_id': self.data.service_id,
'table_id': 'fake_table'}
objs = [
{"id": 'id-1', "name": 'name-1'},
{"id": 'id-2', "name": 'name-2'}
]
expected_state = (('id-1', 'name-1'), ('id-2', 'name-2'))
self.row_model.update_items(objs, {}, context=context)
table_row = self.data.state['fake_table']
self.assertEqual(len(expected_state), len(table_row))
for row in expected_state:
self.assertTrue(row in table_row)
def test_update_items_invalid_table(self):
context = {'ds_id': self.data.service_id,
'table_id': 'invalid-table'}
objs = [
{"id": 'id-1', "name": 'name-1'},
{"id": 'id-2', "name": 'name-2'}
]
self.assertRaises(webservice.DataModelException,
self.row_model.update_items, objs, {}, context)

View File

@ -1,175 +0,0 @@
# Copyright 2015 NEC Corporation. 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.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import mock
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.api import rule_model
from congress.api import webservice
from congress.tests import base
from congress.tests2.api import base as api_base
class TestRuleModel(base.SqlTestCase):
def setUp(self):
super(TestRuleModel, self).setUp()
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.rule_model = services['api']['api-rule']
self.node = services['node']
self.action_policy = self.policy_model.get_item('action', {})
self.context = {'policy_id': self.action_policy["name"]}
self._add_test_rule()
def _add_test_rule(self):
test_rule1 = {
"rule": "p(x) :- q(x)",
"name": "test-rule1",
"comment": "test-comment"
}
test_rule2 = {
"rule": 'p(x) :- q(x), not r(x)',
"name": "test-rule2",
"comment": "test-comment-2"
}
test_rule_id, obj = self.rule_model.add_item(test_rule1, {},
context=self.context)
test_rule1["id"] = test_rule_id
self.rule1 = test_rule1
test_rule_id, obj = self.rule_model.add_item(test_rule2, {},
context=self.context)
test_rule2["id"] = test_rule_id
self.rule2 = test_rule2
@mock.patch.object(rule_model.RuleModel, 'policy_name')
def test_add_rule_with_invalid_policy(self, policy_name_mock):
test_rule = {'rule': 'p()', 'name': 'test'}
policy_name_mock.return_value = 'invalid'
self.assertRaises(webservice.DataModelException,
self.rule_model.add_item,
test_rule, {})
# TODO(dse2): Fix this test; it must create a 'beta' service on the dse
# so that when it subscribes, the snapshot can be returned.
# Or fix the subscribe() implementation so that we can subscribe before
# the service has been created.
# def test_add_rule_with_colrefs(self):
# engine = self.engine
# engine.create_policy('beta', kind=datalogbase.DATASOURCE_POLICY_TYPE)
# engine.set_schema(
# 'beta', compile.Schema({'q': ("name", "status", "year")}))
# # insert/retrieve rule with column references
# # just testing that no errors are thrown--correctness elsewhere
# # Assuming that api-models are pass-throughs to functionality
# (id1, _) = self.rule_model.add_item(
# {'rule': 'p(x) :- beta:q(name=x)'},
# {}, context=self.context)
# self.rule_model.get_item(id1, {}, context=self.context)
# def test_add_rule_with_bad_colrefs(self):
# engine = self.engine
# engine.create_policy('beta') # not datasource policy
# # insert/retrieve rule with column references
# # just testing that no errors are thrown--correctness elsewhere
# # Assuming that api-models are pass-throughs to functionality
# self.assertRaises(
# webservice.DataModelException,
# self.rule_model.add_item,
# {'rule': 'p(x) :- beta:q(name=x)'},
# {}, context=self.context)
def test_get_items(self):
ret = self.rule_model.get_items({}, context=self.context)
self.assertTrue(all(p in ret['results']
for p in [self.rule1, self.rule2]))
def test_get_item(self):
expected_ret = self.rule1
ret = self.rule_model.get_item(self.rule1["id"], {},
context=self.context)
self.assertEqual(expected_ret, ret)
def test_get_invalid_item(self):
expected_ret = None
ret = self.rule_model.get_item('invalid-id', {}, context=self.context)
self.assertEqual(expected_ret, ret)
def test_delete_item(self):
expected_ret = self.rule1
ret = self.rule_model.delete_item(self.rule1['id'], {},
context=self.context)
self.assertEqual(expected_ret, ret)
expected_ret = None
ret = self.rule_model.get_item(self.rule1['id'], {},
context=self.context)
self.assertEqual(expected_ret, ret)
def test_rule_api_model_errors(self):
"""Test syntax errors.
Test that syntax errors thrown by the policy runtime
are returned properly to the user so they can see the
error messages.
"""
# lexer error
with self.assertRaisesRegex(
webservice.DataModelException,
"Lex failure"):
self.rule_model.add_item({'rule': 'p#'}, {},
context=self.context)
# parser error
with self.assertRaisesRegex(
webservice.DataModelException,
"Parse failure"):
self.rule_model.add_item({'rule': 'p('}, {},
context=self.context)
# single-rule error: safety in the head
with self.assertRaisesRegex(
webservice.DataModelException,
"Variable x found in head but not in body"):
# TODO(ramineni):check for action
self.context = {'policy_id': 'classification'}
self.rule_model.add_item({'rule': 'p(x,y) :- q(y)'}, {},
context=self.context)
# multi-rule error: recursion through negation
self.rule_model.add_item({'rule': 'p(x) :- q(x), not r(x)'}, {},
context=self.context)
with self.assertRaisesRegex(
webservice.DataModelException,
"Rules are recursive"):
self.rule_model.add_item({'rule': 'r(x) :- q(x), not p(x)'}, {},
context=self.context)
self.rule_model.add_item({'rule': 'p1(x) :- q1(x)'}, {},
context=self.context)
# duplicate rules
with self.assertRaisesRegex(
webservice.DataModelException,
"Rule already exists"):
self.rule_model.add_item({'rule': 'p1(x) :- q1(x)'}, {},
context=self.context)

View File

@ -1,69 +0,0 @@
# Copyright (c) 2015 OpenStack Foundation
#
# 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.
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 congress.api import api_utils
from congress.api import webservice
from congress.tests import base
from congress.tests2.api import base as api_base
class TestSchemaModel(base.TestCase):
def setUp(self):
super(TestSchemaModel, self).setUp()
services = api_base.setup_config()
self.schema_model = services['api']['api-schema']
self.data = services['data']
def test_get_item_all_table(self):
context = {'ds_id': self.data.service_id}
schema = self.data.get_schema()
fake_tables = {'tables':
[api_utils.create_table_dict(
table_, schema) for table_ in schema]}
tables = self.schema_model.get_item(None, {}, context=context)
self.assertEqual(fake_tables, tables)
def test_get_item_table(self):
context = {'ds_id': self.data.service_id, 'table_id': 'fake_table'}
fake_schema = self.data.get_schema()
fake_table = api_utils.create_table_dict(
"fake_table", fake_schema)
table = self.schema_model.get_item(None, {}, context=context)
self.assertEqual(fake_table, table)
def test_get_invalid_datasource_table(self):
context = {'ds_id': self.data.service_id, 'table_id': 'invalid_table'}
try:
self.schema_model.get_item(None, {}, context=context)
except webservice.DataModelException as e:
self.assertEqual(404, e.error_code)
else:
raise Exception("Should not get here")
def test_get_invalid_datasource(self):
context = {'ds_id': 'invalid'}
try:
self.schema_model.get_item(None, {}, context=context)
except webservice.DataModelException as e:
self.assertEqual(404, e.error_code)
else:
raise Exception("Should not get here")

View File

@ -1,118 +0,0 @@
# Copyright (c) 2016 NTT
#
# 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.
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import uuid
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.api import webservice
from congress.tests import base
from congress.tests2.api import base as api_base
class TestStatusModel(base.SqlTestCase):
def setUp(self):
super(TestStatusModel, self).setUp()
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.rule_model = services['api']['api-rule']
self.status_model = services['api']['api-status']
self.node = services['node']
self.datasource = services['data']
def test_get_datasource_status(self):
context = {'ds_id': self.datasource.service_id}
status = self.status_model.get_item(None, {}, context=context)
expected_status_keys = ['last_updated', 'subscriptions',
'last_error', 'subscribers',
'initialized', 'number_of_updates']
self.assertEqual(set(expected_status_keys), set(status.keys()))
def test_get_invalid_datasource_status(self):
context = {'ds_id': 'invalid_id'}
self.assertRaises(webservice.DataModelException,
self.status_model.get_item, None, {},
context=context)
def test_policy_id_status(self):
result = self.policy_model.add_item({'name': 'test_policy'}, {})
context = {'policy_id': result[0]}
status = self.status_model.get_item(None, {}, context=context)
expected_status = {'name': 'test_policy',
'id': result[0]}
self.assertEqual(expected_status, status)
# test with policy_name
context = {'policy_id': result[1]['name']}
status = self.status_model.get_item(None, {}, context=context)
self.assertEqual(expected_status, status)
def test_invalid_policy_id_status(self):
invalid_id = uuid.uuid4()
context = {'policy_id': invalid_id}
self.assertRaises(webservice.DataModelException,
self.status_model.get_item, None, {},
context=context)
def test_rule_status_policy_id(self):
result = self.policy_model.add_item({'name': 'test_policy'}, {})
policy_id = result[0]
policy_name = result[1]['name']
result = self.rule_model.add_item({'name': 'test_rule',
'rule': 'p(x) :- q(x)'}, {},
context={'policy_id': 'test_policy'})
context = {'policy_id': policy_id, 'rule_id': result[0]}
status = self.status_model.get_item(None, {}, context=context)
expected_status = {'name': 'test_rule',
'id': result[0],
'comment': '',
'original_str': 'p(x) :- q(x)'}
self.assertEqual(expected_status, status)
# test with policy_name
context = {'policy_id': policy_name, 'rule_id': result[0]}
status = self.status_model.get_item(None, {}, context=context)
expected_status = {'name': 'test_rule',
'id': result[0],
'comment': '',
'original_str': 'p(x) :- q(x)'}
self.assertEqual(expected_status, status)
def test_rule_status_invalid_rule_policy_id(self):
result = self.policy_model.add_item({'name': 'test_policy'}, {})
policy_id = result[0]
invalid_rule = uuid.uuid4()
context = {'policy_id': policy_id, 'rule_id': invalid_rule}
self.assertRaises(webservice.DataModelException,
self.status_model.get_item, None, {},
context=context)
def test_rule_status_invalid_policy_id(self):
invalid_policy = uuid.uuid4()
invalid_rule = uuid.uuid4()
context = {'policy_id': invalid_policy, 'rule_id': invalid_rule}
self.assertRaises(webservice.DataModelException,
self.status_model.get_item, None, {},
context=context)

View File

@ -1,149 +0,0 @@
# Copyright (c) 2015 OpenStack Foundation
#
# 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.
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 congress.api import webservice
from congress.tests import base
from congress.tests2.api import base as api_base
class TestTableModel(base.SqlTestCase):
def setUp(self):
super(TestTableModel, self).setUp()
services = api_base.setup_config()
self.policy_model = services['api']['api-policy']
self.table_model = services['api']['api-table']
self.api_rule = services['api']['api-rule']
self.node = services['node']
self.engine = services['engine']
self.data = services['data']
# create test policy
self._create_test_policy()
def _create_test_policy(self):
# create policy
self.policy_model.add_item({"name": 'test-policy'}, {})
def test_get_datasource_table_with_id(self):
context = {'ds_id': self.data.service_id,
'table_id': 'fake_table'}
expected_ret = {'id': 'fake_table'}
ret = self.table_model.get_item('fake_table', {}, context)
self.assertEqual(expected_ret, ret)
def test_get_datasource_table_with_name(self):
context = {'ds_id': self.data.service_id,
'table_id': 'fake_table'}
expected_ret = {'id': 'fake_table'}
ret = self.table_model.get_item('fake_table', {}, context)
self.assertEqual(expected_ret, ret)
def test_get_invalid_datasource(self):
context = {'ds_id': 'invalid-id',
'table_id': 'fake_table'}
self.assertRaises(webservice.DataModelException,
self.table_model.get_item, 'fake_table',
{}, context)
def test_get_invalid_datasource_table(self):
context = {'ds_id': self.data.service_id,
'table_id': 'invalid-table'}
expected_ret = None
ret = self.table_model.get_item('invalid-table', {}, context)
self.assertEqual(expected_ret, ret)
def test_get_policy_table(self):
context = {'policy_id': 'test-policy',
'table_id': 'p'}
expected_ret = {'id': 'p'}
self.api_rule.add_item({'rule': 'p(x) :- q(x)'}, {}, context=context)
self.api_rule.add_item({'rule': 'q(x) :- r(x)'}, {}, context=context)
ret = self.table_model.get_item('p', {}, context)
self.assertEqual(expected_ret, ret)
def test_get_invalid_policy(self):
context = {'policy_id': 'test-policy',
'table_id': 'fake-table'}
invalid_context = {'policy_id': 'invalid-policy',
'table_id': 'fake-table'}
expected_ret = None
self.api_rule.add_item({'rule': 'p(x) :- q(x)'}, {}, context=context)
self.api_rule.add_item({'rule': 'q(x) :- r(x)'}, {}, context=context)
ret = self.table_model.get_item('test-policy',
{}, invalid_context)
self.assertEqual(expected_ret, ret)
def test_get_invalid_policy_table(self):
context = {'policy_id': 'test-policy',
'table_id': 'fake-table'}
invalid_context = {'policy_id': 'test-policy',
'table_id': 'invalid-name'}
expected_ret = None
self.api_rule.add_item({'rule': 'p(x) :- q(x)'}, {}, context=context)
self.api_rule.add_item({'rule': 'q(x) :- r(x)'}, {}, context=context)
ret = self.table_model.get_item('test-policy', {},
invalid_context)
self.assertEqual(expected_ret, ret)
def test_get_items_datasource_table(self):
context = {'ds_id': self.data.service_id}
expected_ret = {'results': [{'id': 'fake_table'}]}
ret = self.table_model.get_items({}, context)
self.assertEqual(expected_ret, ret)
def test_get_items_invalid_datasource(self):
context = {'ds_id': 'invalid-id',
'table_id': 'fake-table'}
self.assertRaises(webservice.DataModelException,
self.table_model.get_items, {}, context)
def _get_id_list_from_return(self, result):
return [r['id'] for r in result['results']]
def test_get_items_policy_table(self):
context = {'policy_id': 'test-policy'}
expected_ret = {'results': [{'id': x} for x in ['q', 'p', 'r']]}
self.api_rule.add_item({'rule': 'p(x) :- q(x)'}, {}, context=context)
self.api_rule.add_item({'rule': 'q(x) :- r(x)'}, {}, context=context)
ret = self.table_model.get_items({}, context)
self.assertEqual(set(self._get_id_list_from_return(expected_ret)),
set(self._get_id_list_from_return(ret)))
def test_get_items_invalid_policy(self):
context = {'policy_id': 'test-policy'}
invalid_context = {'policy_id': 'invalid-policy'}
expected_ret = None
self.api_rule.add_item({'rule': 'p(x) :- q(x)'}, {}, context=context)
self.api_rule.add_item({'rule': 'q(x) :- r(x)'}, {}, context=context)
ret = self.table_model.get_items({}, invalid_context)
self.assertEqual(expected_ret, ret)

View File

@ -1,76 +0,0 @@
# Copyright (c) 2016 Styra, 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.
#
import eventlet
import mock
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.datasources import datasource_driver
from congress.tests import base
from congress.tests.datasources import test_datasource_driver
from congress.tests.datasources import test_driver
class TestDS(test_datasource_driver.TestDatasourceDriver):
pass
class TestPollingDataSourceDriver(base.TestCase):
class TestDriver(datasource_driver.PollingDataSourceDriver):
def __init__(self):
super(TestPollingDataSourceDriver.TestDriver, self).__init__(
'', '', None, None, None)
self.node = 'node'
self._rpc_server = mock.MagicMock()
self._init_end_start_poll()
def setUp(self):
super(TestPollingDataSourceDriver, self).setUp()
@mock.patch.object(eventlet, 'spawn')
def test_init_consistence(self, mock_spawn):
test_driver = TestPollingDataSourceDriver.TestDriver()
mock_spawn.assert_not_called()
self.assertIsNone(test_driver.worker_greenthread)
test_driver.start()
mock_spawn.assert_called_once_with(test_driver.poll_loop,
test_driver.poll_time)
self.assertTrue(test_driver.initialized)
self.assertIsNotNone(test_driver.worker_greenthread)
@mock.patch.object(eventlet.greenthread, 'kill')
@mock.patch.object(eventlet, 'spawn')
def test_cleanup(self, mock_spawn, mock_kill):
dummy_thread = dict()
mock_spawn.return_value = dummy_thread
test_driver = TestPollingDataSourceDriver.TestDriver()
test_driver.start()
self.assertEqual(test_driver.worker_greenthread, dummy_thread)
test_driver.stop()
mock_kill.assert_called_once_with(dummy_thread)
self.assertIsNone(test_driver.worker_greenthread)
class TestExecution(test_datasource_driver.TestExecutionDriver):
pass
class TestDriver(test_driver.TestDriver):
pass

View File

@ -1,260 +0,0 @@
# Copyright (c) 2014 OpenStack Foundation
#
# 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.
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 congress import exception
from congress.managers import datasource as datasource_manager
from congress.tests import base
from congress.tests import fake_datasource
from congress.tests import helper
from congress.policy_engines.agnostic import Dse2Runtime
class TestDataSourceManager(base.SqlTestCase):
def setUp(self):
super(TestDataSourceManager, self).setUp()
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource'])
self.datasource_mgr = datasource_manager.DataSourceManager
self.datasource_mgr.validate_configured_drivers()
node = helper.make_dsenode_new_partition('testnode')
self.dseNode = node
engine = Dse2Runtime('engine')
node.register_service(engine)
self.datasource_mgr.set_dseNode(node)
def _get_datasource_request(self):
return {'id': 'asdf',
'name': 'aaron',
'driver': '',
'description': 'hello world!',
'enabled': True,
'type': None,
'config': {}}
def test_make_datasource_dict(self):
req = self._get_datasource_request()
result = self.datasource_mgr.make_datasource_dict(req)
self.assertEqual(req, result)
def test_validate_create_datasource_invalid_driver(self):
req = self._get_datasource_request()
self.assertRaises(exception.InvalidDriver,
self.datasource_mgr.validate_create_datasource,
req)
def test_validate_create_datasource_invalid_config_invalid_options(self):
req = self._get_datasource_request()
req['driver'] = 'invalid_datasource'
self.assertRaises(exception.InvalidDriver,
self.datasource_mgr.validate_create_datasource,
req)
def test_validate_create_datasource_missing_config_options(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
# This is still missing some required options
req['config'] = {'auth_url': '1234'}
self.assertRaises(exception.MissingRequiredConfigOptions,
self.datasource_mgr.validate_create_datasource,
req)
def test_add_datasource(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
result = self.datasource_mgr.add_datasource(req)
for key, value in req.items():
self.assertEqual(value, result[key])
# TODO(thinrichs): test that ensure the DB, the policy engine,
# and the datasource manager are all in sync
def test_get_datasouce(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
result = self.datasource_mgr.add_datasource(req)
result = self.datasource_mgr.get_datasource(result['id'])
for key, value in req.items():
self.assertEqual(value, result[key])
def test_get_datasources(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['name'] = 'datasource1'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
self.datasource_mgr.add_datasource(req)
req['name'] = 'datasource2'
self.datasource_mgr.add_datasource(req)
result = self.datasource_mgr.get_datasources()
req['name'] = 'datasource1'
for key, value in req.items():
self.assertEqual(value, result[0][key])
req['name'] = 'datasource2'
for key, value in req.items():
self.assertEqual(value, result[1][key])
def test_get_datasources_hide_secret(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['name'] = 'datasource1'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
self.datasource_mgr.add_datasource(req)
req['name'] = 'datasource2'
self.datasource_mgr.add_datasource(req)
# Value will be set as <hidden>
req['config']['password'] = "<hidden>"
result = self.datasource_mgr.get_datasources(filter_secret=True)
req['name'] = 'datasource1'
for key, value in req.items():
self.assertEqual(value, result[0][key])
req['name'] = 'datasource2'
for key, value in req.items():
self.assertEqual(value, result[1][key])
def test_create_datasource_duplicate_name(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['name'] = 'datasource1'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
self.datasource_mgr.add_datasource(req)
self.assertRaises(exception.DatasourceNameInUse,
self.datasource_mgr.add_datasource, req)
# TODO(dse2): need to implement dseNode.unregister_service to enable
# delete_datasource
#
# def test_delete_datasource(self):
# req = self._get_datasource_request()
# req['driver'] = 'fake_datasource'
# req['config'] = {'auth_url': 'foo',
# 'username': 'armax',
# 'password': 'password',
# 'tenant_name': 'armax'}
# # let driver generate this for us.
# del req['id']
# result = self.datasource_mgr.add_datasource(req)
# self.datasource_mgr.delete_datasource(result['id'])
# self.assertRaises(datasource_manager.DatasourceNotFound,
# self.datasource_mgr.get_datasource,
# result['id'])
# engine = self.cage.service_object('engine')
# self.assertRaises(exception.PolicyRuntimeException,
# engine.assert_policy_exists, req['name'])
# # TODO(thinrichs): test that we've actually removed
# # the row from the DB
def test_delete_datasource_error(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
result = self.datasource_mgr.add_datasource(req)
engine = self.dseNode.service_object('engine')
engine.create_policy('alice')
engine.insert('p(x) :- %s:q(x)' % req['name'], 'alice')
self.assertRaises(exception.DanglingReference,
self.datasource_mgr.delete_datasource,
result['id'])
def test_delete_invalid_datasource(self):
self.assertRaises(exception.DatasourceNotFound,
self.datasource_mgr.delete_datasource,
"does_not_exist")
def test_get_driver_schema(self):
schema = self.datasource_mgr.get_driver_schema(
'fake_datasource')
self.assertEqual(
schema,
fake_datasource.FakeDataSource.get_schema())
def test_get_datasouce_schema_driver_not_found(self):
self.assertRaises(exception.DatasourceNotFound,
self.datasource_mgr.get_datasource_schema,
"does_not_exist")
def test_duplicate_driver_name_raises(self):
# Load the driver twice
cfg.CONF.set_override(
'drivers',
['congress.tests.fake_datasource.FakeDataSource',
'congress.tests.fake_datasource.FakeDataSource'])
self.datasource_mgr = datasource_manager.DataSourceManager
self.assertRaises(exception.BadConfig,
self.datasource_mgr.validate_configured_drivers)
def test_datasource_spawn_datasource_poll(self):
req = self._get_datasource_request()
req['driver'] = 'fake_datasource'
req['config'] = {'auth_url': 'foo',
'username': 'armax',
'password': 'password',
'tenant_name': 'armax'}
# let driver generate this for us.
del req['id']
result = self.datasource_mgr.add_datasource(req)
self.datasource_mgr.request_refresh(result['id'])
# TODO(thinrichs): test that the driver actually polls
def test_datasource_spawn_datasource_poll_not_found(self):
self.assertRaises(exception.DatasourceNotFound,
self.datasource_mgr.request_refresh,
"does_not_exist")

View File

@ -1,460 +0,0 @@
# -*- 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
import mock
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
import neutronclient.v2_0
from oslo_log import log as logging
from congress.datasources import neutronv2_driver
from congress.datasources import nova_driver
from congress import harness
from congress.tests import base
from congress.tests.datasources import test_neutron_driver as test_neutron
from congress.tests import helper
from congress.tests2.api import base as api_base
LOG = logging.getLogger(__name__)
class BaseTestPolicyCongress(base.SqlTestCase):
def setUp(self):
super(BaseTestPolicyCongress, self).setUp()
self.services = api_base.setup_config(with_fake_datasource=False)
self.api = self.services['api']
self.node = self.services['node']
self.engine = self.services['engine']
self.neutronv2 = self._create_neutron_mock('neutron')
def _create_neutron_mock(self, name):
# Register Neutron service
args = helper.datasource_openstack_args()
neutronv2 = neutronv2_driver.NeutronV2Driver(name, args=args)
self.node.register_service(neutronv2)
neutron_mock = mock.MagicMock(spec=neutronclient.v2_0.client.Client)
neutronv2.neutron = neutron_mock
# 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.return_value = network1
neutron_mock.list_ports.return_value = port_response
neutron_mock.list_routers.return_value = router_response
neutron_mock.list_security_groups.return_value = sg_group_response
return neutronv2
class TestCongress(BaseTestPolicyCongress):
def setUp(self):
"""Setup tests that use multiple mock neutron instances."""
super(TestCongress, self).setUp()
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: sorted(self.query('q', 'alpha')['results']),
sorted([{'data': (1, 2)}, {'data': (2, 3)}]))
helper.retry_check_function_return_value(
lambda: list(self.query('q', 'alpha').keys()),
['results'])
def test_policy_datasource(self):
self.create_policy('alpha')
self.create_fake_datasource('fake')
self.engine.synchronize_policies()
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)
def test_rule_insert_delete(self):
self.api['api-policy'].add_item({'name': 'alice'}, {})
context = {'policy_id': 'alice'}
(id1, _) = self.api['api-rule'].add_item(
{'rule': 'p(x) :- plus(y, 1, x), q(y)'}, {}, context=context)
ds = self.api['api-rule'].get_items({}, context)['results']
self.assertEqual(len(ds), 1)
self.api['api-rule'].delete_item(id1, {}, context)
ds = self.engine.policy_object('alice').content()
self.assertEqual(len(ds), 0)
def test_datasource_request_refresh(self):
# neutron polls automatically here, which is why register_service
# starts its service.
neutron = self.neutronv2
neutron.stop()
self.assertEqual(neutron.refresh_request_queue.qsize(), 0)
neutron.request_refresh()
self.assertEqual(neutron.refresh_request_queue.qsize(), 1)
neutron.start()
neutron.request_refresh()
f = lambda: neutron.refresh_request_queue.qsize()
helper.retry_check_function_return_value(f, 0)
def test_datasource_poll(self):
neutron = self.neutronv2
neutron.stop()
neutron._translate_ports({'ports': []})
self.assertEqual(len(neutron.state['ports']), 0)
neutron.start()
f = lambda: len(neutron.state['ports'])
helper.retry_check_function_return_value_not_eq(f, 0)
class APILocalRouting(BaseTestPolicyCongress):
def setUp(self):
super(APILocalRouting, self).setUp()
# set up second API+PE node
self.services = api_base.setup_config(
with_fake_datasource=False, node_id='testnode2',
same_partition_as_node=self.node)
self.api2 = self.services['api']
self.node2 = self.services['node']
self.engine2 = self.services['engine']
self.data = self.services['data']
# add different data to two PE instances
# going directly to agnostic not via API to make sure faulty API
# routing (subject of the test) would not affect test accuracy
self.engine.create_policy('policy')
self.engine2.create_policy('policy')
self.engine.insert('p(1) :- NOT q()', 'policy')
# self.engine1.insert('p(1)', 'policy')
self.engine2.insert('p(2) :- NOT q()', 'policy')
self.engine2.insert('p(3) :- NOT q()', 'policy')
def test_intranode_pe_routing(self):
for i in range(0, 5): # run multiple times (non-determinism)
result = self.api['api-row'].get_items(
{}, {'policy_id': 'policy', 'table_id': 'p'})
self.assertEqual(len(result['results']), 1)
result = self.api2['api-row'].get_items(
{}, {'policy_id': 'policy', 'table_id': 'p'})
self.assertEqual(len(result['results']), 2)
def test_non_PE_service_reachable(self):
# intranode
result = self.api['api-row'].get_items(
{}, {'ds_id': 'neutron', 'table_id': 'ports'})
self.assertEqual(len(result['results']), 1)
# internode
result = self.api2['api-row'].get_items(
{}, {'ds_id': 'neutron', 'table_id': 'ports'})
self.assertEqual(len(result['results']), 1)
def test_internode_pe_routing(self):
'''test reach internode PE when intranode PE not available'''
self.node.unregister_service('engine')
result = self.api['api-row'].get_items(
{}, {'policy_id': 'policy', 'table_id': 'p'})
self.assertEqual(len(result['results']), 2)
result = self.api2['api-row'].get_items(
{}, {'policy_id': 'policy', 'table_id': 'p'})
self.assertEqual(len(result['results']), 2)
class TestPolicyExecute(BaseTestPolicyCongress):
def setUp(self):
super(TestPolicyExecute, self).setUp()
self.nova = self._register_test_datasource('nova')
def _register_test_datasource(self, name):
args = helper.datasource_openstack_args()
if name == 'nova':
ds = nova_driver.NovaDriver('nova', args=args)
if name == 'neutron':
ds = neutronv2_driver.NeutronV2Driver('neutron', args=args)
self.node.register_service(ds)
ds.update_from_datasource = mock.MagicMock()
return ds
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("testing")
nova = self.nova
nova.nova_client = nova_client
# insert rule and data
self.api['api-policy'].add_item({'name': 'alice'}, {})
(id1, _) = self.api['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['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['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['api-rule'].add_item(
{'rule': 'r(3)'}, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 2)
# delete relevant data
self.api['api-rule'].delete_item(
id2, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 2)
# delete policy rule
self.api['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.nova
nova.nova_client = nova_client
# insert rule and data
self.api['api-policy'].add_item({'name': 'alice'}, {})
self.api['api-rule'].add_item(
{'rule': 'q(1)'}, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 0)
self.api['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.nova
nova.nova_client = nova_client
self.api['api-policy'].add_item({'name': 'alice'}, {})
self.api['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['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.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['api-policy'].add_item({'name': 'alice'}, {})
(id1, rule1) = self.api['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['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['api-rule'].add_item(
{'rule': 'q(2)'}, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 1)
# delete all data
self.api['api-rule'].delete_item(
id2, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 1)
self.api['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['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['api-rule'].delete_item(
id1, {}, context={'policy_id': 'alice'})
self.assertEqual(len(self.engine.logger.messages), 2)
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.neutronv2
neutron.neutron = neutron_client
# insert rule and data
self.api['api-policy'].add_item({'name': 'alice'}, {})
(id1, _) = self.api['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['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_neutron_policy_poll_and_subscriptions(self):
"""Test polling and publishing of neutron updates."""
policy = self.engine.DEFAULT_THEORY
neutron2 = self._create_neutron_mock('neutron2')
self.engine.initialize_datasource('neutron',
self.neutronv2.get_schema())
self.engine.initialize_datasource('neutron2',
self.neutronv2.get_schema())
str_rule = ('p(x0, y0) :- neutron:networks(x0, x1, x2, x3, x4, x5), '
'neutron2:networks(y0, y1, y2, y3, y4, y5)')
rule = {'rule': str_rule, 'name': 'testrule1', 'comment': 'test'}
self.api['api-rule'].add_item(rule, {}, context={'policy_id': policy})
# Test policy subscriptions
subscriptions = self.engine.subscription_list()
self.assertEqual(sorted([('neutron', 'networks'),
('neutron2', 'networks')]), sorted(subscriptions))
# Test multiple instances
self.neutronv2.poll()
neutron2.poll()
ans = ('p("240ff9df-df35-43ae-9df5-27fae87f2492", '
' "240ff9df-df35-43ae-9df5-27fae87f2492") ')
helper.retry_check_db_equal(self.engine, 'p(x, y)', ans, target=policy)

View File

@ -1,94 +0,0 @@
# Copyright (c) 2016 Styra, 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.
#
from oslo_config import cfg
cfg.CONF.distributed_architecture = True
from congress.tests.datalog import test_compiler
from congress.tests.datalog import test_factset
from congress.tests.datalog import test_materialized
from congress.tests.datalog import test_nonrecur
from congress.tests.datalog import test_ordered_set
from congress.tests.datalog import test_ruleset
from congress.tests.datalog import test_unify
from congress.tests.datalog import test_utility
class TestCompilerParser(test_compiler.TestParser):
pass
class TestCompilerColumn(test_compiler.TestColumnReferences):
pass
class TestCompilerCompiler(test_compiler.TestCompiler):
pass
class TestCompilerGraph(test_compiler.TestDependencyGraph):
pass
class TestFact(test_factset.TestFactSet):
pass
class TestMaterialized(test_materialized.TestRuntime):
pass
class TestNonrecurRuntime(test_nonrecur.TestRuntime):
pass
class TestNonrecurNegation(test_nonrecur.TestSelectNegation):
pass
class TestNonrecurArity(test_nonrecur.TestArity):
pass
class TestNonrecurInstances(test_nonrecur.TestInstances):
pass
class TestOrdered(test_ordered_set.TestOrderedSet):
pass
class TestRuleSet(test_ruleset.TestRuleSet):
pass
class TestUnifyUnify(test_unify.TestUnify):
pass
class TestUnifyMatch(test_unify.TestMatch):
pass
class TestUtilityGraph(test_utility.TestGraph):
pass
class TestUtilityBagGraph(test_utility.TestBagGraph):
pass
class TestUtilityIterstr(test_utility.TestIterstr):
pass

View File

@ -0,0 +1,9 @@
# need to run at least 1 test or tox fails
from congress.tests import base
class TestRuntime(base.TestCase):
def test_noop(self):
pass