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:
huruifeng 2015-04-21 17:02:54 +08:00
parent 305d7584ed
commit b6832923b2
3 changed files with 357 additions and 0 deletions

View 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')

View 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')

View 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