3cef984de9
Base class for modify actions. Entry points based action registration. Action manager loads and execute actions. Out-of-the-box actions implementation. Partially implements: blueprint policy-based-env-modification Change-Id: Ib148c5f2b7efb1186d77f5461ef9324cca1287c2
158 lines
5.5 KiB
Python
158 lines
5.5 KiB
Python
# Copyright (c) 2014 OpenStack Foundation.
|
|
# All Rights Reserved.
|
|
#
|
|
# 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 re
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from murano.common.i18n import _, _LI
|
|
from murano.policy import congress_rules
|
|
from murano.policy.modify.actions import action_manager as am
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class ValidationError(Exception):
|
|
"""Raised for validation errors."""
|
|
pass
|
|
|
|
|
|
class ModelPolicyEnforcer(object):
|
|
"""Policy Enforcer Implementation using Congress client
|
|
|
|
Converts murano model to list of congress data rules.
|
|
|
|
We ask congress using simulation api of congress rest client
|
|
to resolve "murano_system:predeploy_errors(env_id, obj_id, msg)"
|
|
table along with congress data rules to return validation results.
|
|
"""
|
|
|
|
def __init__(self, environment, action_manager=None):
|
|
self._environment = environment
|
|
self._client_manager = environment.clients
|
|
self._action_manager = action_manager or am.ModifyActionManager()
|
|
|
|
def modify(self, obj, package_loader=None):
|
|
"""Modifies model using Congress rule engine.
|
|
|
|
@type obj: object model
|
|
@param obj: Representation of model starting on
|
|
environment level (['Objects'])
|
|
@type class_loader: murano.dsl.class_loader.MuranoClassLoader
|
|
@param class_loader: Optional. Used for evaluating parent class types
|
|
@raises ValidationError in case validation was not successful
|
|
"""
|
|
|
|
model = obj.to_dictionary()
|
|
|
|
LOG.debug('Modifying model')
|
|
LOG.debug(model)
|
|
|
|
env_id = model['?']['id']
|
|
|
|
result = self._execute_simulation(package_loader, env_id, model,
|
|
'predeploy_modify(eid, oid, action)')
|
|
|
|
raw_actions = result["result"]
|
|
if raw_actions:
|
|
actions = self._parse_simulation_result('predeploy_modify',
|
|
env_id, raw_actions)
|
|
for action in actions:
|
|
self._action_manager.apply_action(obj, action)
|
|
|
|
def validate(self, model, package_loader=None):
|
|
"""Validate model using Congress rule engine.
|
|
|
|
@type model: dict
|
|
@param model: Dictionary representation of model starting on
|
|
environment level (['Objects'])
|
|
@type package_loader: murano.dsl.package_loader.MuranoPackageLoader
|
|
@param package_loader: Optional. Used for evaluating parent class types
|
|
@raises ValidationError in case validation was not successful
|
|
"""
|
|
|
|
if model is None:
|
|
return
|
|
|
|
env_id = model['?']['id']
|
|
|
|
validation_result = self._execute_simulation(
|
|
package_loader, env_id, model,
|
|
'predeploy_errors(eid, oid, msg)')
|
|
|
|
if validation_result["result"]:
|
|
|
|
messages = self._parse_simulation_result(
|
|
'predeploy_errors', env_id,
|
|
validation_result["result"])
|
|
|
|
if messages:
|
|
result_str = "\n ".join(map(str, messages))
|
|
msg = _("Murano object model validation failed: {0}").format(
|
|
"\n " + result_str)
|
|
LOG.error(msg)
|
|
raise ValidationError(msg)
|
|
else:
|
|
LOG.info(_LI('Model valid'))
|
|
|
|
def _execute_simulation(self, package_loader, env_id, model, query):
|
|
rules = congress_rules.CongressRulesManager().convert(
|
|
model, package_loader, self._environment.tenant_id)
|
|
rules_str = map(str, rules)
|
|
# cleanup of data populated by murano driver
|
|
rules_str.insert(0, 'deleteEnv("{0}")'.format(env_id))
|
|
rules_line = " ".join(rules_str)
|
|
LOG.debug('Congress rules: \n {rules} '
|
|
.format(rules='\n '.join(rules_str)))
|
|
|
|
client = self._check_client()
|
|
|
|
validation_result = client.execute_policy_action(
|
|
"murano_system",
|
|
"simulate",
|
|
False,
|
|
False,
|
|
{'query': query,
|
|
'action_policy': 'murano_action',
|
|
'sequence': rules_line})
|
|
return validation_result
|
|
|
|
def _check_client(self):
|
|
client = self._client_manager.get_congress_client(self._environment)
|
|
if not client:
|
|
raise ValueError(_('Congress client is not configured!'))
|
|
return client
|
|
|
|
@staticmethod
|
|
def _parse_simulation_result(query, env_id, results):
|
|
"""Transforms list of strings in format
|
|
['predeploy_errors("env_id_1", "obj_id_1", "message1")',
|
|
'predeploy_errors("env_id_2", "obj_id_2", "message2")']
|
|
to list of strings with message only filtered to provided
|
|
env_id (e.g. 'env_id_1'):
|
|
['message2']
|
|
"""
|
|
|
|
messages = []
|
|
regexp = query + '\("([^"]*)",\s*"([^"]*)",\s*"(.*)"\)'
|
|
for result in results:
|
|
match = re.search(regexp, result)
|
|
if match:
|
|
if env_id in match.group(1):
|
|
messages.append(match.group(3))
|
|
|
|
return messages
|