vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / aria / modeling / utils.py
1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements.  See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License.  You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """
17 Miscellaneous modeling utilities.
18 """
19
20 import os
21 from json import JSONEncoder
22 from StringIO import StringIO
23
24 from . import exceptions
25 from ..utils.type import validate_value_type
26 from ..utils.collections import OrderedDict
27 from ..utils.formatting import string_list_as_string
28
29
30 class ModelJSONEncoder(JSONEncoder):
31     """
32     JSON encoder that automatically unwraps ``value`` attributes.
33     """
34     def __init__(self, *args, **kwargs):
35         # Just here to make sure Sphinx doesn't grab the base constructor's docstring
36         super(ModelJSONEncoder, self).__init__(*args, **kwargs)
37
38     def default(self, o):  # pylint: disable=method-hidden
39         from .mixins import ModelMixin
40         if isinstance(o, ModelMixin):
41             if hasattr(o, 'value'):
42                 dict_to_return = o.to_dict(fields=('value',))
43                 return dict_to_return['value']
44             else:
45                 return o.to_dict()
46         else:
47             return JSONEncoder.default(self, o)
48
49
50 class NodeTemplateContainerHolder(object):
51     """
52     Wrapper that allows using a :class:`~aria.modeling.models.NodeTemplate` model directly as the
53     ``container_holder`` input for :func:`~aria.modeling.functions.evaluate`.
54     """
55
56     def __init__(self, node_template):
57         self.container = node_template
58         self.service = None
59
60     @property
61     def service_template(self):
62         return self.container.service_template
63
64
65 # def validate_no_undeclared_inputs(declared_inputs, supplied_inputs):
66 #
67 #     undeclared_inputs = [input for input in supplied_inputs if input not in declared_inputs]
68 #     if undeclared_inputs:
69 #         raise exceptions.UndeclaredInputsException(
70 #             'Undeclared inputs have been provided: {0}; Declared inputs: {1}'
71 #             .format(string_list_as_string(undeclared_inputs),
72 #                     string_list_as_string(declared_inputs.keys())))
73
74
75 def validate_required_inputs_are_supplied(declared_inputs, supplied_inputs):
76     required_inputs = [input for input in declared_inputs.values() if input.required]
77     missing_required_inputs = [input for input in required_inputs
78                                if input.name not in supplied_inputs and not str(input.value)]
79     if missing_required_inputs:
80         raise exceptions.MissingRequiredInputsException(
81             'Required inputs {0} have not been provided values'
82             .format(string_list_as_string(missing_required_inputs)))
83
84
85 def merge_parameter_values(provided_values, declared_parameters, model_cls=None):
86     """
87     Merges parameter values according to those declared by a type.
88
89     Exceptions will be raised for validation errors.
90
91     :param provided_values: provided parameter values or None
92     :type provided_values: {:obj:`basestring`: object}
93     :param declared_parameters: declared parameters
94     :type declared_parameters: {:obj:`basestring`: :class:`~aria.modeling.models.Parameter`}
95     :param model_cls: the model class that should be created from a provided value
96     :type model_cls: :class:`~aria.modeling.models.Input` or :class:`~aria.modeling.models.Argument`
97     :return: the merged parameters
98     :rtype: {:obj:`basestring`: :class:`~aria.modeling.models.Parameter`}
99     :raises ~aria.modeling.exceptions.UndeclaredInputsException: if a key in
100      ``parameter_values`` does not exist in ``declared_parameters``
101     :raises ~aria.modeling.exceptions.MissingRequiredInputsException: if a key in
102      ``declared_parameters`` does not exist in ``parameter_values`` and also has no default value
103     :raises ~aria.modeling.exceptions.ParametersOfWrongTypeException: if a value in
104       ``parameter_values`` does not match its type in ``declared_parameters``
105     """
106
107     provided_values = provided_values or {}
108     provided_values_of_wrong_type = OrderedDict()
109     model_parameters = OrderedDict()
110     model_cls = model_cls or _get_class_from_sql_relationship(declared_parameters)
111
112     for declared_parameter_name, declared_parameter in declared_parameters.iteritems():
113         if declared_parameter_name in provided_values:
114             # a value has been provided
115             value = provided_values[declared_parameter_name]
116
117             # Validate type
118             type_name = declared_parameter.type_name
119             try:
120                 validate_value_type(value, type_name)
121             except ValueError:
122                 provided_values_of_wrong_type[declared_parameter_name] = type_name
123             except RuntimeError:
124                 # TODO This error shouldn't be raised (or caught), but right now we lack support
125                 # for custom data_types, which will raise this error. Skipping their validation.
126                 pass
127             model_parameters[declared_parameter_name] = model_cls(                                  # pylint: disable=unexpected-keyword-arg
128                 name=declared_parameter_name,
129                 type_name=type_name,
130                 description=declared_parameter.description,
131                 value=value)
132         else:
133             # Copy default value from declaration
134             model_parameters[declared_parameter_name] = model_cls(
135                 value=declared_parameter._value,
136                 name=declared_parameter.name,
137                 type_name=declared_parameter.type_name,
138                 description=declared_parameter.description)
139
140     if provided_values_of_wrong_type:
141         error_message = StringIO()
142         for param_name, param_type in provided_values_of_wrong_type.iteritems():
143             error_message.write('Parameter "{0}" is not of declared type "{1}"{2}'
144                                 .format(param_name, param_type, os.linesep))
145         raise exceptions.ParametersOfWrongTypeException(error_message.getvalue())
146
147     return model_parameters
148
149
150 def parameters_as_values(the_dict):
151     return dict((k, v.value) for k, v in the_dict.iteritems())
152
153
154 def dict_as_arguments(the_dict):
155     return OrderedDict((name, value.as_argument()) for name, value in the_dict.iteritems())
156
157
158 class classproperty(object):                                                                        # pylint: disable=invalid-name
159     def __init__(self, f):
160         self._func = f
161         self.__doct__ = f.__doc__
162
163     def __get__(self, instance, owner):
164         return self._func(owner)
165
166
167 def fix_doc(cls):
168     """
169     Class decorator to use the last base class's docstring and make sure Sphinx doesn't grab the
170     base constructor's docstring.
171     """
172     original_init = cls.__init__
173     def init(*args, **kwargs):
174         original_init(*args, **kwargs)
175
176     cls.__init__ = init
177     cls.__doc__ = cls.__bases__[-1].__doc__
178
179     return cls
180
181
182 def _get_class_from_sql_relationship(field):
183     class_ = field._sa_adapter.owner_state.class_
184     prop_name = field._sa_adapter.attr.key
185     return getattr(class_, prop_name).property.mapper.class_