From fd3c74b86bfb29921004799760d60a2720af6861 Mon Sep 17 00:00:00 2001 From: nirajsingh Date: Thu, 23 Aug 2018 14:56:52 +0000 Subject: [PATCH] Add reservation policy support This patch adds `tosca.policies.reservation` policy_type in TOSCA definitions and it's related unit tests. Tacker Blueprint: https://blueprints.launchpad.net/tacker/+spec/reservation-vnfm Tacker Spec: https://review.openstack.org/#/c/561840/ Change-Id: Ic5d790df938b40d75bc50252e1e688e9c09eb568 --- .../elements/TOSCA_definition_1_0.yaml | 5 ++ toscaparser/elements/policytype.py | 8 ++- toscaparser/policy.py | 14 ++++- toscaparser/reservation.py | 47 +++++++++++++++++ .../tests/data/policies/tacker_defs.yaml | 23 +++++++++ toscaparser/tests/test_toscadef.py | 13 +++++ toscaparser/tests/test_toscatplvalidation.py | 51 +++++++++++++++++++ 7 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 toscaparser/reservation.py diff --git a/toscaparser/elements/TOSCA_definition_1_0.yaml b/toscaparser/elements/TOSCA_definition_1_0.yaml index 27c3e492..1f676665 100644 --- a/toscaparser/elements/TOSCA_definition_1_0.yaml +++ b/toscaparser/elements/TOSCA_definition_1_0.yaml @@ -953,6 +953,11 @@ policy_types: description: The TOSCA Policy Type definition that is used to declare performance requirements for TOSCA nodes or groups of nodes. + tosca.policies.Reservation: + derived_from: tosca.policies.Root + description: The TOSCA Policy Type definition that is used to create + TOSCA nodes or group of nodes based on the reservation. + ########################################################################## # Group Type. # Group Type represents logical grouping of TOSCA nodes that have an diff --git a/toscaparser/elements/policytype.py b/toscaparser/elements/policytype.py index 0061e0bf..63f94fc2 100644 --- a/toscaparser/elements/policytype.py +++ b/toscaparser/elements/policytype.py @@ -21,9 +21,9 @@ class PolicyType(StatefulEntityType): '''TOSCA built-in policies type.''' SECTIONS = (DERIVED_FROM, METADATA, PROPERTIES, VERSION, DESCRIPTION, - TARGETS, TRIGGERS, TYPE) = \ + TARGETS, TRIGGERS, TYPE, RESERVATION) = \ ('derived_from', 'metadata', 'properties', 'version', - 'description', 'targets', 'triggers', 'type') + 'description', 'targets', 'triggers', 'type', 'reservation') def __init__(self, ptype, custom_def=None): super(PolicyType, self).__init__(ptype, self.POLICY_PREFIX, @@ -55,6 +55,10 @@ class PolicyType(StatefulEntityType): self.targets_list = self.defs[self.TARGETS] self._validate_targets(self.targets_list, custom_def) + self.reservation = None + if self.RESERVATION in self.defs: + self.reservation = self.defs[self.RESERVATION] + def _get_parent_policies(self): policies = {} parent_policy = self.parent_type.type if self.parent_type else None diff --git a/toscaparser/policy.py b/toscaparser/policy.py index fedbeb4c..50cd9882 100644 --- a/toscaparser/policy.py +++ b/toscaparser/policy.py @@ -16,13 +16,15 @@ import logging from toscaparser.common.exception import ExceptionCollector from toscaparser.common.exception import UnknownFieldError from toscaparser.entity_template import EntityTemplate +from toscaparser.reservation import Reservation from toscaparser.triggers import Triggers from toscaparser.utils import validateutils -SECTIONS = (TYPE, METADATA, DESCRIPTION, PROPERTIES, TARGETS, TRIGGERS) = \ +SECTIONS = (TYPE, METADATA, DESCRIPTION, PROPERTIES, TARGETS, TRIGGERS, + RESERVATION) = \ ('type', 'metadata', 'description', - 'properties', 'targets', 'triggers') + 'properties', 'targets', 'triggers', 'reservation') log = logging.getLogger('tosca') @@ -42,6 +44,7 @@ class Policy(EntityTemplate): self.targets_list = targets self.targets_type = targets_type self.triggers = self._triggers(policy.get(TRIGGERS)) + self.reservation = self._reservation(policy.get(RESERVATION)) self.properties = None if 'properties' in policy: self.properties = policy['properties'] @@ -73,6 +76,13 @@ class Policy(EntityTemplate): triggerObjs.append(triggersObj) return triggerObjs + def _reservation(self, reservation): + reservationObjs = [] + if reservation: + reservationObj = Reservation(reservation) + reservationObjs.append(reservationObj) + return reservationObjs + def _validate_keys(self): for key in self.entity_tpl.keys(): if key not in SECTIONS: diff --git a/toscaparser/reservation.py b/toscaparser/reservation.py new file mode 100644 index 00000000..82954427 --- /dev/null +++ b/toscaparser/reservation.py @@ -0,0 +1,47 @@ +# Copyright (C) 2018 NTT DATA +# 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 logging + +from toscaparser.common.exception import UnknownFieldError +from toscaparser.entity_template import EntityTemplate +from toscaparser.entity_template import MissingRequiredFieldError + + +SECTIONS = (START_ACTIONS, BEFORE_END_ACTIONS, END_ACTIONS, PROPERTIES) = \ + ('start_actions', 'before_end_actions', 'end_actions', 'properties') + +log = logging.getLogger('tosca') + + +class Reservation(EntityTemplate): + + '''Reservation defined in policies of topology template''' + + def __init__(self, reservation_tpl): + self.reservation_tpl = reservation_tpl + self._validate_keys() + self._validate_missing_field() + + def _validate_keys(self): + for key in self.reservation_tpl.keys(): + if key not in SECTIONS: + raise UnknownFieldError(what='Reservation', field=key) + + def _validate_missing_field(self): + for key in SECTIONS: + if key not in self.reservation_tpl.keys(): + raise MissingRequiredFieldError(what='Reservation', + required=key) diff --git a/toscaparser/tests/data/policies/tacker_defs.yaml b/toscaparser/tests/data/policies/tacker_defs.yaml index 96b0d458..05b2348c 100644 --- a/toscaparser/tests/data/policies/tacker_defs.yaml +++ b/toscaparser/tests/data/policies/tacker_defs.yaml @@ -181,3 +181,26 @@ policy_types: required: false default: 120 description: Wait time (in seconds) between consecutive scaling operations. During the cooldown period... + + tosca.policies.tacker.Reservation: + derived_from: tosca.policies.Reservation + reservation: + start_actions: + type: map + entry_schema: + type: string + required: True + before_end_actions: + type: map + entry_schema: + type: string + required: True + end_actions: + type: map + entry_schema: + type: string + required: True + properties: + lease_id: + type: string + required: True diff --git a/toscaparser/tests/test_toscadef.py b/toscaparser/tests/test_toscadef.py index 2e97b0e0..236f318c 100644 --- a/toscaparser/tests/test_toscadef.py +++ b/toscaparser/tests/test_toscadef.py @@ -39,6 +39,7 @@ artif_vm_qcow2_type = ArtifactTypeDef('tosca.artifacts.' policy_root_type = PolicyType('tosca.policies.Root') policy_placement_type = PolicyType('tosca.policies.Placement') policy_scaling_type = PolicyType('tosca.policies.Scaling') +policy_reservation_type = PolicyType('tosca.policies.Reservation') policy_update_type = PolicyType('tosca.policies.Update') policy_performance_type = PolicyType('tosca.policies.Performance') group_type = GroupType('tosca.groups.Root') @@ -334,6 +335,18 @@ class ToscaDefTest(TestCase): for name in policy_performance_type.defs], key=lambda x: str(x))) + self.assertEqual('tosca.policies.Root', + policy_reservation_type.parent_type.type) + self.assertEqual({}, policy_reservation_type.parent_policies) + self.assertEqual(sorted(['tosca.policies.Root', + 'The TOSCA Policy Type definition that ' + 'is used to create TOSCA nodes or group ' + 'of nodes based on the reservation.'], + key=lambda x: str(x)), + sorted([policy_reservation_type.get_policy(name) + for name in policy_reservation_type.defs], + key=lambda x: str(x))) + def test_port_spec(self): tosca_def = EntityType.TOSCA_DEF port_spec = tosca_def.get('tosca.datatypes.network.PortSpec') diff --git a/toscaparser/tests/test_toscatplvalidation.py b/toscaparser/tests/test_toscatplvalidation.py index d4abe3ef..4aae93cb 100644 --- a/toscaparser/tests/test_toscatplvalidation.py +++ b/toscaparser/tests/test_toscatplvalidation.py @@ -21,6 +21,7 @@ from toscaparser.parameters import Output from toscaparser.policy import Policy from toscaparser.relationship_template import RelationshipTemplate from toscaparser.repositories import Repository +from toscaparser.reservation import Reservation from toscaparser.tests.base import TestCase from toscaparser.topology_template import TopologyTemplate from toscaparser.tosca_template import ToscaTemplate @@ -1870,3 +1871,53 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml os.path.dirname(os.path.abspath(__file__)), "data/test_long_rel.yaml") self.assertIsNotNone(ToscaTemplate(tpl_path)) + + def test_policy_reservation_valid_keyname_heat_resources(self): + tpl_snippet = ''' + reservation: + start_actions: [SP_RSV] + before_end_actions: [SP_RSV] + end_actions: [noop] + properties: + lease_id: '58d2239c-f181-4915-bdcb-040f7ef911a7' + ''' + reservation = (toscaparser.utils.yamlparser.simple_parse(tpl_snippet)) + name = list(reservation.keys())[0] + Reservation(reservation[name]) + + def test_policy_reservation_invalid_keyname_heat_resources(self): + tpl_snippet = ''' + reservation: + start_actions: [SP_RSV] + before_actions: [SP_RSV] + end_actions: [noop] + properties: + lease_id: '58d2239c-f181-4915-bdcb-040f7ef911a7' + ''' + reservation = (toscaparser.utils.yamlparser.simple_parse(tpl_snippet)) + name = list(reservation.keys())[0] + expectedmessage = _( + 'Reservation contains unknown field ' + '"before_actions". Refer to the definition ' + 'to verify valid values.') + err = self.assertRaises( + exception.UnknownFieldError, + lambda: Reservation(reservation[name])) + self.assertEqual(expectedmessage, err.__str__()) + + def test_policy_reservation_missing_key_heat_resources(self): + tpl_snippet = ''' + reservation: + start_actions: [SP_RSV] + end_actions: [noop] + properties: + lease_id: '58d2239c-f181-4915-bdcb-040f7ef911a7' + ''' + reservation = (toscaparser.utils.yamlparser.simple_parse(tpl_snippet)) + name = list(reservation.keys())[0] + expectedmessage = _('Reservation is missing ' + 'required field "before_end_actions".') + err = self.assertRaises( + exception.MissingRequiredFieldError, + lambda: Reservation(reservation[name])) + self.assertEqual(expectedmessage, err.__str__())