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
This commit is contained in:
parent
f2ca5eb7aa
commit
fd3c74b86b
|
@ -953,6 +953,11 @@ policy_types:
|
||||||
description: The TOSCA Policy Type definition that is used to declare
|
description: The TOSCA Policy Type definition that is used to declare
|
||||||
performance requirements for TOSCA nodes or groups of nodes.
|
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.
|
||||||
# Group Type represents logical grouping of TOSCA nodes that have an
|
# Group Type represents logical grouping of TOSCA nodes that have an
|
||||||
|
|
|
@ -21,9 +21,9 @@ class PolicyType(StatefulEntityType):
|
||||||
|
|
||||||
'''TOSCA built-in policies type.'''
|
'''TOSCA built-in policies type.'''
|
||||||
SECTIONS = (DERIVED_FROM, METADATA, PROPERTIES, VERSION, DESCRIPTION,
|
SECTIONS = (DERIVED_FROM, METADATA, PROPERTIES, VERSION, DESCRIPTION,
|
||||||
TARGETS, TRIGGERS, TYPE) = \
|
TARGETS, TRIGGERS, TYPE, RESERVATION) = \
|
||||||
('derived_from', 'metadata', 'properties', 'version',
|
('derived_from', 'metadata', 'properties', 'version',
|
||||||
'description', 'targets', 'triggers', 'type')
|
'description', 'targets', 'triggers', 'type', 'reservation')
|
||||||
|
|
||||||
def __init__(self, ptype, custom_def=None):
|
def __init__(self, ptype, custom_def=None):
|
||||||
super(PolicyType, self).__init__(ptype, self.POLICY_PREFIX,
|
super(PolicyType, self).__init__(ptype, self.POLICY_PREFIX,
|
||||||
|
@ -55,6 +55,10 @@ class PolicyType(StatefulEntityType):
|
||||||
self.targets_list = self.defs[self.TARGETS]
|
self.targets_list = self.defs[self.TARGETS]
|
||||||
self._validate_targets(self.targets_list, custom_def)
|
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):
|
def _get_parent_policies(self):
|
||||||
policies = {}
|
policies = {}
|
||||||
parent_policy = self.parent_type.type if self.parent_type else None
|
parent_policy = self.parent_type.type if self.parent_type else None
|
||||||
|
|
|
@ -16,13 +16,15 @@ import logging
|
||||||
from toscaparser.common.exception import ExceptionCollector
|
from toscaparser.common.exception import ExceptionCollector
|
||||||
from toscaparser.common.exception import UnknownFieldError
|
from toscaparser.common.exception import UnknownFieldError
|
||||||
from toscaparser.entity_template import EntityTemplate
|
from toscaparser.entity_template import EntityTemplate
|
||||||
|
from toscaparser.reservation import Reservation
|
||||||
from toscaparser.triggers import Triggers
|
from toscaparser.triggers import Triggers
|
||||||
from toscaparser.utils import validateutils
|
from toscaparser.utils import validateutils
|
||||||
|
|
||||||
|
|
||||||
SECTIONS = (TYPE, METADATA, DESCRIPTION, PROPERTIES, TARGETS, TRIGGERS) = \
|
SECTIONS = (TYPE, METADATA, DESCRIPTION, PROPERTIES, TARGETS, TRIGGERS,
|
||||||
|
RESERVATION) = \
|
||||||
('type', 'metadata', 'description',
|
('type', 'metadata', 'description',
|
||||||
'properties', 'targets', 'triggers')
|
'properties', 'targets', 'triggers', 'reservation')
|
||||||
|
|
||||||
log = logging.getLogger('tosca')
|
log = logging.getLogger('tosca')
|
||||||
|
|
||||||
|
@ -42,6 +44,7 @@ class Policy(EntityTemplate):
|
||||||
self.targets_list = targets
|
self.targets_list = targets
|
||||||
self.targets_type = targets_type
|
self.targets_type = targets_type
|
||||||
self.triggers = self._triggers(policy.get(TRIGGERS))
|
self.triggers = self._triggers(policy.get(TRIGGERS))
|
||||||
|
self.reservation = self._reservation(policy.get(RESERVATION))
|
||||||
self.properties = None
|
self.properties = None
|
||||||
if 'properties' in policy:
|
if 'properties' in policy:
|
||||||
self.properties = policy['properties']
|
self.properties = policy['properties']
|
||||||
|
@ -73,6 +76,13 @@ class Policy(EntityTemplate):
|
||||||
triggerObjs.append(triggersObj)
|
triggerObjs.append(triggersObj)
|
||||||
return triggerObjs
|
return triggerObjs
|
||||||
|
|
||||||
|
def _reservation(self, reservation):
|
||||||
|
reservationObjs = []
|
||||||
|
if reservation:
|
||||||
|
reservationObj = Reservation(reservation)
|
||||||
|
reservationObjs.append(reservationObj)
|
||||||
|
return reservationObjs
|
||||||
|
|
||||||
def _validate_keys(self):
|
def _validate_keys(self):
|
||||||
for key in self.entity_tpl.keys():
|
for key in self.entity_tpl.keys():
|
||||||
if key not in SECTIONS:
|
if key not in SECTIONS:
|
||||||
|
|
|
@ -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)
|
|
@ -181,3 +181,26 @@ policy_types:
|
||||||
required: false
|
required: false
|
||||||
default: 120
|
default: 120
|
||||||
description: Wait time (in seconds) between consecutive scaling operations. During the cooldown period...
|
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
|
||||||
|
|
|
@ -39,6 +39,7 @@ artif_vm_qcow2_type = ArtifactTypeDef('tosca.artifacts.'
|
||||||
policy_root_type = PolicyType('tosca.policies.Root')
|
policy_root_type = PolicyType('tosca.policies.Root')
|
||||||
policy_placement_type = PolicyType('tosca.policies.Placement')
|
policy_placement_type = PolicyType('tosca.policies.Placement')
|
||||||
policy_scaling_type = PolicyType('tosca.policies.Scaling')
|
policy_scaling_type = PolicyType('tosca.policies.Scaling')
|
||||||
|
policy_reservation_type = PolicyType('tosca.policies.Reservation')
|
||||||
policy_update_type = PolicyType('tosca.policies.Update')
|
policy_update_type = PolicyType('tosca.policies.Update')
|
||||||
policy_performance_type = PolicyType('tosca.policies.Performance')
|
policy_performance_type = PolicyType('tosca.policies.Performance')
|
||||||
group_type = GroupType('tosca.groups.Root')
|
group_type = GroupType('tosca.groups.Root')
|
||||||
|
@ -334,6 +335,18 @@ class ToscaDefTest(TestCase):
|
||||||
for name in policy_performance_type.defs],
|
for name in policy_performance_type.defs],
|
||||||
key=lambda x: str(x)))
|
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):
|
def test_port_spec(self):
|
||||||
tosca_def = EntityType.TOSCA_DEF
|
tosca_def = EntityType.TOSCA_DEF
|
||||||
port_spec = tosca_def.get('tosca.datatypes.network.PortSpec')
|
port_spec = tosca_def.get('tosca.datatypes.network.PortSpec')
|
||||||
|
|
|
@ -21,6 +21,7 @@ from toscaparser.parameters import Output
|
||||||
from toscaparser.policy import Policy
|
from toscaparser.policy import Policy
|
||||||
from toscaparser.relationship_template import RelationshipTemplate
|
from toscaparser.relationship_template import RelationshipTemplate
|
||||||
from toscaparser.repositories import Repository
|
from toscaparser.repositories import Repository
|
||||||
|
from toscaparser.reservation import Reservation
|
||||||
from toscaparser.tests.base import TestCase
|
from toscaparser.tests.base import TestCase
|
||||||
from toscaparser.topology_template import TopologyTemplate
|
from toscaparser.topology_template import TopologyTemplate
|
||||||
from toscaparser.tosca_template import ToscaTemplate
|
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__)),
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
"data/test_long_rel.yaml")
|
"data/test_long_rel.yaml")
|
||||||
self.assertIsNotNone(ToscaTemplate(tpl_path))
|
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__())
|
||||||
|
|
Loading…
Reference in New Issue