Skip to content

Commit

Permalink
Merge pull request #1450 from steiler/newLinkInputFormat
Browse files Browse the repository at this point in the history
Implement new Links format
  • Loading branch information
hellt committed Jul 20, 2023
2 parents 15e7775 + 06e7e25 commit 56005c0
Show file tree
Hide file tree
Showing 14 changed files with 661 additions and 86 deletions.
37 changes: 7 additions & 30 deletions clab/clab.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@ func filterClabNodes(c *CLab, nodeFilter []string) error {

log.Infof("Applying node filter: %q", nodeFilter)

// newNodes := make(map[string]*types.NodeDefinition, len(c.Config.Topology.Nodes))
newLinks := make([]*types.LinkConfig, 0, len(c.Config.Topology.Links))

// filter nodes
for name := range c.Config.Topology.Nodes {
if exists := slices.Contains(nodeFilter, name); !exists {
Expand All @@ -153,35 +150,15 @@ func filterClabNodes(c *CLab, nodeFilter []string) error {
}

// filter links
for _, l := range c.Config.Topology.Links {
// get the endpoints of the link and extract the node names
// to remove the links which have either side in the node filter
splitEpAside := strings.Split(l.Endpoints[0], ":")
if len(splitEpAside) != 2 {
continue
}

epA := splitEpAside[0]

splitEpBside := strings.Split(l.Endpoints[1], ":")
if len(splitEpBside) != 2 {
continue
}

epB := splitEpBside[0]

containsAside := slices.Contains(nodeFilter, epA)
containsBside := slices.Contains(nodeFilter, epB)

// if both endpoints of a link belong to the node filter, keep the link
if containsAside && containsBside {
log.Debugf("Including link %+v", l)
newLinks = append(newLinks, l)
for id, l := range c.Links {
for _, nodeName := range []string{l.A.Node.ShortName, l.B.Node.ShortName} {
// if both endpoints of a link belong to the node filter, keep the link
if !slices.Contains(nodeFilter, nodeName) {
delete(c.Links, id)
break
}
}
}
// replace the original collection of links with the links that have both endpoints in the node filter
c.Config.Topology.Links = newLinks

return nil
}

Expand Down
124 changes: 95 additions & 29 deletions clab/clab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,22 @@ func Test_filterClabNodes(t *testing.T) {
},
"two nodes, one link between them, one filter node": {
c: &CLab{
Links: map[int]*types.Link{
0: {
A: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node1",
},
EndpointName: "eth1",
},
B: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node2",
},
EndpointName: "eth2",
},
},
},
Config: &Config{
Topology: &types.Topology{
Nodes: map[string]*types.NodeDefinition{
Expand All @@ -270,11 +286,6 @@ func Test_filterClabNodes(t *testing.T) {
Kind: "linux",
},
},
Links: []*types.LinkConfig{
{
Endpoints: []string{"node1:eth1", "node2:eth2"},
},
},
},
},
},
Expand All @@ -285,6 +296,22 @@ func Test_filterClabNodes(t *testing.T) {
},
"two nodes, one link between them, no filter": {
c: &CLab{
Links: map[int]*types.Link{
0: {
A: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node1",
},
EndpointName: "eth1",
},
B: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node2",
},
EndpointName: "eth1",
},
},
},
Config: &Config{
Topology: &types.Topology{
Nodes: map[string]*types.NodeDefinition{
Expand All @@ -295,11 +322,6 @@ func Test_filterClabNodes(t *testing.T) {
Kind: "linux",
},
},
Links: []*types.LinkConfig{
{
Endpoints: []string{"node1:eth1", "node2:eth1"},
},
},
},
},
},
Expand All @@ -310,6 +332,36 @@ func Test_filterClabNodes(t *testing.T) {
},
"three nodes, two links, two nodes in the filter": {
c: &CLab{
Links: map[int]*types.Link{
0: {
A: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node1",
},
EndpointName: "eth1",
},
B: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node2",
},
EndpointName: "eth1",
},
},
1: {
A: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node2",
},
EndpointName: "eth2",
},
B: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node3",
},
EndpointName: "eth2",
},
},
},
Config: &Config{
Topology: &types.Topology{
Nodes: map[string]*types.NodeDefinition{
Expand All @@ -323,14 +375,6 @@ func Test_filterClabNodes(t *testing.T) {
Kind: "linux",
},
},
Links: []*types.LinkConfig{
{
Endpoints: []string{"node1:eth1", "node2:eth1"},
},
{
Endpoints: []string{"node2:eth2", "node3:eth2"},
},
},
},
},
},
Expand All @@ -341,6 +385,36 @@ func Test_filterClabNodes(t *testing.T) {
},
"three nodes, two links, one nodes in the filter": {
c: &CLab{
Links: map[int]*types.Link{
0: {
A: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node1",
},
EndpointName: "eth1",
},
B: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node2",
},
EndpointName: "eth1",
},
},
1: {
A: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node2",
},
EndpointName: "eth2",
},
B: &types.Endpoint{
Node: &types.NodeConfig{
ShortName: "node3",
},
EndpointName: "eth2",
},
},
},
Config: &Config{
Topology: &types.Topology{
Nodes: map[string]*types.NodeDefinition{
Expand All @@ -354,14 +428,6 @@ func Test_filterClabNodes(t *testing.T) {
Kind: "linux",
},
},
Links: []*types.LinkConfig{
{
Endpoints: []string{"node1:eth1", "node2:eth1"},
},
{
Endpoints: []string{"node2:eth2", "node3:eth2"},
},
},
},
},
},
Expand Down Expand Up @@ -413,9 +479,9 @@ func Test_filterClabNodes(t *testing.T) {
// sort the nodes to make the test deterministic
slices.Sort(filteredNodes)

filteredLinks := make([][]string, 0, len(tt.c.Config.Topology.Links))
for _, l := range tt.c.Config.Topology.Links {
filteredLinks = append(filteredLinks, l.Endpoints)
filteredLinks := make([][]string, 0, len(tt.c.Links))
for _, l := range tt.c.Links {
filteredLinks = append(filteredLinks, []string{l.A.String(), l.B.String()})
}

if cmp.Diff(filteredNodes, tt.wantNodes) != "" {
Expand Down
31 changes: 12 additions & 19 deletions clab/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ func (c *CLab) processStartupConfig(nodeCfg *types.NodeConfig) error {
return nil
}

// NewLink initializes a new link object.
func (c *CLab) NewLink(l *types.LinkConfig) *types.Link {
// NewLink initializes a new link object from the link definition provided via topology file.
func (c *CLab) NewLink(l *types.LinkDefinition) *types.Link {
if len(l.Endpoints) != 2 {
log.Fatalf("endpoint %q has wrong syntax, unexpected number of items", l.Endpoints) // skipcq: RVV-A0003
}
Expand Down Expand Up @@ -404,6 +404,7 @@ func (c *CLab) NewEndpoint(e string) *types.Endpoint {
}

// CheckTopologyDefinition runs topology checks and returns any errors found.
// This function runs after topology file is parsed and all nodes/links are initialized.
func (c *CLab) CheckTopologyDefinition(ctx context.Context) error {
var err error

Expand Down Expand Up @@ -431,22 +432,23 @@ func (c *CLab) CheckTopologyDefinition(ctx context.Context) error {
return nil
}

// verifyLinks checks if all the endpoints in the links section of the topology file
// appear only once.
func (c *CLab) verifyLinks() error {
endpoints := map[string]struct{}{}
// dups accumulates duplicate links
dups := []string{}
for _, lc := range c.Config.Topology.Links {
for _, e := range lc.Endpoints {
if err := checkEndpoint(e); err != nil {
return err
}
if _, ok := endpoints[e]; ok {
dups = append(dups, e)
for _, l := range c.Links {
for _, e := range []*types.Endpoint{l.A, l.B} {
e_string := e.String()
if _, ok := endpoints[e_string]; ok {
dups = append(dups, e_string)
}
endpoints[e] = struct{}{}
endpoints[e_string] = struct{}{}
}
}
if len(dups) != 0 {
sort.Strings(dups) // sort for deterministic error message
return fmt.Errorf("endpoints %q appeared more than once in the links section of the topology file", dups)
}
return nil
Expand Down Expand Up @@ -592,15 +594,6 @@ func (c *CLab) verifyRootNetnsInterfaceUniqueness() error {
return nil
}

// checkEndpoint runs checks on the endpoint syntax.
func checkEndpoint(e string) error {
split := strings.Split(e, ":")
if len(split) != 2 {
return fmt.Errorf("malformed endpoint definition: %s", e)
}
return nil
}

// resolveBindPaths resolves the host paths in a bind string, such as /hostpath:/remotepath(:options) string
// it allows host path to have `~` and relative path to an absolute path
// the list of binds will be changed in place.
Expand Down
16 changes: 10 additions & 6 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,16 @@ func generateTopologyConfig(name, network, ipv4range, ipv6range string,
Type: nodes[i+1].typ,
}
}
config.Topology.Links = append(config.Topology.Links, &types.LinkConfig{
Endpoints: []string{
node1 + ":" + fmt.Sprintf(interfaceFormat[nodes[i].kind], k+1+interfaceOffset),
node2 + ":" + fmt.Sprintf(interfaceFormat[nodes[i+1].kind], j+1),
},
})
config.Topology.Links = append(config.Topology.Links,
&types.LinkDefinition{
// Type: string(types.LinkTypeBrief),
LinkConfig: types.LinkConfig{
Endpoints: []string{
node1 + ":" + fmt.Sprintf(interfaceFormat[nodes[i].kind], k+1+interfaceOffset),
node2 + ":" + fmt.Sprintf(interfaceFormat[nodes[i+1].kind], j+1),
},
},
})
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions types/endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package types

type EndpointRaw struct {
Node string `yaml:"node"`
Iface string `yaml:"interface"`
Mac string `yaml:"mac,omitempty"`
}
Loading

0 comments on commit 56005c0

Please sign in to comment.