aboutsummaryrefslogtreecommitdiffstats
path: root/extras/hs-test
diff options
context:
space:
mode:
authorMaros Ondrejicka <maros.ondrejicka@pantheon.tech>2023-01-26 10:07:29 +0100
committerFlorin Coras <florin.coras@gmail.com>2023-02-09 17:02:43 +0000
commitffa3f60290499bdacc37a97c49f2e794b5e1f18c (patch)
treec23820e0d91d2a1a0eef2596b0de60c06f1ea303 /extras/hs-test
parent7a6532bb9f3b9c429f92d11a6ccbf55638906d0a (diff)
hs-test: configure VPP from test context
Instead of configuring VPP instances running inside of a container, now the configuration is going to be done from within the test context by using binary API and shared volume that exposes api socket. This converts just some of the test cases, rest is to follow. Type: test Signed-off-by: Maros Ondrejicka <maros.ondrejicka@pantheon.tech> Change-Id: I87e4ab15de488f0eebb01ff514596265fc2a787f
Diffstat (limited to 'extras/hs-test')
-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()
+}