Merge "Set vlan interface up"
[ovn4nfv-k8s-plugin.git] / internal / pkg / ovn / common.go
1 package ovn
2
3 import (
4         "encoding/json"
5         "fmt"
6         "github.com/vishvananda/netlink"
7         "math/big"
8         "math/rand"
9         "net"
10         logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
11         "strings"
12         "time"
13 )
14
15 var log = logf.Log.WithName("ovn")
16
17 // CreateVlan creates VLAN with vlanID
18 func CreateVlan(vlanID, interfaceName, logicalInterfaceName string) error {
19         if interfaceName == "" || vlanID == "" || logicalInterfaceName == "" {
20                 return fmt.Errorf("CreateVlan invalid parameters: %v %v %v", interfaceName, vlanID, logicalInterfaceName)
21         }
22         _, err := netlink.LinkByName(logicalInterfaceName)
23         if err == nil {
24                 return err
25         }
26         stdout, stderr, err := RunIP("link", "add", "link", interfaceName, "name", logicalInterfaceName, "type", "vlan", "id", vlanID)
27         if err != nil {
28                 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
29                 return err
30         }
31         stdout, stderr, err = RunIP("link", "set", logicalInterfaceName, "alias", "nfn-"+logicalInterfaceName)
32         if err != nil {
33                 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
34                 return err
35         }
36         stdout, stderr, err = RunIP("link", "set", "dev", logicalInterfaceName, "up")
37         if err != nil {
38                 log.Error(err, "Failed to enable Vlan", "stdout", stdout, "stderr", stderr)
39                 return err
40         }
41         return nil
42 }
43
44 // DeleteVlan deletes VLAN with logicalInterface Name
45 func DeleteVlan(logicalInterfaceName string) error {
46         if logicalInterfaceName == "" {
47                 return fmt.Errorf("DeleteVlan invalid parameters")
48         }
49         stdout, stderr, err := RunIP("link", "del", "dev", logicalInterfaceName)
50         if err != nil {
51                 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
52                 return err
53         }
54         return nil
55 }
56
57 // GetVlan returns a list of VLAN configured on the node
58 func GetVlan() []string {
59         var intfList []string
60         links, err := netlink.LinkList()
61         if err != nil {
62
63         }
64         for _, l := range links {
65                 if strings.Contains(l.Attrs().Alias, "nfn-") {
66                         intfList = append(intfList, l.Attrs().Name)
67                 }
68         }
69         return intfList
70 }
71
72 // CreatePnBridge creates Provider network bridge and mappings
73 func CreatePnBridge(nwName, brName, intfName string) error {
74         if nwName == "" || brName == "" || intfName == "" {
75                 return fmt.Errorf("CreatePnBridge invalid parameters")
76         }
77         // Create Bridge
78         stdout, stderr, err := RunOVSVsctl("--may-exist", "add-br", brName)
79         if err != nil {
80                 log.Error(err, "Failed to create Bridge", "stdout", stdout, "stderr", stderr)
81                 return err
82         }
83         stdout, stderr, err = RunOVSVsctl("--may-exist", "add-port", brName, intfName)
84         if err != nil {
85                 log.Error(err, "Failed to add port to Bridge", "stdout", stdout, "stderr", stderr)
86                 return err
87         }
88         stdout, stderr, err = RunOVSVsctl("set", "bridge", brName, "external_ids:nfn="+nwName)
89         if err != nil {
90                 log.Error(err, "Failed to set nfn-alias", "stdout", stdout, "stderr", stderr)
91                 return err
92         }
93         // Update ovn-bridge-mappings
94         updateOvnBridgeMapping(brName, nwName, "add")
95         return nil
96 }
97
98 // DeletePnBridge creates Provider network bridge and mappings
99 func DeletePnBridge(nwName, brName string) error {
100         if nwName == "" || brName == "" {
101                 return fmt.Errorf("DeletePnBridge invalid parameters")
102         }
103         // Delete Bridge
104         stdout, stderr, err := RunOVSVsctl("--if-exist", "del-br", brName)
105         if err != nil {
106                 log.Error(err, "Failed to delete Bridge", "stdout", stdout, "stderr", stderr)
107                 return err
108         }
109         updateOvnBridgeMapping(brName, nwName, "delete")
110
111         return nil
112 }
113
114 // GetPnBridge returns Provider networks with external ids
115 func GetPnBridge(externalID string) []string {
116         if externalID == "" {
117                 log.Error(fmt.Errorf("GetBridge invalid parameters"), "Invalid")
118         }
119         stdout, stderr, err := RunOVSVsctl("list-br")
120         if err != nil {
121                 log.Error(err, "No bridges found", "stdout", stdout, "stderr", stderr)
122                 return nil
123         }
124         brNames := strings.Split(stdout, "\n")
125         var brList []string
126         for _, name := range brNames {
127                 stdout, stderr, err = RunOVSVsctl("get", "bridge", name, "external_ids:"+externalID)
128                 if err != nil {
129                         if !strings.Contains(stderr, "no key") {
130                                 log.Error(err, "Unknown error reading external_ids", "stdout", stdout, "stderr", stderr)
131                         }
132                         continue
133                 }
134                 if stdout == "" {
135                         continue
136                 } else {
137                         brList = append(brList, name)
138                 }
139         }
140         return brList
141 }
142
143 // Update ovn-bridge-mappings
144 func updateOvnBridgeMapping(brName, nwName, action string) error {
145         stdout, stderr, err := RunOVSVsctl("get", "open", ".", "external-ids:ovn-bridge-mappings")
146         if err != nil {
147                 if !strings.Contains(stderr, "no key") {
148                         log.Error(err, "Failed to get ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
149                         return err
150                 }
151         }
152         // Convert csv string to map
153         mm := make(map[string]string)
154         if len(stdout) > 0 {
155                 am := strings.Split(stdout, ",")
156                 for _, label := range am {
157                         l := strings.Split(label, ":")
158                         if len(l) == 0 {
159                                 log.Error(fmt.Errorf("Syntax error label: %v", label), "ovnBridgeMapping")
160                                 return nil
161                         }
162                         mm[strings.TrimSpace(l[0])] = strings.TrimSpace(l[1])
163                 }
164         }
165         if action == "add" {
166                 mm[nwName] = brName
167         } else if action == "delete" {
168                 delete(mm, nwName)
169                 if len(mm) == 0 {
170                         // No mapping needed
171                         stdout, stderr, err = RunOVSVsctl("remove", "open", ".", "external-ids", "ovn-bridge-mappings")
172                         if err != nil {
173                                 log.Error(err, "Failed to remove ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
174                                 return err
175                         }
176                         return nil
177                 }
178         } else {
179                 return fmt.Errorf("Invalid action %s", action)
180         }
181         var mapping string
182         for key, value := range mm {
183                 mapping = mapping + fmt.Sprintf("%s:%s,", key, value)
184         }
185         // Remove trailing ,
186         mapping = mapping[:len(mapping)-1]
187         extIDMap := "external-ids:ovn-bridge-mappings=" + mapping
188
189         stdout, stderr, err = RunOVSVsctl("set", "open", ".", extIDMap)
190         if err != nil {
191                 log.Error(err, "Failed to set ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
192                 return err
193         }
194         return nil
195 }
196
197 func parseOvnNetworkObject(ovnnetwork string) ([]map[string]interface{}, error) {
198         var ovnNet []map[string]interface{}
199
200         if ovnnetwork == "" {
201                 return nil, fmt.Errorf("parseOvnNetworkObject:error")
202         }
203
204         if err := json.Unmarshal([]byte(ovnnetwork), &ovnNet); err != nil {
205                 return nil, fmt.Errorf("parseOvnNetworkObject: failed to load ovn network err: %v | ovn network: %v", err, ovnnetwork)
206         }
207
208         return ovnNet, nil
209 }
210
211 func setupDistributedRouter(name string) error {
212
213         // Create a single common distributed router for the cluster.
214         stdout, stderr, err := RunOVNNbctl("--", "--may-exist", "lr-add", name, "--", "set", "logical_router", name, "external_ids:ovn4nfv-cluster-router=yes")
215         if err != nil {
216                 log.Error(err, "Failed to create a single common distributed router for the cluster", "stdout", stdout, "stderr", stderr)
217                 return err
218         }
219         // Create a logical switch called "ovn4nfv-join" that will be used to connect gateway routers to the distributed router.
220         // The "ovn4nfv-join" will be allocated IP addresses in the range 100.64.1.0/24.
221         stdout, stderr, err = RunOVNNbctl("--may-exist", "ls-add", "ovn4nfv-join")
222         if err != nil {
223                 log.Error(err, "Failed to create logical switch called \"ovn4nfv-join\"", "stdout", stdout, "stderr", stderr)
224                 return err
225         }
226         // Connect the distributed router to "ovn4nfv-join".
227         routerMac, stderr, err := RunOVNNbctl("--if-exist", "get", "logical_router_port", "rtoj-"+name, "mac")
228         if err != nil {
229                 log.Error(err, "Failed to get logical router port rtoj-", "name", name, "stdout", stdout, "stderr", stderr)
230                 return err
231         }
232         if routerMac == "" {
233                 routerMac = generateMac()
234                 stdout, stderr, err = RunOVNNbctl("--", "--may-exist", "lrp-add", name, "rtoj-"+name, routerMac, "100.64.1.1/24", "--", "set", "logical_router_port", "rtoj-"+name, "external_ids:connect_to_ovn4nfvjoin=yes")
235                 if err != nil {
236                         log.Error(err, "Failed to add logical router port rtoj", "name", name, "stdout", stdout, "stderr", stderr)
237                         return err
238                 }
239         }
240         // Connect the switch "ovn4nfv-join" to the router.
241         stdout, stderr, err = RunOVNNbctl("--", "--may-exist", "lsp-add", "ovn4nfv-join", "jtor-"+name, "--", "set", "logical_switch_port", "jtor-"+name, "type=router", "options:router-port=rtoj-"+name, "addresses="+"\""+routerMac+"\"")
242         if err != nil {
243                 log.Error(err, "Failed to add logical switch port to logical router", "stdout", stdout, "stderr", stderr)
244                 return err
245         }
246         return nil
247 }
248
249 // generateMac generates mac address.
250 func generateMac() string {
251         prefix := "00:00:00"
252         newRand := rand.New(rand.NewSource(time.Now().UnixNano()))
253         mac := fmt.Sprintf("%s:%02x:%02x:%02x", prefix, newRand.Intn(255), newRand.Intn(255), newRand.Intn(255))
254         return mac
255 }
256
257 // NextIP returns IP incremented by 1
258 func NextIP(ip net.IP) net.IP {
259         i := ipToInt(ip)
260         return intToIP(i.Add(i, big.NewInt(1)))
261 }
262
263 func ipToInt(ip net.IP) *big.Int {
264         if v := ip.To4(); v != nil {
265                 return big.NewInt(0).SetBytes(v)
266         }
267         return big.NewInt(0).SetBytes(ip.To16())
268 }
269
270 func intToIP(i *big.Int) net.IP {
271         return net.IP(i.Bytes())
272 }