Skip to content

Commit

Permalink
Merge pull request #482 from networkop/cumulus-ignite
Browse files Browse the repository at this point in the history
Adding support for Cumulus with Ignite
  • Loading branch information
hellt committed Jul 6, 2021
2 parents 86e34f3 + 2d8b6f9 commit 4d5938e
Show file tree
Hide file tree
Showing 73 changed files with 2,603 additions and 357 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ lab-examples/**/clab-*

# ignore the following files/directories
graph/*

license.key
container-lab
containerlab
site/
.vscode/
.DS_Store
__rd*
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ lint:

clint:
docker run -it --rm -v $$(pwd):/app -w /app golangci/golangci-lint:v1.40.1 golangci-lint run -v

docs:
docker run -v $$(pwd):/docs --entrypoint mkdocs squidfunk/mkdocs-material:7.1.8 build --clean --strict

82 changes: 60 additions & 22 deletions clab/clab.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package clab

import (
"context"
"fmt"
"os"
"sync"
"time"
Expand All @@ -20,13 +21,14 @@ import (
)

type CLab struct {
Config *Config
TopoFile *TopoFile
m *sync.RWMutex
Nodes map[string]nodes.Node
Links map[int]*types.Link
Runtime runtime.ContainerRuntime
Dir *Directory
Config *Config
TopoFile *TopoFile
m *sync.RWMutex
Nodes map[string]nodes.Node
Links map[int]*types.Link
Runtimes map[string]runtime.ContainerRuntime
globalRuntime string
Dir *Directory

debug bool
timeout time.Duration
Expand Down Expand Up @@ -66,10 +68,11 @@ func WithRuntime(name string, d bool, dur time.Duration, gracefulShutdown bool)
default:
name = runtime.DockerRuntime
}
c.globalRuntime = name

if rInit, ok := runtime.ContainerRuntimes[name]; ok {
c.Runtime = rInit()
err := c.Runtime.Init(
r := rInit()
err := r.Init(
runtime.WithConfig(&runtime.RuntimeConfig{
Timeout: dur,
Debug: d,
Expand All @@ -79,6 +82,9 @@ func WithRuntime(name string, d bool, dur time.Duration, gracefulShutdown bool)
if err != nil {
log.Fatalf("failed to init the container runtime: %s", err)
}

c.Runtimes[name] = r

return
}
log.Fatalf("unknown container runtime %q", name)
Expand Down Expand Up @@ -109,7 +115,7 @@ func WithGracefulShutdown(gracefulShutdown bool) ClabOption {
}

// NewContainerLab function defines a new container lab
func NewContainerLab(opts ...ClabOption) *CLab {
func NewContainerLab(opts ...ClabOption) (*CLab, error) {
c := &CLab{
Config: &Config{
Mgmt: new(types.MgmtNet),
Expand All @@ -119,13 +125,16 @@ func NewContainerLab(opts ...ClabOption) *CLab {
m: new(sync.RWMutex),
Nodes: make(map[string]nodes.Node),
Links: make(map[int]*types.Link),
Runtimes: make(map[string]runtime.ContainerRuntime),
}

for _, o := range opts {
o(c)
}

return c
err := c.parseTopology()

return c, err
}

// initMgmtNetwork sets management network config
Expand All @@ -150,6 +159,10 @@ func (c *CLab) initMgmtNetwork() error {
return nil
}

func (c *CLab) GlobalRuntime() runtime.ContainerRuntime {
return c.Runtimes[c.globalRuntime]
}

func (c *CLab) CreateNodes(ctx context.Context, workers uint) {
wg := new(sync.WaitGroup)
wg.Add(int(workers))
Expand All @@ -173,7 +186,7 @@ func (c *CLab) CreateNodes(ctx context.Context, workers uint) {
continue
}
// Deploy
err = node.Deploy(ctx, c.Runtime)
err = node.Deploy(ctx)
if err != nil {
log.Errorf("failed deploy phase for node %q: %v", node.Config().ShortName, err)
continue
Expand Down Expand Up @@ -247,37 +260,62 @@ func (c *CLab) CreateLinks(ctx context.Context, workers uint, postdeploy bool) {
wg.Wait()
}

func (c *CLab) DeleteNodes(ctx context.Context, workers uint, containers []types.GenericContainer) {
func (c *CLab) DeleteNodes(ctx context.Context, workers uint, deleteCandidates map[string]nodes.Node) {
wg := new(sync.WaitGroup)

ctrChan := make(chan *types.GenericContainer)
nodeChan := make(chan nodes.Node)
wg.Add(int(workers))
for i := uint(0); i < workers; i++ {
go func(i uint) {
defer wg.Done()
for {
select {
case cont := <-ctrChan:
if cont == nil {
case n := <-nodeChan:
if n == nil {
log.Debugf("Worker %d terminating...", i)
return
}
err := c.Runtime.DeleteContainer(ctx, cont)
err := n.Delete(ctx)
if err != nil {
log.Errorf("could not remove container '%s': %v", cont.ID, err)
log.Errorf("could not remove container %q: %v", n.Config().LongName, err)
}
case <-ctx.Done():
return
}
}
}(i)
}
for _, ctr := range containers {
ctr := ctr
ctrChan <- &ctr
for _, n := range deleteCandidates {
nodeChan <- n
}
close(ctrChan)
close(nodeChan)

wg.Wait()

}

func (c *CLab) ListContainers(ctx context.Context, labels []*types.GenericFilter) ([]types.GenericContainer, error) {
var containers []types.GenericContainer

for _, r := range c.Runtimes {
ctrs, err := r.ListContainers(ctx, labels)
if err != nil {
return containers, fmt.Errorf("could not list containers: %v", err)
}
containers = append(containers, ctrs...)
}
return containers, nil
}

func (c *CLab) GetNodeRuntime(query string) (runtime.ContainerRuntime, error) {
shortName, err := getShortName(c.Config.Name, query)
if err != nil {
return nil, err
}

if node, ok := c.Nodes[shortName]; ok {
return node.GetRuntime(), nil
}

return nil, fmt.Errorf("could not find a container matching name %q", query)
}
97 changes: 79 additions & 18 deletions clab/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/mitchellh/go-homedir"
log "github.com/sirupsen/logrus"
"github.com/srl-labs/containerlab/nodes"
clabRuntimes "github.com/srl-labs/containerlab/runtime"
"github.com/srl-labs/containerlab/types"
"github.com/srl-labs/containerlab/utils"
"github.com/vishvananda/netlink"
Expand Down Expand Up @@ -57,6 +58,7 @@ var kinds = []string{
"ovs-bridge",
"mysocketio",
"host",
"cvx",
}

// Config defines lab configuration as it is provided in the YAML file
Expand All @@ -68,7 +70,7 @@ type Config struct {
}

// ParseTopology parses the lab topology
func (c *CLab) ParseTopology() error {
func (c *CLab) parseTopology() error {
log.Infof("Parsing & checking topology file: %s", c.TopoFile.fullName)
log.Debugf("Lab name: %s", c.Config.Name)

Expand All @@ -92,9 +94,52 @@ func (c *CLab) ParseTopology() error {
nodeNames = append(nodeNames, nodeName)
}
sort.Strings(nodeNames)

// collect node runtimes in a map[NodeName] -> RuntimeName
var nodeRuntimes = make(map[string]string)

for nodeName, topologyNode := range c.Config.Topology.Nodes {
// this case is when runtime was overridden at the node level
if r := c.Config.Topology.GetNodeRuntime(nodeName); r != "" {
nodeRuntimes[nodeName] = r
continue
}

// this case if for non-default runtimes overriding the global default
if r, ok := nodes.NonDefaultRuntimes[topologyNode.GetKind()]; ok {
nodeRuntimes[nodeName] = r
continue
}

// saving the global default runtime
nodeRuntimes[nodeName] = c.globalRuntime
}

// initialize any extra runtimes
for _, r := range nodeRuntimes {
// this is the case for already init'ed runtimes
if _, ok := c.Runtimes[r]; ok {
continue
}

if rInit, ok := clabRuntimes.ContainerRuntimes[r]; ok {

newRuntime := rInit()
defaultConfig := c.Runtimes[c.globalRuntime].Config()
err := newRuntime.Init(
clabRuntimes.WithConfig(&defaultConfig),
)
if err != nil {
return fmt.Errorf("failed to init the container runtime: %s", err)
}

c.Runtimes[r] = newRuntime
}
}

var err error
for idx, nodeName := range nodeNames {
err = c.NewNode(nodeName, c.Config.Topology.Nodes[nodeName], idx)
err = c.NewNode(nodeName, nodeRuntimes[nodeName], c.Config.Topology.Nodes[nodeName], idx)
if err != nil {
return err
}
Expand All @@ -103,11 +148,12 @@ func (c *CLab) ParseTopology() error {
// i represents the endpoint integer and l provide the link struct
c.Links[i] = c.NewLink(l)
}

return nil
}

// NewNode initializes a new node object
func (c *CLab) NewNode(nodeName string, nodeDef *types.NodeDefinition, idx int) error {
func (c *CLab) NewNode(nodeName, nodeRuntime string, nodeDef *types.NodeDefinition, idx int) error {
nodeCfg, err := c.createNodeCfg(nodeName, nodeDef, idx)
if err != nil {
return err
Expand All @@ -116,11 +162,12 @@ func (c *CLab) NewNode(nodeName string, nodeDef *types.NodeDefinition, idx int)
// Init
nodeInitializer, ok := nodes.Nodes[nodeCfg.Kind]
if !ok {
log.Fatalf("node %q refers to a kind %q which is not supported. Supported kinds are %q", nodeCfg.ShortName, nodeCfg.Kind, kinds)
return fmt.Errorf("node %q refers to a kind %q which is not supported. Supported kinds are %q", nodeCfg.ShortName, nodeCfg.Kind, kinds)
}
n := nodeInitializer()
// Init
err = n.Init(nodeCfg, nodes.WithMgmtNet(c.Config.Mgmt))

err = n.Init(nodeCfg, nodes.WithRuntime(c.Runtimes[nodeRuntime]), nodes.WithMgmtNet(c.Config.Mgmt))
if err != nil {
log.Errorf("failed to initialize node %q: %v", nodeCfg.ShortName, err)
return fmt.Errorf("failed to initialize node %q: %v", nodeCfg.ShortName, err)
Expand Down Expand Up @@ -161,6 +208,9 @@ func (c *CLab) createNodeCfg(nodeName string, nodeDef *types.NodeDefinition, idx
Publish: c.Config.Topology.GetNodePublish(nodeName),
Sysctls: make(map[string]string),
Endpoints: make([]*types.Endpoint, 0),
Sandbox: c.Config.Topology.GetNodeSandbox(nodeName),
Kernel: c.Config.Topology.GetNodeKernel(nodeName),
Runtime: c.Config.Topology.GetNodeRuntime(nodeName),
}

log.Debugf("node config: %+v", nodeCfg)
Expand Down Expand Up @@ -323,21 +373,22 @@ func (c *CLab) verifyLinks() error {
// VerifyImages will check if image referred in the node config
// either pullable or is available in the local image store
func (c *CLab) VerifyImages(ctx context.Context) error {
images := map[string]struct{}{}

images := make(map[string]string)

for _, node := range c.Nodes {
// skip image verification for bridge kinds
if node.Config().Kind == "bridge" || node.Config().Kind == "ovs-bridge" {
continue
}
if node.Config().Image == "" {
return fmt.Errorf("missing required image for node %s", node.Config().ShortName)

for _, imageName := range node.GetImages() {
if imageName == "" {
return fmt.Errorf("missing required image for node %q", node.Config().ShortName)
}
images[imageName] = node.GetRuntime().GetName()
}

images[node.Config().Image] = struct{}{}
}

for image := range images {
err := c.Runtime.PullImageIfRequired(ctx, image)
for image, runtimeName := range images {
err := c.Runtimes[runtimeName].PullImageIfRequired(ctx, image)
if err != nil {
return err
}
Expand All @@ -351,10 +402,11 @@ func (c *CLab) VerifyContainersUniqueness(ctx context.Context) error {
nctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()

containers, err := c.Runtime.ListContainers(nctx, nil)
containers, err := c.ListContainers(nctx, nil)
if err != nil {
return fmt.Errorf("could not list containers: %v", err)
return err
}

if len(containers) == 0 {
return nil
}
Expand All @@ -380,7 +432,7 @@ func (c *CLab) VerifyContainersUniqueness(ctx context.Context) error {
}
}

return err
return nil
}

// verifyHostIfaces ensures that host interfaces referenced in the topology
Expand Down Expand Up @@ -556,3 +608,12 @@ func sysMemory(v string) uint64 {
}
return m
}

// returns nodeCfg.ShortName based on the provided containerName and labName
func getShortName(labName, containerName string) (string, error) {
result := strings.Split(containerName, "-"+labName+"-")
if len(result) != 2 {
return "", fmt.Errorf("failed to parse container name %q", containerName)
}
return result[1], nil
}
Loading

0 comments on commit 4d5938e

Please sign in to comment.