diff options
author | Maros Ondrejicka <mondreji@cisco.com> | 2023-02-07 20:40:27 +0100 |
---|---|---|
committer | Florin Coras <florin.coras@gmail.com> | 2023-02-10 05:23:32 +0000 |
commit | 7550dd268f80334cbb9127feefe35319b9c7e572 (patch) | |
tree | 08350b3d4dcf5453941312565c63303d95735903 | |
parent | 2908f8cf07c21f385f80d83fdad826a0eea98977 (diff) |
hs-test: refactor test cases from no-topo suite
This converts remaining tests to configation of VPP from test context.
Type: test
Change-Id: I386714f6b290e03d1757c2a033a25fae0340f5d6
Signed-off-by: Maros Ondrejicka <mondreji@cisco.com>
-rw-r--r-- | extras/hs-test/Makefile | 1 | ||||
-rw-r--r-- | extras/hs-test/actions.go | 82 | ||||
-rw-r--r-- | extras/hs-test/container.go | 34 | ||||
-rw-r--r-- | extras/hs-test/docker/Dockerfile.vpp | 2 | ||||
-rw-r--r-- | extras/hs-test/echo_test.go | 4 | ||||
-rw-r--r-- | extras/hs-test/framework_test.go | 19 | ||||
-rw-r--r-- | extras/hs-test/hst_suite.go | 5 | ||||
-rw-r--r-- | extras/hs-test/http_test.go | 45 | ||||
-rw-r--r-- | extras/hs-test/ldp_test.go | 2 | ||||
-rw-r--r-- | extras/hs-test/linux_iperf_test.go | 2 | ||||
-rw-r--r-- | extras/hs-test/main.go | 140 | ||||
-rw-r--r-- | extras/hs-test/netconfig.go | 213 | ||||
-rw-r--r-- | extras/hs-test/proxy_test.go | 4 | ||||
-rw-r--r-- | extras/hs-test/suite_no_topo_test.go | 42 | ||||
-rw-r--r-- | extras/hs-test/topo-containers/single.yaml | 2 | ||||
-rw-r--r-- | extras/hs-test/topo.go | 54 | ||||
-rw-r--r-- | extras/hs-test/utils.go | 63 | ||||
-rw-r--r-- | extras/hs-test/vcl_test.go | 10 | ||||
-rw-r--r-- | extras/hs-test/vppinstance.go | 132 |
19 files changed, 207 insertions, 649 deletions
diff --git a/extras/hs-test/Makefile b/extras/hs-test/Makefile index c50e681915a..cd3a284208f 100644 --- a/extras/hs-test/Makefile +++ b/extras/hs-test/Makefile @@ -2,7 +2,6 @@ all: build docker build: go build ./tools/http_server - go build . docker: bash ./script/build.sh diff --git a/extras/hs-test/actions.go b/extras/hs-test/actions.go deleted file mode 100644 index 9233e2d1ea2..00000000000 --- a/extras/hs-test/actions.go +++ /dev/null @@ -1,82 +0,0 @@ -package main - -import ( - "context" - "os" - - "git.fd.io/govpp.git/api" - interfaces "github.com/edwarnicke/govpp/binapi/interface" - "github.com/edwarnicke/govpp/binapi/interface_types" - ip_types "github.com/edwarnicke/govpp/binapi/ip_types" - "github.com/edwarnicke/govpp/binapi/session" - "github.com/edwarnicke/govpp/binapi/tapv2" - "github.com/edwarnicke/vpphelper" -) - -var ( - workDir, _ = os.Getwd() -) - -type ConfFn func(context.Context, api.Connection) error - -type Actions struct { -} - -func (a *Actions) ConfigureTap(args []string) *ActionResult { - var startup Stanza - startup. - NewStanza("session"). - Append("enable"). - Append("use-app-socket-api").Close() - - ctx, cancel := newVppContext() - defer cancel() - con, vppErrCh := vpphelper.StartAndDialContext(ctx, - vpphelper.WithRootDir(workDir), - vpphelper.WithVppConfig(configTemplate+startup.ToString())) - exitOnErrCh(ctx, cancel, vppErrCh) - ifaceClient := interfaces.NewServiceClient(con) - - pref, err := ip_types.ParseIP4Prefix("10.10.10.2/24") - if err != nil { - return NewActionResult(err, ActionResultWithDesc("failed to parse ip4 address")) - } - createTapReply, err := tapv2.NewServiceClient(con).TapCreateV2(ctx, &tapv2.TapCreateV2{ - HostIfNameSet: true, - HostIfName: "tap0", - HostIP4PrefixSet: true, - HostIP4Prefix: ip_types.IP4AddressWithPrefix(pref), - }) - if err != nil { - return NewActionResult(err, ActionResultWithDesc("failed to configure tap")) - } - ipPrefix, err := ip_types.ParseAddressWithPrefix("10.10.10.1/24") - if err != nil { - return NewActionResult(err, ActionResultWithDesc("parsing ip address failed")) - } - ipAddress := &interfaces.SwInterfaceAddDelAddress{ - IsAdd: true, - SwIfIndex: createTapReply.SwIfIndex, - Prefix: ipPrefix, - } - _, errx := ifaceClient.SwInterfaceAddDelAddress(ctx, ipAddress) - if errx != nil { - return NewActionResult(err, ActionResultWithDesc("configuring ip address failed")) - } - _, err = ifaceClient.SwInterfaceSetFlags(ctx, &interfaces.SwInterfaceSetFlags{ - SwIfIndex: createTapReply.SwIfIndex, - Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, - }) - if err != nil { - return NewActionResult(err, ActionResultWithDesc("failed to set interface state")) - } - _, err = session.NewServiceClient(con).SessionEnableDisable(ctx, &session.SessionEnableDisable{ - IsEnable: true, - }) - if err != nil { - return NewActionResult(err, ActionResultWithDesc("configuration failed")) - } - writeSyncFile(OkResult()) - <-ctx.Done() - return nil -} diff --git a/extras/hs-test/container.go b/extras/hs-test/container.go index cc2d441f7ec..8ece8a8952b 100644 --- a/extras/hs-test/container.go +++ b/extras/hs-test/container.go @@ -9,6 +9,10 @@ import ( "github.com/edwarnicke/exechelper" ) +var ( + workDir, _ = os.Getwd() +) + type Volume struct { hostDir string containerDir string @@ -114,9 +118,7 @@ func (c *Container) GetContainerWorkDir() (res string) { } 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" - cmd += syncPath cmd += c.getVolumesAsCliOption() cmd += c.getEnvVarsAsCliOption() cmd += " --name " + c.name + " " + c.image + " " + c.extraRunningArgs @@ -185,10 +187,6 @@ func (c *Container) getEnvVarsAsCliOption() string { return cliOption } -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 @@ -249,30 +247,6 @@ func (c *Container) exec(command string, arguments ...any) string { return string(byteOutput) } -func (c *Container) execAction(args string) (string, error) { - syncFile := c.getSyncPath() + "/rc" - os.Remove(syncFile) - - workDir := c.getWorkDirAsCliOption() - cmd := fmt.Sprintf("docker exec -d %s %s hs-test %s", - workDir, - c.name, - args) - err := exechelper.Run(cmd) - if err != nil { - return "", err - } - res, err := waitForSyncFile(syncFile) - if err != nil { - return "", fmt.Errorf("failed to read sync file while executing 'hs-test %s': %v", args, err) - } - o := res.StdOutput + res.ErrOutput - if res.Code != 0 { - return o, fmt.Errorf("cmd resulted in non-zero value %d: %s", res.Code, res.Desc) - } - return o, err -} - func (c *Container) stop() error { if c.vppInstance != nil && c.vppInstance.apiChannel != nil { c.vppInstance.disconnect() diff --git a/extras/hs-test/docker/Dockerfile.vpp b/extras/hs-test/docker/Dockerfile.vpp index f95df2277c9..d9b3f276442 100644 --- a/extras/hs-test/docker/Dockerfile.vpp +++ b/extras/hs-test/docker/Dockerfile.vpp @@ -20,8 +20,6 @@ COPY \ COPY vpp-data/bin/* /usr/bin/ COPY vpp-data/lib/* /usr/lib/ -COPY hs-test /usr/local/bin/hs-test - RUN addgroup vpp ENTRYPOINT ["tail", "-f", "/dev/null"] diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go index b32d66ee533..70f9e4c31a1 100644 --- a/extras/hs-test/echo_test.go +++ b/extras/hs-test/echo_test.go @@ -6,12 +6,12 @@ func (s *VethsSuite) TestEchoBuiltin() { serverVpp.vppctl("test echo server " + " private-segment-size 1g fifo-size 4 no-echo" + - " uri tcp://" + serverVeth.Ip4AddressString() + "/1234") + " uri tcp://" + serverVeth.IP4AddressString() + "/1234") clientVpp := s.getContainerByName("client-vpp").vppInstance o := 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.Ip4AddressString() + "/1234") + " fifo-size 4 uri tcp://" + serverVeth.IP4AddressString() + "/1234") s.log(o) } diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go index 6de8f167f70..fdce5c45bf7 100644 --- a/extras/hs-test/framework_test.go +++ b/extras/hs-test/framework_test.go @@ -6,25 +6,6 @@ import ( "github.com/stretchr/testify/suite" ) -func setupSuite(s *suite.Suite, topologyName string) func() { - t := s.T() - topology, err := LoadTopology(NetworkTopologyDir, topologyName) - if err != nil { - t.Fatalf("error on loading topology '%s': %v", topologyName, err) - } - err = topology.Configure() - if err != nil { - t.Fatalf("failed to configure %s: %v", topologyName, err) - } - - return func() { - if IsPersistent() { - return - } - topology.Unconfigure() - } -} - func TestTapSuite(t *testing.T) { var m TapSuite suite.Run(t, &m) diff --git a/extras/hs-test/hst_suite.go b/extras/hs-test/hst_suite.go index 24b4fc242d4..286536da305 100644 --- a/extras/hs-test/hst_suite.go +++ b/extras/hs-test/hst_suite.go @@ -25,7 +25,6 @@ func IsVerbose() bool { type HstSuite struct { suite.Suite - teardownSuite func() containers map[string]*Container volumes []string netConfigs []NetConfig @@ -34,10 +33,6 @@ type HstSuite struct { } func (s *HstSuite) TearDownSuite() { - if s.teardownSuite != nil { - s.teardownSuite() // TODO remove this after config moved to SetupTest() for each suite - } - s.unconfigureNetworkTopology() } diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go index 912c98283b1..5e88fe00cc6 100644 --- a/extras/hs-test/http_test.go +++ b/extras/hs-test/http_test.go @@ -4,13 +4,11 @@ import ( "fmt" "os" "os/exec" - "strings" - "time" ) func (s *NsSuite) TestHttpTps() { iface := s.netInterfaces[clientInterface] - client_ip := iface.Ip4AddressString() + client_ip := iface.IP4AddressString() port := "8080" finished := make(chan error, 1) @@ -33,7 +31,7 @@ func (s *VethsSuite) TestHttpCli() { serverContainer.vppInstance.vppctl("http cli server") - uri := "http://" + serverVeth.Ip4AddressString() + "/80" + uri := "http://" + serverVeth.IP4AddressString() + "/80" o := clientContainer.vppInstance.vppctl("http cli client" + " uri " + uri + " query /show/version") @@ -42,33 +40,21 @@ func (s *VethsSuite) TestHttpCli() { s.assertContains(o, "<html>", "<html> not found in the result!") } -func waitForApp(vppInst *VppInstance, appName string, timeout int) error { - for i := 0; i < timeout; i++ { - o := vppInst.vppctl("show app") - if strings.Contains(o, appName) { - return nil - } - time.Sleep(1 * time.Second) - } - return fmt.Errorf("Timeout while waiting for app '%s'", appName) -} - func (s *NoTopoSuite) TestNginx() { query := "return_ok" finished := make(chan error, 1) - vppCont := s.getContainerByName("vpp") - vppInst := NewVppInstance(vppCont) - vppInst.actionFuncName = "ConfigureTap" - s.assertNil(vppInst.start(), "failed to start vpp") nginxCont := s.getContainerByName("nginx") s.assertNil(nginxCont.run()) - err := waitForApp(vppInst, "-app", 5) + vpp := s.getContainerByName("vpp").vppInstance + err := vpp.waitForApp("-app", 5) s.assertNil(err) + serverAddress := s.netInterfaces[tapNameVpp].IP4AddressString() + defer func() { os.Remove(query) }() - go startWget(finished, "10.10.10.1", "80", query, "") + go startWget(finished, serverAddress, "80", query, "") s.assertNil(<-finished) } @@ -78,6 +64,8 @@ func runNginxPerf(s *NoTopoSuite, mode, ab_or_wrk string) error { var args []string var exeName string + serverAddress := s.netInterfaces[tapNameVpp].IP4AddressString() + if ab_or_wrk == "ab" { args = []string{"-n", fmt.Sprintf("%d", nRequests), "-c", fmt.Sprintf("%d", nClients)} @@ -86,28 +74,25 @@ func runNginxPerf(s *NoTopoSuite, mode, ab_or_wrk string) error { } else if mode != "cps" { return fmt.Errorf("invalid mode %s; expected cps/rps", mode) } - args = append(args, "http://10.10.10.1:80/64B.json") + args = append(args, "http://"+serverAddress+":80/64B.json") exeName = "ab" } else { args = []string{"-c", fmt.Sprintf("%d", nClients), "-t", "2", "-d", "30", - "http://10.10.10.1:80"} + "http://" + serverAddress + ":80"} exeName = "wrk" } - vppCont := s.getContainerByName("vpp") - vppInst := NewVppInstance(vppCont) - vppInst.actionFuncName = "ConfigureTap" - s.assertNil(vppInst.start(), "failed to start vpp") + vpp := s.getContainerByName("vpp").vppInstance nginxCont := s.getContainerByName("nginx") s.assertNil(nginxCont.run()) - err := waitForApp(vppInst, "-app", 5) + err := vpp.waitForApp("-app", 5) s.assertNil(err) cmd := exec.Command(exeName, args...) - fmt.Println(cmd) + s.log(cmd) o, _ := cmd.CombinedOutput() - fmt.Print(string(o)) + s.log(string(o)) return nil } diff --git a/extras/hs-test/ldp_test.go b/extras/hs-test/ldp_test.go index 17f78ca4db3..59d31e13890 100644 --- a/extras/hs-test/ldp_test.go +++ b/extras/hs-test/ldp_test.go @@ -62,7 +62,7 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() { s.log("attaching client to vpp") var clnRes = make(chan string, 1) clnEnv := append(os.Environ(), ldpreload, "VCL_CONFIG="+clientVclFileName) - serverVethAddress := s.netInterfaces[serverInterfaceName].Ip4AddressString() + serverVethAddress := s.netInterfaces[serverInterfaceName].IP4AddressString() go StartClientApp(serverVethAddress, clnEnv, clnCh, clnRes) s.log(<-clnRes) diff --git a/extras/hs-test/linux_iperf_test.go b/extras/hs-test/linux_iperf_test.go index 154a9b543b9..2c3437fe915 100644 --- a/extras/hs-test/linux_iperf_test.go +++ b/extras/hs-test/linux_iperf_test.go @@ -14,7 +14,7 @@ func (s *TapSuite) TestLinuxIperf() { s.assertNil(err) s.log("server running") - ipAddress := s.netInterfaces["tap0"].Ip4AddressString() + ipAddress := s.netInterfaces["tap0"].IP4AddressString() go StartClientApp(ipAddress, nil, clnCh, clnRes) s.log("client running") s.log(<-clnRes) diff --git a/extras/hs-test/main.go b/extras/hs-test/main.go deleted file mode 100644 index 8fa1458767d..00000000000 --- a/extras/hs-test/main.go +++ /dev/null @@ -1,140 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "os" - "os/signal" - "reflect" -) - -var actions Actions - -func newVppContext() (context.Context, context.CancelFunc) { - ctx, cancel := signal.NotifyContext( - context.Background(), - os.Interrupt, - ) - return ctx, cancel -} - -func exitOnErrCh(ctx context.Context, cancel context.CancelFunc, errCh <-chan error) { - // If we already have an error, log it and exit - select { - case err := <-errCh: - fmt.Printf("%v", err) - default: - } - go func(ctx context.Context, errCh <-chan error) { - <-errCh - cancel() - }(ctx, errCh) -} - -func writeSyncFile(res *ActionResult) error { - syncFile := "/tmp/sync/rc" - - var jsonRes JsonResult - - jsonRes.ErrOutput = res.ErrOutput - jsonRes.StdOutput = res.StdOutput - if res.Err != nil { - jsonRes.Code = 1 - jsonRes.Desc = fmt.Sprintf("%s :%v", res.Desc, res.Err) - } else { - jsonRes.Code = 0 - } - - str, err := json.Marshal(jsonRes) - if err != nil { - return fmt.Errorf("error marshaling json result data! %v", err) - } - - _, err = os.Open(syncFile) - if err != nil { - // expecting the file does not exist - f, e := os.Create(syncFile) - if e != nil { - return fmt.Errorf("failed to open sync file") - } - defer f.Close() - f.Write([]byte(str)) - } else { - return fmt.Errorf("sync file exists, delete the file first") - } - return nil -} - -func NewActionResult(err error, opts ...ActionResultOptionFn) *ActionResult { - res := &ActionResult{ - Err: err, - } - for _, o := range opts { - o(res) - } - return res -} - -type ActionResultOptionFn func(res *ActionResult) - -func ActionResultWithDesc(s string) ActionResultOptionFn { - return func(res *ActionResult) { - res.Desc = s - } -} - -func ActionResultWithStderr(s string) ActionResultOptionFn { - return func(res *ActionResult) { - res.ErrOutput = s - } -} - -func ActionResultWithStdout(s string) ActionResultOptionFn { - return func(res *ActionResult) { - res.StdOutput = s - } -} - -func OkResult() *ActionResult { - return NewActionResult(nil) -} - -func processArgs() *ActionResult { - nArgs := len(os.Args) - 1 // skip program name - if nArgs < 1 { - return NewActionResult(fmt.Errorf("internal: no action specified!")) - } - action := os.Args[1] - methodValue := reflect.ValueOf(&actions).MethodByName(action) - if !methodValue.IsValid() { - return NewActionResult(fmt.Errorf("internal unknown action %s!", action)) - } - methodIface := methodValue.Interface() - fn := methodIface.(func([]string) *ActionResult) - return fn(os.Args) -} - -func main() { - if len(os.Args) == 0 { - fmt.Println("args required") - return - } - - if os.Args[1] == "rm" { - topology, err := LoadTopology(NetworkTopologyDir, os.Args[2]) - if err != nil { - fmt.Printf("falied to load topologies: %v\n", err) - os.Exit(1) - } - topology.Unconfigure() - os.Exit(0) - } - - var err error - res := processArgs() - err = writeSyncFile(res) - if err != nil { - fmt.Printf("failed to write to sync file: %v\n", err) - } -} diff --git a/extras/hs-test/netconfig.go b/extras/hs-test/netconfig.go index 45ff3790eaf..d94b3269889 100644 --- a/extras/hs-test/netconfig.go +++ b/extras/hs-test/netconfig.go @@ -12,16 +12,10 @@ import ( ) type ( - MacAddress = ethernet_types.MacAddress - AddressWithPrefix = ip_types.AddressWithPrefix - InterfaceIndex = interface_types.InterfaceIndex - - LegacyNetConfig struct { - Configure func() error - Unconfigure func() - } - - NetTopology []LegacyNetConfig + MacAddress = ethernet_types.MacAddress + AddressWithPrefix = ip_types.AddressWithPrefix + IP4AddressWithPrefix = ip_types.IP4AddressWithPrefix + InterfaceIndex = interface_types.InterfaceIndex NetConfig interface { Configure() error @@ -38,8 +32,9 @@ type ( NetInterface interface { NetConfig SetAddress(string) - Ip4AddressWithPrefix() AddressWithPrefix - Ip4AddressString() string + AddressWithPrefix() AddressWithPrefix + IP4AddressWithPrefix() IP4AddressWithPrefix + IP4AddressString() string SetIndex(InterfaceIndex) Index() InterfaceIndex HwAddress() MacAddress @@ -102,12 +97,18 @@ func (b *NetInterfaceBase) Index() InterfaceIndex { return b.index } -func (b *NetInterfaceBase) Ip4AddressWithPrefix() AddressWithPrefix { +func (b *NetInterfaceBase) AddressWithPrefix() AddressWithPrefix { address, _ := ip_types.ParseAddressWithPrefix(b.ip4address) return address } -func (b *NetInterfaceBase) Ip4AddressString() string { +func (b *NetInterfaceBase) IP4AddressWithPrefix() IP4AddressWithPrefix { + IP4Prefix, _ := ip_types.ParseIP4Prefix(b.ip4address) + IP4AddressWithPrefix := ip_types.IP4AddressWithPrefix(IP4Prefix) + return IP4AddressWithPrefix +} + +func (b *NetInterfaceBase) IP4AddressString() string { return strings.Split(b.ip4address, "/")[0] } @@ -137,136 +138,6 @@ func (iface *NetworkInterfaceVeth) Configure() error { return nil } -func (iface *NetworkInterfaceVeth) Unconfigure() { - DelLink(iface.name) -} - -func (iface *NetworkInterfaceVeth) PeerIp4AddressString() string { - return strings.Split(iface.peerIp4Address, "/")[0] -} - -func (iface *NetworkInterfaceTap) Configure() error { - err := AddTap(iface.name, iface.Ip4AddressString()) - 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 { - err := c.Configure() - if err != nil { - return err - } - } - return nil -} - -func (t *NetTopology) Unconfigure() { - for _, c := range *t { - c.Unconfigure() - } -} - -func newConfigFn(cfg NetDevConfig) func() error { - t := cfg["type"] - if t == "netns" { - return func() error { return AddNetns(cfg["name"].(string)) } - } else if t == "veth" { - return func() error { - var peerNs string - peer := cfg["peer"].(NetDevConfig) - peerName := peer["name"].(string) - err := AddVethPair(cfg["name"].(string), peerName) - if err != nil { - return err - } - - if peer["netns"] != nil { - peerNs = peer["netns"].(string) - if peerNs != "" { - err := LinkSetNetns(peerName, peerNs) - if err != nil { - return err - } - } - } - if peer["ip4"] != nil { - err = AddAddress(peerName, peer["ip4"].(string), peerNs) - if err != nil { - return fmt.Errorf("failed to add configure address for %s: %v", peerName, err) - } - } - return nil - } - } else if t == "bridge" { - return func() error { return configureBridge(cfg) } - } - return nil -} - -func newUnconfigFn(cfg NetDevConfig) func() { - t := cfg["type"] - name := cfg["name"].(string) - - if t == "netns" { - return func() { DelNetns(name) } - } else if t == "veth" { - return func() { DelLink(name) } - } else if t == "bridge" { - return func() { DelBridge(name, cfg["netns"].(string)) } - } - return nil -} - -func NewNetConfig(cfg NetDevConfig) LegacyNetConfig { - var nc LegacyNetConfig - - nc.Configure = newConfigFn(cfg) - nc.Unconfigure = newUnconfigFn(cfg) - - 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, a *Addresser) (NetworkInterfaceVeth, error) { var veth NetworkInterfaceVeth var err error @@ -300,6 +171,14 @@ func NewVeth(cfg NetDevConfig, a *Addresser) (NetworkInterfaceVeth, error) { return veth, nil } +func (iface *NetworkInterfaceVeth) Unconfigure() { + DelLink(iface.name) +} + +func (iface *NetworkInterfaceVeth) PeerIp4AddressString() string { + return strings.Split(iface.peerIp4Address, "/")[0] +} + func NewTap(cfg NetDevConfig, a *Addresser) (NetworkInterfaceTap, error) { var tap NetworkInterfaceTap tap.addresser = a @@ -313,6 +192,52 @@ func NewTap(cfg NetDevConfig, a *Addresser) (NetworkInterfaceTap, error) { return tap, nil } +func (iface *NetworkInterfaceTap) Configure() error { + err := AddTap(iface.name, iface.IP4AddressString()) + if err != nil { + return err + } + return nil +} + +func (iface *NetworkInterfaceTap) Unconfigure() { + DelLink(iface.name) +} + +func NewNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) { + var networkNamespace NetworkNamespace + networkNamespace.name = cfg["name"].(string) + networkNamespace.category = "netns" + return networkNamespace, nil +} + +func (ns *NetworkNamespace) Configure() error { + return addDelNetns(ns.name, true) +} + +func (ns *NetworkNamespace) Unconfigure() { + addDelNetns(ns.name, false) +} + +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 (b *NetworkBridge) Configure() error { + return AddBridge(b.name, b.interfaces, b.networkNamespace) +} + +func (b *NetworkBridge) Unconfigure() { + DelBridge(b.name, b.networkNamespace) +} + func DelBridge(brName, ns string) error { err := SetDevDown(brName, ns) if err != err { diff --git a/extras/hs-test/proxy_test.go b/extras/hs-test/proxy_test.go index 4c183517c37..d8918f1842b 100644 --- a/extras/hs-test/proxy_test.go +++ b/extras/hs-test/proxy_test.go @@ -35,7 +35,7 @@ func testProxyHttpTcp(s *NsSuite) error { " --retry-on-http-error=503 --tries=10"+ " -O %s %s:555/%s", outputFile, - clientVeth.Ip4AddressString(), + clientVeth.IP4AddressString(), srcFile, ) s.log(c) @@ -56,7 +56,7 @@ func configureVppProxy(s *NsSuite) error { testVppProxy := s.getContainerByName("vpp").vppInstance output := testVppProxy.vppctl( "test proxy server server-uri tcp://%s/555 client-uri tcp://%s/666", - clientVeth.Ip4AddressString(), + clientVeth.IP4AddressString(), serverVeth.PeerIp4AddressString(), ) s.log("proxy configured...", output) diff --git a/extras/hs-test/suite_no_topo_test.go b/extras/hs-test/suite_no_topo_test.go index 421decc1a61..01958b0a804 100644 --- a/extras/hs-test/suite_no_topo_test.go +++ b/extras/hs-test/suite_no_topo_test.go @@ -1,10 +1,50 @@ package main +const ( + singleTopoContainerVpp = "vpp" + singleTopoContainerNginx = "nginx" + + tapNameVpp = "vppTap" + tapNameHost = "hostTap" +) + type NoTopoSuite struct { HstSuite } func (s *NoTopoSuite) SetupSuite() { - s.teardownSuite = func() {} s.loadContainerTopology("single") + + s.addresser = NewAddresser(&s.HstSuite) + + var vppTapDevConfig = NetDevConfig{"name": tapNameVpp} + vppTap, _ := NewTap(vppTapDevConfig, s.addresser) + + var hostTapDevConfig = NetDevConfig{"name": tapNameHost} + hostTap, _ := NewTap(hostTapDevConfig, s.addresser) + + s.netInterfaces = make(map[string]NetInterface) + s.netInterfaces[vppTap.Name()] = &vppTap + s.netInterfaces[hostTap.Name()] = &hostTap +} + +func (s *NoTopoSuite) SetupTest() { + s.SetupVolumes() + s.SetupContainers() + + // Setup test conditions + var startupConfig Stanza + startupConfig. + NewStanza("session"). + Append("enable"). + Append("use-app-socket-api").Close() + + container := s.getContainerByName(singleTopoContainerVpp) + vpp, _ := container.newVppInstance(startupConfig) + vpp.start() + + vppTapAddress := s.netInterfaces[tapNameVpp].AddressWithPrefix() + hostTapAddress := s.netInterfaces[tapNameHost].IP4AddressWithPrefix() + + vpp.createTap("tap0", hostTapAddress, vppTapAddress) } diff --git a/extras/hs-test/topo-containers/single.yaml b/extras/hs-test/topo-containers/single.yaml index 9ecdc904d4d..a8ce48994ea 100644 --- a/extras/hs-test/topo-containers/single.yaml +++ b/extras/hs-test/topo-containers/single.yaml @@ -1,7 +1,7 @@ --- volumes: - volume: &shared-vol - host-dir: shared-vol + host-dir: /tmp/shared-vol containers: - name: "vpp" diff --git a/extras/hs-test/topo.go b/extras/hs-test/topo.go index b7e883bd773..d77d2dab33f 100644 --- a/extras/hs-test/topo.go +++ b/extras/hs-test/topo.go @@ -2,11 +2,6 @@ package main import ( "fmt" - "io/ioutil" - "os" - "strings" - - "gopkg.in/yaml.v3" ) type NetDevConfig map[string]interface{} @@ -28,52 +23,3 @@ func AddAddress(device, address, ns string) error { } return nil } - -func convertToNetConfig(t *YamlTopology) (*NetTopology, error) { - var topology NetTopology - for _, dev := range t.Devices { - topology = append(topology, NewNetConfig(dev)) - } - return &topology, nil -} - -func loadTopoFile(topoName string) (*NetTopology, error) { - var yamlTopo YamlTopology - - data, err := ioutil.ReadFile(topoName) - if err != nil { - return nil, fmt.Errorf("read error: %v", err) - } - - err = yaml.Unmarshal(data, &yamlTopo) - if err != nil { - return nil, fmt.Errorf("error parsing topology data: %v", err) - } - - return convertToNetConfig(&yamlTopo) -} - -func LoadTopology(path, topoName string) (*NetTopology, error) { - dir, err := os.Open(path) - if err != nil { - return nil, err - } - defer dir.Close() - - files, err := dir.Readdir(0) - if err != nil { - return nil, err - } - - for i := range files { - file := files[i] - fileName := file.Name() - - // cut off file extension - f := strings.Split(fileName, ".")[0] - if f == topoName { - return loadTopoFile(path + fileName) - } - } - return nil, fmt.Errorf("topology '%s' not found", topoName) -} diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go index c5889035c40..f912880a78a 100644 --- a/extras/hs-test/utils.go +++ b/extras/hs-test/utils.go @@ -1,55 +1,15 @@ package main import ( - "encoding/json" "errors" "fmt" "io" - "io/ioutil" "os" "os/exec" "strings" "time" ) -// TODO remove `configTemplate` once its usage has been replaced everywhere with VppConfig -const configTemplate = `unix { - nodaemon - log %[1]s/var/log/vpp/vpp.log - full-coredump - cli-listen %[1]s/var/run/vpp/cli.sock - runtime-dir %[1]s/var/run - gid vpp -} - -api-trace { - on -} - -api-segment { - gid vpp -} - -socksvr { - socket-name %[1]s/var/run/vpp/api.sock -} - -statseg { - socket-name %[1]s/var/run/vpp/stats.sock -} - -plugins { - plugin default { disable } - - plugin unittest_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 vclTemplate = `vcl { app-socket-api %[1]s/var/run/app_ns_sockets/%[2]s app-scope-global @@ -126,29 +86,6 @@ func StartClientApp(ipAddress string, env []string, clnCh chan error, clnRes cha } } -func waitForSyncFile(fname string) (*JsonResult, error) { - var res JsonResult - - for i := 0; i < 360; i++ { - f, err := os.Open(fname) - if err == nil { - defer f.Close() - - data, err := ioutil.ReadFile(fname) - if err != nil { - return nil, fmt.Errorf("read error: %v", err) - } - err = json.Unmarshal(data, &res) - if err != nil { - return nil, fmt.Errorf("json unmarshal error: %v", err) - } - return &res, nil - } - time.Sleep(1 * time.Second) - } - return nil, fmt.Errorf("no sync file found") -} - func assertFileSize(f1, f2 string) error { fi1, err := os.Stat(f1) if err != nil { diff --git a/extras/hs-test/vcl_test.go b/extras/hs-test/vcl_test.go index b9ce60afbaf..7cf4ab75dfd 100644 --- a/extras/hs-test/vcl_test.go +++ b/extras/hs-test/vcl_test.go @@ -20,7 +20,7 @@ func (s *VethsSuite) TestVclEchoTcp() { } func (s *VethsSuite) testVclEcho(proto string) { - serverVethAddress := s.netInterfaces["vppsrv"].Ip4AddressString() + serverVethAddress := s.netInterfaces["vppsrv"].IP4AddressString() uri := proto + "://" + serverVethAddress + "/12344" echoSrvContainer := s.getContainerByName("server-application") @@ -62,7 +62,7 @@ func (s *VethsSuite) testRetryAttach(proto string) { s.log("... Running first echo client test, before disconnect.") serverVeth := s.netInterfaces[serverInterfaceName] - serverVethAddress := serverVeth.Ip4AddressString() + serverVethAddress := serverVeth.IP4AddressString() echoClnContainer := s.getTransientContainerByName("client-application") clientVclConfContent := fmt.Sprintf(vclTemplate, echoClnContainer.GetContainerWorkDir(), "2") @@ -95,13 +95,13 @@ func (s *VethsSuite) TestTcpWithLoss() { serverVeth := s.netInterfaces[serverInterfaceName] serverVpp.vppctl("test echo server uri tcp://%s/20022", - serverVeth.Ip4AddressString()) + serverVeth.IP4AddressString()) clientVpp := s.getContainerByName("client-vpp").vppInstance // Ensure that VPP doesn't abort itself with NSIM enabled // Warning: Removing this ping will make the test fail! - clientVpp.vppctl("ping %s", serverVeth.Ip4AddressString()) + clientVpp.vppctl("ping %s", serverVeth.IP4AddressString()) // Add loss of packets with Network Delay Simulator clientVpp.vppctl("set nsim poll-main-thread delay 0.01 ms bandwidth 40 gbit" + @@ -111,7 +111,7 @@ func (s *VethsSuite) TestTcpWithLoss() { // Do echo test from client-vpp container output := clientVpp.vppctl("test echo client uri tcp://%s/20022 mbytes 50", - serverVeth.Ip4AddressString()) + serverVeth.IP4AddressString()) s.assertEqual(true, len(output) != 0) s.assertNotContains(output, "failed: timeout") s.log(output) diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go index 257798a4f3d..b45ad6028fe 100644 --- a/extras/hs-test/vppinstance.go +++ b/extras/hs-test/vppinstance.go @@ -1,9 +1,10 @@ package main import ( - "encoding/json" "fmt" "github.com/edwarnicke/exechelper" + "strings" + "time" "go.fd.io/govpp" "go.fd.io/govpp/api" @@ -11,6 +12,7 @@ import ( 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/tapv2" "go.fd.io/govpp/binapi/vpe" "go.fd.io/govpp/core" ) @@ -79,14 +81,6 @@ func (vpp *VppInstance) Suite() *HstSuite { return vpp.container.suite } -func (vpp *VppInstance) setVppProxy() { - vpp.actionFuncName = "ConfigureVppProxy" -} - -func (vpp *VppInstance) setEnvoyProxy() { - vpp.actionFuncName = "ConfigureEnvoyProxy" -} - func (vpp *VppInstance) setCliSocket(filePath string) { vpp.config.CliSocketFilePath = filePath } @@ -107,24 +101,7 @@ func (vpp *VppInstance) getEtcDir() string { return vpp.container.GetContainerWorkDir() + "/etc/vpp" } -func (vpp *VppInstance) legacyStart() error { - serializedConfig, err := serializeVppConfig(vpp.config) - if err != nil { - return fmt.Errorf("serialize vpp config: %v", err) - } - args := fmt.Sprintf("%s '%s'", vpp.actionFuncName, serializedConfig) - _, err = vpp.container.execAction(args) - 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() @@ -185,33 +162,15 @@ func (vpp *VppInstance) vppctl(command string, arguments ...any) string { return string(output) } -func NewVppInstance(c *Container) *VppInstance { - vppConfig := new(VppConfig) - vppConfig.CliSocketFilePath = defaultCliSocketFilePath - vpp := new(VppInstance) - vpp.container = c - vpp.config = vppConfig - return vpp -} - -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) - } - return string(serializedConfig), nil -} - -func deserializeVppConfig(input string) (VppConfig, error) { - var vppConfig VppConfig - err := json.Unmarshal([]byte(input), &vppConfig) - if err != nil { - // Since input is not a valid JSON it is going be used as a variant value - // for compatibility reasons - vppConfig.Variant = input - vppConfig.CliSocketFilePath = defaultCliSocketFilePath +func (vpp *VppInstance) waitForApp(appName string, timeout int) error { + for i := 0; i < timeout; i++ { + o := vpp.vppctl("show app") + if strings.Contains(o, appName) { + return nil + } + time.Sleep(1 * time.Second) } - return vppConfig, nil + return fmt.Errorf("Timeout while waiting for app '%s'", appName) } func (vpp *VppInstance) createAfPacket( @@ -247,29 +206,26 @@ func (vpp *VppInstance) createAfPacket( } // Add address - if veth.Ip4AddressWithPrefix() == (AddressWithPrefix{}) { + if veth.AddressWithPrefix() == (AddressWithPrefix{}) { + var err error + var ip4Address string if veth.peerNetworkNamespace != "" { - ip4Address, err := veth.addresser. + ip4Address, err = veth.addresser. NewIp4AddressWithNamespace(veth.peerNetworkNamespace) - if err == nil { - veth.SetAddress(ip4Address) - } else { - return 0, err - } } else { - ip4Address, err := veth.addresser. + ip4Address, err = veth.addresser. NewIp4Address() - if err == nil { - veth.SetAddress(ip4Address) - } else { - return 0, err - } + } + if err == nil { + veth.SetAddress(ip4Address) + } else { + return 0, err } } addressReq := &interfaces.SwInterfaceAddDelAddress{ IsAdd: true, SwIfIndex: veth.Index(), - Prefix: veth.Ip4AddressWithPrefix(), + Prefix: veth.AddressWithPrefix(), } addressReply := &interfaces.SwInterfaceAddDelAddressReply{} @@ -308,6 +264,50 @@ func (vpp *VppInstance) addAppNamespace( return nil } +func (vpp *VppInstance) createTap( + hostInterfaceName string, + hostIp4Address IP4AddressWithPrefix, + vppIp4Address AddressWithPrefix, +) error { + createTapReq := &tapv2.TapCreateV2{ + HostIfNameSet: true, + HostIfName: hostInterfaceName, + HostIP4PrefixSet: true, + HostIP4Prefix: hostIp4Address, + } + createTapReply := &tapv2.TapCreateV2Reply{} + + // Create tap interface + if err := vpp.apiChannel.SendRequest(createTapReq).ReceiveReply(createTapReply); err != nil { + return err + } + + // Add address + addAddressReq := &interfaces.SwInterfaceAddDelAddress{ + IsAdd: true, + SwIfIndex: createTapReply.SwIfIndex, + Prefix: vppIp4Address, + } + addAddressReply := &interfaces.SwInterfaceAddDelAddressReply{} + + if err := vpp.apiChannel.SendRequest(addAddressReq).ReceiveReply(addAddressReply); err != nil { + return err + } + + // Set interface to up + upReq := &interfaces.SwInterfaceSetFlags{ + SwIfIndex: createTapReply.SwIfIndex, + Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, + } + upReply := &interfaces.SwInterfaceSetFlagsReply{} + + if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil { + return err + } + + return nil +} + func (vpp *VppInstance) disconnect() { vpp.connection.Disconnect() vpp.apiChannel.Close() |