Skip to content

Commit

Permalink
Merge pull request #383 from kellerza/config
Browse files Browse the repository at this point in the history
Config template support 🚀
  • Loading branch information
hellt authored Jun 30, 2021
2 parents 3db4ffb + e88a5e7 commit 929abda
Show file tree
Hide file tree
Showing 24 changed files with 1,376 additions and 16 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ bin/*
rpm
rpm/*
dist
private

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# clab directories
lab-examples/**/clab-*

# ignore the following files/directories
graph/*
license.key
Expand All @@ -24,4 +28,5 @@ containerlab
.vscode/
.DS_Store
__rd*
tests/out
tests/out

13 changes: 13 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
repos:
- repo: https://github.com/codespell-project/codespell
rev: v2.0.0
hooks:
- id: codespell
- repo: https://github.com/syntaqx/git-hooks
rev: v0.0.17
hooks:
- id: forbid-binary
- id: go-fmt
- id: go-test
- id: go-mod-tidy
- id: shfmt
10 changes: 7 additions & 3 deletions clab/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (c *CLab) createNodeCfg(nodeName string, nodeDef *types.NodeDefinition, idx
log.Debugf("node config: %+v", nodeCfg)
var err error
// initialize config
nodeCfg.StartupConfig, err = c.Config.Topology.GetNodeConfig(nodeCfg.ShortName)
nodeCfg.StartupConfig, err = c.Config.Topology.GetNodeStartupConfig(nodeCfg.ShortName)
if err != nil {
return nil, err
}
Expand All @@ -188,6 +188,8 @@ func (c *CLab) createNodeCfg(nodeName string, nodeDef *types.NodeDefinition, idx
}
nodeCfg.Labels = c.Config.Topology.GetNodeLabels(nodeCfg.ShortName)

nodeCfg.Config = c.Config.Topology.GetNodeConfigDispatcher(nodeCfg.ShortName)

return nodeCfg, nil
}

Expand All @@ -196,11 +198,13 @@ func (c *CLab) NewLink(l *types.LinkConfig) *types.Link {
if len(l.Endpoints) != 2 {
log.Fatalf("endpoint %q has wrong syntax, unexpected number of items", l.Endpoints)
}

return &types.Link{
A: c.NewEndpoint(l.Endpoints[0]),
B: c.NewEndpoint(l.Endpoints[1]),
MTU: defaultVethLinkMTU,
Labels: l.Labels,
Vars: l.Vars,
}
}

Expand All @@ -221,7 +225,7 @@ func (c *CLab) NewEndpoint(e string) *types.Endpoint {
if len(endpoint.EndpointName) > 15 {
log.Fatalf("interface '%s' name exceeds maximum length of 15 characters", endpoint.EndpointName)
}
// generate unqiue MAC
// generate unique MAC
endpoint.MAC = utils.GenMac(clabOUI)

// search the node pointer for a node name referenced in endpoint section
Expand Down Expand Up @@ -416,7 +420,7 @@ func (c *CLab) verifyRootNetnsInterfaceUniqueness() error {
for _, e := range endpoints {
if e.Node.Kind == nodes.NodeKindBridge || e.Node.Kind == nodes.NodeKindOVS || e.Node.Kind == nodes.NodeKindHOST {
if _, ok := rootNsIfaces[e.EndpointName]; ok {
return fmt.Errorf(`interface %s defined for node %s has already been used in other bridges, ovs-bridges or host interfaces.
return fmt.Errorf(`interface %s defined for node %s has already been used in other bridges, ovs-bridges or host interfaces.
Make sure that nodes of these kinds use unique interface names`, e.EndpointName, e.Node.ShortName)
}
rootNsIfaces[e.EndpointName] = struct{}{}
Expand Down
41 changes: 41 additions & 0 deletions clab/config/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package config

import (
"strings"
)

// Split a string on commas and trim each line
func SplitTrim(s string) []string {
res := strings.Split(s, ",")
for i, v := range res {
res[i] = strings.Trim(v, " \n\t")
}
return res
}

// The new agreed node config
type NodeSettings struct {
Vars map[string]string
Transport string
Templates []string
}

// Temporary function to extract NodeSettings from the Labels
// In the next phase node settings will be added to the clab file
func GetNodeConfig(labels map[string]string) NodeSettings {
nc := NodeSettings{
Vars: labels,
Transport: "ssh",
}
if len(TemplateNames) > 0 {
nc.Templates = TemplateNames
} else if t, ok := labels["templates"]; ok {
nc.Templates = SplitTrim(t)
} else {
nc.Templates = []string{"base"}
}
if t, ok := labels["transport"]; ok {
nc.Transport = t
}
return nc
}
110 changes: 110 additions & 0 deletions clab/config/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package config

import (
"encoding/json"
"fmt"
"strings"

jT "github.com/kellerza/template"

log "github.com/sirupsen/logrus"
"github.com/srl-labs/containerlab/nodes"
"github.com/srl-labs/containerlab/types"
)

// templates to execute
var TemplateNames []string

// path to additional templates
var TemplatePaths []string

type NodeConfig struct {
TargetNode *types.NodeConfig
// All the variables used to render the template
Vars map[string]interface{}
// the Rendered templates
Data []string
Info []string
}

func RenderAll(nodes map[string]nodes.Node, links map[int]*types.Link) (map[string]*NodeConfig, error) {
res := make(map[string]*NodeConfig)

if len(TemplatePaths) == 0 {
return nil, fmt.Errorf("please specify one of more paths with --template-path")
}

if len(TemplateNames) == 0 {
var err error
TemplateNames, err = GetTemplateNamesInDirs(TemplatePaths)
if err != nil {
return nil, err
}
if len(TemplateNames) == 0 {
return nil, fmt.Errorf("no templates files were found by %s path", TemplatePaths)
}
}

tmpl, err := jT.New("", jT.SearchPath(TemplatePaths...))
if err != nil {
return nil, err
}

for nodeName, vars := range PrepareVars(nodes, links) {
res[nodeName] = &NodeConfig{
TargetNode: nodes[nodeName].Config(),
Vars: vars,
}

for _, baseN := range TemplateNames {
tmplN := fmt.Sprintf("%s__%s.tmpl", baseN, vars["role"])
data1, err := tmpl.ExecuteTemplate(tmplN, vars)
if err != nil {
return nil, err
}
data1 = strings.ReplaceAll(strings.Trim(data1, "\n \t"), "\n\n\n", "\n\n")
res[nodeName].Data = append(res[nodeName].Data, data1)
res[nodeName].Info = append(res[nodeName].Info, tmplN)
}
}
return res, nil
}

// Implement stringer for conf snippet
func (c *NodeConfig) String() string {

s := fmt.Sprintf("%s: %v", c.TargetNode.ShortName, c.Info)
return s
}

// Print the config
func (c *NodeConfig) Print(printLines int) {
var s strings.Builder

s.WriteString(c.TargetNode.ShortName)

if log.IsLevelEnabled(log.DebugLevel) {
s.WriteString(" vars = ")
vars, _ := json.MarshalIndent(c.Vars, "", " ")
s.Write(vars[0 : len(vars)-1])
s.WriteString(" }")
}

if printLines > 0 {
for idx, conf := range c.Data {
fmt.Fprintf(&s, "\n Template %s for %s = [[", c.Info[idx], c.TargetNode.ShortName)

cl := strings.SplitN(conf, "\n", printLines+1)
if len(cl) > printLines {
cl[printLines] = "..."
}
for _, l := range cl {
s.WriteString("\n ")
s.WriteString(l)
}
s.WriteString("\n ]]")
}
}

log.Infoln(s.String())
}
Loading

0 comments on commit 929abda

Please sign in to comment.