--- /dev/null
+If you would like to contribute to the development of OpenStack,
+you must follow the steps in this page:
+
+ http://docs.openstack.org/infra/manual/developers.html
+
+Once those steps have been completed, changes to OpenStack
+should be submitted for review via the Gerrit tool, following
+the workflow documented at:
+
+ http://docs.openstack.org/infra/manual/developers.html#development-workflow
+
+Pull requests submitted through GitHub will be ignored.
+
+Bugs should be filed on Launchpad, not GitHub:
+
+ https://bugs.launchpad.net/tosca-parser
\ No newline at end of file
--- /dev/null
+tosca-parser Style Commandments
+===============================================
+
+Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
\ No newline at end of file
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-pbr>=1.8 # Apache-2.0
-Babel>=2.3.4 # BSD
-cliff>=2.3.0 # Apache-2.0
+pbr!=2.1.0,>=2.0.0 # Apache-2.0
+Babel!=2.4.0,>=2.3.4 # BSD
+cliff>=2.6.0 # Apache-2.0
PyYAML>=3.10.0 # MIT
python-dateutil>=2.4.2 # BSD
six>=1.9.0 # MIT
-requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
+requests>=2.14.2 # Apache-2.0
url = https://launchpad.net/tosca-parser
summary = Parser for TOSCA Simple Profile in YAML.
description-file =
- README.md
+ README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://docs.openstack.org/developer/tosca-parser/
console_scripts =
tosca-parser = toscaparser.shell:main
+[build_sphinx]
+source-dir = doc/source
+build-dir = doc/build
+all_files = 1
+
+[upload_sphinx]
+upload-dir = doc/build/html
+
[compile_catalog]
directory = toscaparser/locale
domain = toscaparser
pass
setuptools.setup(
- setup_requires=['pbr>=1.8'],
+ setup_requires=['pbr>=2.0.0'],
pbr=True)
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-hacking<0.11,>=0.10.0
-coverage>=4.0 # Apache-2.0
+hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
+coverage!=4.4,>=4.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
oslotest>=1.10.0 # Apache-2.0
oslosphinx>=4.7.0 # Apache-2.0
python-subunit>=0.0.18 # Apache-2.0/BSD
-sphinx>=1.5.1 # BSD
+sphinx>=1.6.2 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
relationship: tosca.relationships.network.LinksTo
node: tosca.nodes.network.Network
+ tosca.nodes.network.FloatingIP:
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA FloatingIP node represents a floating IP that can associate to a Port.
+ properties:
+ floating_network:
+ type: string
+ required: true
+ floating_ip_address:
+ type: string
+ required: false
+ port_id:
+ type: string
+ required: false
+ requirements:
+ - link:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ node: tosca.nodes.network.Port
+
tosca.nodes.ObjectStorage:
derived_from: tosca.nodes.Root
description: >
description: The TOSCA Policy Type definition that is used to govern
scaling of TOSCA nodes or groups of nodes.
+ tosca.policies.Monitoring:
+ derived_from: tosca.policies.Root
+ description: The TOSCA Policy Type definition that is used to govern
+ monitoring of TOSCA nodes or groups of nodes.
+
tosca.policies.Update:
derived_from: tosca.policies.Root
description: The TOSCA Policy Type definition that is used to govern
captype = value['capability']
value = (self.
_get_node_type_by_cap(captype))
- # _get_node_type_by_cap(key, captype))
- # relation = self._get_relation(key, value)
keyword = key
node_type = value
rtype = RelationshipType(relation, keyword, self.custom_def)
for entry_schema, entry_schema_type in meta_data.items():
if isinstance(entry_schema_type, dict) and not \
- entry_schema_type.get('type') == 'string':
+ entry_schema_type.get('type') == 'string':
ExceptionCollector.appendException(
InvalidTypeError(what='"%s" defined in policy for '
'metadata "%s"'
# then check the req or caps
attr = self._find_req_or_cap_attribute(self.args[1],
self.args[2])
+ if not attr:
+ return
value_type = attr.schema['type']
if len(self.args) > index:
target_node = self._find_node_template(target_name)
target_type = target_node.type_definition
for capability in target_type.get_capabilities_objects():
- if capability.type in \
- hosted_on_rel['valid_target_types']:
+ if capability.inherits_from(
+ hosted_on_rel['valid_target_types']):
if self._attribute_exists_in_type(target_type):
return target_node
return self._find_host_containing_attribute(
def _find_req_or_cap_property(self, req_or_cap, property_name):
node_tpl = self._find_node_template(self.args[0])
+ if node_tpl is None:
+ return None
# Find property in node template's requirements
for r in node_tpl.requirements:
for req, node_name in r.items():
def _get_capability_property(self,
node_template,
capability_name,
- property_name):
+ property_name,
+ throw_errors=True):
"""Gets a node template capability property."""
caps = node_template.get_capabilities()
if caps and capability_name in caps.keys():
props = cap.get_properties()
if props and property_name in props.keys():
property = props[property_name].value
- if not property:
+ if property is None and throw_errors:
ExceptionCollector.appendException(
KeyError(_('Property "%(prop)s" was not found in '
'capability "%(cap)s" of node template '
'ntpl1': node_template.name,
'ntpl2': self.context.name}))
return property
- msg = _('Requirement/Capability "{0}" referenced from node template '
- '"{1}" was not found in node template "{2}".').format(
- capability_name,
- self.context.name,
- node_template.name)
- ExceptionCollector.appendException(KeyError(msg))
+ if throw_errors:
+ msg = _('Requirement/Capability "{0}" referenced from '
+ 'node template "{1}" was not found in node template'
+ ' "{2}".').format(capability_name,
+ self.context.name,
+ node_template.name)
+ ExceptionCollector.appendException(KeyError(msg))
+ else:
+ return None
def _find_property(self, property_name):
node_tpl = self._find_node_template(self.args[0])
return self.context
# enable the HOST value in the function
if node_template_name == HOST:
- return self._find_host_containing_property()
+ node = self._find_host_containing_property()
+ if node is None:
+ ExceptionCollector.appendException(
+ KeyError(_(
+ "Property '{0}' not found in capability/requirement"
+ " '{1}' referenced from node template {2}").
+ format(self.args[2],
+ self.args[1],
+ self.context.name)))
+ return None
+ else:
+ return node
if node_template_name == TARGET:
if not isinstance(self.context.type_definition, RelationshipType):
ExceptionCollector.appendException(
ExceptionCollector.appendException(
KeyError(_(
'Node template "{0}" was not found.'
- ).format(node_template_name)))
+ ' referenced from node template {1}'
+ ).format(node_template_name,
+ self.context.name)))
def _get_index_value(self, value, index):
if isinstance(value, list):
target_node = self._find_node_template(target_name)
target_type = target_node.type_definition
for capability in target_type.get_capabilities_objects():
- if capability.type in hosted_on_rel['valid_target_types']:
+ if capability.inherits_from(
+ hosted_on_rel['valid_target_types']):
if self._property_exists_in_type(target_type):
return target_node
+ # If requirement was not found, look in node
+ # template's capabilities
+ if (len(self.args) > 2 and
+ self._get_capability_property(target_node,
+ self.args[1],
+ self.args[2],
+ False)
+ is not None):
+ return target_node
return self._find_host_containing_property(
target_name)
return None
--- /dev/null
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ Define a capability class that inherits from tosca.capabilities.Container
+
+capability_types:
+
+ tosca.capabilities.ContainerChild:
+ derived_from: tosca.capabilities.Container
+
+node_types:
+
+ tosca.nodes.SomeNode:
+ derived_from: tosca.nodes.Root
+ properties:
+ some_prop:
+ type: string
+ requirements:
+ - host_child:
+ capability: tosca.capabilities.ContainerChild
+ node: tosca.nodes.SomeNode2
+ relationship: tosca.relationships.HostedOn
+
+ tosca.nodes.SomeNode2:
+ derived_from: tosca.nodes.Root
+ capabilities:
+ host_child:
+ type: tosca.capabilities.ContainerChild
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
--- /dev/null
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+capability_types:
+
+ tosca.capabilities.SomeCap:
+ derived_from: tosca.capabilities.Container
+
+node_types:
+
+ tosca.nodes.NodeWithReq:
+ derived_from: tosca.nodes.SoftwareComponent
+ requirements:
+ - host:
+ capability: tosca.capabilities.SomeCap
+ relationship: tosca.relationships.HostedOn
+ occurrences: [1, 1]
+
+ tosca.nodes.NodeWithCap:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ host:
+ type: tosca.capabilities.SomeCap
capability_types:
tosca.capabilities.SomeCap:
- derived_from: tosca.capabilities.Root
+ derived_from: tosca.capabilities.Container
properties:
type:
type: string
tosca.nodes.SomeNode:
derived_from: tosca.nodes.Root
+ properties:
+ some_prop:
+ type: string
+ required: false
+ default: some
requirements:
- some_req:
capability: tosca.capabilities.SomeCap
--- /dev/null
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ TOSCA simple profile to test the get attribute function with HOST parameter
+ using a node that has as capability a child class of Container
+
+imports:
+ - ../custom_types/container_cap_child.yaml
+
+topology_template:
+
+ node_templates:
+
+ test_node:
+ type: tosca.nodes.SomeNode
+ properties:
+ some_prop: { get_attribute: [ HOST, public_address ] }
+ requirements:
+ - host_child: test_node2
+
+ test_node2:
+ type: tosca.nodes.SomeNode2
+ requirements:
+ - host: server
+
+ server:
+ type: tosca.nodes.Compute
+
--- /dev/null
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA test for boolean properties
+
+node_types:
+
+ tosca.nodes.SoftwareComponentTest:
+ derived_from: tosca.nodes.SoftwareComponent
+ properties:
+ some_prop:
+ type: boolean
+
+ tosca.nodes.ComputeTest:
+ derived_from: tosca.nodes.Compute
+ capabilities:
+ endpoint:
+ type: tosca.capabilities.Endpoint
+
+topology_template:
+
+ node_templates:
+
+ software:
+ type: tosca.nodes.SoftwareComponentTest
+ properties:
+ some_prop: { get_property: [ HOST, endpoint, secure ] }
+ requirements:
+ - host: server
+
+ server:
+ type: tosca.nodes.ComputeTest
+ capabilities:
+ endpoint:
+ properties:
+ network_name: PUBLIC
+ secure: false
+
--- /dev/null
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA test for the IM
+
+imports:
+ - test: ../custom_types/node_with_cap.yaml
+
+topology_template:
+
+ node_templates:
+
+ some_node:
+ type: tosca.nodes.SomeNode
+ properties:
+ some_prop: { get_property: [ HOST, some_req, type ] }
+ requirements:
+ - some_req: server
+
+ server:
+ type: tosca.nodes.NodeWithCap
+ capabilities:
+ some_req:
+ properties:
+ type: someval
+
inputs:
strategy: LEAST_USED
implementation: Senlin.webhook()
+ high_cpu_usage:
+ description: trigger
+ meter_name: cpu_util
+ condition:
+ constraint: utilization greater_than 60%
+ threshold: 60
+ period: 600
+ evaluations: 1
+ method: average
+ comparison_operator: gt
+ metadata: SG1
+ action: [SP1]
+
+ low_cpu_usage:
+ description: trigger
+ meter_name: cpu_util
+ condition:
+ constraint: utilization less_than 20%
+ threshold: 20
+ period: 600
+ evaluations: 1
+ method: average
+ comparison_operator: gt
+ metadata: SG1
+ action: [SP1]
+
- my_groups_placement:
type: mycompany.mytypes.myScalingPolicy
targets: [ webserver_group ]
--- /dev/null
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA simple profile to test a custom defined capability
+
+imports:
+ - custom_types/custom_cap.yaml
+
+topology_template:
+
+ node_templates:
+
+ node_req:
+ type: tosca.nodes.NodeWithReq
+ requirements:
+ - host: node_cap
+
+ node_cap:
+ type: tosca.nodes.NodeWithCap
+ requirements:
+ - host: server
+
+ server:
+ type: tosca.nodes.Compute
derived_from: tosca.nodes.Database
example.SomeApp:
- derived_from: tosca.nodes.SoftwareComponent
+ derived_from: tosca.nodes.WebApplication
properties:
admin_user:
type: string
self.assertIsInstance(source_port, functions.GetProperty)
self.assertEqual(3306, source_port.result())
+ def test_get_prop_cap_host(self):
+ tosca_tpl = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/functions/test_get_prop_cap_host.yaml")
+ some_node = self._get_node('some_node',
+ ToscaTemplate(tosca_tpl))
+ some_prop = some_node.get_properties()['some_prop']
+ self.assertIsInstance(some_prop.value, functions.GetProperty)
+ self.assertEqual('someval', some_prop.value.result())
+
+ def test_get_prop_cap_bool(self):
+ tosca_tpl = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/functions/test_get_prop_cap_bool.yaml")
+ some_node = self._get_node('software',
+ ToscaTemplate(tosca_tpl))
+ some_prop = some_node.get_properties()['some_prop']
+ self.assertIsInstance(some_prop.value, functions.GetProperty)
+ self.assertEqual(False, some_prop.value.result())
+
class GetAttributeTest(TestCase):
self.assertIsNotNone(self._load_template(
'functions/test_get_implicit_attribute.yaml'))
+ def test_get_attribute_capability_inheritance(self):
+ self.assertIsNotNone(self._load_template(
+ 'functions/test_container_cap_child.yaml'))
+
class ConcatTest(TestCase):
tpl_name = "app"
expected_type = "example.SomeApp"
expected_properties = ['admin_user', 'pool_size']
- expected_capabilities = ['feature', 'message_receiver']
+ expected_capabilities = ['app_endpoint', 'feature', 'message_receiver']
expected_requirements = [{'host': {'node': 'websrv'}}]
expected_relationshp = ['tosca.relationships.HostedOn']
expected_host = ['websrv']
lambda: Policy(name, policies[name], None, None))
self.assertEqual(expectedmessage, err.__str__())
- def test_policy_trigger_valid_keyname(self):
+ def test_policy_trigger_valid_keyname_senlin_resources(self):
tpl_snippet = '''
triggers:
- resize_compute:
name = list(triggers.keys())[0]
Triggers(name, triggers[name])
- def test_policy_trigger_invalid_keyname(self):
+ def test_policy_trigger_valid_keyname_heat_resources(self):
+ tpl_snippet = '''
+ triggers:
+ - high_cpu_usage:
+ description: trigger
+ meter_name: cpu_util
+ condition:
+ constraint: utilization greater_than 60%
+ threshold: 60
+ period: 600
+ evaluations: 1
+ method: average
+ comparison_operator: gt
+ metadata: SG1
+ action: [SP1]
+ '''
+ triggers = (toscaparser.utils.yamlparser.
+ simple_parse(tpl_snippet))['triggers'][0]
+ name = list(triggers.keys())[0]
+ Triggers(name, triggers[name])
+
+ def test_policy_trigger_invalid_keyname_senlin_resources(self):
tpl_snippet = '''
triggers:
- resize_compute:
lambda: Triggers(name, triggers[name]))
self.assertEqual(expectedmessage, err.__str__())
+ def test_policy_trigger_invalid_keyname_heat_resources(self):
+ tpl_snippet = '''
+ triggers:
+ - high_cpu_usage:
+ description: trigger
+ meter_name: cpu_util
+ condition:
+ constraint: utilization greater_than 60%
+ threshold: 60
+ period: 600
+ evaluations: 1
+ method: average
+ comparison_operator: gt
+ metadata1: SG1
+ action: [SP1]
+ '''
+ triggers = (toscaparser.utils.yamlparser.
+ simple_parse(tpl_snippet))['triggers'][0]
+ name = list(triggers.keys())[0]
+ expectedmessage = _(
+ 'Triggers "high_cpu_usage" contains unknown field '
+ '"metadata1". Refer to the definition '
+ 'to verify valid values.')
+ err = self.assertRaises(
+ exception.UnknownFieldError,
+ lambda: Triggers(name, triggers[name]))
+ self.assertEqual(expectedmessage, err.__str__())
+
def test_policy_missing_required_keyname(self):
tpl_snippet = '''
policies:
from toscaparser.common.exception import ExceptionCollector
from toscaparser.common.exception import UnknownFieldError
from toscaparser.entity_template import EntityTemplate
-
-SECTIONS = (DESCRIPTION, EVENT, SCHEDULE, TARGET_FILTER, CONDITION, ACTION) = \
- ('description', 'event_type', 'schedule',
- 'target_filter', 'condition', 'action')
-CONDITION_KEYNAMES = (CONTRAINT, PERIOD, EVALUATIONS, METHOD) = \
- ('constraint', 'period', 'evaluations', 'method')
+from toscaparser.utils import validateutils
+
+SECTIONS = (DESCRIPTION, EVENT, SCHEDULE, METER_NAME, METADATA,
+ TARGET_FILTER, CONDITION, ACTION) = \
+ ('description', 'event_type', 'schedule', 'meter_name',
+ 'metadata', 'target_filter', 'condition', 'action')
+CONDITION_KEYNAMES = (CONSTRAINT, PERIOD, EVALUATIONS, METHOD,
+ THRESHOLD, COMPARISON_OPERATOR) = \
+ ('constraint', 'period', 'evaluations',
+ 'method', 'threshold', 'comparison_operator')
log = logging.getLogger('tosca')
self.trigger_tpl = trigger_tpl
self._validate_keys()
self._validate_condition()
+ self._validate_input()
def get_description(self):
return self.trigger_tpl['description']
ExceptionCollector.appendException(
UnknownFieldError(what='Triggers "%s"' % self.name,
field=key))
+
+ def _validate_input(self):
+ for key, value in self.get_condition().items():
+ if key in [PERIOD, EVALUATIONS]:
+ validateutils.validate_integer(value)
+ elif key == THRESHOLD:
+ validateutils.validate_numeric(value)
+ elif key in [METER_NAME, METHOD]:
+ validateutils.validate_string(value)
[tox]
-minversion = 1.6
+minversion = 2.0
envlist = py27,pep8
skipsdist = True
commands = oslo_debug_helper -t toscaparser/tests {posargs}
[flake8]
-# H803 skipped on purpose per list discussion.
-# E123, E125 skipped as they are invalid PEP-8.
-
show-source = True
ignore = E123,E125,H803
builtins = _