From 07ffe574af0c112c42401413b088681b581456b5 Mon Sep 17 00:00:00 2001 From: Simeon Monov Date: Tue, 17 Feb 2015 20:30:04 +0100 Subject: [PATCH] Add TOSCA networking definiton 1. Add the TOSCA networking features definiton to TOSCA_definition.yaml: - tosca.nodes.network.Network - tosca.nodes.network.Port - tosca.capabilities.network.Linkable - tosca.capabilities.network.Bindable - tosca.relations.network.LinksTo - tosca.relations.network.BindsTo 2. Add support for implicit declaration of requirements by type. For example: tosca.nodes.network.Network: derived_from: tosca.nodes.Root ........ requirements: - binding: type: tosca.capabilities.network.Bindable - link: type: tosca.capabilities.network.Linkable ........ Change-Id: I34c7c9091d05bff4641937fe72b56e7a7b57a1e6 Partially-implements: blueprint tosca-networking --- .../toscalib/elements/TOSCA_definition.yaml | 152 ++++++++++++++++++ translator/toscalib/elements/entitytype.py | 7 +- translator/toscalib/elements/nodetype.py | 38 +++++ translator/toscalib/tests/test_toscadef.py | 24 ++- translator/toscalib/tests/test_toscatpl.py | 5 +- 5 files changed, 220 insertions(+), 6 deletions(-) diff --git a/translator/toscalib/elements/TOSCA_definition.yaml b/translator/toscalib/elements/TOSCA_definition.yaml index 1449fb1..c21c0d0 100644 --- a/translator/toscalib/elements/TOSCA_definition.yaml +++ b/translator/toscalib/elements/TOSCA_definition.yaml @@ -86,6 +86,8 @@ tosca.nodes.Compute: capabilities: host: type: tosca.capabilities.Container + binding: + type: tosca.capabilities.network.Bindable requirements: - attachment: tosca.nodes.BlockStorage type: AttachTo @@ -181,6 +183,133 @@ tosca.nodes.BlockStorage: attachment: type: tosca.capabilities.Attachment +tosca.nodes.network.Network: + derived_from: tosca.nodes.Root + description: > + The TOSCA Network node represents a simple, logical network service. + properties: + ip_version: + type: integer + required: no + default: 4 + constraints: + - valid_values: [ 4, 6 ] + description: > + The IP version of the requested network. Valid values are 4 for ipv4 + or 6 for ipv6. + cidr: + type: string + required: no + description: > + The cidr block of the requested network. + start_ip: + type: string + required: no + description: > + The IP address to be used as the start of a pool of addresses within + the full IP range derived from the cidr block. + end_ip: + type: string + required: no + description: > + The IP address to be used as the end of a pool of addresses within + the full IP range derived from the cidr block. + gateway_ip: + type: string + required: no + description: > + The gateway IP address. + network_name: + type: string + required: no + description: > + An identifier that represents an existing Network instance in the + underlying cloud infrastructure or can be used as the name of the + newly created network. If network_name is provided and no other + properties are provided (with exception of network_id), then an + existing network instance will be used. If network_name is provided + alongside with more properties then a new network with this name will + be created. + network_id: + type: string + required: no + description: > + An identifier that represents an existing Network instance in the + underlying cloud infrastructure. This property is mutually exclusive + with all other properties except network_name. This can be used alone + or together with network_name to identify an existing network. + segmentation_id: + type: string + required: no + description: > + A segmentation identifier in the underlying cloud infrastructure. + E.g. VLAN ID, GRE tunnel ID, etc.. + dhcp_enabled: + type: boolean + required: no + default: true + description: > + Indicates should DHCP service be enabled on the network or not. + capabilities: + link: + type: tosca.capabilities.network.Linkable + +tosca.nodes.network.Port: + derived_from: tosca.nodes.Root + description: > + The TOSCA Port node represents a logical entity that associates between + Compute and Network normative types. The Port node type effectively + represents a single virtual NIC on the Compute node instance. + properties: + ip_address: + type: string + required: no + description: > + Allow the user to set a static IP. + order: + type: integer + required: no + default: 0 + constraints: + - greater_or_equal: 0 + description: > + The order of the NIC on the compute instance (e.g. eth2). + is_default: + type: boolean + required: no + default: false + description: > + If is_default=true this port will be used for the default gateway + route. Only one port that is associated to single compute node can + set as is_default=true. + ip_range_start: + type: string + required: no + description: > + Defines the starting IP of a range to be allocated for the compute + instances that are associated with this Port. + ip_range_end: + type: string + required: no + description: > + Defines the ending IP of a range to be allocated for the compute + instances that are associated with this Port. + attributes: + ip_address: + type: string + requirements: + - binding: + description: > + Binding requirement expresses the relationship between Port and + Compute nodes. Effectevely it indicates that the Port will be + attached to specific Compute node instance + type: tosca.capabilities.network.Bindable + - link: + description: > + Link requirement expresses the relationship between Port and Network + nodes. It indicates which network this port will connect to. + type: tosca.capabilities.network.Linkable + ########################################################################## # Relationship Type. # A Relationship Type is a reusable entity that defines the type of one @@ -213,6 +342,14 @@ tosca.relationships.AttachTo: required: false type: string +tosca.relationships.network.LinksTo: + derived_from: tosca.relationships.DependsOn + valid_targets: [ tosca.capabilities.network.Linkable ] + +tosca.relationships.network.BindsTo: + derived_from: tosca.relationships.DependsOn + valid_targets: [ tosca.capabilities.network.Bindable ] + ########################################################################## # Capability Type. # A Capability Type is a reusable entity that describes a kind of @@ -266,6 +403,21 @@ tosca.capabilities.DatabaseEndpoint: tosca.capabilities.Attachment: derived_from: tosca.capabilities.Root +tosca.capabilities.network.Linkable: + derived_from: tosca.capabilities.Root + description: > + A node type that includes the Linkable capability indicates that it can + be pointed by tosca.relationships.network.LinksTo relationship type, which + represents an association relationship between Port and Network node types. + +tosca.capabilities.network.Bindable: + derived_from: tosca.capabilities.Root + description: > + A node type that includes the Bindable capability indicates that it can + be pointed by tosca.relationships.network.BindsTo relationship type, which + represents a network association relationship between Port and Compute node + types. + ########################################################################## # Interfaces Type. # The Interfaces element describes a list of one or more interface diff --git a/translator/toscalib/elements/entitytype.py b/translator/toscalib/elements/entitytype.py index 3b2fc65..9d04582 100644 --- a/translator/toscalib/elements/entitytype.py +++ b/translator/toscalib/elements/entitytype.py @@ -35,11 +35,14 @@ class EntityType(object): TOSCA_DEF = loader(TOSCA_DEF_FILE) - RELATIONSHIP_TYPE = (DEPENDSON, HOSTEDON, CONNECTSTO, ATTACHTO) = \ + RELATIONSHIP_TYPE = (DEPENDSON, HOSTEDON, CONNECTSTO, ATTACHTO, + LINKSTO, BINDSTO) = \ ('tosca.relationships.DependsOn', 'tosca.relationships.HostedOn', 'tosca.relationships.ConnectsTo', - 'tosca.relationships.AttachTo') + 'tosca.relationships.AttachTo', + 'tosca.relationships.network.LinksTo', + 'tosca.relationships.network.BindsTo') NODE_PREFIX = 'tosca.nodes.' RELATIONSHIP_PREFIX = 'tosca.relationships.' diff --git a/translator/toscalib/elements/nodetype.py b/translator/toscalib/elements/nodetype.py index 72f4a52..8741263 100644 --- a/translator/toscalib/elements/nodetype.py +++ b/translator/toscalib/elements/nodetype.py @@ -42,6 +42,15 @@ class NodeType(StatefulEntityType): relationship = {} requires = self.get_all_requirements() if requires: + # NOTE(sdmonov): Check if requires is a dict. + # If it is a dict convert it to a list of dicts. + # This is needed because currently the code below supports only + # lists as requirements definition. The following check will + # make sure if a map (dict) was provided it will be converted to + # a list before proceeding to the parsing. + if isinstance(requires, dict): + requires = [{key: value} for key, value in requires.items()] + keyword = None node_type = None for req in requires: @@ -61,6 +70,14 @@ class NodeType(StatefulEntityType): if key == 'interfaces': continue else: + # If value is a dict and has a type key we need + # to lookup the node type using the capability type + + if isinstance(value, dict) and \ + 'type' in value: + captype = value['type'] + value = \ + self._get_node_type_by_cap(key, captype) relation = self._get_relation(key, value) keyword = key node_type = value @@ -69,6 +86,27 @@ class NodeType(StatefulEntityType): relationship[rtype] = relatednode return relationship + def _get_node_type_by_cap(self, key, cap): + '''Find the node type that has the provided capability + + This method will lookup all node types if they have the + provided capability. + ''' + + # Filter the node types + node_types = [node_type for node_type in self.TOSCA_DEF.keys() + if node_type.startswith(self.NODE_PREFIX) and + node_type != 'tosca.nodes.Root'] + + for node_type in node_types: + node_def = self.TOSCA_DEF[node_type] + if isinstance(node_def, dict) and 'capabilities' in node_def: + node_caps = node_def['capabilities'] + for value in node_caps.values(): + if isinstance(value, dict) and \ + 'type' in value and value['type'] == cap: + return node_type + def _get_relation(self, key, ndtype): relation = None ntype = NodeType(ndtype) diff --git a/translator/toscalib/tests/test_toscadef.py b/translator/toscalib/tests/test_toscadef.py index 479d863..ee0840a 100644 --- a/translator/toscalib/tests/test_toscadef.py +++ b/translator/toscalib/tests/test_toscadef.py @@ -15,6 +15,8 @@ from translator.toscalib.elements.nodetype import NodeType from translator.toscalib.tests.base import TestCase compute_type = NodeType('tosca.nodes.Compute') component_type = NodeType('tosca.nodes.SoftwareComponent') +network_type = NodeType('tosca.nodes.network.Network') +network_port_type = NodeType('tosca.nodes.network.Port') class ToscaDefTest(TestCase): @@ -22,14 +24,23 @@ class ToscaDefTest(TestCase): self.assertEqual(compute_type.type, "tosca.nodes.Compute") self.assertRaises(exception.InvalidTypeError, NodeType, 'tosca.nodes.Invalid') + self.assertEqual(network_type.type, "tosca.nodes.network.Network") + self.assertEqual(network_port_type.type, "tosca.nodes.network.Port") def test_parent_type(self): self.assertEqual(compute_type.parent_type.type, "tosca.nodes.Root") + self.assertEqual(network_type.parent_type.type, "tosca.nodes.Root") + self.assertEqual(network_port_type.parent_type.type, + "tosca.nodes.Root") def test_capabilities(self): self.assertEqual( - ['tosca.capabilities.Container'], - [c.type for c in compute_type.capabilities]) + sorted(['tosca.capabilities.Container', + 'tosca.capabilities.network.Bindable']), + sorted([c.type for c in compute_type.capabilities])) + self.assertEqual( + ['tosca.capabilities.network.Linkable'], + [c.type for c in network_type.capabilities]) def test_properties_def(self): self.assertEqual( @@ -59,6 +70,15 @@ class ToscaDefTest(TestCase): ('tosca.relationships.HostedOn', ['tosca.capabilities.Container']), [(relation.type, relation.valid_targets) for relation in list(component_type.relationship.keys())]) + self.assertIn( + ('tosca.relationships.network.BindsTo', 'tosca.nodes.Compute'), + [(relation.type, node.type) for + relation, node in network_port_type.relationship.items()]) + self.assertIn( + ('tosca.relationships.network.LinksTo', + 'tosca.nodes.network.Network'), + [(relation.type, node.type) for + relation, node in network_port_type.relationship.items()]) def test_interfaces(self): self.assertEqual(compute_type.interfaces, None) diff --git a/translator/toscalib/tests/test_toscatpl.py b/translator/toscalib/tests/test_toscatpl.py index 38bfa38..cbf6ff8 100644 --- a/translator/toscalib/tests/test_toscatpl.py +++ b/translator/toscalib/tests/test_toscatpl.py @@ -157,8 +157,9 @@ class ToscaTemplateTest(TestCase): for tpl in tosca_tpl.nodetemplates: compute_type = NodeType(tpl.type) self.assertEqual( - ['tosca.capabilities.Container'], - [c.type for c in compute_type.capabilities]) + sorted(['tosca.capabilities.Container', + 'tosca.capabilities.network.Bindable']), + sorted([c.type for c in compute_type.capabilities])) def test_template_with_no_inputs(self): tosca_tpl = self._load_template('test_no_inputs_in_template.yaml')