python: Remove unnecessary semicolons and slashes.
[cascardo/ovs.git] / vtep / ovs-vtep
1 #! /usr/bin/env python
2 # Copyright (C) 2013 Nicira, Inc. All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 # Limitations:
17 #     - Doesn't support multicast other than "unknown-dst"
18
19 import argparse
20 import re
21 import shlex
22 import subprocess
23 import sys
24 import time
25 import types
26
27 import ovs.dirs
28 import ovs.util
29 import ovs.daemon
30 import ovs.unixctl.server
31 import ovs.vlog
32
33
34 VERSION = "0.99"
35
36 root_prefix = ""
37
38 __pychecker__ = 'no-reuseattr'  # Remove in pychecker >= 0.8.19.
39 vlog = ovs.vlog.Vlog("ovs-vtep")
40 exiting = False
41
42 ps_name = ""
43 ps_type = ""
44 Tunnel_Ip = ""
45 Lswitches = {}
46 Bindings = {}
47 ls_count = 0
48 tun_id = 0
49 bfd_bridge = "vtep_bfd"
50 bfd_ref = {}
51
52
53 def call_prog(prog, args_list):
54     cmd = [prog, "-vconsole:off"] + args_list
55     output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
56     if len(output) == 0 or output[0] is None:
57         output = ""
58     else:
59         output = output[0].strip()
60     return output
61
62
63 def ovs_vsctl(args):
64     return call_prog("ovs-vsctl", shlex.split(args))
65
66
67 def ovs_ofctl(args):
68     return call_prog("ovs-ofctl", shlex.split(args))
69
70
71 def vtep_ctl(args):
72     return call_prog("vtep-ctl", shlex.split(args))
73
74
75 def unixctl_exit(conn, unused_argv, unused_aux):
76     global exiting
77     exiting = True
78     conn.reply(None)
79
80
81 class Logical_Switch(object):
82     def __init__(self, ls_name):
83         global ls_count
84         self.name = ls_name
85         ls_count += 1
86         self.short_name = "vtep_ls" + str(ls_count)
87         vlog.info("creating lswitch %s (%s)" % (self.name, self.short_name))
88         self.ports = {}
89         self.tunnels = {}
90         self.local_macs = set()
91         self.remote_macs = {}
92         self.unknown_dsts = set()
93         self.tunnel_key = 0
94         self.setup_ls()
95
96     def __del__(self):
97         vlog.info("destroying lswitch %s" % self.name)
98
99     def setup_ls(self):
100         column = vtep_ctl("--columns=tunnel_key find logical_switch "
101                               "name=%s" % self.name)
102         tunnel_key = column.partition(":")[2].strip()
103         if tunnel_key and isinstance(eval(tunnel_key), types.IntType):
104             self.tunnel_key = tunnel_key
105             vlog.info("using tunnel key %s in %s"
106                       % (self.tunnel_key, self.name))
107         else:
108             self.tunnel_key = 0
109             vlog.warn("invalid tunnel key for %s, using 0" % self.name)
110
111         if ps_type:
112             ovs_vsctl("--may-exist add-br %s -- set Bridge %s datapath_type=%s"
113                       % (self.short_name, self.short_name, ps_type))
114         else:
115             ovs_vsctl("--may-exist add-br %s" % self.short_name)
116
117         ovs_vsctl("br-set-external-id %s vtep_logical_switch true"
118                   % self.short_name)
119         ovs_vsctl("br-set-external-id %s logical_switch_name %s"
120                   % (self.short_name, self.name))
121
122         vtep_ctl("clear-local-macs %s" % self.name)
123         vtep_ctl("add-mcast-local %s unknown-dst %s" % (self.name, Tunnel_Ip))
124
125         ovs_ofctl("del-flows %s" % self.short_name)
126         ovs_ofctl("add-flow %s priority=0,action=drop" % self.short_name)
127
128     def cleanup_ls(self):
129         for port_no, tun_name, remote_ip in self.tunnels.itervalues():
130             del_bfd(remote_ip)
131
132     def update_flood(self):
133         flood_ports = self.ports.values()
134
135         # Traffic flowing from one 'unknown-dst' should not be flooded to
136         # port belonging to another 'unknown-dst'.
137         for tunnel in self.unknown_dsts:
138             port_no = self.tunnels[tunnel][0]
139             ovs_ofctl("add-flow %s table=1,priority=1,in_port=%s,action=%s"
140                         % (self.short_name, port_no, ",".join(flood_ports)))
141
142         # Traffic coming from a VTEP physical port should only be flooded to
143         # one 'unknown-dst' and to all other physical ports that belong to that
144         # VTEP device and this logical switch.
145         for tunnel in self.unknown_dsts:
146             port_no = self.tunnels[tunnel][0]
147             flood_ports.append(port_no)
148             break
149
150         ovs_ofctl("add-flow %s table=1,priority=0,action=%s"
151                   % (self.short_name, ",".join(flood_ports)))
152
153     def add_lbinding(self, lbinding):
154         vlog.info("adding %s binding to %s" % (lbinding, self.name))
155         port_no = ovs_vsctl("get Interface %s ofport" % lbinding)
156         self.ports[lbinding] = port_no
157         ovs_ofctl("add-flow %s in_port=%s,action=learn(table=1,"
158                   "priority=1000,idle_timeout=15,cookie=0x5000,"
159                   "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],"
160                   "output:NXM_OF_IN_PORT[]),resubmit(,1)"
161                   % (self.short_name, port_no))
162
163         self.update_flood()
164
165     def del_lbinding(self, lbinding):
166         vlog.info("removing %s binding from %s" % (lbinding, self.name))
167         port_no = self.ports[lbinding]
168         ovs_ofctl("del-flows %s in_port=%s" % (self.short_name, port_no))
169         del self.ports[lbinding]
170         self.update_flood()
171
172     def add_tunnel(self, tunnel):
173         global tun_id
174         vlog.info("adding tunnel %s" % tunnel)
175         encap, ip = tunnel.split("/")
176
177         if encap != "vxlan_over_ipv4":
178             vlog.warn("unsupported tunnel format %s" % encap)
179             return
180
181         tun_id += 1
182         tun_name = "vx" + str(tun_id)
183
184         ovs_vsctl("add-port %s %s -- set Interface %s type=vxlan "
185                   "options:key=%s options:remote_ip=%s"
186                   % (self.short_name, tun_name, tun_name, self.tunnel_key, ip))
187
188         for i in range(10):
189             port_no = ovs_vsctl("get Interface %s ofport" % tun_name)
190             if port_no != "-1":
191                 break
192             elif i == 9:
193                 vlog.warn("couldn't create tunnel %s" % tunnel)
194                 ovs_vsctl("del-port %s %s" % (self.short_name, tun_name))
195                 return
196
197             # Give the system a moment to allocate the port number
198             time.sleep(0.5)
199
200         self.tunnels[tunnel] = (port_no, tun_name, ip)
201
202         add_bfd(ip)
203
204         ovs_ofctl("add-flow %s table=0,priority=1000,in_port=%s,"
205                   "actions=resubmit(,1)"
206                   % (self.short_name, port_no))
207
208     def del_tunnel(self, tunnel):
209         vlog.info("removing tunnel %s" % tunnel)
210
211         port_no, tun_name, remote_ip = self.tunnels[tunnel]
212         ovs_ofctl("del-flows %s table=0,in_port=%s"
213                     % (self.short_name, port_no))
214         ovs_vsctl("del-port %s %s" % (self.short_name, tun_name))
215
216         del_bfd(remote_ip)
217
218         del self.tunnels[tunnel]
219
220     def update_local_macs(self):
221         flows = ovs_ofctl("dump-flows %s cookie=0x5000/-1,table=1"
222                           % self.short_name).splitlines()
223         macs = set()
224         for f in flows:
225             mac = re.split(r'.*dl_dst=(.*) .*', f)
226             if len(mac) == 3:
227                 macs.add(mac[1])
228
229         for mac in macs.difference(self.local_macs):
230             vlog.info("adding local ucast %s to %s" % (mac, self.name))
231             vtep_ctl("add-ucast-local %s %s %s" % (self.name, mac, Tunnel_Ip))
232
233         for mac in self.local_macs.difference(macs):
234             vlog.info("removing local ucast %s from %s" % (mac, self.name))
235             vtep_ctl("del-ucast-local %s %s" % (self.name, mac))
236
237         self.local_macs = macs
238
239     def add_remote_mac(self, mac, tunnel):
240         port_no = self.tunnels.get(tunnel, (0, ""))[0]
241         if not port_no:
242             return
243
244         ovs_ofctl("add-flow %s table=1,priority=1000,dl_dst=%s,action=%s"
245                   % (self.short_name, mac, port_no))
246
247     def del_remote_mac(self, mac):
248         ovs_ofctl("del-flows %s table=1,dl_dst=%s" % (self.short_name, mac))
249
250     def update_remote_macs(self):
251         remote_macs = {}
252         unknown_dsts = set()
253         tunnels = set()
254         parse_ucast = True
255
256         mac_list = vtep_ctl("list-remote-macs %s" % self.name).splitlines()
257         for line in mac_list:
258             if (line.find("mcast-mac-remote") != -1):
259                 parse_ucast = False
260                 continue
261
262             entry = re.split(r'  (.*) -> (.*)', line)
263             if len(entry) != 4:
264                 continue
265
266             if parse_ucast:
267                 remote_macs[entry[1]] = entry[2]
268             else:
269                 if entry[1] != "unknown-dst":
270                     continue
271
272                 unknown_dsts.add(entry[2])
273
274             tunnels.add(entry[2])
275
276         old_tunnels = set(self.tunnels.keys())
277
278         for tunnel in tunnels.difference(old_tunnels):
279             self.add_tunnel(tunnel)
280
281         for tunnel in old_tunnels.difference(tunnels):
282             self.del_tunnel(tunnel)
283
284         for mac in remote_macs.keys():
285             if (self.remote_macs.get(mac) != remote_macs[mac]):
286                 self.add_remote_mac(mac, remote_macs[mac])
287
288         for mac in self.remote_macs.keys():
289             if mac not in remote_macs:
290                 self.del_remote_mac(mac)
291
292         self.remote_macs = remote_macs
293
294         if (self.unknown_dsts != unknown_dsts):
295             self.unknown_dsts = unknown_dsts
296             self.update_flood()
297
298     def update_stats(self):
299         # Map Open_vSwitch's "interface:statistics" to columns of
300         # vtep's logical_binding_stats. Since we are using the 'interface' from
301         # the logical switch to collect stats, packets transmitted from it
302         # is received in the physical switch and vice versa.
303         stats_map = {'tx_packets': 'packets_to_local',
304                      'tx_bytes': 'bytes_to_local',
305                      'rx_packets': 'packets_from_local',
306                      'rx_bytes': 'bytes_from_local'}
307
308         # Go through all the logical switch's interfaces that end with "-l"
309         # and copy the statistics to logical_binding_stats.
310         for interface in self.ports.iterkeys():
311             if not interface.endswith("-l"):
312                 continue
313             # Physical ports can have a '-' as part of its name.
314             vlan, remainder = interface.split("-", 1)
315             pp_name, logical = remainder.rsplit("-", 1)
316             uuid = vtep_ctl("get physical_port %s vlan_stats:%s"
317                             % (pp_name, vlan))
318             if not uuid:
319                 continue
320
321             for (mapfrom, mapto) in stats_map.iteritems():
322                 value = ovs_vsctl("get interface %s statistics:%s"
323                                 % (interface, mapfrom)).strip('"')
324                 vtep_ctl("set logical_binding_stats %s %s=%s"
325                         % (uuid, mapto, value))
326
327     def run(self):
328         self.update_local_macs()
329         self.update_remote_macs()
330         self.update_stats()
331
332
333 def get_vtep_tunnel(remote_ip):
334     # Get the physical_locator record for the local tunnel end point.
335     column = vtep_ctl("--columns=_uuid find physical_locator "
336                       "dst_ip=%s" % Tunnel_Ip)
337     local = column.partition(":")[2].strip()
338     if not local:
339         return (None, None, None)
340
341     # Get the physical_locator record for the remote tunnel end point.
342     column = vtep_ctl("--columns=_uuid find physical_locator "
343                       "dst_ip=%s" % remote_ip)
344     remote = column.partition(":")[2].strip()
345     if not remote:
346         return (None, None, None)
347
348     column = vtep_ctl("--columns=_uuid find tunnel "
349                       "local=%s remote=%s" % (local, remote))
350     tunnel = column.partition(":")[2].strip()
351
352     return (local, remote, tunnel)
353
354
355 def create_vtep_tunnel(remote_ip):
356     local, remote, tunnel = get_vtep_tunnel(remote_ip)
357     if not local or not remote:
358         return None
359
360     if not tunnel:
361         vlog.info("creating tunnel record in vtep for remote_ip:%s"
362                   % remote_ip)
363         tunnel = vtep_ctl("add physical_switch %s tunnels @tun -- "
364                           "--id=@tun create Tunnel local=%s remote=%s"
365                           % (ps_name, local, remote))
366     return tunnel
367
368
369 def destroy_vtep_tunnel(remote_ip):
370     local, remote, tunnel = get_vtep_tunnel(remote_ip)
371     if tunnel:
372         vlog.info("destroying tunnel record in vtep for remote_ip:%s"
373                   % remote_ip)
374         vtep_ctl("remove physical_switch %s tunnels %s "
375                  "-- --if-exists destroy tunnel %s"
376                  % (ps_name, tunnel, tunnel))
377
378
379 def add_bfd(remote_ip):
380     # The VTEP emulator creates one OVS bridge for every logical switch.
381     # Multiple logical switches can have multiple OVS tunnels to the
382     # same machine (with different tunnel ids). But VTEP schema expects
383     # a single BFD session between two physical locators. Therefore
384     # create a separate bridge ('bfd_bridge') and create a single OVS tunnel
385     # between two phsyical locators (using reference counter).
386     if remote_ip in bfd_ref:
387         bfd_ref[remote_ip] += 1
388         return
389
390     vlog.info("adding bfd tunnel for remote_ip:%s" % remote_ip)
391
392     port_name = "bfd" + remote_ip
393     # Don't enable BFD yet. Enabling or disabling BFD is based on
394     # the controller setting a value in VTEP DB's tunnel record.
395     ovs_vsctl("--may-exist add-port %s %s "
396               " -- set Interface %s type=vxlan options:remote_ip=%s"
397               % (bfd_bridge, port_name, port_name, remote_ip))
398     bfd_ref[remote_ip] = 1
399
400     # Ideally, we should create a 'tunnel' record in the VTEP DB here.
401     # To create a 'tunnel' record, we need 2 entries in 'physical_locator'
402     # table (one for local and one for remote). But, 'physical_locator'
403     # can be created/destroyed asynchronously when the remote controller
404     # adds/removes entries in Ucast_Macs_Remote table. To prevent race
405     # conditions, pass the responsibility of creating a 'tunnel' record
406     # to run_bfd() which runs more often.
407
408
409 def del_bfd(remote_ip):
410     if remote_ip in bfd_ref:
411         if bfd_ref[remote_ip] == 1:
412             port_name = "bfd" + remote_ip
413             vlog.info("deleting bfd tunnel for remote_ip:%s" % remote_ip)
414             ovs_vsctl("--if-exists del-port %s" % port_name)
415             destroy_vtep_tunnel(remote_ip)
416             del bfd_ref[remote_ip]
417         else:
418             bfd_ref[remote_ip] -= 1
419
420
421 def run_bfd():
422     bfd_ports = ovs_vsctl("list-ports %s" % bfd_bridge).split()
423     for port in bfd_ports:
424         remote_ip = ovs_vsctl("get interface %s options:remote_ip" % port)
425         tunnel = create_vtep_tunnel(remote_ip)
426         if not tunnel:
427             continue
428
429         bfd_params_default = {'bfd_params:enable': 'false',
430                               'bfd_params:min_rx': 1000,
431                               'bfd_params:min_tx': 100,
432                               'bfd_params:decay_min_rx': 0,
433                               'bfd_params:cpath_down': 'false',
434                               'bfd_params:check_tnl_key': 'false'}
435         bfd_params_values = {}
436
437         for key, default in bfd_params_default.iteritems():
438             column = vtep_ctl("--if-exists get tunnel %s %s"
439                                % (tunnel, key))
440             if not column:
441                 bfd_params_values[key] = default
442             else:
443                 bfd_params_values[key] = column
444
445         for key, value in bfd_params_values.iteritems():
446             new_key = key.replace('_params', '')
447             ovs_vsctl("set interface %s %s=%s" % (port, new_key, value))
448
449         bfd_status = ['bfd_status:state', 'bfd_status:forwarding',
450                       'bfd_status:diagnostic', 'bfd_status:remote_state',
451                       'bfd_status:remote_diagnostic']
452         for key in bfd_status:
453             value = ovs_vsctl("--if-exists get interface %s %s" % (port, key))
454             if value:
455                 vtep_ctl("set tunnel %s %s=%s" % (tunnel, key, value))
456             else:
457                 new_key = key.replace('bfd_status:', '')
458                 vtep_ctl("remove tunnel %s bfd_status %s" % (tunnel, new_key))
459
460         vtep_ctl("set tunnel %s bfd_status:enabled=%s"
461                  % (tunnel, bfd_params_values['bfd_params:enable']))
462
463         # Add the defaults as described in VTEP schema to make it explicit.
464         bfd_lconf_default = {'bfd_config_local:bfd_dst_ip': '169.254.1.0',
465                              'bfd_config_local:bfd_dst_mac':
466                                     '00:23:20:00:00:01'}
467         for key, value in bfd_lconf_default.iteritems():
468             vtep_ctl("set tunnel %s %s=%s" % (tunnel, key, value))
469
470         # bfd_config_remote options from VTEP DB should be populated to
471         # corresponding OVS DB values.
472         bfd_dst_ip = vtep_ctl("--if-exists get tunnel %s "
473                               "bfd_config_remote:bfd_dst_ip" % (tunnel))
474         if not bfd_dst_ip:
475             bfd_dst_ip = "169.254.1.1"
476
477         bfd_dst_mac = vtep_ctl("--if-exists get tunnel %s "
478                               "bfd_config_remote:bfd_dst_mac" % (tunnel))
479         if not bfd_dst_mac:
480             bfd_dst_mac = "00:23:20:00:00:01"
481
482         ovs_vsctl("set interface %s bfd:bfd_dst_ip=%s "
483                   "bfd:bfd_remote_dst_mac=%s bfd:bfd_local_dst_mac=%s"
484                   % (port, bfd_dst_ip,
485                   bfd_lconf_default['bfd_config_local:bfd_dst_mac'],
486                   bfd_dst_mac))
487
488
489 def add_binding(binding, ls):
490     vlog.info("adding binding %s" % binding)
491
492     vlan, pp_name = binding.split("-", 1)
493     pbinding = binding + "-p"
494     lbinding = binding + "-l"
495
496     # Create a patch port that connects the VLAN+port to the lswitch.
497     # Do them as two separate calls so if one side already exists, the
498     # other side is created.
499     ovs_vsctl("add-port %s %s "
500               " -- set Interface %s type=patch options:peer=%s"
501               % (ps_name, pbinding, pbinding, lbinding))
502     ovs_vsctl("add-port %s %s "
503               " -- set Interface %s type=patch options:peer=%s"
504               % (ls.short_name, lbinding, lbinding, pbinding))
505
506     port_no = ovs_vsctl("get Interface %s ofport" % pp_name)
507     patch_no = ovs_vsctl("get Interface %s ofport" % pbinding)
508     vlan_ = vlan.lstrip('0')
509     if vlan_:
510         ovs_ofctl("add-flow %s in_port=%s,dl_vlan=%s,action=strip_vlan,%s"
511                   % (ps_name, port_no, vlan_, patch_no))
512         ovs_ofctl("add-flow %s in_port=%s,action=mod_vlan_vid:%s,%s"
513                   % (ps_name, patch_no, vlan_, port_no))
514     else:
515         ovs_ofctl("add-flow %s in_port=%s,action=%s"
516                   % (ps_name, port_no, patch_no))
517         ovs_ofctl("add-flow %s in_port=%s,action=%s"
518                   % (ps_name, patch_no, port_no))
519
520     # Create a logical_bindings_stats record.
521     if not vlan_:
522         vlan_ = "0"
523     vtep_ctl("set physical_port %s vlan_stats:%s=@stats -- "
524              "--id=@stats create logical_binding_stats packets_from_local=0"
525              % (pp_name, vlan_))
526
527     ls.add_lbinding(lbinding)
528     Bindings[binding] = ls.name
529
530
531 def del_binding(binding, ls):
532     vlog.info("removing binding %s" % binding)
533
534     vlan, pp_name = binding.split("-", 1)
535     pbinding = binding + "-p"
536     lbinding = binding + "-l"
537
538     port_no = ovs_vsctl("get Interface %s ofport" % pp_name)
539     patch_no = ovs_vsctl("get Interface %s ofport" % pbinding)
540     vlan_ = vlan.lstrip('0')
541     if vlan_:
542         ovs_ofctl("del-flows %s in_port=%s,dl_vlan=%s"
543                   % (ps_name, port_no, vlan_))
544         ovs_ofctl("del-flows %s in_port=%s" % (ps_name, patch_no))
545     else:
546         ovs_ofctl("del-flows %s in_port=%s" % (ps_name, port_no))
547         ovs_ofctl("del-flows %s in_port=%s" % (ps_name, patch_no))
548
549     ls.del_lbinding(lbinding)
550
551     # Destroy the patch port that connects the VLAN+port to the lswitch
552     ovs_vsctl("del-port %s %s -- del-port %s %s"
553               % (ps_name, pbinding, ls.short_name, lbinding))
554
555     # Remove the record that links vlan with stats in logical_binding_stats.
556     vtep_ctl("remove physical_port %s vlan_stats %s" % (pp_name, vlan))
557
558     del Bindings[binding]
559
560
561 def handle_physical():
562     # Gather physical ports except the patch ports we created
563     ovs_ports = ovs_vsctl("list-ports %s" % ps_name).split()
564     ovs_port_set = set([port for port in ovs_ports if port[-2:] != "-p"])
565
566     vtep_pp_set = set(vtep_ctl("list-ports %s" % ps_name).split())
567
568     for pp_name in ovs_port_set.difference(vtep_pp_set):
569         vlog.info("adding %s to %s" % (pp_name, ps_name))
570         vtep_ctl("add-port %s %s" % (ps_name, pp_name))
571
572     for pp_name in vtep_pp_set.difference(ovs_port_set):
573         vlog.info("deleting %s from %s" % (pp_name, ps_name))
574         vtep_ctl("del-port %s %s" % (ps_name, pp_name))
575
576     new_bindings = set()
577     for pp_name in vtep_pp_set:
578         binding_set = set(vtep_ctl("list-bindings %s %s"
579                                    % (ps_name, pp_name)).splitlines())
580
581         for b in binding_set:
582             vlan, ls_name = b.split()
583             if ls_name not in Lswitches:
584                 Lswitches[ls_name] = Logical_Switch(ls_name)
585
586             binding = "%s-%s" % (vlan, pp_name)
587             ls = Lswitches[ls_name]
588             new_bindings.add(binding)
589
590             if binding in Bindings:
591                 if Bindings[binding] == ls_name:
592                     continue
593                 else:
594                     del_binding(binding, Lswitches[Bindings[binding]])
595
596             add_binding(binding, ls)
597
598     dead_bindings = set(Bindings.keys()).difference(new_bindings)
599     for binding in dead_bindings:
600         ls_name = Bindings[binding]
601         ls = Lswitches[ls_name]
602
603         del_binding(binding, ls)
604
605         if not len(ls.ports):
606             ls.cleanup_ls()
607             ovs_vsctl("del-br %s" % Lswitches[ls_name].short_name)
608             vtep_ctl("clear-local-macs %s" % Lswitches[ls_name].name)
609             del Lswitches[ls_name]
610
611
612 def setup():
613     br_list = ovs_vsctl("list-br").split()
614     if (ps_name not in br_list):
615         ovs.util.ovs_fatal(0, "couldn't find OVS bridge %s" % ps_name, vlog)
616
617     global ps_type
618     ps_type = ovs_vsctl("get Bridge %s datapath_type" % ps_name).strip('"')
619
620     call_prog("vtep-ctl", ["set", "physical_switch", ps_name,
621                            'description="OVS VTEP Emulator"'])
622
623     tunnel_ips = vtep_ctl("get physical_switch %s tunnel_ips"
624                           % ps_name).strip('[]"').split(", ")
625     if len(tunnel_ips) != 1 or not tunnel_ips[0]:
626         ovs.util.ovs_fatal(0, "exactly one 'tunnel_ips' should be set", vlog)
627
628     global Tunnel_Ip
629     Tunnel_Ip = tunnel_ips[0]
630
631     ovs_ofctl("del-flows %s" % ps_name)
632
633     # Remove any logical bridges from the previous run
634     for br in br_list:
635         if ovs_vsctl("br-get-external-id %s vtep_logical_switch"
636                      % br) == "true":
637             # Remove the remote side of any logical switch
638             ovs_ports = ovs_vsctl("list-ports %s" % br).split()
639             for port in ovs_ports:
640                 port_type = ovs_vsctl("get Interface %s type"
641                                       % port).strip('"')
642                 if port_type != "patch":
643                     continue
644
645                 peer = ovs_vsctl("get Interface %s options:peer"
646                                  % port).strip('"')
647                 if (peer):
648                     ovs_vsctl("del-port %s" % peer)
649
650             ovs_vsctl("del-br %s" % br)
651
652         if br == bfd_bridge:
653             bfd_ports = ovs_vsctl("list-ports %s" % bfd_bridge).split()
654             for port in bfd_ports:
655                 remote_ip = ovs_vsctl("get interface %s options:remote_ip"
656                                       % port)
657                 destroy_vtep_tunnel(remote_ip)
658
659             ovs_vsctl("del-br %s" % br)
660
661     if ps_type:
662         ovs_vsctl("add-br %s -- set Bridge %s datapath_type=%s"
663                   % (bfd_bridge, bfd_bridge, ps_type))
664     else:
665         ovs_vsctl("add-br %s" % bfd_bridge)
666
667     # Remove local-mac entries from the previous run.  Otherwise, if a vlan
668     # binding is removed while the emulator is *not* running, the corresponding
669     # local-mac entries are never cleaned up.
670     vtep_ls = set(vtep_ctl("list-ls").split())
671     for ls_name in vtep_ls:
672         vtep_ctl("clear-local-macs %s" % ls_name)
673
674
675 def main():
676     parser = argparse.ArgumentParser()
677     parser.add_argument("ps_name", metavar="PS-NAME",
678                         help="Name of physical switch.")
679     parser.add_argument("--root-prefix", metavar="DIR",
680                         help="Use DIR as alternate root directory"
681                         " (for testing).")
682     parser.add_argument("--version", action="version",
683                         version="%s %s" % (ovs.util.PROGRAM_NAME, VERSION))
684
685     ovs.vlog.add_args(parser)
686     ovs.daemon.add_args(parser)
687     args = parser.parse_args()
688     ovs.vlog.handle_args(args)
689     ovs.daemon.handle_args(args)
690
691     global root_prefix
692     if args.root_prefix:
693         root_prefix = args.root_prefix
694
695     global ps_name
696     ps_name = args.ps_name
697
698     ovs.daemon.daemonize()
699
700     ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None)
701     error, unixctl = ovs.unixctl.server.UnixctlServer.create(None,
702                                                              version=VERSION)
703     if error:
704         ovs.util.ovs_fatal(error, "could not create unixctl server", vlog)
705
706     setup()
707
708     while True:
709         unixctl.run()
710         if exiting:
711             break
712
713         handle_physical()
714
715         for ls_name, ls in Lswitches.items():
716             ls.run()
717
718         run_bfd()
719
720         poller = ovs.poller.Poller()
721         unixctl.wait(poller)
722         poller.timer_wait(1000)
723         poller.block()
724
725     unixctl.close()
726
727 if __name__ == '__main__':
728     try:
729         main()
730     except SystemExit:
731         # Let system.exit() calls complete normally
732         raise
733     except:
734         vlog.exception("traceback")
735         sys.exit(ovs.daemon.RESTART_EXIT_CODE)