]> git.proxmox.com Git - mirror_ovs.git/blame - vtep/ovs-vtep
python: Fix invalid varable/function references.
[mirror_ovs.git] / vtep / ovs-vtep
CommitLineData
f6783a7a 1#! /usr/bin/env python
40791399
JP
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
19import argparse
20import re
260f76b7 21import shlex
40791399
JP
22import subprocess
23import sys
24import time
25import types
26
27import ovs.dirs
28import ovs.util
29import ovs.daemon
30import ovs.unixctl.server
31import ovs.vlog
32
33
34VERSION = "0.99"
35
36root_prefix = ""
37
38__pychecker__ = 'no-reuseattr' # Remove in pychecker >= 0.8.19.
39vlog = ovs.vlog.Vlog("ovs-vtep")
40exiting = False
41
cedb277b 42ps_name = ""
69f8f7e1 43ps_type = ""
40791399
JP
44Tunnel_Ip = ""
45Lswitches = {}
46Bindings = {}
47ls_count = 0
48tun_id = 0
91902638
GS
49bfd_bridge = "vtep_bfd"
50bfd_ref = {}
40791399
JP
51
52def 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
61def ovs_vsctl(args):
260f76b7 62 return call_prog("ovs-vsctl", shlex.split(args))
40791399
JP
63
64def ovs_ofctl(args):
260f76b7 65 return call_prog("ovs-ofctl", shlex.split(args))
40791399
JP
66
67def vtep_ctl(args):
260f76b7 68 return call_prog("vtep-ctl", shlex.split(args))
40791399
JP
69
70
71def unixctl_exit(conn, unused_argv, unused_aux):
72 global exiting
73 exiting = True
74 conn.reply(None)
75
76
77class 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
69f8f7e1
DDP
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
40791399
JP
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
91902638
GS
124 def cleanup_ls(self):
125 for port_no, tun_name, remote_ip in self.tunnels.itervalues():
126 del_bfd(remote_ip)
127
40791399
JP
128 def update_flood(self):
129 flood_ports = self.ports.values()
130
36b87338
GS
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.
40791399
JP
141 for tunnel in self.unknown_dsts:
142 port_no = self.tunnels[tunnel][0]
143 flood_ports.append(port_no)
36b87338 144 break
40791399
JP
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
91902638
GS
196 self.tunnels[tunnel] = (port_no, tun_name, ip)
197
198 add_bfd(ip)
40791399
JP
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
91902638 207 port_no, tun_name, remote_ip = self.tunnels[tunnel]
40791399
JP
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
91902638
GS
212 del_bfd(remote_ip)
213
40791399
JP
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
0bd87945
GS
309 # Physical ports can have a '-' as part of its name.
310 vlan, remainder = interface.split("-", 1)
311 pp_name, logical = remainder.rsplit("-", 1)
40791399
JP
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
91902638
GS
328def 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
349def 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
362def 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
371def 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
400def 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
411def 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
cedb277b 478def add_binding(binding, ls):
40791399
JP
479 vlog.info("adding binding %s" % binding)
480
0bd87945 481 vlan, pp_name = binding.split("-", 1)
40791399
JP
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
cedb277b 519def del_binding(binding, ls):
40791399
JP
520 vlog.info("removing binding %s" % binding)
521
5aa5d00e 522 vlan, pp_name = binding.split("-", 1)
40791399
JP
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
cedb277b 548def handle_physical():
40791399
JP
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:
cedb277b 581 del_binding(binding, Lswitches[Bindings[binding]])
40791399 582
cedb277b 583 add_binding(binding, ls)
40791399
JP
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
cedb277b 591 del_binding(binding, ls)
40791399
JP
592
593 if not len(ls.ports):
91902638 594 ls.cleanup_ls()
40791399 595 ovs_vsctl("del-br %s" % Lswitches[ls_name].short_name)
5a2ea6b5 596 vtep_ctl("clear-local-macs %s" % Lswitches[ls_name].name)
40791399
JP
597 del Lswitches[ls_name]
598
cedb277b 599def setup():
40791399
JP
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
69f8f7e1
DDP
604 global ps_type
605 ps_type = ovs_vsctl("get Bridge %s datapath_type" % ps_name).strip('"')
606
40791399
JP
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
91902638
GS
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)
5697ca99 644 destroy_vtep_tunnel(remote_ip)
91902638
GS
645
646 ovs_vsctl("del-br %s" % br)
647
69f8f7e1
DDP
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)
91902638 653
978534e1
AC
654 # Remove local-mac entries from the previous run. Otherwise, if a vlan
655 # binding is removed while the emulator is *not* running, the corresponding
656 # local-mac entries are never cleaned up.
657 vtep_ls = set(vtep_ctl("list-ls").split())
658 for ls_name in vtep_ls:
659 vtep_ctl("clear-local-macs %s" % ls_name)
660
40791399
JP
661
662def main():
663 parser = argparse.ArgumentParser()
664 parser.add_argument("ps_name", metavar="PS-NAME",
665 help="Name of physical switch.")
666 parser.add_argument("--root-prefix", metavar="DIR",
667 help="Use DIR as alternate root directory"
668 " (for testing).")
669 parser.add_argument("--version", action="version",
670 version="%s %s" % (ovs.util.PROGRAM_NAME, VERSION))
671
672 ovs.vlog.add_args(parser)
673 ovs.daemon.add_args(parser)
674 args = parser.parse_args()
675 ovs.vlog.handle_args(args)
676 ovs.daemon.handle_args(args)
677
678 global root_prefix
679 if args.root_prefix:
680 root_prefix = args.root_prefix
681
cedb277b 682 global ps_name
40791399
JP
683 ps_name = args.ps_name
684
685 ovs.daemon.daemonize()
686
687 ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None)
688 error, unixctl = ovs.unixctl.server.UnixctlServer.create(None,
689 version=VERSION)
690 if error:
691 ovs.util.ovs_fatal(error, "could not create unixctl server", vlog)
692
cedb277b 693 setup()
40791399
JP
694
695 while True:
696 unixctl.run()
697 if exiting:
698 break
699
cedb277b 700 handle_physical()
40791399
JP
701
702 for ls_name, ls in Lswitches.items():
703 ls.run()
704
91902638
GS
705 run_bfd()
706
40791399
JP
707 poller = ovs.poller.Poller()
708 unixctl.wait(poller)
709 poller.timer_wait(1000)
710 poller.block()
711
712 unixctl.close()
713
714if __name__ == '__main__':
715 try:
716 main()
717 except SystemExit:
718 # Let system.exit() calls complete normally
719 raise
720 except:
721 vlog.exception("traceback")
722 sys.exit(ovs.daemon.RESTART_EXIT_CODE)