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