Skip to content

Commit

Permalink
Change in requirements occurrences behavior
Browse files Browse the repository at this point in the history
1) The implied default is [1,1] to match the spec
2) Puccini will create default assignments to match the lower bound
   amount
  • Loading branch information
tliron committed Dec 17, 2021
1 parent 165964b commit 65e46c9
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 49 deletions.
32 changes: 12 additions & 20 deletions examples/tosca/requirements-and-capabilities.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ topology_template:

node_templates:

# Because the "socket" requirement is not controlled by "occurrences" at the node type
# it will be automatically assigned to "light1"
# That requirements has 3 matching capabilities in this topology,
# By default Puccini will automatically create requirement assignments to ensure that
# the lower bound of the requirement "occurrences" is met, with the default being [ 1, 1 ]
# That requirement has 3 matching capabilities in this topology,
# So Puccini will arbitrarily satisfy it with one of them
light1:
type: LightBulb
Expand Down Expand Up @@ -134,18 +134,8 @@ topology_template:
- socket:
capability: SuperSocket

# You can specify the same requirement more than once, which will lead to multiple relationships
# (However, the number of times you may do so can be limited by "occurrences" at the node type requirement)
light6:
type: LightBulb
requirements:
- socket:
capability: SuperSocket
# An "empty" requirement will use whatever we specified at the node type
- socket: {}

# You can use "node_filter" to further require property values at the target node
light7:
light6:
type: LightBulb
requirements:
- socket:
Expand All @@ -158,7 +148,7 @@ topology_template:

# You can also put "node_filter" at the node itself, in which case the filter will
# apply to *all* requirements
light8:
light7:
type: LightBulb
node_filter:
properties:
Expand All @@ -169,7 +159,7 @@ topology_template:
capability: Socket

# "node_filter" can also work on capability properties
light9:
light8:
type: LightBulb
requirements:
- socket:
Expand All @@ -183,14 +173,14 @@ topology_template:

# You can add a relationship type to the requirement
# (If you don't, Puccini creates an empty relationship with no properties)
light10:
light9:
type: LightBulb
requirements:
- socket:
relationship: SmartPlug

# The long notation lets you assign values to the relationship
light11:
light10:
type: LightBulb
requirements:
- socket:
Expand All @@ -200,13 +190,15 @@ topology_template:
vendor: Smart Appliances Industries

# Yet another option is to use a relationship template (see below) instead of a type
light12:
light11:
type: LightBulb
requirements:
- socket:
relationship: smart_plug

# "occurrences" for "socket" requirement is [ 2, 4 ], so we must assign it *at least* twice
# If the lower bound of occurences is > 1 then you can specify the same requirement more than once,
# which will lead to multiple relationships
# Here it is [ 2, UNBOUNDED ]
fan1:
type: Fan
requirements:
Expand Down
1 change: 0 additions & 1 deletion examples/tosca/simple-for-nfv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ topology_template:
type: tosca:VduCpd
requirements:
- virtual_binding: firewall
- virtual_binding: router

firewall:
type: tosca:VDU.Compute
Expand Down
49 changes: 24 additions & 25 deletions tosca/grammars/tosca_v2_0/requirement-assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,33 +124,22 @@ func (self *RequirementAssignments) Render(definitions RequirementDefinitions, c
// it would be assigned

for key, definition := range definitions {
if definition.Occurrences == nil {
// The TOSCA spec says that occurrences has an "implied default of [1,1]"
// Our interpretation is that we should automatically add a single assignment if none was specified

found := false
for _, assignment := range *self {
if assignment.Name == key {
found = true
break
}
}
// The TOSCA spec says that definition occurrences has an "implied default of [1,1]"
occurrences := definition.Occurrences
if occurrences == nil {
occurrences = ReadRangeEntity(definition.Context.FieldChild("occurrences", ard.List{1, 1})).(*RangeEntity)
}

if !found {
*self = append(*self, NewDefaultRequirementAssignment(len(*self), definition, context))
}
} else {
// Check occurrences
var count uint64
for _, assignment := range *self {
if assignment.Name == key {
count++
}
}
count := self.Count(key)

if !definition.Occurrences.Range.InRange(count) {
context.ReportNotInRange(fmt.Sprintf("number of requirement %q assignments", definition.Name), count, definition.Occurrences.Range.Lower, definition.Occurrences.Range.Upper)
}
// Automatically add missing assignments
for i := count; i < occurrences.Range.Lower; i++ {
*self = append(*self, NewDefaultRequirementAssignment(len(*self), definition, context))
count++
}

if !occurrences.Range.InRange(count) {
context.ReportNotInRange(fmt.Sprintf("number of requirement %q assignments", definition.Name), count, occurrences.Range.Lower, occurrences.Range.Upper)
}
}

Expand Down Expand Up @@ -202,3 +191,13 @@ func (self RequirementAssignments) Normalize(nodeTemplate *NodeTemplate, normalN
requirement.Normalize(nodeTemplate, normalNodeTemplate)
}
}

func (self *RequirementAssignments) Count(key string) uint64 {
var count uint64
for _, assignment := range *self {
if assignment.Name == key {
count++
}
}
return count
}
7 changes: 4 additions & 3 deletions tosca/parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,11 @@ do more than just validate: we also change the value, for example by applying de
There are furthermore various custom validations per entity. One worth mentioning here is
requirement validation. Specifically, we take into account the `occurrences` field, which limits the
number of times a requirement may be assigned. The TOSCA spec mentions that the implied default
`occurrences` is \[1,1\] (the TOSCA spec oddly says that in a `range` the upper bound must be
`occurrences` is \[1,1] (the TOSCA spec oddly says that in a `range` the upper bound must be
greater than the lower bound, so that \[1,1] is impossible: one of many contradictions we must
resolve). However, we further assume that if `occurrences` is not specified then it is also intended
for the requirement to be automatically assigned if not explicitly specified.
resolve). Puccini will automatically assign requirements to match the lower bound of `occurrences`,
so if the lower bound is 2 and you have only assigned one then an additional one will be assigned
for you.

At the end of this phase the templates are considered "complete" in that we should not have to
access their types for any data. All fields have been rendered.

0 comments on commit 65e46c9

Please sign in to comment.