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:
parent
67a8520f8b
commit
54d040149f
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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('\'', '')
|
||||
|
34
translator/tests/data/hot_output/hot_single_server.yaml
Normal file
34
translator/tests/data/hot_output/hot_single_server.yaml
Normal 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
|
||||
|
32
translator/tests/data/tosca_single_server.yaml
Normal file
32
translator/tests/data/tosca_single_server.yaml
Normal 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] }
|
58
translator/tests/test_translated_output_order.py
Normal file
58
translator/tests/test_translated_output_order.py
Normal 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)
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user