Skip to content

Commit

Permalink
Feature: Adds/Extends support for TOSCA functions
Browse files Browse the repository at this point in the history
- Adds support for multiple interfaces on a NodeType
- Adds support for performing attribute assignment via SetAttribute
- Adds support for performing retriving property as PropertyAssignment
- Extends EvaluateStatement support of get_property and get_attribute
- Adds support for setting the value of an input property of a TopologyTemplate
- Adds more parsing tests that validate the parsed results have the expected structure and values.
  • Loading branch information
kenjones-cisco committed Oct 13, 2016
1 parent 984ce78 commit 68cef20
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 34 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
gotosca
*.swp

vendor/
Expand Down
58 changes: 40 additions & 18 deletions node_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ func (n *NodeTemplate) getInterface() (string, InterfaceType, error) {
// All the Operations will be filled
func (n *NodeTemplate) fillInterface(s ServiceTemplateDefinition) {
nt := s.NodeTypes[n.Type]
name, intf, err := n.getInterface()
if err != nil {
if len(n.Interfaces) == 0 {
// If no interface is found, take the one frome the node type
var myInterfaces map[string]InterfaceType
myInterfaces = make(map[string]InterfaceType, 1)
Expand All @@ -90,23 +89,28 @@ func (n *NodeTemplate) fillInterface(s ServiceTemplateDefinition) {
n.Interfaces = myInterfaces
return
}
_, intf2, _ := nt.getInterface()
re := regexp.MustCompile(fmt.Sprintf("%v$", name))
operations := make(map[string]OperationDefinition, 0)
for ifacename, iface := range s.InterfaceTypes {
if re.MatchString(ifacename) {
for op := range iface.Operations {
v, ok := intf.Operations[op]
_, ok2 := intf2[op]
switch {
case !ok && ok2:
operations[op] = OperationDefinition{nil, intf2[op].Description, intf2[op].Implementation}
case ok:
operations[op] = v
default:
for name, intf := range n.Interfaces {
_, intf2, _ := nt.getInterfaceByName(name)
re := regexp.MustCompile(fmt.Sprintf("%v$", name))
operations := make(map[string]OperationDefinition, 0)
for ifacename, iface := range s.InterfaceTypes {
if re.MatchString(ifacename) {
for op := range iface.Operations {
v, ok := intf.Operations[op]
_, ok2 := intf2[op]
switch {
case !ok && ok2:
operations[op] = OperationDefinition{nil, intf2[op].Description, intf2[op].Implementation}
case ok:
if v.Implementation == "" {
v.Implementation = intf2[op].Implementation
}
operations[op] = v
default:
}
tmp := InterfaceType{n.Interfaces[name].Description, n.Interfaces[name].Version, operations, n.Interfaces[name].Inputs}
n.Interfaces[name] = tmp
}
tmp := InterfaceType{n.Interfaces[name].Description, n.Interfaces[name].Version, operations, n.Interfaces[name].Inputs}
n.Interfaces[name] = tmp
}
}
}
Expand All @@ -115,3 +119,21 @@ func (n *NodeTemplate) fillInterface(s ServiceTemplateDefinition) {
func (n *NodeTemplate) setName(name string) {
n.Name = name
}

// SetAttribute provides the ability to set a value to a named attribute
func (n *NodeTemplate) SetAttribute(prop string, value string) {
attrb := n.Attributes[prop]
aa := make(map[string][]string, 1)
aa["value"] = append([]string{}, value)
if len(attrb) != 0 {
//found one
n.Attributes[prop] = aa
} else {
attrbs := make(map[string]AttributeAssignment, len(n.Attributes)+1)
for key, val := range n.Attributes {
attrbs[key] = val
}
attrbs[prop] = aa
n.Attributes = attrbs
}
}
9 changes: 9 additions & 0 deletions node_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,12 @@ func (n *NodeType) getInterface() (string, InterfaceDefinition, error) {
}
return "", InterfaceDefinition{}, fmt.Errorf("No Interface found")
}

func (n *NodeType) getInterfaceByName(iname string) (string, InterfaceDefinition, error) {
for name, value := range n.Interfaces {
if iname == name {
return name, value, nil
}
}
return "", InterfaceDefinition{}, fmt.Errorf("No Interface found")
}
9 changes: 9 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ func merge(s, t ServiceTemplateDefinition) ServiceTemplateDefinition {
intf[key] = val
}
s.InterfaceTypes = intf
// PolicyType
pt := make(map[string]PolicyType, len(s.PolicyTypes)+len(t.PolicyTypes))
for key, val := range t.PolicyTypes {
pt[key] = val
}
for key, val := range s.PolicyTypes {
pt[key] = val
}
s.PolicyTypes = pt
return s
}

Expand Down
97 changes: 82 additions & 15 deletions service_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (

// ServiceTemplateDefinition is the meta structure containing an entire tosca document as described in
// http://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.0/csd03/TOSCA-Simple-Profile-YAML-v1.0-csd03.html
// Updated (kenjones): Adds policy_types
type ServiceTemplateDefinition struct {
DefinitionsVersion Version `yaml:"tosca_definitions_version" json:"tosca_definitions_version"` // A.9.3.1 tosca_definitions_version
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Expand Down Expand Up @@ -59,6 +58,21 @@ func (s *ServiceTemplateDefinition) GetProperty(node, prop string) PA {
return PA{PA: output, Origin: node}
}

// GetAttribute returns the attribute of a Node
func (s *ServiceTemplateDefinition) GetAttribute(node, attr string) PA {
var paa PropertyAssignment
for n, nt := range s.TopologyTemplate.NodeTemplates {
if n == node {
if aa, ok := nt.Attributes[attr]; ok {
for k, v := range aa {
paa[k] = reflect.ValueOf(v).Interface().([]interface{})
}
}
}
}
return PA{PA: paa, Origin: node}
}

// EvaluateStatement handles executing a statement for a pre-defined function
func (s *ServiceTemplateDefinition) EvaluateStatement(i interface{}) (interface{}, error) {
if ww, ok := i.(PA); ok {
Expand All @@ -83,12 +97,6 @@ func (s *ServiceTemplateDefinition) EvaluateStatement(i interface{}) (interface{
pa := reflect.ValueOf(val).Interface().(map[interface{}]interface{})
paa := make(PropertyAssignment, 0)
for k, v := range pa {
/*
var o []string
for _, vvv := range reflect.ValueOf(v).Interface().([]interface{}) {
o = append(o, vvv.(string))
}
*/
paa[k.(string)] = reflect.ValueOf(v).Interface().([]interface{})
if paa[k.(string)][0] == "SELF" {
paa[k.(string)][0] = ww.Origin
Expand All @@ -104,17 +112,76 @@ func (s *ServiceTemplateDefinition) EvaluateStatement(i interface{}) (interface{
return s.TopologyTemplate.Inputs[v[0].(string)].Value, nil
// Find the inputs and returns it
case "get_property":
if len(v) == 1 {
pa := s.GetProperty(ww.Origin, v[0].(string))
st, _ := s.EvaluateStatement(pa)
return st, nil
} //else
node := v[0].(string)
if v[0].(string) == "SELF" {
node = ww.Origin
}
if len(v) == 2 {
//refer to Properties
pa := s.GetProperty(node, v[1].(string))
st, _ := s.EvaluateStatement(pa)
return st, nil
}
if len(v) == 3 {
var st []string
//refer to requirements
rname := v[1].(string)
for _, r := range s.TopologyTemplate.NodeTemplates[node].Requirements {
for rn, rr := range r {
if rn == rname {
pa := s.GetProperty(rr.Node, v[2].(string))
vst, _ := s.EvaluateStatement(pa)
st = append(st, vst.(string))
}
}
}
return st, nil
}
case "get_attribute":
if len(v) == 1 {
node := s.TopologyTemplate.NodeTemplates[ww.Origin]
st := node.Attributes[v[0].(string)]["value"]
return st, nil
}
node := v[0].(string)
pa := s.GetProperty(node, v[1].(string))
st, _ := s.EvaluateStatement(pa)
return st, nil
/*
case "get_attribute":
ret := append([]string{"get_attribute"}, v...)
return ret, nil
*/
if v[0].(string) == "SELF" {
node = ww.Origin
}
if len(v) == 2 {
rnode := s.TopologyTemplate.NodeTemplates[node]
st := rnode.Attributes[v[1].(string)]["value"]
return st, nil
}
if len(v) == 3 {
// refer to node requirements
znode := s.TopologyTemplate.NodeTemplates[node]
var st []string
//refer to requirements
rname := v[1].(string)
for _, r := range znode.Requirements {
for rn, rr := range r {
if rn == rname {
vst := s.TopologyTemplate.NodeTemplates[rr.Node].Attributes[v[2].(string)]["value"]
st = append(st, vst...)
}
}
}
return st, nil
}
}
}
}
return []string{}, nil
}

// SetInput sets an input value on a Service Template Definition
func (s *ServiceTemplateDefinition) SetInput(prop string, value string) {
var input = s.TopologyTemplate.Inputs[prop]
input.Value = value
s.TopologyTemplate.Inputs[prop] = input
}
86 changes: 86 additions & 0 deletions service_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,92 @@ func TestParse(t *testing.T) {

}
}

func TestParseVerifyNodeTemplate(t *testing.T) {
fname := "./tests/example1.yaml"
var s ServiceTemplateDefinition
o, err := os.Open(fname)
if err != nil {
t.Fatal(err)
}
err = s.Parse(o)
if err != nil {
t.Log("Error in processing", fname)
t.Fatal(err)
}
if s.TopologyTemplate.NodeTemplates["my_server"].Type != "tosca.nodes.Compute" {
t.Log(fname, "missing NodeTemplate `my_server`")
t.Fail()
}
}

func TestParseVerifyMultipleNodeTemplate(t *testing.T) {
fname := "./tests/example3.yaml"
var s ServiceTemplateDefinition
o, err := os.Open(fname)
if err != nil {
t.Fatal(err)
}
err = s.Parse(o)
if err != nil {
t.Log("Error in processing", fname)
t.Fatal(err)
}

if s.TopologyTemplate.NodeTemplates["mysql"].Type != "tosca.nodes.DBMS.MySQL" {
t.Log(fname, "missing NodeTemplate `mysql`")
t.Fail()
}

if s.TopologyTemplate.NodeTemplates["db_server"].Type != "tosca.nodes.Compute" {
t.Log(fname, "missing NodeTemplate `db_server`")
t.Fail()
}
}

func TestParseVerifyInputOutput(t *testing.T) {
fname := "./tests/example2.yaml"
var s ServiceTemplateDefinition
o, err := os.Open(fname)
if err != nil {
t.Fatal(err)
}
err = s.Parse(o)
if err != nil {
t.Log("Error in processing", fname)
t.Fatal(err)
}

if s.TopologyTemplate.Inputs["cpus"].Type != "integer" {
t.Log(fname, "missing Input `cpus`")
t.Fail()
}

if s.TopologyTemplate.Outputs["server_ip"].Description != "The private IP address of the provisioned server." {
t.Log(fname, "missing Output `server_ip`")
t.Fail()
}
}

func TestParseVerifyCustomTypes(t *testing.T) {
fname := "./tests/test_host_assignment.yaml"
var s ServiceTemplateDefinition
o, err := os.Open(fname)
if err != nil {
t.Fatal(err)
}
err = s.Parse(o)
if err != nil {
t.Log("Error in processing", fname)
t.Fatal(err)
}

if s.NodeTypes["tosca.nodes.SoftwareComponent.Collectd"].DerivedFrom != "tosca.nodes.SoftwareComponent" {
t.Log(fname, "missing NodeTypes `tosca.nodes.SoftwareComponent.Collectd`")
t.Fail()
}
}

func TestParseCsar(t *testing.T) {

testsko := []string{
Expand Down

0 comments on commit 68cef20

Please sign in to comment.