add blockstorage attachment notation
Implement blockstorage attachment notations described in E1.4.2 and E1.4.3 of TOSCA wd02. Partially Implements: blueprint new-blockstorage-templates-tosca Change-Id: I7fe61b4458840bedeac88f58161cece12f2929bb
This commit is contained in:
parent
6aac081a24
commit
154b0e716a
@ -30,6 +30,8 @@ class ToscaBlockStorageAttachment(HotResource):
|
||||
for prop in self.nodetemplate.properties:
|
||||
if isinstance(prop.value, GetInput):
|
||||
tosca_props[prop.name] = {'get_param': prop.value.input_name}
|
||||
else:
|
||||
tosca_props[prop.name] = prop.value
|
||||
self.properties = tosca_props
|
||||
#instance_uuid and volume_id for Cinder volume attachment
|
||||
self.properties['instance_uuid'] = self.instace_uuid
|
||||
|
@ -31,7 +31,7 @@ class TOSCATranslator(object):
|
||||
self._resolve_input()
|
||||
self.hot_template.description = self.tosca.description
|
||||
self.hot_template.parameters = self._translate_inputs()
|
||||
self.node_translator = TranslateNodeTemplates(self.tosca.nodetemplates,
|
||||
self.node_translator = TranslateNodeTemplates(self.tosca,
|
||||
self.hot_template)
|
||||
self.hot_template.resources = self.node_translator.translate()
|
||||
self.hot_template.outputs = self._translate_outputs()
|
||||
|
@ -62,8 +62,9 @@ TOSCA_TO_HOT_PROPERTIES = {'properties': 'input'}
|
||||
class TranslateNodeTemplates():
|
||||
'''Translate TOSCA NodeTemplates to Heat Resources.'''
|
||||
|
||||
def __init__(self, nodetemplates, hot_template):
|
||||
self.nodetemplates = nodetemplates
|
||||
def __init__(self, tosca, hot_template):
|
||||
self.tosca = tosca
|
||||
self.nodetemplates = self.tosca.nodetemplates
|
||||
self.hot_template = hot_template
|
||||
# list of all HOT resources generated
|
||||
self.hot_resources = []
|
||||
@ -165,11 +166,21 @@ class TranslateNodeTemplates():
|
||||
if value.type == 'tosca.nodes.BlockStorage':
|
||||
attach = True
|
||||
if attach:
|
||||
relationship_tpl = None
|
||||
for req in node.requirements:
|
||||
for rkey, rval in req.items():
|
||||
if rkey == 'type':
|
||||
rval = rval + "_" + str(suffix)
|
||||
att = RelationshipTemplate(req, rval, None)
|
||||
relationship_tpl = req
|
||||
elif rkey == 'template':
|
||||
relationship_tpl = \
|
||||
self.tosca._tpl_relationship_templates()[rval]
|
||||
else:
|
||||
continue
|
||||
if relationship_tpl:
|
||||
rval_new = rval + "_" + str(suffix)
|
||||
att = RelationshipTemplate(
|
||||
relationship_tpl, rval_new,
|
||||
self.tosca._tpl_relationship_types())
|
||||
hot_node = ToscaBlockStorageAttachment(att, ntpl,
|
||||
node.name,
|
||||
volume_name
|
||||
|
@ -15,10 +15,12 @@ inputs:
|
||||
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.
|
||||
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.
|
||||
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:
|
||||
|
@ -0,0 +1,72 @@
|
||||
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_web_app_tier_1:
|
||||
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: MyAttachTo
|
||||
|
||||
my_web_app_tier_2:
|
||||
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: MyAttachTo
|
||||
properties:
|
||||
location: /some_other_data_location
|
||||
|
||||
my_storage:
|
||||
type: tosca.nodes.BlockStorage
|
||||
properties:
|
||||
size: { get_input: storage_size }
|
||||
snapshot_id: { get_input: storage_snapshot_id }
|
||||
|
||||
relationship_types:
|
||||
MyAttachTo:
|
||||
derived_from: tosca.relationships.AttachTo
|
||||
properties: # follows the syntax of property definitions
|
||||
location:
|
||||
type: string
|
||||
default: /default_location
|
@ -0,0 +1,82 @@
|
||||
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_web_app_tier_1:
|
||||
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
|
||||
template: storage_attachto_1
|
||||
|
||||
my_web_app_tier_2:
|
||||
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
|
||||
template: storage_attachto_2
|
||||
|
||||
my_storage:
|
||||
type: tosca.nodes.BlockStorage
|
||||
properties:
|
||||
size: { get_input: storage_size }
|
||||
snapshot_id: { get_input: storage_snapshot_id }
|
||||
|
||||
relationship_templates:
|
||||
storage_attachto_1:
|
||||
type: MyAttachTo
|
||||
properties:
|
||||
location: /my_data_location
|
||||
|
||||
storage_attachto_2:
|
||||
type: MyAttachTo
|
||||
properties:
|
||||
location: /some_other_data_location
|
||||
|
||||
relationship_types:
|
||||
MyAttachTo:
|
||||
derived_from: tosca.relationships.AttachTo
|
||||
properties: # follows the syntax of property definitions
|
||||
location:
|
||||
type: string
|
||||
default: /default_location
|
||||
|
@ -59,6 +59,71 @@ class ToscaBlockStorageTest(TestCase):
|
||||
self.assertEqual({'get_resource': 'my_storage'},
|
||||
outputs['volume_id']['value'])
|
||||
|
||||
def test_translate_storage_notation1(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_notation1.yaml")
|
||||
|
||||
tosca = ToscaTemplate(tosca_tpl)
|
||||
translate = TOSCATranslator(tosca, self.parsed_params)
|
||||
output = translate.translate()
|
||||
|
||||
expected_resource_1 = {'type': 'OS::Cinder::VolumeAttachment',
|
||||
'properties':
|
||||
{'instance_uuid': 'my_web_app_tier_1',
|
||||
'location': '/default_location',
|
||||
'volume_id': 'my_storage'}}
|
||||
expected_resource_2 = {'type': 'OS::Cinder::VolumeAttachment',
|
||||
'properties':
|
||||
{'instance_uuid': 'my_web_app_tier_2',
|
||||
'location': '/some_other_data_location',
|
||||
'volume_id': 'my_storage'}}
|
||||
|
||||
output_dict = translator.toscalib.utils.yamlparser.simple_parse(output)
|
||||
|
||||
resources = output_dict.get('resources')
|
||||
self.assertIn('myattachto_1', resources.keys())
|
||||
self.assertIn('myattachto_2', resources.keys())
|
||||
self.assertIn(expected_resource_1, resources.values())
|
||||
self.assertIn(expected_resource_2, resources.values())
|
||||
|
||||
def test_translate_storage_notation2(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_notation2.yaml")
|
||||
|
||||
tosca = ToscaTemplate(tosca_tpl)
|
||||
translate = TOSCATranslator(tosca, self.parsed_params)
|
||||
output = translate.translate()
|
||||
|
||||
expected_resource_1 = {'type': 'OS::Cinder::VolumeAttachment',
|
||||
'properties':
|
||||
{'instance_uuid': 'my_web_app_tier_1',
|
||||
'location': '/my_data_location',
|
||||
'volume_id': 'my_storage'}}
|
||||
expected_resource_2 = {'type': 'OS::Cinder::VolumeAttachment',
|
||||
'properties':
|
||||
{'instance_uuid': 'my_web_app_tier_2',
|
||||
'location': '/some_other_data_location',
|
||||
'volume_id': 'my_storage'}}
|
||||
|
||||
output_dict = translator.toscalib.utils.yamlparser.simple_parse(output)
|
||||
|
||||
resources = output_dict.get('resources')
|
||||
# Resource name suffix depends on nodetemplates order in dict, which is
|
||||
# not certain. So we have two possibilities of resources name.
|
||||
if resources.get('storage_attachto_1_1'):
|
||||
self.assertIn('storage_attachto_1_1', resources.keys())
|
||||
self.assertIn('storage_attachto_2_2', resources.keys())
|
||||
else:
|
||||
self.assertIn('storage_attachto_1_2', resources.keys())
|
||||
self.assertIn('storage_attachto_2_1', resources.keys())
|
||||
|
||||
self.assertIn(expected_resource_1, resources.values())
|
||||
self.assertIn(expected_resource_2, resources.values())
|
||||
|
||||
def test_translate_multi_storage(self):
|
||||
'''TOSCA template with multiple BlockStorage and Attachment.'''
|
||||
tosca_tpl = os.path.join(
|
||||
|
@ -10,6 +10,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from translator.toscalib.common.exception import InvalidSchemaError
|
||||
|
||||
|
||||
class PropertyDef(object):
|
||||
'''TOSCA built-in Property type.'''
|
||||
@ -19,6 +21,13 @@ class PropertyDef(object):
|
||||
self.value = value
|
||||
self.schema = schema
|
||||
|
||||
try:
|
||||
self.schema['type']
|
||||
except KeyError:
|
||||
msg = (_("Property definition of %(pname)s must have type.") %
|
||||
dict(pname=self.name))
|
||||
raise InvalidSchemaError(message=msg)
|
||||
|
||||
@property
|
||||
def required(self):
|
||||
if self.schema:
|
||||
|
@ -21,6 +21,13 @@ class RelationshipType(StatefulEntityType):
|
||||
self.capability_name = capability_name
|
||||
self.custom_def = custom_def
|
||||
|
||||
@property
|
||||
def parent_type(self):
|
||||
'''Return a relationship this reletionship is derived from.'''
|
||||
prel = self.derived_from(self.defs)
|
||||
if prel:
|
||||
return RelationshipType(prel)
|
||||
|
||||
@property
|
||||
def valid_targets(self):
|
||||
return self.entity_value(self.defs, 'valid_targets')
|
||||
|
@ -29,10 +29,12 @@ class StatefulEntityType(EntityType):
|
||||
'remove_target']
|
||||
|
||||
def __init__(self, entitytype, prefix, custom_def=None):
|
||||
entire_entitytype = entitytype
|
||||
if not entitytype.startswith(self.TOSCA):
|
||||
entitytype = prefix + entitytype
|
||||
if entitytype in list(self.TOSCA_DEF.keys()):
|
||||
self.defs = self.TOSCA_DEF[entitytype]
|
||||
entire_entitytype = prefix + entitytype
|
||||
if entire_entitytype in list(self.TOSCA_DEF.keys()):
|
||||
self.defs = self.TOSCA_DEF[entire_entitytype]
|
||||
entitytype = entire_entitytype
|
||||
elif custom_def and entitytype in list(custom_def.keys()):
|
||||
self.defs = custom_def[entitytype]
|
||||
else:
|
||||
|
@ -38,7 +38,7 @@ class EntityTemplate(object):
|
||||
custom_def)
|
||||
if entity_name == 'relationship_type':
|
||||
self.type_definition = RelationshipType(self.entity_tpl['type'],
|
||||
custom_def)
|
||||
None, custom_def)
|
||||
self._properties = None
|
||||
self._interfaces = None
|
||||
self._requirements = None
|
||||
|
@ -133,7 +133,7 @@ class NodeTemplate(EntityTemplate):
|
||||
|
||||
def _validate_requirements(self):
|
||||
type_requires = self.type_definition.get_all_requirements()
|
||||
allowed_reqs = []
|
||||
allowed_reqs = ["template"]
|
||||
if type_requires:
|
||||
for treq in type_requires:
|
||||
for key in treq:
|
||||
|
@ -29,7 +29,7 @@ class RelationshipTemplate(EntityTemplate):
|
||||
super(RelationshipTemplate, self).__init__(name,
|
||||
relationship_template,
|
||||
'relationship_type',
|
||||
custom_def=None)
|
||||
custom_def)
|
||||
self.name = name.lower()
|
||||
|
||||
def validate(self):
|
||||
|
@ -30,8 +30,7 @@ class PropertyTest(TestCase):
|
||||
test_property_schema)
|
||||
error = self.assertRaises(InvalidTypeError,
|
||||
propertyInstance.validate)
|
||||
self.assertEqual('Type "tosca.datatypes.network.Fish" '
|
||||
'is not a valid type.', str(error))
|
||||
self.assertEqual('Type "Fish" is not a valid type.', str(error))
|
||||
|
||||
def test_list(self):
|
||||
test_property_schema = {'type': 'list'}
|
||||
|
@ -220,7 +220,7 @@ class ToscaTemplateValidationTest(TestCase):
|
||||
interfaces:
|
||||
tosca.interfaces.node.Lifecycle:
|
||||
create: webserver_install.sh
|
||||
start: webserver_start.sh
|
||||
start: d.sh
|
||||
'''
|
||||
expectedmessage = ('Requirements of template webserver '
|
||||
'must be of type: "list".')
|
||||
|
@ -98,6 +98,7 @@ class ToscaTemplate(object):
|
||||
|
||||
def _relationship_templates(self):
|
||||
custom_types = {}
|
||||
# Handle custom relationships defined in outer template file
|
||||
imports = self._tpl_imports()
|
||||
if imports:
|
||||
for definition in imports:
|
||||
@ -109,8 +110,15 @@ class ToscaTemplate(object):
|
||||
custom_type = YAML_LOADER(def_file)
|
||||
rel_types = custom_type.get('relationship_types') or {}
|
||||
for name in rel_types:
|
||||
defintion = rel_types[name]
|
||||
custom_types[name] = defintion
|
||||
definition = rel_types[name]
|
||||
custom_types[name] = definition
|
||||
|
||||
# Handle custom relationships defined in current template file
|
||||
rel_types = self._tpl_relationship_types()
|
||||
for name in rel_types:
|
||||
definition = rel_types[name]
|
||||
custom_types[name] = definition
|
||||
|
||||
rel_templates = []
|
||||
tpls = self._tpl_relationship_templates()
|
||||
for name in tpls:
|
||||
@ -145,6 +153,9 @@ class ToscaTemplate(object):
|
||||
def _tpl_relationship_templates(self):
|
||||
return self.tpl.get(RELATIONSHIP_TEMPLATES) or {}
|
||||
|
||||
def _tpl_relationship_types(self):
|
||||
return self.tpl.get(RELATIONSHIP_TYPES) or {}
|
||||
|
||||
def _tpl_outputs(self):
|
||||
return self.tpl.get(OUTPUTS) or {}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user