summaryrefslogtreecommitdiffstats
path: root/extras/hs-test/vppinstance.go
diff options
context:
space:
mode:
authorMaros Ondrejicka <maros.ondrejicka@pantheon.tech>2023-01-26 10:07:29 +0100
committerFlorin Coras <florin.coras@gmail.com>2023-02-09 17:02:43 +0000
commitffa3f60290499bdacc37a97c49f2e794b5e1f18c (patch)
treec23820e0d91d2a1a0eef2596b0de60c06f1ea303 /extras/hs-test/vppinstance.go
parent7a6532bb9f3b9c429f92d11a6ccbf55638906d0a (diff)
hs-test: configure VPP from test context
Instead of configuring VPP instances running inside of a container, now the configuration is going to be done from within the test context by using binary API and shared volume that exposes api socket. This converts just some of the test cases, rest is to follow. Type: test Signed-off-by: Maros Ondrejicka <maros.ondrejicka@pantheon.tech> Change-Id: I87e4ab15de488f0eebb01ff514596265fc2a787f
Diffstat (limited to 'extras/hs-test/vppinstance.go')
-rw-r--r--extras/hs-test/vppinstance.go190
1 files changed, 179 insertions, 11 deletions
diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go
index 14d6ab6ba9d..3f9ea871565 100644
--- a/extras/hs-test/vppinstance.go
+++ b/extras/hs-test/vppinstance.go
@@ -4,6 +4,15 @@ import (
"encoding/json"
"fmt"
"github.com/edwarnicke/exechelper"
+
+ "go.fd.io/govpp"
+ "go.fd.io/govpp/api"
+ "go.fd.io/govpp/binapi/af_packet"
+ interfaces "go.fd.io/govpp/binapi/interface"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/session"
+ "go.fd.io/govpp/binapi/vpe"
+ "go.fd.io/govpp/core"
)
const vppConfigTemplate = `unix {
@@ -32,27 +41,34 @@ statseg {
}
plugins {
+ plugin default { disable }
+
plugin unittest_plugin.so { enable }
- plugin dpdk_plugin.so { disable }
- plugin crypto_aesni_plugin.so { enable }
plugin quic_plugin.so { enable }
+ plugin af_packet_plugin.so { enable }
+ plugin hs_apps_plugin.so { enable }
+ plugin http_plugin.so { enable }
}
`
const (
defaultCliSocketFilePath = "/var/run/vpp/cli.sock"
+ defaultApiSocketFilePath = "/var/run/vpp/api.sock"
)
type VppInstance struct {
container *Container
- config VppConfig
+ config *VppConfig
actionFuncName string
+ connection *core.Connection
+ apiChannel api.Channel
}
type VppConfig struct {
Variant string
CliSocketFilePath string
+ additionalConfig Stanza
}
func (vc *VppConfig) getTemplate() string {
@@ -82,10 +98,22 @@ func (vpp *VppInstance) setCliSocket(filePath string) {
}
func (vpp *VppInstance) getCliSocket() string {
- return fmt.Sprintf("%s%s", vpp.container.workDir, vpp.config.CliSocketFilePath)
+ return fmt.Sprintf("%s%s", vpp.container.GetContainerWorkDir(), vpp.config.CliSocketFilePath)
}
-func (vpp *VppInstance) start() error {
+func (vpp *VppInstance) getRunDir() string {
+ return vpp.container.GetContainerWorkDir() + "/var/run/vpp"
+}
+
+func (vpp *VppInstance) getLogDir() string {
+ return vpp.container.GetContainerWorkDir() + "/var/log/vpp"
+}
+
+func (vpp *VppInstance) getEtcDir() string {
+ return vpp.container.GetContainerWorkDir() + "/etc/vpp"
+}
+
+func (vpp *VppInstance) legacyStart() error {
if vpp.actionFuncName == "" {
return fmt.Errorf("vpp start failed: action function name must not be blank")
}
@@ -99,14 +127,70 @@ func (vpp *VppInstance) start() error {
if err != nil {
return fmt.Errorf("vpp start failed: %s", err)
}
+ return nil
+}
+
+func (vpp *VppInstance) start() error {
+ if vpp.actionFuncName != "" {
+ return vpp.legacyStart()
+ }
+
+ // Create folders
+ containerWorkDir := vpp.container.GetContainerWorkDir()
+
+ vpp.container.exec("mkdir --mode=0700 -p " + vpp.getRunDir())
+ vpp.container.exec("mkdir --mode=0700 -p " + vpp.getLogDir())
+ vpp.container.exec("mkdir --mode=0700 -p " + vpp.getEtcDir())
+
+ // Create startup.conf inside the container
+ configContent := fmt.Sprintf(vppConfigTemplate, containerWorkDir, vpp.config.CliSocketFilePath)
+ configContent += vpp.config.additionalConfig.ToString()
+ startupFileName := vpp.getEtcDir() + "/startup.conf"
+ vpp.container.createFile(startupFileName, configContent)
+
+ // Start VPP
+ if err := vpp.container.execServer("vpp -c " + startupFileName); err != nil {
+ return err
+ }
+
+ // Connect to VPP and store the connection
+ sockAddress := vpp.container.GetHostWorkDir() + defaultApiSocketFilePath
+ conn, connEv, err := govpp.AsyncConnect(
+ sockAddress,
+ core.DefaultMaxReconnectAttempts,
+ core.DefaultReconnectInterval)
+ if err != nil {
+ fmt.Println("async connect error: ", err)
+ }
+ vpp.connection = conn
+
+ // ... wait for Connected event
+ e := <-connEv
+ if e.State != core.Connected {
+ fmt.Println("connecting to VPP failed: ", e.Error)
+ }
+
+ // ... check compatibility of used messages
+ ch, err := conn.NewAPIChannel()
+ if err != nil {
+ fmt.Println("creating channel failed: ", err)
+ }
+ if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
+ fmt.Println("compatibility error: ", err)
+ }
+ if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
+ fmt.Println("compatibility error: ", err)
+ }
+ vpp.apiChannel = ch
return nil
}
-func (vpp *VppInstance) vppctl(command string) (string, error) {
- cliExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
- vpp.container.name, vpp.getCliSocket(), command)
- output, err := exechelper.CombinedOutput(cliExecCommand)
+func (vpp *VppInstance) vppctl(command string, arguments ...any) (string, error) {
+ vppCliCommand := fmt.Sprintf(command, arguments...)
+ containerExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
+ vpp.container.name, vpp.getCliSocket(), vppCliCommand)
+ output, err := exechelper.CombinedOutput(containerExecCommand)
if err != nil {
return "", fmt.Errorf("vppctl failed: %s", err)
}
@@ -115,7 +199,7 @@ func (vpp *VppInstance) vppctl(command string) (string, error) {
}
func NewVppInstance(c *Container) *VppInstance {
- var vppConfig VppConfig
+ vppConfig := new(VppConfig)
vppConfig.CliSocketFilePath = defaultCliSocketFilePath
vpp := new(VppInstance)
vpp.container = c
@@ -123,7 +207,7 @@ func NewVppInstance(c *Container) *VppInstance {
return vpp
}
-func serializeVppConfig(vppConfig VppConfig) (string, error) {
+func serializeVppConfig(vppConfig *VppConfig) (string, error) {
serializedConfig, err := json.Marshal(vppConfig)
if err != nil {
return "", fmt.Errorf("vpp start failed: serializing configuration failed: %s", err)
@@ -142,3 +226,87 @@ func deserializeVppConfig(input string) (VppConfig, error) {
}
return vppConfig, nil
}
+
+func (vpp *VppInstance) createAfPacket(
+ veth *NetworkInterfaceVeth,
+) (interface_types.InterfaceIndex, error) {
+ createReq := &af_packet.AfPacketCreateV2{
+ UseRandomHwAddr: true,
+ HostIfName: veth.Name(),
+ }
+ if veth.hwAddress != (MacAddress{}) {
+ createReq.UseRandomHwAddr = false
+ createReq.HwAddr = veth.hwAddress
+ }
+ createReply := &af_packet.AfPacketCreateV2Reply{}
+
+ if err := vpp.apiChannel.SendRequest(createReq).ReceiveReply(createReply); err != nil {
+ return 0, err
+ }
+ veth.index = createReply.SwIfIndex
+
+ // Set to up
+ upReq := &interfaces.SwInterfaceSetFlags{
+ SwIfIndex: veth.index,
+ Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
+ }
+ upReply := &interfaces.SwInterfaceSetFlagsReply{}
+
+ if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
+ return 0, err
+ }
+
+ // Add address
+ if veth.ip4Address == (AddressWithPrefix{}) {
+ ipPrefix, err := vpp.container.suite.NewAddress()
+ if err != nil {
+ return 0, err
+ }
+ veth.ip4Address = ipPrefix
+ }
+ addressReq := &interfaces.SwInterfaceAddDelAddress{
+ IsAdd: true,
+ SwIfIndex: veth.index,
+ Prefix: veth.ip4Address,
+ }
+ addressReply := &interfaces.SwInterfaceAddDelAddressReply{}
+
+ if err := vpp.apiChannel.SendRequest(addressReq).ReceiveReply(addressReply); err != nil {
+ return 0, err
+ }
+
+ return veth.index, nil
+}
+
+func (vpp *VppInstance) addAppNamespace(
+ secret uint64,
+ ifx interface_types.InterfaceIndex,
+ namespaceId string,
+) error {
+ req := &session.AppNamespaceAddDelV2{
+ Secret: secret,
+ SwIfIndex: ifx,
+ NamespaceID: namespaceId,
+ }
+ reply := &session.AppNamespaceAddDelV2Reply{}
+
+ if err := vpp.apiChannel.SendRequest(req).ReceiveReply(reply); err != nil {
+ return err
+ }
+
+ sessionReq := &session.SessionEnableDisable{
+ IsEnable: true,
+ }
+ sessionReply := &session.SessionEnableDisableReply{}
+
+ if err := vpp.apiChannel.SendRequest(sessionReq).ReceiveReply(sessionReply); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (vpp *VppInstance) disconnect() {
+ vpp.connection.Disconnect()
+ vpp.apiChannel.Close()
+}