aboutsummaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
authorRitu Sood <ritu.sood@intel.com>2018-11-10 09:56:52 -0800
committerVictor Morales <victor.morales@intel.com>2018-11-20 01:50:58 -0800
commit5026d1d89b05eac5e004279b742df6745a73d93a (patch)
tree8f9aed1e476706e008b746debda6d616bd0ac7a5 /cmd
parent9506ae48eb545d502cc3685a99862740d28e7afb (diff)
Seed code for the Plugin
The code includes ovn4nfvk8s Plugin & CNI. It implements multiple OVN interfaces for Pods and assumes Multus (or similar CNI) calls its CNI not as first CNI. Change-Id: I524c1d18752eb6dbc8d34addd3b60d5bbaa06ff4 Signed-off-by: Ritu Sood <ritu.sood@intel.com> Signed-off-by: Victor Morales <victor.morales@intel.com>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/ovn4nfvk8s-cni/.gitkeep0
-rw-r--r--cmd/ovn4nfvk8s-cni/app/helper_linux.go160
-rw-r--r--cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni.go290
-rw-r--r--cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni_test.go56
-rw-r--r--cmd/ovn4nfvk8s/.gitkeep0
-rw-r--r--cmd/ovn4nfvk8s/ovn4nfvk8s.go132
6 files changed, 638 insertions, 0 deletions
diff --git a/cmd/ovn4nfvk8s-cni/.gitkeep b/cmd/ovn4nfvk8s-cni/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/cmd/ovn4nfvk8s-cni/.gitkeep
+++ /dev/null
diff --git a/cmd/ovn4nfvk8s-cni/app/helper_linux.go b/cmd/ovn4nfvk8s-cni/app/helper_linux.go
new file mode 100644
index 0000000..1a98a61
--- /dev/null
+++ b/cmd/ovn4nfvk8s-cni/app/helper_linux.go
@@ -0,0 +1,160 @@
+// +build linux
+
+package app
+
+import (
+ "fmt"
+ "net"
+ "os/exec"
+ "regexp"
+ "strings"
+
+ "github.com/sirupsen/logrus"
+
+ "github.com/containernetworking/cni/pkg/skel"
+ "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/containernetworking/plugins/pkg/ip"
+ "github.com/containernetworking/plugins/pkg/ns"
+ "github.com/vishvananda/netlink"
+)
+
+func renameLink(curName, newName string) error {
+ link, err := netlink.LinkByName(curName)
+ if err != nil {
+ return err
+ }
+
+ if err := netlink.LinkSetDown(link); err != nil {
+ return err
+ }
+ if err := netlink.LinkSetName(link, newName); err != nil {
+ return err
+ }
+ if err := netlink.LinkSetUp(link); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func setupInterface(netns ns.NetNS, containerID, ifName, macAddress, ipAddress, gatewayIP, defaultGateway string, mtu int) (*current.Interface, *current.Interface, error) {
+ hostIface := &current.Interface{}
+ contIface := &current.Interface{}
+
+ var oldHostVethName string
+ err := netns.Do(func(hostNS ns.NetNS) error {
+ // create the veth pair in the container and move host end into host netns
+ hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)
+ if err != nil {
+ return fmt.Errorf("failed to setup veth %s: %v", ifName, err)
+ //return err
+ }
+ hostIface.Mac = hostVeth.HardwareAddr.String()
+ contIface.Name = containerVeth.Name
+
+ link, err := netlink.LinkByName(contIface.Name)
+ if err != nil {
+ return fmt.Errorf("failed to lookup %s: %v", contIface.Name, err)
+ }
+
+ hwAddr, err := net.ParseMAC(macAddress)
+ if err != nil {
+ return fmt.Errorf("failed to parse mac address for %s: %v", contIface.Name, err)
+ }
+ err = netlink.LinkSetHardwareAddr(link, hwAddr)
+ if err != nil {
+ return fmt.Errorf("failed to add mac address %s to %s: %v", macAddress, contIface.Name, err)
+ }
+ contIface.Mac = macAddress
+ contIface.Sandbox = netns.Path()
+
+ addr, err := netlink.ParseAddr(ipAddress)
+ if err != nil {
+ return err
+ }
+ err = netlink.AddrAdd(link, addr)
+ if err != nil {
+ return fmt.Errorf("failed to add IP addr %s to %s: %v", ipAddress, contIface.Name, err)
+ }
+
+ if defaultGateway == "true" {
+ gw := net.ParseIP(gatewayIP)
+ if gw == nil {
+ return fmt.Errorf("parse ip of gateway failed")
+ }
+ err = ip.AddRoute(nil, gw, link)
+ if err != nil {
+ logrus.Errorf("ip.AddRoute failed %v gw %v link %v", err, gw, link)
+ return err
+ }
+ }
+ oldHostVethName = hostVeth.Name
+
+ return nil
+ })
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // rename the host end of veth pair
+ re := regexp.MustCompile("(\\d+)\\D*\\z")
+ index := re.FindAllString(ifName, -1)
+ hostIface.Name = containerID[:14] + index[0]
+ if err := renameLink(oldHostVethName, hostIface.Name); err != nil {
+ return nil, nil, fmt.Errorf("failed to rename %s to %s: %v", oldHostVethName, hostIface.Name, err)
+ }
+
+ return hostIface, contIface, nil
+}
+
+// ConfigureInterface sets up the container interface
+var ConfigureInterface = func(args *skel.CmdArgs, namespace, podName, macAddress, ipAddress, gatewayIP, interfaceName, defaultGateway string, mtu int) ([]*current.Interface, error) {
+ netns, err := ns.GetNS(args.Netns)
+ if err != nil {
+ return nil, fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
+ }
+ defer netns.Close()
+ hostIface, contIface, err := setupInterface(netns, args.ContainerID, interfaceName, macAddress, ipAddress, gatewayIP, defaultGateway, mtu)
+ if err != nil {
+ return nil, err
+ }
+ var ifaceID string
+ if interfaceName != "" {
+ ifaceID = fmt.Sprintf("%s_%s_%s", namespace, podName, interfaceName)
+ } else {
+ ifaceID = fmt.Sprintf("%s_%s", namespace, podName)
+ }
+
+ ovsArgs := []string{
+ "add-port", "br-int", hostIface.Name, "--", "set",
+ "interface", hostIface.Name,
+ fmt.Sprintf("external_ids:attached_mac=%s", macAddress),
+ fmt.Sprintf("external_ids:iface-id=%s", ifaceID),
+ fmt.Sprintf("external_ids:ip_address=%s", ipAddress),
+ fmt.Sprintf("external_ids:sandbox=%s", args.ContainerID),
+ }
+
+ var out []byte
+ out, err = exec.Command("ovs-vsctl", ovsArgs...).CombinedOutput()
+ if err != nil {
+ return nil, fmt.Errorf("failure in plugging pod interface: %v\n %q", err, string(out))
+ }
+
+ return []*current.Interface{hostIface, contIface}, nil
+}
+
+// PlatformSpecificCleanup deletes the OVS port
+func PlatformSpecificCleanup(ifaceName string) (bool, error) {
+ done := false
+ ovsArgs := []string{
+ "del-port", "br-int", ifaceName,
+ }
+ out, err := exec.Command("ovs-vsctl", ovsArgs...).CombinedOutput()
+ if err != nil && !strings.Contains(string(out), "no port named") {
+ // DEL should be idempotent; don't return an error just log it
+ logrus.Warningf("failed to delete OVS port %s: %v\n %q", ifaceName, err, string(out))
+ done = true
+ }
+
+ return done, nil
+}
diff --git a/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni.go b/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni.go
new file mode 100644
index 0000000..923363b
--- /dev/null
+++ b/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni.go
@@ -0,0 +1,290 @@
+// +build linux
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+
+ "github.com/containernetworking/cni/pkg/skel"
+ "github.com/containernetworking/cni/pkg/types"
+ "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/containernetworking/cni/pkg/version"
+ "k8s.io/apimachinery/pkg/util/wait"
+
+ kexec "k8s.io/utils/exec"
+ "ovn4nfv-k8s-plugin/internal/pkg/kube"
+
+ "ovn4nfv-k8s-plugin/cmd/ovn4nfvk8s-cni/app"
+ "ovn4nfv-k8s-plugin/internal/pkg/config"
+)
+
+func argString2Map(args string) (map[string]string, error) {
+ argsMap := make(map[string]string)
+
+ pairs := strings.Split(args, ";")
+ for _, pair := range pairs {
+ kv := strings.Split(pair, "=")
+ if len(kv) != 2 {
+ return nil, fmt.Errorf("ARGS: invalid pair %q", pair)
+ }
+ keyString := kv[0]
+ valueString := kv[1]
+ argsMap[keyString] = valueString
+ }
+
+ return argsMap, nil
+}
+
+func parseOvnNetworkObject(ovnnetwork string) ([]map[string]string, error) {
+ var ovnNet []map[string]string
+
+ if ovnnetwork == "" {
+ return nil, fmt.Errorf("parseOvnNetworkObject:error")
+ }
+
+ if err := json.Unmarshal([]byte(ovnnetwork), &ovnNet); err != nil {
+ return nil, fmt.Errorf("parseOvnNetworkObject: failed to load ovn network err: %v | ovn network: %v", err, ovnnetwork)
+ }
+
+ return ovnNet, nil
+}
+
+func mergeWithResult(srcObj, dstObj types.Result) (types.Result, error) {
+
+ if dstObj == nil {
+ return srcObj, nil
+ }
+ src, err := current.NewResultFromResult(srcObj)
+ if err != nil {
+ return nil, fmt.Errorf("Couldn't convert old result to current version: %v", err)
+ }
+ dst, err := current.NewResultFromResult(dstObj)
+ if err != nil {
+ return nil, fmt.Errorf("Couldn't convert old result to current version: %v", err)
+ }
+
+ ifacesLength := len(dst.Interfaces)
+
+ for _, iface := range src.Interfaces {
+ dst.Interfaces = append(dst.Interfaces, iface)
+ }
+ for _, ip := range src.IPs {
+ if ip.Interface != nil && *(ip.Interface) != -1 {
+ ip.Interface = current.Int(*(ip.Interface) + ifacesLength)
+ }
+ dst.IPs = append(dst.IPs, ip)
+ }
+ for _, route := range src.Routes {
+ dst.Routes = append(dst.Routes, route)
+ }
+
+ for _, ns := range src.DNS.Nameservers {
+ dst.DNS.Nameservers = append(dst.DNS.Nameservers, ns)
+ }
+ for _, s := range src.DNS.Search {
+ dst.DNS.Search = append(dst.DNS.Search, s)
+ }
+ for _, opt := range src.DNS.Options {
+ dst.DNS.Options = append(dst.DNS.Options, opt)
+ }
+ // TODO: what about DNS.domain?
+ return dst, nil
+}
+
+func prettyPrint(i interface{}) string {
+ s, _ := json.MarshalIndent(i, "", "\t")
+ return string(s)
+}
+
+func addMultipleInterfaces(args *skel.CmdArgs, ovnAnnotation, namespace, podName string) types.Result {
+ logrus.Infof("ovn4nfvk8s-cni: addMultipleInterfaces ")
+
+ var ovnAnnotatedMap []map[string]string
+ ovnAnnotatedMap, err := parseOvnNetworkObject(ovnAnnotation)
+ if err != nil {
+ logrus.Errorf("addLogicalPort : Error Parsing Ovn Network List %v", ovnAnnotatedMap)
+ return nil
+ }
+ if namespace == "" || podName == "" {
+ logrus.Errorf("required CNI variable missing")
+ return nil
+ }
+ var interfacesArray []*current.Interface
+ var index int
+ var result *current.Result
+ var dstResult types.Result
+ for _, ovnNet := range ovnAnnotatedMap {
+ ipAddress := ovnNet["ip_address"]
+ macAddress := ovnNet["mac_address"]
+ gatewayIP := ovnNet["gateway_ip"]
+ defaultGateway := ovnNet["defaultGateway"]
+
+ if ipAddress == "" || macAddress == "" || gatewayIP == "" {
+ logrus.Errorf("failed in pod annotation key extract")
+ return nil
+ }
+
+ index++
+ interfaceName := ovnNet["interface"]
+ if interfaceName == "" {
+ logrus.Errorf("addMultipleInterfaces: interface can't be null")
+ return nil
+ }
+ logrus.Debugf("addMultipleInterfaces: ipAddress %v %v", ipAddress, interfaceName)
+ interfacesArray, err = app.ConfigureInterface(args, namespace, podName, macAddress, ipAddress, gatewayIP, interfaceName, defaultGateway, config.Default.MTU)
+ if err != nil {
+ logrus.Errorf("Failed to configure interface in pod: %v", err)
+ return nil
+ }
+ addr, addrNet, err := net.ParseCIDR(ipAddress)
+ if err != nil {
+ logrus.Errorf("failed to parse IP address %q: %v", ipAddress, err)
+ return nil
+ }
+ ipVersion := "6"
+ if addr.To4() != nil {
+ ipVersion = "4"
+ }
+ var routes types.Route
+ if defaultGateway == "true" {
+ defaultAddr, defaultAddrNet, _ := net.ParseCIDR("0.0.0.0/0")
+ routes = types.Route{Dst: net.IPNet{IP: defaultAddr, Mask: defaultAddrNet.Mask}, GW: net.ParseIP(gatewayIP)}
+
+ result = &current.Result{
+ Interfaces: interfacesArray,
+ IPs: []*current.IPConfig{
+ {
+ Version: ipVersion,
+ Interface: current.Int(1),
+ Address: net.IPNet{IP: addr, Mask: addrNet.Mask},
+ Gateway: net.ParseIP(gatewayIP),
+ },
+ },
+ Routes: []*types.Route{&routes},
+ }
+ } else {
+ result = &current.Result{
+ Interfaces: interfacesArray,
+ IPs: []*current.IPConfig{
+ {
+ Version: ipVersion,
+ Interface: current.Int(1),
+ Address: net.IPNet{IP: addr, Mask: addrNet.Mask},
+ Gateway: net.ParseIP(gatewayIP),
+ },
+ },
+ }
+
+ }
+ // Build the result structure to pass back to the runtime
+ dstResult, err = mergeWithResult(types.Result(result), dstResult)
+ if err != nil {
+ logrus.Errorf("Failed to merge results: %v", err)
+ return nil
+ }
+ }
+ logrus.Infof("addMultipleInterfaces: %s", prettyPrint(dstResult))
+ return dstResult
+}
+
+func cmdAdd(args *skel.CmdArgs) error {
+ logrus.Infof("ovn4nfvk8s-cni: cmdAdd ")
+ conf := &types.NetConf{}
+ if err := json.Unmarshal(args.StdinData, conf); err != nil {
+ return fmt.Errorf("failed to load netconf: %v", err)
+ }
+
+ argsMap, err := argString2Map(args.Args)
+ if err != nil {
+ return err
+ }
+
+ namespace := argsMap["K8S_POD_NAMESPACE"]
+ podName := argsMap["K8S_POD_NAME"]
+ if namespace == "" || podName == "" {
+ return fmt.Errorf("required CNI variable missing")
+ }
+
+ clientset, err := config.NewClientset(&config.Kubernetes)
+ if err != nil {
+ return fmt.Errorf("Could not create clientset for kubernetes: %v", err)
+ }
+ kubecli := &kube.Kube{KClient: clientset}
+
+ // Get the IP address and MAC address from the API server.
+ var annotationBackoff = wait.Backoff{Duration: 1 * time.Second, Steps: 14, Factor: 1.5, Jitter: 0.1}
+ var annotation map[string]string
+ if err := wait.ExponentialBackoff(annotationBackoff, func() (bool, error) {
+ annotation, err = kubecli.GetAnnotationsOnPod(namespace, podName)
+ if err != nil {
+ // TODO: check if err is non recoverable
+ logrus.Warningf("Error while obtaining pod annotations - %v", err)
+ return false, nil
+ }
+ if _, ok := annotation["ovnIfaceList"]; ok {
+ return true, nil
+ }
+ return false, nil
+ }); err != nil {
+ return fmt.Errorf("failed to get pod annotation - %v", err)
+ }
+ logrus.Infof("ovn4nfvk8s-cni: Annotation Found ")
+ ovnAnnotation, ok := annotation["ovnIfaceList"]
+ if !ok {
+ return fmt.Errorf("Error while obtaining pod annotations")
+ }
+ result := addMultipleInterfaces(args, ovnAnnotation, namespace, podName)
+ return result.Print()
+}
+
+func cmdDel(args *skel.CmdArgs) error {
+ logrus.Infof("ovn4nfvk8s-cni: cmdDel ")
+ for i := 0; i < 10; i++ {
+ ifaceName := args.ContainerID[:14] + strconv.Itoa(i)
+ done, err := app.PlatformSpecificCleanup(ifaceName)
+ if err != nil {
+ logrus.Errorf("Teardown error: %v", err)
+ }
+ if done {
+ break
+ }
+ }
+ return nil
+}
+
+func main() {
+ logrus.Infof("ovn4nfvk8s-cni CNI Invoked by Multus")
+ c := cli.NewApp()
+ c.Name = "ovn4nfvk8s-cni"
+ c.Usage = "a CNI plugin to set up or tear down a additional interfaces with OVN"
+ c.Version = "0.0.2"
+ c.Flags = config.Flags
+
+ exec := kexec.New()
+ c.Action = func(ctx *cli.Context) error {
+ if _, err := config.InitConfig(ctx, exec, nil); err != nil {
+ return err
+ }
+
+ skel.PluginMain(cmdAdd, cmdDel, version.All)
+ return nil
+ }
+
+ if err := c.Run(os.Args); err != nil {
+ // Print the error to stdout in conformance with the CNI spec
+ e, ok := err.(*types.Error)
+ if !ok {
+ e = &types.Error{Code: 100, Msg: err.Error()}
+ }
+ e.Print()
+ }
+}
diff --git a/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni_test.go b/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni_test.go
new file mode 100644
index 0000000..d5b7b6b
--- /dev/null
+++ b/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni_test.go
@@ -0,0 +1,56 @@
+// +build linux
+
+package main
+
+import (
+ "github.com/containernetworking/cni/pkg/skel"
+ "github.com/containernetworking/cni/pkg/types/current"
+ "ovn4nfv-k8s-plugin/cmd/ovn4nfvk8s-cni/app"
+ "testing"
+)
+
+func TestAddMultipleInterfaces(t *testing.T) {
+ oldConfigureInterface := app.ConfigureInterface
+ // as we are exiting, revert ConfigureInterface back at end of function
+ defer func() { app.ConfigureInterface = oldConfigureInterface }()
+ app.ConfigureInterface = func(args *skel.CmdArgs, namespace, podName, macAddress, ipAddress, gatewayIP, interfaceName, defaultGateway string, mtu int) ([]*current.Interface, error) {
+ return []*current.Interface{
+ {
+ Name: "pod",
+ Mac: "0a:00:00:00:00:0c",
+ Sandbox: "102103104",
+ }}, nil
+ }
+ args := &skel.CmdArgs{"102103104", "default", "eth0", "", "", nil}
+
+ ovnAnnotation := "[{\"ip_address\":\"172.16.24.2/24\", \"mac_address\":\"0a:00:00:00:00:0c\", \"gateway_ip\": \"172.16.24.1\",\"interface\":\"net0\"}] "
+ result := addMultipleInterfaces(args, ovnAnnotation, "default", "pod")
+ if result == nil {
+ t.Errorf("Failed addMultipleInterfaces %+v", ovnAnnotation)
+ }
+ ovnAnnotation = "[{\"ip_address\":\"172.16.24.2/24\", \"mac_address\":\"0a:00:00:00:00:0c\", \"gateway_ip\": \"172.16.24.1\",\"defaultGateway\":\"true\",\"interface\":\"net0\"}] "
+ result = addMultipleInterfaces(args, ovnAnnotation, "default", "pod")
+ if result == nil {
+ t.Errorf("Failed addMultipleInterfaces %+v", ovnAnnotation)
+ }
+ ovnAnnotation = "[{\"ip_address\":\"172.16.24.2/24\", \"mac_address\":\"0a:00:00:00:00:0c\", \"gateway_ip\": \"172.16.24.1\"}] "
+ result = addMultipleInterfaces(args, ovnAnnotation, "default", "pod")
+ if result != nil {
+ t.Errorf("Failed addMultipleInterfaces %+v", ovnAnnotation)
+ }
+ ovnAnnotation = "[{\"mac_address\":\"0a:00:00:00:00:0c\", \"gateway_ip\": \"172.16.24.1\",\"interface\":\"net0\"}] "
+ result = addMultipleInterfaces(args, ovnAnnotation, "default", "pod")
+ if result != nil {
+ t.Errorf("Failed addMultipleInterfaces %+v", ovnAnnotation)
+ }
+ ovnAnnotation = "[{\"ip_address\":\"172.16.24.2/24\", \"mac_address\":\"0a:00:00:00:00:0c\", \"gateway_ip\": \"172.16.24.1\",\"interface\":\"net0\"}, {\"ip_address\":\"172.16.25.2/24\", \"mac_address\":\"0a:00:00:00:00:0d\", \"gateway_ip\": \"172.16.25.1\",\"interface\":\"net1\"}]"
+ result = addMultipleInterfaces(args, ovnAnnotation, "default", "pod")
+ if result == nil {
+ t.Errorf("Failed addMultipleInterfaces %+v", ovnAnnotation)
+ }
+ ovnAnnotation = "[{\"ip_address\":\"172.16.24.2/24\", \"mac_address\":\"0a:00:00:00:00:0c\", \"gateway_ip\": \"172.16.24.1\",\"interface\":\"net0\", \"defaultGateway\":\"true\"}, {\"ip_address\":\"172.16.25.2/24\", \"mac_address\":\"0a:00:00:00:00:0d\", \"gateway_ip\": \"172.16.25.1\",\"interface\":\"net1\"}]"
+ result = addMultipleInterfaces(args, ovnAnnotation, "default", "pod")
+ if result == nil {
+ t.Errorf("Failed addMultipleInterfaces %+v", ovnAnnotation)
+ }
+}
diff --git a/cmd/ovn4nfvk8s/.gitkeep b/cmd/ovn4nfvk8s/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/cmd/ovn4nfvk8s/.gitkeep
+++ /dev/null
diff --git a/cmd/ovn4nfvk8s/ovn4nfvk8s.go b/cmd/ovn4nfvk8s/ovn4nfvk8s.go
new file mode 100644
index 0000000..d097558
--- /dev/null
+++ b/cmd/ovn4nfvk8s/ovn4nfvk8s.go
@@ -0,0 +1,132 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+
+ kexec "k8s.io/utils/exec"
+
+ "ovn4nfv-k8s-plugin/internal/pkg/config"
+ "ovn4nfv-k8s-plugin/internal/pkg/factory"
+ "ovn4nfv-k8s-plugin/internal/pkg/ovn"
+ "ovn4nfv-k8s-plugin/internal/pkg/util"
+)
+
+func main() {
+ c := cli.NewApp()
+ c.Name = "ovn4nfvk8s"
+ c.Usage = "run ovn4nfvk8s to start pod watchers"
+ c.Version = config.Version
+ c.Flags = append([]cli.Flag{
+ // Daemon file
+ cli.StringFlag{
+ Name: "pidfile",
+ Usage: "Name of file that will hold the ovn4nfvk8s pid (optional)",
+ },
+ }, config.Flags...)
+ c.Action = func(c *cli.Context) error {
+ return runOvnKube(c)
+ }
+
+ if err := c.Run(os.Args); err != nil {
+ logrus.Fatal(err)
+ }
+}
+
+func delPidfile(pidfile string) {
+ if pidfile != "" {
+ if _, err := os.Stat(pidfile); err == nil {
+ if err := os.Remove(pidfile); err != nil {
+ logrus.Errorf("%s delete failed: %v", pidfile, err)
+ }
+ }
+ }
+}
+
+func runOvnKube(ctx *cli.Context) error {
+ fmt.Println("ovn4nfvk8s started")
+ exec := kexec.New()
+ _, err := config.InitConfig(ctx, exec, nil)
+ if err != nil {
+ return err
+ }
+ pidfile := ctx.String("pidfile")
+
+ c := make(chan os.Signal, 2)
+ signal.Notify(c, os.Interrupt, syscall.SIGTERM)
+ go func() {
+ <-c
+ delPidfile(pidfile)
+ os.Exit(1)
+ }()
+
+ defer delPidfile(pidfile)
+
+ if pidfile != "" {
+ // need to test if already there
+ _, err := os.Stat(pidfile)
+
+ // Create if it doesn't exist, else exit with error
+ if os.IsNotExist(err) {
+ if err := ioutil.WriteFile(pidfile, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
+ logrus.Errorf("failed to write pidfile %s (%v). Ignoring..", pidfile, err)
+ }
+ } else {
+ // get the pid and see if it exists
+ pid, err := ioutil.ReadFile(pidfile)
+ if err != nil {
+ logrus.Errorf("pidfile %s exists but can't be read", pidfile)
+ return err
+ }
+ _, err1 := os.Stat("/proc/" + string(pid[:]) + "/cmdline")
+ if os.IsNotExist(err1) {
+ // Left over pid from dead process
+ if err := ioutil.WriteFile(pidfile, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
+ logrus.Errorf("failed to write pidfile %s (%v). Ignoring..", pidfile, err)
+ }
+ } else {
+ logrus.Errorf("pidfile %s exists and ovn4nfvk8s is running", pidfile)
+ os.Exit(1)
+ }
+ }
+ }
+
+ if err = util.SetExec(exec); err != nil {
+ logrus.Errorf("Failed to initialize exec helper: %v", err)
+ return err
+ }
+
+ clientset, err := config.NewClientset(&config.Kubernetes)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ // Create distributed router and gateway for the deployment
+ err = ovn.SetupMaster("ovn4nfv-master")
+ if err != nil {
+ logrus.Errorf(err.Error())
+ panic(err.Error())
+ }
+ // create factory and start the ovn controller
+ stopChan := make(chan struct{})
+ factory, err := factory.NewWatchFactory(clientset, stopChan)
+ if err != nil {
+ panic(err.Error)
+ }
+
+ ovnController := ovn.NewOvnController(clientset, factory)
+ if err := ovnController.Run(); err != nil {
+ logrus.Errorf(err.Error())
+ panic(err.Error())
+ }
+ // run forever
+ select {}
+
+ return nil
+}