5 "k8s.io/apimachinery/pkg/util/wait"
13 "k8s.io/client-go/kubernetes"
14 "github.com/containernetworking/cni/pkg/types"
15 "github.com/containernetworking/cni/pkg/types/current"
16 "ovn4nfv-k8s-plugin/internal/pkg/kube"
17 "k8s.io/apimachinery/pkg/api/errors"
18 "ovn4nfv-k8s-plugin/internal/pkg/config"
19 "ovn4nfv-k8s-plugin/cmd/ovn4nfvk8s-cni/app"
23 ovn4nfvAnnotationTag = "k8s.plugin.opnfv.org/ovnInterfaces"
26 func parseOvnNetworkObject(ovnnetwork string) ([]map[string]string, error) {
27 var ovnNet []map[string]string
30 return nil, fmt.Errorf("parseOvnNetworkObject:error")
33 if err := json.Unmarshal([]byte(ovnnetwork), &ovnNet); err != nil {
34 return nil, fmt.Errorf("parseOvnNetworkObject: failed to load ovn network err: %v | ovn network: %v", err, ovnnetwork)
40 func mergeWithResult(srcObj, dstObj types.Result) (types.Result, error) {
45 src, err := current.NewResultFromResult(srcObj)
47 return nil, fmt.Errorf("Couldn't convert old result to current version: %v", err)
49 dst, err := current.NewResultFromResult(dstObj)
51 return nil, fmt.Errorf("Couldn't convert old result to current version: %v", err)
54 ifacesLength := len(dst.Interfaces)
56 for _, iface := range src.Interfaces {
57 dst.Interfaces = append(dst.Interfaces, iface)
59 for _, ip := range src.IPs {
60 if ip.Interface != nil && *(ip.Interface) != -1 {
61 ip.Interface = current.Int(*(ip.Interface) + ifacesLength)
63 dst.IPs = append(dst.IPs, ip)
65 for _, route := range src.Routes {
66 dst.Routes = append(dst.Routes, route)
69 for _, ns := range src.DNS.Nameservers {
70 dst.DNS.Nameservers = append(dst.DNS.Nameservers, ns)
72 for _, s := range src.DNS.Search {
73 dst.DNS.Search = append(dst.DNS.Search, s)
75 for _, opt := range src.DNS.Options {
76 dst.DNS.Options = append(dst.DNS.Options, opt)
78 // TODO: what about DNS.domain?
82 func prettyPrint(i interface{}) string {
83 s, _ := json.MarshalIndent(i, "", "\t")
87 func isNotFoundError(err error) bool {
88 statusErr, ok := err.(*errors.StatusError)
89 return ok && statusErr.Status().Code == http.StatusNotFound
92 func (cr *CNIServerRequest) addMultipleInterfaces(ovnAnnotation, namespace, podName string) types.Result {
93 klog.Infof("ovn4nfvk8s-cni: addMultipleInterfaces ")
94 var ovnAnnotatedMap []map[string]string
95 ovnAnnotatedMap, err := parseOvnNetworkObject(ovnAnnotation)
97 klog.Errorf("addLogicalPort : Error Parsing Ovn Network List %v %v", ovnAnnotatedMap, err)
100 if namespace == "" || podName == "" {
101 klog.Errorf("required CNI variable missing")
104 var interfacesArray []*current.Interface
106 var result *current.Result
107 var dstResult types.Result
108 for _, ovnNet := range ovnAnnotatedMap {
109 ipAddress := ovnNet["ip_address"]
110 macAddress := ovnNet["mac_address"]
111 gatewayIP := ovnNet["gateway_ip"]
112 defaultGateway := ovnNet["defaultGateway"]
114 if ipAddress == "" || macAddress == "" {
115 klog.Errorf("failed in pod annotation key extract")
120 interfaceName := ovnNet["interface"]
121 if interfaceName == "" {
122 klog.Errorf("addMultipleInterfaces: interface can't be null")
125 klog.Infof("addMultipleInterfaces: ipAddress %v %v", ipAddress, interfaceName)
126 interfacesArray, err = app.ConfigureInterface(cr.Netns, cr.SandboxID, cr.IfName, namespace, podName, macAddress, ipAddress, gatewayIP, interfaceName, defaultGateway, index, config.Default.MTU)
128 klog.Errorf("Failed to configure interface in pod: %v", err)
131 addr, addrNet, err := net.ParseCIDR(ipAddress)
133 klog.Errorf("failed to parse IP address %q: %v", ipAddress, err)
137 if addr.To4() != nil {
140 var routes types.Route
141 if defaultGateway == "true" {
142 defaultAddr, defaultAddrNet, _ := net.ParseCIDR("0.0.0.0/0")
143 routes = types.Route{Dst: net.IPNet{IP: defaultAddr, Mask: defaultAddrNet.Mask}, GW: net.ParseIP(gatewayIP)}
145 result = ¤t.Result{
146 Interfaces: interfacesArray,
147 IPs: []*current.IPConfig{
150 Interface: current.Int(1),
151 Address: net.IPNet{IP: addr, Mask: addrNet.Mask},
152 Gateway: net.ParseIP(gatewayIP),
155 Routes: []*types.Route{&routes},
158 result = ¤t.Result{
159 Interfaces: interfacesArray,
160 IPs: []*current.IPConfig{
163 Interface: current.Int(1),
164 Address: net.IPNet{IP: addr, Mask: addrNet.Mask},
165 Gateway: net.ParseIP(gatewayIP),
171 // Build the result structure to pass back to the runtime
172 dstResult, err = mergeWithResult(types.Result(result), dstResult)
174 klog.Errorf("Failed to merge results: %v", err)
178 klog.Infof("addMultipleInterfaces: results %s", prettyPrint(dstResult))
182 func (cr *CNIServerRequest) addRoutes(ovnAnnotation string, dstResult types.Result) types.Result {
183 klog.Infof("ovn4nfvk8s-cni: addRoutes ")
184 var ovnAnnotatedMap []map[string]string
185 ovnAnnotatedMap, err := parseOvnNetworkObject(ovnAnnotation)
187 klog.Errorf("addLogicalPort : Error Parsing Ovn Route List %v", err)
191 var result types.Result
192 var routes []*types.Route
193 for _, ovnNet := range ovnAnnotatedMap {
197 if dst == "" || gw == "" || dev == "" {
198 klog.Errorf("failed in pod annotation key extract")
201 err = app.ConfigureRoute(cr.Netns, dst, gw, dev)
203 klog.Errorf("Failed to configure interface in pod: %v", err)
206 dstAddr, dstAddrNet, _ := net.ParseCIDR(dst)
207 routes = append(routes, &types.Route{
208 Dst: net.IPNet{IP: dstAddr, Mask: dstAddrNet.Mask},
213 result = ¤t.Result{
216 // Build the result structure to pass back to the runtime
217 dstResult, err = mergeWithResult(result, dstResult)
219 klog.Errorf("Failed to merge results: %v", err)
222 klog.Infof("addRoutes: results %s", prettyPrint(dstResult))
226 func (cr *CNIServerRequest) cmdAdd(kclient kubernetes.Interface) ([]byte, error) {
227 klog.Infof("ovn4nfvk8s-cni: cmdAdd")
228 namespace := cr.PodNamespace
229 podname := cr.PodName
230 if namespace == "" || podname == "" {
231 return nil, fmt.Errorf("required CNI variable missing")
233 klog.Infof("ovn4nfvk8s-cni: cmdAdd for pod podname:%s and namespace:%s", podname, namespace)
234 kubecli := &kube.Kube{KClient: kclient}
235 // Get the IP address and MAC address from the API server.
236 var annotationBackoff = wait.Backoff{Duration: 1 * time.Second, Steps: 14, Factor: 1.5, Jitter: 0.1}
237 var annotation map[string]string
239 if err = wait.ExponentialBackoff(annotationBackoff, func() (bool, error) {
240 annotation, err = kubecli.GetAnnotationsOnPod(namespace, podname)
242 if isNotFoundError(err) {
243 return false, fmt.Errorf("Error - pod not found - %v", err)
245 klog.Infof("ovn4nfvk8s-cni: cmdAdd Warning - Error while obtaining pod annotations - %v", err)
248 if _, ok := annotation[ovn4nfvAnnotationTag]; ok {
253 return nil, fmt.Errorf("failed to get pod annotation - %v", err)
256 klog.Infof("ovn4nfvk8s-cni: cmdAdd Annotation Found ")
257 ovnAnnotation, ok := annotation[ovn4nfvAnnotationTag]
259 return nil, fmt.Errorf("Error while obtaining pod annotations")
261 result := cr.addMultipleInterfaces(ovnAnnotation, namespace, podname)
262 //Add Routes to the pod if annotation found for routes
263 ovnRouteAnnotation, ok := annotation["ovnNetworkRoutes"]
265 klog.Infof("ovn4nfvk8s-cni: ovnNetworkRoutes Annotation Found %+v", ovnRouteAnnotation)
266 result = cr.addRoutes(ovnRouteAnnotation, result)
270 klog.Errorf("result struct the ovn4nfv-k8s-plugin cniserver")
271 return nil, fmt.Errorf("result is nil from cni server response")
274 responseBytes, err := json.Marshal(result)
276 return nil, fmt.Errorf("failed to marshal pod request response: %v", err)
279 return responseBytes, nil
282 func (cr *CNIServerRequest) cmdDel() ([]byte, error) {
283 klog.Infof("cmdDel ")
284 for i := 0; i < 10; i++ {
285 ifaceName := cr.SandboxID[:14] + strconv.Itoa(i)
286 done, err := app.PlatformSpecificCleanup(ifaceName)
288 klog.Errorf("Teardown error: %v", err)