Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
ARIA-139 Support attributes
* Fully implement attribute support in parser
* New intrinsic function evaluation mechanism
* Implemented more intrinsic functions, including get_attribute
* Fix to one-on-one relationship back population
* Fixes to TOSCA use case examples
* Indirectly related: re-enabled node_filter mechanism and reworked
filter constraints in order to make them serializable
* utils/type is much more robust now and consolidates all conversions
and names
* Moved dsl_specification to new utils/specification (because utils/type
uses it)
  • Loading branch information
tliron committed May 11, 2017
1 parent 2ee06b8 commit 60ea3ebb21e762d36115db26563a93dd3cb72003
Showing 45 changed files with 1,210 additions and 603 deletions.
@@ -77,10 +77,14 @@ def create_service(self, service_template_id, inputs, service_name=None):
consumption.ConsumerChain(
context,
(
consumption.CoerceServiceInstanceValues,
consumption.ValidateServiceInstance,
consumption.SatisfyRequirements,
consumption.CoerceServiceInstanceValues,
consumption.ValidateCapabilities,
consumption.FindHosts,
consumption.ConfigureOperations
consumption.ConfigureOperations,
consumption.CoerceServiceInstanceValues
)).consume()
if context.validation.dump_issues():
raise exceptions.InstantiationError('Failed to instantiate service template')
@@ -0,0 +1,28 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.


class NodeTemplateConstraint(object):
"""
Used to constrain requirements for node templates.
Must be serializable.
"""

def matches(self, source_node_template, target_node_template):
"""
Returns true is the target matches the constraint for the source.
"""
raise NotImplementedError
@@ -13,20 +13,118 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from ..parser.consumption import ConsumptionContext
from ..parser.exceptions import InvalidValueError
from ..utils.collections import OrderedDict
from . import exceptions


class Function(object):
"""
An intrinsic function.
Base class for intrinsic functions. Serves as a placeholder for a value that should eventually
be derived by "evaluating" (calling) the function.
Serves as a placeholder for a value that should eventually be derived by calling the function.
Note that this base class is provided as a convenience and you do not have to inherit it: any
object with an ``__evaluate__`` method would be treated similarly.
"""

@property
def as_raw(self):
raise NotImplementedError

def _evaluate(self, context, container):
def __evaluate__(self, container_holder):
"""
Evaluates the function if possible. If impossible, raises
:class:`CannotEvaluateFunctionException` (do not just return None).
:rtype: Evaluation (or any object with ``value`` and ``final`` properties)
"""

raise NotImplementedError

def __deepcopy__(self, memo):
# Circumvent cloning in order to maintain our state
return self


class Evaluation(object):
"""
An evaluated :class:`Function` return value.
"""

def __init__(self, value, final=False):
self.value = value
self.final = final


def evaluate(value, container_holder, report_issues=False): # pylint: disable=too-many-branches
"""
Recursively attempts to call ``__evaluate__``. If an evaluation occurred will return an
:class:`Evaluation`, otherwise it will be None. If any evaluation is non-final, then the entire
evaluation will also be non-final.
The ``container_holder`` argument should have three properties: ``container`` should return
the model that contains the value, ``service`` should return the containing
:class:`aria.modeling.models.Service` model or None, and ``service_template`` should return the
containing :class:`aria.modeling.models.ServiceTemplate` model or None.
"""

evaluated = False
final = True

if hasattr(value, '__evaluate__'):
try:
evaluation = value.__evaluate__(container_holder)

# Verify evaluation structure
if (evaluation is None) \
or (not hasattr(evaluation, 'value')) \
or (not hasattr(evaluation, 'final')):
raise InvalidValueError('bad __evaluate__ implementation')

evaluated = True
value = evaluation.value
final = evaluation.final

# The evaluated value might itself be evaluable
evaluation = evaluate(value, container_holder, report_issues)
if evaluation is not None:
value = evaluation.value
if not evaluation.final:
final = False
except exceptions.CannotEvaluateFunctionException:
pass
except InvalidValueError as e:
if report_issues:
context = ConsumptionContext.get_thread_local()
context.validation.report(e.issue)

elif isinstance(value, list):
evaluated_list = []
for v in value:
evaluation = evaluate(v, container_holder, report_issues)
if evaluation is not None:
evaluated_list.append(evaluation.value)
evaluated = True
if not evaluation.final:
final = False
else:
evaluated_list.append(v)
if evaluated:
value = evaluated_list

elif isinstance(value, dict):
evaluated_dict = OrderedDict()
for k, v in value.iteritems():
evaluation = evaluate(v, container_holder, report_issues)
if evaluation is not None:
evaluated_dict[k] = evaluation.value
evaluated = True
if not evaluation.final:
final = False
else:
evaluated_dict[k] = v
if evaluated:
value = evaluated_dict

return Evaluation(value, final) if evaluated else None
@@ -124,7 +124,7 @@ def as_raw(self):
def validate(self):
pass

def coerce_values(self, container, report_issues):
def coerce_values(self, report_issues):
pass

def dump(self):
@@ -146,13 +146,18 @@ def one_to_one(model_class,
false to disable
:type back_populates: basestring|bool
"""
if back_populates is None:
back_populates = model_class.__tablename__
backref_kwargs = None
if back_populates is not NO_BACK_POP:
if back_populates is None:
back_populates = model_class.__tablename__
backref_kwargs = {'name': back_populates, 'uselist': False}
back_populates = None

return _relationship(model_class,
other_table,
fk=fk,
back_populates=back_populates,
backref_kwargs=backref_kwargs,
other_fk=other_fk)


@@ -190,6 +195,7 @@ def one_to_many(model_class,
rel_kwargs.setdefault('cascade', 'all')
if back_populates is None:
back_populates = model_class.__tablename__

return _relationship(
model_class,
child_table,
@@ -330,10 +336,11 @@ def _relationship(model_class,

if backref_kwargs:
assert back_populates is None
return relationship(lambda: _get_class_for_table(model_class, other_table_name),
backref=backref(**backref_kwargs),
**relationship_kwargs
)
return relationship(
lambda: _get_class_for_table(model_class, other_table_name),
backref=backref(**backref_kwargs),
**relationship_kwargs
)
else:
if back_populates is not NO_BACK_POP:
relationship_kwargs['back_populates'] = back_populates

0 comments on commit 60ea3eb

Please sign in to comment.