]>
git.proxmox.com Git - ovs.git/blob - vtep/ovs-vtep
2 # Copyright (C) 2013 Nicira, Inc. All Rights Reserved.
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:
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # - Doesn't support multicast other than "unknown-dst"
29 import ovs
.unixctl
.server
31 from six
.moves
import range
39 __pychecker__
= 'no-reuseattr' # Remove in pychecker >= 0.8.19.
40 vlog
= ovs
.vlog
.Vlog("ovs-vtep")
50 bfd_bridge
= "vtep_bfd"
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:
60 output
= output
[0].decode().strip()
65 return call_prog("ovs-vsctl", shlex
.split(args
))
69 return call_prog("ovs-ofctl", shlex
.split(args
))
73 return call_prog("vtep-ctl", shlex
.split(args
))
76 def unixctl_exit(conn
, unused_argv
, unused_aux
):
82 class Logical_Switch(object):
83 def __init__(self
, ls_name
, ps_name
):
87 self
.short_name
= ps_name
+ "_vtep_ls" + str(ls_count
)
88 vlog
.info("creating lswitch %s (%s)" % (self
.name
, self
.short_name
))
91 self
.local_macs
= set()
93 self
.unknown_dsts
= set()
95 self
.replication_mode
= "service_node"
98 vlog
.info("destroying lswitch %s" % self
.name
)
103 ovs_vsctl("--may-exist add-br %s -- set Bridge %s datapath_type=%s"
104 % (self
.short_name
, self
.short_name
, ps_type
))
106 ovs_vsctl("--may-exist add-br %s" % self
.short_name
)
108 ovs_vsctl("br-set-external-id %s vtep_logical_switch true"
110 ovs_vsctl("br-set-external-id %s logical_switch_name %s"
111 % (self
.short_name
, self
.name
))
113 vtep_ctl("clear-local-macs %s" % self
.name
)
114 vtep_ctl("add-mcast-local %s unknown-dst %s" % (self
.name
, Tunnel_Ip
))
116 ovs_ofctl("del-flows %s" % self
.short_name
)
117 ovs_ofctl("add-flow %s priority=0,action=drop" % self
.short_name
)
119 def cleanup_ls(self
):
120 for port_no
, tun_name
, remote_ip
in six
.itervalues(self
.tunnels
):
123 def update_flood(self
):
124 flood_ports
= list(self
.ports
.values())
126 # Traffic flowing from one 'unknown-dst' should not be flooded to
127 # port belonging to another 'unknown-dst'.
128 for tunnel
in self
.unknown_dsts
:
129 port_no
= self
.tunnels
[tunnel
][0]
130 ovs_ofctl("add-flow %s table=1,priority=1,in_port=%s,action=%s"
131 % (self
.short_name
, port_no
, ",".join(flood_ports
)))
133 # Traffic coming from a VTEP physical port should always be flooded to
134 # all the other physical ports that belong to that VTEP device and
135 # this logical switch. If the replication mode is service node then
136 # send to one unknown_dst node (the first one here); else we assume the
137 # replication mode is source node and we send the packet to all
139 for tunnel
in self
.unknown_dsts
:
140 port_no
= self
.tunnels
[tunnel
][0]
141 flood_ports
.append(port_no
)
142 if self
.replication_mode
== "service_node":
145 ovs_ofctl("add-flow %s table=1,priority=0,action=%s"
146 % (self
.short_name
, ",".join(flood_ports
)))
148 def add_lbinding(self
, lbinding
):
149 vlog
.info("adding %s binding to %s" % (lbinding
, self
.name
))
150 port_no
= ovs_vsctl("get Interface %s ofport" % lbinding
)
151 self
.ports
[lbinding
] = port_no
152 ovs_ofctl("add-flow %s in_port=%s,action=learn(table=1,"
153 "priority=1000,idle_timeout=15,cookie=0x5000,"
154 "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],"
155 "output:NXM_OF_IN_PORT[]),resubmit(,1)"
156 % (self
.short_name
, port_no
))
160 def del_lbinding(self
, lbinding
):
161 vlog
.info("removing %s binding from %s" % (lbinding
, self
.name
))
162 port_no
= self
.ports
[lbinding
]
163 ovs_ofctl("del-flows %s in_port=%s" % (self
.short_name
, port_no
))
164 del self
.ports
[lbinding
]
167 def add_tunnel(self
, tunnel
, tunnel_key
):
169 vlog
.info("adding tunnel %s" % tunnel
)
170 encap
, ip
= tunnel
.split("/")
172 if encap
!= "vxlan_over_ipv4":
173 vlog
.warn("unsupported tunnel format %s" % encap
)
177 tun_name
= "vx" + str(tun_id
)
179 ovs_vsctl("add-port %s %s -- set Interface %s type=vxlan "
180 "options:key=%s options:remote_ip=%s"
181 % (self
.short_name
, tun_name
, tun_name
, tunnel_key
, ip
))
184 port_no
= ovs_vsctl("get Interface %s ofport" % tun_name
)
188 vlog
.warn("couldn't create tunnel %s" % tunnel
)
189 ovs_vsctl("del-port %s %s" % (self
.short_name
, tun_name
))
192 # Give the system a moment to allocate the port number
195 self
.tunnels
[tunnel
] = (port_no
, tun_name
, ip
)
199 ovs_ofctl("add-flow %s table=0,priority=1000,in_port=%s,"
200 "actions=resubmit(,1)"
201 % (self
.short_name
, port_no
))
203 def del_tunnel(self
, tunnel
):
204 vlog
.info("removing tunnel %s" % tunnel
)
206 port_no
, tun_name
, remote_ip
= self
.tunnels
[tunnel
]
207 ovs_ofctl("del-flows %s table=0,in_port=%s"
208 % (self
.short_name
, port_no
))
209 ovs_vsctl("del-port %s %s" % (self
.short_name
, tun_name
))
213 del self
.tunnels
[tunnel
]
215 def update_local_macs(self
):
216 flows
= ovs_ofctl("dump-flows %s cookie=0x5000/-1,table=1"
217 % self
.short_name
).splitlines()
220 mac
= re
.split(r
'.*dl_dst=(.*) .*', f
)
224 for mac
in macs
.difference(self
.local_macs
):
225 vlog
.info("adding local ucast %s to %s" % (mac
, self
.name
))
226 vtep_ctl("add-ucast-local %s %s %s" % (self
.name
, mac
, Tunnel_Ip
))
228 for mac
in self
.local_macs
.difference(macs
):
229 vlog
.info("removing local ucast %s from %s" % (mac
, self
.name
))
230 vtep_ctl("del-ucast-local %s %s" % (self
.name
, mac
))
232 self
.local_macs
= macs
234 def add_remote_mac(self
, mac
, tunnel
):
235 port_no
= self
.tunnels
.get(tunnel
, (0, ""))[0]
239 ovs_ofctl("add-flow %s table=1,priority=1000,dl_dst=%s,action=%s"
240 % (self
.short_name
, mac
, port_no
))
242 def del_remote_mac(self
, mac
):
243 ovs_ofctl("del-flows %s table=1,dl_dst=%s" % (self
.short_name
, mac
))
245 def update_remote_macs(self
):
251 column
= vtep_ctl("--columns=tunnel_key find logical_switch "
252 "name=%s" % self
.name
)
253 tunnel_key
= column
.partition(":")[2].strip()
254 if tunnel_key
and isinstance(eval(tunnel_key
), six
.integer_types
):
255 vlog
.info("update_remote_macs: using tunnel key %s in %s"
256 % (tunnel_key
, self
.name
))
258 vlog
.info("Invalid tunnel key %s in %s post VTEP DB requery"
259 % (tunnel_key
, self
.name
))
262 mac_list
= vtep_ctl("list-remote-macs %s" % self
.name
).splitlines()
263 for line
in mac_list
:
264 if (line
.find("mcast-mac-remote") != -1):
268 entry
= re
.split(r
' (.*) -> (.*)', line
)
273 remote_macs
[entry
[1]] = entry
[2]
275 if entry
[1] != "unknown-dst":
278 unknown_dsts
.add(entry
[2])
280 tunnels
.add(entry
[2])
282 old_tunnels
= set(self
.tunnels
.keys())
284 for tunnel
in tunnels
.difference(old_tunnels
):
285 self
.add_tunnel(tunnel
, tunnel_key
)
287 for tunnel
in old_tunnels
.difference(tunnels
):
288 self
.del_tunnel(tunnel
)
290 for mac
in six
.iterkeys(remote_macs
):
291 if (self
.remote_macs
.get(mac
) != remote_macs
[mac
]):
292 self
.add_remote_mac(mac
, remote_macs
[mac
])
294 for mac
in six
.iterkeys(self
.remote_macs
):
295 if mac
not in remote_macs
:
296 self
.del_remote_mac(mac
)
298 self
.remote_macs
= remote_macs
300 replication_mode
= vtep_ctl("get logical_switch %s replication_mode"
303 # Replication mode is an optional column and if it is not set,
304 # replication mode defaults to service_node.
305 if replication_mode
== "[]":
306 replication_mode
= "service_node"
308 # If the logical switch level replication mode has changed then
309 # update to that value.
310 update_flood_set
= False
311 if replication_mode
!= self
.replication_mode
:
312 self
.replication_mode
= replication_mode
313 vlog
.info("%s replication mode changed to %s" %
314 (self
.name
, self
.replication_mode
))
315 update_flood_set
= True
317 if (self
.unknown_dsts
!= unknown_dsts
):
318 self
.unknown_dsts
= unknown_dsts
319 update_flood_set
= True
321 # If either the replication mode has changed or the unknown
322 # destinations set has changed, update the flooding decision.
323 if update_flood_set
is True:
326 def update_stats(self
):
327 # Map Open_vSwitch's "interface:statistics" to columns of
328 # vtep's logical_binding_stats. Since we are using the 'interface' from
329 # the logical switch to collect stats, packets transmitted from it
330 # is received in the physical switch and vice versa.
331 stats_map
= {'tx_packets': 'packets_to_local',
332 'tx_bytes': 'bytes_to_local',
333 'rx_packets': 'packets_from_local',
334 'rx_bytes': 'bytes_from_local'}
336 # Go through all the logical switch's interfaces that end with "-l"
337 # and copy the statistics to logical_binding_stats.
338 for interface
in six
.iterkeys(self
.ports
):
339 if not interface
.endswith("-l"):
341 # Physical ports can have a '-' as part of its name.
342 vlan
, remainder
= interface
.split("-", 1)
343 pp_name
, logical
= remainder
.rsplit("-", 1)
344 uuid
= vtep_ctl("get physical_port %s vlan_stats:%s"
349 for mapfrom
, mapto
in six
.iteritems(stats_map
):
350 value
= ovs_vsctl("get interface %s statistics:%s"
351 % (interface
, mapfrom
)).strip('"')
352 vtep_ctl("set logical_binding_stats %s %s=%s"
353 % (uuid
, mapto
, value
))
356 self
.update_local_macs()
357 self
.update_remote_macs()
361 def get_vtep_tunnel(remote_ip
):
362 # Get the physical_locator record for the local tunnel end point.
363 column
= vtep_ctl("--columns=_uuid find physical_locator "
364 "dst_ip=%s" % Tunnel_Ip
)
365 local
= column
.partition(":")[2].strip()
367 return (None, None, None)
369 # Get the physical_locator record for the remote tunnel end point.
370 column
= vtep_ctl("--columns=_uuid find physical_locator "
371 "dst_ip=%s" % remote_ip
)
372 remote
= column
.partition(":")[2].strip()
374 return (None, None, None)
376 column
= vtep_ctl("--columns=_uuid find tunnel "
377 "local=%s remote=%s" % (local
, remote
))
378 tunnel
= column
.partition(":")[2].strip()
380 return (local
, remote
, tunnel
)
383 def create_vtep_tunnel(remote_ip
):
384 local
, remote
, tunnel
= get_vtep_tunnel(remote_ip
)
385 if not local
or not remote
:
389 vlog
.info("creating tunnel record in vtep for remote_ip:%s"
391 tunnel
= vtep_ctl("add physical_switch %s tunnels @tun -- "
392 "--id=@tun create Tunnel local=%s remote=%s"
393 % (ps_name
, local
, remote
))
397 def destroy_vtep_tunnel(remote_ip
):
398 local
, remote
, tunnel
= get_vtep_tunnel(remote_ip
)
400 vlog
.info("destroying tunnel record in vtep for remote_ip:%s"
402 vtep_ctl("remove physical_switch %s tunnels %s "
403 "-- --if-exists destroy tunnel %s"
404 % (ps_name
, tunnel
, tunnel
))
407 def add_bfd(remote_ip
):
408 # The VTEP emulator creates one OVS bridge for every logical switch.
409 # Multiple logical switches can have multiple OVS tunnels to the
410 # same machine (with different tunnel ids). But VTEP schema expects
411 # a single BFD session between two physical locators. Therefore
412 # create a separate bridge ('bfd_bridge') and create a single OVS tunnel
413 # between two phsyical locators (using reference counter).
414 if remote_ip
in bfd_ref
:
415 bfd_ref
[remote_ip
] += 1
418 vlog
.info("adding bfd tunnel for remote_ip:%s" % remote_ip
)
420 port_name
= "bfd" + remote_ip
421 # Don't enable BFD yet. Enabling or disabling BFD is based on
422 # the controller setting a value in VTEP DB's tunnel record.
423 ovs_vsctl("--may-exist add-port %s %s "
424 " -- set Interface %s type=vxlan options:remote_ip=%s"
425 % (bfd_bridge
, port_name
, port_name
, remote_ip
))
426 bfd_ref
[remote_ip
] = 1
428 # Ideally, we should create a 'tunnel' record in the VTEP DB here.
429 # To create a 'tunnel' record, we need 2 entries in 'physical_locator'
430 # table (one for local and one for remote). But, 'physical_locator'
431 # can be created/destroyed asynchronously when the remote controller
432 # adds/removes entries in Ucast_Macs_Remote table. To prevent race
433 # conditions, pass the responsibility of creating a 'tunnel' record
434 # to run_bfd() which runs more often.
437 def del_bfd(remote_ip
):
438 if remote_ip
in bfd_ref
:
439 if bfd_ref
[remote_ip
] == 1:
440 port_name
= "bfd" + remote_ip
441 vlog
.info("deleting bfd tunnel for remote_ip:%s" % remote_ip
)
442 ovs_vsctl("--if-exists del-port %s" % port_name
)
443 destroy_vtep_tunnel(remote_ip
)
444 del bfd_ref
[remote_ip
]
446 bfd_ref
[remote_ip
] -= 1
450 bfd_ports
= ovs_vsctl("list-ports %s" % bfd_bridge
).split()
451 for port
in bfd_ports
:
452 remote_ip
= ovs_vsctl("get interface %s options:remote_ip" % port
)
453 tunnel
= create_vtep_tunnel(remote_ip
)
457 bfd_params_default
= {'bfd_params:enable': 'false',
458 'bfd_params:min_rx': 1000,
459 'bfd_params:min_tx': 100,
460 'bfd_params:decay_min_rx': 0,
461 'bfd_params:cpath_down': 'false',
462 'bfd_params:check_tnl_key': 'false'}
463 bfd_params_values
= {}
465 for key
, default
in six
.iteritems(bfd_params_default
):
466 column
= vtep_ctl("--if-exists get tunnel %s %s"
469 bfd_params_values
[key
] = default
471 bfd_params_values
[key
] = column
473 for key
, value
in six
.iteritems(bfd_params_values
):
474 new_key
= key
.replace('_params', '')
475 ovs_vsctl("set interface %s %s=%s" % (port
, new_key
, value
))
477 bfd_status
= ['bfd_status:state', 'bfd_status:forwarding',
478 'bfd_status:diagnostic', 'bfd_status:remote_state',
479 'bfd_status:remote_diagnostic']
480 for key
in bfd_status
:
481 value
= ovs_vsctl("--if-exists get interface %s %s" % (port
, key
))
483 vtep_ctl("set tunnel %s %s=%s" % (tunnel
, key
, value
))
485 new_key
= key
.replace('bfd_status:', '')
486 vtep_ctl("remove tunnel %s bfd_status %s" % (tunnel
, new_key
))
488 vtep_ctl("set tunnel %s bfd_status:enabled=%s"
489 % (tunnel
, bfd_params_values
['bfd_params:enable']))
491 # Add the defaults as described in VTEP schema to make it explicit.
492 bfd_lconf_default
= {'bfd_config_local:bfd_dst_ip': '169.254.1.0',
493 'bfd_config_local:bfd_dst_mac':
495 for key
, value
in six
.iteritems(bfd_lconf_default
):
496 vtep_ctl("set tunnel %s %s=%s" % (tunnel
, key
, value
))
498 # bfd_config_remote options from VTEP DB should be populated to
499 # corresponding OVS DB values.
500 bfd_dst_ip
= vtep_ctl("--if-exists get tunnel %s "
501 "bfd_config_remote:bfd_dst_ip" % (tunnel
))
503 bfd_dst_ip
= "169.254.1.1"
505 bfd_dst_mac
= vtep_ctl("--if-exists get tunnel %s "
506 "bfd_config_remote:bfd_dst_mac" % (tunnel
))
508 bfd_dst_mac
= "00:23:20:00:00:01"
510 ovs_vsctl("set interface %s bfd:bfd_dst_ip=%s "
511 "bfd:bfd_remote_dst_mac=%s bfd:bfd_local_dst_mac=%s"
513 bfd_lconf_default
['bfd_config_local:bfd_dst_mac'],
517 def add_binding(binding
, ls
):
518 vlog
.info("adding binding %s" % binding
)
520 vlan
, pp_name
= binding
.split("-", 1)
521 pbinding
= binding
+ "-p"
522 lbinding
= binding
+ "-l"
524 # Create a patch port that connects the VLAN+port to the lswitch.
525 # Do them as two separate calls so if one side already exists, the
526 # other side is created.
527 ovs_vsctl("add-port %s %s "
528 " -- set Interface %s type=patch options:peer=%s"
529 % (ps_name
, pbinding
, pbinding
, lbinding
))
530 ovs_vsctl("add-port %s %s "
531 " -- set Interface %s type=patch options:peer=%s"
532 % (ls
.short_name
, lbinding
, lbinding
, pbinding
))
534 port_no
= ovs_vsctl("get Interface %s ofport" % pp_name
)
535 patch_no
= ovs_vsctl("get Interface %s ofport" % pbinding
)
536 vlan_
= vlan
.lstrip('0')
538 ovs_ofctl("add-flow %s in_port=%s,dl_vlan=%s,action=strip_vlan,%s"
539 % (ps_name
, port_no
, vlan_
, patch_no
))
540 ovs_ofctl("add-flow %s in_port=%s,action=mod_vlan_vid:%s,%s"
541 % (ps_name
, patch_no
, vlan_
, port_no
))
543 ovs_ofctl("add-flow %s in_port=%s,action=%s"
544 % (ps_name
, port_no
, patch_no
))
545 ovs_ofctl("add-flow %s in_port=%s,action=%s"
546 % (ps_name
, patch_no
, port_no
))
548 # Create a logical_bindings_stats record.
551 vtep_ctl("set physical_port %s vlan_stats:%s=@stats -- "
552 "--id=@stats create logical_binding_stats packets_from_local=0"
555 ls
.add_lbinding(lbinding
)
556 Bindings
[binding
] = ls
.name
559 def del_binding(binding
, ls
):
560 vlog
.info("removing binding %s" % binding
)
562 vlan
, pp_name
= binding
.split("-", 1)
563 pbinding
= binding
+ "-p"
564 lbinding
= binding
+ "-l"
566 port_no
= ovs_vsctl("get Interface %s ofport" % pp_name
)
567 patch_no
= ovs_vsctl("get Interface %s ofport" % pbinding
)
568 vlan_
= vlan
.lstrip('0')
570 ovs_ofctl("del-flows %s in_port=%s,dl_vlan=%s"
571 % (ps_name
, port_no
, vlan_
))
572 ovs_ofctl("del-flows %s in_port=%s" % (ps_name
, patch_no
))
574 ovs_ofctl("--strict del-flows %s in_port=%s" % (ps_name
, port_no
))
575 ovs_ofctl("--strict del-flows %s in_port=%s" % (ps_name
, patch_no
))
577 ls
.del_lbinding(lbinding
)
579 # Destroy the patch port that connects the VLAN+port to the lswitch
580 ovs_vsctl("del-port %s %s -- del-port %s %s"
581 % (ps_name
, pbinding
, ls
.short_name
, lbinding
))
583 # Remove the record that links vlan with stats in logical_binding_stats.
584 vtep_ctl("remove physical_port %s vlan_stats %s" % (pp_name
, vlan
))
586 del Bindings
[binding
]
589 def handle_physical():
590 # Gather physical ports except the patch ports we created
591 ovs_ports
= ovs_vsctl("list-ports %s" % ps_name
).split()
592 ovs_port_set
= set([port
for port
in ovs_ports
if port
[-2:] != "-p"])
594 vtep_pp_set
= set(vtep_ctl("list-ports %s" % ps_name
).split())
596 for pp_name
in ovs_port_set
.difference(vtep_pp_set
):
597 vlog
.info("adding %s to %s" % (pp_name
, ps_name
))
598 vtep_ctl("add-port %s %s" % (ps_name
, pp_name
))
600 for pp_name
in vtep_pp_set
.difference(ovs_port_set
):
601 vlog
.info("deleting %s from %s" % (pp_name
, ps_name
))
602 vtep_ctl("del-port %s %s" % (ps_name
, pp_name
))
605 for pp_name
in vtep_pp_set
:
606 binding_set
= set(vtep_ctl("list-bindings %s %s"
607 % (ps_name
, pp_name
)).splitlines())
609 for b
in binding_set
:
610 vlan
, ls_name
= b
.split()
611 if ls_name
not in Lswitches
:
612 Lswitches
[ls_name
] = Logical_Switch(ls_name
, ps_name
)
614 binding
= "%s-%s" % (vlan
, pp_name
)
615 ls
= Lswitches
[ls_name
]
616 new_bindings
.add(binding
)
618 if binding
in Bindings
:
619 if Bindings
[binding
] == ls_name
:
622 del_binding(binding
, Lswitches
[Bindings
[binding
]])
624 add_binding(binding
, ls
)
626 dead_bindings
= set(Bindings
.keys()).difference(new_bindings
)
627 for binding
in dead_bindings
:
628 ls_name
= Bindings
[binding
]
629 ls
= Lswitches
[ls_name
]
631 del_binding(binding
, ls
)
633 if not len(ls
.ports
):
635 ovs_vsctl("del-br %s" % Lswitches
[ls_name
].short_name
)
636 vtep_ctl("clear-local-macs %s" % Lswitches
[ls_name
].name
)
637 del Lswitches
[ls_name
]
641 br_list
= ovs_vsctl("list-br").split()
642 if (ps_name
not in br_list
):
643 ovs
.util
.ovs_fatal(0, "couldn't find OVS bridge %s" % ps_name
, vlog
)
646 ps_type
= ovs_vsctl("get Bridge %s datapath_type" % ps_name
).strip('"')
648 call_prog("vtep-ctl", ["set", "physical_switch", ps_name
,
649 'description="OVS VTEP Emulator"'])
651 tunnel_ips
= vtep_ctl("get physical_switch %s tunnel_ips"
652 % ps_name
).strip('[]"').split(", ")
653 if len(tunnel_ips
) != 1 or not tunnel_ips
[0]:
654 ovs
.util
.ovs_fatal(0, "exactly one 'tunnel_ips' should be set", vlog
)
657 Tunnel_Ip
= tunnel_ips
[0]
659 ovs_ofctl("del-flows %s" % ps_name
)
661 # Remove any logical bridges from the previous run
663 if ovs_vsctl("br-get-external-id %s vtep_logical_switch"
665 # Remove the remote side of any logical switch
666 ovs_ports
= ovs_vsctl("list-ports %s" % br
).split()
667 for port
in ovs_ports
:
668 port_type
= ovs_vsctl("get Interface %s type"
670 if port_type
!= "patch":
673 peer
= ovs_vsctl("get Interface %s options:peer"
676 ovs_vsctl("del-port %s" % peer
)
678 ovs_vsctl("del-br %s" % br
)
681 bfd_ports
= ovs_vsctl("list-ports %s" % bfd_bridge
).split()
682 for port
in bfd_ports
:
683 remote_ip
= ovs_vsctl("get interface %s options:remote_ip"
685 destroy_vtep_tunnel(remote_ip
)
687 ovs_vsctl("del-br %s" % br
)
690 ovs_vsctl("add-br %s -- set Bridge %s datapath_type=%s"
691 % (bfd_bridge
, bfd_bridge
, ps_type
))
693 ovs_vsctl("add-br %s" % bfd_bridge
)
695 # Remove local-mac entries from the previous run. Otherwise, if a vlan
696 # binding is removed while the emulator is *not* running, the corresponding
697 # local-mac entries are never cleaned up.
698 vtep_ls
= set(vtep_ctl("list-ls").split())
699 for ls_name
in vtep_ls
:
700 vtep_ctl("clear-local-macs %s" % ls_name
)
704 parser
= argparse
.ArgumentParser()
705 parser
.add_argument("ps_name", metavar
="PS-NAME",
706 help="Name of physical switch.")
707 parser
.add_argument("--root-prefix", metavar
="DIR",
708 help="Use DIR as alternate root directory"
710 parser
.add_argument("--version", action
="version",
711 version
="%s %s" % (ovs
.util
.PROGRAM_NAME
, VERSION
))
713 ovs
.vlog
.add_args(parser
)
714 ovs
.daemon
.add_args(parser
)
715 args
= parser
.parse_args()
716 ovs
.vlog
.handle_args(args
)
717 ovs
.daemon
.handle_args(args
)
721 root_prefix
= args
.root_prefix
724 ps_name
= args
.ps_name
726 ovs
.daemon
.daemonize()
728 ovs
.unixctl
.command_register("exit", "", 0, 0, unixctl_exit
, None)
729 error
, unixctl
= ovs
.unixctl
.server
.UnixctlServer
.create(None,
732 ovs
.util
.ovs_fatal(error
, "could not create unixctl server", vlog
)
743 for ls_name
, ls
in six
.iteritems(Lswitches
):
748 poller
= ovs
.poller
.Poller()
750 poller
.timer_wait(1000)
755 if __name__
== '__main__':
759 # Let system.exit() calls complete normally
762 vlog
.exception("traceback")
763 sys
.exit(ovs
.daemon
.RESTART_EXIT_CODE
)