adding the sfc features
[ovn4nfv-k8s-plugin.git] / internal / pkg / utils / chain.go
1 /*
2  * Copyright 2020 Intel Corporation, Inc
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package nfn
18
19 import (
20         "context"
21         "fmt"
22         "ovn4nfv-k8s-plugin/internal/pkg/network"
23         "ovn4nfv-k8s-plugin/internal/pkg/ovn"
24         k8sv1alpha1 "ovn4nfv-k8s-plugin/pkg/apis/k8s/v1alpha1"
25         "strings"
26
27         pb "ovn4nfv-k8s-plugin/internal/pkg/nfnNotify/proto"
28
29         "github.com/containernetworking/plugins/pkg/ns"
30         "github.com/docker/docker/client"
31         v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32         "k8s.io/client-go/kubernetes"
33         "sigs.k8s.io/controller-runtime/pkg/client/config"
34         logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
35 )
36
37 var log = logf.Log.WithName("chaining")
38
39 type RoutingInfo struct {
40         Name                 string            // Name of the pod
41         Namespace            string            // Namespace of the Pod
42         Id                   string            // Container ID for pod
43         Node                 string            // Hostname where Pod is scheduled
44         LeftNetworkRoute     k8sv1alpha1.Route // TODO: Update to support multiple networks
45         RightNetworkRoute    k8sv1alpha1.Route // TODO: Update to support multiple networks
46         DynamicNetworkRoutes []k8sv1alpha1.Route
47 }
48
49 var chainRoutingInfo []RoutingInfo
50
51 // Calcuate route to get to left and right edge networks and other networks (not adjacent) in the chain
52 func calculateDeploymentRoutes(namespace, label string, pos int, num int, ln []k8sv1alpha1.RoutingNetwork, rn []k8sv1alpha1.RoutingNetwork, networkList, deploymentList []string) (r RoutingInfo, err error) {
53
54         var nextLeftIP string
55         var nextRightIP string
56
57         r.Namespace = namespace
58         // Get a config to talk to the apiserver
59         cfg, err := config.GetConfig()
60         if err != nil {
61                 return RoutingInfo{}, err
62         }
63         var k *kubernetes.Clientset
64         k, err = kubernetes.NewForConfig(cfg)
65         if err != nil {
66                 log.Error(err, "Error building Kuberenetes clientset")
67                 return RoutingInfo{}, err
68         }
69         lo := v1.ListOptions{LabelSelector: label}
70         // List the Pods matching the Labels
71         pods, err := k.CoreV1().Pods(namespace).List(lo)
72         if err != nil {
73                 log.Error(err, "Deloyment with label not found", "label", label)
74                 return RoutingInfo{}, err
75         }
76         // LOADBALANCER NOT YET SUPPORTED - Assuming deployment has only one Pod
77         if len(pods.Items) <= 0 {
78                 log.Error(err, "Deloyment with label not found", "label", label)
79                 return RoutingInfo{}, fmt.Errorf("Pod not found")
80         }
81         // Get the containerID of the first container
82         r.Id = strings.TrimPrefix(pods.Items[0].Status.ContainerStatuses[0].ContainerID, "docker://")
83         r.Name = pods.Items[0].GetName()
84         r.Node = pods.Items[0].Spec.NodeName
85         // Calcluate IP addresses for next neighbours on both sides
86         if pos == 0 {
87                 nextLeftIP = ln[0].GatewayIP
88         } else {
89                 name := strings.Split(deploymentList[pos-1], "=")
90                 nextLeftIP, err = ovn.GetIPAdressForPod(networkList[pos-1], name[1])
91                 if err != nil {
92                         return RoutingInfo{}, err
93                 }
94         }
95         if pos == num-1 {
96                 nextRightIP = rn[0].GatewayIP
97         } else {
98                 name := strings.Split(deploymentList[pos+1], "=")
99                 nextRightIP, err = ovn.GetIPAdressForPod(networkList[pos], name[1])
100                 if err != nil {
101                         return RoutingInfo{}, err
102                 }
103         }
104         // Calcuate left right Route to be inserted in Pod
105         r.LeftNetworkRoute.Dst = ln[0].Subnet
106         r.LeftNetworkRoute.GW = nextLeftIP
107         r.RightNetworkRoute.Dst = rn[0].Subnet
108         r.RightNetworkRoute.GW = nextRightIP
109         // For each network that is not adjacent add route
110         for i := 0; i < len(networkList); i++ {
111                 if i == pos || i == pos-1 {
112                         continue
113                 } else {
114                         var rt k8sv1alpha1.Route
115                         rt.Dst, err = ovn.GetNetworkSubnet(networkList[i])
116                         if err != nil {
117                                 return RoutingInfo{}, err
118                         }
119                         if i > pos {
120                                 rt.GW = nextRightIP
121                         } else {
122                                 rt.GW = nextLeftIP
123                         }
124                         r.DynamicNetworkRoutes = append(r.DynamicNetworkRoutes, rt)
125                 }
126         }
127
128         //Add Default Route based on Right Network
129         rt := k8sv1alpha1.Route{
130                 GW:  nextRightIP,
131                 Dst: "0.0.0.0",
132         }
133         r.DynamicNetworkRoutes = append(r.DynamicNetworkRoutes, rt)
134         return
135 }
136
137 func CalculateRoutes(cr *k8sv1alpha1.NetworkChaining) ([]RoutingInfo, error) {
138         //
139         var deploymentList []string
140         var networkList []string
141
142         // TODO: Add Validation of Input to this function
143         ln := cr.Spec.RoutingSpec.LeftNetwork
144         rn := cr.Spec.RoutingSpec.RightNetwork
145         chains := strings.Split(cr.Spec.RoutingSpec.NetworkChain, ",")
146         i := 0
147         for _, chain := range chains {
148                 if i%2 == 0 {
149                         deploymentList = append(deploymentList, chain)
150                 } else {
151                         networkList = append(networkList, chain)
152                 }
153                 i++
154         }
155         num := len(deploymentList)
156         log.Info("Display the num", "num", num)
157         log.Info("Display the ln", "ln", ln)
158         log.Info("Display the rn", "rn", rn)
159         log.Info("Display the networklist", "networkList", networkList)
160         log.Info("Display the deploymentlist", "deploymentList", deploymentList)
161         for i, deployment := range deploymentList {
162                 r, err := calculateDeploymentRoutes(cr.Namespace, deployment, i, num, ln, rn, networkList, deploymentList)
163                 if err != nil {
164                         return nil, err
165                 }
166                 chainRoutingInfo = append(chainRoutingInfo, r)
167         }
168         return chainRoutingInfo, nil
169 }
170
171 func ContainerAddRoute(containerPid int, route []*pb.RouteData) error {
172         str := fmt.Sprintf("/host/proc/%d/ns/net", containerPid)
173
174         hostNet, err := network.GetHostNetwork()
175         if err != nil {
176                 log.Error(err, "Failed to get host network")
177                 return err
178         }
179
180         nms, err := ns.GetNS(str)
181         if err != nil {
182                 log.Error(err, "Failed namesapce", "containerID", containerPid)
183                 return err
184         }
185         defer nms.Close()
186         err = nms.Do(func(_ ns.NetNS) error {
187                 podGW, err := network.GetDefaultGateway()
188                 if err != nil {
189                         log.Error(err, "Failed to get pod default gateway")
190                         return err
191                 }
192
193                 stdout, stderr, err := ovn.RunIP("route", "add", hostNet, "via", podGW)
194                 if err != nil && !strings.Contains(stderr, "RTNETLINK answers: File exists") {
195                         log.Error(err, "Failed to ip route add", "stdout", stdout, "stderr", stderr)
196                         return err
197                 }
198
199                 for _, r := range route {
200                         dst := r.GetDst()
201                         gw := r.GetGw()
202                         // Replace default route
203                         if dst == "0.0.0.0" {
204                                 stdout, stderr, err := ovn.RunIP("route", "replace", "default", "via", gw)
205                                 if err != nil {
206                                         log.Error(err, "Failed to ip route replace", "stdout", stdout, "stderr", stderr)
207                                         return err
208                                 }
209                         } else {
210                                 stdout, stderr, err := ovn.RunIP("route", "add", dst, "via", gw)
211                                 if err != nil && !strings.Contains(stderr, "RTNETLINK answers: File exists") {
212                                         log.Error(err, "Failed to ip route add", "stdout", stdout, "stderr", stderr)
213                                         return err
214                                 }
215                         }
216                 }
217                 return nil
218         })
219         if err != nil {
220                 log.Error(err, "Failed Netns Do", "containerID", containerPid)
221                 return err
222         }
223         return nil
224 }
225
226 func GetPidForContainer(id string) (int, error) {
227         cli, err := client.NewEnvClient()
228         if err != nil {
229                 fmt.Println("Unable to create docker client")
230                 return 0, err
231         }
232         cli.NegotiateAPIVersion(context.Background())
233         cj, err := cli.ContainerInspect(context.Background(), id)
234         if err != nil {
235                 fmt.Println("Unable to Inspect docker container")
236                 return 0, err
237         }
238         if cj.State.Pid == 0 {
239                 return 0, fmt.Errorf("Container not found %s", id)
240         }
241         return cj.State.Pid, nil
242
243 }