diff options
-rw-r--r-- | extras/hs-test/actions.go | 80 | ||||
-rw-r--r-- | extras/hs-test/container.go | 115 | ||||
-rw-r--r-- | extras/hs-test/echo_test.go | 17 | ||||
-rw-r--r-- | extras/hs-test/framework_test.go | 141 | ||||
-rw-r--r-- | extras/hs-test/go.mod | 6 | ||||
-rw-r--r-- | extras/hs-test/go.sum | 82 | ||||
-rw-r--r-- | extras/hs-test/hst_suite.go | 289 | ||||
-rw-r--r-- | extras/hs-test/http_test.go | 16 | ||||
-rw-r--r-- | extras/hs-test/ldp_test.go | 16 | ||||
-rw-r--r-- | extras/hs-test/netconfig.go | 176 | ||||
-rw-r--r-- | extras/hs-test/suite_veth_test.go | 84 | ||||
-rw-r--r-- | extras/hs-test/topo-containers/2peerVeth.yaml | 20 | ||||
-rw-r--r-- | extras/hs-test/topo-network/2peerVeth.yaml | 1 | ||||
-rw-r--r-- | extras/hs-test/utils.go | 2 | ||||
-rw-r--r-- | extras/hs-test/vcl_test.go | 73 | ||||
-rw-r--r-- | extras/hs-test/vppinstance.go | 190 |
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() +} |