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>
This commit is contained in:
Maros Ondrejicka
2023-02-07 20:40:27 +01:00
committed by Florin Coras
parent 2908f8cf07
commit 7550dd268f
19 changed files with 207 additions and 649 deletions

View File

@ -2,7 +2,6 @@ all: build docker
build:
go build ./tools/http_server
go build .
docker:
bash ./script/build.sh

View File

@ -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
}

View File

@ -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()

View File

@ -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"]

View File

@ -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)
}

View File

@ -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)

View File

@ -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()
}

View File

@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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)

View File

@ -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)
}

View File

@ -1,7 +1,7 @@
---
volumes:
- volume: &shared-vol
host-dir: shared-vol
host-dir: /tmp/shared-vol
containers:
- name: "vpp"

View File

@ -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)
}

View File

@ -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 {

View File

@ -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)

View File

@ -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)
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 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
}
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()