Assembly descriptors

Assembly descriptors are YAML files that describe the building blocks of applications that are managed by the orchestration component of IBM® Cloud Pak for Network Automation.

The basic building blocks are described in resource descriptors. Resource descriptors are then composed into assembly descriptors to describe a complete application or service that the orchestration component can manage.

Assembly descriptors include descriptions of relationships between components, which allow the orchestration component to provision the component instances that it manages. If you want to manage the configuration of those instances after they are provisioned, you must use a tool other than IBM Cloud Pak for Network Automation. Assemblies can also reference other assemblies and existing infrastructure items, such as external network instances.

The following table summarizes the sections in assembly descriptors:

Section Required Description
name Y The type, name, and version of this descriptor.
description N A meaningful description of the contents of this descriptor.
properties Y The properties and values for the assembly instance that is created by using this descriptor.
operations N The operations that can be called when this assembly is involved in a relationship in a higher-level descriptor.
composition Y The components that make up the assembly and are instantiated when the assembly is installed.
references N The existing assemblies or externally managed systems that this assembly can refer to.
relationships N The details of relationships between assembly components, such as the capabilities that one component requires from another.
data-types N The customized data types that you might want to use for properties.
capabilities N The functions that the assembly provides.
requirements N The capabilities that the assembly requires to work successfully.

name section

Note: When you create the descriptor as part of a package in LMCTL, you do not need to specify the name section. When you deploy the package to an orchestration environment, if no descriptor name is specified, a default name is created automatically. For more information, see Naming rules.

In the name section, you specify a name and a version number for the descriptor. The name section comprises the following components, which are separated by a double colon (::):

Descriptor Type
Specifies the type of descriptor: assembly or resource.
Name
Specifies a name for the descriptor. The name must be unique for the descriptor type. To make sure that the name is unique, use a convention such as companyname.resourcename when you name the descriptor. The name must conform to the following rules:
  • Starts with a letter (uppercase or lowercase).
  • Ends with either a letter (uppercase or lowercase) or a number.
  • Can include letters, digits, and non-consecutive underscores and hyphens.
  • No spaces.
  • Maximum length of 50 characters.
Version
Specifies one or more number values that are separated by a single period character (.). Alphabetic characters are not allowed. Therefore, the following values are valid: 1.0 and 1.0.0. The following value is not valid: v1.0
The following example shows a valid assembly descriptor name:
name: assembly::Example_company2.neutron_network::1.0.0
Tip: To update a descriptor, you must create a copy of the descriptor and update the version number.

description section

In the description section, you provide a meaningful description of the descriptor. The following example shows the name and description sections of a resource descriptor:

name: resource::c_streamer::1.0
description: Component package for c_streamer 

properties section

In the higher level properties section of an assembly descriptor, you define the properties of an assembly instance, that is, the set of properties that are required to orchestrate an assembly through to the Active state. These properties can be understood as the context for the management of the assembly during its lifecycle.

Assembly descriptors can also contain other, low-level properties sections. For example, assembly components, which are described in the composition section, include their own sets of properties. For all properties sections in an assembly descriptor, the same rules, attributes, and structure apply.

The properties in this section are assembly-wide properties, that is, they can be referred to in low-level properties sections anywhere in the assembly descriptor.

Each property is identified by a name and a number of attributes.

Attribute Description Required Type Restrictions
<property_name>

The property name, which must be unique within its property section or scope.

To specify the deployment location properties for this assembly instance, set this attribute to deploymentLocation.

To specify the resource manager properties for this assembly instance, set this attribute to resourceManager.

For more information, see deploymentLocation and resourceManager properties.

Y String
  • No spaces.
  • Minimum length of one alphanumeric character.
  • No periods (.).
  • Must be valid YAML.
type The data type of the property.

For more information about the map and list types, see Structured properties.

For more information about customized data types, see data-types section.

Y The following options are available:
  • string (Default)
  • boolean
  • integer
  • float
  • timestamp
  • map
  • list
  • key
  • certificate
  • Customized data type
 
required Indicates that a value for this property must be provided when the assembly is instantiated. N Boolean  
description A textual description of the purpose of the property. N String  
default A default value that is provided to the assembly if an override is not provided at instantiation time. N String  
read-only Indicates that a value cannot be provided at instantiation time and that the assembly might provide an updated value when instantiated. A default value or a mapped property might be provided. N Boolean  
volatile Identifies that changes to this property do not trigger a re-installation of the assembly. For more information, see Volatile properties. N Boolean  
value A static value for the property, which cannot be overridden at instantiation time.

You can identify a value that is mapped from one or more properties of the parent assembly or from the properties of any peer component. To identify a mapped property, use the following syntax:

${<sourceProperty>}

N String or reference You must include a space between the value: string and the value itself, for example, value: 123.
Example of a properties section
properties:
  deploymentLocation: 
 ​   type: string​  
 ​   required: true
​    description: The name of the openstack project (tenant) to install this assembly in. 

  resourceManager:
​    value: '${resourceManager}'

  numOfStreamers: # the name of the property
​    type: string​
    description: the number of streamers that should be created at install time​    
    default: 2
    
  tenant_key_name:
​    type: string​
    required: true
​    description: The SSH key for the current tenant

flavor:
​    value: m1.small

cluster_public_ip_address:
​    type: string​
    description: the public IP address for this cluster​
    read-only: true​
    value: '${balancer.publicIp}'
Properties and required or optional status

Properties are optional unless they are explicitly defined as required by using a required: true attribute. The required status affects a higher-level assembly only and means that a value must be present (that is, not null) for a property.

If a value is required, it's a good idea to include it when you define the property. The inclusion of the value helps designers of other assembly descriptors to ensure that the value is represented in assemblies when the property is mapped or promoted. You can check the value in the value attribute, the default value, or in the value that is passed in from the intent request.

Read-only properties

Properties that are marked as read-only: true are not overridden by values that are mapped in from an enclosing assembly or from the intent request. The read-only attribute is typically used for properties that are calculated from, or returned by, the assembly instance itself.

Property values
You can assign a default value or a specific value to a property. To set a property value to the value of another property in the higher level properties section of the descriptor, use the following syntax:
value: '${<sourceProperty>}'
To set a higher level property value to the value of a property in a low-level properties section, include the name of the object that the low-level property belongs to. For example, if your descriptor contains a component that is called balancer, use the following syntax to set a higher level property to the value of the publicIp property for this component:
value: ‘${balancer.publicIp}’

The orchestration component assigns an internal name and identifier to each resource instance that it creates. It also supplies the index number of a resource in a cluster. These values can be useful when you define properties because they indicate the unique resource instance names. To access these values, you can set property values to references such as ${instance.name}, ${instance.id}, or ${instance.index}. The orchestration component then replaces the references with the appropriate values.

deploymentLocation and resourceManager properties

The deploymentLocation and resourceManager properties appear only in assembly descriptors.

In the deploymentLocation property, you specify the virtual infrastructure location where you want the resources that are created for this assembly instance to be deployed. For example, you might set the value of this property to the name of a Kubernetes namespace or an OpenStack tenant.

In the resourceManager property, you specify the name of the resource manager that manages the resources that are created for this assembly instance. This property must be set to Brent.

Property data types

Properties can have the following scalar types:

  • string
  • boolean
  • integer
  • float
  • timestamp
Example

This example shows how you can assign scalar types to properties and set default and hard-coded values for each type:

msg:
    type: string
    default: "hello world"
  test_integer:
    description: This is the test integer description from the parent
    type: integer
    value: 100
  test_boolean:
    type: boolean
    default: true
    readOnly: true
  test_float:
    type: float
    value: 3.141592654
  test_timestamp:
    type: timestamp
    value: 2019-09-07T15:50-04:00
    volatile: true

Structured properties

In addition to using scalar types, you can define properties that use a structured type, which must be either list or map.

Use the list type to define a list of values that can each be accessed by using the value's position in the array. Use the map type to define a set of key-value pairs, where each value can be accessed by using its corresponding key.

Lists and maps can be nested. For example, each value in a list might be another list.

You can describe lists in the following ways:
  • Brackets. For example, you might describe a list of three elements in this format: [0, 1, 2]
  • A hyphenated format that shows each listed item on a separate line. For example, you might describe a list of three lists in this format:
        - [0,1,2]
        - [3,4,5]
        - [6,7,8]
You can describe maps in the following ways:
  • Braces. For example, you might describe a map of three elements like this: {a: 100, b: 101, c: 102}
  • A list that shows each mapped item on a separate line. For example, you might describe a map of three items in this format:
        a: 100
        b: 101
        c: 102

Use the entry-schema property to define the data type of each value in a list or map. In addition to scalar and structured data types, you can use customized data types that are defined in the data-types section.

Example
This example shows the following structured data items:
  • A list of three integers that is called test_list and is described by using brackets.
  • A map that is called test_map and contains three floating-point values and their corresponding keys, floatkey1, floatkey2, and floatkey3.
  • A list of maps that is called test_data and is described by using a hyphenated format that shows each listed item on a separate line. This list contains the following complex nested structure:
    • In each higher level map in the list, the value of the nested-property list element is another map that contains two elements, which are identified by the keys first-level and double-nested.
    • The double-nested element is another map that contains two elements, which are identified by the keys second-level and deeply-nested.
    • The deeply-nested element is another map that contains one element, which is identified by the key third-level.

test_list:
  type: list
  entry-schema:
    type: integer
    default: [-2147483648, 0, 2147483647]
test_map:
  type: map
  entry-schema:
    type: float
    description: This the the test map inner description from the parent
  default:
    floatkey1: -3.141592654
    floatkey2: 0.123456789
    floatkey3: 3.141592654
test_data:
  default:
    - { id: 'internal1', name: 'public1', nested-property: {first-level: '11', double-nested: {second-level: '12', deeply-nested: {third-level: '13'}}}}
    - { id: 'internal2', name: 'public2', nested-property: {first-level: '21', double-nested: {second-level: '22', deeply-nested: {third-level: '23'}}}}
    - { id: 'internal3', name: 'public3', nested-property: {first-level: '31', double-nested: {second-level: '32', deeply-nested: {third-level: '33'}}}}
  type: list
  entry_schema:
    type: map
  description: Array of structured data to be passed to the 'D' group

Structured property mapping

After you define maps and lists that contain data, you can access the data and map it into other descriptor properties. You can map both scalar and structured data.

This example shows how you can map structured data into an assembly component's properties from a list of maps. This list is called test_map and contains the following complex nested structure:
  • In each higher level map in the list, the value of the list-property list element is another list that contains three string elements.
  • In each higher level map in the list, the value of the nested-property list element is another map that contains three elements, which are identified by the keys first-level, nested-list, and double-nested.
  • The nested-list element is another list that contains three string elements.
  • The double-nested element is another map that contains two elements, which are identified by the keys second-level and deeply-nested.
  • The deeply-nested element is another map that contains one element, which is identified by the key third-level.

In this example, string values from the structured list are mapped to component properties by setting the value attribute for each component property to a list value. The list value that each component property is mapped to is indicated by a comment, that is, //.

properties:
  test_map:
    type: map
    default: { id: 'internal1', 
               name: 'public1', 
               list-property: ['entry1', 'entry2', 'entry3'],
               nested-property: 
                 {first-level: '11', 
                 nested-list: ['entry12', 'entry22', 'entry32'],
                 double-nested: {second-level: '12', 
                                 deeply-nested: {third-level: '13'}}}}
​    ...

composition:
      ...
    component1:
        properties:
            data-nested1-value:
                value: ${test_map.nested-property.first-level}  // value = '11'
            data-nested2-value:
                value: ${test_map.nested-property.double-nested.second-level} // value = '12'
            data-nested3-value:
                value: ${test_map.nested-property.double-nested.deeply-nested.third-level} // value = '13'
            data-list-first-value:
                value: ${test_map.list-property[0]} // value = 'entry1'
            data-nested-list-second-value: 
                value: ${test_map.nested-property.nested-list[1]} // will be set to value 'entry22'  

See Defining component groups for property mapping in the context of a group.

operations section

In the operations section, you specify the component operations that you want to make available to higher-level assembly descriptors that include this assembly in relationships.

Operations and relationships

Relationships between assembly components are created and ceased through the calling of operations. Each component can be a resource or another assembly.

Operations are described in resource descriptors. After you describe a resource's operations, you can define relationships that call these operations in the relationships section of the containing assembly descriptor. When you define a relationship that involves a resource, you can specify which of the resource's operations are called when the relationship is created or ceased.

Similarly, when you define a relationship that involves another assembly, you might want the relationship to call operations for that other assembly. In this case, you must first ensure that these operations are exposed in that other assembly’s descriptor by including them in its operations section. This process is known as publishing operations.

Publishing operations

In an assembly descriptor, you publish operations so that they can be called from higher-level descriptors that contain the assembly and use it in relationships.

When you publish an operation by including it in the operations section, use the following format:
<assembly_operation_name>:
​        source-operation: <component_name>.<component_operation_name>
  • <assembly_operation_name> is the name that you want to use to publish this operation in this assembly. This name must be used by higher-level descriptors that call this operation.
  • <component_name> is the name of the component that contains the operation that you want to publish.
  • <component_operation_name> is the defined name of the operation in the component’s descriptor.
Example
This sample scenario shows how you can publish operations to enable the setup of relationships that involve assemblies:Example for publishing operations
  • Operations R_op1 and R_op2 are described in the resource descriptor for resource_R.
  • The assembly descriptor for assembly_B contains resource_R.
  • The assembly descriptor for assembly_A contains assembly_B and resource_S.
  • In the descriptor for assembly_A, you decide to define a relationship between assembly_B and resource_S that calls operations R_op1 and R_op2.
  • To allow this relationship to be defined, you publish the two operations in the descriptor for assembly_B by adding them to its operations section.
  • In the descriptor for assembly_A, you can now define a relationship between resource_S and assembly_B that calls the two operations.
In the descriptor for assembly_B, when you include resource_R and publish its operations, you might specify these attributes:
name: assembly::assembly_B::1.0
...
composition:
  res_R:
​    type: resource::resource_R::1.0
​    quantity: 1
​    properties:
       deploymentLocation:
         ​  value: '${deploymentLocation}' ​ 
       resourceManager:
          ​ value: '${resourceManager}'       
    ...
...
...
operations:
​    Operation_1:
​        source-operation: res_R.R_op1
    Operation_2:
​        source-operation: res_R.R_op2
...

By specifying these attributes, you publish resource_R’s operations, that is, R_op1 and R_op2, by using the names Operation_1 and Operation_2 respectively.

In the descriptor for assembly_A, you might then define this relationship between assembly_B and resource_S, which calls the operations when the relationship is created and ceased:
name: assembly::assembly_A::1.0
...
composition:
  assm_B:
​    type: assembly::assembly_B::1.0
​    properties:
      ...
  res_S:
​    type: 'resource::resource_S::1.0'
    properties:
 ​     ...                   
  ...
...
relationships: 
  balanceStreamer:
​    source-capabilities: 
​    - assm_B.VideoStream
​    target-requirements: 
​    - res_S.HttpServer
​    source-state: Active
​    target-state: Active
​    properties:
​      max_connections:
​        value: '${max_connections}'
​      server_ip:
​        value: '${source.privateIp}'
​      server_port:
​        value: '8080'     
​    lifecycle:
​      Create:
​      - assm_B.Operation_1
​      Cease:
​      - assm_B.Operation_2
Customizing operations on multiple component instances
Depending on your requirements, you might need to create an assembly descriptor that contains multiple instances of a component. In the assembly descriptor, you can describe multiple component instances by taking any of the following actions:
  • In the composition section, you include a component that has one of these attributes:
    • A quantity value that is greater than 1.
    • An items value that is based on a list or a map.
    • A cluster section where the initial-quantity value is greater than 1.
      Note: A cluster that has an initial quantity of 0 or 1 might later contain multiple instances if the cluster's component is scaled out.
  • In the composition section, you include an assembly that contains multiple component instances.
  • In the references section, you refer to an existing assembly instance that contains multiple component instances.
By default, component operations that are called by relationships are called for every instance of the component. You can adjust this behavior by setting the multiple-instances attribute. The following table lists the settings for this attribute and describes the associated behavior:
Setting System behavior Usage notes/recommendations Sample use case
every The operation is called for every component instance. This setting is used by default for the operation if the multiple-instances attribute is not set.

The instance operations are not called in sequence, and therefore might run in parallel.

You want to configure distributed units (DUs) in a gNodeB base station, where each DU instance is distinct and must be configured separately.
any The operation is called for any one component instance. Use this setting when the effect of the operation on all component instances is the same regardless of which instance the operation is called for. You want to update Kafka configuration, where the configuration can be applied to any Kafka node. Kafka then ensures that the configuration is consistent across its cluster.
first The operation is called for the first component instance in a group. Use this setting when the operation is effective only if it is called on the first component instance in the group.
Restriction: If the component is part of a group that is based on a map, you cannot use this setting.
You want to update PostgreSQL database configuration, where a single PostgreSQL node distributes the updated configuration among the other nodes.
For the sample use case that involves configuring DUs in a gNodeB, you might create an assembly descriptor for the gNodeB that includes these attributes:
...
composition:
  DU:
    items: ${DU-map}
​    type: resource::dist_unit::2.1
​    properties:
        deploymentLocation:
         ​  type: string
         ​  value: test ​ 
        resourceManager:
         ​  value: brent ​ 
    ...
...
...
operations:
​    ConfigureDUforSlice:
​        source-operation: DU.Create-3gpp
        multiple-instances: every
...

In a higher-level descriptor, you might then define a relationship that involves the gNodeB assembly and calls the ConfigureDUforSlice operation. Because the multiple-instances attribute is set to every, this operation is called for every DU instance. For example, when a new gNodeB assembly is created through an intent request, this operation is called for every DU instance that is created for the gNodeB.

composition section

In the composition section, you describe the components, that is, the resources or assemblies, that make up the assembly. The components are instantiated when a new instance of the assembly is requested.

A dependency exists between this section and the references section. If the orchestration component cannot uniquely identify all of the referenced instances in the references section, the components in the composition section, and the assembly itself, cannot be instantiated.

For each component, you must define the following attributes:
Attribute Description Required Type Restrictions
<component_name>

A name of your choosing that identifies the component.

When the component is instantiated, this name becomes part of the instance name.

Y String
  • No spaces.
  • Minimum length of one alphanumeric character.
  • No periods (.).
  • Must be valid YAML.
type The type, name, and version number of the assembly or resource descriptor that the component instance is based on. Y String  
dynamicType

A Jinja expression, which is evaluated when an intent request is submitted, that determines which descriptor type to use for the component instance.

This expression can use variables, if statements, and component properties.

N   Only properties that are defined in this component can be used in the expression.
quantity

The number of instances of the component that are created.

The default value is 1.

N Integer

This value is used only in nonclustered environments.

In clustered environments, the value of the cluster.initial-quantity attribute is used instead.

properties The full set of properties that are required to orchestrate the component through to the Active state.

For information about how to create properties, see properties section.

Y    
upgrade-dependencies The components that must be upgraded or reconfigured before this component can be upgraded or reconfigured. N  

Only components that exist in this assembly descriptor can be specified.

Specified resources must include the Reconfigure or Upgrade lifecycle transitions in their descriptors.

Circular dependencies are not allowed, regardless of whether the dependency is direct or indirect. For example, if resource_1 is an upgrade dependency of resource_2, you cannot perform these actions:
  • Specify resource_2 as an upgrade dependency of resource_1.
  • Pass a property from resource_2 to resource_1.
cluster A set of attributes that specify the number of component instances that can be created in, added to, or removed from a cluster. N    
initial-quantity

The initial number of component instances that are created in the cluster.

If this value is not set, the value of minimum-nodes is used.

N Integer

Must contain a value that is between the values of minimum-nodes and maximum-nodes.

This value is used only in clustered environments. In nonclustered environments, the value of the quantity attribute is used instead.

minimum-nodes

The minimum number of instances that can exist in the cluster.

You can set this value to 0 if no instances are required at instantiation time.

The default value is 1.

N Integer Cannot be greater than the value of maximum-nodes.
maximum-nodes The maximum number of instances that can exist in the cluster. N Integer Cannot be less than the value of minimum-nodes.
scaling-increment

The number of instances that are added to, or removed from, the cluster during scaling.

The default value is 1.

N Integer  
items

The list or map that this component is instantiated from.

This list or map is defined in the higher level properties section of the assembly descriptor. To specify the list or map, use the following syntax :

${<list_or_map_name>}

N   Must match the name of a list or map that is defined in the higher level properties section for the assembly.
Examples

In this example, three components are shown with their properties omitted. The quantity of the streamer component is inherited from the numOfStreamers property, which is an assembly-wide property that is defined in the higher level properties section of the descriptor.

The upgrade-dependencies attribute specifies that, if a planned Reconfigure or Upgrade lifecycle transition is to be run on the net_video component, the transition must be run on the streamer component first.

composition:
  streamer: # The component name
​    type: resource::c_streamer::1.0
​    quantity: ${numOfStreamers}
​    properties:
​      ...

  balancer:
​    type: resource::c_balancer::1.0
​    quantity: 1
​    properties:
​      ...

  net_video:
​    type: resource::net_video::1.0
​    quantity: 1
​    properties:
​      ...
    upgrade-dependencies:
      - streamer

This example shows one component with seven properties. The values for some of the properties, such as deploymentLocation and resourceManager, are inherited from assembly-wide properties that are defined in the higher level properties section.

composition:
  streamer:
​    type: resource::c_streamer::1.0
​    cluster:
​      ...
​    properties:
​      deploymentLocation:
​        value: '${deploymentLocation}'
​      resourceManager:
​        value: '${resourceManager}'
​      flavor:
​        value: m1.small
​      server_name:
​        value: ${instance.name}
​      referenced-video-network:
​        value: ${net_video.network-id}
​      availability_zone:
​        value: DMZ
​      mgmtIp:
​        type: string
​        description: MGMT IpAddress of server
​        read-only: true

This example shows how a cluster of instances can be defined for a component. This cluster can contain a minimum of one instance and a maximum of four, and only one instance can be added or removed through scaling. The initial number of instances that are created when an intent request is submitted is inherited from the assembly-wide numOfServers property.

composition:
  streamer:
​    type: resource::c_streamer::1.0
​    cluster:
​      initial-quantity: ${numOfServers}   
​      minimum-nodes: 1                    
​      maximum-nodes: 4                    
​      scaling-increment: 1                

​    properties:
​      data:
​        value: ${data}
​      ip_address:
​        read-only: true

Defining component groups

You can specify that component instances are created from lists or maps that are defined in the higher level properties section of the descriptor.

Components that are instantiated from a list are named based on the index of the list. You can copy data from list items into component instances by using the item keyword.

Components that are instantiated from a map are named based on the keys of the map. You can copy data from the map items into component instances by combining the item keyword and the map keys.

Defining component instance groups by using lists
This example shows how the streamer component is defined from a list of three items, which is called streamers and is defined as a higher level assembly property.
properties:
  streamers:
    type: list
    entry_schema:
      type: string
      default: ['192.0.2.1', '192.0.2.2', '192.0.2.3']
...
composition:
  streamer:
​    type: resource::c_streamer::1.0
​    items: ${streamers}
​    properties:
​      data:
​        value: ${item}

When an intent request is submitted, an instance of the c_streamer resource is created for each item in the list. In each instance, the value of the data property is set to the value of the list item. For example, in the first component instance, the value of the data property is set to 192.0.2.1.

Component instances that are created from a list are named based on their index in the list. In the case of the previous example, the component instances are named as follows:

{Assembly Name}__streamer__1
{Assembly Name}__streamer__2
{Assembly Name}__streamer__3

This naming strategy applies to fixed-size and cluster component groups.

Tip: The sequencing of component list items can affect how topology differences are calculated after subsequent intent requests are submitted. If the list is reordered, or if elements are removed, the topology might appear differently, because the list index forms part of the component instance name. Other unexpected changes might also occur. If items in a list of components might be reordered or removed, consider using a map to define the component group instead.
Defining component instance groups by using maps

This example shows how the streamer component is defined from a map that is called streamers, which is defined as a higher level assembly property. This map contains three maps, each of which contains an ip_addr key-value pair.

properties:
  streamers:
    type: map
    entry_schema:
      type: map
      default:
        - {first: {ip-addr: '192.0.2.1'}}
        - {second: {ip-addr: '192.0.2.2'}}
        - {third: {ip-addr: '192.0.2.3'}}
...
composition:
  streamer:
​    type: resource::c_streamer::1.0
​    items: ${streamers}
​    properties:
​      data:
​        value: ${item.ip_addr}

When an intent request is submitted, an instance of the c_streamer resource is created for each item in the map. Each instance is named based on the assembly name, the component name, and the key of the map item. As a result, the instances have the following names:

{Assembly Name}__streamer__first
{Assembly Name}__streamer__second
{Assembly Name}__streamer__third

In each instance, the value of the data property is set to the value of the ip_addr key in each map item. For example, in the {Assembly Name}__streamer__first instance, the value of the data property is set to 192.0.2.1.

Dynamic type evaluation

You can define components so that their descriptor type is evaluated dynamically at runtime, based on information in the assembly descriptor or on properties that belong to the component instance.

Use the dynamicType attribute to specify a Jinja expression that is evaluated at runtime to determine which type is used for each instance.

In this example, the dynamic type expression uses the value of each component instance's manufacturer property to determine which descriptor to use for the instance.

properties:
  streamers:
    type: list
    entry_schema:
      type: map
...
composition:
  streamer:
​    type: resource::c_streamer::1.0
    dynamicType: >-
      {% if manufacturer == 'Netflix' %}
        {{ 'resource::netflix_streamer::1.0' }}
      {% else %}
        {{ 'resource::basic_streamer::1.0' }}
      {% endif %}
​    items: ${streamers}
​    properties:
      manufacturer:
        value: ${item.mfr}
​      ip_addr:
​        value: ${item.ip_addr}

For components that are instantiated from lists and maps, dynamic type expressions can enable different instances within the same group to have different types. However, for fixed-size groups and clusters, all instances have the same type.

Note: The type attribute must still be specified. You can use type to specify a common descriptor that is suitable in all use cases, then specify a dynamic type expression when you want to use different descriptors in particular use cases.
Resolving descriptor versions dynamically

You can also resolve the version of a descriptor based on the available descriptors in the system. To resolve the descriptor version, use a resolveVersion filter in the dynamic type expression.

In this example, if the value of the component instance's serverType property is Dell, the resolveVersion filter determines that any version of the t_simple descriptor from 1.0 onwards can be used.

composition:
  A:
    type: resource::not_found::1.0
    dynamicType: >-
      {% if serverType == 'Dell' %}
        {{ 'resource::t_simple::[1.0,)'|resolveVersion }}
      {% elif serverType == 'Supermicro' %}
        {{ 'resource::t_simple_indexed::1.0' }}
      {% else %}
        {{ 'resource::t_simple::1.0' }}
      {% endif %}
    items: ${data}

To determine the version, the resolveVersion filter is run on the text in quotes that precedes the pipe symbol (|) , that is, 'resource::t_simple::[1.0,)'. The filter resolves the version range, that is, [1.0,). This range indicates that the earliest version that can be used is 1.0, and any later version can be used. The filter then queries the Apollo descriptor catalog to identify the descriptor to use.

The version range must be specified by using the Maven version range syntax. You must provide at least a minimum version or a maximum version. For more information about acceptable Maven version ranges, see Version Range References in the Oracle documentation.

Restriction: The dynamicType expression is evaluated when an intent request is submitted. If a new descriptor version that matches the expression is added since the last update for an assembly, the descriptor version that is used for the component might change. This change might cause resources to be reconfigured or reinstalled.

references section

In the references section, you describe existing assembly or resource instances that you want to refer to elsewhere in the descriptor. You can describe two types of referenced instance:
  • Assembly instances that the orchestration component created.
  • Externally-managed resource instances.

You can refer to these instances when you specify component properties or create relationships.

When you describe a referenced instance, you must provide sufficient detail to enable the orchestration component to uniquely identify the instance. All referenced instances must be identified before the components in the composition section can be instantiated. If any referenced instance cannot be identified, the assembly is not instantiated.
Note: The descriptors for externally-managed resource instances do not include the Install and Uninstall lifecycle transitions.

For each referenced instance, you must define the following attributes:

Attribute Description Required Type Restrictions
<referenced_instance_name>

A name of your choosing that is used to refer to the instance in this descriptor.

Y String
  • No spaces.
  • Minimum length of one alphanumeric character.
  • No periods (.).
  • Must be valid YAML.
type

The type, name, and version number of the referenced instance's descriptor.

You can use semantic versioning to specify the version number.

Y String  
properties

The properties that the orchestration component uses to uniquely identify the instance.

If the orchestration component created the instance, you can refer to any of the properties in the instance's descriptor.

You must include the deploymentLocation and resourceManager properties, and set their values to the corresponding properties that are defined in the higher level assembly properties section.

For more information about the properties that you can specify, see properties section.

Y    
Examples
In this example, two referenced instances are described. For the storage instance, the following information is provided to help the orchestration component to uniquely identify the instance:
  • The type, name, and version of the instance's assembly descriptor. The exact version is not specified, but semantic versioning is used to restrict the range to versions between 1.0 and 1.9.
  • Three property values, that is, deploymentLocation, resourceManager, and storage-name, that are defined in the higher level properties section of this assembly.

For the ucd_network instance, the following information is provided:

  • The type, name, and version number of the referenced instance's resource descriptor.
  • Three higher level assembly properties, that is, deploymentLocation, resourceManager, and management-network-name.
references:

  storage: # reference to an existing assembly instance
​    type: assembly::storage_cluster::^1.0
​    properties:
​      deploymentLocation:
​        value: '${deploymentLocation}'
​      resourceManager:
​        value: '${resourceManager}'
​      name:
​        value: '${storage-name}'

  management-network: # reference to a neutron network that was not created by the orchestration component 
​    type: resource::ucd_network::1.0
​    properties:
​      deploymentLocation:
​        value: '${deploymentLocation}'     
​      resourceManager:
​        value: '${resourceManager}' 
​      name: 
​        value: ${management-network-name}  

After the orchestration component identifies these instances, their properties can be accessed by using this syntax: ${<referenced-item-name>.<property-name>}

For example, when you describe a component that is called balancer, you might specify these attributes to access the id property from a referenced instance that is called management-network:
composition:
  balancer:
​    type: 'resource::c_balancer::1.0'
​    quantity: '1'
​    properties:
​      …
​      referenced-management-network:
​        value: '${management-network.id}'

relationships section

In the relationships section, you define the relationships that you want to create between source and target components. For each relationship, you provide the following information:
  • The capabilities that the source component provides.
  • The capabilities that the target component requires.
  • The state that each component must reach for the relationship to be created.
  • The operations that are run when the relationship is created and when it is ceased.
For each relationship, you must define the following attributes:
Attribute Description Required Type Restrictions
<relationship_name> A name of your choosing that identifies the relationship. Y String
  • No spaces.
  • Minimum length of one alphanumeric character.
  • No periods (.).
  • Must be valid YAML.
source-capabilities

A list of capabilities that the source component provides.

You use this attribute to identify the source component in the relationship.

If the source component is an assembly component that is described in the composition section, specify each capability in this format: <source-component-name>.<capability-name>

If the source component is a referenced instance that is described in the references section, specify the instance name.

Y String
For assembly components, these restrictions apply:
  • <source-component-name> must match the name of a component that is defined in the composition section of this assembly descriptor.
  • <capability-name> must match name of a capability that is defined in the capabilities section of the source component's descriptor.

For referenced instances, the instance name must match the name that is used in the references section.

target-requirements

A list of capabilities that the target component requires.

You use this attribute to identify the target component in the relationship.

Specify each requirement in this format: <target-component-name>.<requirement-name>

Y String
  • <tqrget-component-name> must match the name of a component that is defined in the composition section of this assembly descriptor.
  • <requirement-name> must match name of a requirement that is defined in the requirements section of the target component's descriptor.
source-state

The state that the source component must reach for the relationship to be created.

By default, the relationship is created after the source component reaches this state. You can change this order of events by using the source-state-modifier attribute.

The relationship is always ceased before the source component leaves this state.

Y String
Must be one of these values:
  • Installed
  • Inactive
  • Active
  • Broken
source-state-modifier

A value that determines whether the relationship is created before or after the source component reaches the state that is specified in source-state.

To create the relationship before the source state is reached, set this attribute to pre, otherwise set it to post.

If this attribute is not specified, the relationship is created after the source state is reached.

N  
Must be one of these values:
  • pre
  • post
target-state

The state that the target component must reach for the relationship to be created.

By default, the relationship is created before the target component reaches this state. You can change this order of events by using the target-state-modifier attribute.

The relationship is always ceased before the target component leaves this state.

Y String
Must be one of these values:
  • Installed
  • Inactive
  • Active
  • Broken
target-state-modifier

A value that determines whether the relationship is created before or after the target component reaches the state that is specified in target-state.

To create the relationship after the target state is reached, set this attribute to post, otherwise set it to pre.

If this attribute is not specified, the relationship is created before the target state is reached.

N  
Must be one of these values:
  • pre
  • post
properties

The properties that apply to this relationship.

These properties can be used by the operations that you specify in the lifecycle.Create and lifecycle.Cease attributes.

You can inherit the values of source or target component properties by using the syntax source.<source-component-property-name> or target.<target-component-property-name>.

For more information about the properties that you can specify, see properties section.

Y    
lifecycle

The lifecycle transitions that this relationship supports, and the operations that are run for each transition.

Only the Create and Cease transitions are supported.

Y    
Create

The operations that are called when the relationship is created. These operations are called one at a time in the specified order.

Specify source operations in the format source.<operation-name> and target operations in the format target.<operation-name>.

Y String

Each operation name must match the name of a defined operation in the operations section of the component's descriptor.

Cease

The operations that are called when the relationship is ceased. These operations are called one at a time in the specified order.

Specify source operations in the format source.<operation-name> and target operations in the format target.<operation-name>.

Y String

Each operation name must match the name of a defined operation in the operations section of the component's descriptor.

Note: If the component is, or contains, a resource that has multiple instances, you can decide whether the operation is called for every instance by setting the multiple-instances attribute in the operations section. For more information, see Customizing operations on multiple component instances.
Example
This example shows how you might configure a relationship between two components.
  • The source component, that is, storage, provides the NFSMountpoint capability. The target component, that is, streamer, requires the RemoteNFSMountPoint capability.
  • The relationship is created after the storage component reaches the Active state and before the streamer component reaches the Inactive state.
  • When the relationship is created, the MountStorage operation, which is defined in the descriptor that the storage component is based on, is called.
  • The relationship contains several properties, including remote_nfs_server_ip, which inherits the value of the privateIp property that is defined for the storage component. These properties can be used by the MountStorage and UnmountStorage operations.
  • The relationship is ceased before the storage component leaves the Active state and before the streamer component leaves the Inactive state.
  • When the relationship is ceased, the UnmountStorage operation, which is defined in the descriptor that the storage component is based on, is called.
relationships: 

  nfs_mount:
​    source-capabilities: 
​    - storage.NFSMountpoint

​    target-requirements: 
​    - streamer.RemoteNFSMountPoint

​    source-state: Active
​    target-state: Inactive

​    properties:
​      remote_nfs_port:
​        value: '2049'
​      remote_nfs_server_ip:
​        value: '${source.privateIp}'
​      remote_mount_point:
​        value: '/'
​      local_mount_point:
​        value: '/mnt'    

​    lifecycle:
​      Create:
​      - source.MountStorage
​      Cease:
​      - source.UnmountStorage

data-types section

In the data-types section, you define customized data types that you can use in descriptor properties. You can define data types for sub-properties within a property, or specify that a property derives a data type from another property.

Derived data types are specified in a derived-from attribute. A data type that derives properties from another data type derives all of the other data type's properties. In addition, data types can reuse other data types in their properties. In this way, you can build up a complex data type hierarchy.

For each data type, you can define the following attributes:
Attribute Description Required Type Restrictions
<data_type_name> A name of your choosing that describes the customized data type. Y String
  • No spaces.
  • Minimum length of one alphanumeric character.
  • No periods (.).
  • Must be valid YAML.
derived-from

The name of another customized data type that this data type is derived from.

All of the other data type's properties are inherited by this data type.

N Customized data type Must match the name of another data type that is defined in this section.
properties The properties of this customized data type.

These properties can include other customized data types.

For information about how to create assembly descriptor properties, see properties section.

For information about how to create resource descriptor properties, see properties and private-properties sections.

Y    
Examples

This example defines a customized data type, tag, which has one property, that is, name. In the server component properties, in the tags list, the entry-schema property is used to specify tag as the data type of each value in the list.

server:
  properties:
    tags:
      type: list
      entry-schema:
        type: tag

tag:
  properties:
    name:
      type: string
In an assembly instance, the server component might contain data that is similar to this sample data:
"server": [
   "tags": [
            {
              "name": "clustered"
            }
            {
              "name": "non-clustered"
            }
          ]
The following example shows a data type hierarchy that contains these data types:
  • Five standard data types, base-type, vnf-cpd, ns-cpd, pnf-cpd, and virtual-link-desc, which include properties that have a type of string.
  • Two data types associated-cpd-id and cpd-or-vl, which reuse some of the five standard data types. For example, associated-cpd-id reuses the vnf-cpd, pnf-cpd, and ns-cpd data types.
  • The sap-type data type, which derives properties from the data type base-type.
    • Because base-type contains a single property, source, sap-type inherits this property.
    • In the properties for the sap map, the entry-schema property is used to specify sap-type as the data type of each value in the map.

properties:
  sap:
    type: map
    entry-schema: sap-type
    default:
      id: "ident1"
      address-segment: "segment"
      cpd-or-virtual-link: 
      ...
  ...
data-types:
  base-type:
    properties:
      source:
        type: string
        description: details about the source of an object

  vnf-cpd:
    properties:
      vnfd-id:
        type: string
      ext-cpd-id:
        type: string
  
  ns-cpd:
    properties:
      ns-id:
        type: string
      ext-cpd-id:
        type: string   
        
  pnf-cpd:
    properties:
      pnf-id:
        type: string
      ext-cpd-id:
        type: string 

  virtual-link-desc:
    properties:
      virtual-link-desc:
        type: string

  associated-cpd-id:
    properties:
      vnf:
        type: vnf-cpd
      pnf:
        type: pnf-cpd
      ns:
        type: ns-cpd

  cpd-or-vl:
    properties:
      virtual-link-desc:
        type: virtual-link-desc
      associated-cpd-id:
        type: associated-cpd-id

  sap-type:
    derived-from: base-type
    properties:
      id: 
        type string
      address-assignment:
        type: string
      cpd-or-virtual-link:
        type: cpd-or-vl

capabilities and requirements sections

In the capabilities section of a component's descriptor, where a component is a resource or an assembly, you define the functions that the component instance provides. In the requirements section, you define the capabilities that the component instance requires to work successfully.

For example, a capabilities section might state that a component instance provides incoming HTTP requests. A requirements section might state that networks of various types are required for a component instance to work.

For each capability and requirement, you must define the following attributes:
Attribute Description Required Type Restrictions
<capb_or_req_name> A name of your choosing that describes the capability or requirement. Y String
  • No spaces.
  • Minimum length of one alphanumeric character.
  • No periods (.).
  • Must be valid YAML.
type

For capabilities, a term of your choosing that indicates the function that this component instance provides.

For requirements, the defined type of the capability that is required for this component instance to work.

If possible, use common industry terms.

Y String  
Examples
In this example, two capabilities that a component instance provides are described:
  • An HTTP response output stream, which is identified by the name VideoStream.
  • An API to the OpenStack Networking service, which allows network objects to be created within OpenStack, and which is identified by the name Network.
capabilities:
​    VideoStream:
​        type: httpStreamOutput
​    Network: 
​        type: neutronNetwork
In this example, these capabilities are described as requirements that a component instance requires to work successfully:
  • The requirement for a HTTP response output stream is identified by the name HttpOutputStream.
  • Requirements for API access to the OpenStack Networking service are identified by the names VideoNetwork and ManagementNetwork.
requirements:
​    HttpOutputStream:
​        type: httpStreamOutput
​    VideoNetwork:
​        type: neutronNetwork
​    ManagementNetwork:
​        type: neutronNetwork