The output template is close to HOT template

heat-translator output is now generated exactly the
same way as we insert (used OrderedDict instead of dict)
eg: output section at the end
    type comes before properties of resources
Added a testcase to compare generated translator output
and expected hot output template

Change-Id: Iba2e4bc73c471f3e3064a9481fc397ad08470c7a
Closes-Bug: #1355542
This commit is contained in:
srinivas_tadepalli 2015-04-01 12:02:25 +05:30
parent 67a8520f8b
commit 54d040149f
7 changed files with 171 additions and 6 deletions

View File

@ -11,6 +11,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from collections import OrderedDict
KEYS = (TYPE, DESCRIPTION, DEFAULT, CONSTRAINTS, HIDDEN, LABEL) = \
('type', 'description', 'default', 'constraints', 'hidden', 'label')
@ -29,7 +31,8 @@ class HotParameter(object):
self.constraints = constraints
def get_dict_output(self):
param_sections = {TYPE: self.type}
param_sections = OrderedDict()
param_sections[TYPE] = self.type
if self.label:
param_sections[LABEL] = self.label
if self.description:

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from collections import OrderedDict
import six
from translator.toscalib.functions import GetInput
@ -178,7 +179,8 @@ class HotResource(object):
return dependent.top_of_chain()
def get_dict_output(self):
resource_sections = {TYPE: self.type}
resource_sections = OrderedDict()
resource_sections[TYPE] = self.type
if self.properties:
resource_sections[PROPERTIES] = self.properties
if self.metadata:

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from collections import OrderedDict
import textwrap
import yaml
@ -31,8 +32,16 @@ class HotTemplate(object):
self.parameters = []
self.description = ""
def represent_ordereddict(self, dumper, data):
nodes = []
for key, value in data.items():
node_key = dumper.represent_data(key)
node_value = dumper.represent_data(value)
nodes.append((node_key, node_value))
return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', nodes)
def output_to_yaml(self):
dict_output = {}
dict_output = OrderedDict()
# Version
version_string = self.VERSION + ": " + self.LATEST + "\n\n"
@ -44,24 +53,25 @@ class HotTemplate(object):
desc_str = self.DESCRIPTION + ": >\n " + wrapped_txt + "\n\n"
# Parameters
all_params = {}
all_params = OrderedDict()
for parameter in self.parameters:
all_params.update(parameter.get_dict_output())
dict_output.update({self.PARAMETERS: all_params})
# Resources
all_resources = {}
all_resources = OrderedDict()
for resource in self.resources:
if not resource.hide_resource:
all_resources.update(resource.get_dict_output())
dict_output.update({self.RESOURCES: all_resources})
# Outputs
all_outputs = {}
all_outputs = OrderedDict()
for output in self.outputs:
all_outputs.update(output.get_dict_output())
dict_output.update({self.OUTPUTS: all_outputs})
yaml.add_representer(OrderedDict, self.represent_ordereddict)
yaml_string = yaml.dump(dict_output, default_flow_style=False)
# get rid of the '' from yaml.dump around numbers
yaml_string = yaml_string.replace('\'', '')

View File

@ -0,0 +1,34 @@
heat_template_version: 2013-05-23
description: >
TOSCA simple profile for Compute.
parameters:
cpus:
type: number
description: Number of CPUs for the server.
default: 2
constraints:
- allowed_values:
- 1
- 2
- 4
- 8
resources:
server:
type: OS::Nova::Server
properties:
flavor: null
image: fedora-amd64-heat-config
key_name: userkey
user_data_format: SOFTWARE_CONFIG
outputs:
server_address:
description: IP address of server instance.
value:
get_attr:
- server
- networks
- private
- 0

View File

@ -0,0 +1,32 @@
tosca_definitions_version: tosca_simple_yaml_1_0_0
description: >
TOSCA simple profile for Compute.
inputs:
cpus:
type: integer
description: Number of CPUs for the server.
constraints:
- valid_values: [ 1, 2, 4, 8 ]
node_templates:
server:
type: tosca.nodes.Compute
properties:
# compute properties (flavor)
disk_size: 10
num_cpus: { get_input: cpus }
mem_size: 4096
capabilities:
os:
properties:
architecture: x86_64
type: Linux
distribution: Fedora
version: 18
outputs:
server_address:
description: IP address of server instance.
value: { get_attribute: [server, private_address] }

View File

@ -0,0 +1,58 @@
# 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
import os
from translator.hot.tosca_translator import TOSCATranslator
from translator.toscalib.tests.base import TestCase
from translator.toscalib.tosca_template import ToscaTemplate
import translator.toscalib.utils.yamlparser
YAML_PARSER = translator.toscalib.utils.yamlparser.simple_ordered_parse
log = logging.getLogger('tosca')
class ToscaTemplateOutputOrderTest(TestCase):
def test_translate_output_order(self):
tosca_yaml_file = "data/tosca_single_server.yaml"
tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
tosca_yaml_file)
parsed_params = {'cpus': 2}
tosca = ToscaTemplate(tosca_tpl)
translate = TOSCATranslator(tosca, parsed_params)
hot_translated_output = translate.translate()
hot_translated_dict = YAML_PARSER(hot_translated_output)
#load expected hot yaml file
hot_yaml_file = "data/hot_output/hot_single_server.yaml"
hot_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
hot_yaml_file)
hot_expected_dict = {}
with open(hot_tpl) as f:
hot_expected_dict = YAML_PARSER(f.read())
#compare generated and expected hot templates
both_equal = True
for generated_item, expected_item in \
zip(hot_translated_dict.items(),
hot_expected_dict.items()):
if generated_item != expected_item:
log.warning("Generated_template : %s \n is not equal to "
"\nExpected_template: %s", generated_item,
expected_item)
both_equal = False
break
self.assertEqual(both_equal, True)

View File

@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from collections import OrderedDict
import yaml
if hasattr(yaml, 'CSafeLoader'):
@ -32,3 +33,28 @@ def simple_parse(tmpl_str):
if tpl is None:
tpl = {}
return tpl
def ordered_load(stream, Loader=yaml.Loader, object_pairs_hook=OrderedDict):
class OrderedLoader(Loader):
pass
def construct_mapping(loader, node):
loader.flatten_mapping(node)
return object_pairs_hook(loader.construct_pairs(node))
OrderedLoader.add_constructor(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
construct_mapping)
return yaml.load(stream, OrderedLoader)
def simple_ordered_parse(tmpl_str):
try:
tpl = ordered_load(tmpl_str)
except yaml.YAMLError as yea:
raise ValueError(yea)
else:
if tpl is None:
tpl = {}
return tpl