adding primary network features
[ovn4nfv-k8s-plugin.git] / internal / pkg / ovn / utils.go
1 package ovn
2
3 import (
4         "bytes"
5         "fmt"
6         kexec "k8s.io/utils/exec"
7         "os"
8         "reflect"
9         "strings"
10         "time"
11 )
12
13 const (
14         ovsCommandTimeout = 15
15         ovnNbctlCommand   = "ovn-nbctl"
16         ovsVsctlCommand   = "ovs-vsctl"
17         ipCommand         = "ip"
18 )
19
20 // Exec runs various OVN and OVS utilities
21 type execHelper struct {
22         exec      kexec.Interface
23         nbctlPath string
24         vsctlPath string
25         ipPath    string
26         hostIP    string
27         hostPort  string
28 }
29
30 var runner *execHelper
31
32 // SetupOvnUtils does internal OVN initialization
33 var SetupOvnUtils = func() error {
34         // Setup Distributed Router
35         err := setupDistributedRouter(ovn4nfvRouterName)
36         if err != nil {
37                 log.Error(err, "Failed to initialize OVN Distributed Router")
38                 return err
39         }
40
41         log.Info("OVN Network", "OVN Default NW", Ovn4nfvDefaultNw, "OVN Subnet", ovnConf.Subnet, "OVN Gateway IP", ovnConf.GatewayIP, "OVN ExcludeIPs", ovnConf.ExcludeIPs)
42         _, err = createOvnLS(Ovn4nfvDefaultNw, ovnConf.Subnet, ovnConf.GatewayIP, ovnConf.ExcludeIPs)
43         if err != nil && !reflect.DeepEqual(err, fmt.Errorf("LS exists")) {
44                 log.Error(err, "Failed to create ovn4nfvk8s default nw")
45                 return err
46         }
47         return nil
48 }
49
50 // SetExec validates executable paths and saves the given exec interface
51 // to be used for running various OVS and OVN utilites
52 func SetExec(exec kexec.Interface) error {
53         var err error
54
55         runner = &execHelper{exec: exec}
56         runner.nbctlPath, err = exec.LookPath(ovnNbctlCommand)
57         if err != nil {
58                 return err
59         }
60         runner.vsctlPath, err = exec.LookPath(ovsVsctlCommand)
61         if err != nil {
62                 return err
63         }
64         runner.ipPath, err = exec.LookPath(ipCommand)
65         if err != nil {
66                 return err
67         }
68         runner.hostIP = os.Getenv("HOST_IP")
69         // OVN Host Port
70         runner.hostPort = "6641"
71         log.Info("Host Port", "IP", runner.hostIP, "Port", runner.hostPort)
72
73         return nil
74 }
75
76 // Run the ovn-ctl command and retry if "Connection refused"
77 // poll waitng for service to become available
78 func runOVNretry(cmdPath string, args ...string) (*bytes.Buffer, *bytes.Buffer, error) {
79
80         retriesLeft := 200
81         for {
82                 stdout, stderr, err := run(cmdPath, args...)
83                 if err == nil {
84                         return stdout, stderr, err
85                 }
86                 // Connection refused
87                 // Master may not be up so keep trying
88                 if strings.Contains(stderr.String(), "Connection refused") {
89                         if retriesLeft == 0 {
90                                 return stdout, stderr, err
91                         }
92                         retriesLeft--
93                         time.Sleep(2 * time.Second)
94                 } else {
95                         // Some other problem for caller to handle
96                         return stdout, stderr, err
97                 }
98         }
99 }
100
101 func run(cmdPath string, args ...string) (*bytes.Buffer, *bytes.Buffer, error) {
102         stdout := &bytes.Buffer{}
103         stderr := &bytes.Buffer{}
104         cmd := runner.exec.Command(cmdPath, args...)
105         cmd.SetStdout(stdout)
106         cmd.SetStderr(stderr)
107         log.V(1).Info("exec:", "cmdPath", cmdPath, "args", strings.Join(args, " "))
108         err := cmd.Run()
109         if err != nil {
110                 log.Info("ovs", "Error:", err, "cmdPath", cmdPath, "args", strings.Join(args, " "), "stdout", stdout, "stderr", stderr)
111         } else {
112                 log.V(1).Info("output:", "stdout", stdout)
113         }
114         return stdout, stderr, err
115 }
116
117 // RunOVNNbctlWithTimeout runs command via ovn-nbctl with a specific timeout
118 func RunOVNNbctlWithTimeout(timeout int, args ...string) (string, string, error) {
119         var cmdArgs []string
120         if len(runner.hostIP) > 0 {
121                 cmdArgs = []string{
122                         fmt.Sprintf("--db=tcp:%s:%s", runner.hostIP, runner.hostPort),
123                 }
124         }
125         cmdArgs = append(cmdArgs, fmt.Sprintf("--timeout=%d", timeout))
126         cmdArgs = append(cmdArgs, args...)
127         stdout, stderr, err := runOVNretry(runner.nbctlPath, cmdArgs...)
128         return strings.Trim(strings.TrimSpace(stdout.String()), "\""), stderr.String(), err
129 }
130
131 // RunOVNNbctl runs a command via ovn-nbctl.
132 func RunOVNNbctl(args ...string) (string, string, error) {
133         return RunOVNNbctlWithTimeout(ovsCommandTimeout, args...)
134 }
135
136 // RunIP runs a command via the iproute2 "ip" utility
137 func RunIP(args ...string) (string, string, error) {
138         stdout, stderr, err := run(runner.ipPath, args...)
139         return strings.TrimSpace(stdout.String()), stderr.String(), err
140 }
141
142 // RunOVSVsctl runs a command via ovs-vsctl.
143 func RunOVSVsctl(args ...string) (string, string, error) {
144         cmdArgs := []string{fmt.Sprintf("--timeout=%d", ovsCommandTimeout)}
145         cmdArgs = append(cmdArgs, args...)
146         stdout, stderr, err := run(runner.vsctlPath, cmdArgs...)
147         return strings.Trim(strings.TrimSpace(stdout.String()), "\""), stderr.String(), err
148 }