-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Harmonize condition syntax across workflows and policies #122
Comments
The following is an example of what an harmonized syntax could look like. It eliminates the
|
We should be able to use functions, and particularly the new custom functions #123 in these conditions. As discussed, we could allow a function both in the map-key side of a condition and after Custom functions can return a boolean type result and of course that could be compared with This suggestion is related to #80 and #119 a regexp match condition #71 would also be obvious, and perhaps easier to express as a function. The argument against this was that expressions with functions in TOSCA could create very cluttered syntax, but compare with #69. If we adopt the Each existing TOSCA constraint, like I am assuming that the Examples The proposal above has: ...
- assert:
- [ server, CAPABILITY, service, level ]: [ { equal: excellent } ] Using functions that would become: ...
- assert:
- $equal: [ [ server, CAPABILITY, service, level ], excellent ] I don't think that reads any worse than the original proposal, and it can be easily distinguished from the legacy condition syntax of TOSCA by the (unescaped) The negated constraint would read: ...
- assert:
- $not: [$equal: [ [ server, CAPABILITY, service, level ], excellent ] ] Or we could compare two paths: ...
- assert:
- $equal: [ [ server, CAPABILITY, service, level ], [ client, RELATIONSHIP, server, state ] ] Of course orchestrators could get creative and introduce ...
- assert:
- $or:
- $equal: [ [ server, CAPABILITY, service, level ], excellent ]
- $equal: [ [ client, RELATIONSHIP, server, state ], DOWN ] ] Of course there are contexts in which you wouldn't be able to call some functions - for example A Ok - this will be postfix syntax for conditions instead of the current sort-of infix, but people have lived with postfix in spreadsheets for decades, so I think they can live with it. |
I like this proposal a lot. It suggests we should use Boolean functions (rather than constraint clauses) as the building blocks for condition statements. Can we start with this proposal and investigate if we can retrofit filters to use similar syntax as well? |
Agree, I think we should use the boolean functions, and also use the automatic functional equivalents of the constraint operators. An advantage to have the functional form is that we don't need the assert keyname (it was just positional sugar anyway). Also, in line with Tal's suggestion we should also keep the get_propery function to access the respective property, i.e. condition:
- $or:
- $equal: [ {get_property: [server, CAPABILITY, service, level]}, excellent ]
- $equal: [ {get_attribute: [client, RELATIONSHIP, server, state]}, down ] We should use the same syntax for the node_filter. In the node_filter case we need a specific keyword(s) to represent the initial context that the tosca_path for that filter starts from. Now, when using a node_filter, the TOSCA processor needs to: a) create a list with all node candidates, b) apply the filter on each of them, c) choose one of the filtered candidates. Thus, the specific tosca_path keyword will always point to the current filter candidate as initial context. I think we need two keywords: NODE_FILTER and CAPABILITY_FILTER. The first one is used when we want to filter on the properties of the node when we know the node type. The second should be used when we want to filter on the properties of the target capability in a target node (for which we might not even know the node type). Note that nothing stops us to use both starting filtering contexts within the same requirement node_filter: node_filter:
- $or:
- $equal: [ {get_property: [CAPABILITY_FILTER, level]}, excellent ]
- $equal: [ {get_property: [NODE_FILTER, id]}, 37 ]
- $equal: [ {get_property: [NODE_FILTER, CAPABILITY, transcoding, codec]}, mpeg ] Note that in the first get_property (in the example above) we point to the target capability of the requirement (without even knowing the symbolic name of that capability in the node type), while the last get_property we point to a specific named capability of the node that may or may not be the target capability of the requirement. The advantage of defining the NODE_FILTER separate from the CAPABILITY_FILTER is that we can use the NODE_FILTER form unchanged also within a node_filter in a node definition that uses the [select] directive. In such a node_filter we cannot use the CAPABILITY_FILTER as there is no relationship construction. Regarding constraints I think we should add the (recursive) operators: and, not, or, xor. Also we should add more operators for string/list/map constraints such as: |
Potential use of filter in a "select" directive node. with SELF as the initial context keyword: mysql_compute:
type: Compute
directives: [ select ]
node_filter:
- $equal: [ {$get_property: [SELF, CAPABILITY, host, num_cpus]}, 2 ]
- $greater_or_equal: [ {$get_property: [SELF, CAPABILITY, host, mem_size]}, 2 GB ]
- $equal: [ {$get_property: [SELF, CAPABILITY, os, architecture]}, x86_64 ]
- $equal: [ {$get_property: [SELF, CAPABILITY, os, type]}, linux ]
- $equal: [ {$get_property: [SELF, CAPABILITY, os, distribution]}, ubuntu ] |
We acknowledge that the output of node_filter conditions is a set of nodes which match the given criteria. c.f. SQL the 'where' clause is implict here. At present the selection of a single node from within that set is delegated to the orchestrator, c.f. SQL 'order by' clause. mysql_compute:
type: Compute
directives: [ select ]
node_filter:
- where:
- $equal: [ {$get_property: [SELF, CAPABILITY, host, num_cpus]}, 2 ]
- $greater_or_equal: [ {$get_property: [SELF, CAPABILITY, host, mem_size]}, 2 GB ]
- $equal: [ {$get_property: [SELF, CAPABILITY, os, architecture]}, x86_64 ]
- $equal: [ {$get_property: [SELF, CAPABILITY, os, type]}, linux ]
- $equal: [ {$get_property: [NODE_FILTER, CAPABILITY, os, distribution]}, ubuntu ]` |
@pmjordan Not everyone would be using an SQL database, or a database at all, so this would be a lot to require. On the other hand, we easily run topologies with 1M nodes and clearly it would not be useful to have to load everything into memory and execute the filtering there. On top, we are able to do node filtering on our equivalent of This becomes relevant when we consider a generic "objective function" because a generic objective function would be impossible to optimize for database queries. Instead, I would prefer the ability to sort on properties - and any objective function that can be expressed in a filter could also be "crystalized" into one or more objective properties. That would allow optimization by database sorting. |
@pmbruun I was not intending to actually use SQL nor a database. I was just comparing the syntax, noting that there are separate clauses for a) deriving the set of matching nodes and b) selecting from that set. |
To summarize the discussion in the last Language WG: An alternative to use the keywords CAPABILITY_FILTER (as initial context for capabilities in requirements filters) and NODE_FILTER (as initial context for nodes in both requirements filters or filters in nodes with "select" directive) we could use:
mysql_compute:
type: Compute
directives: [ select ]
node_filter:
- where:
- $equal: [ {$get_property: [SELF, id]}, 37 ]
- $equal: [ {$get_property: [SELF, CAPABILITY, host, num_cpus]}, 2 ]
- $greater_or_equal: [ {$get_property: [SELF, CAPABILITY, host, mem_size]}, 2 GB ]
node_filter:
- $or:
- $equal: [ {get_property: [REQ_FILTER, CAPABILITY, level]}, excellent ]
- $equal: [ {get_property: [REQ_FILTER, TARGET, id]}, 37 ]
- $equal: [ {get_property: [REQ_FILTER, TARGET, CAPABILITY, transcoding, codec]}, mpeg ] |
In my opinion, there is no need for new keywords. The existing
|
The propsal from @lauwers above seems to be quite elegant, where the SELF within the To update the above example to the actualized tosca_path and filter syntax: node_templates:
basic_server:
type: StreamingServer
capabilities:
service:
properties:
content: basic
premium_server:
type: StreamingServer
capabilities:
service:
properties:
content: premium
subscriber:
type: Subscriber
properties:
name: { $get_input: name }
content: { $get_input: content }
requirements:
- stream:
relationship:
properties:
subscriber_name: { $get_property: [ SELF, SOURCE, name ] }
quality: { $get_input: quality }
node: StreamingServer
node_filter:
- $equal:
- $get_property: [SELF, CAPABILITY, content]
- $get_property: [SELF, SOURCE, content] Note, that since in this case we know the node type, the filter above can be equivalently written as: node_filter:
- $equal:
- $get_property: [SELF, TARGET, CAPABILITY, service, content]
- $get_property: [SELF, SOURCE, content] |
In Calin's example in #122 (comment) the syntax suggests that a node filter is a list of Boolean functions. Why not just use a single Boolean function? If multiple functions need to be evaluated, we should use explicit |
Workflow preconditions are now expressed using Boolean expressions as documented in https://docs.oasis-open.org/tosca/TOSCA/v2.0/csd05/TOSCA-v2.0-csd05.html#_Toc125468883. Similarly, conditions in policy triggers are now expressed using Boolean expressions as well as documented in https://docs.oasis-open.org/tosca/TOSCA/v2.0/csd05/TOSCA-v2.0-csd05.html#_Toc125468856. Using the new harmonized syntax, the original example in this issue is expressed as follows:
|
Condition clauses are used in policy definitions as well as in workflow definitions. While the condition syntax is well thought-out and supports arbitrary boolean expressions, in practice it is very limited since conditions can only apply to properties or attributes of
one single node, capability, or relationship. With current syntax it is not possible to create complex condition expressions that consider multiple nodes and relationships in a service topology.
To make matters worse, workflow definitions and policy definitions use different syntax for specifying the node or relationship that has the property or attribute for which the condition needs to be evaluated. Workflows use target and target_relationship keywords to specify the entity with the property or attribute for which the condition needs to be evaluated. There is no way to identify
properties or attributes in capabilities in a condition clause in a workflow. Policies, on the other hand, use a target_filter keyword
to specify the entity with the property or attribute for which the condition needs to be evaluated. This target filter allows you to
specify a capability.
The following snippet shows a workflow that includes a condition that checks the 'state' attribute of a relationship. It also shows a policy trigger that includes a condition that checks the 'level' attribute of a capability of a node.
At the very least, we should consider harmonizing the condition syntax so it is consistent across policy definitions and workflow
definitions. To support conditions that apply to entire topologies, we should consider supporting ToscaPath expressions in condition
clauses. I'll submit an example separately of what that could look like.
The text was updated successfully, but these errors were encountered: