Use only strings in paste.deploy.loadapp global_conf

1. encode the flags as json
2. pass node_id, bus_id rather than DseNode instance.
   Create DseNode in harness not eventlet_server.

Rationale: global_conf not meant to take non-strings.
Somehow got away with it in python2, but fails in python3.

When dict (and DseNode) passed through global_conf,
paste.deploy.loadapp adds them to configparser defaults,
causing errors when configparser calls string methods on them.

File "/usr/lib/python3.4/configparser.py",
    line 390, in _interpolate_some
    p = rest.find("%")
AttributeError:
    Error in file congress/tests/etc/api-paste.ini:
    'dict' object has no attribute 'find'

Change-Id: I549b9281e72512bb804a7d6f07bf6482e9485ae4
This commit is contained in:
Eric K 2016-09-13 16:13:08 -07:00
parent 40b3b3473b
commit 7c6d7c5ad2
6 changed files with 37 additions and 22 deletions

View File

@ -29,6 +29,7 @@ import sys
import eventlet
import eventlet.wsgi
import greenlet
import json
from oslo_config import cfg
from oslo_log import log as logging
from oslo_service import service
@ -99,34 +100,30 @@ class APIServer(service.ServiceBase):
self.keepalive = keepalive
self.keepidle = keepidle
self.socket = None
self.node = None
self.bus_id = bus_id
# store API, policy-engine, datasource flags; for use in start()
self.flags = kwargs
# 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."""
if self.node is not None:
self.node.start()
if self.socket is None:
self.listen(key=key, backlog=backlog)
try:
kwargs = {'global_conf':
{'node': [self.node],
'flags': self.flags}}
{'node_id': self.name,
'bus_id': self.bus_id,
'flags': json.dumps(self.flags)}}
self.application = deploy.loadapp('config:%s' % self.app_conf,
name='congress', **kwargs)
except Exception:
LOG.exception('Failed to Start %s server', self.node.node_id)
LOG.exception('Failed to Start %s server', self.name)
raise exception.CongressException(
'Failed to Start initializing %s server' % self.node.node_id)
'Failed to Start initializing %s server' % self.name)
self.greenthread = self.pool.spawn(self._run,
self.application,
@ -198,8 +195,8 @@ class APIServer(service.ServiceBase):
def stop(self):
self.kill()
if self.node is not None:
self.node.stop()
# We're not able to stop the DseNode in this case. Is there a need to
# stop the ApiServer without also exiting the process?
def reset(self):
LOG.info("reset() not implemented yet")

View File

@ -39,6 +39,7 @@ from congress.api import status_model
from congress.api.system import driver_model
from congress.api import table_model
from congress.db import datasources as db_datasources
from congress.dse2 import dse_node
from congress import exception
from congress.policy_engines import agnostic
@ -47,18 +48,31 @@ LOG = logging.getLogger(__name__)
ENGINE_SERVICE_NAME = 'engine'
def create2(node, policy_engine=True, datasources=True, api=True):
def create2(node_id=None, bus_id=None, existing_node=None,
policy_engine=True, datasources=True, api=True):
"""Get Congress up.
Creates a DseNode if one is not provided and adds policy_engine,
datasources, api to that node.
:param node is a DseNode
:param node_id is node_id of DseNode to be created
:param bus_id is partition_id of DseNode to be created
:param existing_node is a DseNode (optional; in lieu of previous 2 params)
:param policy_engine controls whether policy_engine is included
:param datasources controls whether datasources are included
:param api controls whether API is included
:returns DseNode
"""
# create DseNode if existing_node not given
if existing_node is None:
assert (not (node_id is None or bus_id is None)),\
'params node_id and bus_id required.'
node = dse_node.DseNode(cfg.CONF, node_id, [], partition_id=bus_id)
else:
assert (node_id is None and bus_id is None),\
'params node_id and bus_id must be None when existing_node given.'
node = existing_node
# create services as required
services = {}
if api:

View File

@ -103,7 +103,7 @@ def create_api_server(conf_path, node_id, host, port, workers, policy_engine,
def create_nonapi_server(node_id, policy_engine, datasources, workers):
congress_server = eventlet_server.Server(
node_id, bus_id=cfg.CONF.dse.bus_id)
harness.create2(node=congress_server.node, api=False,
harness.create2(existing_node=congress_server.node, api=False,
policy_engine=policy_engine,
datasources=datasources)
return node_id, ServerWrapper(congress_server, workers)

View File

@ -15,6 +15,7 @@ from __future__ import division
from __future__ import absolute_import
import functools
import json
import sys
from oslo_log import log as logging
@ -41,9 +42,11 @@ def fail_gracefully(f):
@fail_gracefully
def congress_app_factory(global_conf, **local_conf):
# global_conf only accepts an iteratable value as its dict value
flags_dict = json.loads(global_conf['flags'])
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'])
node_id=global_conf['node_id'],
bus_id=global_conf['bus_id'],
policy_engine=flags_dict['policy_engine'],
api=flags_dict['api'],
datasources=flags_dict['datasources'])
return application.ApiApplication(services['api_service'])

View File

@ -46,8 +46,9 @@ def setup_config(with_fake_datasource=True, node_id='testnode',
cfg.CONF.set_override('datasources', True)
with mock.patch.object(periodics, 'PeriodicWorker', autospec=True):
services = harness.create2(node=node, policy_engine=policy, api=api,
datasources=datasources)
services = harness.create2(
existing_node=node, policy_engine=policy, api=api,
datasources=datasources)
data = None
if with_fake_datasource:

View File

@ -198,7 +198,7 @@ class TestDsePerformance(testbase.SqlTestCase):
[('congress.tests.datasources.performance_datasource_driver'
'.PerformanceTestDriver')])
self.cage = helper.make_dsenode_new_partition("perf")
harness.create2(self.cage)
harness.create2(existing_node=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'),