Merge "Add worker-multiplier for reactive charms"

This commit is contained in:
Jenkins 2017-08-03 15:01:57 +00:00 committed by Gerrit Code Review
commit 729234dde1
4 changed files with 153 additions and 0 deletions

View File

@ -24,6 +24,7 @@ import charms.reactive as reactive
import charms.reactive.bus
import charmhelpers.contrib.hahelpers.cluster as ch_cluster
import charmhelpers.contrib.network.ip as ch_ip
import charmhelpers.contrib.openstack.context as ch_context
import charmhelpers.contrib.openstack.utils as ch_utils
import charmhelpers.core.hookenv as hookenv
import charmhelpers.core.host as ch_host
@ -860,6 +861,63 @@ class APIConfigurationAdapter(ConfigurationAdapter):
ctxt['memcache_port'])
return ctxt
@property
@hookenv.cached
def workers(self):
"""Return the a number of workers that depends on the
config('worker_muliplier') and the number of cpus. This function uses
the charmhelpers.contrib.openstack.context.WorkerConfigContext() to do
the heavy lifting so that any changes in charmhelpers propagate to this
function
:returns: <int> the number of workers to apply to a configuration file.
"""
return ch_context.WorkerConfigContext()()["workers"]
@property
@hookenv.cached
def wsgi_worker_context(self):
"""Return a WSGIWorkerConfigContext dictionary.
This is used to configure a WSGI worker. The charm_instance class can
define some attributes (or properties - anything getattr(...) will work
against for:
wsgi_script: a script/name to pass to the WSGIW... constructor
wsgi_admin_script: a script/name to pass to the WSGIW...
constructor
wsgi_public_script: a script/name to pass to the WSGIW...
constructor
wsgi_process_weight: an float between 0.0 and 1.0 to split the
share of all workers between main, admin and public workers.
wsgi_admin_process_weight: an float between 0.0 and 1.0 to split
the share of all workers between main, admin and public workers
wsgi_public_process_weight: an float between 0.0 and 1.0 to split
the share of all workers between main, admin and public workers
The sum of the process weights should equal 1 to make sense.
:returns: WSGIWorkerConfigContext dictionary.
"""
charm_instance = self.charm_instance or {}
kwargs = dict(
name=getattr(charm_instance, 'name', None),
script=getattr(charm_instance, 'wsgi_script', None),
admin_script=getattr(charm_instance, 'wsgi_admin_script', None),
public_script=getattr(charm_instance, 'wsgi_public_script', None),
process_weight=getattr(
charm_instance, 'wsgi_process_weight', None),
admin_process_weight=getattr(
charm_instance, 'wsgi_admin_process_weight', None),
public_process_weight=getattr(
charm_instance, 'wsgi_public_process_weight', None),
)
# filtering the kwargs of Nones allows the default arguments on
# WSGIWorkerConfigContext.__init__(...) to be used.
filtered_kwargs = dict((k, v) for k, v in kwargs.items()
if v is not None)
return ch_context.WSGIWorkerConfigContext(**filtered_kwargs)()
def make_default_relation_adapter(base_cls, relation, properties):
"""Create a default relation adapter using a base class, and custom

View File

@ -146,6 +146,18 @@ class OpenStackAPICharm(OpenStackCharm):
# If None, then the default ConfigurationAdapter is used.
configuration_class = os_adapters.APIConfigurationAdapter
# These can be overriden in the derived charm class to allow specialism of
# config files. These values are read in the APIConfigurationAdapter and
# used to furnish the dictionary provided from the property
# 'wsgi_worker_context'. e.g. config.wsgi_worker_context.processes would
# be the number of processes for the main API wsgi worker.
wsgi_script = None
wsgi_admin_script = None
wsgi_public_script = None
wsgi_process_weight = None # use the default from charm-helpers
wsgi_admin_process_weight = None # use the default from charm-helpers
wsgi_public_process_weight = None # use the default from charm-helpers
def upgrade_charm(self):
"""Setup token cache in case previous charm version did not."""
self.setup_token_cache()

View File

@ -32,6 +32,8 @@ sys.modules['charmhelpers.contrib.openstack.utils'] = (
charmhelpers.contrib.openstack.utils)
sys.modules['charmhelpers.contrib.openstack.templating'] = (
charmhelpers.contrib.openstack.templating)
sys.modules['charmhelpers.contrib.openstack.context'] = (
charmhelpers.contrib.openstack.context)
sys.modules['charmhelpers.contrib.network'] = charmhelpers.contrib.network
sys.modules['charmhelpers.contrib.network.ip'] = (
charmhelpers.contrib.network.ip)

View File

@ -790,6 +790,87 @@ class TestAPIConfigurationAdapter(unittest.TestCase):
memcache.return_value = {'memcache_url': 'hello'}
self.assertEquals(c.memcache_url, 'hello')
def test_workers(self):
class FakeWorkerConfigContext(object):
def __call__(self):
return {"workers": 8}
with mock.patch.object(adapters.ch_context, 'WorkerConfigContext',
new=FakeWorkerConfigContext):
c = adapters.APIConfigurationAdapter()
self.assertEquals(c.workers, 8)
def test_wsgi_worker_context(self):
class ChInstance1(object):
name = 'test-name'
wsgi_script = 'test-script'
api_ports = {}
class ChInstance2(object):
name = 'test-name'
wsgi_script = 'test-script'
wsgi_admin_script = 'test-admin-script'
wsgi_public_script = 'test-public-script'
wsgi_process_weight = 0.5
wsgi_admin_process_weight = 0.1
wsgi_public_process_weight = 0.4
api_ports = {}
class ChInstance3(object):
name = 'test-name'
wsgi_script = None
wsgi_admin_script = 'test-admin-script'
wsgi_public_script = 'test-public-script'
wsgi_process_weight = None
wsgi_admin_process_weight = 0.1
wsgi_public_process_weight = 0.4
api_ports = {}
class FakeWSGIWorkerConfigContext():
copy_kwargs = None
def __init__(self, **kwargs):
self.__class__.copy_kwargs = kwargs.copy()
def __call__(self):
return "T"
with mock.patch.object(adapters.ch_context, 'WSGIWorkerConfigContext',
new=FakeWSGIWorkerConfigContext):
# start with no charm instance to get default values
c = adapters.APIConfigurationAdapter()
self.assertEquals(c.wsgi_worker_context, "T")
self.assertEquals(FakeWSGIWorkerConfigContext.copy_kwargs, {})
# start with a minimal charm_instance
instance = ChInstance1()
c = adapters.APIConfigurationAdapter(charm_instance=instance)
self.assertEquals(c.wsgi_worker_context, "T")
self.assertEquals(FakeWSGIWorkerConfigContext.copy_kwargs,
{'name': 'test-name', 'script': 'test-script'})
# And then, all the options set:
instance = ChInstance2()
c = adapters.APIConfigurationAdapter(charm_instance=instance)
self.assertEquals(c.wsgi_worker_context, "T")
self.assertEquals(FakeWSGIWorkerConfigContext.copy_kwargs,
{'name': 'test-name',
'script': 'test-script',
'admin_script': 'test-admin-script',
'public_script': 'test-public-script',
'process_weight': 0.5,
'admin_process_weight': 0.1,
'public_process_weight': 0.4})
# and finally, with some of the options set to None, to test
# filtering
instance = ChInstance3()
c = adapters.APIConfigurationAdapter(charm_instance=instance)
self.assertEquals(c.wsgi_worker_context, "T")
self.assertEquals(FakeWSGIWorkerConfigContext.copy_kwargs,
{'name': 'test-name',
'admin_script': 'test-admin-script',
'public_script': 'test-public-script',
'admin_process_weight': 0.1,
'public_process_weight': 0.4})
class FakePeerHARelationAdapter(object):