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