X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=internal%2Fpkg%2Fovn%2Fcommon.go;h=0053e562bc8025639a138c9fe0ff9136f38a8d66;hb=refs%2Fchanges%2F56%2F70656%2F4;hp=09d770bb30548a52027375284aef759188824f60;hpb=a6c37bf8c9c1e9f5072bfc3e43b6ec0061ee2108;p=ovn4nfv-k8s-plugin.git diff --git a/internal/pkg/ovn/common.go b/internal/pkg/ovn/common.go index 09d770b..0053e56 100644 --- a/internal/pkg/ovn/common.go +++ b/internal/pkg/ovn/common.go @@ -1,17 +1,215 @@ +/* + * 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{} @@ -64,6 +262,59 @@ func setupDistributedRouter(name string) error { 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" @@ -88,3 +339,61 @@ func ipToInt(ip net.IP) *big.Int { 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 + // () + // () + // ... + 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) +} +