2 * Copyright 2020 Intel Corporation, Inc
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
22 "github.com/vishvananda/netlink"
26 logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
31 var log = logf.Log.WithName("ovn")
33 // CreateVlan creates VLAN with vlanID
34 func CreateVlan(vlanID, interfaceName, logicalInterfaceName string) error {
35 if interfaceName == "" || vlanID == "" || logicalInterfaceName == "" {
36 return fmt.Errorf("CreateVlan invalid parameters: %v %v %v", interfaceName, vlanID, logicalInterfaceName)
38 _, err := netlink.LinkByName(logicalInterfaceName)
42 stdout, stderr, err := RunIP("link", "add", "link", interfaceName, "name", logicalInterfaceName, "type", "vlan", "id", vlanID)
44 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
47 stdout, stderr, err = RunIP("link", "set", logicalInterfaceName, "alias", "nfn-"+logicalInterfaceName)
49 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
52 stdout, stderr, err = RunIP("link", "set", "dev", logicalInterfaceName, "up")
54 log.Error(err, "Failed to enable Vlan", "stdout", stdout, "stderr", stderr)
60 // DeleteVlan deletes VLAN with logicalInterface Name
61 func DeleteVlan(logicalInterfaceName string) error {
62 if logicalInterfaceName == "" {
63 return fmt.Errorf("DeleteVlan invalid parameters")
65 stdout, stderr, err := RunIP("link", "del", "dev", logicalInterfaceName)
67 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
73 // GetVlan returns a list of VLAN configured on the node
74 func GetVlan() []string {
76 links, err := netlink.LinkList()
80 for _, l := range links {
81 if strings.Contains(l.Attrs().Alias, "nfn-") {
82 intfList = append(intfList, l.Attrs().Name)
88 // CreatePnBridge creates Provider network bridge and mappings
89 func CreatePnBridge(nwName, brName, intfName string) error {
90 if nwName == "" || brName == "" || intfName == "" {
91 return fmt.Errorf("CreatePnBridge invalid parameters")
94 stdout, stderr, err := RunOVSVsctl("--may-exist", "add-br", brName)
96 log.Error(err, "Failed to create Bridge", "stdout", stdout, "stderr", stderr)
99 stdout, stderr, err = RunOVSVsctl("--may-exist", "add-port", brName, intfName)
101 log.Error(err, "Failed to add port to Bridge", "stdout", stdout, "stderr", stderr)
104 stdout, stderr, err = RunOVSVsctl("set", "bridge", brName, "external_ids:nfn="+nwName)
106 log.Error(err, "Failed to set nfn-alias", "stdout", stdout, "stderr", stderr)
109 // Update ovn-bridge-mappings
110 updateOvnBridgeMapping(brName, nwName, "add")
114 // DeletePnBridge creates Provider network bridge and mappings
115 func DeletePnBridge(nwName, brName string) error {
116 if nwName == "" || brName == "" {
117 return fmt.Errorf("DeletePnBridge invalid parameters")
120 stdout, stderr, err := RunOVSVsctl("--if-exist", "del-br", brName)
122 log.Error(err, "Failed to delete Bridge", "stdout", stdout, "stderr", stderr)
125 updateOvnBridgeMapping(brName, nwName, "delete")
130 // GetPnBridge returns Provider networks with external ids
131 func GetPnBridge(externalID string) []string {
132 if externalID == "" {
133 log.Error(fmt.Errorf("GetBridge invalid parameters"), "Invalid")
135 stdout, stderr, err := RunOVSVsctl("list-br")
137 log.Error(err, "No bridges found", "stdout", stdout, "stderr", stderr)
140 brNames := strings.Split(stdout, "\n")
142 for _, name := range brNames {
143 stdout, stderr, err = RunOVSVsctl("get", "bridge", name, "external_ids:"+externalID)
145 if !strings.Contains(stderr, "no key") {
146 log.Error(err, "Unknown error reading external_ids", "stdout", stdout, "stderr", stderr)
153 brList = append(brList, name)
159 // Update ovn-bridge-mappings
160 func updateOvnBridgeMapping(brName, nwName, action string) error {
161 stdout, stderr, err := RunOVSVsctl("get", "open", ".", "external-ids:ovn-bridge-mappings")
163 if !strings.Contains(stderr, "no key") {
164 log.Error(err, "Failed to get ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
168 // Convert csv string to map
169 mm := make(map[string]string)
171 am := strings.Split(stdout, ",")
172 for _, label := range am {
173 l := strings.Split(label, ":")
175 log.Error(fmt.Errorf("Syntax error label: %v", label), "ovnBridgeMapping")
178 mm[strings.TrimSpace(l[0])] = strings.TrimSpace(l[1])
183 } else if action == "delete" {
187 stdout, stderr, err = RunOVSVsctl("remove", "open", ".", "external-ids", "ovn-bridge-mappings")
189 log.Error(err, "Failed to remove ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
195 return fmt.Errorf("Invalid action %s", action)
198 for key, value := range mm {
199 mapping = mapping + fmt.Sprintf("%s:%s,", key, value)
202 mapping = mapping[:len(mapping)-1]
203 extIDMap := "external-ids:ovn-bridge-mappings=" + mapping
205 stdout, stderr, err = RunOVSVsctl("set", "open", ".", extIDMap)
207 log.Error(err, "Failed to set ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
213 func parseOvnNetworkObject(ovnnetwork string) ([]map[string]interface{}, error) {
214 var ovnNet []map[string]interface{}
216 if ovnnetwork == "" {
217 return nil, fmt.Errorf("parseOvnNetworkObject:error")
220 if err := json.Unmarshal([]byte(ovnnetwork), &ovnNet); err != nil {
221 return nil, fmt.Errorf("parseOvnNetworkObject: failed to load ovn network err: %v | ovn network: %v", err, ovnnetwork)
227 func setupDistributedRouter(name string) error {
229 // Create a single common distributed router for the cluster.
230 stdout, stderr, err := RunOVNNbctl("--", "--may-exist", "lr-add", name, "--", "set", "logical_router", name, "external_ids:ovn4nfv-cluster-router=yes")
232 log.Error(err, "Failed to create a single common distributed router for the cluster", "stdout", stdout, "stderr", stderr)
235 // Create a logical switch called "ovn4nfv-join" that will be used to connect gateway routers to the distributed router.
236 // The "ovn4nfv-join" will be allocated IP addresses in the range 100.64.1.0/24.
237 stdout, stderr, err = RunOVNNbctl("--may-exist", "ls-add", "ovn4nfv-join")
239 log.Error(err, "Failed to create logical switch called \"ovn4nfv-join\"", "stdout", stdout, "stderr", stderr)
242 // Connect the distributed router to "ovn4nfv-join".
243 routerMac, stderr, err := RunOVNNbctl("--if-exist", "get", "logical_router_port", "rtoj-"+name, "mac")
245 log.Error(err, "Failed to get logical router port rtoj-", "name", name, "stdout", stdout, "stderr", stderr)
249 routerMac = generateMac()
250 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")
252 log.Error(err, "Failed to add logical router port rtoj", "name", name, "stdout", stdout, "stderr", stderr)
256 // Connect the switch "ovn4nfv-join" to the router.
257 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+"\"")
259 log.Error(err, "Failed to add logical switch port to logical router", "stdout", stdout, "stderr", stderr)
265 // CreateNetwork in OVN controller
266 func createOvnLS(name, subnet, gatewayIP, excludeIps string) (gatewayIPMask string, err error) {
267 var stdout, stderr string
269 output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
270 "--columns=name", "find", "logical_switch", "name="+name)
272 log.Error(err, "Error in reading logical switch", "stderr", stderr)
276 if strings.Compare(name, output) == 0 {
277 log.V(1).Info("Logical Switch already exists, delete first to update/recreate", "name", name)
278 return "", fmt.Errorf("LS exists")
281 _, cidr, err := net.ParseCIDR(subnet)
283 log.Error(err, "ovnNetwork '%s' invalid subnet CIDR", "name", name)
287 firstIP := NextIP(cidr.IP)
288 n, _ := cidr.Mask.Size()
292 gwIP, _, err = net.ParseCIDR(gatewayIP)
294 // Check if this is a valid IP address
295 gwIP = net.ParseIP(gatewayIP)
298 // If no valid Gateway use the first IP address for GatewayIP
300 gatewayIPMask = fmt.Sprintf("%s/%d", firstIP.String(), n)
302 gatewayIPMask = fmt.Sprintf("%s/%d", gwIP.String(), n)
305 // Create a logical switch and set its subnet.
306 if excludeIps != "" {
307 stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "ls-add", name, "--", "set", "logical_switch", name, "other-config:subnet="+subnet, "external-ids:gateway_ip="+gatewayIPMask, "other-config:exclude_ips="+excludeIps)
309 stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "ls-add", name, "--", "set", "logical_switch", name, "other-config:subnet="+subnet, "external-ids:gateway_ip="+gatewayIPMask)
312 log.Error(err, "Failed to create a logical switch", "name", name, "stdout", stdout, "stderr", stderr)
318 // generateMac generates mac address.
319 func generateMac() string {
321 newRand := rand.New(rand.NewSource(time.Now().UnixNano()))
322 mac := fmt.Sprintf("%s:%02x:%02x:%02x", prefix, newRand.Intn(255), newRand.Intn(255), newRand.Intn(255))
326 // NextIP returns IP incremented by 1
327 func NextIP(ip net.IP) net.IP {
329 return intToIP(i.Add(i, big.NewInt(1)))
332 func ipToInt(ip net.IP) *big.Int {
333 if v := ip.To4(); v != nil {
334 return big.NewInt(0).SetBytes(v)
336 return big.NewInt(0).SetBytes(ip.To16())
339 func intToIP(i *big.Int) net.IP {
340 return net.IP(i.Bytes())
343 // Get Subnet for a logical bridge
344 func GetNetworkSubnet(nw string) (string, error) {
345 stdout, stderr, err := RunOVNNbctl("--if-exists",
346 "get", "logical_switch", nw,
347 "other_config:subnet")
349 log.Error(err, "Failed to subnet for network", "stderr", stderr, "stdout", stdout)
355 func GetIPAdressForPod(nw string, name string) (string, error) {
356 _, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
357 "--columns=name", "find", "logical_switch", "name="+nw)
359 log.Error(err, "Error in obtaining list of logical switch", "stderr", stderr)
362 stdout, stderr, err := RunOVNNbctl("lsp-list", nw)
364 log.Error(err, "Failed to list ports", "stderr", stderr, "stdout", stdout)
368 // <port-uuid> (<port-name>)
369 // <port-uuid> (<port-name>)
371 ll := strings.Split(stdout, "\n")
373 return "", fmt.Errorf("IPAdress Not Found")
375 for _, l := range ll {
376 pn := strings.Fields(l)
378 return "", fmt.Errorf("IPAdress Not Found")
380 if strings.Contains(pn[1], name) {
382 s := strings.Replace(pn[1], "(", "", -1)
383 s = strings.Replace(s, ")", "", -1)
384 dna, stderr, err := RunOVNNbctl("get", "logical_switch_port", s, "dynamic_addresses")
386 log.Error(err, "Failed to get dynamic_addresses", "stderr", stderr, "stdout", dna)
390 ipAddr := strings.Fields(dna)
392 return "", fmt.Errorf("IPAdress Not Found")
394 return ipAddr[1], nil
397 return "", fmt.Errorf("IPAdress Not Found %s", name)