Added per-datasource configuration

Previously, all datasources had the same username/password/tenant
configuration information.  Additionally, the name to module mapping
was stored in a different place (but each data source could have
a different module).

This change puts all of the configuration information for datasources
into a single file, and enables the configuration information to
be different for each datasource.

Closes-bug: #1356615
Change-Id: I0fc4274c452ff91663eded7828dc8d8679935b4e
This commit is contained in:
Tim Hinrichs 2014-08-13 09:31:24 -07:00
parent e81e63966b
commit f5b3209e39
16 changed files with 299 additions and 169 deletions

View File

@ -38,6 +38,8 @@ core_opts = [
'true. Not supported on OS X.'),
cfg.StrOpt('policy_path', default=None,
help="The path to the latest policy dump"),
cfg.StrOpt('datasource_file', default=None,
help="The file containing datasource configuration"),
cfg.IntOpt('api_workers', default=1,
help='The number of worker processes to serve the congress '
'API application.'),

View File

@ -22,17 +22,23 @@ import datetime
class DataSourceDriver(deepsix.deepSix):
def __init__(self, name, keys, inbox=None, datapath=None,
poll_time=None, **creds):
if poll_time is None:
poll_time = 10
def __init__(self, name, keys, inbox, datapath, args):
# TODO(thinrichs): not all datasources poll, though for now that's
# the only option. Create PollingDataSourceDriver subclass
# to handle the polling logic.
if args is None:
args = dict()
if 'poll_time' in args:
self.poll_time = args['poll_time']
else:
self.poll_time = 10
# default to open-stack credentials, since that's the common case
self.creds = self.get_credentials(name, args)
self.last_poll_time = None
# a dictionary from tablename to the SET of tuples, both currently
# and in the past.
self.prior_state = dict()
self.state = dict()
self.poll_time = poll_time
self.creds = creds
self.last_poll_time = None
# Make sure all data structures above are set up *before* calling
# this because it will publish info to the bus.
super(DataSourceDriver, self).__init__(name, keys, inbox, datapath)
@ -162,3 +168,29 @@ class DataSourceDriver(deepsix.deepSix):
seconds = diff.seconds + diff.days * 24 * 3600
if seconds > self.poll_time:
self.poll()
def get_credentials(self, name, config_args):
# TODO(thinrichs): Create OpenStack mixin that implements
# OpenStack-specific credential gathering, etc.
d = {}
missing = []
for field in ['username', 'password', 'auth_url', 'tenant_name']:
if field in config_args:
d[field] = config_args[field]
else:
missing.append(field)
if missing:
raise DataSourceConfigException(
"Service {} is missing configuration data for {}".format(
name, missing))
return d
def empty_credentials(self):
return {'username': '',
'password': '',
'auth_url': '',
'tenant_name': ''}
class DataSourceConfigException(Exception):
pass

View File

@ -17,8 +17,6 @@ import neutronclient.v2_0.client
import uuid
from congress.datasources.datasource_driver import DataSourceDriver
from congress.datasources.settings import OS_USERNAME, \
OS_PASSWORD, OS_AUTH_URL, OS_TENANT_NAME
from congress.openstack.common import log as logging
@ -31,25 +29,10 @@ def d6service(name, keys, inbox, datapath, args):
to add to that call, so we included them here instead of
modifying d6cage (and all the d6cage.createservice calls).
"""
if 'client' in args:
client = args['client']
del args['client']
else:
client = None
if 'poll_time' in args:
poll_time = args['poll_time']
del args['poll_time']
else:
poll_time = None
return NeutronDriver(name, keys, inbox=inbox, datapath=datapath,
client=client, poll_time=poll_time, **args)
return NeutronDriver(name, keys, inbox, datapath, args)
class NeutronDriver(DataSourceDriver):
USERNAME = OS_USERNAME
PASSWORD = OS_PASSWORD
AUTH_URL = OS_AUTH_URL
TENANT_NAME = OS_TENANT_NAME
NEUTRON_NETWORKS = "networks"
NEUTRON_NETWORKS_SUBNETS = "networks.subnets"
NEUTRON_PORTS = "ports"
@ -64,17 +47,14 @@ class NeutronDriver(DataSourceDriver):
NEUTRON_SUBNETS = "subnets"
last_updated = -1
def __init__(self, name='', keys='', inbox=None, datapath=None,
client=None, poll_time=None, **creds):
super(NeutronDriver, self).__init__(name, keys, inbox=inbox,
datapath=datapath,
poll_time=poll_time,
**creds)
credentials = self._get_credentials()
if client is None:
self.neutron = neutronclient.v2_0.client.Client(**credentials)
def __init__(self, name='', keys='', inbox=None, datapath=None, args=None):
if args is None:
args = self.empty_credentials()
super(NeutronDriver, self).__init__(name, keys, inbox, datapath, args)
if 'client' in args:
self.neutron = args['client']
else:
self.neutron = client
self.neutron = neutronclient.v2_0.client.Client(**self.creds)
self.raw_state = {}
# TODO(thinrichs): refactor this and the logic in datasource_driver.
@ -360,14 +340,6 @@ class NeutronDriver(DataSourceDriver):
LOG.debug("NEUTRON_SECURITY_GROUPS: %s",
str(self.security_groups))
def _get_credentials(self):
d = {}
d['username'] = self.USERNAME
d['password'] = self.PASSWORD
d['auth_url'] = self.AUTH_URL
d['tenant_name'] = self.TENANT_NAME
return d
# Sample Mapping
# Network :

View File

@ -17,8 +17,6 @@ import datetime
import novaclient.client
from congress.datasources.datasource_driver import DataSourceDriver
from congress.datasources.settings import OS_USERNAME, \
OS_PASSWORD, OS_AUTH_URL, OS_TENANT_NAME
def d6service(name, keys, inbox, datapath, args):
@ -27,18 +25,7 @@ def d6service(name, keys, inbox, datapath, args):
to add to that call, so we included them here instead of
modifying d6cage (and all the d6cage.createservice calls).
"""
if 'client' in args:
client = args['client']
del args['client']
else:
client = None
if 'poll_time' in args:
poll_time = args['poll_time']
del args['poll_time']
else:
poll_time = None
return NovaDriver(name, keys, inbox=inbox, datapath=datapath,
client=client, poll_time=poll_time, **args)
return NovaDriver(name, keys, inbox, datapath, args)
# TODO(thinrichs): figure out how to move even more of this boilerplate
@ -46,10 +33,6 @@ def d6service(name, keys, inbox, datapath, args):
# NeutronDriver, NovaDriver, etc. and move the d6instantiate function to
# DataSourceDriver.
class NovaDriver(DataSourceDriver):
USERNAME = OS_USERNAME
PASSWORD = OS_PASSWORD
AUTH_URL = OS_AUTH_URL
TENANT_NAME = OS_TENANT_NAME
SERVERS = "servers"
FLAVORS = "flavors"
HOSTS = "hosts"
@ -57,18 +40,15 @@ class NovaDriver(DataSourceDriver):
last_updated = -1
def __init__(self, name='', keys='', inbox=None, datapath=None,
client=None, poll_time=None, **creds):
super(NovaDriver, self).__init__(name, keys, inbox=inbox,
datapath=datapath,
poll_time=poll_time,
**creds)
credentials = self.get_nova_credentials_v2()
if client is None:
self.nova_client = novaclient.client.Client(**credentials)
def __init__(self, name='', keys='', inbox=None, datapath=None, args=None):
if args is None:
args = self.empty_credentials()
super(NovaDriver, self).__init__(name, keys, inbox, datapath, args)
if 'client' in args:
self.nova_client = args['client']
else:
self.nova_client = client
self.state = {}
self.creds = self.get_nova_credentials_v2(name, args)
self.nova_client = novaclient.client.Client(**self.creds)
def update_from_datasource(self):
self.state = {}
@ -123,13 +103,14 @@ class NovaDriver(DataSourceDriver):
def get_last_updated_time(self):
return self.last_updated
def get_nova_credentials_v2(self):
def get_nova_credentials_v2(self, name, args):
creds = self.get_credentials(name, args)
d = {}
d['version'] = '2'
d['username'] = self.USERNAME
d['api_key'] = self.PASSWORD
d['auth_url'] = self.AUTH_URL
d['project_id'] = self.TENANT_NAME
d['username'] = creds['username']
d['api_key'] = creds['password']
d['auth_url'] = creds['auth_url']
d['project_id'] = creds['tenant_name']
return d
def _get_tuple_list(self, obj, type):

View File

@ -27,24 +27,14 @@ def d6service(name, keys, inbox, datapath, args):
to add to that call, so we included them here instead of
modifying d6cage (and all the d6cage.createservice calls).
"""
if 'client' in args:
del args['client']
if 'poll_time' in args:
poll_time = args['poll_time']
del args['poll_time']
else:
poll_time = 0
return TestDriver(name, keys, inbox=inbox, datapath=datapath,
poll_time=poll_time, **args)
return TestDriver(name, keys, inbox, datapath, args)
class TestDriver(DataSourceDriver):
def __init__(self, name='', keys='', inbox=None, datapath=None,
poll_time=None, **creds):
super(TestDriver, self).__init__(name, keys, inbox=inbox,
datapath=datapath,
poll_time=poll_time,
**creds)
def __init__(self, name='', keys='', inbox=None, datapath=None, args=None):
if args is None:
args = self._empty_openstack_credentials()
super(TestDriver, self).__init__(name, keys, inbox, datapath, args)
self.msg = None
def receive_msg(self, msg):

View File

@ -17,11 +17,12 @@ import mock
import mox
import neutronclient.v2_0.client
from congress.datasources.datasource_driver import DataSourceConfigException
from congress.datasources.neutron_driver import NeutronDriver
import congress.dse.d6cage
import congress.policy.compile as compile
from congress.dse import d6cage
from congress.policy import compile
from congress.tests import base
import congress.tests.helper as helper
from congress.tests import helper
class TestNeutronDriver(base.TestCase):
@ -33,7 +34,9 @@ class TestNeutronDriver(base.TestCase):
self.ports = port_response
self.neutron_client.list_networks.return_value = self.network
self.neutron_client.list_ports.return_value = self.ports
self.driver = NeutronDriver(poll_time=0)
args = helper.datasource_openstack_args()
args['poll_time'] = 0
self.driver = NeutronDriver(args=args)
def test_list_networks(self):
"""Test conversion of complex network objects to tables."""
@ -183,18 +186,62 @@ class TestNeutronDriver(base.TestCase):
self.assertEqual("5cef03d0-1d02-40bb-8c99-2f442aac6ab0", subnet0)
self.assertEqual("100.0.0.1", ip0)
#### Tests for DataSourceDriver
# Note: these tests are really testing the functionality of the class
# DataSourceDriver, but it's useful to use an actual subclass so
# we can test the functionality end-to-end. We use Neutron for
# that subclass. Leaving it in this file so that it is clear
# that when the Neutron driver changes, these tests may need
# to change as well. Tried to minimize the number of changes
# necessary.
#### Tests for DataSourceDriver
# Note: these tests are really testing the functionality of the class
# DataSourceDriver, but it's useful to use an actual subclass so
# we can test the functionality end-to-end. We use Neutron for
# that subclass. Leaving it in this file so that it is clear
# that when the Neutron driver changes, these tests may need
# to change as well. Tried to minimize the number of changes
# necessary.
class TestDataSourceDriver(base.TestCase):
def test_config(self):
"""Test that Neutron throws an error when improperly configured."""
# username
args = helper.datasource_openstack_args()
del args['username']
try:
self.driver = NeutronDriver(args=args)
except DataSourceConfigException:
pass
else:
self.fail('NeutronDriver failed to throw username exception')
# password
args = helper.datasource_openstack_args()
del args['password']
try:
self.driver = NeutronDriver(args=args)
except DataSourceConfigException:
pass
else:
self.fail('NeutronDriver failed to throw password exception')
# auth_url
args = helper.datasource_openstack_args()
del args['auth_url']
try:
self.driver = NeutronDriver(args=args)
except DataSourceConfigException:
pass
else:
self.fail('NeutronDriver failed to throw auth_url exception')
args = helper.datasource_openstack_args()
del args['tenant_name']
try:
self.driver = NeutronDriver(args=args)
except DataSourceConfigException:
pass
else:
self.fail('NeutronDriver failed to throw tenant_name exception')
def setup_polling(self, debug_mode=False):
"""Setup polling tests."""
cage = congress.dse.d6cage.d6Cage()
cage = d6cage.d6Cage()
# so that we exit once test finishes; all other threads are forced
# to be daemons
cage.daemon = True
@ -220,10 +267,14 @@ class TestNeutronDriver(base.TestCase):
cage.loadModule("NeutronDriver",
helper.data_module_path("neutron_driver.py"))
cage.loadModule("PolicyDriver", helper.policy_module_path())
cage.createservice(name="policy", moduleName="PolicyDriver")
cage.createservice(name="policy", moduleName="PolicyDriver",
args={'d6cage': cage,
'rootdir': helper.data_module_path('')})
args = helper.datasource_openstack_args()
args['poll_time'] = 0
args['client'] = neutron_client
cage.createservice(name="neutron", moduleName="NeutronDriver",
args={'poll_time': 0,
'client': neutron_client})
args=args)
policy = cage.service_object('policy')
# Make it so that we get detailed info from policy engine

View File

@ -30,10 +30,10 @@ class TestNovaDriver(base.TestCase):
super(TestNovaDriver, self).setUp()
nova_client = MagicMock()
self.cs = fakes.NovaFakeClient()
with patch.object(novaclient.client.Client, '__init__',
return_value=nova_client):
self.driver = NovaDriver()
self.driver = NovaDriver(name='nova',
args=helper.datasource_openstack_args())
def test_driver_called(self):
self.assertIsNotNone(self.driver.nova_client)
@ -183,12 +183,15 @@ class TestNovaDriver(base.TestCase):
# 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")
cage.createservice(name="nova", moduleName="NovaDriver",
args={'poll_time': 0})
cage.createservice(name="policy", moduleName="PolicyDriver",
args={'d6cage': cage,
'rootdir': helper.data_module_path('')})
cage.createservice(name="nova", moduleName="NovaDriver", args=args)
# Check that data gets sent from nova to policy as expected
nova = cage.service_object('nova')

View File

@ -30,6 +30,7 @@ import pprint
from Queue import Queue
import sys
import threading
import traceback
from congress.dse.d6message import d6msg
from congress.dse.deepsix import deepSix
@ -175,10 +176,10 @@ class d6Cage(deepSix):
try:
self.log_info("loading module: %s" % (name))
imp.load_source(name, filename)
except Exception, errmsg:
except Exception:
raise DataServiceError(
"error loading module '%s' from '%s': %s" %
(name, filename, errmsg))
(name, filename, traceback.format_exc()))
def load_modules_from_config(self):
for section in self.config['modules'].keys():
@ -224,10 +225,10 @@ class d6Cage(deepSix):
inbox,
self.dataPath,
args)
except Exception, errmsg:
except Exception:
raise DataServiceError(
"Error loading service '%s' of module '%s': %s"
% (name, module, errmsg))
"Error loading service '%s' of module '%s':: \n%s"
% (name, module, traceback.format_exc()))
if svcObject:
self.log_info("created service: {}".format(name))

View File

@ -35,10 +35,10 @@ class TestDSE(unittest.TestCase):
cage.start()
cage.loadModule("TestDriver",
helper.data_module_path("test_driver.py"))
cage.createservice(name="test1", moduleName="TestDriver",
args={'poll_time': 0})
cage.createservice(name="test2", moduleName="TestDriver",
args={'poll_time': 0})
args = helper.datasource_openstack_args()
args['poll_time'] = 0
cage.createservice(name="test1", moduleName="TestDriver", args=args)
cage.createservice(name="test2", moduleName="TestDriver", args=args)
test1 = cage.service_object('test1')
test2 = cage.service_object('test2')
test1.subscribe('test2', 'p', callback=test1.receive_msg)
@ -65,8 +65,10 @@ class TestDSE(unittest.TestCase):
cage.loadModule("TestDriver",
helper.data_module_path("test_driver.py"))
cage.loadModule("TestPolicy", helper.policy_module_path())
cage.createservice(name="data", moduleName="TestDriver")
cage.createservice(name="policy", moduleName="TestPolicy")
cage.createservice(name="data", moduleName="TestDriver",
args=helper.datasource_openstack_args())
cage.createservice(name="policy", moduleName="TestPolicy",
args={'d6cage': cage, 'rootdir': ''})
data = cage.services['data']['object']
policy = cage.services['policy']['object']
policy.subscribe('data', 'p', callback=policy.receive_msg)
@ -84,8 +86,10 @@ class TestDSE(unittest.TestCase):
cage.loadModule("TestDriver",
helper.data_module_path("test_driver.py"))
cage.loadModule("TestPolicy", helper.policy_module_path())
cage.createservice(name="data", moduleName="TestDriver")
cage.createservice(name="policy", moduleName="TestPolicy")
cage.createservice(name="data", moduleName="TestDriver",
args=helper.datasource_openstack_args())
cage.createservice(name="policy", moduleName="TestPolicy",
args={'d6cage': cage, 'rootdir': ''})
data = cage.services['data']['object']
policy = cage.services['policy']['object']
policy.subscribe('data', 'p', callback=policy.receive_data)
@ -106,10 +110,13 @@ class TestDSE(unittest.TestCase):
cage.loadModule("TestDriver",
helper.data_module_path("test_driver.py"))
cage.loadModule("TestPolicy", helper.policy_module_path())
cage.createservice(name="data", moduleName="TestDriver")
cage.createservice(name="data", moduleName="TestDriver",
args=helper.datasource_openstack_args())
# using regular testdriver as API for now
cage.createservice(name="api", moduleName="TestDriver")
cage.createservice(name="policy", moduleName="TestPolicy")
cage.createservice(name="api", moduleName="TestDriver",
args=helper.datasource_openstack_args())
cage.createservice(name="policy", moduleName="TestPolicy",
args={'d6cage': cage, 'rootdir': ''})
data = cage.services['data']['object']
api = cage.services['api']['object']
policy = cage.services['policy']['object']

View File

@ -13,6 +13,8 @@
# under the License.
#
import ConfigParser
import os
import os.path
from congress.dse import d6cage
@ -22,12 +24,13 @@ from congress.openstack.common import log as logging
LOG = logging.getLogger(__name__)
def create(rootdir, statedir):
def create(rootdir, statedir, datasource_config):
"""Get Congress up and running when src is installed in rootdir,
i.e. ROOTDIR=/path/to/congress/congress.
"""
LOG.debug("Starting Congress with rootdir={} and statedir={}".format(
rootdir, statedir))
LOG.debug("Starting Congress with rootdir={}, statedir={}, "
"datasource_config={}".format(
rootdir, statedir, datasource_config))
# create message bus
cage = d6cage.d6Cage()
@ -35,6 +38,9 @@ def create(rootdir, statedir):
cage.start()
cage.system_service_names.add(cage.name)
# read in datasource configurations
cage.config = initialize_config(datasource_config)
# add policy engine
engine_path = os.path.join(rootdir, "policy/dsepolicy.py")
LOG.info("main::start() engine_path: " + str(engine_path))
@ -116,3 +122,20 @@ def create(rootdir, statedir):
engine.subscribe('api-rule', 'policy-update',
callback=engine.receive_policy_update)
return cage
def initialize_config(config_file):
"""Turn config_file into a dictionary of dictionaries, and in so
doing insulate rest of code from idiosyncracies of ConfigParser.
"""
config = ConfigParser.ConfigParser()
config.readfp(open(config_file))
d = {}
for section in config.sections():
e = {}
for opt in config.options(section):
e[opt] = config.get(section, opt)
d[section] = e
LOG.info("Configuration found for {} services: {}".format(
len(d.keys()), ";".join(d.keys())))
return d

View File

@ -13,19 +13,21 @@
# under the License.
#
import os
import re
import sys
import congress.dse.deepsix as deepsix
import congress.policy.compile as compile
import congress.policy.runtime as runtime
from congress.datasources.datasource_driver import DataSourceConfigException
from congress.dse import deepsix
from congress.openstack.common import log as logging
from congress.policy import compile
from congress.policy import runtime
class PolicyServiceMismatch (Exception):
pass
LOG = logging.getLogger(__name__)
def d6service(name, keys, inbox, datapath, args):
return DseRuntime(name, keys, inbox=inbox, dataPath=datapath, **args)
return DseRuntime(name, keys, inbox, datapath, args)
def parse_tablename(tablename):
@ -38,14 +40,13 @@ def parse_tablename(tablename):
class DseRuntime (runtime.Runtime, deepsix.deepSix):
def __init__(self, name, keys, inbox=None, dataPath=None, d6cage=None,
rootdir=None):
def __init__(self, name, keys, inbox, datapath, args):
runtime.Runtime.__init__(self)
deepsix.deepSix.__init__(self, name, keys, inbox=inbox,
dataPath=dataPath)
dataPath=datapath)
self.msg = None
self.d6cage = d6cage
self.rootdir = rootdir
self.d6cage = args['d6cage']
self.rootdir = args['rootdir']
def receive_msg(self, msg):
self.log("received msg " + str(msg))
@ -170,33 +171,25 @@ class DseRuntime (runtime.Runtime, deepsix.deepSix):
been loaded. Also loads module if that has not already been
loaded.
"""
# TODO(thinrichs): work in d6cage's ability to reload a module,
# so that driver updates can be handled without shutting
# everything down. A separate API call?
# TODO(thinrichs): Move all this functionality to a different
# component whose responsibility is spinning these up,
# checking they are still alive, restarting, reporting status, etc.
# Probably d6cage (or a subclass of it).
if self.d6cage is None:
# policy engine is running without ability to create services
return
if service_name in self.d6cage.services:
return
# service("service1", "modulename")
# module("modulename", "/path/to/code.py")
query = ('ans(name, path) :- service("{}", name), '
' module(name, path)').format(service_name)
modules = self.select(query, self.SERVICE_THEORY)
modules = compile.parse(modules)
# TODO(thinrichs): figure out what to do if we can't load the right
# data service. For now we reject the policy update.
# Would be better to have runtime accept policy and ignore rules
# that rely on unknown data. Then if there's a modification
# to the SERVICES policy later, we create the service, and as
# soon as it publishes data, the ignored rules become active.
# Deletions from SERVICES policy should have the opposite effect.
if len(modules) != 1:
raise PolicyServiceMismatch(
"Could not find module for service " + service_name)
module = modules[0] # instance of QUERY above
module_name = module.head.arguments[0].name
module_path = module.head.arguments[1].name
if service_name not in self.d6cage.config:
raise DataSourceConfigException(
"Service %s used in rule but not configured; "
"tables will be empty" % service_name)
service_config = self.d6cage.config[service_name]
if 'module' not in service_config:
raise DataSourceConfigException(
"Service %s config missing 'module' entry" % service_name)
module_path = service_config['module']
module_name = re.sub('[^a-zA-Z0-9_]', '_', module_path)
if not os.path.isabs(module_path) and self.rootdir is not None:
module_path = os.path.join(self.rootdir, module_path)
if module_name not in sys.modules:
@ -205,7 +198,8 @@ class DseRuntime (runtime.Runtime, deepsix.deepSix):
self.d6cage.loadModule(module_name, module_path)
self.log("Trying to create service {} with module {}".format(
service_name, module_name))
self.d6cage.createservice(name=service_name, moduleName=module_name)
self.d6cage.createservice(name=service_name, moduleName=module_name,
args=service_config)
# since both deepSix and Runtime define log (and differently),
# need to switch between them explicitly

View File

@ -49,7 +49,12 @@ def congress_app_factory(global_conf, **local_conf):
policy_path = cfg.CONF.policy_path
if policy_path is None:
policy_path = src_path
cage = harness.create(src_path, policy_path)
data_path = cfg.CONF.datasource_file
if data_path is None:
data_path = os.path.dirname(src_path)
data_path = os.path.join(data_path, 'etc', 'datasources.conf')
cage = harness.create(src_path, policy_path, data_path)
api_resource_mgr = application.ResourceManager()
congress_server.initialize_resources(api_resource_mgr, cage)

View File

@ -0,0 +1,22 @@
[neutron]
module: datasources/neutron_driver.py
username: demo
password: password
auth_url: http://127.0.0.1:5000/v2.0
tenant_name: demo
[neutron2]
module: datasources/neutron_driver.py
username: demo
password: password
auth_url: http://127.0.0.1:5000/v2.0
tenant_name: demo
[nova]
module: datasources/nova_driver.py
username: demo
password: password
auth_url: http://127.0.0.1:5000/v2.0
tenant_name: demo

View File

@ -26,6 +26,7 @@ LOG = logging.getLogger(__name__)
def source_path():
"""Return path to root of source code."""
x = os.path.realpath(__file__)
x, y = os.path.split(x) # drop "helper.py"
x, y = os.path.split(x) # drop "tests"
@ -49,13 +50,42 @@ def policy_module_path():
def api_module_path():
"""Return path to policy engine module."""
"""Return path to api module."""
path = source_path()
path = os.path.join(path, "datasources")
path = os.path.join(path, "test_driver.py")
return path
def test_path():
"""Return path to root of top-level tests."""
path = source_path()
path = os.path.join(path, "tests")
return path
def datasource_config_path():
"""Return path to configuration info for datasources."""
path = test_path()
path = os.path.join(path, "tests", "datasources.conf")
return path
def state_path():
"""Return path to policy logs for testing."""
path = test_path()
path = os.path.join(path, "snapshot")
return path
def datasource_openstack_args():
"""Return basic args for creating an openstack datasource."""
return {'username': '',
'password': '',
'auth_url': '',
'tenant_name': ''}
def pause(factor=1):
"""Timeout so other threads can run."""
time.sleep(factor * 1)

View File

@ -91,10 +91,19 @@ class TestCongress(base.TestCase):
os.makedirs(path)
return path
@classmethod
def config_path(cls):
"""Return path to the filename for datasource config."""
path = os.path.realpath(__file__)
path = os.path.dirname(path) # drop off file
path = os.path.join(path, "datasources.conf")
return path
def setUp(self):
super(TestCongress, self).setUp()
logging.getLogger().setLevel(logging.DEBUG)
cage = harness.create(helper.source_path(), self.state_path())
cage = harness.create(helper.source_path(), self.state_path(),
self.config_path())
engine = cage.service_object('engine')
api = {'policy': cage.service_object('api-policy'),
'rule': cage.service_object('api-rule'),

View File

@ -0,0 +1,8 @@
[neutron]
module: datasources/neutron_driver.py
username: demo
password: password
auth_url: http://127.0.0.1:5000/v2.0
tenant_name: demo