Implement TOSCA Block Storage
Create TOSCA BlockStorage node type and its mapping in Heat. Introduce relationship template which will also provide a base for implementing relationship templates section in the TOSCA template. Introduce tests for generator/translator code. implements blueprint tosca-blockstorage-type Closes-Bug: #1351129 Closes-Bug: #1354632 Change-Id: I7df198ea45875031557e2607d932f14f272bf2e1
This commit is contained in:
parent
f2ba5fa317
commit
ccefa3514e
@ -103,7 +103,7 @@ class HotResource(object):
|
|||||||
# in interfaces_deploy_sequence
|
# in interfaces_deploy_sequence
|
||||||
# TODO(anyone): find some better way to encode this implicit sequence
|
# TODO(anyone): find some better way to encode this implicit sequence
|
||||||
group = {}
|
group = {}
|
||||||
for op, hot in deploy_lookup.iteritems():
|
for op, hot in deploy_lookup.items():
|
||||||
# position to determine potential preceding nodes
|
# position to determine potential preceding nodes
|
||||||
op_index = interfaces_deploy_sequence.index(op)
|
op_index = interfaces_deploy_sequence.index(op)
|
||||||
for preceding_op in \
|
for preceding_op in \
|
||||||
|
@ -61,7 +61,7 @@ class HotTemplate(object):
|
|||||||
all_outputs.update(output.get_dict_output())
|
all_outputs.update(output.get_dict_output())
|
||||||
dict_output.update({self.OUTPUTS: all_outputs})
|
dict_output.update({self.OUTPUTS: all_outputs})
|
||||||
|
|
||||||
yaml_string = yaml.dump(dict_output, default_flow_style=False)
|
yaml_string = yaml.dump(dict_output)
|
||||||
# get rid of the '' from yaml.dump around numbers
|
# get rid of the '' from yaml.dump around numbers
|
||||||
yaml_string = yaml_string.replace('\'', '')
|
yaml_string = yaml_string.replace('\'', '')
|
||||||
return version_string + desc_str + yaml_string
|
return version_string + desc_str + yaml_string
|
||||||
|
32
translator/hot/tosca/tosca_block_storage.py
Normal file
32
translator/hot/tosca/tosca_block_storage.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from translator.hot.syntax.hot_resource import HotResource
|
||||||
|
|
||||||
|
|
||||||
|
class ToscaBlockStorage(HotResource):
|
||||||
|
'''Translate TOSCA node type tosca.nodes.BlockStorage.'''
|
||||||
|
|
||||||
|
def __init__(self, nodetemplate):
|
||||||
|
super(ToscaBlockStorage, self).__init__(nodetemplate,
|
||||||
|
type='OS::Cinder::Volume')
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_properties(self):
|
||||||
|
tosca_props = {}
|
||||||
|
for prop in self.nodetemplate.properties:
|
||||||
|
if isinstance(prop.value, dict):
|
||||||
|
for x, y in prop.value.items():
|
||||||
|
if x == 'get_input':
|
||||||
|
tosca_props[prop.name] = {'get_param': y}
|
||||||
|
self.properties = tosca_props
|
40
translator/hot/tosca/tosca_block_storage_attachment.py
Normal file
40
translator/hot/tosca/tosca_block_storage_attachment.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from translator.hot.syntax.hot_resource import HotResource
|
||||||
|
|
||||||
|
|
||||||
|
class ToscaBlockStorageAttachment(HotResource):
|
||||||
|
'''Translate TOSCA relationship AttachTo for Compute and BlockStorage.'''
|
||||||
|
|
||||||
|
def __init__(self, template, nodetemplates, instace_uuid, volume_id):
|
||||||
|
super(ToscaBlockStorageAttachment,
|
||||||
|
self).__init__(template, type='OS::Cinder::VolumeAttachment')
|
||||||
|
self.nodetemplates = nodetemplates
|
||||||
|
self.instace_uuid = instace_uuid
|
||||||
|
self.volume_id = volume_id
|
||||||
|
|
||||||
|
def handle_properties(self):
|
||||||
|
tosca_props = {}
|
||||||
|
for prop in self.nodetemplate.properties:
|
||||||
|
if isinstance(prop.value, dict):
|
||||||
|
for x, y in prop.value.items():
|
||||||
|
if x == 'get_input':
|
||||||
|
tosca_props[prop.name] = {'get_param': y}
|
||||||
|
self.properties = tosca_props
|
||||||
|
#instance_uuid and volume_id for Cinder volume attachment
|
||||||
|
self.properties['instance_uuid'] = self.instace_uuid
|
||||||
|
self.properties['volume_id'] = self.volume_id
|
||||||
|
|
||||||
|
def handle_life_cycle(self):
|
||||||
|
pass
|
@ -124,8 +124,9 @@ class ToscaCompute(HotResource):
|
|||||||
return this_list
|
return this_list
|
||||||
matching_flavors = []
|
matching_flavors = []
|
||||||
for flavor in this_list:
|
for flavor in this_list:
|
||||||
if this_dict[flavor][attr] >= size:
|
if isinstance(size, int):
|
||||||
matching_flavors.append(flavor)
|
if this_dict[flavor][attr] >= size:
|
||||||
|
matching_flavors.append(flavor)
|
||||||
return matching_flavors
|
return matching_flavors
|
||||||
|
|
||||||
def _match_images(self, this_list, this_dict, attr, prop):
|
def _match_images(self, this_list, this_dict, attr, prop):
|
||||||
|
@ -65,7 +65,9 @@ class TranslateInputs():
|
|||||||
hot_constraints = []
|
hot_constraints = []
|
||||||
if input.constraints:
|
if input.constraints:
|
||||||
for constraint in input.constraints:
|
for constraint in input.constraints:
|
||||||
constraint_name, value = constraint.iteritems().next()
|
for name, value in constraint.items():
|
||||||
|
constraint_name = name
|
||||||
|
value = value
|
||||||
hc, hvalue = self._translate_constraints(constraint_name,
|
hc, hvalue = self._translate_constraints(constraint_name,
|
||||||
value)
|
value)
|
||||||
hot_constraints.append({hc: hvalue})
|
hot_constraints.append({hc: hvalue})
|
||||||
|
@ -11,11 +11,16 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from translator.hot.tosca.tosca_block_storage import ToscaBlockStorage
|
||||||
|
from translator.hot.tosca.tosca_block_storage_attachment import (
|
||||||
|
ToscaBlockStorageAttachment
|
||||||
|
)
|
||||||
from translator.hot.tosca.tosca_compute import ToscaCompute
|
from translator.hot.tosca.tosca_compute import ToscaCompute
|
||||||
from translator.hot.tosca.tosca_database import ToscaDatabase
|
from translator.hot.tosca.tosca_database import ToscaDatabase
|
||||||
from translator.hot.tosca.tosca_dbms import ToscaDbms
|
from translator.hot.tosca.tosca_dbms import ToscaDbms
|
||||||
from translator.hot.tosca.tosca_webserver import ToscaWebserver
|
from translator.hot.tosca.tosca_webserver import ToscaWebserver
|
||||||
from translator.hot.tosca.tosca_wordpress import ToscaWordpress
|
from translator.hot.tosca.tosca_wordpress import ToscaWordpress
|
||||||
|
from translator.toscalib.relationship_template import RelationshipTemplate
|
||||||
|
|
||||||
SECTIONS = (TYPE, PROPERTIES, REQUIREMENTS, INTERFACES, LIFECYCLE, INPUT) = \
|
SECTIONS = (TYPE, PROPERTIES, REQUIREMENTS, INTERFACES, LIFECYCLE, INPUT) = \
|
||||||
('type', 'properties', 'requirements',
|
('type', 'properties', 'requirements',
|
||||||
@ -39,7 +44,8 @@ TOSCA_TO_HOT_TYPE = {'tosca.nodes.Compute': ToscaCompute,
|
|||||||
'tosca.nodes.WebServer': ToscaWebserver,
|
'tosca.nodes.WebServer': ToscaWebserver,
|
||||||
'tosca.nodes.DBMS': ToscaDbms,
|
'tosca.nodes.DBMS': ToscaDbms,
|
||||||
'tosca.nodes.Database': ToscaDatabase,
|
'tosca.nodes.Database': ToscaDatabase,
|
||||||
'tosca.nodes.WebApplication.WordPress': ToscaWordpress}
|
'tosca.nodes.WebApplication.WordPress': ToscaWordpress,
|
||||||
|
'tosca.nodes.BlockStorage': ToscaBlockStorage}
|
||||||
|
|
||||||
TOSCA_TO_HOT_REQUIRES = {'container': 'server', 'host': 'server',
|
TOSCA_TO_HOT_REQUIRES = {'container': 'server', 'host': 'server',
|
||||||
'dependency': 'depends_on', "connects": 'depends_on'}
|
'dependency': 'depends_on', "connects": 'depends_on'}
|
||||||
@ -61,18 +67,39 @@ class TranslateNodeTemplates():
|
|||||||
hot_resources = []
|
hot_resources = []
|
||||||
hot_lookup = {}
|
hot_lookup = {}
|
||||||
|
|
||||||
|
attachment_suffix = 0
|
||||||
# Copy the TOSCA graph: nodetemplate
|
# Copy the TOSCA graph: nodetemplate
|
||||||
for node in self.nodetemplates:
|
for node in self.nodetemplates:
|
||||||
hot_node = TOSCA_TO_HOT_TYPE[node.type](node)
|
hot_node = TOSCA_TO_HOT_TYPE[node.type](node)
|
||||||
hot_resources.append(hot_node)
|
hot_resources.append(hot_node)
|
||||||
hot_lookup[node] = hot_node
|
hot_lookup[node] = hot_node
|
||||||
|
|
||||||
# Handle life cycle operations: this may expand each node into
|
# BlockStorage Attachment is a special case,
|
||||||
# multiple HOT resources and may change their name
|
# which doesn't match to Heat Resources 1 to 1.
|
||||||
|
if node.type == "tosca.nodes.Compute":
|
||||||
|
volume_name = None
|
||||||
|
reuirements = node.requirements
|
||||||
|
# Find the name of associated BlockStorage node
|
||||||
|
for requires in reuirements:
|
||||||
|
for value in requires.values():
|
||||||
|
for n in self.nodetemplates:
|
||||||
|
if n.name == value:
|
||||||
|
volume_name = value
|
||||||
|
break
|
||||||
|
attachment_suffix = attachment_suffix + 1
|
||||||
|
attachment_node = self._get_attachment_node(node,
|
||||||
|
attachment_suffix,
|
||||||
|
volume_name)
|
||||||
|
if attachment_node:
|
||||||
|
hot_resources.append(attachment_node)
|
||||||
|
|
||||||
|
# Handle life cycle operations: this may expand each node
|
||||||
|
# into multiple HOT resources and may change their name
|
||||||
lifecycle_resources = []
|
lifecycle_resources = []
|
||||||
for resource in hot_resources:
|
for resource in hot_resources:
|
||||||
expanded = resource.handle_life_cycle()
|
expanded = resource.handle_life_cycle()
|
||||||
lifecycle_resources += expanded
|
if expanded:
|
||||||
|
lifecycle_resources += expanded
|
||||||
hot_resources += lifecycle_resources
|
hot_resources += lifecycle_resources
|
||||||
|
|
||||||
# Copy the initial dependencies based on the relationship in
|
# Copy the initial dependencies based on the relationship in
|
||||||
@ -98,3 +125,22 @@ class TranslateNodeTemplates():
|
|||||||
resource.handle_properties()
|
resource.handle_properties()
|
||||||
|
|
||||||
return hot_resources
|
return hot_resources
|
||||||
|
|
||||||
|
def _get_attachment_node(self, node, suffix, volume_name):
|
||||||
|
attach = False
|
||||||
|
ntpl = self.nodetemplates
|
||||||
|
for key, value in node.relationship.items():
|
||||||
|
if key.type == 'tosca.relationships.AttachTo':
|
||||||
|
if value.type == 'tosca.nodes.BlockStorage':
|
||||||
|
attach = True
|
||||||
|
if attach:
|
||||||
|
for req in node.requirements:
|
||||||
|
for rkey, rval in req.items():
|
||||||
|
if rkey == 'type':
|
||||||
|
rval = rval + "_" + str(suffix)
|
||||||
|
att = RelationshipTemplate(req, rval, None)
|
||||||
|
hot_node = ToscaBlockStorageAttachment(att, ntpl,
|
||||||
|
node.name,
|
||||||
|
volume_name
|
||||||
|
)
|
||||||
|
return hot_node
|
||||||
|
0
translator/tests/__init__.py
Normal file
0
translator/tests/__init__.py
Normal file
53
translator/tests/base.py
Normal file
53
translator/tests/base.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright 2010-2011 OpenStack Foundation
|
||||||
|
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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 os
|
||||||
|
|
||||||
|
import fixtures
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
_TRUE_VALUES = ('True', 'true', '1', 'yes')
|
||||||
|
|
||||||
|
|
||||||
|
class TestCase(testtools.TestCase):
|
||||||
|
|
||||||
|
"""Test case base class for all unit tests."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Run before each test method to initialize test environment."""
|
||||||
|
|
||||||
|
super(TestCase, self).setUp()
|
||||||
|
test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
|
||||||
|
try:
|
||||||
|
test_timeout = int(test_timeout)
|
||||||
|
except ValueError:
|
||||||
|
# If timeout value is invalid do not set a timeout.
|
||||||
|
test_timeout = 0
|
||||||
|
if test_timeout > 0:
|
||||||
|
self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
|
||||||
|
|
||||||
|
self.useFixture(fixtures.NestedTempfile())
|
||||||
|
self.useFixture(fixtures.TempHomeDir())
|
||||||
|
|
||||||
|
if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES:
|
||||||
|
stdout = self.useFixture(fixtures.StringStream('stdout')).stream
|
||||||
|
self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
|
||||||
|
if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES:
|
||||||
|
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
|
||||||
|
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
|
||||||
|
|
||||||
|
self.log_fixture = self.useFixture(fixtures.FakeLogger())
|
@ -0,0 +1,50 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_1.0
|
||||||
|
|
||||||
|
description: >
|
||||||
|
TOSCA simple profile with server and attached block storage.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
cpus:
|
||||||
|
type: integer
|
||||||
|
description: Number of CPUs for the server.
|
||||||
|
constraints:
|
||||||
|
- valid_values: [ 1, 2, 4, 8 ]
|
||||||
|
storage_size:
|
||||||
|
type: integer
|
||||||
|
default: 1 GB
|
||||||
|
description: Size of the storage to be created.
|
||||||
|
storage_snapshot_id:
|
||||||
|
type: string
|
||||||
|
description: Some identifier that represents an existing snapshot that should be used when creating the block storage.
|
||||||
|
storage_location:
|
||||||
|
type: string
|
||||||
|
description: The relative location (e.g., path on the file system), which provides the root location to address an attached node.
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
my_server:
|
||||||
|
type: tosca.nodes.Compute
|
||||||
|
properties:
|
||||||
|
# compute properties (flavor)
|
||||||
|
disk_size: 10
|
||||||
|
num_cpus: { get_input: cpus }
|
||||||
|
mem_size: 4096
|
||||||
|
# host image properties
|
||||||
|
os_arch: x86_64
|
||||||
|
os_type: Linux
|
||||||
|
os_distribution: Fedora
|
||||||
|
os_version: 18
|
||||||
|
requirements:
|
||||||
|
- attachment: my_storage
|
||||||
|
type: AttachTo
|
||||||
|
properties:
|
||||||
|
location: { get_input: storage_location }
|
||||||
|
my_storage:
|
||||||
|
type: tosca.nodes.BlockStorage
|
||||||
|
properties:
|
||||||
|
size: { get_input: storage_size }
|
||||||
|
snapshot_id: { get_input: storage_snapshot_id }
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
public_ip:
|
||||||
|
description: Public IP address of the newly created compute instance.
|
||||||
|
value: { get_attr: [server, ip_address] }
|
@ -0,0 +1,73 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_1.0
|
||||||
|
|
||||||
|
description: >
|
||||||
|
TOSCA simple profile with server and attached block storage.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
cpus:
|
||||||
|
type: integer
|
||||||
|
description: Number of CPUs for the server.
|
||||||
|
constraints:
|
||||||
|
- valid_values: [ 1, 2, 4, 8 ]
|
||||||
|
storage_size:
|
||||||
|
type: integer
|
||||||
|
default: 1 GB
|
||||||
|
description: Size of the storage to be created.
|
||||||
|
storage_snapshot_id:
|
||||||
|
type: string
|
||||||
|
description: Some identifier that represents an existing snapshot that should be used when creating the block storage.
|
||||||
|
storage_location:
|
||||||
|
type: string
|
||||||
|
description: The relative location (e.g., path on the file system), which provides the root location to address an attached node.
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
my_server:
|
||||||
|
type: tosca.nodes.Compute
|
||||||
|
properties:
|
||||||
|
# compute properties (flavor)
|
||||||
|
disk_size: 10
|
||||||
|
num_cpus: { get_input: cpus }
|
||||||
|
mem_size: 4096
|
||||||
|
# host image properties
|
||||||
|
os_arch: x86_64
|
||||||
|
os_type: Linux
|
||||||
|
os_distribution: Fedora
|
||||||
|
os_version: 18
|
||||||
|
requirements:
|
||||||
|
- attachment: my_storage
|
||||||
|
type: AttachTo
|
||||||
|
properties:
|
||||||
|
location: { get_input: storage_location }
|
||||||
|
my_storage:
|
||||||
|
type: tosca.nodes.BlockStorage
|
||||||
|
properties:
|
||||||
|
size: { get_input: storage_size }
|
||||||
|
snapshot_id: { get_input: storage_snapshot_id }
|
||||||
|
|
||||||
|
my_server2:
|
||||||
|
type: tosca.nodes.Compute
|
||||||
|
properties:
|
||||||
|
# compute properties (flavor)
|
||||||
|
disk_size: 10
|
||||||
|
num_cpus: { get_input: cpus }
|
||||||
|
mem_size: 4096
|
||||||
|
# host image properties
|
||||||
|
os_arch: x86_64
|
||||||
|
os_type: Linux
|
||||||
|
os_distribution: Fedora
|
||||||
|
os_version: 18
|
||||||
|
requirements:
|
||||||
|
- attachment: my_storage2
|
||||||
|
type: AttachTo
|
||||||
|
properties:
|
||||||
|
location: { get_input: storage_location }
|
||||||
|
my_storage2:
|
||||||
|
type: tosca.nodes.BlockStorage
|
||||||
|
properties:
|
||||||
|
size: { get_input: storage_size }
|
||||||
|
snapshot_id: { get_input: storage_snapshot_id }
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
public_ip:
|
||||||
|
description: Public IP address of the newly created compute instance.
|
||||||
|
value: { get_attr: [server, ip_address] }
|
80
translator/tests/test_blockstorage.py
Normal file
80
translator/tests/test_blockstorage.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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 os
|
||||||
|
from translator.hot.tosca_translator import TOSCATranslator
|
||||||
|
from translator.tests.base import TestCase
|
||||||
|
from translator.toscalib.tosca_template import ToscaTemplate
|
||||||
|
import translator.toscalib.utils.yamlparser
|
||||||
|
|
||||||
|
|
||||||
|
class ToscaBlockStorageTest(TestCase):
|
||||||
|
parsed_params = {'storage_snapshot_id': 'test_id',
|
||||||
|
'storage_location': '/test', 'cpus': '1',
|
||||||
|
'storage_size': '1'}
|
||||||
|
|
||||||
|
def test_translate_single_storage(self):
|
||||||
|
'''TOSCA template with single BlockStorage and Attachment.'''
|
||||||
|
tosca_tpl = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"data/tosca_blockstorage_with_attachment.yaml")
|
||||||
|
|
||||||
|
tosca = ToscaTemplate(tosca_tpl)
|
||||||
|
translate = TOSCATranslator(tosca, self.parsed_params)
|
||||||
|
output = translate.translate()
|
||||||
|
|
||||||
|
expected_resouce = {'attachto_1':
|
||||||
|
{'type': 'OS::Cinder::VolumeAttachment',
|
||||||
|
'properties':
|
||||||
|
{'instance_uuid': 'my_server',
|
||||||
|
'location': {'get_param': 'storage_location'},
|
||||||
|
'volume_id': 'my_storage'}}}
|
||||||
|
|
||||||
|
output_dict = translator.toscalib.utils.yamlparser.simple_parse(output)
|
||||||
|
|
||||||
|
resources = output_dict.get('resources')
|
||||||
|
translated_value = resources.get('attachto_1')
|
||||||
|
expected_value = expected_resouce.get('attachto_1')
|
||||||
|
self.assertEqual(translated_value, expected_value)
|
||||||
|
|
||||||
|
def test_translate_multi_storage(self):
|
||||||
|
'''TOSCA template with multiple BlockStorage and Attachment.'''
|
||||||
|
tosca_tpl = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"data/tosca_multiple_blockstorage_with_attachment.yaml")
|
||||||
|
tosca = ToscaTemplate(tosca_tpl)
|
||||||
|
translated_volume_attachment = []
|
||||||
|
translate = TOSCATranslator(tosca, self.parsed_params)
|
||||||
|
output = translate.translate()
|
||||||
|
|
||||||
|
expected_resource_1 = {'type': 'OS::Cinder::VolumeAttachment',
|
||||||
|
'properties':
|
||||||
|
{'instance_uuid': 'my_server',
|
||||||
|
'location': {'get_param': 'storage_location'},
|
||||||
|
'volume_id': 'my_storage'}}
|
||||||
|
|
||||||
|
expected_resource_2 = {'type': 'OS::Cinder::VolumeAttachment',
|
||||||
|
'properties':
|
||||||
|
{'instance_uuid': 'my_server2',
|
||||||
|
'location': {'get_param': 'storage_location'},
|
||||||
|
'volume_id': 'my_storage2'}}
|
||||||
|
|
||||||
|
output_dict = translator.toscalib.utils.yamlparser.simple_parse(output)
|
||||||
|
|
||||||
|
resources = output_dict.get('resources')
|
||||||
|
translated_volume_attachment.append(resources.get('attachto_1'))
|
||||||
|
translated_volume_attachment.append(resources.get('attachto_2'))
|
||||||
|
self.assertIn(expected_resource_1, translated_volume_attachment)
|
||||||
|
self.assertIn(expected_resource_2, translated_volume_attachment)
|
@ -92,6 +92,9 @@ tosca.nodes.Compute:
|
|||||||
capabilities:
|
capabilities:
|
||||||
host:
|
host:
|
||||||
type: tosca.capabilities.Container
|
type: tosca.capabilities.Container
|
||||||
|
requirements:
|
||||||
|
- attachment: tosca.nodes.BlockStorage
|
||||||
|
- type: AttachTo
|
||||||
|
|
||||||
tosca.nodes.SoftwareComponent:
|
tosca.nodes.SoftwareComponent:
|
||||||
derived_from: tosca.nodes.Root
|
derived_from: tosca.nodes.Root
|
||||||
@ -169,6 +172,26 @@ tosca.nodes.WebApplication:
|
|||||||
requirements:
|
requirements:
|
||||||
- host: tosca.nodes.WebServer
|
- host: tosca.nodes.WebServer
|
||||||
|
|
||||||
|
tosca.nodes.BlockStorage:
|
||||||
|
derived_from: tosca.nodes.Root
|
||||||
|
properties:
|
||||||
|
size:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- greater_or_equal: 1
|
||||||
|
volume_id:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
snapshot_id:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
attributes:
|
||||||
|
volumeId:
|
||||||
|
type: string
|
||||||
|
capabilities:
|
||||||
|
attachment:
|
||||||
|
type: tosca.capabilities.Attachment
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# Relationship Type.
|
# Relationship Type.
|
||||||
# A Relationship Type is a reusable entity that defines the type of one
|
# A Relationship Type is a reusable entity that defines the type of one
|
||||||
@ -189,6 +212,19 @@ tosca.relationships.ConnectsTo:
|
|||||||
derived_from: tosca.relationships.DependsOn
|
derived_from: tosca.relationships.DependsOn
|
||||||
valid_targets: [ tosca.capabilities.Endpoint ]
|
valid_targets: [ tosca.capabilities.Endpoint ]
|
||||||
|
|
||||||
|
tosca.relationships.AttachTo:
|
||||||
|
derived_from: tosca.relationships.Root
|
||||||
|
valid_targets: [ tosca.capabilities.Attachment ]
|
||||||
|
properties:
|
||||||
|
location:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
constraints:
|
||||||
|
- min_length: 1
|
||||||
|
device:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# Capability Type.
|
# Capability Type.
|
||||||
# A Capability Type is a reusable entity that describes a kind of
|
# A Capability Type is a reusable entity that describes a kind of
|
||||||
@ -220,6 +256,9 @@ tosca.capabilities.Endpoint:
|
|||||||
tosca.capabilities.DatabaseEndpoint:
|
tosca.capabilities.DatabaseEndpoint:
|
||||||
derived_from: tosca.capabilities.Endpoint
|
derived_from: tosca.capabilities.Endpoint
|
||||||
|
|
||||||
|
tosca.capabilities.Attachment:
|
||||||
|
derived_from: tosca.capabilities.Root
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# Interfaces Type.
|
# Interfaces Type.
|
||||||
# The Interfaces element describes a list of one or more interface
|
# The Interfaces element describes a list of one or more interface
|
||||||
|
@ -13,20 +13,16 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from translator.toscalib.elements.entitytype import EntityType
|
|
||||||
from translator.toscalib.elements.property_definition import PropertyDef
|
from translator.toscalib.elements.property_definition import PropertyDef
|
||||||
|
from translator.toscalib.elements.statefulentitytype import StatefulEntityType
|
||||||
|
|
||||||
|
|
||||||
class CapabilityTypeDef(EntityType):
|
class CapabilityTypeDef(StatefulEntityType):
|
||||||
'''TOSCA built-in capabilities type.'''
|
'''TOSCA built-in capabilities type.'''
|
||||||
|
|
||||||
def __init__(self, name, ctype, ntype, properties):
|
def __init__(self, name, ctype, ntype, properties):
|
||||||
self.name = name
|
self.name = name
|
||||||
if self.CAPABILITY_PREFIX not in ctype:
|
super(CapabilityTypeDef, self).__init__(ctype, self.CAPABILITY_PREFIX)
|
||||||
ctype = self.CAPABILITY_PREFIX + ctype
|
|
||||||
if self.NODE_PREFIX not in ntype:
|
|
||||||
ntype = self.NODE_PREFIX + ntype
|
|
||||||
self.type = ctype
|
|
||||||
self.nodetype = ntype
|
self.nodetype = ntype
|
||||||
self.properties = properties
|
self.properties = properties
|
||||||
self.defs = {}
|
self.defs = {}
|
||||||
|
@ -23,6 +23,11 @@ log = logging.getLogger('tosca')
|
|||||||
class EntityType(object):
|
class EntityType(object):
|
||||||
'''Base class for TOSCA elements.'''
|
'''Base class for TOSCA elements.'''
|
||||||
|
|
||||||
|
SECTIONS = (DERIVED_FROM, PROPERTIES, ATTRIBUTES, REQUIREMENTS,
|
||||||
|
INTERFACES, CAPABILITIES) = \
|
||||||
|
('derived_from', 'properties', 'attributes', 'requirements',
|
||||||
|
'interfaces', 'capabilities')
|
||||||
|
|
||||||
'''TOSCA definition file.'''
|
'''TOSCA definition file.'''
|
||||||
TOSCA_DEF_FILE = os.path.join(
|
TOSCA_DEF_FILE = os.path.join(
|
||||||
os.path.dirname(os.path.abspath(__file__)),
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
@ -32,10 +37,11 @@ class EntityType(object):
|
|||||||
|
|
||||||
TOSCA_DEF = loader(TOSCA_DEF_FILE)
|
TOSCA_DEF = loader(TOSCA_DEF_FILE)
|
||||||
|
|
||||||
RELATIONSHIP_TYPE = (DEPENDSON, HOSTEDON, CONNECTSTO) = \
|
RELATIONSHIP_TYPE = (DEPENDSON, HOSTEDON, CONNECTSTO, ATTACHTO) = \
|
||||||
('tosca.relationships.DependsOn',
|
('tosca.relationships.DependsOn',
|
||||||
'tosca.relationships.HostedOn',
|
'tosca.relationships.HostedOn',
|
||||||
'tosca.relationships.ConnectsTo')
|
'tosca.relationships.ConnectsTo',
|
||||||
|
'tosca.relationships.AttachTo')
|
||||||
|
|
||||||
NODE_PREFIX = 'tosca.nodes.'
|
NODE_PREFIX = 'tosca.nodes.'
|
||||||
RELATIONSHIP_PREFIX = 'tosca.relationships.'
|
RELATIONSHIP_PREFIX = 'tosca.relationships.'
|
||||||
@ -49,3 +55,21 @@ class EntityType(object):
|
|||||||
def entity_value(self, defs, key):
|
def entity_value(self, defs, key):
|
||||||
if key in defs:
|
if key in defs:
|
||||||
return defs[key]
|
return defs[key]
|
||||||
|
|
||||||
|
def get_value(self, ndtype, defs=None, parent=None):
|
||||||
|
value = None
|
||||||
|
if defs is None:
|
||||||
|
defs = self.defs
|
||||||
|
if ndtype in defs:
|
||||||
|
value = defs[ndtype]
|
||||||
|
if parent and not value:
|
||||||
|
p = self.parent_type
|
||||||
|
while value is None:
|
||||||
|
#check parent node
|
||||||
|
if not p:
|
||||||
|
break
|
||||||
|
if p and p.type == 'tosca.nodes.Root':
|
||||||
|
break
|
||||||
|
value = p.get_value(ndtype)
|
||||||
|
p = p.parent_type
|
||||||
|
return value
|
||||||
|
@ -49,7 +49,7 @@ class InterfacesDef(StatefulEntityType):
|
|||||||
elif i == 'input':
|
elif i == 'input':
|
||||||
self.input = self._create_input_functions(j)
|
self.input = self._create_input_functions(j)
|
||||||
else:
|
else:
|
||||||
what = ('Interfaces of node template %s' %
|
what = ('Interfaces of template %s' %
|
||||||
self.node_template.name)
|
self.node_template.name)
|
||||||
raise UnknownFieldError(what=what, field=i)
|
raise UnknownFieldError(what=what, field=i)
|
||||||
else:
|
else:
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from translator.toscalib.common.exception import InvalidNodeTypeError
|
|
||||||
from translator.toscalib.elements.attribute_definition import AttributeDef
|
from translator.toscalib.elements.attribute_definition import AttributeDef
|
||||||
from translator.toscalib.elements.capabilitytype import CapabilityTypeDef
|
from translator.toscalib.elements.capabilitytype import CapabilityTypeDef
|
||||||
from translator.toscalib.elements.interfaces import InterfacesDef
|
from translator.toscalib.elements.interfaces import InterfacesDef
|
||||||
@ -22,26 +21,11 @@ from translator.toscalib.elements.relationshiptype import RelationshipType
|
|||||||
from translator.toscalib.elements.statefulentitytype import StatefulEntityType
|
from translator.toscalib.elements.statefulentitytype import StatefulEntityType
|
||||||
|
|
||||||
|
|
||||||
SECTIONS = (DERIVED_FROM, PROPERTIES, ATTRIBUTES, REQUIREMENTS,
|
|
||||||
INTERFACES, CAPABILITIES) = \
|
|
||||||
('derived_from', 'properties', 'attributes', 'requirements',
|
|
||||||
'interfaces', 'capabilities')
|
|
||||||
|
|
||||||
|
|
||||||
class NodeType(StatefulEntityType):
|
class NodeType(StatefulEntityType):
|
||||||
'''TOSCA built-in node type.'''
|
'''TOSCA built-in node type.'''
|
||||||
|
|
||||||
def __init__(self, ntype, custom_def=None):
|
def __init__(self, ntype, custom_def=None):
|
||||||
super(NodeType, self).__init__()
|
super(NodeType, self).__init__(ntype, self.NODE_PREFIX, custom_def)
|
||||||
if self.NODE_PREFIX not in ntype:
|
|
||||||
ntype = self.NODE_PREFIX + ntype
|
|
||||||
if ntype in list(self.TOSCA_DEF.keys()):
|
|
||||||
self.defs = self.TOSCA_DEF[ntype]
|
|
||||||
elif custom_def and ntype in list(custom_def.keys()):
|
|
||||||
self.defs = custom_def[ntype]
|
|
||||||
else:
|
|
||||||
raise InvalidNodeTypeError(what=ntype)
|
|
||||||
self.type = ntype
|
|
||||||
self.related = {}
|
self.related = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -55,7 +39,7 @@ class NodeType(StatefulEntityType):
|
|||||||
def properties_def(self):
|
def properties_def(self):
|
||||||
'''Return a list of property definition objects.'''
|
'''Return a list of property definition objects.'''
|
||||||
properties = []
|
properties = []
|
||||||
props = self.get_value(PROPERTIES)
|
props = self.get_value(self.PROPERTIES)
|
||||||
if props:
|
if props:
|
||||||
for prop, schema in props.items():
|
for prop, schema in props.items():
|
||||||
properties.append(PropertyDef(prop, None, schema))
|
properties.append(PropertyDef(prop, None, schema))
|
||||||
@ -64,7 +48,7 @@ class NodeType(StatefulEntityType):
|
|||||||
@property
|
@property
|
||||||
def attributes_def(self):
|
def attributes_def(self):
|
||||||
'''Return a list of attribute definition objects.'''
|
'''Return a list of attribute definition objects.'''
|
||||||
attrs = self.get_value(ATTRIBUTES)
|
attrs = self.get_value(self.ATTRIBUTES)
|
||||||
if attrs:
|
if attrs:
|
||||||
return [AttributeDef(attr, None, schema)
|
return [AttributeDef(attr, None, schema)
|
||||||
for attr, schema in attrs.items()]
|
for attr, schema in attrs.items()]
|
||||||
@ -84,6 +68,8 @@ class NodeType(StatefulEntityType):
|
|||||||
if requires:
|
if requires:
|
||||||
for req in requires:
|
for req in requires:
|
||||||
for key, value in req.items():
|
for key, value in req.items():
|
||||||
|
if key == 'type':
|
||||||
|
continue
|
||||||
relation = self._get_relation(key, value)
|
relation = self._get_relation(key, value)
|
||||||
rtype = RelationshipType(relation, key)
|
rtype = RelationshipType(relation, key)
|
||||||
relatednode = NodeType(value)
|
relatednode = NodeType(value)
|
||||||
@ -112,9 +98,9 @@ class NodeType(StatefulEntityType):
|
|||||||
typecapabilities = []
|
typecapabilities = []
|
||||||
self.cap_prop = None
|
self.cap_prop = None
|
||||||
self.cap_type = None
|
self.cap_type = None
|
||||||
caps = self.get_value(CAPABILITIES)
|
caps = self.get_value(self.CAPABILITIES)
|
||||||
if caps is None:
|
if caps is None:
|
||||||
caps = self.get_value(CAPABILITIES, None, True)
|
caps = self.get_value(self.CAPABILITIES, None, True)
|
||||||
if caps:
|
if caps:
|
||||||
cproperties = None
|
cproperties = None
|
||||||
for name, value in caps.items():
|
for name, value in caps.items():
|
||||||
@ -128,24 +114,24 @@ class NodeType(StatefulEntityType):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def requirements(self):
|
def requirements(self):
|
||||||
return self.get_value(REQUIREMENTS)
|
return self.get_value(self.REQUIREMENTS)
|
||||||
|
|
||||||
def get_all_requirements(self):
|
def get_all_requirements(self):
|
||||||
requires = self.requirements
|
requires = self.requirements
|
||||||
parent_node = self.parent_type
|
parent_node = self.parent_type
|
||||||
if requires is None:
|
if requires is None:
|
||||||
requires = self.get_value(REQUIREMENTS, None, True)
|
requires = self.get_value(self.REQUIREMENTS, None, True)
|
||||||
parent_node = parent_node.parent_type
|
parent_node = parent_node.parent_type
|
||||||
if parent_node:
|
if parent_node:
|
||||||
while parent_node.type != 'tosca.nodes.Root':
|
while parent_node.type != 'tosca.nodes.Root':
|
||||||
req = parent_node.get_value(REQUIREMENTS, None, True)
|
req = parent_node.get_value(self.REQUIREMENTS, None, True)
|
||||||
requires.extend(req)
|
requires.extend(req)
|
||||||
parent_node = parent_node.parent_type
|
parent_node = parent_node.parent_type
|
||||||
return requires
|
return requires
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def interfaces(self):
|
def interfaces(self):
|
||||||
return self.get_value(INTERFACES)
|
return self.get_value(self.INTERFACES)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lifecycle_inputs(self):
|
def lifecycle_inputs(self):
|
||||||
@ -180,21 +166,3 @@ class NodeType(StatefulEntityType):
|
|||||||
for key, value in self.get_capability(name):
|
for key, value in self.get_capability(name):
|
||||||
if key == type:
|
if key == type:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def get_value(self, ndtype, defs=None, parent=None):
|
|
||||||
value = None
|
|
||||||
if defs is None:
|
|
||||||
defs = self.defs
|
|
||||||
if ndtype in defs:
|
|
||||||
value = defs[ndtype]
|
|
||||||
if parent and not value:
|
|
||||||
p = self.parent_type
|
|
||||||
while value is None:
|
|
||||||
#check parent node
|
|
||||||
if not p:
|
|
||||||
break
|
|
||||||
if p and p.type == 'tosca.nodes.Root':
|
|
||||||
break
|
|
||||||
value = p.get_value(ndtype)
|
|
||||||
p = p.parent_type
|
|
||||||
return value
|
|
||||||
|
@ -13,18 +13,15 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from translator.toscalib.elements.property_definition import PropertyDef
|
||||||
from translator.toscalib.elements.statefulentitytype import StatefulEntityType
|
from translator.toscalib.elements.statefulentitytype import StatefulEntityType
|
||||||
|
|
||||||
|
|
||||||
class RelationshipType(StatefulEntityType):
|
class RelationshipType(StatefulEntityType):
|
||||||
'''TOSCA built-in relationship type.'''
|
'''TOSCA built-in relationship type.'''
|
||||||
|
def __init__(self, type, capability_name, custom_def=None):
|
||||||
def __init__(self, type, capability_name):
|
super(RelationshipType, self).__init__(type, self.RELATIONSHIP_PREFIX,
|
||||||
super(RelationshipType, self).__init__()
|
custom_def)
|
||||||
self.defs = self.TOSCA_DEF[type]
|
|
||||||
if self.RELATIONSHIP_PREFIX not in type:
|
|
||||||
type = self.RELATIONSHIP_PREFIX + type
|
|
||||||
self.type = type
|
|
||||||
self.capability_name = capability_name
|
self.capability_name = capability_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -35,3 +32,13 @@ class RelationshipType(StatefulEntityType):
|
|||||||
def parent_type(self):
|
def parent_type(self):
|
||||||
'''Return a relationship this relationship is derived from.'''
|
'''Return a relationship this relationship is derived from.'''
|
||||||
return self.derived_from(self.defs)
|
return self.derived_from(self.defs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def properties_def(self):
|
||||||
|
'''Return a list of property definition objects.'''
|
||||||
|
properties = []
|
||||||
|
props = self.get_value(self.PROPERTIES)
|
||||||
|
if props:
|
||||||
|
for prop, schema in props.items():
|
||||||
|
properties.append(PropertyDef(prop, None, schema))
|
||||||
|
return properties
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from translator.toscalib.common.exception import InvalidNodeTypeError
|
||||||
from translator.toscalib.elements.entitytype import EntityType
|
from translator.toscalib.elements.entitytype import EntityType
|
||||||
|
|
||||||
|
|
||||||
@ -27,3 +28,14 @@ class StatefulEntityType(EntityType):
|
|||||||
'post_configure_target',
|
'post_configure_target',
|
||||||
'add_target',
|
'add_target',
|
||||||
'remove_target']
|
'remove_target']
|
||||||
|
|
||||||
|
def __init__(self, entitytype, prefix, custom_def=None):
|
||||||
|
if prefix not in entitytype:
|
||||||
|
entitytype = prefix + entitytype
|
||||||
|
if entitytype in list(self.TOSCA_DEF.keys()):
|
||||||
|
self.defs = self.TOSCA_DEF[entitytype]
|
||||||
|
elif custom_def and entitytype in list(custom_def.keys()):
|
||||||
|
self.defs = custom_def[entitytype]
|
||||||
|
else:
|
||||||
|
raise InvalidNodeTypeError(what=entitytype)
|
||||||
|
self.type = entitytype
|
||||||
|
157
translator/toscalib/entity_template.py
Normal file
157
translator/toscalib/entity_template.py
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
from translator.toscalib.common.exception import MissingRequiredFieldError
|
||||||
|
from translator.toscalib.common.exception import UnknownFieldError
|
||||||
|
from translator.toscalib.elements.capabilitytype import CapabilityTypeDef
|
||||||
|
from translator.toscalib.elements.interfaces import InterfacesDef
|
||||||
|
from translator.toscalib.elements.nodetype import NodeType
|
||||||
|
from translator.toscalib.elements.relationshiptype import RelationshipType
|
||||||
|
from translator.toscalib.properties import Property
|
||||||
|
|
||||||
|
|
||||||
|
class EntityTemplate(object):
|
||||||
|
'''Base class for TOSCA templates.'''
|
||||||
|
|
||||||
|
SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
|
||||||
|
INTERFACES, CAPABILITIES, TYPE) = \
|
||||||
|
('derived_from', 'properties', 'requirements', 'interfaces',
|
||||||
|
'capabilities', 'type')
|
||||||
|
|
||||||
|
def __init__(self, name, template, entity_name, custom_def=None):
|
||||||
|
self.name = name
|
||||||
|
self.entity_tpl = template
|
||||||
|
self.custom_def = custom_def
|
||||||
|
self._validate_field(self.entity_tpl)
|
||||||
|
if entity_name == 'node_type':
|
||||||
|
self.type_definition = NodeType(self.entity_tpl['type'],
|
||||||
|
custom_def)
|
||||||
|
if entity_name == 'relationship_type':
|
||||||
|
self.type_definition = RelationshipType(self.entity_tpl['type'],
|
||||||
|
custom_def)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return self.type_definition.type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def requirements(self):
|
||||||
|
return self.type_definition.get_value(self.REQUIREMENTS,
|
||||||
|
self.entity_tpl)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def properties(self):
|
||||||
|
props = []
|
||||||
|
properties = self.type_definition.get_value(self.PROPERTIES,
|
||||||
|
self.entity_tpl)
|
||||||
|
if properties:
|
||||||
|
for name, value in properties.items():
|
||||||
|
for p in self.type_definition.properties_def:
|
||||||
|
if p.name == name:
|
||||||
|
prop = Property(name, value, p.schema)
|
||||||
|
props.append(prop)
|
||||||
|
return props
|
||||||
|
|
||||||
|
@property
|
||||||
|
def interfaces(self):
|
||||||
|
interfaces = []
|
||||||
|
type_interfaces = self.type_definition.get_value(self.INTERFACES,
|
||||||
|
self.entity_tpl)
|
||||||
|
if type_interfaces:
|
||||||
|
for interface_type, value in type_interfaces.items():
|
||||||
|
for op, op_def in value.items():
|
||||||
|
iface = InterfacesDef(self.type_definition,
|
||||||
|
interfacetype=interface_type,
|
||||||
|
node_template=self,
|
||||||
|
name=op,
|
||||||
|
value=op_def)
|
||||||
|
interfaces.append(iface)
|
||||||
|
return interfaces
|
||||||
|
|
||||||
|
@property
|
||||||
|
def capabilities(self):
|
||||||
|
capability = []
|
||||||
|
properties = {}
|
||||||
|
cap_type = None
|
||||||
|
caps = self.type_definition.get_value(self.CAPABILITIES,
|
||||||
|
self.entity_tpl)
|
||||||
|
if caps:
|
||||||
|
for name, value in caps.items():
|
||||||
|
for prop, val in value.items():
|
||||||
|
properties = val
|
||||||
|
for c in self.type_definition.capabilities:
|
||||||
|
if c.name == name:
|
||||||
|
cap_type = c.type
|
||||||
|
cap = CapabilityTypeDef(name, cap_type,
|
||||||
|
self.name, properties)
|
||||||
|
capability.append(cap)
|
||||||
|
return capability
|
||||||
|
|
||||||
|
def _validate_properties(self, template, entitytype):
|
||||||
|
properties = entitytype.get_value(self.PROPERTIES, template)
|
||||||
|
allowed_props = []
|
||||||
|
required_props = []
|
||||||
|
for p in entitytype.properties_def:
|
||||||
|
allowed_props.append(p.name)
|
||||||
|
if p.required:
|
||||||
|
required_props.append(p.name)
|
||||||
|
if properties:
|
||||||
|
self._common_validate_field(properties, allowed_props,
|
||||||
|
'Properties')
|
||||||
|
#make sure it's not missing any property required by a tosca type
|
||||||
|
missingprop = []
|
||||||
|
for r in required_props:
|
||||||
|
if r not in properties.keys():
|
||||||
|
missingprop.append(r)
|
||||||
|
if missingprop:
|
||||||
|
raise MissingRequiredFieldError(
|
||||||
|
what='Properties of template %s' % self.name,
|
||||||
|
required=missingprop)
|
||||||
|
else:
|
||||||
|
if required_props:
|
||||||
|
raise MissingRequiredFieldError(
|
||||||
|
what='Properties of template %s' % self.name,
|
||||||
|
required=missingprop)
|
||||||
|
|
||||||
|
def _validate_capabilities(self):
|
||||||
|
type_capabilities = self.type_definition.capabilities
|
||||||
|
allowed_caps = []
|
||||||
|
if type_capabilities:
|
||||||
|
for tcap in type_capabilities:
|
||||||
|
allowed_caps.append(tcap.name)
|
||||||
|
capabilities = self.type_definition.get_value(self.CAPABILITIES,
|
||||||
|
self.entity_tpl)
|
||||||
|
if capabilities:
|
||||||
|
self._common_validate_field(capabilities, allowed_caps,
|
||||||
|
'Capabilities')
|
||||||
|
|
||||||
|
def _validate_field(self, template):
|
||||||
|
if not isinstance(template, dict):
|
||||||
|
raise MissingRequiredFieldError(
|
||||||
|
what='Template %s' % self.name, required=self.TYPE)
|
||||||
|
try:
|
||||||
|
template[self.TYPE]
|
||||||
|
except KeyError:
|
||||||
|
raise MissingRequiredFieldError(
|
||||||
|
what='Template %s' % self.name, required=self.TYPE)
|
||||||
|
|
||||||
|
def _common_validate_field(self, schema, allowedlist, section):
|
||||||
|
for name in schema:
|
||||||
|
if name not in allowedlist:
|
||||||
|
raise UnknownFieldError(
|
||||||
|
what='%(section)s of template %(nodename)s'
|
||||||
|
% {'section': section, 'nodename': self.name},
|
||||||
|
field=name)
|
@ -76,7 +76,7 @@ class GetRefProperty(Function):
|
|||||||
if name:
|
if name:
|
||||||
from translator.toscalib.nodetemplate import NodeTemplate
|
from translator.toscalib.nodetemplate import NodeTemplate
|
||||||
tpl = NodeTemplate(
|
tpl = NodeTemplate(
|
||||||
name, self.node_template.node_templates)
|
name, self.node_template.templates)
|
||||||
caps = tpl.capabilities
|
caps = tpl.capabilities
|
||||||
required_cap = self.args[1]
|
required_cap = self.args[1]
|
||||||
required_property = self.args[2]
|
required_property = self.args[2]
|
||||||
|
@ -16,42 +16,24 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from translator.toscalib.common.exception import MissingRequiredFieldError
|
|
||||||
from translator.toscalib.common.exception import TypeMismatchError
|
from translator.toscalib.common.exception import TypeMismatchError
|
||||||
from translator.toscalib.common.exception import UnknownFieldError
|
from translator.toscalib.common.exception import UnknownFieldError
|
||||||
from translator.toscalib.elements.capabilitytype import CapabilityTypeDef
|
|
||||||
from translator.toscalib.elements.interfaces import InterfacesDef
|
from translator.toscalib.elements.interfaces import InterfacesDef
|
||||||
from translator.toscalib.elements.interfaces import LIFECYCLE, CONFIGURE
|
from translator.toscalib.elements.interfaces import LIFECYCLE, CONFIGURE
|
||||||
from translator.toscalib.elements.nodetype import NodeType
|
from translator.toscalib.entity_template import EntityTemplate
|
||||||
from translator.toscalib.properties import Property
|
|
||||||
|
|
||||||
|
|
||||||
SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
|
|
||||||
INTERFACES, CAPABILITIES, TYPE) = \
|
|
||||||
('derived_from', 'properties', 'requirements', 'interfaces',
|
|
||||||
'capabilities', 'type')
|
|
||||||
|
|
||||||
log = logging.getLogger('tosca')
|
log = logging.getLogger('tosca')
|
||||||
|
|
||||||
|
|
||||||
class NodeTemplate(object):
|
class NodeTemplate(EntityTemplate):
|
||||||
'''Node template from a Tosca profile.'''
|
'''Node template from a Tosca profile.'''
|
||||||
def __init__(self, name, node_templates, custom_def=None):
|
def __init__(self, name, node_templates, custom_def=None):
|
||||||
self.name = name
|
super(NodeTemplate, self).__init__(name, node_templates[name],
|
||||||
self.node_templates = node_templates
|
'node_type',
|
||||||
self._validate_field()
|
custom_def)
|
||||||
self.node_template = node_templates[self.name]
|
self.templates = node_templates
|
||||||
self.node_type = NodeType(self.node_template[TYPE], custom_def)
|
|
||||||
self.related = {}
|
self.related = {}
|
||||||
|
|
||||||
@property
|
|
||||||
def type(self):
|
|
||||||
return self.node_type.type
|
|
||||||
|
|
||||||
@property
|
|
||||||
def requirements(self):
|
|
||||||
return self.node_type.get_value(REQUIREMENTS, self.node_template)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def relationship(self):
|
def relationship(self):
|
||||||
relation = {}
|
relation = {}
|
||||||
@ -59,72 +41,26 @@ class NodeTemplate(object):
|
|||||||
if requires:
|
if requires:
|
||||||
for r in requires:
|
for r in requires:
|
||||||
for cap, node in r.items():
|
for cap, node in r.items():
|
||||||
for rtype in self.node_type.relationship.keys():
|
for rtype in self.type_definition.relationship.keys():
|
||||||
if cap == rtype.capability_name:
|
if cap == rtype.capability_name:
|
||||||
rtpl = NodeTemplate(node, self.node_templates)
|
rtpl = NodeTemplate(node, self.templates)
|
||||||
relation[rtype] = rtpl
|
relation[rtype] = rtpl
|
||||||
return relation
|
return relation
|
||||||
|
|
||||||
@property
|
|
||||||
def capabilities(self):
|
|
||||||
capability = []
|
|
||||||
properties = {}
|
|
||||||
cap_type = None
|
|
||||||
caps = self.node_type.get_value(CAPABILITIES, self.node_template)
|
|
||||||
if caps:
|
|
||||||
for name, value in caps.items():
|
|
||||||
for prop, val in value.items():
|
|
||||||
properties = val
|
|
||||||
for c in self.node_type.capabilities:
|
|
||||||
if c.name == name:
|
|
||||||
cap_type = c.type
|
|
||||||
cap = CapabilityTypeDef(name, cap_type,
|
|
||||||
self.name, properties)
|
|
||||||
capability.append(cap)
|
|
||||||
return capability
|
|
||||||
|
|
||||||
@property
|
|
||||||
def interfaces(self):
|
|
||||||
interfaces = []
|
|
||||||
type_interfaces = self.node_type.get_value(INTERFACES,
|
|
||||||
self.node_template)
|
|
||||||
if type_interfaces:
|
|
||||||
for interface_type, value in type_interfaces.items():
|
|
||||||
for op, op_def in value.items():
|
|
||||||
iface = InterfacesDef(self.node_type,
|
|
||||||
interfacetype=interface_type,
|
|
||||||
node_template=self,
|
|
||||||
name=op,
|
|
||||||
value=op_def)
|
|
||||||
interfaces.append(iface)
|
|
||||||
return interfaces
|
|
||||||
|
|
||||||
@property
|
|
||||||
def properties(self):
|
|
||||||
props = []
|
|
||||||
properties = self.node_type.get_value(PROPERTIES, self.node_template)
|
|
||||||
if properties:
|
|
||||||
for name, value in properties.items():
|
|
||||||
for p in self.node_type.properties_def:
|
|
||||||
if p.name == name:
|
|
||||||
prop = Property(name, value, p.schema)
|
|
||||||
props.append(prop)
|
|
||||||
return props
|
|
||||||
|
|
||||||
def _add_next(self, nodetpl, relationship):
|
def _add_next(self, nodetpl, relationship):
|
||||||
self.related[nodetpl] = relationship
|
self.related[nodetpl] = relationship
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def related_nodes(self):
|
def related_nodes(self):
|
||||||
if not self.related:
|
if not self.related:
|
||||||
for relation, node in self.node_type.relationship.items():
|
for relation, node in self.type_definition.relationship.items():
|
||||||
for tpl in self.node_templates:
|
for tpl in self.node_templates:
|
||||||
if tpl == node.type:
|
if tpl == node.type:
|
||||||
self.related[NodeTemplate(tpl)] = relation
|
self.related[NodeTemplate(tpl)] = relation
|
||||||
return self.related.keys()
|
return self.related.keys()
|
||||||
|
|
||||||
def ref_property(self, cap, cap_name, property):
|
def ref_property(self, cap, cap_name, property):
|
||||||
requires = self.node_type.requirements
|
requires = self.type_definition.requirements
|
||||||
name = None
|
name = None
|
||||||
if requires:
|
if requires:
|
||||||
for r in requires:
|
for r in requires:
|
||||||
@ -142,42 +78,32 @@ class NodeTemplate(object):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self._validate_capabilities()
|
self._validate_capabilities()
|
||||||
self._validate_requirments()
|
self._validate_requirements()
|
||||||
self._validate_properties()
|
self._validate_properties(self.entity_tpl, self.type_definition)
|
||||||
self._validate_interfaces()
|
self._validate_interfaces()
|
||||||
for prop in self.properties:
|
for prop in self.properties:
|
||||||
prop.validate()
|
prop.validate()
|
||||||
|
|
||||||
def _validate_capabilities(self):
|
def _validate_requirements(self):
|
||||||
type_capabilities = self.node_type.capabilities
|
type_requires = self.type_definition.get_all_requirements()
|
||||||
allowed_caps = []
|
allowed_reqs = ['type', 'properties', 'interfaces']
|
||||||
if type_capabilities:
|
|
||||||
for tcap in type_capabilities:
|
|
||||||
allowed_caps.append(tcap.name)
|
|
||||||
capabilities = self.node_type.get_value(CAPABILITIES,
|
|
||||||
self.node_template)
|
|
||||||
if capabilities:
|
|
||||||
self._common_validate_field(capabilities, allowed_caps,
|
|
||||||
'Capabilities')
|
|
||||||
|
|
||||||
def _validate_requirments(self):
|
|
||||||
type_requires = self.node_type.get_all_requirements()
|
|
||||||
allowed_reqs = []
|
|
||||||
if type_requires:
|
if type_requires:
|
||||||
for treq in type_requires:
|
for treq in type_requires:
|
||||||
for key in treq:
|
for key in treq:
|
||||||
allowed_reqs.append(key)
|
allowed_reqs.append(key)
|
||||||
requires = self.node_type.get_value(REQUIREMENTS, self.node_template)
|
requires = self.type_definition.get_value(self.REQUIREMENTS,
|
||||||
|
self.entity_tpl)
|
||||||
if requires:
|
if requires:
|
||||||
if not isinstance(requires, list):
|
if not isinstance(requires, list):
|
||||||
raise TypeMismatchError(
|
raise TypeMismatchError(
|
||||||
what='Requirements of node template %s' % self.name,
|
what='Requirements of template %s' % self.name,
|
||||||
type='list')
|
type='list')
|
||||||
for req in requires:
|
for req in requires:
|
||||||
self._common_validate_field(req, allowed_reqs, 'Requirements')
|
self._common_validate_field(req, allowed_reqs, 'Requirements')
|
||||||
|
|
||||||
def _validate_interfaces(self):
|
def _validate_interfaces(self):
|
||||||
ifaces = self.node_type.get_value(INTERFACES, self.node_template)
|
ifaces = self.type_definition.get_value(self.INTERFACES,
|
||||||
|
self.entity_tpl)
|
||||||
if ifaces:
|
if ifaces:
|
||||||
for i in ifaces:
|
for i in ifaces:
|
||||||
for name, value in ifaces.items():
|
for name, value in ifaces.items():
|
||||||
@ -193,51 +119,5 @@ class NodeTemplate(object):
|
|||||||
'Interfaces')
|
'Interfaces')
|
||||||
else:
|
else:
|
||||||
raise UnknownFieldError(
|
raise UnknownFieldError(
|
||||||
what='Interfaces of node template %s' % self.name,
|
what='Interfaces of template %s' % self.name,
|
||||||
field=name)
|
field=name)
|
||||||
|
|
||||||
def _validate_properties(self):
|
|
||||||
properties = self.node_type.get_value(PROPERTIES, self.node_template)
|
|
||||||
allowed_props = []
|
|
||||||
required_props = []
|
|
||||||
for p in self.node_type.properties_def:
|
|
||||||
allowed_props.append(p.name)
|
|
||||||
if p.required:
|
|
||||||
required_props.append(p.name)
|
|
||||||
if properties:
|
|
||||||
self._common_validate_field(properties, allowed_props,
|
|
||||||
'Properties')
|
|
||||||
#make sure it's not missing any property required by a node type
|
|
||||||
missingprop = []
|
|
||||||
for r in required_props:
|
|
||||||
if r not in properties.keys():
|
|
||||||
missingprop.append(r)
|
|
||||||
if missingprop:
|
|
||||||
raise MissingRequiredFieldError(
|
|
||||||
what='Properties of node template %s' % self.name,
|
|
||||||
required=missingprop)
|
|
||||||
else:
|
|
||||||
if required_props:
|
|
||||||
raise MissingRequiredFieldError(
|
|
||||||
what='Properties of node template %s' % self.name,
|
|
||||||
required=missingprop)
|
|
||||||
|
|
||||||
def _validate_field(self):
|
|
||||||
if not isinstance(self.node_templates[self.name], dict):
|
|
||||||
raise MissingRequiredFieldError(
|
|
||||||
what='Node template %s' % self.name, required=TYPE)
|
|
||||||
try:
|
|
||||||
self.node_templates[self.name][TYPE]
|
|
||||||
except KeyError:
|
|
||||||
raise MissingRequiredFieldError(
|
|
||||||
what='Node template %s' % self.name, required=TYPE)
|
|
||||||
self._common_validate_field(self.node_templates[self.name], SECTIONS,
|
|
||||||
'Second level')
|
|
||||||
|
|
||||||
def _common_validate_field(self, schema, allowedlist, section):
|
|
||||||
for name in schema:
|
|
||||||
if name not in allowedlist:
|
|
||||||
raise UnknownFieldError(
|
|
||||||
what='%(section)s of node template %(nodename)s'
|
|
||||||
% {'section': section, 'nodename': self.name},
|
|
||||||
field=name)
|
|
||||||
|
39
translator/toscalib/relationship_template.py
Normal file
39
translator/toscalib/relationship_template.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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 translator.toscalib.entity_template import EntityTemplate
|
||||||
|
|
||||||
|
SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
|
||||||
|
INTERFACES, CAPABILITIES, TYPE) = \
|
||||||
|
('derived_from', 'properties', 'requirements', 'interfaces',
|
||||||
|
'capabilities', 'type')
|
||||||
|
|
||||||
|
log = logging.getLogger('tosca')
|
||||||
|
|
||||||
|
|
||||||
|
class RelationshipTemplate(EntityTemplate):
|
||||||
|
'''Relationship template.'''
|
||||||
|
def __init__(self, relationship_template, name, custom_def=None):
|
||||||
|
super(RelationshipTemplate, self).__init__(name,
|
||||||
|
relationship_template,
|
||||||
|
'relationship_type',
|
||||||
|
custom_def=None)
|
||||||
|
self.name = name.lower()
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
self._validate_properties(self.entity_tpl, self.type_definition)
|
@ -49,7 +49,6 @@ class ToscaDefTest(TestCase):
|
|||||||
sorted([p.name for p in compute_type.attributes_def]))
|
sorted([p.name for p in compute_type.attributes_def]))
|
||||||
|
|
||||||
def test_requirements(self):
|
def test_requirements(self):
|
||||||
self.assertEqual(compute_type.requirements, None)
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[{'host': 'tosca.nodes.Compute'}],
|
[{'host': 'tosca.nodes.Compute'}],
|
||||||
[r for r in component_type.requirements])
|
[r for r in component_type.requirements])
|
||||||
|
@ -14,12 +14,15 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import six
|
||||||
|
|
||||||
from translator.toscalib.common.exception import InvalidNodeTypeError
|
from translator.toscalib.common.exception import InvalidNodeTypeError
|
||||||
from translator.toscalib.common.exception import MissingRequiredFieldError
|
from translator.toscalib.common.exception import MissingRequiredFieldError
|
||||||
from translator.toscalib.common.exception import TypeMismatchError
|
from translator.toscalib.common.exception import TypeMismatchError
|
||||||
from translator.toscalib.common.exception import UnknownFieldError
|
from translator.toscalib.common.exception import UnknownFieldError
|
||||||
from translator.toscalib.nodetemplate import NodeTemplate
|
from translator.toscalib.nodetemplate import NodeTemplate
|
||||||
from translator.toscalib.parameters import Input, Output
|
from translator.toscalib.parameters import Input, Output
|
||||||
|
from translator.toscalib.relationship_template import RelationshipTemplate
|
||||||
from translator.toscalib.tests.base import TestCase
|
from translator.toscalib.tests.base import TestCase
|
||||||
from translator.toscalib.tosca_template import ToscaTemplate
|
from translator.toscalib.tosca_template import ToscaTemplate
|
||||||
import translator.toscalib.utils.yamlparser
|
import translator.toscalib.utils.yamlparser
|
||||||
@ -156,6 +159,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
nodetemplate.capabilities
|
nodetemplate.capabilities
|
||||||
nodetemplate.properties
|
nodetemplate.properties
|
||||||
nodetemplate.interfaces
|
nodetemplate.interfaces
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.assertTrue(isinstance(err, expectederror))
|
self.assertTrue(isinstance(err, expectederror))
|
||||||
self.assertEqual(expectedmessage, err.__str__())
|
self.assertEqual(expectedmessage, err.__str__())
|
||||||
@ -175,7 +179,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
os_distribution: Fedora
|
os_distribution: Fedora
|
||||||
os_version: 18
|
os_version: 18
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Node template server is missing '
|
expectedmessage = ('Template server is missing '
|
||||||
'required field: "type".')
|
'required field: "type".')
|
||||||
self._single_node_template_content_test(tpl_snippet,
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
MissingRequiredFieldError,
|
MissingRequiredFieldError,
|
||||||
@ -200,7 +204,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
db_root_password: { get_property: [ mysql_dbms, \
|
db_root_password: { get_property: [ mysql_dbms, \
|
||||||
dbms_root_password ] }
|
dbms_root_password ] }
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Second level of node template mysql_dbms '
|
expectedmessage = ('Second level of template mysql_dbms '
|
||||||
'contain(s) unknown field: "requirement", '
|
'contain(s) unknown field: "requirement", '
|
||||||
'refer to the TOSCA specs to verify valid values.')
|
'refer to the TOSCA specs to verify valid values.')
|
||||||
self._single_node_template_content_test(tpl_snippet,
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
@ -244,7 +248,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
create: webserver_install.sh
|
create: webserver_install.sh
|
||||||
start: webserver_start.sh
|
start: webserver_start.sh
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Requirements of node template webserver '
|
expectedmessage = ('Requirements of template webserver '
|
||||||
'must be of type: "list".')
|
'must be of type: "list".')
|
||||||
self._single_node_template_content_test(tpl_snippet,
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
TypeMismatchError,
|
TypeMismatchError,
|
||||||
@ -269,7 +273,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
tosca.interfaces.node.Lifecycle:
|
tosca.interfaces.node.Lifecycle:
|
||||||
configure: mysql_database_configure.sh
|
configure: mysql_database_configure.sh
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Requirements of node template mysql_database '
|
expectedmessage = ('Requirements of template mysql_database '
|
||||||
'contain(s) unknown field: "database_endpoint", '
|
'contain(s) unknown field: "database_endpoint", '
|
||||||
'refer to the TOSCA specs to verify valid values.')
|
'refer to the TOSCA specs to verify valid values.')
|
||||||
self._single_node_template_content_test(tpl_snippet,
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
@ -295,7 +299,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
tosca.interfaces.node.Lifecycle:
|
tosca.interfaces.node.Lifecycle:
|
||||||
configure: mysql_database_configure.sh
|
configure: mysql_database_configure.sh
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Capabilities of node template mysql_database '
|
expectedmessage = ('Capabilities of template mysql_database '
|
||||||
'contain(s) unknown field: "http_endpoint", '
|
'contain(s) unknown field: "http_endpoint", '
|
||||||
'refer to the TOSCA specs to verify valid values.')
|
'refer to the TOSCA specs to verify valid values.')
|
||||||
self._single_node_template_content_test(tpl_snippet,
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
@ -317,7 +321,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
os_distribution: Fedora
|
os_distribution: Fedora
|
||||||
os_version: 18
|
os_version: 18
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Properties of node template server is missing '
|
expectedmessage = ('Properties of template server is missing '
|
||||||
'required field: "[\'os_type\']".')
|
'required field: "[\'os_type\']".')
|
||||||
self._single_node_template_content_test(tpl_snippet,
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
MissingRequiredFieldError,
|
MissingRequiredFieldError,
|
||||||
@ -339,7 +343,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
os_version: 18
|
os_version: 18
|
||||||
os_image: F18_x86_64
|
os_image: F18_x86_64
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Properties of node template server contain(s) '
|
expectedmessage = ('Properties of template server contain(s) '
|
||||||
'unknown field: "os_image", refer to the TOSCA '
|
'unknown field: "os_image", refer to the TOSCA '
|
||||||
'specs to verify valid values.')
|
'specs to verify valid values.')
|
||||||
self._single_node_template_content_test(tpl_snippet,
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
@ -367,7 +371,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
wp_db_port: { get_ref_property: [ database_endpoint, \
|
wp_db_port: { get_ref_property: [ database_endpoint, \
|
||||||
database_endpoint, port ] }
|
database_endpoint, port ] }
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Interfaces of node template wordpress '
|
expectedmessage = ('Interfaces of template wordpress '
|
||||||
'contain(s) unknown field: '
|
'contain(s) unknown field: '
|
||||||
'"tosca.interfaces.node.Lifecycles", '
|
'"tosca.interfaces.node.Lifecycles", '
|
||||||
'refer to the TOSCA specs to verify valid values.')
|
'refer to the TOSCA specs to verify valid values.')
|
||||||
@ -395,7 +399,7 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
wp_db_port: { get_ref_property: [ database_endpoint, \
|
wp_db_port: { get_ref_property: [ database_endpoint, \
|
||||||
database_endpoint, port ] }
|
database_endpoint, port ] }
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Interfaces of node template wordpress contain(s) '
|
expectedmessage = ('Interfaces of template wordpress contain(s) '
|
||||||
'unknown field: "config", refer to the TOSCA specs'
|
'unknown field: "config", refer to the TOSCA specs'
|
||||||
' to verify valid values.')
|
' to verify valid values.')
|
||||||
self._single_node_template_content_test(tpl_snippet,
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
@ -422,9 +426,33 @@ class ToscaTemplateValidationTest(TestCase):
|
|||||||
wp_db_port: { get_ref_property: [ database_endpoint, \
|
wp_db_port: { get_ref_property: [ database_endpoint, \
|
||||||
database_endpoint, port ] }
|
database_endpoint, port ] }
|
||||||
'''
|
'''
|
||||||
expectedmessage = ('Interfaces of node template wordpress contain(s) '
|
expectedmessage = ('Interfaces of template wordpress contain(s) '
|
||||||
'unknown field: "inputs", refer to the TOSCA specs'
|
'unknown field: "inputs", refer to the TOSCA specs'
|
||||||
' to verify valid values.')
|
' to verify valid values.')
|
||||||
self._single_node_template_content_test(tpl_snippet,
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
UnknownFieldError,
|
UnknownFieldError,
|
||||||
expectedmessage)
|
expectedmessage)
|
||||||
|
|
||||||
|
def test_relationship_template_properties(self):
|
||||||
|
tpl_snippet = '''
|
||||||
|
relationship_templates:
|
||||||
|
storage_attachto:
|
||||||
|
type: AttachTo
|
||||||
|
properties:
|
||||||
|
device: test_device
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Properties of template '
|
||||||
|
'storage_attachto is missing required field: '
|
||||||
|
'"[\'location\']".')
|
||||||
|
self._single_rel_template_content_test(tpl_snippet,
|
||||||
|
MissingRequiredFieldError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
def _single_rel_template_content_test(self, tpl_snippet, expectederror,
|
||||||
|
expectedmessage):
|
||||||
|
rel_template = (translator.toscalib.utils.yamlparser.
|
||||||
|
simple_parse(tpl_snippet))['relationship_templates']
|
||||||
|
name = list(rel_template.keys())[0]
|
||||||
|
rel_template = RelationshipTemplate(rel_template[name], name)
|
||||||
|
err = self.assertRaises(expectederror, rel_template.validate)
|
||||||
|
self.assertEqual(expectedmessage, six.text_type(err))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user