OASIS Mailing List ArchivesView the OASIS mailing list archive below
or browse/search using MarkMail.

 


Help: OASIS Mailing Lists Help | MarkMail Help

tosca message

[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]


Subject: RE: [tosca] Operation implementations


Thanks Tal, these examples are extremely helpful. Most of the examples in the Version 1.3 specification show the use of bash scripts to provide implementations for interface operations. Your gRPC and SSH examples, together with Adamâs Ansible examples, will help guide us towards a general-purpose approach for specifying operation implementations.

You email touches on most of the issues that need clarification. Iâll try to organize my comments by topic:

The proposed grammar is actually very simple. Instead of "implementation" having that weird structure of a "primary" string and a string-list of "dependencies", which semantically were never very clear, I suggest that it be given a data type using a "type" keyword for both the operation definition and assignment (a refinement of the definition).

 

I believe there are actually two separate issues here:

  1. Do operation implementations need to be âsplit upâ into a âprimaryâ implementation and a list of âdependentâ implementations?
  2. Do operation implementations need to be typed, and if so, how to we specify the âtypeâ of the operation implementation

Iâll offer my perspectives on each of these separately:

Implementation Operation Types

This data type provides syntactical validation for "implementation", but more importantly it represents the processor's mechanism for reacting to the operation event.

 

I donât think there is any disagreement about whether operation implementations should be typed. TOSCA uses types for (almost) everything, and operation implementations should be no exception. As you correctly point out, the type of the operation implementation serves two purposes:

  1. It is used for validation (i.e., to ensure correctness of profiles and service templates at design time)
  2. The orchestrator determines how to handle the operation implementation (i.e., which code to execute) based on its type.

The question then is not whether operation implementations should be typed, but rather which type to use. TOSCA v1.3 uses Artifact Types for this purpose, and youâre proposing to use data types instead. Iâll use your ssh example to compare the two.

Implementations Operations use Data Types

You state that:

The ssh implementation is actually quite similar, but it does have optional support for artifacts. The idea is that the artifacts would be copied over (scp) before executing the ssh command. Here we can extract TOSCA artifacts from the CSAR using the "get_artifact" function or just use any file from a URL.

 

Iâm showing your topology template code snippet here (without the gRPC part):

topology_template:

  node_templates:

    server:

      type: Server

      interfaces:

        Maintenance:

          operations:

            maintenance-off:

              implementation:

                command: [ /usr/bin/bash, -c, '/home/tosca/off.sh $INPUTS > off.log' ]

                files:

                - { get_artifact: [ SELF, main ] }

                - { get_artifact: [ SELF, utils ] }

                - http://acme.org/utils/lib.tar.gz

              inputs:

                # This will be packed as "--immediate=true" in $INPUTS

                immediate: true

      artifacts:

        main:

          file: off.sh

          deploy_path: /home/tosca

        utils:

          file: utils.sh

          deploy_path: /home/tosca

 

Implementation Operations use Artifact Types

Using existing Version 1.3 grammar, your example would be written as follows:

topology_template:

  node_templates:

    server:

      type: Server

      interfaces:

        Maintenance:

          operations:

            maintenance-off:

              implementation:

                type: SSH # artifact type, not data type

                file: off.sh

                properties:
                  address: { get_attribute: [ SELF, address ] }
                  credentials: { get_input: credentials }
                inputs:

                  immediate: true

 

In this example, the âprocessorâ for the SSH artifact type would copy the âoff.shâ script to the remote server (using the provided credentials) and execute it there.

A couple of notes about this snippet:

  1. This snippet shows the use of an inline artifact definition as the operation implementation. An alternative would be to define a named artifact in the âartifactsâ section of the server node template and then reference this named artifact by name in the operation implementation definition.
  2. I know this snippet doesnât handle the âutilsâ files that need to be deployed on the remote server first. In the Version 1.3 spec, that is what the âdependentâ artifacts are for. Iâll discuss this in the next section

 

I believe the existing Artifact Types approach has a lot of advantages over using data types:

 

  1. The TOSCA metamodel uses different âtypes of typesâ for different purposes. Node types and relationship types are used for the topology graph. Capability types are used to terminate relationships. Data types are used do define the âschemasâ of parameters, properties, and attributes. The semantics of a type is derived from the type of the type. If we start overloading what the purpose is of certain types (e.g., by using data types not just for properties and artifacts, but also for operation implementations), then we lose the clean semantics associated with types of types.
  2. In your example, you define (âdeclareâ) the type of the operation implementation in the interface type definition as follows:

interface_types:

  Maintenance:

    operations:

      maintenance-off:

        type: SSH # data type for the implementation. if not declared, must be set in assignment

        inputs:

          immediate:

            type: bool

I believe this is not good practice. An interface type definition should be just that: a definition of the set of operations that can be called within the context of that interface. Interface types should not have an opinion about how they should be implemented. Specifically, they should not restrict the type of their implementations.

  1. More importantly, your approach for handling the properties of the operation implementation data types is invalid TOSCA, in my opinion, and will not work. Here is your âRemoteâ data type definition:

data_types:

  Remote:

    properties:

      address:

        type: string

        default: { get_attribute: [ SELF, address ] }

      credentials:

        type: Credentials

        default: { get_input: credentials }

The âaddressâ property has a default value that references SELF. However, data type definitions do not have a SELF context. That context is only available inside node types or relationship types. In my opinion (and in my implementation) this is invalid TOSCA. This type definition is only valid when it happens to be used inside a node or relationship that happens to have an address attribute of type string and inside a service template that has a credential input of type Credential. We donât know if thatâs the case until an attribute or property of that type is defined inside a node or relationship somewhere. If weâre going to have a typed language, we should be able to validate those types at design time without having to wait until âuse timeâ to see if things will work.

 

Primary and Dependent Artifacts

As I mentioned earlier, my SSH Artifact type doesnât handle the extra files that need to be deployed on the remote server first. While it would be trivial to extend the SSH Artifact type do handle this, the Version 1.3 spec actually anticipates this use case by supporting âdependentâ artifacts in addition to a âprimaryâ artifact. My understanding of the spec is that the list of dependent artifacts are processed first (in the order in which they are defined) and then the primary artifact is processed.

In my implementation, I define a Deployment artifact type for âdeployingâ files onto remote servers. Using this Deployment artifact type, the example above would be supported as follows:

 

topology_template:

  node_templates:

    server:

      type: Server

      interfaces:

        Maintenance:

          operations:

            maintenance-off:

              implementation:

                primary:

                  type: SSH # artifact type, not data type

                  file: off.sh

                  properties:

                    address: { get_attribute: [ SELF, address ] }

                    credentials: { get_input: credentials }

                  inputs:

                    immediate: true

                dependencies:

- type: Deployment

                    file: util.sh

                    properties:

                      address: { get_attribute: [ SELF, address ] }

                      credentials: { get_input: credentials }

                      deploy_path: /home/tosca

 

This results in the âutilsâshâ being deployed into the /home/tosca directory on the remote server, where it can then be used by the âoff.shâ script.

This approach is perhaps slightly more verbose, but it has the advantage that the operation implementation types (i.e., the artifact types) can be designed to be a collection of primitive, re-usable types that can be combined as necessary to provide a full implementation.

On the flip side, what I donât like about this grammar is the artificial distinction between a primary operation implementation and dependent operation implementations. I would much rather see us eliminate the âprimaryâ and âdependenciesâ keywords, and just support lists of operation implementations in the âimplementationâ keyword. As an optimization, we could also support a single operation implementation definition (instead of a list) if only one single operation implementation is required.

Operation Implementations that are not Artifacts

Hopefully the discussion so far has shown that the existing operation implementation grammar supports most of the functionality in your examples. This brings us to the one feature that is not easily supported: how do we specify operation implementations that are not artifacts (i.e., where there is no file that contains the code that needs to be executed or the commands that need to be processed).

The gRPC call has nothing to do with artifacts per se. However, it is expected that there is a gRPC client somewhere that can handle these calls, perhaps statically compiled from a ".proto" file, which perhaps can be included in the CSAR. But that ".proto" file defines the entire membrane, not individual calls. It's also possible to imagine a ".proto"-to-TOSCA translator that would create derived data types and for each supported call and one big interface type.

 

Your gRPC example illustrates this use case. By the way, I believe your example shows grammar that is slightly different from what you intended. If my understanding is correct, the following snippet is grammatically more correct (i.e., the type is specified inside the âimplementationâ block, not the other way around, and the âpropertiesâ keyword is required):

topology_template:

  node_templates:

    server:

      type: Server

      interfaces:

        Maintenance:

          operations:

            maintenance-on:

              implementation:

                type: GRPC

                properties:

                  rpc: StartMaintenanceWithMode

                  timeout: 10 s

              inputs:

                mode: production

 

Calin and I suggested during last weekâs language ad-hoc that this use case can be supported if current artifact definition grammar were extended with an âinlineâ keyword. If the âinlineâ keyword is present, then the âcommandâ or the âexecutableâ or the âscriptâ (or whatever you want to call it) is specified directly inside the TOSCA grammar rather than being retrieved from a file. If the âfileâ keyword is specified, then the contents of that file are used as the command or executable or script. The resulting grammar could look as follows:

topology_template:

  node_templates:

    server:

      type: Server

      interfaces:

        Maintenance:

          operations:

            maintenance-on:

              implementation:

                type: GRPC

                inline: StartMaintenanceWithMode

                properties:

                  timeout: 10 s

              inputs:

                mode: production

 

In this example, the âStartMaintenanceWithModeâ is the gRPC command that needs to be executed, the âmodeâ is an input to that command, and the âtimeoutâ is a value that is provided to the gRPC client.

While I believe this approach will work just fine for all use cases, I understand the problem related to terminology. It is absolutely confusing to use the word âartifactâ to describe an operation implementation that are not artifacts. To avoid this confusion, I would support using a different type of types (different from artifact types) for operation implementations. We could use the term Implementation Type for this purpose.

Summary

As you pointed out, there are more issues that need to be resolved, but hopefully we can come to agreement on some of the basics. Here is my recommendation:

  • Introduce a new Implementation Type that must be used when defining operation implementations (instead of the current Artifact Types)
  • This implementation type defines implementations using either a âfileâ keyword or an âinlineâ keyword (or âcommandâ, or âexecâ, or something rational), where only one or the other is allowed
  • The Implementation Type is used by the orchestrator to determine how the operation must be âcalledâ (or âinvokedâ, or âprocessedâ, or âexecutedâ, or whatever your preferred terminology is).
  • We eliminate the distinction between âprimaryâ and âdependenciesâ, and instead support an ordered list of operation implementations (as well as a single operation implementation).

 

I believe this proposal preserves most of the Version 1.3 grammar while addressing the objections to this grammar that have been raised to date.

Chris

 

 

 

 

From: tosca@lists.oasis-open.org <tosca@lists.oasis-open.org> On Behalf Of Tal Liron
Sent: Thursday, September 30, 2021 10:43 AM
To: Calin Curescu <calin.curescu@ericsson.com>
Cc: Chris Lauwers <lauwers@ubicity.com>; adam souzis <adam@souzis.com>; tosca@lists.oasis-open.org
Subject: Re: [tosca] Operation implementations

 

Sorry it took me a while to write this up, but finally here is a draft of what I mean by "typed implementations".

 

The proposed grammar is actually very simple. Instead of "implementation" having that weird structure of a "primary" string and a string-list of "dependencies", which semantically were never very clear, I suggest that it be given a data type using a "type" keyword for both the operation definition and assignment (a refinement of the definition).

 

This data type provides syntactical validation for "implementation", but more importantly it represents the processor's mechanism for reacting to the operation event.

 

In the example below I'm showing a node with two operations. One of them is a gRPC call and the other is remote execution using ssh. Both of them are "remote", and the default for the Remote data type from which they both derive assumes that the node has an "address" attribute. However, this default address can be overridden and given a different value if necessary, for example if the node is not a server itself, or if another component (an orchestrator) is handling execution.

 

The gRPC call has nothing to do with artifacts per se. However, it is expected that there is a gRPC client somewhere that can handle these calls, perhaps statically compiled from a ".proto" file, which perhaps can be included in the CSAR. But that ".proto" file defines the entire membrane, not individual calls. It's also possible to imagine a ".proto"-to-TOSCA translator that would create derived data types and for each supported call and one big interface type.

 

The ssh implementation is actually quite similar, but it does have optional support for artifacts. The idea is that the artifacts would be copied over (scp) before executing the ssh command. Here we can extract TOSCA artifacts from the CSAR using the "get_artifact" function or just use any file from a URL. One issue is what to do with inputs: setting environment variables may be impossible (PermitUserEnvironment is set to false for most sshd deployments). So, what I am suggesting here is that the processor would know how to pack the inputs into a command-line-compatible string. The result is something which I think is most similar to the spirit of the existing "primary + dependencies" structure of "implementation", but I did want to present some of the complexities regarding where the command runs and how to construct it.

 

Also note a simple use of metadata in order to configure specific aspects of execution for specific orchestrators.

This approach raises a more general issue regarding interfaces in TOSCA. In much of our thinking and examples until now we've treated interfaces as a design element coming from TOSCA. In the Simple Profile specifically they represent the lifecycle as defined by TOSCA. So, our job in that case is to then map that paradigm onto whatever exists in the real world. This mapping needs to be done not just in TOSCA but likely also with some kind of adapter layer. In Ubicity this layer is a collection of executable scripts. But I want to suggest that there's another approach in which we actually start from the real world and find ways to expose it to TOSCA. For example above I mention that an existing ".proto" file (or a Swagger schema) can be translated into TOSCA. In this approach the designer actually has no freedom to create arbitrary interfaces, because they are defined by the environment, and there's no adapter layer: it is a direct membrane to the environment. TOSCA's job, then, is to map data from the topology into that given interface. And that's where better topology traversal functions (get_property/get_attribute) would be really useful.

 

There are a lot of issues I'm not addressing here, but I want to start the discussion simply!

 

The example:

 

interface_types:

  Maintenance:
    operations:
      maintenance-on:
        inputs:
          mode:
            type: string
            constraints:
            - valid_values: [ staging, production ]
      maintenance-off:
        type: SSH # data type for the implementation. if not declared, must be set in assignment
        inputs:
          immediate:
            type: bool

data_types:

  Remote:
    properties:
      address:
        type: string
        default: { get_attribute: [ SELF, address ] }
      credentials:
        type: Credentials
        default: { get_input: credentials }

  GRPC:
    derived_from: Remote
    properties:
      rpc:
        type: string
      timeout:
        type: scalar-unit.time
        required: false
      user-agent:
        type: string
        default: TOSCA

  SSH:
    derived_from: Remote
    metadata:
      myorchestrator.executor: fabric
    properties:
      command:
        type: list
        entry_schema: string
      work-dir:
        type: string
        default: /tmp
      files:
        description: Copy over these files (optional)
        type: list
        entry_schema: string

node_types:

  Server:
    interfaces:
      Maintenance:
        type: Maintenance

topology_template:

  node_templates:

    server:
      type: Server
      interfaces:
        Maintenance:
          operations:
            maintenance-on:
              type: GRPC
              implementation:
                rpc: StartMaintenanceWithMode
                timeout: 10 s
              inputs:
                mode: production
            maintenance-off:
              implementation:
                command: [ /usr/bin/bash, -c, '/home/tosca/off.sh $INPUTS > off.log' ]
                files:
                - { get_artifact: [ SELF, main ] }
                - { get_artifact: [ SELF, utils ] }
                - http://acme.org/utils/lib.tar.gz
              inputs:
                # This will be packed as "--immediate=true" in $INPUTS
                immediate: true
      artifacts:
        main:
          file: off.sh
          deploy_path: /home/tosca
        utils:
          file: utils.sh
          deploy_path: /home/tosca

 

 



[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]