Skip to content

Commit

Permalink
template rendering functions & tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kellerza committed May 25, 2021
1 parent 74c62f1 commit 618d547
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 19 deletions.
81 changes: 77 additions & 4 deletions clab/config/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"path/filepath"
"strconv"
"strings"
"text/template"

Expand Down Expand Up @@ -171,7 +172,20 @@ func (c *ConfigSnippet) String() string {
return fmt.Sprintf("%s: %s %d lines of config", c.TargetNode.LongName, c.source, len(c.Config))
}

func typeof(val interface{}) string {
switch val.(type) {
case string:
return "string"
case int:
return "int"
}
return ""
}

var funcMap = map[string]interface{}{
"expect": func(val interface{}, format interface{}) (interface{}, error) {
return nil, nil
},
"require": func(val interface{}) (interface{}, error) {
if val == nil {
return nil, errors.New("required value not set")
Expand All @@ -192,21 +206,80 @@ var funcMap = map[string]interface{}{
if def == nil {
return nil, fmt.Errorf("default value expected")
}
switch val.(type) {

switch v := val.(type) {
case string:
if val == "" {
if v == "" {
return def, nil
}
default:
if val == nil {
case bool:
if !v {
return def, nil
}
}
if val == nil {
return def, nil
}

// If we have a input value, do some type checking
tval, tdef := typeof(val), typeof(def)
if tval == "string" && tdef == "int" {
if _, err := strconv.Atoi(val.(string)); err == nil {
tval = "int"
}
}
if tdef != tval {
return val, fmt.Errorf("expected type %v, got %v (value=%v)", tdef, tval, val)
}

// Return the value
return val, nil
},
"contains": func(str interface{}, substr interface{}) (interface{}, error) {
return strings.Contains(fmt.Sprintf("%v", str), fmt.Sprintf("%v", substr)), nil
},
"split": func(val interface{}, sep interface{}) (interface{}, error) {
// Start and end values
if val == nil {
return []interface{}{}, nil
}
s := fmt.Sprintf("%v", sep)
if sep == nil {
s = " "
}

v := fmt.Sprintf("%v", val)

res := strings.Split(v, s)
r := make([]interface{}, len(res))
for i, p := range res {
r[i] = p
}
return r, nil
},
"join": func(val interface{}, sep interface{}) (interface{}, error) {
s := fmt.Sprintf("%s", sep)
if sep == nil {
s = " "
}
// Start and end values
switch v := val.(type) {
case []interface{}:
if val == nil {
return "", nil
}
res := make([]string, len(v))
for i, v := range v {
res[i] = fmt.Sprintf("%v", v)
}
return strings.Join(res, s), nil
case []string:
return strings.Join(v, s), nil
case []int, []int16, []int32:
return strings.Trim(strings.Replace(fmt.Sprint(v), " ", s, -1), "[]"), nil
}
return nil, fmt.Errorf("expected array [], got %v", val)
},
"slice": func(val interface{}, start interface{}, end interface{}) (interface{}, error) {
// Start and end values
var s, e int
Expand Down
47 changes: 36 additions & 11 deletions clab/config/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,55 @@ func TestFuncMapDefault(t *testing.T) {
// parameters & return
tSet := map[string][][]interface{}{
"default": {
{nil, 1, 1},
{0, nil, nil, true},
{5, 1, 5},
{5, "1", 5, "invalid types"},
{"a", 1, "a", "invalid types"},
{"", "1", "1"},
{nil, nil, fmt.Errorf("")},
{nil, nil, nil, true},
},
"contains": {
{"aa.", ".", true},
{"ss", ".", false},
},
"split": {
{"a.a", ".", "[a a]"},
{"a bb", nil, "[a bb]"},
{nil, nil, "[]"},
{nil, ".", "[]"},
},
"join": {
{[]interface{}{"a", "b"}, ".", "a.b"},
{[]string{"a", "b"}, ".", "a.b"},
{[]int{1, 2}, ".", "1.2"},
}}

for name, set := range tSet {
fn := funcMap[name].(func(interface{}, interface{}) (interface{}, error))

for _, p := range set {
exp := p[len(p)-1]
var expe error
switch v := exp.(type) {
case error:
expe = v
exp = nil
}
// Execute the funciton
res, err := fn(p[0], p[1])
if res != exp || (err != nil && expe == nil) {
t.Errorf("%v expected %v got %v error %v err: %v", p, exp, res, expe, err)

// expect return value
exp := p[2]
exp_err := len(p) == 4

// Check errors
if err != nil && !exp_err {
t.Errorf("%v no err expected, err: %v", p, err)
}
if err == nil && exp_err {
t.Errorf("%v err expected, non found", p)
}

// Check value
if res != exp {
// allow arrays (match on string only)
if fmt.Sprintf("%v", res) == fmt.Sprintf("%v", exp) {
continue
}
t.Errorf("%v expected %v got %v", p, exp, res)
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions templates/vr-sros/base-node.tmpl
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{{ expect .systemip "ip" }}
{{ expect .sid_idx "0-1000" }}

/configure system login-control idle-timeout 1440

/configure apply-groups ["baseport"]
Expand Down Expand Up @@ -66,13 +69,13 @@

/configure router
autonomous-system {{ default .as_number "64500" }}
mpls-labels sr-labels start {{ default .sid_start "19000" }} end {{ default .sid_end "30000" }}
mpls-labels sr-labels start {{ default .sid_start 19000 }} end {{ default .sid_end 30000 }}

/configure router isis {{ default .isis_iid "0" }}
area-address 49.0000.000{{ default .isis_iid "0" }}
/configure router isis {{ default .isis_iid 0 }}
area-address 49.0000.000{{ default .isis_iid 0 }}
level-capability 2
level 2 wide-metrics-only
interface "system" ipv4-node-sid index {{ .sid_idx }}
interface "system" ipv4-node-sid index {{ require .sid_idx }}
#database-export igp-identifier {{ default .isis_iid "0" }} bgp-ls-identifier value {{ default .isis_iid "0" }}
traffic-engineering
advertise-router-capability area
Expand Down

0 comments on commit 618d547

Please sign in to comment.