forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 1
/
plugin.go
238 lines (202 loc) · 6.41 KB
/
plugin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
package plugin
import (
"fmt"
"os/exec"
"strconv"
"strings"
"github.com/golang/glog"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/apis/componentconfig"
kubeletTypes "k8s.io/kubernetes/pkg/kubelet/container"
knetwork "k8s.io/kubernetes/pkg/kubelet/network"
utilsets "k8s.io/kubernetes/pkg/util/sets"
)
const (
SingleTenantPluginName string = "redhat/openshift-ovs-subnet"
MultiTenantPluginName string = "redhat/openshift-ovs-multitenant"
IngressBandwidthAnnotation string = "kubernetes.io/ingress-bandwidth"
EgressBandwidthAnnotation string = "kubernetes.io/egress-bandwidth"
AssignMacVlanAnnotation string = "pod.network.openshift.io/assign-macvlan"
)
func IsOpenShiftNetworkPlugin(pluginName string) bool {
switch strings.ToLower(pluginName) {
case SingleTenantPluginName, MultiTenantPluginName:
return true
}
return false
}
func IsOpenShiftMultitenantNetworkPlugin(pluginName string) bool {
if strings.ToLower(pluginName) == MultiTenantPluginName {
return true
}
return false
}
//-----------------------------------------------
const (
setUpCmd = "setup"
tearDownCmd = "teardown"
statusCmd = "status"
updateCmd = "update"
)
func (plugin *OsdnNode) getExecutable() string {
return "openshift-sdn-ovs"
}
func (plugin *OsdnNode) Init(host knetwork.Host, _ componentconfig.HairpinMode, _ string, _ int) error {
return nil
}
func (plugin *OsdnNode) Name() string {
if plugin.multitenant {
return MultiTenantPluginName
} else {
return SingleTenantPluginName
}
}
func (plugin *OsdnNode) Capabilities() utilsets.Int {
return utilsets.NewInt(knetwork.NET_PLUGIN_CAPABILITY_SHAPING)
}
func (plugin *OsdnNode) getVNID(namespace string) (string, error) {
if plugin.multitenant {
vnid, err := plugin.vnids.WaitAndGetVNID(namespace)
if err != nil {
return "", err
}
return strconv.FormatUint(uint64(vnid), 10), nil
}
return "0", nil
}
var minRsrc = resource.MustParse("1k")
var maxRsrc = resource.MustParse("1P")
func parseAndValidateBandwidth(value string) (int64, error) {
rsrc, err := resource.ParseQuantity(value)
if err != nil {
return -1, err
}
if rsrc.Value() < minRsrc.Value() {
return -1, fmt.Errorf("resource value %d is unreasonably small (< %d)", rsrc.Value(), minRsrc.Value())
}
if rsrc.Value() > maxRsrc.Value() {
return -1, fmt.Errorf("resource value %d is unreasonably large (> %d)", rsrc.Value(), maxRsrc.Value())
}
return rsrc.Value(), nil
}
func extractBandwidthResources(pod *kapi.Pod) (ingress, egress int64, err error) {
str, found := pod.Annotations[IngressBandwidthAnnotation]
if found {
ingress, err = parseAndValidateBandwidth(str)
if err != nil {
return -1, -1, err
}
}
str, found = pod.Annotations[EgressBandwidthAnnotation]
if found {
egress, err = parseAndValidateBandwidth(str)
if err != nil {
return -1, -1, err
}
}
return ingress, egress, nil
}
func wantsMacvlan(pod *kapi.Pod) (bool, error) {
val, found := pod.Annotations[AssignMacVlanAnnotation]
if !found || val != "true" {
return false, nil
}
for _, container := range pod.Spec.Containers {
if container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {
return true, nil
}
}
return false, fmt.Errorf("Pod has %q annotation but is not privileged", AssignMacVlanAnnotation)
}
func isScriptError(err error) bool {
_, ok := err.(*exec.ExitError)
return ok
}
// Get the last command (which is prefixed with "+" because of "set -x") and its output
// (Unless the script ended with "echo ...; exit", in which case we just return the
// echoed text.)
func getScriptError(output []byte) string {
lines := strings.Split(string(output), "\n")
last := len(lines)
for n := last - 1; n >= 0; n-- {
if strings.HasPrefix(lines[n], "+ exit") {
last = n
} else if strings.HasPrefix(lines[n], "+ echo") {
return strings.Join(lines[n+1:last], "\n")
} else if strings.HasPrefix(lines[n], "+") {
return strings.Join(lines[n:], "\n")
}
}
return string(output)
}
func (plugin *OsdnNode) SetUpPod(namespace string, name string, id kubeletTypes.ContainerID) error {
err := plugin.WaitForPodNetworkReady()
if err != nil {
return err
}
pod, err := plugin.registry.GetPod(plugin.hostName, namespace, name)
if err != nil {
return err
}
if pod == nil {
return fmt.Errorf("failed to retrieve pod %s/%s", namespace, name)
}
ingress, egress, err := extractBandwidthResources(pod)
if err != nil {
return fmt.Errorf("failed to parse pod %s/%s ingress/egress quantity: %v", namespace, name, err)
}
var ingressStr, egressStr string
if ingress > 0 {
ingressStr = fmt.Sprintf("%d", ingress)
}
if egress > 0 {
egressStr = fmt.Sprintf("%d", egress)
}
vnidstr, err := plugin.getVNID(namespace)
if err != nil {
return err
}
macvlan, err := wantsMacvlan(pod)
if err != nil {
return err
}
out, err := exec.Command(plugin.getExecutable(), setUpCmd, id.ID, vnidstr, ingressStr, egressStr, fmt.Sprintf("%t", macvlan)).CombinedOutput()
glog.V(5).Infof("SetUpPod network plugin output: %s, %v", string(out), err)
if isScriptError(err) {
return fmt.Errorf("Error running network setup script: %s", getScriptError(out))
} else {
return err
}
}
func (plugin *OsdnNode) TearDownPod(namespace string, name string, id kubeletTypes.ContainerID) error {
// The script's teardown functionality doesn't need the VNID
out, err := exec.Command(plugin.getExecutable(), tearDownCmd, id.ID, "-1", "-1", "-1").CombinedOutput()
glog.V(5).Infof("TearDownPod network plugin output: %s, %v", string(out), err)
if isScriptError(err) {
return fmt.Errorf("Error running network teardown script: %s", getScriptError(out))
} else {
return err
}
}
func (plugin *OsdnNode) Status() error {
return nil
}
func (plugin *OsdnNode) GetPodNetworkStatus(namespace string, name string, podInfraContainerID kubeletTypes.ContainerID) (*knetwork.PodNetworkStatus, error) {
return nil, nil
}
func (plugin *OsdnNode) UpdatePod(namespace string, name string, id kubeletTypes.DockerID) error {
vnidstr, err := plugin.getVNID(namespace)
if err != nil {
return err
}
out, err := exec.Command(plugin.getExecutable(), updateCmd, string(id), vnidstr).CombinedOutput()
glog.V(5).Infof("UpdatePod network plugin output: %s, %v", string(out), err)
if isScriptError(err) {
return fmt.Errorf("Error running network update script: %s", getScriptError(out))
} else {
return err
}
}
func (plugin *OsdnNode) Event(name string, details map[string]interface{}) {
}