99c6c3090b12927a0cccb06cd4ad8151319f72dd
[integration/test.git] / csit / suites / groupbasedpolicy / common_scripts / infrastructure_launch.py
1 #!/usr/bin/python
2
3 """
4 Spin-up script for Opendaylight GBP and GBPSFC sample concept demonstrations.
5
6 This script has to be run from the environment on which OVS switches and Docker
7 containers are located. This script works together with infrastructure_config.py
8 configuration script where the entire topology for specific scenario is configured. This
9 file defines to which already existing switch docker containers have to be connected.
10 OVS switch have to be created on the same hosting environment as this script is executed
11 from. Local switch name should match one of the names specified in configuration file so that
12 docker containers are created and connected to it.
13
14 Updated: 22.10.2015
15
16 """
17
18 import socket
19 import os
20 import sys
21 import ipaddr
22 from subprocess import call
23 from subprocess import check_output
24 from infrastructure_config import switches
25 from infrastructure_config import hosts
26 from infrastructure_config import defaultContainerImage
27
28
29 def add_controller(sw, ip):
30     """Connects OVS switch to a controller. The switch is specified
31         by it's name and the controller by it's IP address.
32
33     Args:
34         :param sw: name of a switch on which controller is set
35
36         :param ip: IP address of controller
37
38     NOTE:
39         :Required controller should listen on TCP port 6653.
40     """
41
42     try:
43         socket.inet_aton(ip)
44     except socket.error:
45         print "Error: %s is not a valid IPv4 address of controller!" % ip
46         os.exit(2)
47
48     call(['sudo', 'ovs-vsctl', 'set-controller', sw, 'tcp:%s:6653' % ip])
49
50
51 def add_manager(ip):
52     """Sets OVSDB manager for OVS instance.
53
54     Args:
55         :param ip: IP address of specified manager
56
57     NOTE:
58         :Required manager should listen on TCP port 6640.
59     """
60
61     try:
62         socket.inet_aton(ip)
63     except socket.error:
64         print "Error: %s is not a valid IPv4 address of manager!" % ip
65         os.exit(2)
66
67     cmd = ['sudo', 'ovs-vsctl', 'set-manager', 'tcp:%s:6640' % ip]
68     call(cmd)
69
70
71 def add_switch(name, dpid=None):
72     """Adds switch to OVS instance and sets it's DataPath ID
73         if specified.
74
75     Args:
76         :param ip: name of new switch
77
78         :param dpid: DataPath ID of new switch
79     """
80
81     call(['sudo', 'ovs-vsctl', 'add-br', name])  # Add bridge
82     if dpid:
83         if len(dpid) < 16:  # DPID must be 16-bytes in later versions of OVS
84             filler = '0000000000000000'
85             # prepending zeros to match 16-byt length, e.g. 123 -> 0000000000000123
86             dpid = filler[:len(filler) - len(dpid)] + dpid
87         elif len(dpid) > 16:
88             print 'DPID: %s is too long' % dpid
89             sys.exit(3)
90         call(['sudo', 'ovs-vsctl', 'set', 'bridge', name,
91               'other-config:datapath-id=%s' % dpid])
92
93
94 def set_of_version(sw, version='OpenFlow13,OpenFlow12,OpenFlow10'):
95     """Sets OpenFlow protocol versions on OVS switch
96
97     Args:
98         :param sw: name of switch
99
100         :param sw: OpenFlow versions to support on switch
101     """
102
103     call(['sudo', 'ovs-vsctl', 'set', 'bridge', sw, 'protocols={}'.format(version)])
104
105
106 def add_vxlan_tunnel(sw):
107     """Adds VXLAN tunnel to OVS switch.
108
109     Args:
110         :param sw: name of switch
111
112     NOTE:
113         :Remote IP is read from flows.
114     """
115     ifaceName = '{}-vxlan-0'.format(sw)
116     cmd = ['sudo', 'ovs-vsctl', 'add-port', sw, ifaceName,
117            '--', 'set', 'Interface', ifaceName,
118            'type=vxlan',
119            'options:remote_ip=flow',
120            'options:key=flow']
121     call(cmd)
122
123
124 def add_gpe_tunnel(sw):
125     """Adds GPE tunnel to OVS switch.
126
127     Args:
128         :param sw: name of switch
129
130         :param dpid: DataPath ID of new switches
131
132     NOTE:
133         :Remote IP is read from flows.
134     """
135
136     ifaceName = '{}-vxlangpe-0'.format(sw)
137     cmd = ['sudo', 'ovs-vsctl', 'add-port', sw, ifaceName,
138            '--', 'set', 'Interface', ifaceName,
139            'type=vxlan',
140            'options:remote_ip=flow',
141            'options:dst_port=6633',
142            'options:nshc1=flow',
143            'options:nshc2=flow',
144            'options:nshc3=flow',
145            'options:nshc4=flow',
146            'options:nsp=flow',
147            'options:nsi=flow',
148            'options:key=flow']
149     call(cmd)
150
151
152 def launch_container(host, containerImage):
153     # TODO use Docker.py
154     """Runs docker container in background.
155
156     Args:
157         :param host: container host name
158
159         :param dpid: DataPath ID of new switch
160
161     Returns:
162         :returns string: container ID
163
164     NOTE:
165         :No networking set.
166
167     """
168
169     containerID = check_output(['docker',
170                                 'run',
171                                 '-d',
172                                 '--net=none',
173                                 '--name=%s' % host['name'],
174                                 '-h',
175                                 host['name'],
176                                 '-t',
177                                 '-i',
178                                 '--privileged=True',
179                                 containerImage,
180                                 '/bin/bash'])
181     return containerID[:-1]  # Remove extraneous \n from output of above
182
183
184 def connect_container_to_switch(sw, host, containerID):
185     """Connects docker to OVS switch.
186
187     Args:
188         :param sw: name of switch
189
190         :param host: host object to process.
191             Here is an example of such as object
192             {'name': 'h35_2',
193              'mac': '00:00:00:00:35:02',
194              'ip': '10.0.35.2/24',
195              'switch': 'sw1'}
196              Note: 'switch' - name of OVS switch to
197                  which the host will be connected
198
199         :param containerID: ID of docker container
200     """
201
202     hostIP = host['ip']
203     mac = host['mac']
204     nw = ipaddr.IPv4Network(hostIP)
205     broadcast = "{}".format(nw.broadcast)
206     router = "{}".format(nw.network + 1)
207     ovswork_path = os.path.dirname(os.path.realpath(__file__)) + '/ovswork.sh'
208     cmd = [ovswork_path,
209            sw,
210            containerID,
211            hostIP,
212            broadcast,
213            router,
214            mac,
215            host['name']]
216     if ('vlan') in host:
217         cmd.append(host['vlan'])
218     call(cmd)
219
220
221 def launch(switches, hosts, odl_ip='127.0.0.1'):
222     """Connects hosts to switches. Arguments are
223        tied to underlying configuration file. Processing runs
224        for switch, that is present on local environment and
225        for hosts configured to be connected to the switch.
226
227     Args:
228         :param switches: switches to connect to
229             Example of switch object
230             {'name': 'sw1',
231              'dpid': '1'}
232
233         :param hosts: hosts to connect
234             Example of host object
235             {'name': 'h35_2',
236              'mac': '00:00:00:00:35:02',
237              'ip': '10.0.35.2/24',
238              'switch': 'sw1'}
239              Note: 'switch' - name of OVS switch to
240                  which the host will be connected
241
242         :param odl_ip: IP address of ODL, acting as
243             both - manager and controller.
244             Default value is '127.0.0.1'
245     """
246
247     for sw in switches:
248         add_manager(odl_ip)
249         ports = 0
250         first_host = True
251         for host in hosts:
252             if host['switch'] == sw['name']:
253                 if first_host:
254                     add_switch(sw['name'], sw['dpid'])
255                     set_of_version(sw['name'])
256                     add_controller(sw['name'], odl_ip)
257                     add_gpe_tunnel(sw['name'])
258                     add_vxlan_tunnel(sw['name'])
259                 first_host = False
260                 containerImage = defaultContainerImage  # from Config
261                 if ('container_image') in host:  # from Config
262                     containerImage = host['container_image']
263                 containerID = launch_container(host, containerImage)
264                 ports += 1
265                 connect_container_to_switch(
266                     sw['name'], host, containerID)
267                 host['port-name'] = 'vethl-' + host['name']
268                 print "Created container: %s with IP: %s. Connect using docker attach %s," \
269                     "disconnect with 'ctrl-p-q'." % (host['name'], host['ip'], host['name'])
270
271 if __name__ == "__main__":
272     if len(sys.argv) < 2 or len(sys.argv) > 3:
273         print "Please, specify IP of ODL and switch index in arguments."
274         print "usage: ./infrastructure_launch.py ODL_IP SWITCH_INDEX"
275         sys.exit(2)
276
277     controller = sys.argv[1]
278     try:
279         socket.inet_aton(controller)
280     except socket.error:
281         print "Error: %s is not a valid IPv4 address!" % controller
282         sys.exit(2)
283
284     sw_index = int(sys.argv[2])
285     print sw_index
286     print switches[sw_index]
287     if sw_index not in range(0, len(switches) + 1):
288         print len(switches) + 1
289         print "Error: %s is not a valid switch index!" % sw_index
290         sys.exit(2)
291
292     sw_type = switches[sw_index]['type']
293     sw_name = switches[sw_index]['name']
294     if sw_type == 'gbp':
295         print "*****************************"
296         print "Configuring %s as a GBP node." % sw_name
297         print "*****************************"
298         print
299         launch([switches[sw_index]], hosts, controller)
300         print "*****************************"
301         print "OVS status:"
302         print "-----------"
303         print
304         call(['sudo', 'ovs-vsctl', 'show'])
305         print
306         print "Docker containers:"
307         print "------------------"
308         call(['docker', 'ps'])
309         print "*****************************"
310     elif sw_type == 'sff':
311         print "*****************************"
312         print "Configuring %s as an SFF." % sw_name
313         print "*****************************"
314         call(['sudo', 'ovs-vsctl', 'set-manager', 'tcp:%s:6640' % controller])
315         print
316     elif sw_type == 'sf':
317         print "*****************************"
318         print "Configuring %s as an SF." % sw_name
319         print "*****************************"
320         call(['%s/sf-config.sh' % os.path.dirname(os.path.realpath(__file__)), '%s' % sw_name])