Data Types
The data_types
section allows you to define custom datatypes that you can use as the value for properties, attributes, and parameters (inputs and outputs).
Declaration
data_types:
data_type1:
description: ...
properties: ...
data_type2:
derived_from: data_type1
description: ...
properties: ...
Definition
Keyname |
Required |
Type |
Description |
---|---|---|---|
description |
no |
string |
Description for the data type. |
properties |
no |
dictionary |
Dictionary of the data type properties. |
derived_from |
no |
string |
Parent data type. |
description
This property may be used to describe the data type.
properties
The properties
property is used to define the data type schema.
properties
is a dictionary from a property name to a dictionary describing the property. The nested dictionary includes the following keys:
Keyname |
Required |
Type |
Description |
---|---|---|---|
description |
yes |
string |
Description for the property. |
type |
no |
string |
Property type. Not specifying a data type means the type can be anything (including types not listed in the valid types). Valid types: string, integer, float, boolean or a another custom data type. |
default |
no |
<any> |
An optional default value for the property. |
required |
no |
boolean |
Specifies whether the property is required. (Default: true) |
derived_from
The derived_from
property may be used to build over and extend an existing data type.
When a data type derives from another data type, its properties
get merged with the parent’s properties
. The merge is on the property level: A property defined on the parent type will be overridden by a property with the same name defined on the deriving type. An exception to this rule is when a property type
references some other custom data type. This will be explained in detail in the following examples
section.
Examples
Basic Usage
In the following example, we define a my.datatypes.Endpoint
data
type with two properties: ip
and port
. Next, we define a node
type DatabaseService
to represnt some external database service. In
this type’s properties, we define an endpoint
property who’s type is
the endpoint data type previously defined. Lastly, we define a node
template with a DatabaseService
type. This node template fully
configures the endpoint properties (i.e. the ip
and port
).
tosca_definitions_version: tosca_simple_unfurl_1_0_0
data_types:
my.datatypes.Endpoint:
description: Socket endpoint details
properties:
ip:
description: the endpoint IP
type: string
port:
description: the endpoint port
type: integer
node_types:
DatabaseService:
derived_from: tosca.nodes.DBMS
properties:
endpoint:
type: my.datatypes.Endpoint
topology_template:
node_templates:
my_db_service:
type: DatabaseService
properties:
endpoint:
ip: 192.168.15.85
port: 2233
import unfurl
import tosca
from tosca import DataEntity
class my_datatypes_Endpoint(DataEntity):
"""Socket endpoint details"""
_type_name = "my.datatypes.Endpoint"
ip: str
"""the endpoint IP"""
port: int
"""the endpoint port"""
class DatabaseService(tosca.nodes.DBMS):
endpoint: "my_datatypes_Endpoint"
my_db_service = DatabaseService(
"my_db_service",
endpoint=my_datatypes_Endpoint(
ip="192.168.15.85",
port=2233,
),
)
Schema Validations
If we were to miss a property or specify an additional property under
endpoint
, the service template will fail validation.
node_templates:
my_db_service2:
type: DatabaseService
properties:
endpoint:
ip: 192.168.15.85
The above example will fail validation on missing port
property.
Note
If port
had its required
attribute set to false
, no validation failure would take place
Let’s have a look at another example:
node_templates:
my_db_service3:
type: DatabaseService
properties:
endpoint:
ip: 192.168.15.85
port: 2233
some_other_property: the_value
This will fail validation on unexpected some_other_property
that is not
specified in endpoint
’s schema.
Inheritance
We can derive from previously defined data types to extend their schema.
For example, consider the my.datatypes.Endpoint
defined in the
previous example. We can derive from it, to create an endpoint data type
that also includes a user name.
tosca_definitions_version: tosca_simple_unfurl_1_0_0
data_types:
my.datatypes.Endpoint:
...
my.datatypes.ExtendedEndpoint:
derived_from: my.datatypes.Endpoint
properties:
username:
description: Username used to connect to the endpoint
type: string
node_types:
DatabaseService:
derived_from: tosca.nodes.DBMS
properties:
endpoint:
type: my.datatypes.ExtendedEndpoint
topology_template:
node_templates:
my_db_service:
type: DatabaseService
properties:
endpoint:
ip: 192.168.15.85
port: 2233
username: jimmy
import unfurl
import tosca
from tosca import DataEntity
class my_datatypes_Endpoint(DataEntity):
"""Socket endpoint details"""
_type_name = "my.datatypes.Endpoint"
ip: str
"""the endpoint IP"""
port: int
"""the endpoint port"""
class DatabaseService(tosca.nodes.DBMS):
endpoint: "my_datatypes_Endpoint"
my_db_service = DatabaseService(
"my_db_service",
endpoint=my_datatypes_Endpoint(
ip="192.168.15.85",
port=2233,
),
)
Composition
Data type property types can be other data types themselves. We will
reuse the previously defined my.datatypes.Endpoint
. This time, we
will create a my.datatypes.Connection
that will hold endpoint
information + authentication details.
tosca_definitions_version: tosca_simple_unfurl_1_0_0
data_types:
my.datatypes.Endpoint:
...
my.datatypes.Connection:
properties:
endpoint:
type: my.datatypes.Endpoint
auth:
type: my.datatypes.Auth
my.datatypes.Auth:
properties:
username:
type: string
password:
type: string
node_types:
DatabaseService:
derived_from: cloudify.nodes.DBMS
properties:
connection:
type: my.datatypes.Connection
topology_template:
node_templates:
my_db_service:
type: DatabaseService
properties:
connection:
endpoint:
ip: 192.168.15.85
port: 2233
auth:
username: jimmy
password: secret
class Auth(tosca.datatypes.Root):
username: str
password: str
class Endpoint(tosca.datatypes.Root):
ip: str
port: int
class Connection(tosca.datatypes.Root):
endpoint: Endpoint
auth: Auth
class DatabaseService(tosca.nodes.DBMS):
connection: Connection
my_db_service = DatabaseService(
"my_db_service",
connection=Connection(
endpoint=Endpoint(
ip="192.168.15.85",
port=2233
),
auth=Auth(
username="jimmy",
password="secret"
)
)
)
__all__ = ["Auth", "Endpoint", "Connection", "DatabaseService"]
Default Values
Default values can help make highly configurable components easy to use
by setting default values where it makes sense. Consider our previously
defined my.datatypes.Connection
. We can simplify its usage if we
know that port
by default will be 2233
and username by default
will be admin
.
tosca_definitions_version: tosca_simple_unfurl_1_0_0
data_types:
my.datatypes.Connection:
properties:
endpoint:
type: my.datatypes.Endpoint
auth:
type: my.datatypes.Auth
my.datatypes.Endpoint:
description: Socket endpoint details
properties:
ip:
description: the endpoint IP
type: string
port:
default: 2233
type: integer
my.datatypes.Auth:
properties:
username:
default: admin
type: string
password:
type: string
node_types:
DatabaseService:
derived_from: nodes.DBMS
properties:
connection:
type: my.datatypes.Connection
topology_template:
node_templates:
my_db_service:
type: DatabaseService
properties:
connection:
endpoint:
ip: 192.168.15.85
auth:
password: secret
class Auth(tosca.datatypes.Root):
username: str = "admin"
password: str
class Endpoint(tosca.datatypes.Root):
ip: str
port: int = 2233
class Connection(tosca.datatypes.Root):
endpoint: Endpoint
auth: Auth
class DatabaseService(tosca.nodes.DBMS):
connection: Connection
my_db_service = DatabaseService(
"my_db_service",
connection=Connection(
endpoint=Endpoint(
ip="192.168.15.85"
),
auth=Auth(
password="secret"
)
)
)
__all__ = ["Auth", "Endpoint", "Connection", "DatabaseService"]
Notice how the my_db_service
node template only specified the
connection.endpoint.ip
and connection.auth.password
. The other
properties got the default 2233
port and admin
user.
Overriding Default Values
As its name implies, default values are of course, just defaults. As such, you can override them in same way you would configure properties without default values. For example:
node_templates:
my_db_service:
type: DatabaseService
properties:
connection:
endpoint:
ip: 192.168.15.85
port: 2244
auth:
password: secret
Here we have overridden the default connection.endpoint.port
value and kept the default connection.auth.username
value.
Nested Merging Semantics
Data Type ← Node Type ← Node Template
In this example, we define a data type datatypes.Data1
with three
properties that have their default values set. Next, we define a node
type nodes.MyApp
which has a data1
property of type
datatypes.Data1
. In this type, we override a single nested property
prop2
of the data1
property. Finally, we configure a node
template my_app
of type nodes.MyApp
. This node template
overrides another single nested property prop3
of the data1
property.
data_types:
datatypes.Data1:
properties:
prop1:
default: prop1_default
prop2:
default: prop2_default
prop3:
default: prop3_default
node_types:
nodes.MyApp:
properties:
data1:
type: datatypes.Data1
default:
prop2: prop2_override
topology_template:
node_templates:
my_app:
type: nodes.MyApp
properties:
data1:
prop3: prop3_override
class Data1(tosca.datatypes.Root):
prop1: str = "prop1_default"
prop2: str = "prop2_default"
prop3: str = "prop3_default"
class MyApp(tosca.nodes.Root):
data1: Data1 = Data1(prop2="prop2_override")
my_app = MyApp(
"my_app",
data1=Data1(
prop3="prop3_override"
)
)
__all__ = ["Data1", "MyApp"]
After the service template is parsed, the my_app
node template properties will be:
data1:
prop1: prop1_default
prop2: prop2_override
prop3: prop3_override
This also applies for compound data types, for example:
data_types:
datatypes.Data1:
...
datatypes.Data2:
properties:
data1:
type: datatypes.Data1
default:
prop2: prop2_override
In which case, datatypes.Data2
’s data1
property default value
will be:
data1:
prop1: prop1_default
prop2: prop2_override
prop3: prop3_default
Nested Merging and Inheritance
When a node type derives from another node type, if it overrides a property who’s type is a custom data type and keeps that type explicitly, a similar nested merging logic will apply as described previously. For example:
tosca_definitions_version: tosca_simple_unfurl_1_0_0
data_types:
datatypes.Data1:
properties:
prop1:
default: prop1_default
prop2:
default: prop2_default
prop3:
default: prop3_default
node_types:
nodes.MyApp:
properties:
data2:
type: datatypes.Data1
default:
prop2: prop2_override
nodes.DerivedFromMyApp:
derived_from: nodes.MyApp
properties:
data2:
type: datatypes.Data1
default:
prop3: prop3_override
topology_template
node_templates:
my_app:
type: nodes.DerivedFromMyApp
class Data1(tosca.datatypes.Root):
prop1: str = "prop1_default"
prop2: str = "prop2_default"
prop3: str = "prop3_default"
class MyApp(tosca.nodes.Root):
data2: Data1 = Data1(prop2="prop2_override")
class DerivedFromMyApp(MyApp):
data2: Data1 = Data1(prop3="prop3_override")
my_app = DerivedFromMyApp("my_app")
__all__ = ["Data1", "MyApp", "DerivedFromMyApp"]
After the service template is parsed, the my_app
node template properties will be:
data1:
prop1: prop1_default
prop2: prop2_override
prop3: prop3_override
See also
For more information, refer to TOSCA Data Types Section