Skip to content

Commit

Permalink
Merge pull request #534 from kellerza/vars_interface2
Browse files Browse the repository at this point in the history
config variables as `interface{}`
  • Loading branch information
hellt authored Jul 22, 2021
2 parents 77b78b8 + 2023992 commit 87afca0
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 116 deletions.
14 changes: 0 additions & 14 deletions clab/config/helpers.go

This file was deleted.

11 changes: 7 additions & 4 deletions clab/config/template.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package config

import (
"encoding/json"
"fmt"
"path/filepath"
"strings"
Expand All @@ -13,6 +12,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/srl-labs/containerlab/nodes"
"github.com/srl-labs/containerlab/types"
"gopkg.in/yaml.v2"
)

// templates to execute
Expand Down Expand Up @@ -116,10 +116,13 @@ func (c *NodeConfig) Print(printLines int, forceDebug ...bool) {

if log.IsLevelEnabled(log.DebugLevel) || len(forceDebug) > 0 {
s.WriteString(" vars = ")
vars, _ := json.MarshalIndent(c.Vars, "", " ")
vars, err := yaml.Marshal(c.Vars)
if err != nil {
log.Warnf("error printing vars for node %s: %s", c.TargetNode.ShortName, err)
s.WriteString(err.Error())
}
if len(vars) > 0 {
s.Write(vars[0 : len(vars)-1])
s.WriteString(" }")
s.Write(vars)
}
}

Expand Down
81 changes: 42 additions & 39 deletions clab/config/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"fmt"
"path/filepath"
"reflect"
"strconv"
"strings"

Expand All @@ -13,16 +14,16 @@ import (
)

const (
vkNodes = "nodes" // reserved, used for all nodes
vkNodeName = "node" // reserved, used for the node's ShortName
vkLinks = "links" // reserved, used for all link in a node
vkRole = "role" // optional, will default to the node's Kind. Used to select the template
vkFarEnd = "far" // reserved, used for far-end link & node info

vkSystemIP = "systemip" // optional, system IP if present could be used to calc link IPs
vkLinkIP = "ip" // optional, link IP
vkLinkName = "name" // optional, from ShortNames
vkLinkNr = "linknr" // optional, link number in case you have multiple, used to calculate the name
vkNodeName = "clab_node" // reserved, used for the node's ShortName
vkNodes = "clab_nodes" // reserved, used for all nodes
vkLinks = "clab_links" // reserved, used for all link in a node
vkFarEnd = "clab_far" // reserved, used for far-end link & node info
vkRole = "clab_role" // optional, will default to the node's Kind. Used to select the template

vkSystemIP = "clab_system_ip" // optional, system IP if present could be used to calc link IPs
vkLinkIP = "clab_link_ip" // optional, link IP
vkLinkName = "clab_link_name" // optional, from ShortNames
vkLinkNum = "clab_link_num" // optional, link number in case you have multiple, used to calculate the name
)

type Dict map[string]interface{}
Expand Down Expand Up @@ -101,35 +102,37 @@ func prepareLinkVars(lIdx int, link *types.Link, varsA, varsB Dict) error {
(varsB[vkFarEnd]).(Dict)[key] = v1
}

// Split all fields with a comma...
// Ensure values are added to both ends of the link
for k, v := range link.Vars {

r := SplitTrim(v)

if k == vkFarEnd || k == vkNodeName {
return fmt.Errorf("%s: reserved variable name '%s' found", link.String(), k)
}

if k == vkLinkIP && len(r) == 1 {
// calc the remote IP
ipF, err := ipFarEndS(v)
if err != nil {
return fmt.Errorf("%s: %s", link.String(), err)
vv := reflect.ValueOf(v)
if vv.Kind() == reflect.Slice || vv.Kind() == reflect.Array {
// Array/slice should contain 2 values, one for each end of the link
if vv.Len() != 2 {
return fmt.Errorf("%s: variable %s should contain 2 elements, found %d: %v", link.String(), k, vv.Len(), v)
}
r = append(r, ipF)
addValues(k, vv.Index(0).Interface(), vv.Index(1).Interface())
continue
}

if len(r) == 1 { // Ensure we add single values to local and far-end
r = append(r, r[0])
}
if len(r) > 2 { // too many values
log.Warnf("%s: variable %s contains %d comma separated values, should be 1 or 2: %s", link.String(), k, len(r), v)
if k == vkLinkIP {
// Calculate the remote IP
vs := fmt.Sprintf("%v", v)
ipF, err := ipFarEndS(vs)
if err != nil {
return fmt.Errorf("%s: %s", link.String(), err)
}
addValues(k, vs, ipF)
continue
}

addValues(k, r[0], r[1])
addValues(k, v, v)
}

// Run through a list of additional values to add if they are not present
// Add additional values if they are not present
add := map[string]func(link *types.Link) (string, string, error){
vkLinkIP: linkIP,
vkLinkName: linkName,
Expand All @@ -151,13 +154,13 @@ func prepareLinkVars(lIdx int, link *types.Link, varsA, varsB Dict) error {
return nil
}

// Create a link name using the node names and optional linkNr
// Create a link name using the node names and optional link_num
func linkName(link *types.Link) (string, string, error) {
var linkNr string
if v, ok := link.Vars[vkLinkNr]; ok {
linkNr = fmt.Sprintf("_%v", v)
var linkNo string
if v, ok := link.Vars[vkLinkNum]; ok {
linkNo = fmt.Sprintf("_%v", v)
}
return fmt.Sprintf("to_%s%s", link.B.Node.ShortName, linkNr), fmt.Sprintf("to_%s%s", link.A.Node.ShortName, linkNr), nil
return fmt.Sprintf("to_%s%s", link.B.Node.ShortName, linkNo), fmt.Sprintf("to_%s%s", link.A.Node.ShortName, linkNo), nil
}

// Calculate link IP from the system IPs at both ends
Expand All @@ -168,25 +171,25 @@ func linkIP(link *types.Link) (string, string, error) {
_, okA := link.A.Node.Config.Vars[vkSystemIP]
_, okB := link.B.Node.Config.Vars[vkSystemIP]
if okA != okB {
return "", "", fmt.Errorf("%s var required on all nodes", vkSystemIP)
log.Warnf("to auto-generate link IPs, a %s variable is required on all nodes", vkSystemIP)
}
if !okA {
if !okA || !okB {
return "", "", nil
}
sysA, err := netaddr.ParseIPPrefix(link.A.Node.Config.Vars[vkSystemIP])
sysA, err := netaddr.ParseIPPrefix(fmt.Sprintf("%v", link.A.Node.Config.Vars[vkSystemIP]))
if err != nil {
return "", "", fmt.Errorf("no 'ip' on link & the '%s' of %s: %s", vkSystemIP, link.A.Node.ShortName, err)
}
sysB, err := netaddr.ParseIPPrefix(link.B.Node.Config.Vars[vkSystemIP])
sysB, err := netaddr.ParseIPPrefix(fmt.Sprintf("%v", link.B.Node.Config.Vars[vkSystemIP]))
if err != nil {
return "", "", fmt.Errorf("no 'ip' on link & the '%s' of %s: %s", vkSystemIP, link.B.Node.ShortName, err)
}

o4 := 0
if v, ok := link.Vars[vkLinkNr]; ok {
if v, ok := link.Vars[vkLinkNum]; ok {
o4, err = strconv.Atoi(fmt.Sprintf("%v", v))
if err != nil {
log.Warnf("%s is expected to contain a number, got %s", vkLinkNr, v)
log.Warnf("%s is expected to contain a number, got %s", vkLinkNum, v)
}
o4 *= 2
}
Expand All @@ -197,7 +200,7 @@ func linkIP(link *types.Link) (string, string, error) {
}
ipA, err = netaddr.ParseIPPrefix(fmt.Sprintf("1.%d.%d.%d/31", o2, o3, o4))
if err != nil {
log.Errorf("could not create link IP from systemip: %s", err)
log.Errorf("could not create link IP from %s: %s", vkSystemIP, err)
}
return ipA.String(), ipFarEnd(ipA).String(), nil
}
Expand Down
98 changes: 98 additions & 0 deletions clab/config/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package config

import (
"runtime"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/srl-labs/containerlab/types"
"inet.af/netaddr"
)

Expand All @@ -27,6 +30,12 @@ func TestFarEndIP(t *testing.T) {

for k, v := range lst {
n := ipFarEnd(netaddr.MustParseIPPrefix(k))
n2, _ := ipFarEndS(k)

if n2 != n.String() {
t.Errorf("far end %s != %s, expected %s", n, n2, v)
}

if n.IsZero() && v == "" {
continue
}
Expand All @@ -52,3 +61,92 @@ func TestIPLastOctect(t *testing.T) {
}

}

func gettestLink() *types.Link {
return &types.Link{
A: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "a",
Config: &types.ConfigDispatcher{
Vars: map[string]interface{}{
vkSystemIP: "10.0.0.1/32",
},
},
},
},
B: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "b",
Config: &types.ConfigDispatcher{
Vars: map[string]interface{}{
vkSystemIP: "10.0.0.2/32",
},
},
},
},
Vars: map[string]interface{}{},
}
}

func assert(t *testing.T, val, exp interface{}) {
if !cmp.Equal(val, exp) {
_, fn, line, _ := runtime.Caller(1)
t.Errorf("assert failed on line %v in %s\n%s", line, fn, cmp.Diff(val, exp))
}
}

func TestLinkName(t *testing.T) {
l := gettestLink()
n1, n2, _ := linkName(l)
assert(t, n1, "to_b")
assert(t, n2, "to_a")

l.Vars[vkLinkNum] = 1
n1, n2, _ = linkName(l)
assert(t, n1, "to_b_1")
assert(t, n2, "to_a_1")
}

func TestLinkIP(t *testing.T) {
l := gettestLink()
n1, n2, _ := linkIP(l)
assert(t, n1, "1.1.2.0/31")
assert(t, n2, "1.1.2.1/31")

l.Vars[vkLinkNum] = 1
n1, n2, _ = linkIP(l)
assert(t, n1, "1.1.2.2/31")
assert(t, n2, "1.1.2.3/31")
}

func TestPrepareLinkVars(t *testing.T) {
a := make(Dict)
b := make(Dict)
l := gettestLink()
_ = prepareLinkVars(0, l, a, b)
assert(t, a, Dict{
vkFarEnd: Dict{vkLinkIP: "1.1.2.1/31", vkLinkName: "to_a", vkNodeName: "b"},
vkLinkIP: "1.1.2.0/31",
vkLinkName: "to_b",
})
assert(t, b, Dict{
vkFarEnd: Dict{vkLinkIP: "1.1.2.0/31", vkLinkName: "to_b", vkNodeName: "a"},
vkLinkIP: "1.1.2.1/31",
vkLinkName: "to_a",
})

l.Vars[vkLinkIP] = []string{"1.1.2.0/16", "1.1.2.1/16"}
l.Vars[vkLinkName] = "the_same"

_ = prepareLinkVars(0, l, a, b)
assert(t, a, Dict{
vkFarEnd: Dict{vkLinkIP: "1.1.2.1/16", vkLinkName: "the_same", vkNodeName: "b"},
vkLinkIP: "1.1.2.0/16",
vkLinkName: "the_same",
})
assert(t, b, Dict{
vkFarEnd: Dict{vkLinkIP: "1.1.2.0/16", vkLinkName: "the_same", vkNodeName: "a"},
vkLinkIP: "1.1.2.1/16",
vkLinkName: "the_same",
})
}
20 changes: 10 additions & 10 deletions lab-examples/vr05/sros4.clab.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,40 @@ topology:
sr1:
config:
vars:
systemip: 10.0.50.31/32
clab_system_ip: 10.0.50.31/32
sid_idx: 1
sr2:
config:
vars:
systemip: 10.0.50.32/32
clab_system_ip: 10.0.50.32/32
sid_idx: 2
sr3:
config:
vars:
systemip: 10.0.50.33/32
clab_system_ip: 10.0.50.33/32
sid_idx: 3
sr4:
config:
vars:
systemip: 10.0.50.34/32
clab_system_ip: 10.0.50.34/32
sid_idx: 4
links:
- endpoints: [sr1:eth1, sr2:eth2]
vars:
port: 1/1/c1/1, 1/1/c2/1
ip: 1.1.1.2/30
vlan: "99,99"
port: [1/1/c1/1, 1/1/c2/1]
clab_link_ip: 1.1.1.2/30
vlan: [99, 99]
isis_iid: 0
- endpoints: [sr2:eth1, sr3:eth2]
vars:
port: 1/1/c1/1, 1/1/c2/1
port: [1/1/c1/1, 1/1/c2/1]
vlan: 98
isis_iid: 0
- endpoints: [sr3:eth1, sr4:eth2]
vars:
port: 1/1/c1/1, 1/1/c2/1
port: [1/1/c1/1, 1/1/c2/1]
isis_iid: 0
- endpoints: [sr4:eth1, sr1:eth2]
vars:
port: 1/1/c1/1, 1/1/c2/1
port: [1/1/c1/1, 1/1/c2/1]
isis_iid: 0
8 changes: 4 additions & 4 deletions lab-examples/vr05/vr01.clab.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ topology:
image: ghcr.io/nokia/srlinux
config:
vars:
systemip: 10.0.50.50/32
clab_system_ip: 10.0.50.50/32
isis_iid: 0
sid_idx: 11
sros:
kind: vr-sros
image: registry.srlinux.dev/pub/vr-sros:21.2.R1
type: sr-1
license: license-sros21.txt
license: ~/license/license-sros21.txt
config:
vars:
systemip: 10.0.50.51/32
clab_system_ip: 10.0.50.51/32
sid_idx: 10
isis_iid: 0

links:
- endpoints: ["srl:e1-1", "sros:eth1"]
vars:
port: ethernet-1/1, 1/1/c1/1
port: [ethernet-1/1, 1/1/c1/1]
vlan: 10
isis_iid: 0
Loading

0 comments on commit 87afca0

Please sign in to comment.