-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathovn-k8-overlay.py
executable file
·340 lines (278 loc) · 10.3 KB
/
ovn-k8-overlay.py
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#!/usr/bin/python
import argparse
import ast
import atexit
import getpass
import json
import os
import re
import requests
import shlex
import subprocess
import sys
import time
import uuid
from docker import Client
OVN_REMOTE = ""
OVN_BRIDGE = "br-int"
def call_popen(cmd):
child = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output = child.communicate()
if child.returncode:
raise RuntimeError("Fatal error executing %s" % (cmd))
if len(output) == 0 or output[0] == None:
output = ""
else:
output = output[0].strip()
return output
def call_prog(prog, args_list):
cmd = [prog, "--timeout=5", "-vconsole:off"] + args_list
return call_popen(cmd)
def ovs_vsctl(args):
return call_prog("ovs-vsctl", shlex.split(args))
def ovn_nbctl(args):
args_list = shlex.split(args)
database_option = "%s=%s" % ("--db", OVN_REMOTE)
args_list.insert(0, database_option)
return call_prog("ovn-nbctl", args_list)
def plugin_init(args):
pass
def get_annotations(pod_name, namespace):
api_server = ovs_vsctl("--if-exists get open_vswitch . "
"external-ids:api_server").strip('"')
if not api_server:
return None
url = "http://%s/api/v1/pods" % (api_server)
response = requests.get("http://0.0.0.0:8080/api/v1/pods")
if response:
pods = response.json()['items']
else:
return None
for pod in pods:
if (pod['metadata']['namespace'] == namespace and
pod['metadata']['name'] == pod_name):
annotations = pod['metadata'].get('annotations', "")
if annotations:
return annotations
else:
return None
def associate_security_group(lport_id, security_group_id):
pass
def get_ovn_remote():
try:
global OVN_REMOTE
OVN_REMOTE = ovs_vsctl("get Open_vSwitch . "
"external_ids:ovn-remote").strip('"')
except Exception as e:
error = "failed to fetch ovn-remote (%s)" % (str(e))
def plugin_setup(args):
ns = args.k8_args[0]
pod_name = args.k8_args[1]
container_id = args.k8_args[2]
get_ovn_remote()
client = Client(base_url='unix://var/run/docker.sock')
try:
inspect = client.inspect_container(container_id)
pid = inspect["State"]["Pid"]
ip_address = inspect["NetworkSettings"]["IPAddress"]
netmask = inspect["NetworkSettings"]["IPPrefixLen"]
mac = inspect["NetworkSettings"]["MacAddress"]
gateway_ip = inspect["NetworkSettings"]["Gateway"]
except Exception as e:
error = "failed to get container pid and ip address (%s)" % (str(e))
sys.exit(error)
if not pid:
sys.exit("failed to fetch the pid")
netns_dst = "/var/run/netns/%s" % (pid)
if not os.path.isfile(netns_dst):
netns_src = "/proc/%s/ns/net" % (pid)
command = "ln -s %s %s" % (netns_src, netns_dst)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "failed to create the netns link"
sys.exit(error)
# Delete the existing veth pair
command = "ip netns exec %s ip link del eth0" % (pid)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "failed to delete the default veth pair"
sys.stderr.write(error)
veth_outside = container_id[0:15]
veth_inside = container_id[0:13] + "_c"
command = "ip link add %s type veth peer name %s" \
% (veth_outside, veth_inside)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to create veth pair (%s)" % (str(e))
sys.exit(error)
# Up the outer interface
command = "ip link set %s up" % veth_outside
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to admin up veth_outside (%s)" % (str(e))
sys.exit(error)
# Move the inner veth inside the container
command = "ip link set %s netns %s" % (veth_inside, pid)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to move veth inside (%s)" % (str(e))
sys.exit(error)
# Change the name of veth_inside to eth0
command = "ip netns exec %s ip link set dev %s name eth0" \
% (pid, veth_inside)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to change name to eth0 (%s)" % (str(e))
sys.exit(error)
# Up the inner interface
command = "ip netns exec %s ip link set eth0 up" % (pid)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to admin up veth_inside (%s)" % (str(e))
sys.exit(error)
# Set the mtu to handle tunnels
command = "ip netns exec %s ip link set dev eth0 mtu %s" \
% (pid, 1450)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to set mtu (%s)" % (str(e))
sys.exit(error)
# Set the ip address
command = "ip netns exec %s ip addr add %s/%s dev eth0" \
% (pid, ip_address, netmask)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to set ip address (%s)" % (str(e))
sys.exit(error)
# Set the mac address
command = "ip netns exec %s ip link set dev eth0 address %s" % (pid, mac)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to set mac address (%s)" % (str(e))
sys.exit(error)
# Set the gateway
command = "ip netns exec %s ip route add default via %s" \
% (pid, gateway_ip)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to set gateway (%s)" % (str(e))
sys.exit(error)
# Get the logical switch
try:
lswitch = ovs_vsctl("--if-exists get open_vswitch . "
"external_ids:lswitch").strip('"')
if not lswitch:
error = "No lswitch created for this host"
sys.exit(error)
except Exception as e:
error = "Failed to get external_ids:lswitch (%s)" % (str(e))
sys.exit(error)
# Create a logical port
try:
ovn_nbctl("lport-add %s %s" % (lswitch, container_id))
except Exception as e:
error = "lport-add %s" % (str(e))
sys.exit(error)
# Set the ip address and mac address
try:
ovn_nbctl("lport-set-addresses %s \"%s %s\""
% (container_id, mac, ip_address))
except Exception as e:
error = "lport-set-addresses %s" % (str(e))
sys.exit(error)
# Add the port to a OVS bridge and set the vlan
try:
ovs_vsctl("add-port %s %s -- set interface %s "
"external_ids:attached_mac=%s external_ids:iface-id=%s "
"external_ids:ip_address=%s"
% (OVN_BRIDGE, veth_outside, veth_outside, mac,
container_id, ip_address))
except Exception as e:
ovn_nbctl("lport-del %s" % container_id)
error = "failed to create a OVS port. (%s)" % (str(e))
sys.exit(error)
annotations = get_annotations(ns, pod_name)
if annotations:
security_group = annotations.get("security-group", "")
if security_group:
associate_security_group(lport, security_group)
def plugin_status(args):
ns = args.k8_args[0]
pod_name = args.k8_args[1]
container_id = args.k8_args[2]
veth_outside = container_id[0:15]
ip_address = ovs_vsctl("--if-exists get interface %s "
"external_ids:ip_address"
% (veth_outside)).strip('"')
if ip_address:
style = {"ip": ip_address}
print json.dumps(style)
def disassociate_security_group(lport_id):
pass
def plugin_teardown(args):
ns = args.k8_args[0]
pod_name = args.k8_args[1]
container_id = args.k8_args[2]
get_ovn_remote()
veth_outside = container_id[0:15]
command = "ip link delete %s" % (veth_outside)
try:
call_popen(shlex.split(command))
except Exception as e:
error = "Failed to delete veth_outside (%s)" % (str(e))
sys.stderr.write(error)
annotations = get_annotations(ns, pod_name)
if annotations:
security_group = annotations.get("security-group", "")
if security_group:
disassociate_security_group(container_id)
try:
ovn_nbctl("lport-del %s" % container_id)
except Exception as e:
error = "failed to delete logical port (%s)" % (str(e))
sys.stderr.write(error)
try:
ovs_vsctl("del-port %s" % (veth_outside))
except Exception as e:
error = "failed to delete OVS port (%s)" % (veth_outside)
sys.stderr.write(error)
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title='Subcommands',
dest='command_name')
# Parser for sub-command init
parser_plugin_init = subparsers.add_parser('init', help="kubectl init")
parser_plugin_init.set_defaults(func=plugin_init)
# Parser for sub-command setup
parser_plugin_setup = subparsers.add_parser('setup',
help="setup pod networking")
parser_plugin_setup.add_argument('k8_args', nargs=3,
help='arguments passed by kubectl')
parser_plugin_setup.set_defaults(func=plugin_setup)
# Parser for sub-command status
parser_plugin_status = subparsers.add_parser('status',
help="pod status")
parser_plugin_status.add_argument('k8_args', nargs=3,
help='arguments passed by kubectl')
parser_plugin_status.set_defaults(func=plugin_status)
# Parser for sub-command teardown
parser_plugin_teardown = subparsers.add_parser('teardown',
help="pod teardown")
parser_plugin_teardown.add_argument('k8_args', nargs=3,
help='arguments passed by kubectl')
parser_plugin_teardown.set_defaults(func=plugin_teardown)
args = parser.parse_args()
args.func(args)
if __name__ == '__main__':
main()