diff --git a/pkg/cnidel/cniconfs.go b/pkg/cnidel/cniconfs.go index 4b8fa561..ee1b8302 100644 --- a/pkg/cnidel/cniconfs.go +++ b/pkg/cnidel/cniconfs.go @@ -33,12 +33,25 @@ var ( //This function creates CNI configuration for all static-level backends //The CNI binary matching with NetowrkType is invoked with the CNI config file matching with NetworkID parameter -func readCniConfigFile(cniconfDir string, netInfo *danmtypes.DanmNet) ([]byte, error) { +func readCniConfigFile(cniconfDir string, netInfo *danmtypes.DanmNet, ipamOptions datastructs.IpamConfig) ([]byte, error) { cniConfig := netInfo.Spec.NetworkID rawConfig, err := ioutil.ReadFile(cniconfDir + "/" + cniConfig + ".conf") if err != nil { return nil, errors.New("Could not load CNI config file: " + cniConfig +".conf for plugin:" + netInfo.Spec.NetworkType + " from directory:" + cniconfDir) } + //Only overwrite "ipam" of the static CNI config if user wants + if len(ipamOptions.Ips) > 0 { + genericCniConf := map[string]interface{}{} + err = json.Unmarshal(rawConfig, &genericCniConf) + if err != nil { + return nil, errors.New("could not Unmarshal CNI config file:" + cniConfig + ".conf for plugin: " + netInfo.Spec.NetworkType + ", because:" + err.Error()) + } + ipamRaw,_ := json.Marshal(ipamOptions) + ipamInGenericFormat := map[string]interface{}{} + json.Unmarshal(ipamRaw, &ipamInGenericFormat) + genericCniConf["ipam"] = ipamInGenericFormat + rawConfig,_ = json.Marshal(genericCniConf) + } return rawConfig, nil } @@ -78,7 +91,9 @@ func getMacvlanCniConfig(netInfo *danmtypes.DanmNet, ipamOptions datastructs.Ipa macvlanConfig.Master = danmep.DetermineHostDeviceName(netInfo) macvlanConfig.Mode = "bridge" //TODO: make these params configurable if required macvlanConfig.MTU = 1500 - macvlanConfig.Ipam = ipamOptions + if len(ipamOptions.Ips) > 0 { + macvlanConfig.Ipam = ipamOptions + } rawConfig, err := json.Marshal(macvlanConfig) if err != nil { return nil, errors.New("Error putting together CNI config for MACVLAN plugin: " + err.Error()) diff --git a/pkg/cnidel/cnidel.go b/pkg/cnidel/cnidel.go index 56ee41bf..4d8849d0 100644 --- a/pkg/cnidel/cnidel.go +++ b/pkg/cnidel/cnidel.go @@ -3,6 +3,7 @@ package cnidel import ( "errors" "log" + "net" "os" "strconv" "strings" @@ -47,7 +48,7 @@ func DelegateInterfaceSetup(netConf *datastructs.NetConf, danmClient danmclients err error ipamOptions datastructs.IpamConfig ) - if isIpamNeeded(netInfo.Spec.NetworkType) { + if isIpamNeeded(netInfo, ep) { ep.Spec.Iface.Address, ep.Spec.Iface.AddressIPv6, _, err = ipam.Reserve(danmClient, *netInfo, ep.Spec.Iface.Address, ep.Spec.Iface.AddressIPv6) if err != nil { return nil, errors.New("IP address reservation failed for network:" + netInfo.ObjectMeta.Name + " with error:" + err.Error()) @@ -83,12 +84,16 @@ func DelegateInterfaceSetup(netConf *datastructs.NetConf, danmClient danmclients return cniResult, nil } -func isIpamNeeded(cniType string) bool { - if cni, ok := SupportedNativeCnis[strings.ToLower(cniType)]; ok { +func isIpamNeeded(netInfo *danmtypes.DanmNet, ep *danmtypes.DanmEp) bool { + if cni, ok := SupportedNativeCnis[strings.ToLower(netInfo.Spec.NetworkType)]; ok { return cni.ipamNeeded - } else { - return false } + //For static delegates we should only overwrite the original IPAM if both the network is L3, and IP was requested from the Pod + if (ep.Spec.Iface.Address != "" && netInfo.Spec.Options.Cidr != "" ) || + (ep.Spec.Iface.AddressIPv6 != "" && netInfo.Spec.Options.Net6 != "" ) { + return true + } + return false } func IsDeviceNeeded(cniType string) bool { @@ -101,9 +106,6 @@ func IsDeviceNeeded(cniType string) bool { func getCniIpamConfig(netinfo *danmtypes.DanmNet, ip4, ip6 string) (datastructs.IpamConfig, error) { var ipSlice = []datastructs.IpamIp{} - if ip4 == "" && ip6 == "" && netinfo.Spec.NetworkType != "sriov" { - return datastructs.IpamConfig{}, errors.New("unfortunetaly 3rd party CNI plugins usually don't support foregoing putting any IPs on an interface, so with heavy hearts but we need to fail this network delegation operation") - } if ip4 != "" { ipSlice = append(ipSlice, datastructs.IpamIp{ IpCidr: ip4, @@ -126,7 +128,7 @@ func getCniPluginConfig(netConf *datastructs.NetConf, netInfo *danmtypes.DanmNet if cni, ok := SupportedNativeCnis[strings.ToLower(netInfo.Spec.NetworkType)]; ok { return cni.readConfig(netInfo, ipamOptions, ep, cni.CNIVersion) } else { - return readCniConfigFile(netConf.CniConfigDir, netInfo) + return readCniConfigFile(netConf.CniConfigDir, netInfo, ipamOptions) } } @@ -212,7 +214,11 @@ func freeDelegatedIp(danmClient danmclientset.Interface, netInfo *danmtypes.Danm if netInfo.Spec.NetworkType == "flannel" && ip != ""{ flannelIpExhaustionWorkaround(ip) } - if isIpamNeeded(netInfo.Spec.NetworkType) && ip != "" { + _, v4Subnet, _ := net.ParseCIDR(netInfo.Spec.Options.Cidr) + _, v6Subnet, _ := net.ParseCIDR(netInfo.Spec.Options.Net6) + parsedIp := net.ParseIP(ip) + //We only need to Free an IP if it was allocated by DANM IPAM, and it was allocated by DANM only if it falls into any of the defined subnets + if parsedIp != nil && ((v4Subnet != nil && v4Subnet.Contains(parsedIp)) || (v6Subnet != nil && v6Subnet.Contains(parsedIp))) { err := ipam.Free(danmClient, *netInfo, ip) if err != nil { return errors.New("cannot give back ip address: " + ip + " for network:" + netInfo.ObjectMeta.Name + diff --git a/test/uts/cnidel_test/cnidel_test.go b/test/uts/cnidel_test/cnidel_test.go index cdc6d7d6..d2d92292 100644 --- a/test/uts/cnidel_test/cnidel_test.go +++ b/test/uts/cnidel_test/cnidel_test.go @@ -107,7 +107,7 @@ var expectedCniConfigs = []CniConf { {"sriov-l3", []byte(`{"cniexp":{"cnitype":"sriov","ip":"192.168.1.65/26","env":{"CNI_COMMAND":"ADD","CNI_IFNAME":"eth0"}},"cniconf":{"cniVersion":"0.3.1","name":"sriov-test","type":"sriov","master":"enp175s0f1","l2enable":false,"vlan":500,"deviceID":"0000:af:06.0","ipam":{"type":"fakeipam","ips":[{"ipcidr":"192.168.1.65/26","version":4}]}}}`)}, {"sriov-l2", []byte(`{"cniexp":{"cnitype":"sriov","env":{"CNI_COMMAND":"ADD","CNI_IFNAME":"eth0"}},"cniconf":{"cniVersion":"0.3.1","name":"sriov-test","type":"sriov","master":"enp175s0f1","l2enable":true,"vlan":500,"deviceID":"0000:af:06.0"}}`)}, {"deleteflannel", []byte(`{"cniexp":{"cnitype":"flannel","env":{"CNI_COMMAND":"DEL","CNI_IFNAME":"eth0"}},"cniconf":{"name":"cbr0","type":"flannel","delegate":{"hairpinMode":true,"isDefaultGateway":true}}}`)}, - {"deletemacvlan", []byte(`{"cniexp":{"cnitype":"macvlan","env":{"CNI_COMMAND":"DEL","CNI_IFNAME":"ens1f0"}},"cniconf":{"cniVersion":"0.3.1","master":"ens1f0","mode":"bridge","mtu":1500,"ipam":{"type":"fakeipam"}}}`)}, + {"deletemacvlan", []byte(`{"cniexp":{"cnitype":"macvlan","env":{"CNI_COMMAND":"DEL","CNI_IFNAME":"ens1f0"}},"cniconf":{"cniVersion":"0.3.1","master":"ens1f0","mode":"bridge","mtu":1500}}`)}, } var testCniConfFiles = []CniConf {