Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scrapligo as transport #737

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions clab/config/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,18 @@ import (
"fmt"

"github.com/srl-labs/containerlab/clab/config/transport"
"github.com/srl-labs/containerlab/nodes"
)

func Send(cs *NodeConfig, _ string) error {
var tx transport.Transport
var err error

ct, ok := cs.TargetNode.Labels["config.transport"]
if !ok {
ct = "ssh"
}

if ct == "ssh" {
if len(nodes.DefaultCredentials[cs.TargetNode.Kind]) < 2 {
return fmt.Errorf("SSH credentials for node %s of type %s not found, cannot configure", cs.TargetNode.ShortName, cs.TargetNode.Kind)
}
tx, err = transport.NewSSHTransport(
cs.TargetNode,
transport.WithUserNamePassword(
nodes.DefaultCredentials[cs.TargetNode.Kind][0],
nodes.DefaultCredentials[cs.TargetNode.Kind][1]),
transport.HostKeyCallback(),
)
tx, err = transport.NewScrapliTransport(cs.TargetNode)
if err != nil {
return err
}
Expand All @@ -35,7 +24,6 @@ func Send(cs *NodeConfig, _ string) error {
} else {
return fmt.Errorf("unknown transport: %s", ct)
}

err = transport.Write(tx, cs.TargetNode.LongName, cs.Data, cs.Info)
if err != nil {
return err
Expand Down
17 changes: 11 additions & 6 deletions clab/config/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ type NodeConfig struct {
// All the variables used to render the template
Vars map[string]interface{}
// the Rendered templates
Data []string
Info []string
Data []string // rendered configuration commands as a slice of strings
Info []string // template names used to produce the rendered config
}

// Load templates from all paths for the specific role/kind
// LoadTemplates loads from all paths for the specific role/kind
func LoadTemplates(tmpl *template.Template, role string) error {
for _, p := range TemplatePaths {
fn := filepath.Join(p, fmt.Sprintf("*__%s.tmpl", role))
Expand All @@ -44,7 +44,9 @@ func LoadTemplates(tmpl *template.Template, role string) error {
return nil
}

func RenderAll(allnodes map[string]*NodeConfig) error {
// RenderAll renders all templates for all the nodes and stores the result of the templating
// in the nodes map
func RenderAll(nodes map[string]*NodeConfig) error {

if len(TemplatePaths) == 0 { // default is the install path
TemplatePaths = []string{"@"}
Expand All @@ -67,23 +69,26 @@ func RenderAll(allnodes map[string]*NodeConfig) error {

tmpl := template.New("").Funcs(jT.Funcs)

for _, nc := range allnodes {
for _, nc := range nodes {
for _, baseN := range TemplateNames {
tmplN := fmt.Sprintf("%s__%s.tmpl", baseN, nc.Vars[vkRole])
log.Debugf("Looking up template %v", tmplN)

if l := tmpl.Lookup(tmplN); l == nil {
err := LoadTemplates(tmpl, fmt.Sprintf("%s", nc.Vars[vkRole]))
if err != nil {
log.Warnf("Unable to load template %s (%+v); skipping", tmplN, err)
continue
}

l = tmpl.Lookup(tmplN)
log.Debugf("Got a lookup result %+v (of type %T)", l, l)
if l == nil {
log.Warnf("No template found for %s; skipping..", nc.TargetNode.ShortName)
continue
}
log.Debugf("Got a lookup result %+v (of type %T)", l, l)
}

var buf strings.Builder
err := tmpl.ExecuteTemplate(&buf, tmplN, nc.Vars)
log.Debugf("Executed a template %s with an error code %v", tmplN, err)
Expand Down
156 changes: 156 additions & 0 deletions clab/config/transport/scrapli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package transport

import (
"fmt"
"time"

"github.com/scrapli/scrapligo/cfg"
"github.com/scrapli/scrapligo/driver/base"
"github.com/scrapli/scrapligo/driver/core"
"github.com/scrapli/scrapligo/driver/network"
log "github.com/sirupsen/logrus"
"github.com/srl-labs/containerlab/nodes"
"github.com/srl-labs/containerlab/types"
)

// Scrapligo native platform names per each kind
var NetworkDriver = map[string]string{
"ceos": "arista_eos",
"crpd": "juniper_junos",
"srl": "nokia_sros",
"vr-csr": "cisco_iosxe",
"vr-n9kv": "cisco_nxos",
"vr-nxos": "cisco_nxos",
"vr-pan": "paloalto_panos",
"vr-sros": "nokia_sros",
"vr-veos": "arista_eos",
"vr-vmx": "juniper_junos",
"vr-vqfx": "juniper_junos",
"vr-xrv": "cisco_iosxr",
"vr-xrv9k": "cisco_iosxr",
}

type ScrapliTransport struct {
Driver *network.Driver
Options *ScrapliOptions
}

type ScrapliOptions struct {
Platform string
Port int
AuthUsername string
AuthPassword string
AuthSecondary string
AuthPrivateKey string
AuthStrictKey bool
SSHConfigFile string
SSHKnownHostsFile string
}

func NewScrapliTransport(node *types.NodeConfig) (*ScrapliTransport, error) {
t := node.Config.Transport
// check if kind is supported in scrapli
_, ok := NetworkDriver[node.Kind]
if !ok {
return nil, fmt.Errorf("unable to find scrapli platform for kind: %v", node.Kind)
}
// setting default values
if t.Scrapli.Port == 0 {
t.Scrapli.Port = 22
}
if t.Scrapli.SSHConfigFile == "" {
if t.Scrapli.AuthUsername == "" {
t.Scrapli.AuthUsername = nodes.DefaultCredentials[node.Kind][0]
}
if t.Scrapli.AuthPassword == "" {
t.Scrapli.AuthPassword = nodes.DefaultCredentials[node.Kind][1]
}
}
if node.Kind == "crpd" {
if t.Scrapli.AuthSecondary == "" {
t.Scrapli.AuthSecondary = nodes.DefaultCredentials[node.Kind][2]
}
}
options := &ScrapliOptions{
Platform: NetworkDriver[node.Kind],
Port: t.Scrapli.Port,
AuthUsername: t.Scrapli.AuthUsername,
AuthPassword: t.Scrapli.AuthPassword,
AuthSecondary: t.Scrapli.AuthSecondary,
AuthPrivateKey: t.Scrapli.AuthPrivateKey,
AuthStrictKey: t.Scrapli.AuthStrictKey,
SSHConfigFile: t.Scrapli.SSHConfigFile,
SSHKnownHostsFile: t.Scrapli.SSHKnownHostsFile,
}
driver, err := options.NewScrapliDriver(node.LongName)
if err != nil {
return nil, fmt.Errorf("failed to create driver: %v", err)
}
return &ScrapliTransport{
Driver: driver,
Options: options,
}, nil
}

func (t *ScrapliOptions) NewScrapliDriver(host string) (*network.Driver, error) {
d, err := core.NewCoreDriver(
host,
t.Platform,
base.WithTimeoutOps(5*time.Second),
base.WithPort(t.Port),
base.WithAuthStrictKey(t.AuthStrictKey),
base.WithSSHConfigFile(t.SSHConfigFile),
base.WithAuthUsername(t.AuthUsername),
base.WithAuthPassword(t.AuthPassword),
base.WithAuthSecondary(t.AuthSecondary),
base.WithAuthPrivateKey(t.AuthPrivateKey),
)
if err != nil {
return nil, err
}
return d, nil
}

func (d *ScrapliTransport) Connect(host string, _ ...TransportOption) error {
err := d.Driver.Open()
if err != nil {
return fmt.Errorf("failed to open driver: %v", err)
}
log.Infof("Connected to %s\n", host)
return nil
}

func (t *ScrapliTransport) Write(data, _ *string) error {
if *data == "" {
return nil
}
c, err := cfg.NewCfgDriver(
t.Driver,
t.Options.Platform,
)
if err != nil {
return fmt.Errorf("failed to create config driver: %v", err)
}
prepareErr := c.Prepare()
if prepareErr != nil {
return fmt.Errorf("failed running prepare method: %v", prepareErr)
}
_, err = c.LoadConfig(
string(*data),
false, //don't load replace. Load merge/set instead
)
if err != nil {
return fmt.Errorf("failed to load config: %+v", err)
}
_, err = c.CommitConfig()
if err != nil {
return fmt.Errorf("failed to commit config: %+v", err)
}
log.Infof("Config written to %s", t.Driver.Host)
return nil
}

func (t *ScrapliTransport) Close() {
t.Driver.Close()
log.Infof("Connection to %s closed successfully", t.Driver.Host)
}
15 changes: 8 additions & 7 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ func configRun(_ *cobra.Command, args []string) error {
return err
}

allConfig := config.PrepareVars(c.Nodes, c.Links)
configs := config.PrepareVars(c.Nodes, c.Links)

err = config.RenderAll(allConfig)
err = config.RenderAll(configs)
if err != nil {
return err
}
Expand All @@ -96,10 +96,10 @@ func configRun(_ *cobra.Command, args []string) error {
}

var wg sync.WaitGroup
deploy := func(n string) {
configFn := func(n string) {
defer wg.Done()

cs, ok := allConfig[n]
cs, ok := configs[n]
if !ok {
log.Errorf("Invalid node in filter: %s", n)
return
Expand All @@ -110,13 +110,14 @@ func configRun(_ *cobra.Command, args []string) error {
log.Warnf("%s: %s", cs.TargetNode.ShortName, err)
}
}

wg.Add(len(configFilter))
for _, node := range configFilter {
// On debug this will not be executed concurrently
if log.IsLevelEnabled(log.DebugLevel) {
deploy(node)
configFn(node)
} else {
go deploy(node)
go configFn(node)
}
}
wg.Wait()
Expand Down Expand Up @@ -148,7 +149,7 @@ func init() {
configCmd.Flags().StringSliceVarP(&config.TemplatePaths, "template-path", "p", []string{}, "comma separated list of paths to search for templates")
_ = configCmd.MarkFlagDirname("template-path")
configCmd.Flags().StringSliceVarP(&config.TemplateNames, "template-list", "l", []string{}, "comma separated list of template names to render")
configCmd.Flags().StringSliceVarP(&configFilter, "filter", "f", []string{}, "comma separated list of nodes to include")
configCmd.Flags().StringSliceVarP(&configFilter, "filter", "f", []string{}, "comma separated list of nodes to include")
configCmd.Flags().SortFlags = false

configCmd.AddCommand(configSendCmd)
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/srl-labs/containerlab
go 1.17

require (
github.com/AlekSi/pointer v1.2.0
github.com/awalterschulze/gographviz v2.0.1+incompatible
github.com/cloudflare/cfssl v1.4.1
github.com/containerd/containerd v1.5.10
Expand Down Expand Up @@ -68,6 +69,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee // indirect
github.com/carlmontanari/difflibgo v0.0.0-20210718170140-424f52054f94 // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
Expand Down Expand Up @@ -212,7 +214,7 @@ require (
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 // indirect
go.opencensus.io v0.23.0 // indirect
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect
gocloud.dev v0.24.0 // indirect
golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b // indirect
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
Expand Down
6 changes: 5 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg=
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0=
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
github.com/Azure/azure-amqp-common-go/v3 v3.1.0/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0=
github.com/Azure/azure-amqp-common-go/v3 v3.1.1/go.mod h1:YsDaPfaO9Ub2XeSKdIy2DfwuiQlHQCauHJwSqtrkECI=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
Expand Down Expand Up @@ -273,6 +275,7 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee h1:BnPxIde0gjtTnc9Er7cxvBk8DHLWhEux0SxayC8dP6I=
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/carlmontanari/difflibgo v0.0.0-20210718170140-424f52054f94 h1:NGwk1xS2VoCNj8Y7RSVM/oEI6p7yUihghKjNbY5QRDs=
github.com/carlmontanari/difflibgo v0.0.0-20210718170140-424f52054f94/go.mod h1:+3MuSIeC3qmdSesR12cTLeb47R/Vvo+bHdB6hC5HShk=
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
Expand Down Expand Up @@ -1737,8 +1740,9 @@ go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg=
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
gocloud.dev v0.24.0 h1:cNtHD07zQQiv02OiwwDyVMuHmR7iQt2RLkzoAgz7wBs=
gocloud.dev v0.24.0/go.mod h1:uA+als++iBX5ShuG4upQo/3Zoz49iIPlYUWHV5mM8w8=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
Expand Down
3 changes: 3 additions & 0 deletions nodes/crpd/crpd.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ func createCRPDFiles(nodeCfg *types.NodeConfig) error {
log.Debug("Writing sshd_config succeeded")

if nodeCfg.License != "" {
// create license directory path /config/license/safenet
utils.CreateDirectory(path.Join(nodeCfg.LabDir, "/config/license/safenet"), 0777)

// copy license file to node specific lab directory
src := nodeCfg.License
dst = filepath.Join(nodeCfg.LabDir, licDir, "junos_sfnt.lic")
Expand Down
1 change: 1 addition & 0 deletions nodes/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ var DefaultConfigTemplates = map[string]string{
// DefaultCredentials holds default username and password per each kind
var DefaultCredentials = map[string][]string{
"srl": {"admin", "admin"},
"crpd": {"root", "clab123", "clab123"},
"vr-pan": {"admin", "Admin@123"},
"vr-n9kv": {"admin", "admin"},
"vr-ftosv": {"admin", "admin"},
Expand Down
Loading