/
vrouter-port-control
executable file
·340 lines (304 loc) · 12.9 KB
/
vrouter-port-control
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
#
# Copyright (c) 2015 Juniper Networks, Inc. All rights reserved.
#
import sys
import argparse
import requests
from requests.exceptions import ConnectionError
import json
import os
import errno
import datetime
import re
import time
import socket
PORT_PATH = "/var/lib/contrail/ports/"
PORT_IPC_VROUTER_AGENT_PORT = 9091
TIMEOUT_SECS = 15
DPDK_NETLINK_TCP_PORT = 20914
PORT_OPEN_TIMEOUT_SECS = 120
DPDK_NETLINK_SOCK_PATH="/var/run/vrouter/dpdk_netlink"
class VrouterPortControl(object):
def __init__(self, args_str=None):
self._args = None
if not args_str:
args_str = ' '.join(sys.argv[1:])
self._parse_args(args_str)
# TODO: Move this to other place as it tries to create directory every time
self.CreateDirectoryTree(PORT_PATH)
port_type_value = 0
ret_code = 0
headers = {'content-type': 'application/json'}
base_url = "http://localhost:" + str(PORT_IPC_VROUTER_AGENT_PORT)
if self._args.oper == "add":
if self._args.vif_type == "VhostUser":
if not self._args.vhostuser_socket:
sys.exit(1)
if not self.WaitForPortOpen(PORT_IPC_VROUTER_AGENT_PORT):
sys.exit(2)
if not self.WaitForNetlinkSocket():
sys.exit(3)
if self._args.port_type == "NovaVMPort":
port_type_value = 0
elif self._args.port_type == "NameSpacePort":
port_type_value = 1
elif self._args.port_type == "ESXiPort":
port_type_value = 2
u = self._args.vm_project_uuid
project_id = ""
if (u and (len(u) == 32)):
u = u[:8] + '-' + u[8:]
u = u[:13] + '-' + u[13:]
u = u[:18] + '-' + u[18:]
project_id = u[:23] + '-' + u[23:]
url = base_url + "/port"
payload = self.GetJSonDict(port_type_value, project_id)
json_dump = json.dumps(payload)
if not self._args.no_persist:
ret_code = self.WriteToFile(port_type_value, project_id)
try:
'''We post the request to agent even if WriteToFile has failed.
Agent will write to file if file is not already present
'''
r = requests.post(url, data=json_dump, headers=headers)
'''Overwrite the ret_code based on agent response '''
if r.status_code != 200:
print "Request failed: %s" % r.text
ret_code = 4
else:
ret_code = 0
if ret_code == 0 and self._args.vif_type == "VhostUser" and \
self._args.vhostuser_mode == 0:
#In DPDK mode we should wait until socket is created by
#vrouter module
ret_code = self.WaitForSocketFile()
except ConnectionError:
'''In DPDK mode return failure when we are not able to connect
to agent
'''
if self._args.vif_type == "VhostUser":
ret_code = 5
pass
elif self._args.oper == "delete":
if not self._args.no_persist:
self.DeleteFile()
url = base_url + "/port/" + self._args.uuid
try:
r = requests.delete(url, data=None, headers=headers)
if r.status_code != 200:
print "Request failed: %s" % r.text
ret_code = 1
except ConnectionError:
pass
elif self._args.oper == "enable":
url = base_url + "/enable-port/" + self._args.uuid
try:
r = requests.put(url, data=None, headers=headers)
if r.status_code != 200:
print "Request failed: %s" % r.text
ret_code = 1
except ConnectionError:
pass
elif self._args.oper == "disable":
url = base_url + "/disable-port/" + self._args.uuid
try:
r = requests.put(url, data=None, headers=headers)
if r.status_code != 200:
print "Request failed: %s" % r.text
ret_code = 1
except ConnectionError:
pass
sys.exit(ret_code)
# end __init__
def StripQuotes(self, arg):
if arg and arg.startswith('"') and arg.endswith('"'):
return arg[1:-1]
# end StripQuotes
def IsNumber(self, s):
try:
int(s)
return True
except ValueError:
return False
# end IsNumber
def _parse_args(self, args_str):
strip_quotes = False
if '"' in args_str:
regex = re.compile("\s+(?=\-\-[_a-zA-Z0-9]+=\".*?\")")
strip_quotes = True
else:
regex = re.compile("\s+(?=\-\-)")
# Turn off help, so we print all options in response to -h
conf_parser = argparse.ArgumentParser(add_help=False)
args, remaining_argv = conf_parser.parse_known_args(
regex.split(args_str)
)
# Don't surpress add_help here so it will handle -h
parser = argparse.ArgumentParser(
# Inherit options from config_parser
parents=[conf_parser],
# print script description with -h/--help
description=__doc__,
# Don't mess with format of description
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument('--oper', help="Operation add/delete ",
required=True)
parser.add_argument('--uuid', help="port UUID", required=True)
parser.add_argument('--instance_uuid', help="instance UUID")
parser.add_argument('--vn_uuid', help="VN UUID")
parser.add_argument('--vm_project_uuid', help="VM UUID")
parser.add_argument('--ip_address', help="IP Address")
parser.add_argument('--ipv6_address', help="IPv6 Address")
parser.add_argument('--vm_name', help="VM Name")
parser.add_argument('--mac', help="MAC address")
parser.add_argument('--tap_name', help="System name of interface")
parser.add_argument("--port_type", help="Port type", default="NovaVMPort")
parser.add_argument("--tx_vlan_id", help="Transmit VLAN ID")
parser.add_argument("--rx_vlan_id", help="Receive VLAN ID")
parser.add_argument("--no_persist", type=bool,
help="Dont't store port information in files",
default=False)
parser.add_argument("--vif_type", help="VIF type", default="Vrouter")
parser.add_argument("--vhostuser_socket", help="Path of vhostuser socket file")
parser.add_argument("--vhostuser_mode",
help = "mode of operation of vhostuser socket",
default="0")
self._args = parser.parse_args(remaining_argv)
if strip_quotes:
self._args.oper = self.StripQuotes(self._args.oper)
self._args.uuid = self.StripQuotes(self._args.uuid)
self._args.instance_uuid = self.StripQuotes(self._args.instance_uuid)
self._args.ip_address = self.StripQuotes(self._args.ip_address)
self._args.ipv6_address = self.StripQuotes(self._args.ipv6_address)
self._args.vn_uuid = self.StripQuotes(self._args.vn_uuid)
self._args.vm_name = self.StripQuotes(self._args.vm_name)
self._args.vm_project_uuid = self.StripQuotes(self._args.vm_project_uuid)
self._args.mac = self.StripQuotes(self._args.mac)
self._args.tap_name = self.StripQuotes(self._args.tap_name)
self._args.port_type = self.StripQuotes(self._args.port_type)
self._args.rx_vlan_id = self.StripQuotes(self._args.rx_vlan_id)
self._args.tx_vlan_id = self.StripQuotes(self._args.tx_vlan_id)
self._args.vif_type = self.StripQuotes(self._args.vif_type)
self._args.vhostuser_socket = self.StripQuotes(self._args.vhostuser_socket)
self._args.vhostuser_mode = self.StripQuotes(self._args.vhostuser_mode)
if self._args.no_persist:
self._args.no_persist = True
oper_list = ['add', 'delete', 'enable', 'disable']
if self._args.oper not in oper_list:
print "Invalid argument for oper %s" % (self._args.oper)
sys.exit(1)
if self._args.oper == "add":
port_type_list = ['NovaVMPort', 'NameSpacePort', 'ESXiPort']
if self._args.port_type not in port_type_list:
print "Invalid argument for port_type %s" % (self._args.port_type)
sys.exit(1)
vif_type_list = ['VhostUser', 'Vrouter']
if self._args.vif_type not in vif_type_list:
print "Invalid argument for vif_type %s" % (self._args.vif_type)
sys.exit(1)
if self._args.rx_vlan_id:
if not self.IsNumber(self._args.rx_vlan_id):
print "Invalid argument for rx_vlan_id %s" % (self._args.rx_vlan_id)
sys.exit(1)
self._args.rx_vlan_id = int(self._args.rx_vlan_id)
if self._args.tx_vlan_id:
if not self.IsNumber(self._args.tx_vlan_id):
print "Invalid argument for tx_vlan_id %s" % (self._args.tx_vlan_id)
sys.exit(1)
self._args.tx_vlan_id = int(self._args.tx_vlan_id)
if self._args.vhostuser_mode:
self._args.vhostuser_mode = int(self._args.vhostuser_mode)
#end _parse_args
def CreateDirectoryTree(self, path):
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
# end CreateDirectoryTree
def GetJSonDict(self, port_type, project_id):
data = {
"id": self._args.uuid,
"instance-id": self._args.instance_uuid,
"ip-address": self._args.ip_address,
"ip6-address": self._args.ipv6_address,
"vn-id": self._args.vn_uuid,
"display-name": self._args.vm_name,
"vm-project-id": project_id,
"mac-address": self._args.mac,
"system-name": self._args.tap_name,
"type": port_type,
"rx-vlan-id": self._args.rx_vlan_id,
"tx-vlan-id": self._args.tx_vlan_id,
"vhostuser-mode" : self._args.vhostuser_mode,
"author": __file__,
"time": str(datetime.datetime.now())
}
return data
# end GetJSonDict
def WriteToFile(self, port_type, project_id):
filename = ("%s%s" % (PORT_PATH, self._args.uuid))
data = self.GetJSonDict(port_type, project_id)
try:
with open(filename, 'w') as outfile:
json.dump(data, outfile, True)
outfile.close()
except:
return 1
return 0
# end WriteToFile
def DeleteFile(self):
filename = ("%s%s" % (PORT_PATH, self._args.uuid))
if os.path.isfile(filename):
os.remove(filename)
# end DeleteFile
def WaitForNetlinkSocket(self):
timeout_usecs = TIMEOUT_SECS * 1000000
sleep_usecs = 1000
for i in range(1,(timeout_usecs/sleep_usecs)):
if os.path.exists(DPDK_NETLINK_SOCK_PATH):
#TODO: need to check if the socket path is opened
return True
time.sleep(sleep_usecs/1000000.0)
return False
#end WaitForNetlinkSocket
def WaitForSocketFile(self):
timeout_usecs = TIMEOUT_SECS * 1000000
sleep_usecs = 1000
for i in range(1, (timeout_usecs / sleep_usecs)):
if os.path.exists(self._args.vhostuser_socket):
return 0
# sleep takes time in seconds. Convert usecs to secs.
time.sleep(sleep_usecs / 1000000.0)
return 6
# end WaitForSocketFile
def IsPortOpen(self, port_num):
s = socket.socket()
try:
s.connect(('127.0.0.1', port_num))
s.shutdown(socket.SHUT_RDWR)
s.close()
return True
except socket.error:
return False
# end IsPortOpen
def WaitForPortOpen(self, port_num):
timeout_usecs = PORT_OPEN_TIMEOUT_SECS * 1000000
sleep_usecs = 1000000
for i in range(1, (timeout_usecs / sleep_usecs)):
if self.IsPortOpen(port_num):
return True
# sleep takes time in seconds. Convert usecs to secs.
time.sleep(sleep_usecs / 1000000.0)
return False
# end WaitForPortOpen
# end class VrouterPortControl
def main(args_str=None):
VrouterPortControl(args_str)
# end main
if __name__ == "__main__":
main()