]>
git.proxmox.com Git - mirror_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"
30 import ovs
.unixctl
.server
38 __pychecker__
= 'no-reuseattr' # Remove in pychecker >= 0.8.19.
39 vlog
= ovs
.vlog
.Vlog("ovs-vtep")
49 bfd_bridge
= "vtep_bfd"
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:
58 output
= output
[0].strip()
62 return call_prog("ovs-vsctl", shlex
.split(args
))
65 return call_prog("ovs-ofctl", shlex
.split(args
))
68 return call_prog("vtep-ctl", shlex
.split(args
))
71 def unixctl_exit(conn
, unused_argv
, unused_aux
):
77 class Logical_Switch(object):
78 def __init__(self
, ls_name
):
82 self
.short_name
= "vtep_ls" + str(ls_count
)
83 vlog
.info("creating lswitch %s (%s)" % (self
.name
, self
.short_name
))
86 self
.local_macs
= set()
88 self
.unknown_dsts
= set()
93 vlog
.info("destroying lswitch %s" % self
.name
)
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
))
105 vlog
.warn("invalid tunnel key for %s, using 0" % self
.name
)
108 ovs_vsctl("--may-exist add-br %s -- set Bridge %s datapath_type=%s"
109 % (self
.short_name
, self
.short_name
, ps_type
))
111 ovs_vsctl("--may-exist add-br %s" % self
.short_name
)
113 ovs_vsctl("br-set-external-id %s vtep_logical_switch true"
115 ovs_vsctl("br-set-external-id %s logical_switch_name %s"
116 % (self
.short_name
, self
.name
))
118 vtep_ctl("clear-local-macs %s" % self
.name
)
119 vtep_ctl("add-mcast-local %s unknown-dst %s" % (self
.name
, Tunnel_Ip
))
121 ovs_ofctl("del-flows %s" % self
.short_name
)
122 ovs_ofctl("add-flow %s priority=0,action=drop" % self
.short_name
)
124 def cleanup_ls(self
):
125 for port_no
, tun_name
, remote_ip
in self
.tunnels
.itervalues():
128 def update_flood(self
):
129 flood_ports
= self
.ports
.values()
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
)))
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
)
146 ovs_ofctl("add-flow %s table=1,priority=0,action=%s"
147 % (self
.short_name
, ",".join(flood_ports
)))
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
))
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
]
168 def add_tunnel(self
, tunnel
):
170 vlog
.info("adding tunnel %s" % tunnel
)
171 encap
, ip
= tunnel
.split("/")
173 if encap
!= "vxlan_over_ipv4":
174 vlog
.warn("unsupported tunnel format %s" % encap
)
178 tun_name
= "vx" + str(tun_id
)
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
))
185 port_no
= ovs_vsctl("get Interface %s ofport" % tun_name
)
189 vlog
.warn("couldn't create tunnel %s" % tunnel
)
190 ovs_vsctl("del-port %s %s" % (self
.short_name
, tun_name
))
193 # Give the system a moment to allocate the port number
196 self
.tunnels
[tunnel
] = (port_no
, tun_name
, ip
)
200 ovs_ofctl("add-flow %s table=0,priority=1000,in_port=%s,"
201 "actions=resubmit(,1)"
202 % (self
.short_name
, port_no
))
204 def del_tunnel(self
, tunnel
):
205 vlog
.info("removing tunnel %s" % tunnel
)
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
))
214 del self
.tunnels
[tunnel
]
216 def update_local_macs(self
):
217 flows
= ovs_ofctl("dump-flows %s cookie=0x5000/-1,table=1"
218 % self
.short_name
).splitlines()
221 mac
= re
.split(r
'.*dl_dst=(.*) .*', f
)
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
))
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
))
233 self
.local_macs
= macs
235 def add_remote_mac(self
, mac
, tunnel
):
236 port_no
= self
.tunnels
.get(tunnel
, (0,""))[0]
240 ovs_ofctl("add-flow %s table=1,priority=1000,dl_dst=%s,action=%s"
241 % (self
.short_name
, mac
, port_no
))
243 def del_remote_mac(self
, mac
):
244 ovs_ofctl("del-flows %s table=1,dl_dst=%s" % (self
.short_name
, mac
))
246 def update_remote_macs(self
):
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):
258 entry
= re
.split(r
' (.*) -> (.*)', line
)
263 remote_macs
[entry
[1]] = entry
[2]
265 if entry
[1] != "unknown-dst":
268 unknown_dsts
.add(entry
[2])
270 tunnels
.add(entry
[2])
272 old_tunnels
= set(self
.tunnels
.keys())
274 for tunnel
in tunnels
.difference(old_tunnels
):
275 self
.add_tunnel(tunnel
)
277 for tunnel
in old_tunnels
.difference(tunnels
):
278 self
.del_tunnel(tunnel
)
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
])
284 for mac
in self
.remote_macs
.keys():
285 if not remote_macs
.has_key(mac
):
286 self
.del_remote_mac(mac
)
288 self
.remote_macs
= remote_macs
290 if (self
.unknown_dsts
!= unknown_dsts
):
291 self
.unknown_dsts
= unknown_dsts
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'}
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"):
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"
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
))
324 self
.update_local_macs()
325 self
.update_remote_macs()
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()
334 return (None, None, None)
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()
341 return (None, None, None)
343 column
= vtep_ctl("--columns=_uuid find tunnel "
344 "local=%s remote=%s" % (local
, remote
))
345 tunnel
= column
.partition(":")[2].strip()
347 return (local
, remote
, tunnel
)
349 def create_vtep_tunnel(remote_ip
):
350 local
, remote
, tunnel
= get_vtep_tunnel(remote_ip
)
351 if not local
or not remote
:
355 vlog
.info("creating tunnel record in vtep for remote_ip:%s"
357 tunnel
= vtep_ctl("add physical_switch %s tunnels @tun -- "
358 "--id=@tun create Tunnel local=%s remote=%s"
359 %(ps_name
, local
, remote
))
362 def destroy_vtep_tunnel(remote_ip
):
363 local
, remote
, tunnel
= get_vtep_tunnel(remote_ip
)
365 vlog
.info("destroying tunnel record in vtep for remote_ip:%s"
367 vtep_ctl("remove physical_switch %s tunnels %s "
368 "-- --if-exists destroy tunnel %s"
369 % (ps_name
, tunnel
, tunnel
))
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
382 vlog
.info("adding bfd tunnel for remote_ip:%s" % remote_ip
)
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
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.
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
]
409 bfd_ref
[remote_ip
] -= 1
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
)
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
= {}
427 for key
, default
in bfd_params_default
.iteritems():
428 column
= vtep_ctl("--if-exists get tunnel %s %s"
431 bfd_params_values
[key
] = default
433 bfd_params_values
[key
] = column
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
))
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
))
445 vtep_ctl("set tunnel %s %s=%s" %(tunnel
, key
, value
))
447 new_key
= key
.replace('bfd_status:', '')
448 vtep_ctl("remove tunnel %s bfd_status %s" % (tunnel
, new_key
))
450 vtep_ctl("set tunnel %s bfd_status:enabled=%s"
451 % (tunnel
, bfd_params_values
['bfd_params:enable']))
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' :
457 for key
, value
in bfd_lconf_default
.iteritems():
458 vtep_ctl("set tunnel %s %s=%s" %(tunnel
, key
, value
))
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
))
465 bfd_dst_ip
= "169.254.1.1"
467 bfd_dst_mac
= vtep_ctl("--if-exists get tunnel %s "
468 "bfd_config_remote:bfd_dst_mac" % (tunnel
))
470 bfd_dst_mac
= "00:23:20:00:00:01"
472 ovs_vsctl("set interface %s bfd:bfd_dst_ip=%s "
473 "bfd:bfd_remote_dst_mac=%s bfd:bfd_local_dst_mac=%s"
475 bfd_lconf_default
['bfd_config_local:bfd_dst_mac'],
478 def add_binding(binding
, ls
):
479 vlog
.info("adding binding %s" % binding
)
481 vlan
, pp_name
= binding
.split("-", 1)
482 pbinding
= binding
+"-p"
483 lbinding
= binding
+"-l"
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
))
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')
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
))
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
))
509 # Create a logical_bindings_stats record.
512 vtep_ctl("set physical_port %s vlan_stats:%s=@stats --\
513 --id=@stats create logical_binding_stats packets_from_local=0"\
516 ls
.add_lbinding(lbinding
)
517 Bindings
[binding
] = ls
.name
519 def del_binding(binding
, ls
):
520 vlog
.info("removing binding %s" % binding
)
522 vlan
, pp_name
= binding
.split("-", 1)
523 pbinding
= binding
+"-p"
524 lbinding
= binding
+"-l"
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')
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
))
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
))
537 ls
.del_lbinding(lbinding
)
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
))
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
))
546 del Bindings
[binding
]
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"])
553 vtep_pp_set
= set(vtep_ctl("list-ports %s" % ps_name
).split())
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
))
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
))
564 for pp_name
in vtep_pp_set
:
565 binding_set
= set(vtep_ctl("list-bindings %s %s"
566 % (ps_name
, pp_name
)).splitlines())
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
)
573 binding
= "%s-%s" % (vlan
, pp_name
)
574 ls
= Lswitches
[ls_name
]
575 new_bindings
.add(binding
)
577 if Bindings
.has_key(binding
):
578 if Bindings
[binding
] == ls_name
:
581 del_binding(binding
, Lswitches
[Bindings
[binding
]])
583 add_binding(binding
, ls
)
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
]
591 del_binding(binding
, ls
)
593 if not len(ls
.ports
):
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
]
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
)
605 ps_type
= ovs_vsctl("get Bridge %s datapath_type" % ps_name
).strip('"')
607 call_prog("vtep-ctl", ["set", "physical_switch", ps_name
,
608 'description="OVS VTEP Emulator"'])
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
)
616 Tunnel_Ip
= tunnel_ips
[0]
618 ovs_ofctl("del-flows %s" % ps_name
)
620 # Remove any logical bridges from the previous run
622 if ovs_vsctl("br-get-external-id %s vtep_logical_switch"
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"
629 if port_type
!= "patch":
632 peer
= ovs_vsctl("get Interface %s options:peer"
635 ovs_vsctl("del-port %s" % peer
)
637 ovs_vsctl("del-br %s" % br
)
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"
644 tunnel
= destroy_vtep_tunnel(remote_ip
)
646 ovs_vsctl("del-br %s" % br
)
649 ovs_vsctl("add-br %s -- set Bridge %s datapath_type=%s"
650 % (bfd_bridge
, bfd_bridge
, ps_type
))
652 ovs_vsctl("add-br %s" % bfd_bridge
)
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"
662 parser
.add_argument("--version", action
="version",
663 version
="%s %s" % (ovs
.util
.PROGRAM_NAME
, VERSION
))
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
)
673 root_prefix
= args
.root_prefix
676 ps_name
= args
.ps_name
678 ovs
.daemon
.daemonize()
680 ovs
.unixctl
.command_register("exit", "", 0, 0, unixctl_exit
, None)
681 error
, unixctl
= ovs
.unixctl
.server
.UnixctlServer
.create(None,
684 ovs
.util
.ovs_fatal(error
, "could not create unixctl server", vlog
)
695 for ls_name
, ls
in Lswitches
.items():
700 poller
= ovs
.poller
.Poller()
702 poller
.timer_wait(1000)
707 if __name__
== '__main__':
711 # Let system.exit() calls complete normally
714 vlog
.exception("traceback")
715 sys
.exit(ovs
.daemon
.RESTART_EXIT_CODE
)