d972c9d9b8ec33e7f3612b6601738191e0cbbe42
[vpp.git] / extras / hs-test / hst_suite.go
1 package main
2
3 import (
4         "flag"
5         "io/ioutil"
6         "os"
7         "time"
8
9         "github.com/edwarnicke/exechelper"
10         "github.com/stretchr/testify/assert"
11         "github.com/stretchr/testify/suite"
12         "gopkg.in/yaml.v3"
13 )
14
15 const (
16         DEFAULT_NETWORK_NUM int = 1
17 )
18
19 var isPersistent = flag.Bool("persist", false, "persists topology config")
20 var isVerbose = flag.Bool("verbose", false, "verbose test output")
21 var isUnconfiguring = flag.Bool("unconfigure", false, "remove topology")
22 var isVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
23 var nConfiguredCpus = flag.Int("cpus", 1, "number of CPUs assigned to vpp")
24
25 type HstSuite struct {
26         suite.Suite
27         containers       map[string]*Container
28         volumes          []string
29         netConfigs       []NetConfig
30         netInterfaces    map[string]*NetInterface
31         ip4AddrAllocator *Ip4AddressAllocator
32         testIds          map[string]string
33         cpuAllocator     *CpuAllocatorT
34         cpuContexts      []*CpuContext
35         cpuPerVpp        int
36 }
37
38 func (s *HstSuite) SetupSuite() {
39         var err error
40         s.cpuAllocator, err = CpuAllocator()
41         if err != nil {
42                 s.FailNow("failed to init cpu allocator: %v", err)
43         }
44         s.cpuPerVpp = *nConfiguredCpus
45 }
46
47 func (s *HstSuite) AllocateCpus() []int {
48         cpuCtx, err := s.cpuAllocator.Allocate(s.cpuPerVpp)
49         s.assertNil(err)
50         s.AddCpuContext(cpuCtx)
51         return cpuCtx.cpus
52 }
53
54 func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) {
55         s.cpuContexts = append(s.cpuContexts, cpuCtx)
56 }
57
58 func (s *HstSuite) TearDownSuite() {
59         s.unconfigureNetworkTopology()
60 }
61
62 func (s *HstSuite) TearDownTest() {
63         if *isPersistent {
64                 return
65         }
66         for _, c := range s.cpuContexts {
67                 c.Release()
68         }
69         s.resetContainers()
70         s.removeVolumes()
71 }
72
73 func (s *HstSuite) skipIfUnconfiguring() {
74         if *isUnconfiguring {
75                 s.skip("skipping to unconfigure")
76         }
77 }
78
79 func (s *HstSuite) SetupTest() {
80         s.skipIfUnconfiguring()
81         s.setupVolumes()
82         s.setupContainers()
83 }
84
85 func (s *HstSuite) setupVolumes() {
86         for _, volume := range s.volumes {
87                 cmd := "docker volume create --name=" + volume
88                 s.log(cmd)
89                 exechelper.Run(cmd)
90         }
91 }
92
93 func (s *HstSuite) setupContainers() {
94         for _, container := range s.containers {
95                 if !container.isOptional {
96                         container.run()
97                 }
98         }
99 }
100
101 func (s *HstSuite) hstFail() {
102         s.T().FailNow()
103 }
104
105 func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
106         if !assert.Nil(s.T(), object, msgAndArgs...) {
107                 s.hstFail()
108         }
109 }
110
111 func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
112         if !assert.NotNil(s.T(), object, msgAndArgs...) {
113                 s.hstFail()
114         }
115 }
116
117 func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
118         if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
119                 s.hstFail()
120         }
121 }
122
123 func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
124         if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
125                 s.hstFail()
126         }
127 }
128
129 func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
130         if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
131                 s.hstFail()
132         }
133 }
134
135 func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
136         if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
137                 s.hstFail()
138         }
139 }
140
141 func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) {
142         if !assert.NotEmpty(s.T(), object, msgAndArgs...) {
143                 s.hstFail()
144         }
145 }
146
147 func (s *HstSuite) log(args ...any) {
148         if *isVerbose {
149                 s.T().Helper()
150                 s.T().Log(args...)
151         }
152 }
153
154 func (s *HstSuite) skip(args ...any) {
155         s.log(args...)
156         s.T().SkipNow()
157 }
158
159 func (s *HstSuite) SkipIfMultiWorker(args ...any) {
160         if *nConfiguredCpus > 1 {
161                 s.skip("test case not supported with multiple vpp workers")
162         }
163 }
164
165 func (s *HstSuite) resetContainers() {
166         for _, container := range s.containers {
167                 container.stop()
168         }
169 }
170
171 func (s *HstSuite) removeVolumes() {
172         for _, volumeName := range s.volumes {
173                 cmd := "docker volume rm " + volumeName
174                 exechelper.Run(cmd)
175                 os.RemoveAll(volumeName)
176         }
177 }
178
179 func (s *HstSuite) getContainerByName(name string) *Container {
180         return s.containers[name]
181 }
182
183 /*
184  * Create a copy and return its address, so that individial tests which call this
185  * are not able to modify the original container and affect other tests by doing that
186  */
187 func (s *HstSuite) getTransientContainerByName(name string) *Container {
188         containerCopy := *s.containers[name]
189         return &containerCopy
190 }
191
192 func (s *HstSuite) loadContainerTopology(topologyName string) {
193         data, err := ioutil.ReadFile(containerTopologyDir + topologyName + ".yaml")
194         if err != nil {
195                 s.T().Fatalf("read error: %v", err)
196         }
197         var yamlTopo YamlTopology
198         err = yaml.Unmarshal(data, &yamlTopo)
199         if err != nil {
200                 s.T().Fatalf("unmarshal error: %v", err)
201         }
202
203         for _, elem := range yamlTopo.Volumes {
204                 volumeMap := elem["volume"].(VolumeConfig)
205                 hostDir := volumeMap["host-dir"].(string)
206                 s.volumes = append(s.volumes, hostDir)
207         }
208
209         s.containers = make(map[string]*Container)
210         for _, elem := range yamlTopo.Containers {
211                 newContainer, err := newContainer(elem)
212                 newContainer.suite = s
213                 if err != nil {
214                         s.T().Fatalf("container config error: %v", err)
215                 }
216                 s.containers[newContainer.name] = newContainer
217         }
218 }
219
220 func (s *HstSuite) loadNetworkTopology(topologyName string) {
221         data, err := ioutil.ReadFile(networkTopologyDir + topologyName + ".yaml")
222         if err != nil {
223                 s.T().Fatalf("read error: %v", err)
224         }
225         var yamlTopo YamlTopology
226         err = yaml.Unmarshal(data, &yamlTopo)
227         if err != nil {
228                 s.T().Fatalf("unmarshal error: %v", err)
229         }
230
231         s.ip4AddrAllocator = NewIp4AddressAllocator()
232         s.netInterfaces = make(map[string]*NetInterface)
233         for _, elem := range yamlTopo.Devices {
234                 switch elem["type"].(string) {
235                 case NetNs:
236                         {
237                                 if namespace, err := newNetNamespace(elem); err == nil {
238                                         s.netConfigs = append(s.netConfigs, &namespace)
239                                 } else {
240                                         s.T().Fatalf("network config error: %v", err)
241                                 }
242                         }
243                 case Veth, Tap:
244                         {
245                                 if netIf, err := newNetworkInterface(elem, s.ip4AddrAllocator); err == nil {
246                                         s.netConfigs = append(s.netConfigs, netIf)
247                                         s.netInterfaces[netIf.Name()] = netIf
248                                 } else {
249                                         s.T().Fatalf("network config error: %v", err)
250                                 }
251                         }
252                 case Bridge:
253                         {
254                                 if bridge, err := newBridge(elem); err == nil {
255                                         s.netConfigs = append(s.netConfigs, &bridge)
256                                 } else {
257                                         s.T().Fatalf("network config error: %v", err)
258                                 }
259                         }
260                 }
261         }
262 }
263
264 func (s *HstSuite) configureNetworkTopology(topologyName string) {
265         s.loadNetworkTopology(topologyName)
266
267         if *isUnconfiguring {
268                 return
269         }
270
271         for _, nc := range s.netConfigs {
272                 if err := nc.configure(); err != nil {
273                         s.T().Fatalf("network config error: %v", err)
274                 }
275         }
276 }
277
278 func (s *HstSuite) unconfigureNetworkTopology() {
279         if *isPersistent {
280                 return
281         }
282         for _, nc := range s.netConfigs {
283                 nc.unconfigure()
284         }
285 }
286
287 func (s *HstSuite) getTestId() string {
288         testName := s.T().Name()
289
290         if s.testIds == nil {
291                 s.testIds = map[string]string{}
292         }
293
294         if _, ok := s.testIds[testName]; !ok {
295                 s.testIds[testName] = time.Now().Format("2006-01-02_15-04-05")
296         }
297
298         return s.testIds[testName]
299 }