From 229f5fcf188cf710f4a8fb269d92f1a1d04a99da Mon Sep 17 00:00:00 2001 From: Filip Tehlar Date: Tue, 9 Aug 2022 14:44:47 +0000 Subject: misc: add test framework for host stack Type: feature Signed-off-by: Filip Tehlar Change-Id: I5a64a2c095cae3a4d5f8fdc73e624b010339ec8e --- extras/hs-test/Dockerfile.vpp | 19 ++ extras/hs-test/Makefile | 10 + extras/hs-test/README | 9 + extras/hs-test/actions.go | 268 +++++++++++++++++++ extras/hs-test/echo_test.go | 51 ++++ extras/hs-test/envoy/envoy.log | 0 extras/hs-test/envoy/proxy.yaml | 52 ++++ extras/hs-test/envoy/vcl.conf | 7 + extras/hs-test/framework_test.go | 82 ++++++ extras/hs-test/go.mod | 30 +++ extras/hs-test/go.sum | 143 ++++++++++ extras/hs-test/ldp_test.go | 114 ++++++++ extras/hs-test/linux_iperf_test.go | 26 ++ extras/hs-test/main.go | 155 +++++++++++ extras/hs-test/netconfig.go | 283 ++++++++++++++++++++ extras/hs-test/proxy_test.go | 98 +++++++ extras/hs-test/script/build.sh | 13 + extras/hs-test/test | 4 + extras/hs-test/tools/http_server/http_server.go | 26 ++ extras/hs-test/topo.go | 75 ++++++ extras/hs-test/topo/2peerVeth.yaml | 23 ++ extras/hs-test/topo/ns.yaml | 21 ++ extras/hs-test/topo/tap.yaml | 5 + extras/hs-test/tps_test.go | 36 +++ extras/hs-test/utils.go | 334 ++++++++++++++++++++++++ extras/hs-test/vars | 4 + extras/hs-test/vcl_test.go | 86 ++++++ 27 files changed, 1974 insertions(+) create mode 100755 extras/hs-test/Dockerfile.vpp create mode 100755 extras/hs-test/Makefile create mode 100755 extras/hs-test/README create mode 100755 extras/hs-test/actions.go create mode 100755 extras/hs-test/echo_test.go create mode 100755 extras/hs-test/envoy/envoy.log create mode 100755 extras/hs-test/envoy/proxy.yaml create mode 100755 extras/hs-test/envoy/vcl.conf create mode 100755 extras/hs-test/framework_test.go create mode 100755 extras/hs-test/go.mod create mode 100755 extras/hs-test/go.sum create mode 100755 extras/hs-test/ldp_test.go create mode 100755 extras/hs-test/linux_iperf_test.go create mode 100755 extras/hs-test/main.go create mode 100755 extras/hs-test/netconfig.go create mode 100755 extras/hs-test/proxy_test.go create mode 100755 extras/hs-test/script/build.sh create mode 100755 extras/hs-test/test create mode 100755 extras/hs-test/tools/http_server/http_server.go create mode 100755 extras/hs-test/topo.go create mode 100755 extras/hs-test/topo/2peerVeth.yaml create mode 100755 extras/hs-test/topo/ns.yaml create mode 100755 extras/hs-test/topo/tap.yaml create mode 100755 extras/hs-test/tps_test.go create mode 100755 extras/hs-test/utils.go create mode 100755 extras/hs-test/vars create mode 100755 extras/hs-test/vcl_test.go (limited to 'extras') diff --git a/extras/hs-test/Dockerfile.vpp b/extras/hs-test/Dockerfile.vpp new file mode 100755 index 00000000000..92577870b42 --- /dev/null +++ b/extras/hs-test/Dockerfile.vpp @@ -0,0 +1,19 @@ +FROM ubuntu:22.04 + +RUN apt-get update \ + && apt-get install -y openssl libapr1 libnuma1 libsubunit0 \ + iproute2 libnl-3-dev libnl-route-3-dev python3 iputils-ping \ + vim gdb \ + && rm -rf /var/lib/apt/lists/* + +COPY vpp-data/lib/vat2_plugins/ /usr/lib/vat2_plugins/ +COPY vpp-data/lib/vpp_api_test_plugins/ /usr/lib/vpp_api_test_plugins/ +COPY vpp-data/lib/vpp_plugins/ /usr/lib/x86_64-linux-gnu/vpp_plugins/ +COPY vpp-data/bin/* /usr/bin/ +COPY vpp-data/lib/* /usr/lib/ + +COPY hs-test /hs-test + +RUN addgroup vpp + +ENTRYPOINT ["tail", "-f", "/dev/null"] diff --git a/extras/hs-test/Makefile b/extras/hs-test/Makefile new file mode 100755 index 00000000000..90265551c87 --- /dev/null +++ b/extras/hs-test/Makefile @@ -0,0 +1,10 @@ +all: build docker + +build: + go build ./tools/http_server + go build . + +docker: + bash ./script/build.sh + +.PHONY: docker diff --git a/extras/hs-test/README b/extras/hs-test/README new file mode 100755 index 00000000000..06b2ca644a8 --- /dev/null +++ b/extras/hs-test/README @@ -0,0 +1,9 @@ +Host stack test framework +------------------------- + +For building docker image run `make` first and `./test` to run all the tests. +`./test` script is basically a wrapper for `go test` and accepts its parameters, +for example following runs a specific test: `./test -run Veth/EchoBuilt`. + +Root privileges is required to run tests as it uses linux `ip` command for +configuring topology. diff --git a/extras/hs-test/actions.go b/extras/hs-test/actions.go new file mode 100755 index 00000000000..aa82f49c4c0 --- /dev/null +++ b/extras/hs-test/actions.go @@ -0,0 +1,268 @@ +package main + +import ( + "bytes" + "context" + "fmt" + "os" + "path/filepath" + + "git.fd.io/govpp.git/api" + "github.com/edwarnicke/exechelper" + "github.com/edwarnicke/govpp/binapi/af_packet" + 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/vlib" + "github.com/edwarnicke/vpphelper" +) + +func RegisterActions() { + cfgTable = make(map[string]func([]string) *ActionResult) + reg("echo-srv-internal", Configure2Veths) + reg("echo-cln-internal", Configure2Veths) + reg("echo-client", RunEchoClient) + reg("echo-server", RunEchoServer) + reg("vpp-proxy", ConfigureVppProxy) + reg("vpp-envoy", ConfigureEnvoyProxy) + reg("http-tps", ConfigureHttpTps) + reg("2veths", Configure2Veths) +} + +func configureProxyTcp(ifName0, ipAddr0, ifName1, ipAddr1 string) ConfFn { + return func(ctx context.Context, + vppConn api.Connection) error { + + _, err := configureAfPacket(ctx, vppConn, ifName0, ipAddr0) + if err != nil { + fmt.Printf("failed to create af packet: %v", err) + return err + } + _, err = configureAfPacket(ctx, vppConn, ifName1, ipAddr1) + if err != nil { + fmt.Printf("failed to create af packet: %v", err) + return err + } + return nil + } +} + +func ConfigureVppProxy(args []string) *ActionResult { + ctx, cancel := newVppContext() + defer cancel() + + con, vppErrCh := vpphelper.StartAndDialContext(ctx, vpphelper.WithVppConfig(configTemplate)) + exitOnErrCh(ctx, cancel, vppErrCh) + + confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24") + err := confFn(ctx, con) + if err != nil { + return NewActionResult(err, ActionResultWithDesc("configuration failed")) + } + writeSyncFile(OkResult()) + <-ctx.Done() + return nil +} + +func ConfigureEnvoyProxy(args []string) *ActionResult { + var startup Stanza + startup. + NewStanza("session"). + Append("enable"). + Append("use-app-socket-api"). + Append("evt_qs_memfd_seg"). + Append("event-queue-length 100000").Close() + ctx, cancel := newVppContext() + defer cancel() + + con, vppErrCh := vpphelper.StartAndDialContext(ctx, + vpphelper.WithVppConfig(configTemplate+startup.ToString()), + vpphelper.WithRootDir("/tmp/vpp-envoy")) + exitOnErrCh(ctx, cancel, vppErrCh) + + confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24") + err := confFn(ctx, con) + if err != nil { + return NewActionResult(err, ActionResultWithDesc("configuration failed")) + } + err0 := exechelper.Run("chmod 777 -R /tmp/vpp-envoy") + if err0 != nil { + return NewActionResult(err, ActionResultWithDesc("setting permissions failed")) + } + writeSyncFile(OkResult()) + <-ctx.Done() + return nil +} + +func getArgs() string { + s := "" + for i := 2; i < len(os.Args); i++ { + s = s + " " + os.Args[i] + } + return s +} + +func ApiCliInband(root, cmd string) *ActionResult { + ctx, _ := newVppContext() + con := vpphelper.DialContext(ctx, filepath.Join(root, "/var/run/vpp/api.sock")) + cliInband := vlib.CliInband{Cmd: cmd} + cliInbandReply, err := vlib.NewServiceClient(con).CliInband(ctx, &cliInband) + return NewActionResult(err, ActionResultWithStdout(cliInbandReply.Reply)) +} + +func RunEchoClient(args []string) *ActionResult { + outBuff := bytes.NewBuffer([]byte{}) + errBuff := bytes.NewBuffer([]byte{}) + + cmd := fmt.Sprintf("vpp_echo client socket-name /tmp/echo-cln/var/run/app_ns_sockets/2 use-app-socket-api uri %s://10.10.10.1/12344", 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 RunEchoServer(args []string) *ActionResult { + cmd := fmt.Sprintf("vpp_echo server TX=RX socket-name /tmp/echo-srv/var/run/app_ns_sockets/1 use-app-socket-api uri %s://10.10.10.1/12344", args[2]) + errCh := exechelper.Start(cmd) + select { + case err := <-errCh: + writeSyncFile(NewActionResult(err, ActionResultWithDesc("echo_server: "))) + default: + } + writeSyncFile(OkResult()) + return nil +} + +func RunEchoSrvInternal() *ActionResult { + cmd := fmt.Sprintf("test echo server %s uri tcp://10.10.10.1/1234", getArgs()) + return ApiCliInband("/tmp/2veths", cmd) +} + +func RunEchoClnInternal() *ActionResult { + cmd := fmt.Sprintf("test echo client %s uri tcp://10.10.10.1/1234", getArgs()) + return ApiCliInband("/tmp/2veths", cmd) +} +func configure2vethsTopo(ifName, interfaceAddress, namespaceId string, secret uint64) ConfFn { + return func(ctx context.Context, + vppConn api.Connection) error { + + swIfIndex, err := configureAfPacket(ctx, vppConn, ifName, interfaceAddress) + if err != nil { + fmt.Printf("failed to create af packet: %v", err) + } + _, er := session.NewServiceClient(vppConn).AppNamespaceAddDelV2(ctx, &session.AppNamespaceAddDelV2{ + Secret: secret, + SwIfIndex: swIfIndex, + NamespaceID: namespaceId, + }) + if er != nil { + fmt.Printf("add app namespace: %v", err) + return err + } + + _, er1 := session.NewServiceClient(vppConn).SessionEnableDisable(ctx, &session.SessionEnableDisable{ + IsEnable: true, + }) + if er1 != nil { + fmt.Printf("session enable %v", err) + return err + } + return nil + } +} + +func Configure2Veths(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.WithVppConfig(configTemplate+startup.ToString()), + vpphelper.WithRootDir(fmt.Sprintf("/tmp/%s", args[1]))) + exitOnErrCh(ctx, cancel, vppErrCh) + + var fn func(context.Context, api.Connection) error + if args[2] == "srv" { + fn = configure2vethsTopo("vppsrv", "10.10.10.1/24", "1", 1) + } else { + fn = configure2vethsTopo("vppcln", "10.10.10.2/24", "2", 2) + } + err := fn(ctx, con) + if err != nil { + return NewActionResult(err, ActionResultWithDesc("configuration failed")) + } + writeSyncFile(OkResult()) + <-ctx.Done() + return nil +} + +func configureAfPacket(ctx context.Context, vppCon api.Connection, + name, interfaceAddress string) (interface_types.InterfaceIndex, error) { + ifaceClient := interfaces.NewServiceClient(vppCon) + afPacketCreate := &af_packet.AfPacketCreateV2{ + UseRandomHwAddr: true, + HostIfName: name, + NumRxQueues: 1, + } + afPacketCreateRsp, err := af_packet.NewServiceClient(vppCon).AfPacketCreateV2(ctx, afPacketCreate) + if err != nil { + fmt.Printf("failed to create af packet: %v", err) + return 0, err + } + _, err = ifaceClient.SwInterfaceSetFlags(ctx, &interfaces.SwInterfaceSetFlags{ + SwIfIndex: afPacketCreateRsp.SwIfIndex, + Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, + }) + if err != nil { + fmt.Printf("set interface state up failed: %v\n", err) + return 0, err + } + ipPrefix, err := ip_types.ParseAddressWithPrefix(interfaceAddress) + if err != nil { + fmt.Printf("parse ip address %v\n", err) + return 0, err + } + ipAddress := &interfaces.SwInterfaceAddDelAddress{ + IsAdd: true, + SwIfIndex: afPacketCreateRsp.SwIfIndex, + Prefix: ipPrefix, + } + _, errx := ifaceClient.SwInterfaceAddDelAddress(ctx, ipAddress) + if errx != nil { + fmt.Printf("add ip address %v\n", err) + return 0, err + } + return afPacketCreateRsp.SwIfIndex, nil +} + +func ConfigureHttpTps(args []string) *ActionResult { + ctx, cancel := newVppContext() + defer cancel() + con, vppErrCh := vpphelper.StartAndDialContext(ctx, + vpphelper.WithVppConfig(configTemplate)) + exitOnErrCh(ctx, cancel, vppErrCh) + + confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24") + err := confFn(ctx, con) + if err != nil { + return NewActionResult(err, ActionResultWithDesc("configuration failed")) + } + + _, err = session.NewServiceClient(con).SessionEnableDisable(ctx, &session.SessionEnableDisable{ + IsEnable: true, + }) + if err != nil { + return NewActionResult(err, ActionResultWithDesc("configuration failed")) + } + Vppcli("", "http tps uri tcp://0.0.0.0/8080") + writeSyncFile(OkResult()) + <-ctx.Done() + return nil +} diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go new file mode 100755 index 00000000000..55f75007590 --- /dev/null +++ b/extras/hs-test/echo_test.go @@ -0,0 +1,51 @@ +package main + +import ( + "fmt" + + "github.com/edwarnicke/exechelper" +) + +func (s *Veths2Suite) TestEchoBuiltin() { + t := s.T() + srvInstance := "echo-srv-internal" + clnInstance := "echo-cln-internal" + err := dockerRun(srvInstance, "") + if err != nil { + t.Errorf("%v", err) + return + } + defer func() { exechelper.Run("docker stop " + srvInstance) }() + + err = dockerRun(clnInstance, "") + if err != nil { + t.Errorf("%v", err) + return + } + defer func() { exechelper.Run("docker stop " + clnInstance) }() + + _, err = hstExec("2veths srv", srvInstance) + if err != nil { + t.Errorf("%v", err) + return + } + + _, err = hstExec("2veths cln", clnInstance) + if err != nil { + t.Errorf("%v", err) + return + } + + _, err = hstExec("echo-srv-internal private-segment-size 1g fifo-size 4 no-echo", srvInstance) + if err != nil { + t.Errorf("%v", err) + return + } + + o, err := hstExec("echo-cln-internal nclients 10000 bytes 1 syn-timeout 100 test-timeout 100 no-return private-segment-size 1g fifo-size 4", clnInstance) + if err != nil { + t.Errorf("%v", err) + return + } + fmt.Println(o) +} diff --git a/extras/hs-test/envoy/envoy.log b/extras/hs-test/envoy/envoy.log new file mode 100755 index 00000000000..e69de29bb2d diff --git a/extras/hs-test/envoy/proxy.yaml b/extras/hs-test/envoy/proxy.yaml new file mode 100755 index 00000000000..e4a5b81ff65 --- /dev/null +++ b/extras/hs-test/envoy/proxy.yaml @@ -0,0 +1,52 @@ +admin: + access_log_path: /tmp/envoy.log + address: + socket_address: + address: 0.0.0.0 + port_value: 8081 +static_resources: + listeners: + # define a reverse proxy on :10001 that always uses :80 as an origin. + - address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 555 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: service + domains: ["*"] + routes: + - match: + prefix: "/" + route: + cluster: proxy_service + http_filters: + - name: envoy.filters.http.router + clusters: + - name: proxy_service + connect_timeout: 0.25s + type: LOGICAL_DNS + dns_lookup_family: V4_ONLY + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: proxy_service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 10.0.1.1 + port_value: 666 +bootstrap_extensions: + - name: envoy.extensions.vcl.vcl_socket_interface + typed_config: + "@type": type.googleapis.com/envoy.extensions.vcl.v3alpha.VclSocketInterface +default_socket_interface: "envoy.extensions.vcl.vcl_socket_interface" diff --git a/extras/hs-test/envoy/vcl.conf b/extras/hs-test/envoy/vcl.conf new file mode 100755 index 00000000000..164435a6ae5 --- /dev/null +++ b/extras/hs-test/envoy/vcl.conf @@ -0,0 +1,7 @@ +vcl { + rx-fifo-size 400000 + tx-fifo-size 400000 + app-scope-global + use-mq-eventfd + app-socket-api /tmp/vpp-envoy/var/run/app_ns_sockets/default +} diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go new file mode 100755 index 00000000000..78a289265be --- /dev/null +++ b/extras/hs-test/framework_test.go @@ -0,0 +1,82 @@ +package main + +import ( + "testing" + "time" + + "github.com/stretchr/testify/suite" +) + +type TapSuite struct { + suite.Suite + teardownSuite func() +} + +func (s *TapSuite) SetupSuite() { + time.Sleep(1 * time.Second) + s.teardownSuite = setupSuite(&s.Suite, "tap") +} + +func (s *TapSuite) TearDownSuite() { + s.teardownSuite() +} + +type Veths2Suite struct { + suite.Suite + teardownSuite func() +} + +func (s *Veths2Suite) SetupSuite() { + time.Sleep(1 * time.Second) + s.teardownSuite = setupSuite(&s.Suite, "2peerVeth") +} + +func (s *Veths2Suite) TearDownSuite() { + s.teardownSuite() +} + +type NsSuite struct { + suite.Suite + teardownSuite func() +} + +func (s *NsSuite) SetupSuite() { + s.teardownSuite = setupSuite(&s.Suite, "ns") +} + +func (s *NsSuite) TearDownSuite() { + s.teardownSuite() +} + +func setupSuite(s *suite.Suite, topologyName string) func() { + t := s.T() + topology, err := LoadTopology(TopologyDir, 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) + } + + t.Logf("topo %s loaded", topologyName) + return func() { + topology.Unconfigure() + } +} + +func TestTapSuite(t *testing.T) { + var m TapSuite + suite.Run(t, &m) +} + +func TestNs(t *testing.T) { + var m NsSuite + suite.Run(t, &m) +} + +func TestVeths2(t *testing.T) { + var m Veths2Suite + suite.Run(t, &m) + +} diff --git a/extras/hs-test/go.mod b/extras/hs-test/go.mod new file mode 100755 index 00000000000..0674b587925 --- /dev/null +++ b/extras/hs-test/go.mod @@ -0,0 +1,30 @@ +module fd.io/hs-test + +go 1.18 + +require ( + git.fd.io/govpp.git v0.4.0 + github.com/edwarnicke/exechelper v1.0.2 + 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 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/edwarnicke/log v1.0.0 // indirect + github.com/fsnotify/fsnotify v1.4.9 // indirect + 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 + 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 new file mode 100755 index 00000000000..13cd56c20b0 --- /dev/null +++ b/extras/hs-test/go.sum @@ -0,0 +1,143 @@ +git.fd.io/govpp.git v0.3.6-0.20210202134006-4c1cccf48cd1/go.mod h1:OCVd4W8SH+666KRQoMj6PM+oipLDZAHhqMz9B1TGbgI= +git.fd.io/govpp.git v0.3.6-0.20210927044411-385ccc0d8ba9/go.mod h1:OCVd4W8SH+666KRQoMj6PM+oipLDZAHhqMz9B1TGbgI= +git.fd.io/govpp.git v0.4.0 h1:u/hxo5rwTpwmR8ambm5Xtf1WXEeDyoYOrD2m8TKcD34= +git.fd.io/govpp.git v0.4.0/go.mod h1:OCVd4W8SH+666KRQoMj6PM+oipLDZAHhqMz9B1TGbgI= +github.com/bennyscetbun/jsongo v1.1.0/go.mod h1:suxbVmjBV8+A2BBAM5EYVh6Uj8j3rqJhzWf3hv7Ff8U= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/edwarnicke/exechelper v1.0.2 h1:dD49Ui2U0FBFxxhalnKw6vLS0P0TkgnXBRvKL/xmC5w= +github.com/edwarnicke/exechelper v1.0.2/go.mod h1:/T271jtNX/ND4De6pa2aRy2+8sNtyCDB1A2pp4M+fUs= +github.com/edwarnicke/govpp v0.0.0-20220311182453-f32f292e0e91 h1:iDVzIaYZjTg+hQGcDeOLJZc+5VfKvW7ye54EFX3CRQA= +github.com/edwarnicke/govpp v0.0.0-20220311182453-f32f292e0e91/go.mod h1:kHDnxA+SSNFeMEHz7xvhub1zvx4mOTRlWWRCay2n5NM= +github.com/edwarnicke/log v1.0.0 h1:T6uRNCmR99GTt/CpRr2Gz8eGW8fm0HMThDNGdNxPaGk= +github.com/edwarnicke/log v1.0.0/go.mod h1:eWsQQlQ0IU5wHlJvyXFH3dS8s2g9GzN7JnXodo6yaIY= +github.com/edwarnicke/vpphelper v0.0.0-20210617172001-3e6797de32c3 h1:BGWyFzJ9QfXOd0jzTe08d98KOXus/3KS4aSgrZKTGx8= +github.com/edwarnicke/vpphelper v0.0.0-20210617172001-3e6797de32c3/go.mod h1:2KXgJqOUUCh9S4Zs2jAycQLqAcRGge3q+GXV2rN55ZQ= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +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= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +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/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= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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= +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/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= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +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= diff --git a/extras/hs-test/ldp_test.go b/extras/hs-test/ldp_test.go new file mode 100755 index 00000000000..0783b189522 --- /dev/null +++ b/extras/hs-test/ldp_test.go @@ -0,0 +1,114 @@ +package main + +import ( + "fmt" + "os" + "time" + + "github.com/edwarnicke/exechelper" +) + +func (s *Veths2Suite) TestLDPreloadIperfVpp() { + t := s.T() + var clnVclConf, srvVclConf Stanza + + srvInstance := "vpp-ldp-srv" + clnInstance := "vpp-ldp-cln" + srvPath := "/tmp/" + srvInstance + clnPath := "/tmp/" + clnInstance + srvVcl := srvPath + "/vcl_srv.conf" + clnVcl := clnPath + "/vcl_cln.conf" + + exechelper.Run("mkdir " + srvPath) + exechelper.Run("mkdir " + clnPath) + + ldpreload := os.Getenv("HST_LDPRELOAD") + s.Assert().NotEqual("", ldpreload) + + ldpreload = "LD_PRELOAD=" + ldpreload + + stopServerCh := make(chan struct{}, 1) + srvCh := make(chan error, 1) + clnCh := make(chan error) + + fmt.Println("starting VPPs") + + err := dockerRun(srvInstance, fmt.Sprintf("-v /tmp/%s:/tmp", srvInstance)) + if err != nil { + t.Errorf("%v", err) + return + } + defer func() { exechelper.Run("docker stop " + srvInstance) }() + + err = dockerRun(clnInstance, fmt.Sprintf("-v /tmp/%s:/tmp", clnInstance)) + if err != nil { + t.Errorf("%v", err) + return + } + defer func() { exechelper.Run("docker stop " + clnInstance) }() + + _, err = hstExec("2veths srv", srvInstance) + if err != nil { + t.Errorf("%v", err) + return + } + + _, err = hstExec("2veths cln", clnInstance) + if err != nil { + t.Errorf("%v", err) + return + } + + err = clnVclConf. + NewStanza("vcl"). + Append("rx-fifo-size 4000000"). + Append("tx-fifo-size 4000000"). + Append("app-scope-local"). + Append("app-scope-global"). + Append("use-mq-eventfd"). + Append(fmt.Sprintf("app-socket-api /tmp/%s/2veths/var/run/app_ns_sockets/2", clnInstance)).Close(). + SaveToFile(clnVcl) + if err != nil { + t.Errorf("%v", err) + t.FailNow() + } + + err = srvVclConf. + NewStanza("vcl"). + Append("rx-fifo-size 4000000"). + Append("tx-fifo-size 4000000"). + Append("app-scope-local"). + Append("app-scope-global"). + Append("use-mq-eventfd"). + Append(fmt.Sprintf("app-socket-api /tmp/%s/2veths/var/run/app_ns_sockets/1", srvInstance)).Close(). + SaveToFile(srvVcl) + if err != nil { + t.Errorf("%v", err) + t.FailNow() + } + fmt.Printf("attaching server to vpp") + + // FIXME + time.Sleep(5 * time.Second) + + srvEnv := append(os.Environ(), ldpreload, "VCL_CONFIG="+srvVcl) + go StartServerApp(srvCh, stopServerCh, srvEnv) + + err = <-srvCh + if err != nil { + s.FailNow("vcl server", "%v", err) + } + + fmt.Println("attaching client to vpp") + clnEnv := append(os.Environ(), ldpreload, "VCL_CONFIG="+clnVcl) + go StartClientApp(clnEnv, clnCh) + + // wait for client's result + err = <-clnCh + if err != nil { + s.Failf("client", "%v", err) + } + + // stop server + stopServerCh <- struct{}{} +} diff --git a/extras/hs-test/linux_iperf_test.go b/extras/hs-test/linux_iperf_test.go new file mode 100755 index 00000000000..92a85cf6bbe --- /dev/null +++ b/extras/hs-test/linux_iperf_test.go @@ -0,0 +1,26 @@ +package main + +func (s *TapSuite) TestLinuxIperf() { + t := s.T() + clnCh := make(chan error) + stopServerCh := make(chan struct{}) + srvCh := make(chan error, 1) + defer func() { + stopServerCh <- struct{}{} + }() + + go StartServerApp(srvCh, stopServerCh, nil) + err := <-srvCh + if err != nil { + t.Errorf("%v", err) + t.FailNow() + } + t.Log("server running") + go StartClientApp(nil, clnCh) + t.Log("client running") + err = <-clnCh + if err != nil { + s.Failf("client", "%v", err) + } + t.Log("Test completed") +} diff --git a/extras/hs-test/main.go b/extras/hs-test/main.go new file mode 100755 index 00000000000..10149178ce6 --- /dev/null +++ b/extras/hs-test/main.go @@ -0,0 +1,155 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "os/exec" + "os/signal" + + "git.fd.io/govpp.git/api" +) + +type CfgTable map[string]func([]string) *ActionResult + +var cfgTable CfgTable + +type ConfFn func(context.Context, api.Connection) error + +func newVppContext() (context.Context, context.CancelFunc) { + ctx, cancel := signal.NotifyContext( + context.Background(), + os.Interrupt, + ) + return ctx, cancel +} + +func Vppcli(runDir, command string) (string, error) { + cmd := exec.Command("vppctl", "-s", fmt.Sprintf("%s/var/run/vpp/cli.sock", runDir), command) + o, err := cmd.CombinedOutput() + if err != nil { + fmt.Printf("failed to execute command: '%v'.\n", err) + } + fmt.Printf("Command output %s", string(o)) + return string(o), err +} + +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 frst") + } + 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.ErrOutput = s + } +} + +func OkResult() *ActionResult { + return NewActionResult(nil) +} + +func reg(key string, fn func([]string) *ActionResult) { + cfgTable[key] = fn +} + +func processArgs() *ActionResult { + fn := cfgTable[os.Args[1]] + if fn == nil { + return NewActionResult(fmt.Errorf("internal: no config found for %s", os.Args[1])) + } + return fn(os.Args) +} + +func main() { + if len(os.Args) == 0 { + fmt.Println("args required") + return + } + + if os.Args[1] == "rm" { + topology, err := LoadTopology(TopologyDir, os.Args[2]) + if err != nil { + fmt.Printf("falied to load topologies: %v\n", err) + os.Exit(1) + } + topology.Unconfigure() + os.Exit(0) + } + + RegisterActions() + + 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 new file mode 100755 index 00000000000..f3f3c1b2fcb --- /dev/null +++ b/extras/hs-test/netconfig.go @@ -0,0 +1,283 @@ +package main + +import ( + "errors" + "fmt" + "os/exec" +) + +type NetType string + +const ( + NetNs NetType = "netns" + Veth = "veth" + Tap = "tap" +) + +type NetConfig struct { + Configure func() error + Unconfigure func() +} + +type NetTopology []NetConfig + +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) } + } else if t == "tap" { + return func() error { return configureTap(cfg) } + } + return nil +} + +func newUnconfigFn(cfg NetDevConfig) func() { + t := cfg["type"] + name := cfg["name"].(string) + + if t == "tap" { + return func() { DelLink(name) } + } else 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) NetConfig { + var nc NetConfig + + nc.Configure = newConfigFn(cfg) + nc.Unconfigure = newUnconfigFn(cfg) + + return nc +} + +func DelBridge(brName, ns string) error { + err := SetDevDown(brName, ns) + if err != err { + return err + } + + err = addDelBridge(brName, ns, false) + if err != nil { + return err + } + + return nil +} + +func configureBridge(dev NetDevConfig) error { + var ifs []string + for _, v := range dev["interfaces"].([]interface{}) { + ifs = append(ifs, v.(string)) + } + return AddBridge(dev["name"].(string), ifs, dev["netns"].(string)) +} + +func configureTap(dev NetDevConfig) error { + return AddTap(dev["name"].(string), dev["ip4"].(string)) +} + +func SetDevUp(dev, ns string) error { + return setDevUpDown(dev, ns, true) +} + +func SetDevDown(dev, ns string) error { + return setDevUpDown(dev, ns, false) +} + +func AddTap(ifName, ifAddress string) error { + cmd := exec.Command("ip", "tuntap", "add", ifName, "mode", "tap") + o, err := cmd.CombinedOutput() + if err != nil { + s := fmt.Sprintf("error creating tap %s: %v: %s", ifName, err, string(o)) + return errors.New(s) + } + + cmd = exec.Command("ip", "addr", "add", ifAddress, "dev", ifName) + err = cmd.Run() + if err != nil { + DelLink(ifName) + s := fmt.Sprintf("error setting addr for tap %s: %v", ifName, err) + return errors.New(s) + } + + err = SetDevUp(ifName, "") + if err != nil { + DelLink(ifName) + return err + } + return nil +} + +func DelLink(ifName string) { + cmd := exec.Command("ip", "link", "del", ifName) + cmd.Run() +} + +func setDevUpDown(dev, ns string, isUp bool) error { + var op string + if isUp { + op = "up" + } else { + op = "down" + } + c := []string{"ip", "link", "set", "dev", dev, op} + cmd := appendNetns(c, ns) + err := cmd.Run() + if err != nil { + s := fmt.Sprintf("error bringing %s device %s!", dev, op) + return errors.New(s) + } + return nil +} + +func AddVethPair(ifName, peerName string) error { + cmd := exec.Command("ip", "link", "add", ifName, "type", "veth", "peer", "name", peerName) + err := cmd.Run() + if err != nil { + return fmt.Errorf("creating veth pair failed: %v", err) + } + err = SetDevUp(ifName, "") + if err != nil { + return fmt.Errorf("set link up failed: %v", err) + } + return nil +} + +func addDelNetns(name string, isAdd bool) error { + var op string + if isAdd { + op = "add" + } else { + op = "del" + } + cmd := exec.Command("ip", "netns", op, name) + _, err := cmd.CombinedOutput() + if err != nil { + return errors.New("add/del netns failed") + } + return nil +} + +func AddNetns(nsName string) error { + return addDelNetns(nsName, true) +} + +func DelNetns(nsName string) error { + return addDelNetns(nsName, false) +} + +func LinkSetNetns(ifName, ns string) error { + cmd := exec.Command("ip", "link", "set", "dev", ifName, "up", "netns", ns) + err := cmd.Run() + if err != nil { + return fmt.Errorf("error setting device '%s' to netns '%s: %v", ifName, ns, err) + } + return nil +} + +func NewCommand(s []string, ns string) *exec.Cmd { + return appendNetns(s, ns) +} + +func appendNetns(s []string, ns string) *exec.Cmd { + var cmd *exec.Cmd + if ns == "" { + // use default namespace + cmd = exec.Command(s[0], s[1:]...) + } else { + var args = []string{"netns", "exec", ns} + args = append(args, s[:]...) + cmd = exec.Command("ip", args...) + } + return cmd +} + +func addDelBridge(brName, ns string, isAdd bool) error { + var op string + if isAdd { + op = "addbr" + } else { + op = "delbr" + } + var c = []string{"brctl", op, brName} + cmd := appendNetns(c, ns) + err := cmd.Run() + if err != nil { + s := fmt.Sprintf("%s %s failed!", op, brName) + return errors.New(s) + } + return nil +} + +func AddBridge(brName string, ifs []string, ns string) error { + err := addDelBridge(brName, ns, true) + if err != nil { + return err + } + + for _, v := range ifs { + c := []string{"brctl", "addif", brName, v} + cmd := appendNetns(c, ns) + err = cmd.Run() + if err != nil { + s := fmt.Sprintf("error adding %s to bridge %s: %v", v, brName, err) + return errors.New(s) + } + } + err = SetDevUp(brName, ns) + if err != nil { + return err + } + return nil +} diff --git a/extras/hs-test/proxy_test.go b/extras/hs-test/proxy_test.go new file mode 100755 index 00000000000..797de5255fa --- /dev/null +++ b/extras/hs-test/proxy_test.go @@ -0,0 +1,98 @@ +package main + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/edwarnicke/exechelper" +) + +func testProxyHttpTcp(t *testing.T, dockerInstance string, proxySetup func() error) error { + const outputFile = "test.data" + const srcFile = "10M" + stopServer := make(chan struct{}, 1) + serverRunning := make(chan struct{}, 1) + + volumeArgs := fmt.Sprintf("-v shared-vol:/tmp/%s", dockerInstance) + err := dockerRun(dockerInstance, volumeArgs) + if err != nil { + return fmt.Errorf("failed to start container: %v", err) + } + defer func() { exechelper.Run("docker stop " + dockerInstance) }() + + // start & configure vpp in the container + _, err = hstExec(dockerInstance, dockerInstance) + if err != nil { + return fmt.Errorf("error starting vpp in container: %v", err) + } + + fmt.Println("VPP running and configured...") + + if err := proxySetup(); err != nil { + return fmt.Errorf("failed to setup proxy: %v", err) + } + fmt.Println("Proxy configured...") + + // create test file + err = exechelper.Run(fmt.Sprintf("ip netns exec server truncate -s %s %s", srcFile, srcFile)) + if err != nil { + return fmt.Errorf("failed to run truncate command") + } + defer func() { os.Remove(srcFile) }() + + fmt.Println("Test file created...") + + go startHttpServer(serverRunning, stopServer, ":666", "server") + // TODO better error handling and recovery + <-serverRunning + + defer func(chan struct{}) { + stopServer <- struct{}{} + }(stopServer) + + fmt.Println("http server started...") + + c := fmt.Sprintf("ip netns exec client wget --retry-connrefused --retry-on-http-error=503 --tries=10 -O %s 10.0.0.2:555/%s", outputFile, srcFile) + _, err = exechelper.CombinedOutput(c) + if err != nil { + return fmt.Errorf("failed to run wget: %v", err) + } + stopServer <- struct{}{} + + defer func() { os.Remove(outputFile) }() + + if err = assertFileSize(outputFile, srcFile); err != nil { + return err + } + return nil +} + +func (s *NsSuite) TestVppProxyHttpTcp() { + t := s.T() + dockerInstance := "vpp-proxy" + err := testProxyHttpTcp(t, dockerInstance, configureVppProxy) + if err != nil { + t.Errorf("%v", err) + } +} + +func (s *NsSuite) TestEnvoyProxyHttpTcp() { + t := s.T() + exechelper.Run("docker volume create --name=shared-vol") + defer func() { + exechelper.Run("docker stop envoy") + }() + + ctx, cancel := context.WithCancel(context.Background()) + + dockerInstance := "vpp-envoy" + err := testProxyHttpTcp(t, dockerInstance, func() error { + return setupEnvoy(t, ctx, dockerInstance) + }) + if err != nil { + t.Errorf("%v", err) + } + cancel() +} diff --git a/extras/hs-test/script/build.sh b/extras/hs-test/script/build.sh new file mode 100755 index 00000000000..d80823b5859 --- /dev/null +++ b/extras/hs-test/script/build.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +source vars + +bin=vpp-data/bin +lib=vpp-data/lib + +mkdir -p ${bin} ${lib} || true + +cp ${VPP_WS}/build-root/build-vpp_debug-native/vpp/bin/* ${bin} +cp -r ${VPP_WS}/build-root/build-vpp_debug-native/vpp/lib/x86_64-linux-gnu/* ${lib} + +docker build -t hs-test/vpp -f Dockerfile.vpp . diff --git a/extras/hs-test/test b/extras/hs-test/test new file mode 100755 index 00000000000..0bccc871711 --- /dev/null +++ b/extras/hs-test/test @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +source vars +sudo -E go test -buildvcs=false -v $@ diff --git a/extras/hs-test/tools/http_server/http_server.go b/extras/hs-test/tools/http_server/http_server.go new file mode 100755 index 00000000000..2b6512be5fd --- /dev/null +++ b/extras/hs-test/tools/http_server/http_server.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "io" + "net/http" + "os" +) + +func main() { + if len(os.Args) < 2 { + fmt.Println("arg expected") + os.Exit(1) + } + + http.HandleFunc("/10M", func(w http.ResponseWriter, r *http.Request) { + file, _ := os.Open("10M") + defer file.Close() + io.Copy(w, file) + }) + err := http.ListenAndServe(os.Args[1], nil) + if err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } +} diff --git a/extras/hs-test/topo.go b/extras/hs-test/topo.go new file mode 100755 index 00000000000..f11761460d8 --- /dev/null +++ b/extras/hs-test/topo.go @@ -0,0 +1,75 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + + "gopkg.in/yaml.v3" +) + +type NetDevConfig map[string]interface{} + +type YamlTopology struct { + Devices []NetDevConfig `yaml:"devices"` +} + +func AddAddress(device, address, ns string) error { + c := []string{"ip", "addr", "add", address, "dev", device} + cmd := appendNetns(c, ns) + err := cmd.Run() + if err != nil { + return fmt.Errorf("failed to set ip address for %s: %v", device, err) + } + 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/topo/2peerVeth.yaml b/extras/hs-test/topo/2peerVeth.yaml new file mode 100755 index 00000000000..d40f3803879 --- /dev/null +++ b/extras/hs-test/topo/2peerVeth.yaml @@ -0,0 +1,23 @@ +--- +devices: + - name: "hsns" + type: "netns" + + - name: "vppsrv" + type: "veth" + peer: + name: "vppsrv_veth" + netns: "hsns" + + - name: "vppcln" + type: "veth" + peer: + name: "vppcln_veth" + netns: "hsns" + + - name: "br" + type: "bridge" + netns: "hsns" + interfaces: + - vppsrv_veth + - vppcln_veth diff --git a/extras/hs-test/topo/ns.yaml b/extras/hs-test/topo/ns.yaml new file mode 100755 index 00000000000..c1c8c540b2b --- /dev/null +++ b/extras/hs-test/topo/ns.yaml @@ -0,0 +1,21 @@ +--- +devices: + - name: "client" + type: "netns" + + - name: "server" + type: "netns" + + - name: "vpp0" + type: "veth" + peer: + name: "client" + netns: "client" + ip4: "10.0.0.1/24" + + - name: "vpp1" + type: "veth" + peer: + name: "server" + netns: "server" + ip4: "10.0.1.1/24" \ No newline at end of file diff --git a/extras/hs-test/topo/tap.yaml b/extras/hs-test/topo/tap.yaml new file mode 100755 index 00000000000..4cd95d6e48a --- /dev/null +++ b/extras/hs-test/topo/tap.yaml @@ -0,0 +1,5 @@ +--- +devices: + - name: "tap0" + type: "tap" + ip4: "10.10.10.1/24" diff --git a/extras/hs-test/tps_test.go b/extras/hs-test/tps_test.go new file mode 100755 index 00000000000..dd87da1718e --- /dev/null +++ b/extras/hs-test/tps_test.go @@ -0,0 +1,36 @@ +package main + +import ( + "github.com/edwarnicke/exechelper" +) + +func (s *NsSuite) TestHttpTps() { + t := s.T() + finished := make(chan error, 1) + server_ip := "10.0.0.2" + port := "8080" + dockerInstance := "http-tps" + + t.Log("starting vpp..") + + err := dockerRun(dockerInstance, "") + if err != nil { + t.Errorf("%v", err) + return + } + defer func() { exechelper.Run("docker stop " + dockerInstance) }() + + // start & configure vpp in the container + _, err = hstExec(dockerInstance, dockerInstance) + if err != nil { + t.Errorf("%v", err) + return + } + + go startWget(finished, server_ip, port, "client") + // wait for client + err = <-finished + if err != nil { + t.Errorf("%v", err) + } +} diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go new file mode 100755 index 00000000000..25303591a43 --- /dev/null +++ b/extras/hs-test/utils.go @@ -0,0 +1,334 @@ +package main + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "strings" + "testing" + "time" + + "github.com/edwarnicke/exechelper" +) + +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 unittest_plugin.so { enable } + plugin dpdk_plugin.so { disable } + plugin crypto_aesni_plugin.so { enable } + plugin quic_plugin.so { enable } +} + +` + +const TopologyDir string = "topo/" + +type Stanza struct { + content string + pad int +} + +type ActionResult struct { + Err error + Desc string + ErrOutput string + StdOutput string +} + +type JsonResult struct { + Code int + Desc string + ErrOutput string + StdOutput string +} + +func StartServerApp(running chan error, done chan struct{}, env []string) { + cmd := exec.Command("iperf3", "-4", "-s") + if env != nil { + cmd.Env = env + } + err := cmd.Start() + if err != nil { + msg := fmt.Errorf("failed to start iperf server: %v", err) + running <- msg + return + } + running <- nil + <-done + cmd.Process.Kill() +} + +func StartClientApp(env []string, clnCh chan error) { + defer func() { + clnCh <- nil + }() + + nTries := 0 + + for { + cmd := exec.Command("iperf3", "-c", "10.10.10.1", "-u", "-l", "1460", "-b", "10g") + if env != nil { + cmd.Env = env + } + o, err := cmd.CombinedOutput() + if err != nil { + if nTries > 5 { + clnCh <- fmt.Errorf("failed to start client app '%s'.\n%s", err, o) + return + } + time.Sleep(1 * time.Second) + nTries++ + continue + } else { + fmt.Printf("Client output: %s", o) + } + break + } +} + +// run vpphelper in docker +func hstExec(args string, instance string) (string, error) { + syncFile := fmt.Sprintf("/tmp/%s/sync/rc", instance) + os.Remove(syncFile) + + c := "docker exec -d " + instance + " /hs-test " + args + err := exechelper.Run(c) + 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 waitForSyncFile(fname string) (*JsonResult, error) { + var res JsonResult + + for i := 0; i < 60; 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 dockerRun(instance, args string) error { + exechelper.Run(fmt.Sprintf("mkdir -p /tmp/%s/sync", instance)) + syncPath := fmt.Sprintf("-v /tmp/%s/sync:/tmp/sync", instance) + cmd := "docker run --cap-add=all -d --privileged --network host --rm " + cmd += syncPath + cmd += " " + args + cmd += " --name " + instance + " hs-test/vpp" + fmt.Println(cmd) + return exechelper.Run(cmd) +} + +func assertFileSize(f1, f2 string) error { + fi1, err := os.Stat(f1) + if err != nil { + return err + } + + fi2, err1 := os.Stat(f2) + if err1 != nil { + return err1 + } + + if fi1.Size() != fi2.Size() { + return fmt.Errorf("file sizes differ (%d vs %d)", fi1.Size(), fi2.Size()) + } + return nil +} + +func dockerExec(cmd string, instance string) ([]byte, error) { + c := "docker exec -d " + instance + " " + cmd + return exechelper.CombinedOutput(c) +} + +func startEnvoy(ctx context.Context, dockerInstance string) <-chan error { + errCh := make(chan error) + wd, err := os.Getwd() + if err != nil { + errCh <- err + return errCh + } + + c := []string{"docker", "run", "--rm", "--name", "envoy", + "-v", fmt.Sprintf("%s/envoy/proxy.yaml:/etc/envoy/envoy.yaml", wd), + "-v", fmt.Sprintf("shared-vol:/tmp/%s", dockerInstance), + "-v", fmt.Sprintf("%s/envoy:/tmp", wd), + "-e", "VCL_CONFIG=/tmp/vcl.conf", + "envoyproxy/envoy-contrib:v1.21-latest"} + fmt.Println(c) + + go func(errCh chan error) { + count := 0 + var cmd *exec.Cmd + for ; ; count++ { + cmd = NewCommand(c, "") + err = cmd.Start() + if err == nil { + break + } + if count > 5 { + errCh <- fmt.Errorf("Failed to start envoy docker after %d attempts", count) + return + } + } + + err = cmd.Wait() + if err != nil { + errCh <- fmt.Errorf("failed to start docker: %v", err) + return + } + <-ctx.Done() + }(errCh) + return errCh +} + +func setupEnvoy(t *testing.T, ctx context.Context, dockerInstance string) error { + errCh := startEnvoy(ctx, dockerInstance) + select { + case err := <-errCh: + return err + default: + } + + go func(ctx context.Context, errCh <-chan error) { + for { + select { + // handle cancel() call from outside to gracefully stop the routine + case <-ctx.Done(): + return + default: + select { + case err := <-errCh: + fmt.Printf("error while running envoy: %v", err) + default: + } + } + } + }(ctx, errCh) + return nil +} + +func configureVppProxy() error { + _, err := dockerExec("vppctl test proxy server server-uri tcp://10.0.0.2/555 client-uri tcp://10.0.1.1/666", + "vpp-proxy") + if err != nil { + return fmt.Errorf("error while configuring vpp proxy test: %v", err) + } + return nil +} + +func startHttpServer(running chan struct{}, done chan struct{}, addressPort, netNs string) { + cmd := NewCommand([]string{"./http_server", addressPort}, netNs) + err := cmd.Start() + if err != nil { + fmt.Println("Failed to start http server") + return + } + running <- struct{}{} + <-done + cmd.Process.Kill() +} + +func startWget(finished chan error, server_ip, port string, netNs string) { + fname := "test_file_10M" + defer func() { + finished <- errors.New("wget error") + }() + + cmd := NewCommand([]string{"wget", "--tries=5", "-q", "-O", "/dev/null", server_ip + ":" + port + "/" + fname}, + netNs) + o, err := cmd.CombinedOutput() + if err != nil { + fmt.Printf("wget error: '%s'.\n%s", err, o) + return + } + fmt.Printf("Client output: %s", o) + finished <- nil +} + +func (c *Stanza) NewStanza(name string) *Stanza { + c.Append("\n" + name + " {") + c.pad += 2 + return c +} + +func (c *Stanza) Append(name string) *Stanza { + c.content += strings.Repeat(" ", c.pad) + c.content += name + "\n" + return c +} + +func (c *Stanza) Close() *Stanza { + c.content += "}\n" + c.pad -= 2 + return c +} + +func (s *Stanza) ToString() string { + return s.content +} + +func (s *Stanza) SaveToFile(fileName string) error { + fo, err := os.Create(fileName) + if err != nil { + return err + } + defer fo.Close() + + _, err = io.Copy(fo, strings.NewReader(s.content)) + return err +} diff --git a/extras/hs-test/vars b/extras/hs-test/vars new file mode 100755 index 00000000000..1717b114de7 --- /dev/null +++ b/extras/hs-test/vars @@ -0,0 +1,4 @@ +export VPP_WS=../../ + +export HST_LDPRELOAD=${VPP_WS}/build-root/build-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvcl_ldpreload.so +export PATH=${VPP_WS}/build-root/build-vpp_debug-native/vpp/bin:$PATH diff --git a/extras/hs-test/vcl_test.go b/extras/hs-test/vcl_test.go new file mode 100755 index 00000000000..8c4afe8fe30 --- /dev/null +++ b/extras/hs-test/vcl_test.go @@ -0,0 +1,86 @@ +package main + +import ( + "fmt" + + "github.com/edwarnicke/exechelper" +) + +func (s *Veths2Suite) TestVclEchoQuic() { + s.T().Skip("quic test skipping..") + s.testVclEcho("quic") +} + +func (s *Veths2Suite) TestVclEchoUdp() { + s.T().Skip("udp echo currently broken in vpp, skipping..") + s.testVclEcho("udp") +} + +func (s *Veths2Suite) TestVclEchoTcp() { + s.testVclEcho("tcp") +} + +func (s *Veths2Suite) testVclEcho(proto string) { + t := s.T() + + exechelper.Run("docker volume create --name=echo-srv-vol") + exechelper.Run("docker volume create --name=echo-cln-vol") + + srvInstance := "vpp-echo-srv" + clnInstance := "vpp-echo-cln" + echoSrv := "echo-srv" + echoCln := "echo-cln" + + err := dockerRun(srvInstance, "-v echo-srv-vol:/tmp/2veths") + if err != nil { + t.Errorf("%v", err) + return + } + defer func() { exechelper.Run("docker stop " + srvInstance) }() + + err = dockerRun(clnInstance, "-v echo-cln-vol:/tmp/2veths") + if err != nil { + t.Errorf("%v", err) + return + } + defer func() { exechelper.Run("docker stop " + clnInstance) }() + + err = dockerRun(echoSrv, fmt.Sprintf("-v echo-srv-vol:/tmp/%s", echoSrv)) + if err != nil { + t.Errorf("%v", err) + return + } + defer func() { exechelper.Run("docker stop " + echoSrv) }() + + err = dockerRun(echoCln, fmt.Sprintf("-v echo-cln-vol:/tmp/%s", echoCln)) + if err != nil { + t.Errorf("%v", err) + return + } + defer func() { exechelper.Run("docker stop " + echoCln) }() + + _, err = hstExec("2veths srv", srvInstance) + if err != nil { + t.Errorf("%v", err) + return + } + + _, err = hstExec("2veths cln", clnInstance) + if err != nil { + t.Errorf("%v", err) + return + } + + // run server app + _, err = hstExec("echo-server "+proto, echoSrv) + if err != nil { + t.Errorf("echo server: %v", err) + return + } + + o, err := hstExec("echo-client "+proto, echoCln) + if err != nil { + t.Errorf("echo client: %v", err) + } + fmt.Println(o) +} -- cgit 1.2.3-korg