diff --git a/clab/config.go b/clab/config.go index b4cd84607..c632605e1 100644 --- a/clab/config.go +++ b/clab/config.go @@ -33,6 +33,8 @@ const ( hostNSPath = "__host" // veth link mtu. jacked up to 65k to allow jumbo testing of various sizes defaultVethLinkMTU = 65000 + // containerlab's reserved OUI + clabOUI = "aa:c1:ab" ) // supported kinds @@ -368,6 +370,7 @@ func (c *CLab) NewNode(nodeName string, nodeCfg NodeConfig, idx int) error { node.Fqdn = nodeName + "." + c.Config.Name + ".io" node.LabDir = c.Dir.Lab + "/" + nodeName node.Index = idx + node.Endpoints = []*types.Endpoint{} node.NetworkMode = strings.ToLower(nodeCfg.NetworkMode) @@ -513,6 +516,10 @@ func (c *CLab) NewEndpoint(e string) *types.Endpoint { } nName := split[0] // node name epName := split[1] // endpoint name + + // generate unqiue MAC + endpoint.MAC = genMac(clabOUI) + // search the node pointer for a node name referenced in endpoint section switch nName { // "host" is a special reference to host namespace @@ -532,8 +539,9 @@ func (c *CLab) NewEndpoint(e string) *types.Endpoint { } default: for name, n := range c.Nodes { - if name == split[0] { + if name == nName { endpoint.Node = n + n.Endpoints = append(n.Endpoints, endpoint) break } } diff --git a/clab/netlink.go b/clab/netlink.go index 105bd7b69..d3ed265d5 100644 --- a/clab/netlink.go +++ b/clab/netlink.go @@ -81,8 +81,18 @@ func (c *CLab) CreateVirtualWiring(l *types.Link) (err error) { BRndmName = l.B.EndpointName } + // Generate MAC addresses + aMAC, err := net.ParseMAC(l.A.MAC) + if err != nil { + return err + } + bMAC, err := net.ParseMAC(l.B.MAC) + if err != nil { + return err + } + // create veth pair in the root netns - vA.Link, vB.Link, err = createVethIface(ARndmName, BRndmName, l.MTU) + vA.Link, vB.Link, err = createVethIface(ARndmName, BRndmName, l.MTU, aMAC, bMAC) if err != nil { return err } @@ -108,14 +118,16 @@ func (c *CLab) CreateVirtualWiring(l *types.Link) (err error) { // createVethIface takes two veth endpoint structs and create a veth pair and return // veth interface links. -func createVethIface(ifName, peerName string, mtu int) (linkA netlink.Link, linkB netlink.Link, err error) { +func createVethIface(ifName, peerName string, mtu int, aMAC, bMAC net.HardwareAddr) (linkA netlink.Link, linkB netlink.Link, err error) { linkA = &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{ - Name: ifName, - Flags: net.FlagUp, - MTU: mtu, + Name: ifName, + HardwareAddr: aMAC, + Flags: net.FlagUp, + MTU: mtu, }, - PeerName: peerName, + PeerName: peerName, + PeerHardwareAddr: bMAC, } if err := netlink.LinkAdd(linkA); err != nil { diff --git a/cmd/destroy.go b/cmd/destroy.go index e0891e9e5..e8c9cc443 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -115,6 +115,7 @@ func init() { destroyCmd.Flags().BoolVarP(&cleanup, "cleanup", "", false, "delete lab directory") destroyCmd.Flags().BoolVarP(&graceful, "graceful", "", false, "attempt to stop containers before removing") destroyCmd.Flags().BoolVarP(&all, "all", "a", false, "destroy all containerlab labs") + destroyCmd.Flags().UintVarP(&maxWorkers, "max-workers", "", 0, "limit the maximum number of workers deleteing nodes") } func deleteEntriesFromHostsFile(containers []types.GenericContainer, bridgeName string) error { @@ -181,22 +182,44 @@ func destroyLab(ctx context.Context, c *clab.CLab) (err error) { labDir = filepath.Dir(containers[0].Labels["clab-node-lab-dir"]) } + if maxWorkers == 0 { + maxWorkers = uint(len(containers)) + } + log.Infof("Destroying container lab: %s", c.Config.Name) + ctrChan := make(chan *types.GenericContainer) wg := new(sync.WaitGroup) - wg.Add(len(containers)) - for _, cont := range containers { - go func(cont types.GenericContainer) { + wg.Add(int(maxWorkers)) + for i := uint(0); i < maxWorkers; i++ { + + go func(i uint) { defer wg.Done() - name := cont.ID - if len(cont.Names) > 0 { - name = strings.TrimLeft(cont.Names[0], "/") - } - err := c.Runtime.DeleteContainer(ctx, name) - if err != nil { - log.Errorf("could not remove container '%s': %v", name, err) + for { + select { + case cont := <-ctrChan: + if cont == nil { + log.Debugf("Worker %d terminating...", i) + return + } + name := cont.ID + if len(cont.Names) > 0 { + name = strings.TrimLeft(cont.Names[0], "/") + } + err := c.Runtime.DeleteContainer(ctx, name) + if err != nil { + log.Errorf("could not remove container '%s': %v", name, err) + } + case <-ctx.Done(): + return + } } - }(cont) + }(i) } + for _, ctr := range containers { + ctrChan <- &ctr + } + close(ctrChan) + wg.Wait() // remove the lab directories diff --git a/types/types.go b/types/types.go index ed2148ec9..ae7913917 100644 --- a/types/types.go +++ b/types/types.go @@ -24,6 +24,8 @@ type Endpoint struct { Node *Node // e1-x, eth, etc EndpointName string + // mac address + MAC string } // mgmtNet struct defines the management network options @@ -77,6 +79,8 @@ type Node struct { Publish []string //list of ports to publish with mysocketctl // container labels Labels map[string]string + // Slice of pointers to local endpoints + Endpoints []*Endpoint } // GenerateConfig generates configuration for the nodes