applied the except and raise fixers to the master branch to close the gap with py3
[nepi.git] / src / nepi / resources / planetlab / scripts / pl-vif-create.py
1 #
2 #    NEPI, a framework to manage network experiments
3 #    Copyright (C) 2013 INRIA
4 #
5 #    This program is free software: you can redistribute it and/or modify
6 #    it under the terms of the GNU General Public License version 2 as
7 #    published by the Free Software Foundation;
8 #
9 #    This program is distributed in the hope that it will be useful,
10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #    GNU General Public License for more details.
13 #
14 #    You should have received a copy of the GNU General Public License
15 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 #
17 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
18
19 from __future__ import print_function
20
21 import base64
22 import errno
23 import passfd
24 import socket
25 import vsys
26 from optparse import OptionParser
27
28 STOP_MSG = "STOP"
29 PASSFD_MSG = "PASSFD"
30
31 def create_socket(socket_name):
32     sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
33     sock.bind(socket_name)
34     return sock
35
36 def recv_msg(conn):
37     msg = []
38     chunk = ''
39
40     while '\n' not in chunk:
41         try:
42             chunk = conn.recv(1024)
43         except (OSError, socket.error) as e:
44             if e[0] != errno.EINTR:
45                 raise
46             # Ignore eintr errors
47             continue
48
49         if chunk:
50             msg.append(chunk)
51         else:
52             # empty chunk = EOF
53             break
54
55     msg = ''.join(msg).split('\n')[0]
56     # The message might have arguments that will be appended
57     # as a '|' separated list after the message type
58     args = msg.split("|")
59     msg = args.pop(0)
60
61     dmsg = base64.b64decode(msg)
62     dargs = []
63     for arg in args:
64         darg = base64.b64decode(arg)
65         dargs.append(darg.rstrip())
66
67     return (dmsg.rstrip(), dargs)
68
69 def send_reply(conn, reply):
70     encoded = base64.b64encode(reply)
71     conn.send("%s\n" % encoded)
72
73 def stop_action():
74     return "STOP-ACK"
75
76 def passfd_action(fd, args):
77     """ Sends the file descriptor associated to the TAP device 
78     to another process through a unix socket.
79     """
80     address = args.pop(0)
81     print(address)
82     sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
83     sock.connect(address)
84     passfd.sendfd(sock, fd, '0')
85     return "PASSFD-ACK"
86
87 def get_options():
88     usage = ("usage: %prog -t <vif-type> -a <ip4-address> -n <net-prefix> "
89         "-s <snat> -p <pointopoint> -q <txqueuelen> -f <vif-name-file> "
90         "-S <socket-name>")
91     
92     parser = OptionParser(usage = usage)
93
94     parser.add_option("-t", "--vif-type", dest="vif_type",
95         help = "Virtual interface type. Either IFF_TAP or IFF_TUN. "
96             "Defaults to IFF_TAP. ", type="str")
97
98     parser.add_option("-a", "--ip4-address", dest="ip4_address",
99         help = "IPv4 address to assign to interface. It must belong to the "
100             "network segment owned by the slice, given by the vsys_vnet tag. ",
101         type="str")
102
103     parser.add_option("-n", "--net-prefix", dest="net_prefix",
104         help = "IPv4 network prefix for the interface. It must be the one "
105             "given by the slice's vsys_vnet tag. ",
106         type="int")
107
108     parser.add_option("-s", "--snat", dest="snat", default = False,
109         action="store_true", help="Enable SNAT for the interface")
110
111     parser.add_option("-p", "--pointopoint", dest="pointopoint",
112         help = "Peer end point for the interface  ", default = None,
113         type="str")
114
115     parser.add_option("-q", "--txqueuelen", dest="txqueuelen",
116         help = "Size of transmision queue. Defaults to 0.",
117         default = 0,
118         type="int")
119
120     parser.add_option("-f", "--vif-name-file", dest="vif_name_file",
121         help = "File to store the virtual interface name assigned by the OS", 
122         default = "vif_name", type="str")
123
124     parser.add_option("-S", "--socket-name", dest="socket_name",
125         help = "Name for the unix socket used to interact with this process", 
126         type="str")
127
128     (options, args) = parser.parse_args()
129     
130     vif_type = vsys.IFF_TAP
131     if options.vif_type and options.vif_type == "IFF_TUN":
132         vif_type = vsys.IFF_TUN
133
134     return (vif_type, options.ip4_address, options.net_prefix, 
135             options.snat, options.pointopoint, options.txqueuelen,
136             options.vif_name_file, options.socket_name)
137
138 if __name__ == '__main__':
139
140     (vif_type, ip4_address, net_prefix, snat, pointopoint, 
141             txqueuelen, vif_name_file, socket_name) = get_options()
142
143     (fd, vif_name) = vsys.fd_tuntap(vif_type)
144   
145     vsys.vif_up(vif_name, ip4_address, net_prefix, snat = snat, 
146             pointopoint = pointopoint, txqueuelen = txqueuelen) 
147      
148     # Saving interface name to vif_name_file
149     with open(vif_name_file, 'w') as f:
150         f.write(vif_name)
151
152     # create unix socket to receive instructions
153     sock = create_socket(socket_name)
154     sock.listen(0)
155
156     # wait for messages to arrive and process them
157     stop = False
158
159     while not stop:
160         conn, addr = sock.accept()
161         conn.settimeout(5)
162
163         while not stop:
164             try:
165                 (msg, args) = recv_msg(conn)
166             except socket.timeout as e:
167                 # Ingore time-out
168                 continue
169
170             if not msg:
171                 # Ignore - connection lost
172                 break
173
174             if msg == STOP_MSG:
175                 stop = True
176                 reply = stop_action()
177             elif msg == PASSFD_MSG:
178                 reply = passfd_action(fd, args)
179
180             try:
181                 send_reply(conn, reply)
182             except socket.error:
183                 break
184