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