Allow murano-agent to be disabled

In some circumstances murano-agent isn't required (e.g. in
environments where heat SW config is capable alone of performing
configuration). In this case it's not necessary to have the
additional overhead of rabbitMQ connections for the AgentListener
that will never receive a message.

Patch adds a config option 'disable_murano_agent' that no-ops
AgentLister.start() and raises an exception on Agent._send()

Change-Id: I565caaae21925c48f2a0adea18036239cac91c77
Implements: blueprint disable-murano-agent
This commit is contained in:
Steve McLellan 2014-07-25 17:59:33 -05:00
parent 68b220e179
commit e2bea76426
7 changed files with 186 additions and 5 deletions

View File

@ -270,6 +270,16 @@
#db_max_retries=20
[engine]
#
# Options defined in murano.common.config
#
# Disallow the use of murano-agent (boolean value)
#disable_murano_agent=false
[heat]
#

View File

@ -168,6 +168,12 @@ stats_opt = [
'Default value is 5 minutes.'))
]
engine_opts = [
cfg.BoolOpt('disable_murano_agent', default=False,
help=_('Disallow the use of murano-agent'))
]
# TODO(sjmc7): move into engine opts?
metadata_dir = cfg.StrOpt('metadata-dir', default='./meta',
help='Metadata dir')
@ -196,6 +202,7 @@ CONF.register_opts(heat_opts, group='heat')
CONF.register_opts(neutron_opts, group='neutron')
CONF.register_opts(keystone_opts, group='keystone')
CONF.register_opts(murano_opts, group='murano')
CONF.register_opts(engine_opts, group='engine')
CONF.register_opt(cfg.StrOpt('file_server'))
CONF.register_cli_opt(cfg.StrOpt('murano_metadata_url'))
CONF.register_cli_opt(metadata_dir)

View File

@ -20,7 +20,9 @@ import types
import uuid
import eventlet.event
import logging
import murano.common.config as config
import murano.common.messaging as messaging
import murano.dsl.murano_class as murano_class
import murano.dsl.murano_object as murano_object
@ -28,6 +30,9 @@ import murano.dsl.yaql_expression as yaql_expression
import murano.engine.system.common as common
LOG = logging.getLogger(__name__)
class AgentException(Exception):
pass
@ -35,19 +40,36 @@ class AgentException(Exception):
@murano_class.classname('io.murano.system.Agent')
class Agent(murano_object.MuranoObject):
def initialize(self, _context, host):
environment = yaql_expression.YaqlExpression(
self._enabled = False
if config.CONF.engine.disable_murano_agent:
LOG.debug("murano-agent is disabled by the server")
return
self._environment = self._get_environment(_context)
self._enabled = True
self._queue = str('e%s-h%s' % (
self._environment.object_id, host.object_id)).lower()
def _get_environment(self, _context):
return yaql_expression.YaqlExpression(
"$host.find('io.murano.Environment').require()"
).evaluate(_context)
self._queue = str('e%s-h%s' % (
environment.object_id, host.object_id)).lower()
self._environment = environment
@property
def enabled(self):
return self._enabled
def queueName(self):
return self._queue
def _send(self, template, wait_results):
def _check_enabled(self):
if config.CONF.engine.disable_murano_agent:
raise AgentException(
"Use of murano-agent is disallowed "
"by the server configuration")
def _send(self, template, wait_results):
"""Send a message over the MQ interface"""
msg_id = template.get('ID', uuid.uuid4().hex)
if wait_results:
event = eventlet.event.Event()
@ -77,17 +99,21 @@ class Agent(murano_object.MuranoObject):
return None
def call(self, template, resources):
self._check_enabled()
plan = self.buildExecutionPlan(template, resources)
return self._send(plan, True)
def send(self, template, resources):
self._check_enabled()
plan = self.buildExecutionPlan(template, resources)
return self._send(plan, False)
def callRaw(self, plan):
self._check_enabled()
return self._send(plan, True)
def sendRaw(self, plan):
self._check_enabled()
return self._send(plan, False)
def _process_v1_result(self, result):

View File

@ -15,6 +15,7 @@
import eventlet
import murano.common.config as config
import murano.dsl.murano_class as murano_class
import murano.dsl.murano_object as murano_object
import murano.engine.system.common as common
@ -24,26 +25,49 @@ from murano.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class AgentListenerException(Exception):
pass
@murano_class.classname('io.murano.system.AgentListener')
class AgentListener(murano_object.MuranoObject):
def initialize(self, _context, name):
self._enabled = False
if config.CONF.engine.disable_murano_agent:
return
self._enabled = True
self._results_queue = str('-execution-results-%s' % name.lower())
self._subscriptions = {}
self._receive_thread = None
@property
def enabled(self):
return self._enabled
def queueName(self):
return self._results_queue
def start(self):
if config.CONF.engine.disable_murano_agent:
# Noop
LOG.debug("murano-agent is disabled by the server")
return
if self._receive_thread is None:
self._receive_thread = eventlet.spawn(self._receive)
def stop(self):
# _receive_thread will be None if agent is disabled
if self._receive_thread is not None:
self._receive_thread.kill()
self._receive_thread = None
def subscribe(self, message_id, event):
if config.CONF.engine.disable_murano_agent:
raise AgentListenerException(
"Use of murano-agent is disallowed "
"by the server configuration")
self._subscriptions[message_id] = event
def _receive(self):

View File

@ -0,0 +1,16 @@
Name: AgentListenerTests
Namespaces:
sys: io.murano.system
Properties:
agentListener:
Contract: $.class(sys:AgentListener)
Usage: Runtime
Methods:
testAgentListener:
Body:
- $.agentListener: new(sys:AgentListener, name => 'hello')
- Return: $.agentListener

View File

@ -0,0 +1,16 @@
Name: AgentTests
Namespaces:
sys: io.murano.system
Properties:
agent:
Contract: $.class(sys:Agent)
Usage: Runtime
Methods:
testAgent:
Body:
- $.agent: new(sys:Agent, host => $)
- Return: $.agent

View File

@ -0,0 +1,82 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# 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 mock
from murano.engine.system import agent
from murano.engine.system import agent_listener
from murano.tests.dsl.foundation import object_model as om
from murano.tests.dsl.foundation import test_case
class TestAgentListener(test_case.DslTestCase):
def setUp(self):
super(TestAgentListener, self).setUp()
# Register Agent class
self.class_loader.import_class(agent_listener.AgentListener)
model = om.Object(
'AgentListenerTests')
self.runner = self.new_runner(model)
def test_listener_enabled(self):
self.override_config('disable_murano_agent', False, 'engine')
al = self.runner.testAgentListener()
self.assertTrue(al.enabled)
al.subscribe('msgid', 'event')
self.assertEqual({'msgid': 'event'}, al._subscriptions)
def test_listener_disabled(self):
self.override_config('disable_murano_agent', True, 'engine')
al = self.runner.testAgentListener()
self.assertFalse(al.enabled)
self.assertRaises(agent_listener.AgentListenerException,
al.subscribe, 'msgid', 'event')
class TestAgent(test_case.DslTestCase):
def setUp(self):
super(TestAgent, self).setUp()
# Register Agent class
self.class_loader.import_class(agent.Agent)
model = om.Object(
'AgentTests')
self.runner = self.new_runner(model)
def test_agent_enabled(self):
self.override_config('disable_murano_agent', False, 'engine')
m = mock.MagicMock()
# Necessary because otherwise there'll be an Environment lookup
agent_cls = 'murano.engine.system.agent.Agent'
with mock.patch(agent_cls + '._get_environment') as f:
f.return_value = m
a = self.runner.testAgent()
self.assertTrue(a.enabled)
self.assertEqual(m, a._environment)
with mock.patch(agent_cls + '._send') as s:
s.return_value = mock.MagicMock()
a.sendRaw({})
s.assert_called_with({}, False)
def test_agent_disabled(self):
self.override_config('disable_murano_agent', True, 'engine')
a = self.runner.testAgent()
self.assertFalse(a.enabled)
self.assertRaises(agent.AgentException, a.call, {}, None)
self.assertRaises(agent.AgentException, a.send, {}, None)
self.assertRaises(agent.AgentException, a.callRaw, {})
self.assertRaises(agent.AgentException, a.sendRaw, {})