hs-test: support for multiple workers

Type: test

Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
Change-Id: Ie90e4b02c268bc3ca40171b03829f5686fb83162
This commit is contained in:
Filip Tehlar
2023-04-28 10:29:47 +02:00
committed by Florin Coras
parent 47f3527108
commit 608d0069d9
13 changed files with 173 additions and 48 deletions

View File

@ -19,6 +19,10 @@ ifeq ($(DEBUG),)
DEBUG=false DEBUG=false
endif endif
ifeq ($(CPUS),)
CPUS=1
endif
ifeq ($(UBUNTU_CODENAME),) ifeq ($(UBUNTU_CODENAME),)
UBUNTU_CODENAME=$(shell grep '^UBUNTU_CODENAME=' /etc/os-release | cut -f2- -d=) UBUNTU_CODENAME=$(shell grep '^UBUNTU_CODENAME=' /etc/os-release | cut -f2- -d=)
endif endif
@ -47,6 +51,7 @@ help:
@echo " UNCONFIGURE=[true|false] - unconfigure selected test" @echo " UNCONFIGURE=[true|false] - unconfigure selected test"
@echo " DEBUG=[true|false] - attach VPP to GDB" @echo " DEBUG=[true|false] - attach VPP to GDB"
@echo " TEST=[test-name] - specific test to run" @echo " TEST=[test-name] - specific test to run"
@echo " CPUS=[n-cpus] - number of cpus to run with vpp"
@echo @echo
@echo "List of all tests:" @echo "List of all tests:"
$(call list_tests) $(call list_tests)
@ -64,7 +69,7 @@ build-vpp-debug:
.PHONY: test .PHONY: test
test: .deps.ok .build.vpp test: .deps.ok .build.vpp
@bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \ @bash ./test --persist=$(PERSIST) --verbose=$(VERBOSE) \
--unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --unconfigure=$(UNCONFIGURE) --debug=$(DEBUG) --test=$(TEST) --cpus=$(CPUS)
build-go: build-go:
go build ./tools/http_server go build ./tools/http_server

View File

@ -216,16 +216,12 @@ func (c *Container) getEnvVarsAsCliOption() string {
return cliOption return cliOption
} }
func (c *Container) newVppInstance(additionalConfig ...Stanza) (*VppInstance, error) { func (c *Container) newVppInstance(cpus []int, additionalConfigs ...Stanza) (*VppInstance, error) {
vpp := new(VppInstance) vpp := new(VppInstance)
vpp.container = c vpp.container = c
vpp.cpus = cpus
if len(additionalConfig) > 0 { vpp.additionalConfig = append(vpp.additionalConfig, additionalConfigs...)
vpp.additionalConfig = additionalConfig[0]
}
c.vppInstance = vpp c.vppInstance = vpp
return vpp, nil return vpp, nil
} }

69
extras/hs-test/cpu.go Normal file
View File

@ -0,0 +1,69 @@
package main
import (
"bufio"
"fmt"
"os"
)
var CPU_PATH = "/sys/fs/cgroup/cpuset.cpus.effective"
type CpuContext struct {
cpuAllocator *CpuAllocatorT
cpus []int
}
func (c *CpuContext) Release() {
c.cpuAllocator.cpus = append(c.cpuAllocator.cpus, c.cpus...)
c.cpus = c.cpus[:0] // empty the list
}
type CpuAllocatorT struct {
cpus []int
}
var cpuAllocator *CpuAllocatorT = nil
func (c *CpuAllocatorT) Allocate(nCpus int) (*CpuContext, error) {
var cpuCtx CpuContext
if len(c.cpus) < nCpus {
return nil, fmt.Errorf("could not allocate %d CPUs; available: %d", nCpus, len(c.cpus))
}
cpuCtx.cpus = c.cpus[0:nCpus]
cpuCtx.cpuAllocator = c
c.cpus = c.cpus[nCpus:]
return &cpuCtx, nil
}
func (c *CpuAllocatorT) readCpus(fname string) error {
var first, last int
file, err := os.Open(CPU_PATH)
if err != nil {
return err
}
defer file.Close()
sc := bufio.NewScanner(file)
sc.Scan()
line := sc.Text()
_, err = fmt.Sscanf(line, "%d-%d", &first, &last)
if err != nil {
return err
}
for i := first; i <= last; i++ {
c.cpus = append(c.cpus, i)
}
return nil
}
func CpuAllocator() (*CpuAllocatorT, error) {
if cpuAllocator == nil {
cpuAllocator = new(CpuAllocatorT)
err := cpuAllocator.readCpus(CPU_PATH)
if err != nil {
return nil, err
}
}
return cpuAllocator, nil
}

View File

@ -14,13 +14,14 @@ import (
) )
const ( const (
defaultNetworkNumber int = 1 DEFAULT_NETWORK_NUM int = 1
) )
var isPersistent = flag.Bool("persist", false, "persists topology config") var isPersistent = flag.Bool("persist", false, "persists topology config")
var isVerbose = flag.Bool("verbose", false, "verbose test output") var isVerbose = flag.Bool("verbose", false, "verbose test output")
var isUnconfiguring = flag.Bool("unconfigure", false, "remove topology") var isUnconfiguring = flag.Bool("unconfigure", false, "remove topology")
var isVppDebug = flag.Bool("debug", false, "attach gdb to vpp") var isVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
var nConfiguredCpus = flag.Int("cpus", 1, "number of CPUs assigned to vpp")
type HstSuite struct { type HstSuite struct {
suite.Suite suite.Suite
@ -30,6 +31,29 @@ type HstSuite struct {
netInterfaces map[string]*NetInterface netInterfaces map[string]*NetInterface
addresser *Addresser addresser *Addresser
testIds map[string]string testIds map[string]string
cpuAllocator *CpuAllocatorT
cpuContexts []*CpuContext
cpuPerVpp int
}
func (s *HstSuite) SetupSuite() {
var err error
s.cpuAllocator, err = CpuAllocator()
if err != nil {
s.FailNow("failed to init cpu allocator: %v", err)
}
s.cpuPerVpp = *nConfiguredCpus
}
func (s *HstSuite) AllocateCpus() []int {
cpuCtx, err := s.cpuAllocator.Allocate(s.cpuPerVpp)
s.assertNil(err)
s.AddCpuContext(cpuCtx)
return cpuCtx.cpus
}
func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) {
s.cpuContexts = append(s.cpuContexts, cpuCtx)
} }
func (s *HstSuite) TearDownSuite() { func (s *HstSuite) TearDownSuite() {
@ -40,6 +64,9 @@ func (s *HstSuite) TearDownTest() {
if *isPersistent { if *isPersistent {
return return
} }
for _, c := range s.cpuContexts {
c.Release()
}
s.resetContainers() s.resetContainers()
s.removeVolumes() s.removeVolumes()
} }
@ -66,7 +93,7 @@ func (s *HstSuite) setupVolumes() {
func (s *HstSuite) setupContainers() { func (s *HstSuite) setupContainers() {
for _, container := range s.containers { for _, container := range s.containers {
if container.isOptional == false { if !container.isOptional {
container.run() container.run()
} }
} }
@ -130,6 +157,12 @@ func (s *HstSuite) skip(args ...any) {
s.T().SkipNow() s.T().SkipNow()
} }
func (s *HstSuite) SkipIfMultiWorker(args ...any) {
if *nConfiguredCpus > 1 {
s.skip("test case not supported with multiple vpp workers")
}
}
func (s *HstSuite) resetContainers() { func (s *HstSuite) resetContainers() {
for _, container := range s.containers { for _, container := range s.containers {
container.stop() container.stop()

View File

@ -77,7 +77,7 @@ func newNetworkInterface(cfg NetDevConfig, a *Addresser) (*NetInterface, error)
var err error var err error
newInterface.addresser = a newInterface.addresser = a
newInterface.name = cfg["name"].(string) newInterface.name = cfg["name"].(string)
newInterface.networkNumber = defaultNetworkNumber newInterface.networkNumber = DEFAULT_NETWORK_NUM
if interfaceType, ok := cfg["type"]; ok { if interfaceType, ok := cfg["type"]; ok {
newInterface.category = interfaceType.(string) newInterface.category = interfaceType.(string)

View File

@ -14,27 +14,25 @@ type NginxSuite struct {
} }
func (s *NginxSuite) SetupSuite() { func (s *NginxSuite) SetupSuite() {
s.HstSuite.SetupSuite()
s.loadNetworkTopology("2taps") s.loadNetworkTopology("2taps")
s.loadContainerTopology("nginxProxyAndServer") s.loadContainerTopology("nginxProxyAndServer")
} }
func (s *NginxSuite) SetupTest() { func (s *NginxSuite) SetupTest() {
s.skipIfUnconfiguring() s.HstSuite.SetupTest()
s.setupVolumes()
s.setupContainers()
// Setup test conditions // Setup test conditions
var startupConfig Stanza var sessionConfig Stanza
startupConfig. sessionConfig.
newStanza("session"). newStanza("session").
append("enable"). append("enable").
append("use-app-socket-api").close() append("use-app-socket-api").close()
cpus := s.AllocateCpus()
// ... for proxy // ... for proxy
vppProxyContainer := s.getContainerByName(vppProxyContainerName) vppProxyContainer := s.getContainerByName(vppProxyContainerName)
proxyVpp, _ := vppProxyContainer.newVppInstance(startupConfig) proxyVpp, _ := vppProxyContainer.newVppInstance(cpus, sessionConfig)
proxyVpp.start() proxyVpp.start()
clientInterface := s.netInterfaces[mirroringClientInterfaceName] clientInterface := s.netInterfaces[mirroringClientInterfaceName]

View File

@ -12,25 +12,24 @@ type NoTopoSuite struct {
} }
func (s *NoTopoSuite) SetupSuite() { func (s *NoTopoSuite) SetupSuite() {
s.HstSuite.SetupSuite()
s.loadNetworkTopology("tap") s.loadNetworkTopology("tap")
s.loadContainerTopology("single") s.loadContainerTopology("single")
} }
func (s *NoTopoSuite) SetupTest() { func (s *NoTopoSuite) SetupTest() {
s.skipIfUnconfiguring() s.HstSuite.SetupTest()
s.setupVolumes()
s.setupContainers()
// Setup test conditions // Setup test conditions
var startupConfig Stanza var sessionConfig Stanza
startupConfig. sessionConfig.
newStanza("session"). newStanza("session").
append("enable"). append("enable").
append("use-app-socket-api").close() append("use-app-socket-api").close()
cpus := s.AllocateCpus()
container := s.getContainerByName(singleTopoContainerVpp) container := s.getContainerByName(singleTopoContainerVpp)
vpp, _ := container.newVppInstance(startupConfig) vpp, _ := container.newVppInstance(cpus, sessionConfig)
vpp.start() vpp.start()
tapInterface := s.netInterfaces[tapInterfaceName] tapInterface := s.netInterfaces[tapInterfaceName]

View File

@ -11,27 +11,26 @@ type NsSuite struct {
} }
func (s *NsSuite) SetupSuite() { func (s *NsSuite) SetupSuite() {
s.HstSuite.SetupSuite()
s.configureNetworkTopology("ns") s.configureNetworkTopology("ns")
s.loadContainerTopology("ns") s.loadContainerTopology("ns")
} }
func (s *NsSuite) SetupTest() { func (s *NsSuite) SetupTest() {
s.skipIfUnconfiguring() s.HstSuite.SetupTest()
s.setupVolumes()
s.setupContainers()
// Setup test conditions // Setup test conditions
var startupConfig Stanza var sessionConfig Stanza
startupConfig. sessionConfig.
newStanza("session"). newStanza("session").
append("enable"). append("enable").
append("use-app-socket-api"). append("use-app-socket-api").
append("evt_qs_memfd_seg"). append("evt_qs_memfd_seg").
append("event-queue-length 100000").close() append("event-queue-length 100000").close()
cpus := s.AllocateCpus()
container := s.getContainerByName("vpp") container := s.getContainerByName("vpp")
vpp, _ := container.newVppInstance(startupConfig) vpp, _ := container.newVppInstance(cpus, sessionConfig)
vpp.start() vpp.start()
idx, err := vpp.createAfPacket(s.netInterfaces[serverInterface]) idx, err := vpp.createAfPacket(s.netInterfaces[serverInterface])

View File

@ -10,6 +10,6 @@ type TapSuite struct {
func (s *TapSuite) SetupSuite() { func (s *TapSuite) SetupSuite() {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
s.HstSuite.SetupSuite()
s.configureNetworkTopology("tap") s.configureNetworkTopology("tap")
} }

View File

@ -16,22 +16,18 @@ type VethsSuite struct {
func (s *VethsSuite) SetupSuite() { func (s *VethsSuite) SetupSuite() {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
s.HstSuite.SetupSuite()
s.configureNetworkTopology("2peerVeth") s.configureNetworkTopology("2peerVeth")
s.loadContainerTopology("2peerVeth") s.loadContainerTopology("2peerVeth")
} }
func (s *VethsSuite) SetupTest() { func (s *VethsSuite) SetupTest() {
s.skipIfUnconfiguring() s.HstSuite.SetupTest()
s.setupVolumes()
s.setupContainers()
// Setup test conditions // Setup test conditions
var startupConfig Stanza var sessionConfig Stanza
startupConfig. sessionConfig.
newStanza("session"). newStanza("session").
append("enable"). append("enable").
append("use-app-socket-api").close() append("use-app-socket-api").close()
@ -39,7 +35,8 @@ func (s *VethsSuite) SetupTest() {
// ... For server // ... For server
serverContainer := s.getContainerByName("server-vpp") serverContainer := s.getContainerByName("server-vpp")
serverVpp, _ := serverContainer.newVppInstance(startupConfig) cpus := s.AllocateCpus()
serverVpp, _ := serverContainer.newVppInstance(cpus, sessionConfig)
s.assertNotNil(serverVpp) s.assertNotNil(serverVpp)
s.setupServerVpp() s.setupServerVpp()
@ -47,7 +44,8 @@ func (s *VethsSuite) SetupTest() {
// ... For client // ... For client
clientContainer := s.getContainerByName("client-vpp") clientContainer := s.getContainerByName("client-vpp")
clientVpp, _ := clientContainer.newVppInstance(startupConfig) cpus = s.AllocateCpus()
clientVpp, _ := clientContainer.newVppInstance(cpus, sessionConfig)
s.assertNotNil(clientVpp) s.assertNotNil(clientVpp)
s.setupClientVpp() s.setupClientVpp()
@ -67,7 +65,6 @@ func (s *VethsSuite) setupServerVpp() {
namespaceSecret := "1" namespaceSecret := "1"
err = serverVpp.addAppNamespace(1, idx, namespaceSecret) err = serverVpp.addAppNamespace(1, idx, namespaceSecret)
s.assertNil(err) s.assertNil(err)
} }
func (s *VethsSuite) setupClientVpp() { func (s *VethsSuite) setupClientVpp() {

View File

@ -38,6 +38,9 @@ case "${i}" in
unconfigure_set=1 unconfigure_set=1
fi fi
;; ;;
--cpus=*)
args="$args -cpus ${i#*=}"
;;
--test=*) --test=*)
tc_name="${i#*=}" tc_name="${i#*=}"
if [ $tc_name != "all" ]; then if [ $tc_name != "all" ]; then

View File

@ -126,7 +126,7 @@ func startWget(finished chan error, server_ip, port, query, netNs string) {
if err != nil { if err != nil {
finished <- fmt.Errorf("wget error: '%v\n\n%s'", err, o) finished <- fmt.Errorf("wget error: '%v\n\n%s'", err, o)
return return
} else if strings.Contains(string(o), "200 OK") == false { } else if !strings.Contains(string(o), "200 OK") {
finished <- fmt.Errorf("wget error: response not 200 OK") finished <- fmt.Errorf("wget error: response not 200 OK")
return return
} }

View File

@ -72,9 +72,10 @@ const (
type VppInstance struct { type VppInstance struct {
container *Container container *Container
additionalConfig Stanza additionalConfig []Stanza
connection *core.Connection connection *core.Connection
apiChannel api.Channel apiChannel api.Channel
cpus []int
} }
func (vpp *VppInstance) getSuite() *HstSuite { func (vpp *VppInstance) getSuite() *HstSuite {
@ -113,7 +114,10 @@ func (vpp *VppInstance) start() error {
defaultApiSocketFilePath, defaultApiSocketFilePath,
defaultLogFilePath, defaultLogFilePath,
) )
configContent += vpp.additionalConfig.toString() configContent += vpp.generateCpuConfig()
for _, c := range vpp.additionalConfig {
configContent += c.toString()
}
startupFileName := vpp.getEtcDir() + "/startup.conf" startupFileName := vpp.getEtcDir() + "/startup.conf"
vpp.container.createFile(startupFileName, configContent) vpp.container.createFile(startupFileName, configContent)
@ -341,3 +345,25 @@ func (vpp *VppInstance) disconnect() {
vpp.connection.Disconnect() vpp.connection.Disconnect()
vpp.apiChannel.Close() vpp.apiChannel.Close()
} }
func (vpp *VppInstance) generateCpuConfig() string {
var c Stanza
var s string
if len(vpp.cpus) < 1 {
return ""
}
c.newStanza("cpu").
append(fmt.Sprintf("main-core %d", vpp.cpus[0]))
workers := vpp.cpus[1:]
if len(workers) > 0 {
for i := 0; i < len(workers); i++ {
if i != 0 {
s = s + ", "
}
s = s + fmt.Sprintf("%d", workers[i])
}
c.append(fmt.Sprintf("corelist-workers %s", s))
}
return c.close().toString()
}