241 lines
7.8 KiB
Python
241 lines
7.8 KiB
Python
# Copyright (c) 2014 Mirantis, Inc.
|
|
#
|
|
# 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 six
|
|
from six.moves import range
|
|
|
|
from murano.dsl import constants
|
|
from murano.dsl import dsl_exception
|
|
from murano.dsl import exceptions
|
|
from murano.dsl import expressions
|
|
from murano.dsl import helpers
|
|
from murano.dsl import yaql_expression
|
|
|
|
|
|
class CodeBlock(expressions.DslExpression):
|
|
def __init__(self, body):
|
|
if not isinstance(body, list):
|
|
body = [body]
|
|
self.code_block = map(expressions.parse_expression, body)
|
|
|
|
def execute(self, context):
|
|
for expr in self.code_block:
|
|
if hasattr(expr, 'virtual_instruction'):
|
|
instruction = expr.virtual_instruction
|
|
context[constants.CTX_CURRENT_INSTRUCTION] = instruction
|
|
|
|
try:
|
|
expr.execute(context)
|
|
except (dsl_exception.MuranoPlException,
|
|
exceptions.InternalFlowException):
|
|
raise
|
|
except Exception as ex:
|
|
raise dsl_exception.MuranoPlException.from_python_exception(
|
|
ex, context)
|
|
|
|
|
|
class MethodBlock(CodeBlock):
|
|
def __init__(self, body, name=None):
|
|
super(MethodBlock, self).__init__(body)
|
|
self._name = name
|
|
|
|
def execute(self, context):
|
|
new_context = context.create_child_context()
|
|
new_context[constants.CTX_VARIABLE_SCOPE] = True
|
|
try:
|
|
super(MethodBlock, self).execute(new_context)
|
|
except exceptions.ReturnException as e:
|
|
return e.value
|
|
except exceptions.BreakException:
|
|
raise exceptions.DslInvalidOperationError(
|
|
'Break cannot be used on method level')
|
|
except exceptions.ContinueException:
|
|
raise exceptions.DslInvalidOperationError(
|
|
'Continue cannot be used on method level')
|
|
else:
|
|
return None
|
|
|
|
|
|
class ReturnMacro(expressions.DslExpression):
|
|
def __init__(self, Return):
|
|
self._value = Return
|
|
|
|
def execute(self, context):
|
|
raise exceptions.ReturnException(
|
|
helpers.evaluate(self._value, context))
|
|
|
|
|
|
class BreakMacro(expressions.DslExpression):
|
|
def __init__(self, Break):
|
|
if Break:
|
|
raise exceptions.DslSyntaxError('Break cannot have value')
|
|
|
|
def execute(self, context):
|
|
raise exceptions.BreakException()
|
|
|
|
|
|
class ContinueMacro(expressions.DslExpression):
|
|
def __init__(self, Continue):
|
|
if Continue:
|
|
raise exceptions.DslSyntaxError('Continue cannot have value')
|
|
|
|
def execute(self, context):
|
|
raise exceptions.ContinueException()
|
|
|
|
|
|
class ParallelMacro(CodeBlock):
|
|
def __init__(self, Parallel, Limit=None):
|
|
super(ParallelMacro, self).__init__(Parallel)
|
|
self._limit = Limit or len(self.code_block)
|
|
|
|
def execute(self, context):
|
|
if not self.code_block:
|
|
return
|
|
limit = helpers.evaluate(self._limit, context)
|
|
helpers.parallel_select(
|
|
self.code_block,
|
|
lambda expr: expr.execute(context.create_child_context()),
|
|
limit)
|
|
|
|
|
|
class IfMacro(expressions.DslExpression):
|
|
def __init__(self, If, Then, Else=None):
|
|
self._code1 = CodeBlock(Then)
|
|
self._code2 = None if Else is None else CodeBlock(Else)
|
|
self._condition = If
|
|
|
|
def execute(self, context):
|
|
if helpers.evaluate(self._condition, context):
|
|
self._code1.execute(context)
|
|
elif self._code2 is not None:
|
|
self._code2.execute(context)
|
|
|
|
|
|
class WhileDoMacro(expressions.DslExpression):
|
|
def __init__(self, While, Do):
|
|
if not isinstance(While, yaql_expression.YaqlExpression):
|
|
raise TypeError()
|
|
self._code = CodeBlock(Do)
|
|
self._condition = While
|
|
|
|
def execute(self, context):
|
|
while self._condition(context):
|
|
try:
|
|
self._code.execute(context)
|
|
except exceptions.BreakException:
|
|
break
|
|
except exceptions.ContinueException:
|
|
continue
|
|
|
|
|
|
class ForMacro(expressions.DslExpression):
|
|
def __init__(self, For, In, Do):
|
|
if not isinstance(For, six.string_types):
|
|
raise exceptions.DslSyntaxError(
|
|
'For value must be of string type')
|
|
self._code = CodeBlock(Do)
|
|
self._var = For
|
|
self._collection = In
|
|
|
|
def execute(self, context):
|
|
collection = helpers.evaluate(self._collection, context)
|
|
for t in collection:
|
|
context[self._var] = t
|
|
try:
|
|
self._code.execute(context)
|
|
except exceptions.BreakException:
|
|
break
|
|
except exceptions.ContinueException:
|
|
continue
|
|
|
|
|
|
class RepeatMacro(expressions.DslExpression):
|
|
def __init__(self, Repeat, Do):
|
|
if not isinstance(Repeat, (int, yaql_expression.YaqlExpression)):
|
|
raise exceptions.DslSyntaxError(
|
|
'Repeat value must be either int or expression')
|
|
self._count = Repeat
|
|
self._code = CodeBlock(Do)
|
|
|
|
def execute(self, context):
|
|
count = helpers.evaluate(self._count, context)
|
|
for _ in range(0, count):
|
|
try:
|
|
self._code.execute(context)
|
|
except exceptions.BreakException:
|
|
break
|
|
except exceptions.ContinueException:
|
|
continue
|
|
|
|
|
|
class MatchMacro(expressions.DslExpression):
|
|
def __init__(self, Match, Value, Default=None):
|
|
if not isinstance(Match, dict):
|
|
raise exceptions.DslSyntaxError(
|
|
'Match value must be of dictionary type')
|
|
self._switch = Match
|
|
self._value = Value
|
|
self._default = None if Default is None else CodeBlock(Default)
|
|
|
|
def execute(self, context):
|
|
match_value = helpers.evaluate(self._value, context)
|
|
for key, value in six.iteritems(self._switch):
|
|
if key == match_value:
|
|
CodeBlock(value).execute(context)
|
|
return
|
|
if self._default is not None:
|
|
self._default.execute(context)
|
|
|
|
|
|
class SwitchMacro(expressions.DslExpression):
|
|
def __init__(self, Switch, Default=None):
|
|
if not isinstance(Switch, dict):
|
|
raise exceptions.DslSyntaxError(
|
|
'Switch value must be of dictionary type')
|
|
self._switch = Switch
|
|
self._default = None if Default is None else CodeBlock(Default)
|
|
|
|
def execute(self, context):
|
|
matched = False
|
|
for key, value in six.iteritems(self._switch):
|
|
if helpers.evaluate(key, context):
|
|
matched = True
|
|
CodeBlock(value).execute(context)
|
|
|
|
if self._default is not None and not matched:
|
|
self._default.execute(context)
|
|
|
|
|
|
class DoMacro(expressions.DslExpression):
|
|
def __init__(self, Do):
|
|
self._code = CodeBlock(Do)
|
|
|
|
def execute(self, context):
|
|
self._code.execute(context)
|
|
|
|
|
|
def register():
|
|
expressions.register_macro(DoMacro)
|
|
expressions.register_macro(ReturnMacro)
|
|
expressions.register_macro(BreakMacro)
|
|
expressions.register_macro(ContinueMacro)
|
|
expressions.register_macro(ParallelMacro)
|
|
expressions.register_macro(IfMacro)
|
|
expressions.register_macro(WhileDoMacro)
|
|
expressions.register_macro(ForMacro)
|
|
expressions.register_macro(RepeatMacro)
|
|
expressions.register_macro(MatchMacro)
|
|
expressions.register_macro(SwitchMacro)
|