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:
parent
68b220e179
commit
e2bea76426
@ -270,6 +270,16 @@
|
|||||||
#db_max_retries=20
|
#db_max_retries=20
|
||||||
|
|
||||||
|
|
||||||
|
[engine]
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options defined in murano.common.config
|
||||||
|
#
|
||||||
|
|
||||||
|
# Disallow the use of murano-agent (boolean value)
|
||||||
|
#disable_murano_agent=false
|
||||||
|
|
||||||
|
|
||||||
[heat]
|
[heat]
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -168,6 +168,12 @@ stats_opt = [
|
|||||||
'Default value is 5 minutes.'))
|
'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',
|
metadata_dir = cfg.StrOpt('metadata-dir', default='./meta',
|
||||||
help='Metadata dir')
|
help='Metadata dir')
|
||||||
|
|
||||||
@ -196,6 +202,7 @@ CONF.register_opts(heat_opts, group='heat')
|
|||||||
CONF.register_opts(neutron_opts, group='neutron')
|
CONF.register_opts(neutron_opts, group='neutron')
|
||||||
CONF.register_opts(keystone_opts, group='keystone')
|
CONF.register_opts(keystone_opts, group='keystone')
|
||||||
CONF.register_opts(murano_opts, group='murano')
|
CONF.register_opts(murano_opts, group='murano')
|
||||||
|
CONF.register_opts(engine_opts, group='engine')
|
||||||
CONF.register_opt(cfg.StrOpt('file_server'))
|
CONF.register_opt(cfg.StrOpt('file_server'))
|
||||||
CONF.register_cli_opt(cfg.StrOpt('murano_metadata_url'))
|
CONF.register_cli_opt(cfg.StrOpt('murano_metadata_url'))
|
||||||
CONF.register_cli_opt(metadata_dir)
|
CONF.register_cli_opt(metadata_dir)
|
||||||
|
@ -20,7 +20,9 @@ import types
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import eventlet.event
|
import eventlet.event
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import murano.common.config as config
|
||||||
import murano.common.messaging as messaging
|
import murano.common.messaging as messaging
|
||||||
import murano.dsl.murano_class as murano_class
|
import murano.dsl.murano_class as murano_class
|
||||||
import murano.dsl.murano_object as murano_object
|
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
|
import murano.engine.system.common as common
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AgentException(Exception):
|
class AgentException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -35,19 +40,36 @@ class AgentException(Exception):
|
|||||||
@murano_class.classname('io.murano.system.Agent')
|
@murano_class.classname('io.murano.system.Agent')
|
||||||
class Agent(murano_object.MuranoObject):
|
class Agent(murano_object.MuranoObject):
|
||||||
def initialize(self, _context, host):
|
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()"
|
"$host.find('io.murano.Environment').require()"
|
||||||
).evaluate(_context)
|
).evaluate(_context)
|
||||||
|
|
||||||
self._queue = str('e%s-h%s' % (
|
@property
|
||||||
environment.object_id, host.object_id)).lower()
|
def enabled(self):
|
||||||
self._environment = environment
|
return self._enabled
|
||||||
|
|
||||||
def queueName(self):
|
def queueName(self):
|
||||||
return self._queue
|
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)
|
msg_id = template.get('ID', uuid.uuid4().hex)
|
||||||
if wait_results:
|
if wait_results:
|
||||||
event = eventlet.event.Event()
|
event = eventlet.event.Event()
|
||||||
@ -77,17 +99,21 @@ class Agent(murano_object.MuranoObject):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def call(self, template, resources):
|
def call(self, template, resources):
|
||||||
|
self._check_enabled()
|
||||||
plan = self.buildExecutionPlan(template, resources)
|
plan = self.buildExecutionPlan(template, resources)
|
||||||
return self._send(plan, True)
|
return self._send(plan, True)
|
||||||
|
|
||||||
def send(self, template, resources):
|
def send(self, template, resources):
|
||||||
|
self._check_enabled()
|
||||||
plan = self.buildExecutionPlan(template, resources)
|
plan = self.buildExecutionPlan(template, resources)
|
||||||
return self._send(plan, False)
|
return self._send(plan, False)
|
||||||
|
|
||||||
def callRaw(self, plan):
|
def callRaw(self, plan):
|
||||||
|
self._check_enabled()
|
||||||
return self._send(plan, True)
|
return self._send(plan, True)
|
||||||
|
|
||||||
def sendRaw(self, plan):
|
def sendRaw(self, plan):
|
||||||
|
self._check_enabled()
|
||||||
return self._send(plan, False)
|
return self._send(plan, False)
|
||||||
|
|
||||||
def _process_v1_result(self, result):
|
def _process_v1_result(self, result):
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
import eventlet
|
import eventlet
|
||||||
|
|
||||||
|
import murano.common.config as config
|
||||||
import murano.dsl.murano_class as murano_class
|
import murano.dsl.murano_class as murano_class
|
||||||
import murano.dsl.murano_object as murano_object
|
import murano.dsl.murano_object as murano_object
|
||||||
import murano.engine.system.common as common
|
import murano.engine.system.common as common
|
||||||
@ -24,26 +25,49 @@ from murano.openstack.common import log as logging
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AgentListenerException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@murano_class.classname('io.murano.system.AgentListener')
|
@murano_class.classname('io.murano.system.AgentListener')
|
||||||
class AgentListener(murano_object.MuranoObject):
|
class AgentListener(murano_object.MuranoObject):
|
||||||
def initialize(self, _context, name):
|
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._results_queue = str('-execution-results-%s' % name.lower())
|
||||||
self._subscriptions = {}
|
self._subscriptions = {}
|
||||||
self._receive_thread = None
|
self._receive_thread = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self):
|
||||||
|
return self._enabled
|
||||||
|
|
||||||
def queueName(self):
|
def queueName(self):
|
||||||
return self._results_queue
|
return self._results_queue
|
||||||
|
|
||||||
def start(self):
|
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:
|
if self._receive_thread is None:
|
||||||
self._receive_thread = eventlet.spawn(self._receive)
|
self._receive_thread = eventlet.spawn(self._receive)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
# _receive_thread will be None if agent is disabled
|
||||||
if self._receive_thread is not None:
|
if self._receive_thread is not None:
|
||||||
self._receive_thread.kill()
|
self._receive_thread.kill()
|
||||||
self._receive_thread = None
|
self._receive_thread = None
|
||||||
|
|
||||||
def subscribe(self, message_id, event):
|
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
|
self._subscriptions[message_id] = event
|
||||||
|
|
||||||
def _receive(self):
|
def _receive(self):
|
||||||
|
16
murano/tests/dsl/meta/AgentListenerTests.yaml
Normal file
16
murano/tests/dsl/meta/AgentListenerTests.yaml
Normal 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
|
16
murano/tests/dsl/meta/AgentTests.yaml
Normal file
16
murano/tests/dsl/meta/AgentTests.yaml
Normal 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
|
82
murano/tests/dsl/test_agent.py
Normal file
82
murano/tests/dsl/test_agent.py
Normal 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, {})
|
Loading…
Reference in New Issue
Block a user