summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/data_types.py
diff options
context:
space:
mode:
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/data_types.py')
-rw-r--r--azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/data_types.py561
1 files changed, 561 insertions, 0 deletions
diff --git a/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/data_types.py b/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/data_types.py
new file mode 100644
index 0000000..216f1e4
--- /dev/null
+++ b/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/data_types.py
@@ -0,0 +1,561 @@
+# 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.
+
+import re
+from datetime import (datetime, tzinfo, timedelta)
+try:
+ from functools import total_ordering
+except ImportError:
+ from total_ordering import total_ordering
+
+from aria.parser import implements_specification
+from aria.utils.collections import (StrictDict, OrderedDict)
+from aria.utils.formatting import safe_repr
+
+from .modeling.data_types import (coerce_to_data_type_class, report_issue_for_bad_format,
+ coerce_value)
+
+
+class Timezone(tzinfo):
+ """
+ Timezone as fixed offset in hours and minutes east of UTC.
+ """
+
+ def __init__(self, hours=0, minutes=0):
+ super(Timezone, self).__init__()
+ self._offset = timedelta(hours=hours, minutes=minutes)
+
+ def utcoffset(self, dt): # pylint: disable=unused-argument
+ return self._offset
+
+ def tzname(self, dt): # pylint: disable=unused-argument
+ return str(self._offset)
+
+ def dst(self, dt): # pylint: disable=unused-argument
+ return Timezone._ZERO
+
+ _ZERO = timedelta(0)
+
+
+UTC = Timezone()
+
+
+@total_ordering
+@implements_specification('timestamp', 'yaml-1.1')
+class Timestamp(object):
+ '''
+ TOSCA timestamps follow the YAML specification, which in turn is a variant of ISO8601.
+
+ Long forms and short forms (without time of day and assuming UTC timezone) are supported for
+ parsing. The canonical form (for rendering) matches the long form at the UTC timezone.
+
+ See the `Timestamp Language-Independent Type for YAML Version 1.1 (Working Draft 2005-01-18)
+ <http://yaml.org/type/timestamp.html>`__
+ '''
+
+ REGULAR_SHORT = r'^(?P<year>[0-9][0-9][0-9][0-9])-(?P<month>[0-9][0-9])-(?P<day>[0-9][0-9])$'
+ REGULAR_LONG = \
+ r'^(?P<year>[0-9][0-9][0-9][0-9])-(?P<month>[0-9][0-9]?)-(?P<day>[0-9][0-9]?)' + \
+ r'([Tt]|[ \t]+)' \
+ r'(?P<hour>[0-9][0-9]?):(?P<minute>[0-9][0-9]):(?P<second>[0-9][0-9])' + \
+ r'(?P<fraction>\.[0-9]*)?' + \
+ r'(([ \t]*)Z|(?P<tzhour>[-+][0-9][0-9])?(:(?P<tzminute>[0-9][0-9])?)?)?$'
+ CANONICAL = '%Y-%m-%dT%H:%M:%S'
+
+ def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ value = str(value)
+ match = re.match(Timestamp.REGULAR_SHORT, value)
+ if match is not None:
+ # Parse short form
+ year = int(match.group('year'))
+ month = int(match.group('month'))
+ day = int(match.group('day'))
+ self.value = datetime(year, month, day, tzinfo=UTC)
+ else:
+ match = re.match(Timestamp.REGULAR_LONG, value)
+ if match is not None:
+ # Parse long form
+ year = int(match.group('year'))
+ month = int(match.group('month'))
+ day = int(match.group('day'))
+ hour = match.group('hour')
+ if hour is not None:
+ hour = int(hour)
+ minute = match.group('minute')
+ if minute is not None:
+ minute = int(minute)
+ second = match.group('second')
+ if second is not None:
+ second = int(second)
+ fraction = match.group('fraction')
+ if fraction is not None:
+ fraction = int(float(fraction) * 1000000.0) # convert to microseconds
+ tzhour = match.group('tzhour')
+ if tzhour is not None:
+ tzhour = int(tzhour)
+ else:
+ tzhour = 0
+ tzminute = match.group('tzminute')
+ if tzminute is not None:
+ tzminute = int(tzminute)
+ else:
+ tzminute = 0
+ self.value = datetime(year, month, day, hour, minute, second, fraction,
+ Timezone(tzhour, tzminute))
+ else:
+ raise ValueError(
+ 'timestamp must be formatted as YAML ISO8601 variant or "YYYY-MM-DD": %s'
+ % safe_repr(value))
+
+ @property
+ def as_datetime_utc(self):
+ return self.value.astimezone(UTC)
+
+ @property
+ def as_raw(self):
+ return self.__str__()
+
+ def __str__(self):
+ the_datetime = self.as_datetime_utc
+ return '%s%sZ' \
+ % (the_datetime.strftime(Timestamp.CANONICAL), Timestamp._fraction_as_str(the_datetime))
+
+ def __repr__(self):
+ return repr(self.__str__())
+
+ def __eq__(self, timestamp):
+ if not isinstance(timestamp, Timestamp):
+ return False
+ return self.value == timestamp.value
+
+ def __lt__(self, timestamp):
+ return self.value < timestamp.value
+
+ @staticmethod
+ def _fraction_as_str(the_datetime):
+ return '{0:g}'.format(the_datetime.microsecond / 1000000.0).lstrip('0')
+
+
+@total_ordering
+@implements_specification('3.2.2', 'tosca-simple-1.0')
+class Version(object):
+ """
+ TOSCA supports the concept of "reuse" of type definitions, as well as template definitions which
+ could be version and change over time. It is important to provide a reliable, normative means to
+ represent a version string which enables the comparison and management of types and templates
+ over time. Therefore, the TOSCA TC intends to provide a normative version type (string) for this
+ purpose in future Working Drafts of this specification.
+
+ See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #TYPE_TOSCA_VERSION>`__
+ """
+
+ REGEX = \
+ r'^(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<fix>\d+)' + \
+ r'((\.(?P<qualifier>\d+))(\-(?P<build>\d+))?)?)?$'
+
+ @staticmethod
+ def key(version):
+ """
+ Key method for fast sorting.
+ """
+ return (version.major, version.minor, version.fix, version.qualifier, version.build)
+
+ def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ str_value = str(value)
+ match = re.match(Version.REGEX, str_value)
+ if match is None:
+ raise ValueError(
+ 'version must be formatted as <major_version>.<minor_version>'
+ '[.<fix_version>[.<qualifier>[-<build_version]]]: %s'
+ % safe_repr(value))
+
+ self.value = str_value
+
+ self.major = match.group('major')
+ self.major = int(self.major)
+ self.minor = match.group('minor')
+ self.minor = int(self.minor)
+ self.fix = match.group('fix')
+ if self.fix is not None:
+ self.fix = int(self.fix)
+ self.qualifier = match.group('qualifier')
+ if self.qualifier is not None:
+ self.qualifier = int(self.qualifier)
+ self.build = match.group('build')
+ if self.build is not None:
+ self.build = int(self.build)
+
+ @property
+ def as_raw(self):
+ return self.value
+
+ def __str__(self):
+ return self.value
+
+ def __repr__(self):
+ return repr(self.__str__())
+
+ def __eq__(self, version):
+ if not isinstance(version, Version):
+ return False
+ return (self.major, self.minor, self.fix, self.qualifier, self.build) == \
+ (version.major, version.minor, version.fix, version.qualifier, version.build)
+
+ def __lt__(self, version):
+ if self.major < version.major:
+ return True
+ elif self.major == version.major:
+ if self.minor < version.minor:
+ return True
+ elif self.minor == version.minor:
+ if self.fix < version.fix:
+ return True
+ elif self.fix == version.fix:
+ if self.qualifier < version.qualifier:
+ return True
+ elif self.qualifier == version.qualifier:
+ if self.build < version.build:
+ return True
+ return False
+
+
+@implements_specification('3.2.3', 'tosca-simple-1.0')
+class Range(object):
+ """
+ The range type can be used to define numeric ranges with a lower and upper boundary. For
+ example, this allows for specifying a range of ports to be opened in a firewall.
+
+ See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #TYPE_TOSCA_RANGE>`__
+ """
+
+ def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ if not isinstance(value, list):
+ raise ValueError('range value is not a list: %s' % safe_repr(value))
+ if len(value) != 2:
+ raise ValueError('range value does not have exactly 2 elements: %s' % safe_repr(value))
+
+ def is_int(v):
+ return isinstance(v, int) and (not isinstance(v, bool)) # In Python bool is an int
+
+ if not is_int(value[0]):
+ raise ValueError('lower bound of range is not a valid integer: %s'
+ % safe_repr(value[0]))
+
+ if value[1] != 'UNBOUNDED':
+ if not is_int(value[1]):
+ raise ValueError('upper bound of range is not a valid integer or "UNBOUNDED": %s'
+ % safe_repr(value[0]))
+
+ if value[0] >= value[1]:
+ raise ValueError(
+ 'upper bound of range is not greater than the lower bound: %s >= %s'
+ % (safe_repr(value[0]), safe_repr(value[1])))
+
+ self.value = value
+
+ def is_in(self, value):
+ if value < self.value[0]:
+ return False
+ if (self.value[1] != 'UNBOUNDED') and (value > self.value[1]):
+ return False
+ return True
+
+ @property
+ def as_raw(self):
+ return list(self.value)
+
+
+@implements_specification('3.2.4', 'tosca-simple-1.0')
+class List(list):
+ """
+ The list type allows for specifying multiple values for a parameter of property. For example, if
+ an application allows for being configured to listen on multiple ports, a list of ports could be
+ configured using the list data type.
+
+ See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #TYPE_TOSCA_LIST>`__
+ """
+
+ @staticmethod
+ def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ if not isinstance(value, list):
+ raise ValueError('"list" data type value is not a list: %s' % safe_repr(value))
+
+ entry_schema_type = entry_schema._get_type(context)
+ entry_schema_constraints = entry_schema.constraints
+
+ the_list = List()
+ for v in value:
+ v = coerce_value(context, presentation, entry_schema_type, None,
+ entry_schema_constraints, v, aspect)
+ if v is not None:
+ the_list.append(v)
+
+ return the_list
+
+ # Can't define as property because it's old-style Python class
+ def as_raw(self):
+ return list(self)
+
+
+@implements_specification('3.2.5', 'tosca-simple-1.0')
+class Map(StrictDict):
+ """
+ The map type allows for specifying multiple values for a parameter of property as a map. In
+ contrast to the list type, where each entry can only be addressed by its index in the list,
+ entries in a map are named elements that can be addressed by their keys.
+
+ See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #TYPE_TOSCA_MAP>`__
+ """
+
+ @staticmethod
+ def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ if not isinstance(value, dict):
+ raise ValueError('"map" data type value is not a dict: %s' % safe_repr(value))
+
+ if entry_schema is None:
+ raise ValueError('"map" data type does not define "entry_schema"')
+
+ entry_schema_type = entry_schema._get_type(context)
+ entry_schema_constraints = entry_schema.constraints
+
+ the_map = Map()
+ for k, v in value.iteritems():
+ v = coerce_value(context, presentation, entry_schema_type, None,
+ entry_schema_constraints, v, aspect)
+ if v is not None:
+ the_map[k] = v
+
+ return the_map
+
+ def __init__(self, items=None):
+ super(Map, self).__init__(items, key_class=str)
+
+ # Can't define as property because it's old-style Python class
+ def as_raw(self):
+ return OrderedDict(self)
+
+
+@total_ordering
+@implements_specification('3.2.6', 'tosca-simple-1.0')
+class Scalar(object):
+ """
+ The scalar-unit type can be used to define scalar values along with a unit from the list of
+ recognized units.
+
+ See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #TYPE_TOSCA_SCALAR_UNIT>`__
+ """
+
+ @staticmethod
+ def key(scalar):
+ """
+ Key method for fast sorting.
+ """
+ return scalar.value
+
+ def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ str_value = str(value)
+ match = re.match(self.REGEX, str_value) # pylint: disable=no-member
+ if match is None:
+ raise ValueError('scalar must be formatted as <scalar> <unit>: %s' % safe_repr(value))
+
+ self.factor = float(match.group('scalar'))
+ if self.factor < 0:
+ raise ValueError('scalar is negative: %s' % safe_repr(self.factor))
+
+ self.unit = match.group('unit')
+
+ unit_lower = self.unit.lower()
+ unit_size = None
+ for k, v in self.UNITS.iteritems(): # pylint: disable=no-member
+ if k.lower() == unit_lower:
+ self.unit = k
+ unit_size = v
+ break
+ if unit_size is None:
+ raise ValueError('scalar specified with unsupported unit: %s' % safe_repr(self.unit))
+
+ self.value = self.TYPE(self.factor * unit_size) # pylint: disable=no-member
+
+ @property
+ def as_raw(self):
+ return OrderedDict((
+ ('value', self.value),
+ ('factor', self.factor),
+ ('unit', self.unit),
+ ('unit_size', self.UNITS[self.unit]))) # pylint: disable=no-member
+
+ def __str__(self):
+ return '%s %s' % (self.value, self.UNIT) # pylint: disable=no-member
+
+ def __repr__(self):
+ return repr(self.__str__())
+
+ def __eq__(self, scalar):
+ if isinstance(scalar, Scalar):
+ value = scalar.value
+ else:
+ value = self.TYPE(scalar) # pylint: disable=no-member
+ return self.value == value
+
+ def __lt__(self, scalar):
+ if isinstance(scalar, Scalar):
+ value = scalar.value
+ else:
+ value = self.TYPE(scalar) # pylint: disable=no-member
+ return self.value < value
+
+
+@implements_specification('3.2.6.4', 'tosca-simple-1.0')
+class ScalarSize(Scalar):
+ """
+ Integer scalar for counting bytes.
+
+ See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #TYPE_TOSCA_SCALAR_UNIT_SIZE>`__
+ """
+
+ # See: http://www.regular-expressions.info/floatingpoint.html
+ REGEX = \
+ r'^(?P<scalar>[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\s*(?P<unit>B|kB|KiB|MB|MiB|GB|GiB|TB|TiB)$'
+
+ UNITS = {
+ 'B': 1,
+ 'kB': 1000,
+ 'KiB': 1024,
+ 'MB': 1000000,
+ 'MiB': 1048576,
+ 'GB': 1000000000,
+ 'GiB': 1073741824,
+ 'TB': 1000000000000,
+ 'TiB': 1099511627776}
+
+ TYPE = int
+ UNIT = 'bytes'
+
+
+@implements_specification('3.2.6.5', 'tosca-simple-1.0')
+class ScalarTime(Scalar):
+ """
+ Floating point scalar for counting seconds.
+
+ See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #TYPE_TOSCA_SCALAR_UNIT_TIME>`__
+ """
+
+ # See: http://www.regular-expressions.info/floatingpoint.html
+ REGEX = r'^(?P<scalar>[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\s*(?P<unit>ns|us|ms|s|m|h|d)$'
+
+ UNITS = {
+ 'ns': 0.000000001,
+ 'us': 0.000001,
+ 'ms': 0.001,
+ 's': 1.0,
+ 'm': 60.0,
+ 'h': 3600.0,
+ 'd': 86400.0}
+
+ TYPE = float
+ UNIT = 'seconds'
+
+
+@implements_specification('3.2.6.6', 'tosca-simple-1.0')
+class ScalarFrequency(Scalar):
+ """
+ Floating point scalar for counting cycles per second (Hz).
+
+ See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #TYPE_TOSCA_SCALAR_UNIT_FREQUENCY>`__
+ """
+
+ # See: http://www.regular-expressions.info/floatingpoint.html
+ REGEX = r'^(?P<scalar>[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\s*(?P<unit>Hz|kHz|MHz|GHz)$'
+
+ UNITS = {
+ 'Hz': 1.0,
+ 'kHz': 1000.0,
+ 'MHz': 1000000.0,
+ 'GHz': 1000000000.0}
+
+ TYPE = float
+ UNIT = 'Hz'
+
+
+#
+# The following are hooked in the YAML as 'coerce_value' extensions
+#
+
+def coerce_timestamp(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ return coerce_to_data_type_class(context, presentation, Timestamp, entry_schema, constraints,
+ value, aspect)
+
+
+def coerce_version(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ return coerce_to_data_type_class(context, presentation, Version, entry_schema, constraints,
+ value, aspect)
+
+
+def coerce_range(context, presentation, the_type, entry_schema, constraints, value, aspect):
+ if aspect == 'in_range':
+ # When we're in a "in_range" constraint, the values are *not* themselves ranges, but numbers
+ try:
+ return float(value)
+ except ValueError as e:
+ report_issue_for_bad_format(context, presentation, the_type, value, aspect, e)
+ except TypeError as e:
+ report_issue_for_bad_format(context, presentation, the_type, value, aspect, e)
+ else:
+ return coerce_to_data_type_class(context, presentation, Range, entry_schema, constraints,
+ value, aspect)
+
+
+def coerce_list(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ return coerce_to_data_type_class(context, presentation, List, entry_schema, constraints,
+ value, aspect)
+
+
+def coerce_map_value(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ return coerce_to_data_type_class(context, presentation, Map, entry_schema, constraints, value,
+ aspect)
+
+
+def coerce_scalar_unit_size(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+ aspect):
+ return coerce_to_data_type_class(context, presentation, ScalarSize, entry_schema, constraints,
+ value, aspect)
+
+
+def coerce_scalar_unit_time(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+ aspect):
+ return coerce_to_data_type_class(context, presentation, ScalarTime, entry_schema, constraints,
+ value, aspect)
+
+
+def coerce_scalar_unit_frequency(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+ aspect):
+ return coerce_to_data_type_class(context, presentation, ScalarFrequency, entry_schema,
+ constraints, value, aspect)