+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package ovn
import (
"encoding/json"
"fmt"
+ "github.com/vishvananda/netlink"
"math/big"
"math/rand"
"net"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
+ "strings"
"time"
)
var log = logf.Log.WithName("ovn")
+// CreateVlan creates VLAN with vlanID
+func CreateVlan(vlanID, interfaceName, logicalInterfaceName string) error {
+ if interfaceName == "" || vlanID == "" || logicalInterfaceName == "" {
+ return fmt.Errorf("CreateVlan invalid parameters: %v %v %v", interfaceName, vlanID, logicalInterfaceName)
+ }
+ _, err := netlink.LinkByName(logicalInterfaceName)
+ if err == nil {
+ return err
+ }
+ stdout, stderr, err := RunIP("link", "add", "link", interfaceName, "name", logicalInterfaceName, "type", "vlan", "id", vlanID)
+ if err != nil {
+ log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ stdout, stderr, err = RunIP("link", "set", logicalInterfaceName, "alias", "nfn-"+logicalInterfaceName)
+ if err != nil {
+ log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ stdout, stderr, err = RunIP("link", "set", "dev", logicalInterfaceName, "up")
+ if err != nil {
+ log.Error(err, "Failed to enable Vlan", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ return nil
+}
+
+// DeleteVlan deletes VLAN with logicalInterface Name
+func DeleteVlan(logicalInterfaceName string) error {
+ if logicalInterfaceName == "" {
+ return fmt.Errorf("DeleteVlan invalid parameters")
+ }
+ stdout, stderr, err := RunIP("link", "del", "dev", logicalInterfaceName)
+ if err != nil {
+ log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ return nil
+}
+
+// GetVlan returns a list of VLAN configured on the node
+func GetVlan() []string {
+ var intfList []string
+ links, err := netlink.LinkList()
+ if err != nil {
+
+ }
+ for _, l := range links {
+ if strings.Contains(l.Attrs().Alias, "nfn-") {
+ intfList = append(intfList, l.Attrs().Name)
+ }
+ }
+ return intfList
+}
+
+// CreatePnBridge creates Provider network bridge and mappings
+func CreatePnBridge(nwName, brName, intfName string) error {
+ if nwName == "" || brName == "" || intfName == "" {
+ return fmt.Errorf("CreatePnBridge invalid parameters")
+ }
+ // Create Bridge
+ stdout, stderr, err := RunOVSVsctl("--may-exist", "add-br", brName)
+ if err != nil {
+ log.Error(err, "Failed to create Bridge", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ stdout, stderr, err = RunOVSVsctl("--may-exist", "add-port", brName, intfName)
+ if err != nil {
+ log.Error(err, "Failed to add port to Bridge", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ stdout, stderr, err = RunOVSVsctl("set", "bridge", brName, "external_ids:nfn="+nwName)
+ if err != nil {
+ log.Error(err, "Failed to set nfn-alias", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ // Update ovn-bridge-mappings
+ updateOvnBridgeMapping(brName, nwName, "add")
+ return nil
+}
+
+// DeletePnBridge creates Provider network bridge and mappings
+func DeletePnBridge(nwName, brName string) error {
+ if nwName == "" || brName == "" {
+ return fmt.Errorf("DeletePnBridge invalid parameters")
+ }
+ // Delete Bridge
+ stdout, stderr, err := RunOVSVsctl("--if-exist", "del-br", brName)
+ if err != nil {
+ log.Error(err, "Failed to delete Bridge", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ updateOvnBridgeMapping(brName, nwName, "delete")
+
+ return nil
+}
+
+// GetPnBridge returns Provider networks with external ids
+func GetPnBridge(externalID string) []string {
+ if externalID == "" {
+ log.Error(fmt.Errorf("GetBridge invalid parameters"), "Invalid")
+ }
+ stdout, stderr, err := RunOVSVsctl("list-br")
+ if err != nil {
+ log.Error(err, "No bridges found", "stdout", stdout, "stderr", stderr)
+ return nil
+ }
+ brNames := strings.Split(stdout, "\n")
+ var brList []string
+ for _, name := range brNames {
+ stdout, stderr, err = RunOVSVsctl("get", "bridge", name, "external_ids:"+externalID)
+ if err != nil {
+ if !strings.Contains(stderr, "no key") {
+ log.Error(err, "Unknown error reading external_ids", "stdout", stdout, "stderr", stderr)
+ }
+ continue
+ }
+ if stdout == "" {
+ continue
+ } else {
+ brList = append(brList, name)
+ }
+ }
+ return brList
+}
+
+// Update ovn-bridge-mappings
+func updateOvnBridgeMapping(brName, nwName, action string) error {
+ stdout, stderr, err := RunOVSVsctl("get", "open", ".", "external-ids:ovn-bridge-mappings")
+ if err != nil {
+ if !strings.Contains(stderr, "no key") {
+ log.Error(err, "Failed to get ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ }
+ // Convert csv string to map
+ mm := make(map[string]string)
+ if len(stdout) > 0 {
+ am := strings.Split(stdout, ",")
+ for _, label := range am {
+ l := strings.Split(label, ":")
+ if len(l) == 0 {
+ log.Error(fmt.Errorf("Syntax error label: %v", label), "ovnBridgeMapping")
+ return nil
+ }
+ mm[strings.TrimSpace(l[0])] = strings.TrimSpace(l[1])
+ }
+ }
+ if action == "add" {
+ mm[nwName] = brName
+ } else if action == "delete" {
+ delete(mm, nwName)
+ if len(mm) == 0 {
+ // No mapping needed
+ stdout, stderr, err = RunOVSVsctl("remove", "open", ".", "external-ids", "ovn-bridge-mappings")
+ if err != nil {
+ log.Error(err, "Failed to remove ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ return nil
+ }
+ } else {
+ return fmt.Errorf("Invalid action %s", action)
+ }
+ var mapping string
+ for key, value := range mm {
+ mapping = mapping + fmt.Sprintf("%s:%s,", key, value)
+ }
+ // Remove trailing ,
+ mapping = mapping[:len(mapping)-1]
+ extIDMap := "external-ids:ovn-bridge-mappings=" + mapping
+
+ stdout, stderr, err = RunOVSVsctl("set", "open", ".", extIDMap)
+ if err != nil {
+ log.Error(err, "Failed to set ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
+ return err
+ }
+ return nil
+}
+
func parseOvnNetworkObject(ovnnetwork string) ([]map[string]interface{}, error) {
var ovnNet []map[string]interface{}
return nil
}
+// CreateNetwork in OVN controller
+func createOvnLS(name, subnet, gatewayIP, excludeIps string) (gatewayIPMask string, err error) {
+ var stdout, stderr string
+
+ output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
+ "--columns=name", "find", "logical_switch", "name="+name)
+ if err != nil {
+ log.Error(err, "Error in reading logical switch", "stderr", stderr)
+ return
+ }
+
+ if strings.Compare(name, output) == 0 {
+ log.V(1).Info("Logical Switch already exists, delete first to update/recreate", "name", name)
+ return "", fmt.Errorf("LS exists")
+ }
+
+ _, cidr, err := net.ParseCIDR(subnet)
+ if err != nil {
+ log.Error(err, "ovnNetwork '%s' invalid subnet CIDR", "name", name)
+ return
+
+ }
+ firstIP := NextIP(cidr.IP)
+ n, _ := cidr.Mask.Size()
+
+ var gwIP net.IP
+ if gatewayIP != "" {
+ gwIP, _, err = net.ParseCIDR(gatewayIP)
+ if err != nil {
+ // Check if this is a valid IP address
+ gwIP = net.ParseIP(gatewayIP)
+ }
+ }
+ // If no valid Gateway use the first IP address for GatewayIP
+ if gwIP == nil {
+ gatewayIPMask = fmt.Sprintf("%s/%d", firstIP.String(), n)
+ } else {
+ gatewayIPMask = fmt.Sprintf("%s/%d", gwIP.String(), n)
+ }
+
+ // Create a logical switch and set its subnet.
+ if excludeIps != "" {
+ 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)
+ } else {
+ stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "ls-add", name, "--", "set", "logical_switch", name, "other-config:subnet="+subnet, "external-ids:gateway_ip="+gatewayIPMask)
+ }
+ if err != nil {
+ log.Error(err, "Failed to create a logical switch", "name", name, "stdout", stdout, "stderr", stderr)
+ return
+ }
+ return
+}
+
// generateMac generates mac address.
func generateMac() string {
prefix := "00:00:00"
func intToIP(i *big.Int) net.IP {
return net.IP(i.Bytes())
}
+
+// Get Subnet for a logical bridge
+func GetNetworkSubnet(nw string) (string, error) {
+ stdout, stderr, err := RunOVNNbctl("--if-exists",
+ "get", "logical_switch", nw,
+ "other_config:subnet")
+ if err != nil {
+ log.Error(err, "Failed to subnet for network", "stderr", stderr, "stdout", stdout)
+ return "", err
+ }
+ return stdout, nil
+}
+
+func GetIPAdressForPod(nw string, name string) (string, error) {
+ _, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
+ "--columns=name", "find", "logical_switch", "name="+nw)
+ if err != nil {
+ log.Error(err, "Error in obtaining list of logical switch", "stderr", stderr)
+ return "", err
+ }
+ stdout, stderr, err := RunOVNNbctl("lsp-list", nw)
+ if err != nil {
+ log.Error(err, "Failed to list ports", "stderr", stderr, "stdout", stdout)
+ return "", err
+ }
+ // stdout format
+ // <port-uuid> (<port-name>)
+ // <port-uuid> (<port-name>)
+ // ...
+ ll := strings.Split(stdout, "\n")
+ if len(ll) == 0 {
+ return "", fmt.Errorf("IPAdress Not Found")
+ }
+ for _, l := range ll {
+ pn := strings.Fields(l)
+ if len(pn) < 2 {
+ return "", fmt.Errorf("IPAdress Not Found")
+ }
+ if strings.Contains(pn[1], name) {
+ // Found Port
+ s := strings.Replace(pn[1], "(", "", -1)
+ s = strings.Replace(s, ")", "", -1)
+ dna, stderr, err := RunOVNNbctl("get", "logical_switch_port", s, "dynamic_addresses")
+ if err != nil {
+ log.Error(err, "Failed to get dynamic_addresses", "stderr", stderr, "stdout", dna)
+ return "", err
+ }
+ // format - mac:ip
+ ipAddr := strings.Fields(dna)
+ if len(ipAddr) < 2 {
+ return "", fmt.Errorf("IPAdress Not Found")
+ }
+ return ipAddr[1], nil
+ }
+ }
+ return "", fmt.Errorf("IPAdress Not Found %s", name)
+}
+