blob: 1fcffa42e0045a5ed2366fa465e460bf73ae2732 [file] [log] [blame]
Maros Ondrejickaffa3f602023-01-26 10:07:29 +01001package main
2
3import (
Filip Tehlar671cf512023-01-31 10:34:18 +01004 "flag"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +01005 "fmt"
6 "io/ioutil"
7 "os"
Maros Ondrejickaa2d52622023-02-24 11:26:39 +01008 "time"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +01009
10 "github.com/edwarnicke/exechelper"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/suite"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010013 "gopkg.in/yaml.v3"
14)
15
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010016const (
Filip Tehlar608d0062023-04-28 10:29:47 +020017 DEFAULT_NETWORK_NUM int = 1
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010018)
19
Maros Ondrejickae7625d02023-02-28 16:55:01 +010020var isPersistent = flag.Bool("persist", false, "persists topology config")
21var isVerbose = flag.Bool("verbose", false, "verbose test output")
22var isUnconfiguring = flag.Bool("unconfigure", false, "remove topology")
23var isVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
Filip Tehlar608d0062023-04-28 10:29:47 +020024var nConfiguredCpus = flag.Int("cpus", 1, "number of CPUs assigned to vpp")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010025
26type HstSuite struct {
27 suite.Suite
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010028 containers map[string]*Container
29 volumes []string
30 netConfigs []NetConfig
Maros Ondrejicka40cba402023-02-23 13:19:15 +010031 netInterfaces map[string]*NetInterface
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010032 addresser *Addresser
Maros Ondrejickaa2d52622023-02-24 11:26:39 +010033 testIds map[string]string
Filip Tehlar608d0062023-04-28 10:29:47 +020034 cpuAllocator *CpuAllocatorT
35 cpuContexts []*CpuContext
36 cpuPerVpp int
37}
38
39func (s *HstSuite) SetupSuite() {
40 var err error
41 s.cpuAllocator, err = CpuAllocator()
42 if err != nil {
43 s.FailNow("failed to init cpu allocator: %v", err)
44 }
45 s.cpuPerVpp = *nConfiguredCpus
46}
47
48func (s *HstSuite) AllocateCpus() []int {
49 cpuCtx, err := s.cpuAllocator.Allocate(s.cpuPerVpp)
50 s.assertNil(err)
51 s.AddCpuContext(cpuCtx)
52 return cpuCtx.cpus
53}
54
55func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) {
56 s.cpuContexts = append(s.cpuContexts, cpuCtx)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010057}
58
59func (s *HstSuite) TearDownSuite() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010060 s.unconfigureNetworkTopology()
61}
62
63func (s *HstSuite) TearDownTest() {
Maros Ondrejickae7625d02023-02-28 16:55:01 +010064 if *isPersistent {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010065 return
66 }
Filip Tehlar608d0062023-04-28 10:29:47 +020067 for _, c := range s.cpuContexts {
68 c.Release()
69 }
Maros Ondrejickae7625d02023-02-28 16:55:01 +010070 s.resetContainers()
71 s.removeVolumes()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010072}
73
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +010074func (s *HstSuite) skipIfUnconfiguring() {
Maros Ondrejickae7625d02023-02-28 16:55:01 +010075 if *isUnconfiguring {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +010076 s.skip("skipping to unconfigure")
77 }
78}
79
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010080func (s *HstSuite) SetupTest() {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +010081 s.skipIfUnconfiguring()
Maros Ondrejickae7625d02023-02-28 16:55:01 +010082 s.setupVolumes()
83 s.setupContainers()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010084}
85
Maros Ondrejickae7625d02023-02-28 16:55:01 +010086func (s *HstSuite) setupVolumes() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010087 for _, volume := range s.volumes {
88 cmd := "docker volume create --name=" + volume
89 s.log(cmd)
90 exechelper.Run(cmd)
91 }
92}
93
Maros Ondrejickae7625d02023-02-28 16:55:01 +010094func (s *HstSuite) setupContainers() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010095 for _, container := range s.containers {
Filip Tehlar608d0062023-04-28 10:29:47 +020096 if !container.isOptional {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010097 container.run()
98 }
99 }
100}
101
102func (s *HstSuite) hstFail() {
103 s.T().FailNow()
104}
105
106func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
107 if !assert.Nil(s.T(), object, msgAndArgs...) {
108 s.hstFail()
109 }
110}
111
112func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
113 if !assert.NotNil(s.T(), object, msgAndArgs...) {
114 s.hstFail()
115 }
116}
117
118func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
119 if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
120 s.hstFail()
121 }
122}
123
124func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
125 if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
126 s.hstFail()
127 }
128}
129
130func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
131 if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
132 s.hstFail()
133 }
134}
135
136func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
137 if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
138 s.hstFail()
139 }
140}
141
Maros Ondrejicka2ddb2fd2023-02-15 17:44:46 +0100142func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) {
143 if !assert.NotEmpty(s.T(), object, msgAndArgs...) {
144 s.hstFail()
145 }
146}
147
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100148func (s *HstSuite) log(args ...any) {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100149 if *isVerbose {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +0100150 s.T().Helper()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100151 s.T().Log(args...)
152 }
153}
154
155func (s *HstSuite) skip(args ...any) {
156 s.log(args...)
157 s.T().SkipNow()
158}
159
Filip Tehlar608d0062023-04-28 10:29:47 +0200160func (s *HstSuite) SkipIfMultiWorker(args ...any) {
161 if *nConfiguredCpus > 1 {
162 s.skip("test case not supported with multiple vpp workers")
163 }
164}
165
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100166func (s *HstSuite) resetContainers() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100167 for _, container := range s.containers {
168 container.stop()
169 }
170}
171
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100172func (s *HstSuite) removeVolumes() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100173 for _, volumeName := range s.volumes {
174 cmd := "docker volume rm " + volumeName
175 exechelper.Run(cmd)
176 os.RemoveAll(volumeName)
177 }
178}
179
180func (s *HstSuite) getContainerByName(name string) *Container {
181 return s.containers[name]
182}
183
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100184/*
185 * Create a copy and return its address, so that individial tests which call this
186 * are not able to modify the original container and affect other tests by doing that
187 */
188func (s *HstSuite) getTransientContainerByName(name string) *Container {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100189 containerCopy := *s.containers[name]
190 return &containerCopy
191}
192
193func (s *HstSuite) loadContainerTopology(topologyName string) {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100194 data, err := ioutil.ReadFile(containerTopologyDir + topologyName + ".yaml")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100195 if err != nil {
196 s.T().Fatalf("read error: %v", err)
197 }
198 var yamlTopo YamlTopology
199 err = yaml.Unmarshal(data, &yamlTopo)
200 if err != nil {
201 s.T().Fatalf("unmarshal error: %v", err)
202 }
203
204 for _, elem := range yamlTopo.Volumes {
205 volumeMap := elem["volume"].(VolumeConfig)
206 hostDir := volumeMap["host-dir"].(string)
207 s.volumes = append(s.volumes, hostDir)
208 }
209
210 s.containers = make(map[string]*Container)
211 for _, elem := range yamlTopo.Containers {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100212 newContainer, err := newContainer(elem)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100213 newContainer.suite = s
214 if err != nil {
215 s.T().Fatalf("container config error: %v", err)
216 }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100217 s.containers[newContainer.name] = newContainer
218 }
219}
220
221func (s *HstSuite) loadNetworkTopology(topologyName string) {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100222 data, err := ioutil.ReadFile(networkTopologyDir + topologyName + ".yaml")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100223 if err != nil {
224 s.T().Fatalf("read error: %v", err)
225 }
226 var yamlTopo YamlTopology
227 err = yaml.Unmarshal(data, &yamlTopo)
228 if err != nil {
229 s.T().Fatalf("unmarshal error: %v", err)
230 }
231
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100232 s.addresser = newAddresser(s)
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100233 s.netInterfaces = make(map[string]*NetInterface)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100234 for _, elem := range yamlTopo.Devices {
235 switch elem["type"].(string) {
236 case NetNs:
237 {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100238 if namespace, err := newNetNamespace(elem); err == nil {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100239 s.netConfigs = append(s.netConfigs, &namespace)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100240 } else {
241 s.T().Fatalf("network config error: %v", err)
242 }
243 }
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100244 case Veth, Tap:
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100245 {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100246 if netIf, err := newNetworkInterface(elem, s.addresser); err == nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100247 s.netConfigs = append(s.netConfigs, netIf)
248 s.netInterfaces[netIf.Name()] = netIf
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100249 } else {
250 s.T().Fatalf("network config error: %v", err)
251 }
252 }
253 case Bridge:
254 {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100255 if bridge, err := newBridge(elem); err == nil {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100256 s.netConfigs = append(s.netConfigs, &bridge)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100257 } else {
258 s.T().Fatalf("network config error: %v", err)
259 }
260 }
261 }
262 }
263}
264
265func (s *HstSuite) configureNetworkTopology(topologyName string) {
266 s.loadNetworkTopology(topologyName)
267
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100268 if *isUnconfiguring {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +0100269 return
270 }
271
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100272 for _, nc := range s.netConfigs {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100273 if err := nc.configure(); err != nil {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100274 s.T().Fatalf("network config error: %v", err)
275 }
276 }
277}
278
279func (s *HstSuite) unconfigureNetworkTopology() {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100280 if *isPersistent {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100281 return
282 }
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100283 for _, nc := range s.netConfigs {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100284 nc.unconfigure()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100285 }
286}
287
Maros Ondrejickaa2d52622023-02-24 11:26:39 +0100288func (s *HstSuite) getTestId() string {
289 testName := s.T().Name()
290
291 if s.testIds == nil {
292 s.testIds = map[string]string{}
293 }
294
295 if _, ok := s.testIds[testName]; !ok {
Filip Tehlar75776f02023-03-24 13:47:45 +0100296 s.testIds[testName] = time.Now().Format("2006-01-02_15-04-05")
Maros Ondrejickaa2d52622023-02-24 11:26:39 +0100297 }
298
299 return s.testIds[testName]
300}
301
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100302type AddressCounter = int
303
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100304type Addresser struct {
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100305 networks map[int]AddressCounter
306 suite *HstSuite
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100307}
308
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100309func (a *Addresser) addNetwork(networkNumber int) {
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100310 a.networks[networkNumber] = 1
311}
312
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100313func (a *Addresser) newIp4Address(inputNetworkNumber ...int) (string, error) {
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100314 var networkNumber int = 0
315 if len(inputNetworkNumber) > 0 {
316 networkNumber = inputNetworkNumber[0]
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100317 }
318
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100319 if _, ok := a.networks[networkNumber]; !ok {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100320 a.addNetwork(networkNumber)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100321 }
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100322
323 numberOfAddresses := a.networks[networkNumber]
324
325 if numberOfAddresses == 254 {
326 return "", fmt.Errorf("no available IPv4 addresses")
327 }
328
329 address := fmt.Sprintf("10.10.%v.%v/24", networkNumber, numberOfAddresses)
330 a.networks[networkNumber] = numberOfAddresses + 1
331
332 return address, nil
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100333}
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100334
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100335func newAddresser(suite *HstSuite) *Addresser {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100336 var addresser = new(Addresser)
337 addresser.suite = suite
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100338 addresser.networks = make(map[int]AddressCounter)
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100339 addresser.addNetwork(0)
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100340 return addresser
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100341}