summaryrefslogtreecommitdiffstats
path: root/extras
diff options
context:
space:
mode:
Diffstat (limited to 'extras')
-rw-r--r--extras/hs-test/actions.go80
-rw-r--r--extras/hs-test/container.go115
-rw-r--r--extras/hs-test/echo_test.go17
-rw-r--r--extras/hs-test/framework_test.go141
-rw-r--r--extras/hs-test/go.mod6
-rw-r--r--extras/hs-test/go.sum82
-rw-r--r--extras/hs-test/hst_suite.go289
-rw-r--r--extras/hs-test/http_test.go16
-rw-r--r--extras/hs-test/ldp_test.go16
-rw-r--r--extras/hs-test/netconfig.go176
-rw-r--r--extras/hs-test/suite_veth_test.go84
-rw-r--r--extras/hs-test/topo-containers/2peerVeth.yaml20
-rw-r--r--extras/hs-test/topo-network/2peerVeth.yaml1
-rw-r--r--extras/hs-test/utils.go2
-rw-r--r--extras/hs-test/vcl_test.go73
-rw-r--r--extras/hs-test/vppinstance.go190
16 files changed, 891 insertions, 417 deletions
diff --git a/extras/hs-test/actions.go b/extras/hs-test/actions.go
index 279589b5d41..60398704763 100644
--- a/extras/hs-test/actions.go
+++ b/extras/hs-test/actions.go
@@ -1,7 +1,6 @@
package main
import (
- "bytes"
"context"
"fmt"
"os"
@@ -47,17 +46,6 @@ func configureProxyTcp(ifName0, ipAddr0, ifName1, ipAddr1 string) ConfFn {
}
}
-func (a *Actions) RunHttpCliSrv(args []string) *ActionResult {
- cmd := fmt.Sprintf("http cli server")
- return ApiCliInband(workDir, cmd)
-}
-
-func (a *Actions) RunHttpCliCln(args []string) *ActionResult {
- cmd := fmt.Sprintf("http cli client uri http://10.10.10.1/80 query %s", getArgs())
- fmt.Println(cmd)
- return ApiCliInband(workDir, cmd)
-}
-
func (a *Actions) ConfigureVppProxy(args []string) *ActionResult {
ctx, cancel := newVppContext()
defer cancel()
@@ -123,31 +111,6 @@ func ApiCliInband(root, cmd string) *ActionResult {
return NewActionResult(err, ActionResultWithStdout(cliInbandReply.Reply))
}
-func (a *Actions) RunEchoClient(args []string) *ActionResult {
- outBuff := bytes.NewBuffer([]byte{})
- errBuff := bytes.NewBuffer([]byte{})
-
- cmd := fmt.Sprintf("vpp_echo client socket-name %s/var/run/app_ns_sockets/2 use-app-socket-api uri %s://10.10.10.1/12344", workDir, args[2])
- err := exechelper.Run(cmd,
- exechelper.WithStdout(outBuff), exechelper.WithStderr(errBuff),
- exechelper.WithStdout(os.Stdout), exechelper.WithStderr(os.Stderr))
-
- return NewActionResult(err, ActionResultWithStdout(string(outBuff.String())),
- ActionResultWithStderr(string(errBuff.String())))
-}
-
-func (a *Actions) RunEchoServer(args []string) *ActionResult {
- cmd := fmt.Sprintf("vpp_echo server TX=RX socket-name %s/var/run/app_ns_sockets/1 use-app-socket-api uri %s://10.10.10.1/12344", workDir, args[2])
- errCh := exechelper.Start(cmd)
- select {
- case err := <-errCh:
- writeSyncFile(NewActionResult(err, ActionResultWithDesc("echo_server: ")))
- default:
- }
- writeSyncFile(OkResult())
- return nil
-}
-
func (a *Actions) RunEchoSrvInternal(args []string) *ActionResult {
cmd := fmt.Sprintf("test echo server %s uri tcp://10.10.10.1/1234", getArgs())
return ApiCliInband(workDir, cmd)
@@ -158,49 +121,6 @@ func (a *Actions) RunEchoClnInternal(args []string) *ActionResult {
return ApiCliInband(workDir, cmd)
}
-func (a *Actions) RunVclEchoServer(args []string) *ActionResult {
- f, err := os.Create("vcl_1.conf")
- if err != nil {
- return NewActionResult(err, ActionResultWithStderr(("create vcl config: ")))
- }
- socketPath := fmt.Sprintf("%s/var/run/app_ns_sockets/1", workDir)
- fmt.Fprintf(f, vclTemplate, socketPath, "1")
- f.Close()
-
- os.Setenv("VCL_CONFIG", "./vcl_1.conf")
- cmd := fmt.Sprintf("vcl_test_server -p %s 12346", args[2])
- errCh := exechelper.Start(cmd)
- select {
- case err := <-errCh:
- writeSyncFile(NewActionResult(err, ActionResultWithDesc("vcl_test_server: ")))
- default:
- }
- writeSyncFile(OkResult())
- return nil
-}
-
-func (a *Actions) RunVclEchoClient(args []string) *ActionResult {
- outBuff := bytes.NewBuffer([]byte{})
- errBuff := bytes.NewBuffer([]byte{})
-
- f, err := os.Create("vcl_2.conf")
- if err != nil {
- return NewActionResult(err, ActionResultWithStderr(("create vcl config: ")))
- }
- socketPath := fmt.Sprintf("%s/var/run/app_ns_sockets/2", workDir)
- fmt.Fprintf(f, vclTemplate, socketPath, "2")
- f.Close()
-
- os.Setenv("VCL_CONFIG", "./vcl_2.conf")
- cmd := fmt.Sprintf("vcl_test_client -U -p %s 10.10.10.1 12346", args[2])
- err = exechelper.Run(cmd,
- exechelper.WithStdout(outBuff), exechelper.WithStderr(errBuff),
- exechelper.WithStdout(os.Stdout), exechelper.WithStderr(os.Stderr))
-
- return NewActionResult(err, ActionResultWithStdout(string(outBuff.String())),
- ActionResultWithStderr(string(errBuff.String())))
-}
-
func configure2vethsTopo(ifName, interfaceAddress, namespaceId string, secret uint64, optionalHardwareAddress ...string) ConfFn {
return func(ctx context.Context,
vppConn api.Connection) error {
diff --git a/extras/hs-test/container.go b/extras/hs-test/container.go
index 971f3b60ef9..91ca2c2b0b1 100644
--- a/extras/hs-test/container.go
+++ b/extras/hs-test/container.go
@@ -3,24 +3,27 @@ package main
import (
"fmt"
"os"
+ "os/exec"
"strings"
"github.com/edwarnicke/exechelper"
)
type Volume struct {
- hostDir string
- containerDir string
+ hostDir string
+ containerDir string
+ isDefaultWorkDir bool
}
type Container struct {
+ suite *HstSuite
isOptional bool
name string
image string
- workDir string
extraRunningArgs string
volumes map[string]Volume
envVars map[string]string
+ vppInstance *VppInstance
}
func NewContainer(yamlInput ContainerConfig) (*Container, error) {
@@ -59,25 +62,53 @@ func NewContainer(yamlInput ContainerConfig) (*Container, error) {
volumeMap := volu.(ContainerConfig)
hostDir := r.Replace(volumeMap["host-dir"].(string))
containerDir := volumeMap["container-dir"].(string)
- container.addVolume(hostDir, containerDir)
+ isDefaultWorkDir := false
- if isDefaultWorkDir, ok := volumeMap["is-default-work-dir"]; ok &&
- isDefaultWorkDir.(bool) &&
- len(container.workDir) == 0 {
- container.workDir = containerDir
+ if isDefault, ok := volumeMap["is-default-work-dir"]; ok {
+ isDefaultWorkDir = isDefault.(bool)
}
+ container.addVolume(hostDir, containerDir, isDefaultWorkDir)
+
}
}
if _, ok := yamlInput["vars"]; ok {
for _, envVar := range yamlInput["vars"].([]interface{}) {
- container.addEnvVar(envVar)
+ envVarMap := envVar.(ContainerConfig)
+ name := envVarMap["name"].(string)
+ value := envVarMap["value"].(string)
+ container.addEnvVar(name, value)
}
}
return container, nil
}
+func (c *Container) getWorkDirVolume() (res Volume, exists bool) {
+ for _, v := range c.volumes {
+ if v.isDefaultWorkDir {
+ res = v
+ exists = true
+ return
+ }
+ }
+ return
+}
+
+func (c *Container) GetHostWorkDir() (res string) {
+ if v, ok := c.getWorkDirVolume(); ok {
+ res = v.hostDir
+ }
+ return
+}
+
+func (c *Container) GetContainerWorkDir() (res string) {
+ if v, ok := c.getWorkDirVolume(); ok {
+ res = v.containerDir
+ }
+ return
+}
+
func (c *Container) getRunCommand() string {
syncPath := fmt.Sprintf(" -v %s:/tmp/sync", c.getSyncPath())
cmd := "docker run --cap-add=all -d --privileged --network host --rm"
@@ -103,10 +134,11 @@ func (c *Container) run() error {
return nil
}
-func (c *Container) addVolume(hostDir string, containerDir string) {
+func (c *Container) addVolume(hostDir string, containerDir string, isDefaultWorkDir bool) {
var volume Volume
volume.hostDir = hostDir
volume.containerDir = containerDir
+ volume.isDefaultWorkDir = isDefaultWorkDir
c.volumes[hostDir] = volume
}
@@ -127,16 +159,13 @@ func (c *Container) getVolumesAsCliOption() string {
}
func (c *Container) getWorkDirAsCliOption() string {
- if len(c.workDir) == 0 {
- return ""
+ if _, ok := c.getWorkDirVolume(); ok {
+ return fmt.Sprintf(" --workdir=\"%s\"", c.GetContainerWorkDir())
}
- return fmt.Sprintf(" --workdir=\"%s\"", c.workDir)
+ return ""
}
-func (c *Container) addEnvVar(envVar interface{}) {
- envVarMap := envVar.(ContainerConfig)
- name := envVarMap["name"].(string)
- value := envVarMap["value"].(string)
+func (c *Container) addEnvVar(name string, value string) {
c.envVars[name] = value
}
@@ -156,8 +185,54 @@ func (c *Container) getSyncPath() string {
return fmt.Sprintf("/tmp/%s/sync", c.name)
}
+func (c *Container) newVppInstance(additionalConfig ...Stanza) (*VppInstance, error) {
+ vppConfig := new(VppConfig)
+ vppConfig.CliSocketFilePath = defaultCliSocketFilePath
+ if len(additionalConfig) > 0 {
+ vppConfig.additionalConfig = additionalConfig[0]
+ }
+
+ vpp := new(VppInstance)
+ vpp.container = c
+ vpp.config = vppConfig
+
+ c.vppInstance = vpp
+
+ return vpp, nil
+}
+
+func (c *Container) copy(sourceFileName string, targetFileName string) error {
+ cmd := exec.Command("docker", "cp", sourceFileName, c.name+":"+targetFileName)
+ return cmd.Run()
+}
+
+func (c *Container) createFile(destFileName string, content string) error {
+ f, err := os.CreateTemp("/tmp", "hst-config")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(f.Name())
+
+ if _, err := f.Write([]byte(content)); err != nil {
+ return err
+ }
+ if err := f.Close(); err != nil {
+ return err
+ }
+ c.copy(f.Name(), destFileName)
+ return nil
+}
+
+/*
+ * Executes in detached mode so that the started application can continue to run
+ * without blocking execution of test
+ */
+func (c *Container) execServer(command string) error {
+ return exechelper.Run("docker exec -d" + c.getEnvVarsAsCliOption() + " " + c.name + " " + command)
+}
+
func (c *Container) exec(command string) (string, error) {
- cliCommand := "docker exec -d " + c.name + " " + command
+ cliCommand := "docker exec" + c.getEnvVarsAsCliOption() + " " + c.name + " " + command
byteOutput, err := exechelper.CombinedOutput(cliCommand)
return string(byteOutput), err
}
@@ -187,5 +262,9 @@ func (c *Container) execAction(args string) (string, error) {
}
func (c *Container) stop() error {
+ if c.vppInstance != nil && c.vppInstance.apiChannel != nil {
+ c.vppInstance.disconnect()
+ }
+ c.vppInstance = nil
return exechelper.Run("docker stop " + c.name + " -t 0")
}
diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go
index 813297cef2a..1b24e08466d 100644
--- a/extras/hs-test/echo_test.go
+++ b/extras/hs-test/echo_test.go
@@ -1,18 +1,19 @@
package main
func (s *VethsSuite) TestEchoBuiltin() {
- serverContainer := s.getContainerByName("server-vpp")
- _, err := serverContainer.execAction("Configure2Veths srv")
- s.assertNil(err)
+ serverVpp := s.getContainerByName("server-vpp").vppInstance
+ serverVeth := s.veths["vppsrv"]
- clientContainer := s.getContainerByName("client-vpp")
- _, err = clientContainer.execAction("Configure2Veths cln")
+ _, err := serverVpp.vppctl("test echo server " +
+ " private-segment-size 1g fifo-size 4 no-echo" +
+ " uri tcp://" + serverVeth.Address() + "/1234")
s.assertNil(err)
- _, err = serverContainer.execAction("RunEchoSrvInternal private-segment-size 1g fifo-size 4 no-echo")
- s.assertNil(err)
+ clientVpp := s.getContainerByName("client-vpp").vppInstance
- o, err := clientContainer.execAction("RunEchoClnInternal nclients 10000 bytes 1 syn-timeout 100 test-timeout 100 no-return private-segment-size 1g fifo-size 4")
+ o, err := clientVpp.vppctl("test echo client nclients 10000 bytes 1" +
+ " syn-timeout 100 test-timeout 100 no-return private-segment-size 1g" +
+ " fifo-size 4 uri tcp://" + serverVeth.Address() + "/1234")
s.assertNil(err)
s.log(o)
}
diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go
index 76d8e58724f..6de8f167f70 100644
--- a/extras/hs-test/framework_test.go
+++ b/extras/hs-test/framework_test.go
@@ -1,152 +1,11 @@
package main
import (
- "io/ioutil"
- "os"
"testing"
- "github.com/edwarnicke/exechelper"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "gopkg.in/yaml.v3"
)
-func IsPersistent() bool {
- return os.Getenv("HST_PERSIST") == "1"
-}
-
-func IsVerbose() bool {
- return os.Getenv("HST_VERBOSE") == "1"
-}
-
-type HstSuite struct {
- suite.Suite
- teardownSuite func()
- containers map[string]*Container
- volumes []string
-}
-
-func (s *HstSuite) TearDownSuite() {
- s.teardownSuite()
-}
-
-func (s *HstSuite) TearDownTest() {
- if IsPersistent() {
- return
- }
- s.ResetContainers()
- s.RemoveVolumes()
-}
-
-func (s *HstSuite) SetupTest() {
- for _, volume := range s.volumes {
- cmd := "docker volume create --name=" + volume
- s.log(cmd)
- exechelper.Run(cmd)
- }
- for _, container := range s.containers {
- if container.isOptional == false {
- container.run()
- }
- }
-}
-
-func (s *HstSuite) hstFail() {
- s.T().FailNow()
-}
-
-func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
- if !assert.Nil(s.T(), object, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
- if !assert.NotNil(s.T(), object, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
- if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
- if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
- if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
- if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) log(args ...any) {
- if IsVerbose() {
- s.T().Log(args...)
- }
-}
-
-func (s *HstSuite) skip(args ...any) {
- s.log(args...)
- s.T().SkipNow()
-}
-
-func (s *HstSuite) ResetContainers() {
- for _, container := range s.containers {
- container.stop()
- }
-}
-
-func (s *HstSuite) RemoveVolumes() {
- for _, volumeName := range s.volumes {
- cmd := "docker volume rm " + volumeName
- exechelper.Run(cmd)
- }
-}
-
-func (s *HstSuite) getContainerByName(name string) *Container {
- return s.containers[name]
-}
-
-func (s *HstSuite) loadContainerTopology(topologyName string) {
- data, err := ioutil.ReadFile(ContainerTopologyDir + topologyName + ".yaml")
- if err != nil {
- s.T().Fatalf("read error: %v", err)
- }
- var yamlTopo YamlTopology
- err = yaml.Unmarshal(data, &yamlTopo)
- if err != nil {
- s.T().Fatalf("unmarshal error: %v", err)
- }
-
- for _, elem := range yamlTopo.Volumes {
- volumeMap := elem["volume"].(VolumeConfig)
- hostDir := volumeMap["host-dir"].(string)
- s.volumes = append(s.volumes, hostDir)
- }
-
- s.containers = make(map[string]*Container)
- for _, elem := range yamlTopo.Containers {
- newContainer, err := NewContainer(elem)
- if err != nil {
- s.T().Fatalf("config error: %v", err)
- }
- s.log(newContainer.getRunCommand())
- s.containers[newContainer.name] = newContainer
- }
-}
-
func setupSuite(s *suite.Suite, topologyName string) func() {
t := s.T()
topology, err := LoadTopology(NetworkTopologyDir, topologyName)
diff --git a/extras/hs-test/go.mod b/extras/hs-test/go.mod
index 3b11dd2609a..0f6bffb28fc 100644
--- a/extras/hs-test/go.mod
+++ b/extras/hs-test/go.mod
@@ -8,6 +8,7 @@ require (
github.com/edwarnicke/govpp v0.0.0-20220311182453-f32f292e0e91
github.com/edwarnicke/vpphelper v0.0.0-20210617172001-3e6797de32c3
github.com/stretchr/testify v1.7.0
+ go.fd.io/govpp v0.7.0
gopkg.in/yaml.v3 v3.0.1
)
@@ -18,13 +19,10 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe // indirect
- github.com/onsi/gomega v1.16.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
- golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
- golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 // indirect
- golang.org/x/text v0.3.7 // indirect
+ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
)
diff --git a/extras/hs-test/go.sum b/extras/hs-test/go.sum
index e08a0d5043d..531153edd54 100644
--- a/extras/hs-test/go.sum
+++ b/extras/hs-test/go.sum
@@ -19,21 +19,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff/go.mod h1:yUhRXHewUVJ1k89wHKP68xfzk7kwXUx/DV1nx4EBMbw=
-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -47,17 +33,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe h1:ewr1srjRCmcQogPQ/NCx6XCk6LGVmsVCc9Y3vvPZj+Y=
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.1.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
-github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
@@ -73,59 +51,16 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+go.fd.io/govpp v0.7.0 h1:8qideC7G0xPeYz2sjwni8GKWWbNk45Ev73oR1igKDYY=
+go.fd.io/govpp v0.7.0/go.mod h1:VxUPq8HGQH6/9IL9saMURL3UcHsUuN8XmETuao5HA7o=
+golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 h1:8IVLkfbr2cLhv0a/vKq4UFUcJym8RmDoDboxCFWEjYE=
-golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@@ -134,12 +69,7 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/extras/hs-test/hst_suite.go b/extras/hs-test/hst_suite.go
new file mode 100644
index 00000000000..9cf38d95895
--- /dev/null
+++ b/extras/hs-test/hst_suite.go
@@ -0,0 +1,289 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "github.com/edwarnicke/exechelper"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+ "go.fd.io/govpp/binapi/ip_types"
+ "gopkg.in/yaml.v3"
+)
+
+func IsPersistent() bool {
+ return os.Getenv("HST_PERSIST") == "1"
+}
+
+func IsVerbose() bool {
+ return os.Getenv("HST_VERBOSE") == "1"
+}
+
+type HstSuite struct {
+ suite.Suite
+ teardownSuite func()
+ containers map[string]*Container
+ volumes []string
+ networkNamespaces map[string]*NetworkNamespace
+ veths map[string]*NetworkInterfaceVeth
+ taps map[string]*NetworkInterfaceTap
+ bridges map[string]*NetworkBridge
+ numberOfAddresses int
+}
+
+func (s *HstSuite) TearDownSuite() {
+ if s.teardownSuite != nil {
+ s.teardownSuite() // TODO remove this after config moved to SetupTest() for each suite
+ }
+
+ s.unconfigureNetworkTopology()
+}
+
+func (s *HstSuite) TearDownTest() {
+ if IsPersistent() {
+ return
+ }
+ s.ResetContainers()
+ s.RemoveVolumes()
+}
+
+func (s *HstSuite) SetupTest() {
+ s.SetupVolumes()
+ s.SetupContainers()
+}
+
+func (s *HstSuite) SetupVolumes() {
+ for _, volume := range s.volumes {
+ cmd := "docker volume create --name=" + volume
+ s.log(cmd)
+ exechelper.Run(cmd)
+ }
+}
+
+func (s *HstSuite) SetupContainers() {
+ for _, container := range s.containers {
+ if container.isOptional == false {
+ container.run()
+ }
+ }
+}
+
+func (s *HstSuite) hstFail() {
+ s.T().FailNow()
+}
+
+func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
+ if !assert.Nil(s.T(), object, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
+ if !assert.NotNil(s.T(), object, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
+ if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
+ if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
+ if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
+ if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) log(args ...any) {
+ if IsVerbose() {
+ s.T().Log(args...)
+ }
+}
+
+func (s *HstSuite) skip(args ...any) {
+ s.log(args...)
+ s.T().SkipNow()
+}
+
+func (s *HstSuite) ResetContainers() {
+ for _, container := range s.containers {
+ container.stop()
+ }
+}
+
+func (s *HstSuite) RemoveVolumes() {
+ for _, volumeName := range s.volumes {
+ cmd := "docker volume rm " + volumeName
+ exechelper.Run(cmd)
+ os.RemoveAll(volumeName)
+ }
+}
+
+func (s *HstSuite) getContainerByName(name string) *Container {
+ return s.containers[name]
+}
+
+func (s *HstSuite) getContainerCopyByName(name string) *Container {
+ // Create a copy and return its address, so that individial tests which call this
+ // are not able to modify the original container and affect other tests by doing that
+ containerCopy := *s.containers[name]
+ return &containerCopy
+}
+
+func (s *HstSuite) loadContainerTopology(topologyName string) {
+ data, err := ioutil.ReadFile(ContainerTopologyDir + topologyName + ".yaml")
+ if err != nil {
+ s.T().Fatalf("read error: %v", err)
+ }
+ var yamlTopo YamlTopology
+ err = yaml.Unmarshal(data, &yamlTopo)
+ if err != nil {
+ s.T().Fatalf("unmarshal error: %v", err)
+ }
+
+ for _, elem := range yamlTopo.Volumes {
+ volumeMap := elem["volume"].(VolumeConfig)
+ hostDir := volumeMap["host-dir"].(string)
+ s.volumes = append(s.volumes, hostDir)
+ }
+
+ s.containers = make(map[string]*Container)
+ for _, elem := range yamlTopo.Containers {
+ newContainer, err := NewContainer(elem)
+ newContainer.suite = s
+ if err != nil {
+ s.T().Fatalf("container config error: %v", err)
+ }
+ s.log(newContainer.getRunCommand())
+ s.containers[newContainer.name] = newContainer
+ }
+}
+
+func (s *HstSuite) loadNetworkTopology(topologyName string) {
+ data, err := ioutil.ReadFile(NetworkTopologyDir + topologyName + ".yaml")
+ if err != nil {
+ s.T().Fatalf("read error: %v", err)
+ }
+ var yamlTopo YamlTopology
+ err = yaml.Unmarshal(data, &yamlTopo)
+ if err != nil {
+ s.T().Fatalf("unmarshal error: %v", err)
+ }
+
+ s.networkNamespaces = make(map[string]*NetworkNamespace)
+ s.veths = make(map[string]*NetworkInterfaceVeth)
+ s.taps = make(map[string]*NetworkInterfaceTap)
+ s.bridges = make(map[string]*NetworkBridge)
+ for _, elem := range yamlTopo.Devices {
+ switch elem["type"].(string) {
+ case NetNs:
+ {
+ if namespace, err := NewNetNamespace(elem); err == nil {
+ s.networkNamespaces[namespace.Name()] = &namespace
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ case Veth:
+ {
+ if veth, err := NewVeth(elem); err == nil {
+ s.veths[veth.Name()] = &veth
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ case Tap:
+ {
+ if tap, err := NewTap(elem); err == nil {
+ s.taps[tap.Name()] = &tap
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ case Bridge:
+ {
+ if bridge, err := NewBridge(elem); err == nil {
+ s.bridges[bridge.Name()] = &bridge
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ }
+ }
+}
+
+func (s *HstSuite) configureNetworkTopology(topologyName string) {
+ s.loadNetworkTopology(topologyName)
+
+ for _, ns := range s.networkNamespaces {
+ if err := ns.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ for _, veth := range s.veths {
+ if err := veth.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ for _, tap := range s.taps {
+ if err := tap.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ for _, bridge := range s.bridges {
+ if err := bridge.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+}
+
+func (s *HstSuite) unconfigureNetworkTopology() {
+ if IsPersistent() {
+ return
+ }
+ for _, ns := range s.networkNamespaces {
+ ns.Unconfigure()
+ }
+ for _, veth := range s.veths {
+ veth.Unconfigure()
+ }
+ for _, tap := range s.taps {
+ tap.Unconfigure()
+ }
+ for _, bridge := range s.bridges {
+ bridge.Unconfigure()
+ }
+}
+
+func (s *HstSuite) NewAddress() (AddressWithPrefix, error) {
+ var ipPrefix AddressWithPrefix
+ var err error
+
+ if s.numberOfAddresses == 255 {
+ s.T().Fatalf("no available IPv4 addresses")
+ }
+
+ address := fmt.Sprintf("10.10.10.%v/24", s.numberOfAddresses+1)
+ ipPrefix, err = ip_types.ParseAddressWithPrefix(address)
+ if err != nil {
+ return AddressWithPrefix{}, err
+ }
+ s.numberOfAddresses++
+
+ return ipPrefix, nil
+}
diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go
index 28d27bbcb16..52b7c39fa77 100644
--- a/extras/hs-test/http_test.go
+++ b/extras/hs-test/http_test.go
@@ -31,22 +31,18 @@ func (s *VethsSuite) TestHttpCli() {
serverContainer := s.getContainerByName("server-vpp")
clientContainer := s.getContainerByName("client-vpp")
- _, err := serverContainer.execAction("Configure2Veths srv")
- s.assertNil(err)
-
- _, err = clientContainer.execAction("Configure2Veths cln")
- s.assertNil(err)
-
- s.log("configured IPs...")
+ serverVeth := s.veths["vppsrv"]
- _, err = serverContainer.execAction("RunHttpCliSrv")
+ _, err := serverContainer.vppInstance.vppctl("http cli server")
s.assertNil(err)
- s.log("configured http server")
+ uri := "http://" + serverVeth.Address() + "/80"
- o, err := clientContainer.execAction("RunHttpCliCln /show/version")
+ o, err := clientContainer.vppInstance.vppctl("http cli client" +
+ " uri " + uri + " query /show/version")
s.assertNil(err)
+ s.log(o)
s.assertContains(o, "<html>", "<html> not found in the result!")
}
diff --git a/extras/hs-test/ldp_test.go b/extras/hs-test/ldp_test.go
index 2dfdf8b4440..cbba227c328 100644
--- a/extras/hs-test/ldp_test.go
+++ b/extras/hs-test/ldp_test.go
@@ -10,12 +10,10 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() {
var clnVclConf, srvVclConf Stanza
serverContainer := s.getContainerByName("server-vpp")
- serverVolume := serverContainer.getVolumeByHostDir("/tmp/server")
- srvVcl := serverVolume.containerDir + "/vcl_srv.conf"
+ srvVcl := serverContainer.GetHostWorkDir() + "/vcl_srv.conf"
clientContainer := s.getContainerByName("client-vpp")
- clientVolume := clientContainer.getVolumeByHostDir("/tmp/client")
- clnVcl := clientVolume.containerDir + "/vcl_cln.conf"
+ clnVcl := clientContainer.GetHostWorkDir() + "/vcl_cln.conf"
ldpreload := os.Getenv("HST_LDPRELOAD")
s.assertNotEqual("", ldpreload)
@@ -28,20 +26,14 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() {
s.log("starting VPPs")
- originalWorkDir := serverContainer.workDir
- serverContainer.workDir = serverVolume.containerDir
_, err := serverContainer.execAction("Configure2Veths srv")
s.assertNil(err)
- serverContainer.workDir = originalWorkDir
- originalWorkDir = clientContainer.workDir
- clientContainer.workDir = clientVolume.containerDir
_, err = clientContainer.execAction("Configure2Veths cln")
s.assertNil(err)
- clientContainer.workDir = originalWorkDir
clientAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/2",
- clientVolume.containerDir)
+ clientContainer.GetContainerWorkDir())
err = clnVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
@@ -54,7 +46,7 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() {
s.assertNil(err)
serverAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/1",
- serverVolume.containerDir)
+ serverContainer.GetContainerWorkDir())
err = srvVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
diff --git a/extras/hs-test/netconfig.go b/extras/hs-test/netconfig.go
index 46f23c0d323..b93e460b44d 100644
--- a/extras/hs-test/netconfig.go
+++ b/extras/hs-test/netconfig.go
@@ -4,22 +4,128 @@ import (
"errors"
"fmt"
"os/exec"
+ "strings"
+
+ "go.fd.io/govpp/binapi/ethernet_types"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/ip_types"
)
-type NetType string
+type (
+ AddressWithPrefix = ip_types.AddressWithPrefix
+ MacAddress = ethernet_types.MacAddress
+
+ NetConfig struct {
+ Configure func() error
+ Unconfigure func()
+ }
+
+ NetTopology []NetConfig
+
+ NetConfigBase struct {
+ name string
+ category string // what else to call this when `type` is reserved?
+ }
+
+ NetworkInterfaceVeth struct {
+ NetConfigBase
+ index interface_types.InterfaceIndex
+ peerNetworkNamespace string
+ peerName string
+ peerIp4Address string
+ ip4Address ip_types.AddressWithPrefix
+ hwAddress ethernet_types.MacAddress
+ }
+
+ NetworkInterfaceTap struct {
+ NetConfigBase
+ index interface_types.InterfaceIndex
+ ip4Address string
+ }
+
+ NetworkNamespace struct {
+ NetConfigBase
+ }
+
+ NetworkBridge struct {
+ NetConfigBase
+ networkNamespace string
+ interfaces []string
+ }
+)
const (
- NetNs NetType = "netns"
- Veth string = "veth"
- Tap string = "tap"
+ NetNs string = "netns"
+ Veth string = "veth"
+ Tap string = "tap"
+ Bridge string = "bridge"
)
-type NetConfig struct {
- Configure func() error
- Unconfigure func()
+func (b NetConfigBase) Name() string {
+ return b.name
+}
+
+func (b NetConfigBase) Type() string {
+ return b.category
+}
+
+func (iface NetworkInterfaceVeth) Configure() error {
+ err := AddVethPair(iface.name, iface.peerName)
+ if err != nil {
+ return err
+ }
+
+ if iface.peerNetworkNamespace != "" {
+ err := LinkSetNetns(iface.peerName, iface.peerNetworkNamespace)
+ if err != nil {
+ return err
+ }
+ }
+
+ if iface.peerIp4Address != "" {
+ err = AddAddress(iface.peerName, iface.peerIp4Address, iface.peerNetworkNamespace)
+ if err != nil {
+ return fmt.Errorf("failed to add configure address for %s: %v", iface.peerName, err)
+ }
+ }
+ return nil
}
-type NetTopology []NetConfig
+func (iface NetworkInterfaceVeth) Unconfigure() {
+ DelLink(iface.name)
+}
+
+func (iface NetworkInterfaceVeth) Address() string {
+ return strings.Split(iface.ip4Address.String(), "/")[0]
+}
+
+func (iface NetworkInterfaceTap) Configure() error {
+ err := AddTap(iface.name, iface.ip4Address)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (iface NetworkInterfaceTap) Unconfigure() {
+ DelLink(iface.name)
+}
+
+func (ns NetworkNamespace) Configure() error {
+ return addDelNetns(ns.name, true)
+}
+
+func (ns NetworkNamespace) Unconfigure() {
+ addDelNetns(ns.name, false)
+}
+
+func (b NetworkBridge) Configure() error {
+ return AddBridge(b.name, b.interfaces, b.networkNamespace)
+}
+
+func (b NetworkBridge) Unconfigure() {
+ DelBridge(b.name, b.networkNamespace)
+}
func (t *NetTopology) Configure() error {
for _, c := range *t {
@@ -101,6 +207,60 @@ func NewNetConfig(cfg NetDevConfig) NetConfig {
return nc
}
+func NewNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) {
+ var networkNamespace NetworkNamespace
+ networkNamespace.name = cfg["name"].(string)
+ networkNamespace.category = "netns"
+ return networkNamespace, nil
+}
+
+func NewBridge(cfg NetDevConfig) (NetworkBridge, error) {
+ var bridge NetworkBridge
+ bridge.name = cfg["name"].(string)
+ bridge.category = "bridge"
+ for _, v := range cfg["interfaces"].([]interface{}) {
+ bridge.interfaces = append(bridge.interfaces, v.(string))
+ }
+ bridge.networkNamespace = cfg["netns"].(string)
+ return bridge, nil
+}
+
+func NewVeth(cfg NetDevConfig) (NetworkInterfaceVeth, error) {
+ var veth NetworkInterfaceVeth
+ var err error
+ veth.name = cfg["name"].(string)
+ veth.category = "veth"
+
+ if cfg["preset-hw-address"] != nil {
+ veth.hwAddress, err = ethernet_types.ParseMacAddress(cfg["preset-hw-address"].(string))
+ if err != nil {
+ return NetworkInterfaceVeth{}, err
+ }
+ }
+
+ peer := cfg["peer"].(NetDevConfig)
+
+ veth.peerName = peer["name"].(string)
+
+ if peer["netns"] != nil {
+ veth.peerNetworkNamespace = peer["netns"].(string)
+ }
+
+ if peer["ip4"] != nil {
+ veth.peerIp4Address = peer["ip4"].(string)
+ }
+
+ return veth, nil
+}
+
+func NewTap(cfg NetDevConfig) (NetworkInterfaceTap, error) {
+ var tap NetworkInterfaceTap
+ tap.name = cfg["name"].(string)
+ tap.category = "tap"
+ tap.ip4Address = cfg["ip4"].(string)
+ return tap, nil
+}
+
func DelBridge(brName, ns string) error {
err := SetDevDown(brName, ns)
if err != err {
diff --git a/extras/hs-test/suite_veth_test.go b/extras/hs-test/suite_veth_test.go
index 5276072eed6..81a21a2ce5f 100644
--- a/extras/hs-test/suite_veth_test.go
+++ b/extras/hs-test/suite_veth_test.go
@@ -4,12 +4,94 @@ import (
"time"
)
+const (
+ // These correspond to names used in yaml config
+ serverInterfaceName = "vppsrv"
+ clientInterfaceName = "vppcln"
+)
+
type VethsSuite struct {
HstSuite
}
+var ConvertedTests = map[string]any{
+ "TestVeths/TestEchoBuiltin": "",
+ "TestVeths/TestHttpCli": "",
+ "TestVeths/TestVclEchoTcp": "",
+ "TestVeths/TestVclRetryAttach": "",
+}
+
func (s *VethsSuite) SetupSuite() {
time.Sleep(1 * time.Second)
- s.teardownSuite = setupSuite(&s.Suite, "2peerVeth")
+
+ s.configureNetworkTopology("2peerVeth")
+
s.loadContainerTopology("2peerVeth")
}
+
+func (s *VethsSuite) SetupTest() {
+ s.SetupVolumes()
+ s.SetupContainers()
+
+ // TODO remove this after all tests are converted to configuration from test suite
+ if _, ok := ConvertedTests[s.T().Name()]; !ok {
+ return
+ }
+
+ // Setup test conditions
+
+ var startupConfig Stanza
+ startupConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api").Close()
+
+ // ... For server
+ serverContainer := s.getContainerByName("server-vpp")
+
+ serverVpp, _ := serverContainer.newVppInstance(startupConfig)
+ s.assertNotNil(serverVpp)
+
+ s.setupServerVpp()
+
+ // ... For client
+ clientContainer := s.getContainerByName("client-vpp")
+
+ clientVpp, _ := clientContainer.newVppInstance(startupConfig)
+ s.assertNotNil(clientVpp)
+
+ s.setupClientVpp()
+}
+
+func (s *VethsSuite) setupServerVpp() {
+ serverVpp := s.getContainerByName("server-vpp").vppInstance
+
+ err := serverVpp.start()
+ s.assertNil(err)
+
+ serverVeth := s.veths["vppsrv"]
+ idx, err := serverVpp.createAfPacket(serverVeth)
+ s.assertNil(err)
+ s.assertNotEqual(0, idx)
+
+ namespaceSecret := "1"
+ err = serverVpp.addAppNamespace(1, idx, namespaceSecret)
+ s.assertNil(err)
+
+}
+
+func (s *VethsSuite) setupClientVpp() {
+ clientVpp := s.getContainerByName("client-vpp").vppInstance
+
+ err := clientVpp.start()
+ s.assertNil(err)
+
+ clientVeth := s.veths["vppcln"]
+ idx, err := clientVpp.createAfPacket(clientVeth)
+ s.assertNil(err)
+ s.assertNotEqual(0, idx)
+
+ clientNamespaceSecret := "2"
+ err = clientVpp.addAppNamespace(2, idx, clientNamespaceSecret)
+ s.assertNil(err)
+}
diff --git a/extras/hs-test/topo-containers/2peerVeth.yaml b/extras/hs-test/topo-containers/2peerVeth.yaml
index 246e5cac1bd..72af1c9c838 100644
--- a/extras/hs-test/topo-containers/2peerVeth.yaml
+++ b/extras/hs-test/topo-containers/2peerVeth.yaml
@@ -1,33 +1,25 @@
---
volumes:
- volume: &server-vol
- host-dir: server-share
+ host-dir: /tmp/server-share
+ container-dir: /tmp/server-share
+ is-default-work-dir: true
- volume: &client-vol
- host-dir: client-share
+ host-dir: /tmp/client-share
+ container-dir: "/tmp/client-share"
+ is-default-work-dir: true
containers:
- name: "server-vpp"
volumes:
- <<: *server-vol
- container-dir: "/tmp/server-share"
- is-default-work-dir: true
- - host-dir: "/tmp/server"
- container-dir: "/tmp/server"
- name: "client-vpp"
volumes:
- <<: *client-vol
- container-dir: "/tmp/client-share"
- is-default-work-dir: true
- - host-dir: "/tmp/client"
- container-dir: "/tmp/client"
- name: "server-application"
volumes:
- <<: *server-vol
- container-dir: "/tmp/server-share"
- is-default-work-dir: true
- name: "client-application"
volumes:
- <<: *client-vol
- container-dir: "/tmp/client-share"
- is-default-work-dir: true
diff --git a/extras/hs-test/topo-network/2peerVeth.yaml b/extras/hs-test/topo-network/2peerVeth.yaml
index e62e1739a15..9a966dc441f 100644
--- a/extras/hs-test/topo-network/2peerVeth.yaml
+++ b/extras/hs-test/topo-network/2peerVeth.yaml
@@ -5,6 +5,7 @@ devices:
- name: "vppsrv"
type: "veth"
+ preset-hw-address: "00:00:5e:00:53:01"
peer:
name: "vppsrv_veth"
netns: "hsns"
diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go
index 13384989753..cf30ecec4e0 100644
--- a/extras/hs-test/utils.go
+++ b/extras/hs-test/utils.go
@@ -51,7 +51,7 @@ plugins {
`
const vclTemplate = `vcl {
- app-socket-api %[1]s
+ app-socket-api %[1]s/var/run/app_ns_sockets/%[2]s
app-scope-global
app-scope-local
namespace-id %[2]s
diff --git a/extras/hs-test/vcl_test.go b/extras/hs-test/vcl_test.go
index f4273c87b14..6c809f4caec 100644
--- a/extras/hs-test/vcl_test.go
+++ b/extras/hs-test/vcl_test.go
@@ -1,6 +1,7 @@
package main
import (
+ "fmt"
"time"
)
@@ -19,73 +20,78 @@ func (s *VethsSuite) TestVclEchoTcp() {
}
func (s *VethsSuite) testVclEcho(proto string) {
- srvVppContainer := s.getContainerByName("server-vpp")
-
- _, err := srvVppContainer.execAction("Configure2Veths srv")
- s.assertNil(err)
-
- clnVppContainer := s.getContainerByName("client-vpp")
-
- _, err = clnVppContainer.execAction("Configure2Veths cln")
- s.assertNil(err)
+ serverVethAddress := s.veths["vppsrv"].Address()
+ uri := proto + "://" + serverVethAddress + "/12344"
echoSrvContainer := s.getContainerByName("server-application")
-
- // run server app
- _, err = echoSrvContainer.execAction("RunEchoServer " + proto)
+ serverCommand := "vpp_echo server TX=RX" +
+ " socket-name " + echoSrvContainer.GetContainerWorkDir() + "/var/run/app_ns_sockets/1" +
+ " use-app-socket-api" +
+ " uri " + uri
+ s.log(serverCommand)
+ err := echoSrvContainer.execServer(serverCommand)
s.assertNil(err)
echoClnContainer := s.getContainerByName("client-application")
- o, err := echoClnContainer.execAction("RunEchoClient " + proto)
+ clientCommand := "vpp_echo client" +
+ " socket-name " + echoClnContainer.GetContainerWorkDir() + "/var/run/app_ns_sockets/2" +
+ " use-app-socket-api uri " + uri
+ s.log(clientCommand)
+ o, err := echoClnContainer.exec(clientCommand)
s.assertNil(err)
s.log(o)
}
func (s *VethsSuite) TestVclRetryAttach() {
- s.skip()
+ s.skip("this test takes too long, for now it's being skipped")
s.testRetryAttach("tcp")
}
func (s *VethsSuite) testRetryAttach(proto string) {
- srvVppContainer := s.getContainerByName("server-vpp")
+ srvVppContainer := s.getContainerCopyByName("server-vpp")
- _, err := srvVppContainer.execAction("Configure2Veths srv-with-preset-hw-addr")
- s.assertNil(err)
+ echoSrvContainer := s.getContainerByName("server-application")
- clnVppContainer := s.getContainerByName("client-vpp")
+ serverVclConfContent := fmt.Sprintf(vclTemplate, echoSrvContainer.GetContainerWorkDir(), "1")
+ echoSrvContainer.createFile("/vcl.conf", serverVclConfContent)
- _, err = clnVppContainer.execAction("Configure2Veths cln")
- s.assertNil(err)
-
- echoSrvContainer := s.getContainerByName("server-application")
- _, err = echoSrvContainer.execAction("RunVclEchoServer " + proto)
+ echoSrvContainer.addEnvVar("VCL_CONFIG", "/vcl.conf")
+ err := echoSrvContainer.execServer("vcl_test_server -p " + proto + " 12346")
s.assertNil(err)
s.log("This whole test case can take around 3 minutes to run. Please be patient.")
s.log("... Running first echo client test, before disconnect.")
- echoClnContainer := s.getContainerByName("client-application")
- _, err = echoClnContainer.execAction("RunVclEchoClient " + proto)
+
+ serverVeth := s.veths[serverInterfaceName]
+ serverVethAddress := serverVeth.Address()
+
+ echoClnContainer := s.getContainerCopyByName("client-application")
+ clientVclConfContent := fmt.Sprintf(vclTemplate, echoClnContainer.GetContainerWorkDir(), "2")
+ echoClnContainer.createFile("/vcl.conf", clientVclConfContent)
+
+ testClientCommand := "vcl_test_client -U -p " + proto + " " + serverVethAddress + " 12346"
+ echoClnContainer.addEnvVar("VCL_CONFIG", "/vcl.conf")
+ o, err := echoClnContainer.exec(testClientCommand)
+ s.log(o)
s.assertNil(err)
s.log("... First test ended. Stopping VPP server now.")
// Stop server-vpp-instance, start it again and then run vcl-test-client once more
+ srvVppContainer.vppInstance.disconnect()
stopVppCommand := "/bin/bash -c 'ps -C vpp_main -o pid= | xargs kill -9'"
_, err = srvVppContainer.exec(stopVppCommand)
s.assertNil(err)
- time.Sleep(5 * time.Second) // Give parent process time to reap the killed child process
- stopVppCommand = "/bin/bash -c 'ps -C hs-test -o pid= | xargs kill -9'"
- _, err = srvVppContainer.exec(stopVppCommand)
- s.assertNil(err)
- _, err = srvVppContainer.execAction("Configure2Veths srv-with-preset-hw-addr")
- s.assertNil(err)
+
+ s.setupServerVpp()
s.log("... VPP server is starting again, so waiting for a bit.")
time.Sleep(30 * time.Second) // Wait a moment for the re-attachment to happen
s.log("... Running second echo client test, after disconnect and re-attachment.")
- _, err = echoClnContainer.execAction("RunVclEchoClient " + proto)
+ o, err = echoClnContainer.exec(testClientCommand)
+ s.log(o)
s.assertNil(err)
s.log("Done.")
}
@@ -99,7 +105,8 @@ func (s *VethsSuite) TestTcpWithLoss() {
err := serverVpp.start()
s.assertNil(err, "starting VPP failed")
- _, err = serverVpp.vppctl("test echo server uri tcp://10.10.10.1/20022")
+ serverVeth := s.veths[serverInterfaceName]
+ _, err = serverVpp.vppctl("test echo server uri tcp://%s/20022", serverVeth.Address())
s.assertNil(err, "starting echo server failed")
clientContainer := s.getContainerByName("client-vpp")
diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go
index 14d6ab6ba9d..3f9ea871565 100644
--- a/extras/hs-test/vppinstance.go
+++ b/extras/hs-test/vppinstance.go
@@ -4,6 +4,15 @@ import (
"encoding/json"
"fmt"
"github.com/edwarnicke/exechelper"
+
+ "go.fd.io/govpp"
+ "go.fd.io/govpp/api"
+ "go.fd.io/govpp/binapi/af_packet"
+ interfaces "go.fd.io/govpp/binapi/interface"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/session"
+ "go.fd.io/govpp/binapi/vpe"
+ "go.fd.io/govpp/core"
)
const vppConfigTemplate = `unix {
@@ -32,27 +41,34 @@ statseg {
}
plugins {
+ plugin default { disable }
+
plugin unittest_plugin.so { enable }
- plugin dpdk_plugin.so { disable }
- plugin crypto_aesni_plugin.so { enable }
plugin quic_plugin.so { enable }
+ plugin af_packet_plugin.so { enable }
+ plugin hs_apps_plugin.so { enable }
+ plugin http_plugin.so { enable }
}
`
const (
defaultCliSocketFilePath = "/var/run/vpp/cli.sock"
+ defaultApiSocketFilePath = "/var/run/vpp/api.sock"
)
type VppInstance struct {
container *Container
- config VppConfig
+ config *VppConfig
actionFuncName string
+ connection *core.Connection
+ apiChannel api.Channel
}
type VppConfig struct {
Variant string
CliSocketFilePath string
+ additionalConfig Stanza
}
func (vc *VppConfig) getTemplate() string {
@@ -82,10 +98,22 @@ func (vpp *VppInstance) setCliSocket(filePath string) {
}
func (vpp *VppInstance) getCliSocket() string {
- return fmt.Sprintf("%s%s", vpp.container.workDir, vpp.config.CliSocketFilePath)
+ return fmt.Sprintf("%s%s", vpp.container.GetContainerWorkDir(), vpp.config.CliSocketFilePath)
}
-func (vpp *VppInstance) start() error {
+func (vpp *VppInstance) getRunDir() string {
+ return vpp.container.GetContainerWorkDir() + "/var/run/vpp"
+}
+
+func (vpp *VppInstance) getLogDir() string {
+ return vpp.container.GetContainerWorkDir() + "/var/log/vpp"
+}
+
+func (vpp *VppInstance) getEtcDir() string {
+ return vpp.container.GetContainerWorkDir() + "/etc/vpp"
+}
+
+func (vpp *VppInstance) legacyStart() error {
if vpp.actionFuncName == "" {
return fmt.Errorf("vpp start failed: action function name must not be blank")
}
@@ -99,14 +127,70 @@ func (vpp *VppInstance) start() error {
if err != nil {
return fmt.Errorf("vpp start failed: %s", err)
}
+ return nil
+}
+
+func (vpp *VppInstance) start() error {
+ if vpp.actionFuncName != "" {
+ return vpp.legacyStart()
+ }
+
+ // Create folders
+ containerWorkDir := vpp.container.GetContainerWorkDir()
+
+ vpp.container.exec("mkdir --mode=0700 -p " + vpp.getRunDir())
+ vpp.container.exec("mkdir --mode=0700 -p " + vpp.getLogDir())
+ vpp.container.exec("mkdir --mode=0700 -p " + vpp.getEtcDir())
+
+ // Create startup.conf inside the container
+ configContent := fmt.Sprintf(vppConfigTemplate, containerWorkDir, vpp.config.CliSocketFilePath)
+ configContent += vpp.config.additionalConfig.ToString()
+ startupFileName := vpp.getEtcDir() + "/startup.conf"
+ vpp.container.createFile(startupFileName, configContent)
+
+ // Start VPP
+ if err := vpp.container.execServer("vpp -c " + startupFileName); err != nil {
+ return err
+ }
+
+ // Connect to VPP and store the connection
+ sockAddress := vpp.container.GetHostWorkDir() + defaultApiSocketFilePath
+ conn, connEv, err := govpp.AsyncConnect(
+ sockAddress,
+ core.DefaultMaxReconnectAttempts,
+ core.DefaultReconnectInterval)
+ if err != nil {
+ fmt.Println("async connect error: ", err)
+ }
+ vpp.connection = conn
+
+ // ... wait for Connected event
+ e := <-connEv
+ if e.State != core.Connected {
+ fmt.Println("connecting to VPP failed: ", e.Error)
+ }
+
+ // ... check compatibility of used messages
+ ch, err := conn.NewAPIChannel()
+ if err != nil {
+ fmt.Println("creating channel failed: ", err)
+ }
+ if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
+ fmt.Println("compatibility error: ", err)
+ }
+ if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
+ fmt.Println("compatibility error: ", err)
+ }
+ vpp.apiChannel = ch
return nil
}
-func (vpp *VppInstance) vppctl(command string) (string, error) {
- cliExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
- vpp.container.name, vpp.getCliSocket(), command)
- output, err := exechelper.CombinedOutput(cliExecCommand)
+func (vpp *VppInstance) vppctl(command string, arguments ...any) (string, error) {
+ vppCliCommand := fmt.Sprintf(command, arguments...)
+ containerExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
+ vpp.container.name, vpp.getCliSocket(), vppCliCommand)
+ output, err := exechelper.CombinedOutput(containerExecCommand)
if err != nil {
return "", fmt.Errorf("vppctl failed: %s", err)
}
@@ -115,7 +199,7 @@ func (vpp *VppInstance) vppctl(command string) (string, error) {
}
func NewVppInstance(c *Container) *VppInstance {
- var vppConfig VppConfig
+ vppConfig := new(VppConfig)
vppConfig.CliSocketFilePath = defaultCliSocketFilePath
vpp := new(VppInstance)
vpp.container = c
@@ -123,7 +207,7 @@ func NewVppInstance(c *Container) *VppInstance {
return vpp
}
-func serializeVppConfig(vppConfig VppConfig) (string, error) {
+func serializeVppConfig(vppConfig *VppConfig) (string, error) {
serializedConfig, err := json.Marshal(vppConfig)
if err != nil {
return "", fmt.Errorf("vpp start failed: serializing configuration failed: %s", err)
@@ -142,3 +226,87 @@ func deserializeVppConfig(input string) (VppConfig, error) {
}
return vppConfig, nil
}
+
+func (vpp *VppInstance) createAfPacket(
+ veth *NetworkInterfaceVeth,
+) (interface_types.InterfaceIndex, error) {
+ createReq := &af_packet.AfPacketCreateV2{
+ UseRandomHwAddr: true,
+ HostIfName: veth.Name(),
+ }
+ if veth.hwAddress != (MacAddress{}) {
+ createReq.UseRandomHwAddr = false
+ createReq.HwAddr = veth.hwAddress
+ }
+ createReply := &af_packet.AfPacketCreateV2Reply{}
+
+ if err := vpp.apiChannel.SendRequest(createReq).ReceiveReply(createReply); err != nil {
+ return 0, err
+ }
+ veth.index = createReply.SwIfIndex
+
+ // Set to up
+ upReq := &interfaces.SwInterfaceSetFlags{
+ SwIfIndex: veth.index,
+ Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
+ }
+ upReply := &interfaces.SwInterfaceSetFlagsReply{}
+
+ if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
+ return 0, err
+ }
+
+ // Add address
+ if veth.ip4Address == (AddressWithPrefix{}) {
+ ipPrefix, err := vpp.container.suite.NewAddress()
+ if err != nil {
+ return 0, err
+ }
+ veth.ip4Address = ipPrefix
+ }
+ addressReq := &interfaces.SwInterfaceAddDelAddress{
+ IsAdd: true,
+ SwIfIndex: veth.index,
+ Prefix: veth.ip4Address,
+ }
+ addressReply := &interfaces.SwInterfaceAddDelAddressReply{}
+
+ if err := vpp.apiChannel.SendRequest(addressReq).ReceiveReply(addressReply); err != nil {
+ return 0, err
+ }
+
+ return veth.index, nil
+}
+
+func (vpp *VppInstance) addAppNamespace(
+ secret uint64,
+ ifx interface_types.InterfaceIndex,
+ namespaceId string,
+) error {
+ req := &session.AppNamespaceAddDelV2{
+ Secret: secret,
+ SwIfIndex: ifx,
+ NamespaceID: namespaceId,
+ }
+ reply := &session.AppNamespaceAddDelV2Reply{}
+
+ if err := vpp.apiChannel.SendRequest(req).ReceiveReply(reply); err != nil {
+ return err
+ }
+
+ sessionReq := &session.SessionEnableDisable{
+ IsEnable: true,
+ }
+ sessionReply := &session.SessionEnableDisableReply{}
+
+ if err := vpp.apiChannel.SendRequest(sessionReq).ReceiveReply(sessionReply); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (vpp *VppInstance) disconnect() {
+ vpp.connection.Disconnect()
+ vpp.apiChannel.Close()
+}