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