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