Inital Implementation of topology template
Add a class which can read the 'topology_template' section in a service template. And related tests. Substitution mapping is not implemented yet. partially implements: bp tosca-topology-template Change-Id: I5f63c706c97a1078d3c6a99bf349f0043ca20cf5
This commit is contained in:
parent
305d7584ed
commit
b6832923b2
27
translator/toscalib/groups.py
Normal file
27
translator/toscalib/groups.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
class NodeGroup(object):
|
||||||
|
|
||||||
|
def __init__(self, name, group_templates, member_nodes):
|
||||||
|
self.name = name
|
||||||
|
self.tpl = group_templates
|
||||||
|
self.members = member_nodes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def member_names(self):
|
||||||
|
return self.tpl.get('members')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def policies(self):
|
||||||
|
return self.tpl.get('policies')
|
153
translator/toscalib/tests/test_topology_template.py
Normal file
153
translator/toscalib/tests/test_topology_template.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# 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.toscalib.tests.base import TestCase
|
||||||
|
from translator.toscalib.topology_template import TopologyTemplate
|
||||||
|
import translator.toscalib.utils.yamlparser
|
||||||
|
|
||||||
|
YAML_LOADER = translator.toscalib.utils.yamlparser.load_yaml
|
||||||
|
|
||||||
|
|
||||||
|
class TopologyTemplateTest(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
TestCase.setUp(self)
|
||||||
|
'''TOSCA template.'''
|
||||||
|
self.tosca_tpl_path = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"data/topology_template/subsystem.yaml")
|
||||||
|
self.tpl = YAML_LOADER(self.tosca_tpl_path)
|
||||||
|
self.topo_tpl = self.tpl.get('topology_template')
|
||||||
|
self.imports = self.tpl.get('imports')
|
||||||
|
self.topo = TopologyTemplate(self.topo_tpl,
|
||||||
|
self._get_all_custom_def())
|
||||||
|
|
||||||
|
def _get_custom_def(self, type_definition):
|
||||||
|
custom_defs = {}
|
||||||
|
for definition in self.imports:
|
||||||
|
if os.path.isabs(definition):
|
||||||
|
def_file = definition
|
||||||
|
else:
|
||||||
|
tpl_dir = os.path.dirname(os.path.abspath(self.tosca_tpl_path))
|
||||||
|
def_file = os.path.join(tpl_dir, definition)
|
||||||
|
custom_type = YAML_LOADER(def_file)
|
||||||
|
custom_defs.update(custom_type.get(type_definition))
|
||||||
|
return custom_defs
|
||||||
|
|
||||||
|
def _get_all_custom_def(self):
|
||||||
|
custom_defs = {}
|
||||||
|
custom_defs.update(self._get_custom_def('node_types'))
|
||||||
|
custom_defs.update(self._get_custom_def('capability_types'))
|
||||||
|
return custom_defs
|
||||||
|
|
||||||
|
def test_description(self):
|
||||||
|
expected_desc = 'Template of a database including its hosting stack.'
|
||||||
|
self.assertEqual(expected_desc, self.topo.description)
|
||||||
|
|
||||||
|
def test_inputs(self):
|
||||||
|
self.assertEqual(
|
||||||
|
['mq_server_ip', 'my_cpus', 'receiver_port'],
|
||||||
|
sorted([input.name for input in self.topo.inputs]))
|
||||||
|
|
||||||
|
input_name = "receiver_port"
|
||||||
|
expected_description = "Port to be used for receiving messages."
|
||||||
|
for input in self.topo.inputs:
|
||||||
|
if input.name == input_name:
|
||||||
|
self.assertEqual(expected_description, input.description)
|
||||||
|
|
||||||
|
def test_node_tpls(self):
|
||||||
|
'''Test nodetemplate names.'''
|
||||||
|
self.assertEqual(
|
||||||
|
['app', 'server', 'websrv'],
|
||||||
|
sorted([tpl.name for tpl in self.topo.nodetemplates]))
|
||||||
|
|
||||||
|
tpl_name = "app"
|
||||||
|
expected_type = "example.SomeApp"
|
||||||
|
expected_properties = ['admin_user', 'pool_size']
|
||||||
|
expected_capabilities = ['message_receiver']
|
||||||
|
expected_requirements = [{'host': 'websrv'}]
|
||||||
|
expected_relationshp = ['tosca.relationships.HostedOn']
|
||||||
|
expected_host = ['websrv']
|
||||||
|
|
||||||
|
for tpl in self.topo.nodetemplates:
|
||||||
|
if tpl_name == tpl.name:
|
||||||
|
'''Test node type.'''
|
||||||
|
self.assertEqual(tpl.type, expected_type)
|
||||||
|
|
||||||
|
'''Test properties.'''
|
||||||
|
self.assertEqual(
|
||||||
|
expected_properties,
|
||||||
|
sorted(tpl.get_properties().keys()))
|
||||||
|
|
||||||
|
'''Test capabilities.'''
|
||||||
|
self.assertEqual(
|
||||||
|
expected_capabilities,
|
||||||
|
sorted(tpl.get_capabilities().keys()))
|
||||||
|
|
||||||
|
'''Test requirements.'''
|
||||||
|
self.assertEqual(
|
||||||
|
expected_requirements, tpl.requirements)
|
||||||
|
|
||||||
|
'''Test relationship.'''
|
||||||
|
self.assertEqual(
|
||||||
|
expected_relationshp,
|
||||||
|
[x.type for x in tpl.relationships.keys()])
|
||||||
|
self.assertEqual(
|
||||||
|
expected_host,
|
||||||
|
[y.name for y in tpl.relationships.values()])
|
||||||
|
|
||||||
|
'''Test interfaces.'''
|
||||||
|
# TODO(hurf) add interface test when new template is available
|
||||||
|
|
||||||
|
if tpl.name == 'server':
|
||||||
|
'''Test property value'''
|
||||||
|
props = tpl.get_properties()
|
||||||
|
if props and 'mem_size' in props.keys():
|
||||||
|
self.assertEqual(props['mem_size'].value, '4096 MB')
|
||||||
|
'''Test capability'''
|
||||||
|
caps = tpl.get_capabilities()
|
||||||
|
self.assertIn('os', caps.keys())
|
||||||
|
os_props_objs = None
|
||||||
|
os_props = None
|
||||||
|
os_type_prop = None
|
||||||
|
if caps and 'os' in caps.keys():
|
||||||
|
capability = caps['os']
|
||||||
|
os_props_objs = capability.get_properties_objects()
|
||||||
|
os_props = capability.get_properties()
|
||||||
|
os_type_prop = capability.get_property_value('type')
|
||||||
|
break
|
||||||
|
self.assertEqual(
|
||||||
|
['Linux'],
|
||||||
|
[p.value for p in os_props_objs if p.name == 'type'])
|
||||||
|
self.assertEqual(
|
||||||
|
'Linux',
|
||||||
|
os_props['type'].value if 'type' in os_props else '')
|
||||||
|
self.assertEqual('Linux', os_props['type'].value)
|
||||||
|
self.assertEqual('Linux', os_type_prop)
|
||||||
|
|
||||||
|
def test_outputs(self):
|
||||||
|
self.assertEqual(
|
||||||
|
['receiver_ip'],
|
||||||
|
sorted([output.name for output in self.topo.outputs]))
|
||||||
|
|
||||||
|
def test_groups(self):
|
||||||
|
group = self.topo.groups[0]
|
||||||
|
self.assertEqual('webserver_group', group.name)
|
||||||
|
self.assertEqual(['websrv', 'server'], group.member_names)
|
||||||
|
for node in group.members:
|
||||||
|
if node.name == 'server':
|
||||||
|
'''Test property value'''
|
||||||
|
props = node.get_properties()
|
||||||
|
if props and 'mem_size' in props.keys():
|
||||||
|
self.assertEqual(props['mem_size'].value, '4096 MB')
|
177
translator/toscalib/topology_template.py
Normal file
177
translator/toscalib/topology_template.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
# 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.common import exception
|
||||||
|
from translator.toscalib import functions
|
||||||
|
from translator.toscalib.groups import NodeGroup
|
||||||
|
from translator.toscalib.nodetemplate import NodeTemplate
|
||||||
|
from translator.toscalib.parameters import Input
|
||||||
|
from translator.toscalib.parameters import Output
|
||||||
|
from translator.toscalib.relationship_template import RelationshipTemplate
|
||||||
|
from translator.toscalib.tpl_relationship_graph import ToscaGraph
|
||||||
|
|
||||||
|
|
||||||
|
# Topology template key names
|
||||||
|
SECTIONS = (DESCRIPTION, INPUTS, NODE_TEMPLATES,
|
||||||
|
RELATIONSHIP_TEMPLATES, OUTPUTS, GROUPS,
|
||||||
|
SUBSTITUION_MAPPINGS) = \
|
||||||
|
('description', 'inputs', 'node_templates',
|
||||||
|
'relationship_templates', 'outputs', 'groups',
|
||||||
|
'substitution_mappings')
|
||||||
|
|
||||||
|
log = logging.getLogger("tosca.model")
|
||||||
|
|
||||||
|
|
||||||
|
class TopologyTemplate(object):
|
||||||
|
|
||||||
|
'''Load the template data.'''
|
||||||
|
def __init__(self, template, custom_defs):
|
||||||
|
self.tpl = template
|
||||||
|
self.custom_defs = custom_defs
|
||||||
|
self._validate_field()
|
||||||
|
self.description = self._tpl_description()
|
||||||
|
self.inputs = self._inputs()
|
||||||
|
self.relationship_templates = self._relationship_templates()
|
||||||
|
self.nodetemplates = self._nodetemplates()
|
||||||
|
self.outputs = self._outputs()
|
||||||
|
self.graph = ToscaGraph(self.nodetemplates)
|
||||||
|
self.groups = self._groups()
|
||||||
|
self._process_intrinsic_functions()
|
||||||
|
|
||||||
|
def _inputs(self):
|
||||||
|
inputs = []
|
||||||
|
for name, attrs in self._tpl_inputs().items():
|
||||||
|
input = Input(name, attrs)
|
||||||
|
input.validate()
|
||||||
|
inputs.append(input)
|
||||||
|
return inputs
|
||||||
|
|
||||||
|
def _nodetemplates(self):
|
||||||
|
nodetemplates = []
|
||||||
|
tpls = self._tpl_nodetemplates()
|
||||||
|
for name in tpls:
|
||||||
|
tpl = NodeTemplate(name, tpls, self.custom_defs,
|
||||||
|
self.relationship_templates)
|
||||||
|
tpl.validate(self)
|
||||||
|
nodetemplates.append(tpl)
|
||||||
|
return nodetemplates
|
||||||
|
|
||||||
|
def _relationship_templates(self):
|
||||||
|
rel_templates = []
|
||||||
|
tpls = self._tpl_relationship_templates()
|
||||||
|
for name in tpls:
|
||||||
|
tpl = RelationshipTemplate(tpls[name], name, self.custom_defs)
|
||||||
|
rel_templates.append(tpl)
|
||||||
|
return rel_templates
|
||||||
|
|
||||||
|
def _outputs(self):
|
||||||
|
outputs = []
|
||||||
|
for name, attrs in self._tpl_outputs().items():
|
||||||
|
output = Output(name, attrs)
|
||||||
|
output.validate()
|
||||||
|
outputs.append(output)
|
||||||
|
return outputs
|
||||||
|
|
||||||
|
def _substitution_mappings(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _groups(self):
|
||||||
|
groups = []
|
||||||
|
for group_name, group_tpl in self._tpl_groups().items():
|
||||||
|
member_names = group_tpl.get('members')
|
||||||
|
if member_names and len(member_names) > 1:
|
||||||
|
group = NodeGroup(group_name, group_tpl,
|
||||||
|
self._get_group_memerbs(member_names))
|
||||||
|
groups.append(group)
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
return groups
|
||||||
|
|
||||||
|
def _get_group_memerbs(self, member_names):
|
||||||
|
member_nodes = []
|
||||||
|
for member in member_names:
|
||||||
|
for node in self.nodetemplates:
|
||||||
|
if node.name == member:
|
||||||
|
member_nodes.append(node)
|
||||||
|
return member_nodes
|
||||||
|
|
||||||
|
# topology template can act like node template
|
||||||
|
# it is exposed by substitution_mappings.
|
||||||
|
def nodetype(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def capabilities(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def requirements(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _tpl_description(self):
|
||||||
|
return self.tpl[DESCRIPTION].rstrip()
|
||||||
|
|
||||||
|
def _tpl_inputs(self):
|
||||||
|
return self.tpl.get(INPUTS) or {}
|
||||||
|
|
||||||
|
def _tpl_nodetemplates(self):
|
||||||
|
return self.tpl[NODE_TEMPLATES]
|
||||||
|
|
||||||
|
def _tpl_relationship_templates(self):
|
||||||
|
return self.tpl.get(RELATIONSHIP_TEMPLATES) or {}
|
||||||
|
|
||||||
|
def _tpl_outputs(self):
|
||||||
|
return self.tpl.get(OUTPUTS) or {}
|
||||||
|
|
||||||
|
def _tpl_substitution_mappings(self):
|
||||||
|
return self.tpl.get(SUBSTITUION_MAPPINGS) or {}
|
||||||
|
|
||||||
|
def _tpl_groups(self):
|
||||||
|
return self.tpl.get(GROUPS) or {}
|
||||||
|
|
||||||
|
def _validate_field(self):
|
||||||
|
for name in self.tpl:
|
||||||
|
if name not in SECTIONS:
|
||||||
|
raise exception.UnknownFieldError(what='Template', field=name)
|
||||||
|
|
||||||
|
def _process_intrinsic_functions(self):
|
||||||
|
"""Process intrinsic functions
|
||||||
|
|
||||||
|
Current implementation processes functions within node template
|
||||||
|
properties, requirements, interfaces inputs and template outputs.
|
||||||
|
"""
|
||||||
|
for node_template in self.nodetemplates:
|
||||||
|
for prop in node_template.get_properties_objects():
|
||||||
|
prop.value = functions.get_function(self,
|
||||||
|
node_template,
|
||||||
|
prop.value)
|
||||||
|
for interface in node_template.interfaces:
|
||||||
|
if interface.inputs:
|
||||||
|
for name, value in interface.inputs.items():
|
||||||
|
interface.inputs[name] = functions.get_function(
|
||||||
|
self,
|
||||||
|
node_template,
|
||||||
|
value)
|
||||||
|
if node_template.requirements:
|
||||||
|
for req in node_template.requirements:
|
||||||
|
if 'properties' in req:
|
||||||
|
for key, value in req['properties'].items():
|
||||||
|
req['properties'][key] = functions.get_function(
|
||||||
|
self,
|
||||||
|
req,
|
||||||
|
value)
|
||||||
|
|
||||||
|
for output in self.outputs:
|
||||||
|
func = functions.get_function(self, self.outputs, output.value)
|
||||||
|
if isinstance(func, functions.GetAttribute):
|
||||||
|
output.attrs[output.VALUE] = func
|
Loading…
x
Reference in New Issue
Block a user