2 # Copyright (c) 2009 Nicira Networks. -*- python -*-
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.
28 if argv0.find('/') >= 0:
29 argv0 = argv0[argv0.rfind('/') + 1:]
31 DEFAULT_VSWITCHD_CONF = "@sysconfdir@/ovs-vswitchd.conf"
32 vswitchd_conf = DEFAULT_VSWITCHD_CONF
34 DEFAULT_VSWITCHD_TARGET = "ovs-vswitchd"
35 vswitchd_target = DEFAULT_VSWITCHD_TARGET
37 reload_vswitchd = True
41 class Error(Exception):
42 def __init__(self, msg):
43 Exception.__init__(self)
48 syslog.syslog(message)
50 # XXX Most of the functions below should be integrated into a
51 # VSwitchConfiguration object with logically named fields and methods
52 # instead of this mishmash of functionality.
54 # Locks 'filename' for writing.
55 def cfg_lock(filename):
60 lastSlash = filename.rfind('/')
61 prefix = filename[:lastSlash]
62 suffix = filename[lastSlash + 1:]
63 lock_name = "%s/.%s.~lock~" % (prefix, suffix)
65 lock_name = ".%s.~lock~" % filename
68 # Try to open an existing lock file.
70 f = open(lock_name, 'r')
72 if e.errno != errno.ENOENT:
75 # Try to create a new lock file.
77 fd = os.open(lock_name, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0600)
79 if e.errno != errno.EEXIST:
81 # Someone else created the lock file, try again.
85 fcntl.flock(f, fcntl.LOCK_EX)
88 # Read the ovs-vswitchd.conf file named 'filename' and return its contents as a
89 # dictionary that maps from string keys to lists of string values. (Even
90 # singleton values are represented as lists.)
91 def cfg_read(filename, lock=False):
97 f = open('/dev/stdin')
101 sys.stderr.write("%s: could not open %s (%s)\n"
102 % (argv0, filename, e.strerror))
106 rx = re.compile('([-._@$:+a-zA-Z0-9]+)(?:[ \t\r\n\v]*)=(?:[ \t\r\n\v]*)(.*)$')
109 if len(line) == 0 or line[0] == '#':
112 match = rx.match(line)
116 key, value = match.groups()
119 cfg[key].append(value)
122 orig_cfg = cfg_clone(cfg)
126 # Returns a deep copy of 'cfg', which must be in the format returned
131 new[key] = list(cfg[key])
134 # Returns a list of all the configuration lines that are in 'a' but
136 def cfg_subtract(a, b):
140 if key not in b or value not in b[key]:
141 difference.append("%s=%s" % (key, value))
144 def do_cfg_save(cfg, file):
146 added = cfg_subtract(cfg, orig_cfg)
147 removed = cfg_subtract(orig_cfg, cfg)
149 log("configuration changes:")
156 for key in sorted(cfg.keys()):
157 for value in sorted(cfg[key]):
158 file.write("%s=%s\n" % (key, value))
161 target = vswitchd_target
162 if not target.startswith('/'):
163 pid = read_first_line_of_file('%s/%s.pid' % ('@RUNDIR@', target))
164 target = '%s/%s.%s.ctl' % ('@RUNDIR@', target, pid)
166 if not stat.S_ISSOCK(s.st_mode):
167 raise Error("%s is not a Unix domain socket, cannot reload" % target)
168 skt = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
170 f = os.fdopen(skt.fileno(), "r+")
171 f.write("vswitchd/reload\n")
176 def cfg_save(cfg, filename):
178 do_cfg_save(cfg, sys.stdout)
180 tmp_name = filename + ".~tmp~"
181 f = open(tmp_name, 'w')
184 os.rename(tmp_name, filename)
188 # Returns a set of the immediate subsections of 'section' within 'cfg'. For
189 # example, if 'section' is "bridge" and keys bridge.a, bridge.b, bridge.b.c,
190 # and bridge.c.x.y.z exist, returns set(['a', 'b', 'c']).
191 def cfg_get_subsections(cfg, section):
194 if key.startswith(section + "."):
195 dot = key.find(".", len(section) + 1)
198 subsections.add(key[len(section) + 1:dot])
201 # Returns True if 'cfg' contains a key whose single value is 'true'. Otherwise
203 def cfg_get_bool(cfg, name):
204 return name in cfg and cfg[name] == ['true']
206 # If 'cfg' has a port named 'port' configured with an implicit VLAN, returns
207 # that VLAN number. Otherwise, returns 0.
208 def get_port_vlan(cfg, port):
210 return int(cfg["vlan.%s.tag" % port][0])
211 except (ValueError, KeyError):
214 # Returns all the ports within 'bridge' in 'cfg'. If 'vlan' is nonnegative,
215 # the ports returned are only those configured with implicit VLAN 'vlan'.
216 def get_bridge_ports(cfg, bridge, vlan):
218 for port in cfg["bridge.%s.port" % bridge]:
219 if vlan < 0 or get_port_vlan(cfg, port) == vlan:
223 # Returns all the interfaces within 'bridge' in 'cfg'. If 'vlan' is
224 # nonnegative, the interfaces returned are only those whose ports are
225 # configured with implicit VLAN 'vlan'.
226 def get_bridge_ifaces(cfg, bridge, vlan):
228 for port in get_bridge_ports(cfg, bridge, vlan):
229 ifaces.extend(cfg.get("bonding.%s.slave" % port, [port]))
232 # Returns the first line of the file named 'name', with the trailing new-line
233 # (if any) stripped off.
234 def read_first_line_of_file(name):
237 file = open(name, 'r')
238 return file.readline().rstrip('\n')
243 # Returns a bridge ID constructed from the MAC address of network device
244 # 'netdev', in the format "8000.000102030405".
245 def get_bridge_id(netdev):
247 hwaddr = read_first_line_of_file("/sys/class/net/%s/address" % netdev)
248 return "8000.%s" % (hwaddr.replace(":", ""))
250 return "8000.002320ffffff"
252 # Returns a list of 3-tuples based on 'cfg'. Each 3-tuple represents
253 # one real bridge or one fake bridge and has the form (bridge, parent,
254 # vlan), where 'bridge' is the real or fake bridge name, 'parent' is
255 # the same as 'bridge' for a real bridge or the name of the containing
256 # bridge for a fake bridge, and 'vlan' is 0 for a real bridge or a
257 # VLAN number for a fake bridge.
258 def get_bridge_info(cfg):
259 real_bridges = [(br, br, 0) for br in get_real_bridges(cfg)]
261 for linux_bridge, ovs_bridge, vlan in real_bridges:
262 for iface in get_bridge_ifaces(cfg, ovs_bridge, -1):
263 if cfg_get_bool(cfg, "iface.%s.fake-bridge" % iface):
264 fake_bridges.append((iface, ovs_bridge,
265 get_port_vlan(cfg, iface)))
266 return real_bridges + fake_bridges
268 # Returns the real bridges configured in 'cfg'.
269 def get_real_bridges(cfg):
270 return cfg_get_subsections(cfg, "bridge")
272 # Returns the fake bridges configured in 'cfg'.
273 def get_fake_bridges(cfg):
274 return [bridge for bridge, parent, vlan in get_bridge_info(cfg)
277 # Returns all the real and fake bridges configured in 'cfg'.
278 def get_all_bridges(cfg):
279 return [bridge for bridge, parent, vlan in get_bridge_info(cfg)]
281 # Returns the parent bridge and VLAN of real or fake 'bridge' in
282 # 'cfg', where the parent bridge and VLAN are as defined in the
283 # description of get_bridge_info(). Raises an error if no bridge
284 # named 'bridge' exists in 'cfg'.
285 def find_bridge(cfg, bridge):
286 for br, parent, vlan in get_bridge_info(cfg):
289 raise Error("no bridge named %s" % bridge)
291 def del_matching_keys(cfg, pattern):
292 for key in [key for key in cfg.keys() if fnmatch.fnmatch(key, pattern)]:
295 # Deletes anything related to a port named 'port' from 'cfg'. No port
296 # named 'port' need actually exist; this function will clean up
298 def del_port(cfg, port):
299 # The use of [!0-9] keeps an interface of 'eth0' from matching
300 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
302 for iface in cfg.get('bonding.%s.slave' % port, [port]):
303 del_matching_keys(cfg, 'iface.%s.[!0-9]*' % iface)
304 # Yes, this "port" setting applies to interfaces, not ports, *sigh*.
305 del_matching_keys(cfg, 'port.%s.ingress-policing*' % iface)
306 del_matching_keys(cfg, 'bonding.%s.[!0-9]*' % port)
307 del_matching_keys(cfg, 'vlan.%s.[!0-9]*' % port)
308 for key in cfg.keys():
309 if fnmatch.fnmatch(key, 'bridge.*.port'):
310 cfg[key] = [s for s in cfg[key] if s != port]
312 # Returns the name of the (real or fake) bridge in 'cfg' that contains
313 # port 'port', or None if there is no such port.
314 def port_to_bridge(cfg, port):
315 for bridge, parent, vlan in get_bridge_info(cfg):
316 if port != bridge and port in get_bridge_ports(cfg, parent, vlan):
321 print """%(argv0)s: ovs-vswitchd management utility
322 usage: %(argv0)s [OPTIONS] COMMAND [ARG...]
325 add-br BRIDGE create a new bridge named BRIDGE
326 add-br BRIDGE PARENT VLAN create new fake bridge BRIDGE in PARENT on VLAN
327 del-br BRIDGE delete BRIDGE and all of its ports
328 list-br print the names of all the bridges
329 br-exists BRIDGE test whether BRIDGE exists
330 br-to-vlan BRIDGE print the VLAN which BRIDGE is on
331 br-to-parent BRIDGE print the parent of BRIDGE
334 list-ports BRIDGE print the names of all the ports on BRIDGE
335 add-port BRIDGE PORT add network device PORT to BRIDGE
336 add-bond BRIDGE PORT IFACE... add new bonded port PORT in BRIDGE from IFACES
337 del-port [BRIDGE] PORT delete PORT (which may be bonded) from BRIDGE
338 port-to-br PORT print name of bridge that contains PORT
339 A bond is considered to be a single port.
341 Interface commands (a bond consists of multiple interfaces):
342 list-ifaces BRIDGE print the names of all the interfaces on BRIDGE
343 iface-to-br IFACE print name of bridge that contains IFACE
344 A bond is considered to consist of interfaces.
347 --no-syslog do not write mesages to syslog
348 -c, --config=FILE set configuration file
349 (default: %(config)s)
350 -t, --target=PROGRAM|SOCKET set ovs-vswitchd target
351 (default: %(target)s)
352 --no-reload do not make ovs-vswitchd reload its configuration
353 -h, --help display this help message and exit
354 -V, --version display version information and exit
355 Report bugs to bugs@openvswitch.org.""" % {'argv0': argv0,
356 'config': DEFAULT_VSWITCHD_CONF,
357 'target': DEFAULT_VSWITCHD_TARGET}
361 print "ovs-vsctl (Open vSwitch) @VERSION@"
364 def check_conflicts(cfg, name, op):
365 bridges = get_bridge_info(cfg)
366 if name in [bridge for bridge, parent, vlan in bridges]:
367 raise Error("%s because a bridge named %s already exists" % (op, name))
369 for bridge, parent, vlan in bridges:
370 if name in get_bridge_ports(cfg, parent, vlan):
371 raise Error("%s because a port named %s already exists on bridge %s" % (op, name, bridge))
372 if name in get_bridge_ifaces(cfg, parent, vlan):
373 raise Error("%s because an interface named %s already exists on bridge %s" % (op, name, bridge))
375 def cmd_add_br(cfg, bridge, parent=None, vlan=None):
376 check_conflicts(cfg, bridge, "cannot create a bridge named %s" % bridge)
379 if parent in get_fake_bridges(cfg):
380 raise Error("cannot create bridge with fake bridge as parent")
381 if parent not in get_real_bridges(cfg):
382 raise Error("parent bridge %s does not exist" % bridge)
384 if int(vlan) < 0 or int(vlan) > 4095:
387 raise Error("invalid VLAN number %s" % vlan)
389 # Create fake bridge internal port.
390 cfg['iface.%s.internal' % bridge] = ['true']
391 cfg['iface.%s.fake-bridge' % bridge] = ['true']
392 cfg['vlan.%s.tag' % bridge] = [vlan]
394 # Add fake bridge port to parent.
395 cfg['bridge.%s.port' % parent].append(bridge)
397 cfg['bridge.%s.port' % bridge] = [bridge]
399 def cmd_del_br(cfg, bridge):
400 parent, vlan = find_bridge(cfg, bridge)
403 for port in set(get_bridge_ports(cfg, parent, vlan) + [bridge]):
406 del_matching_keys(cfg, 'bridge.%s.[!0-9]*' % bridge)
408 def cmd_list_br(cfg):
409 return get_all_bridges(cfg)
411 def cmd_br_exists(cfg, bridge):
412 if bridge not in get_all_bridges(cfg):
415 def cmd_list_ports(cfg, bridge):
417 parent, vlan = find_bridge(cfg, bridge)
418 for port in get_bridge_ports(cfg, parent, vlan):
423 def do_add_port(cfg, bridge, parent, port, vlan):
424 check_conflicts(cfg, port, "cannot create a port named %s" % port)
425 cfg['bridge.%s.port' % parent].append(port)
427 cfg['vlan.%s.tag' % port] = [vlan]
429 def cmd_add_port(cfg, bridge, port):
430 parent, vlan = find_bridge(cfg, bridge)
431 do_add_port(cfg, bridge, parent, port, vlan)
433 def cmd_add_bond(cfg, bridge, port, *slaves):
434 parent, vlan = find_bridge(cfg, bridge)
435 do_add_port(cfg, bridge, parent, port, vlan)
436 cfg['bonding.%s.slave' % port] = list(slaves)
438 def cmd_del_port(cfg, *args):
441 parent, vlan = find_bridge(cfg, bridge)
442 if port not in get_bridge_ports(cfg, parent, vlan):
443 if port in get_bridge_ports(cfg, parent, -1):
444 raise Error("bridge %s does not have a port %s (although its parent bridge %s does)" % (bridge, port, parent))
446 raise Error("bridge %s does not have a port %s" % (bridge, port))
449 if not port_to_bridge(cfg, port):
450 raise Error("no port %s on any bridge" % port)
453 def cmd_port_to_br(cfg, port):
454 bridge = port_to_bridge(cfg, port)
458 raise Error("no port named %s" % port)
460 def cmd_list_ifaces(cfg, bridge):
462 parent, vlan = find_bridge(cfg, bridge)
463 for iface in get_bridge_ifaces(cfg, parent, vlan):
468 def cmd_iface_to_br(cfg, iface):
469 for bridge, parent, vlan in get_bridge_info(cfg):
470 if iface != bridge and iface in get_bridge_ifaces(cfg, parent, vlan):
472 raise Error("no interface named %s" % iface)
474 def cmd_br_to_vlan(cfg, bridge):
475 parent, vlan = find_bridge(cfg, bridge)
478 def cmd_br_to_parent(cfg, bridge):
479 parent, vlan = find_bridge(cfg, bridge)
482 cmdTable = {'add-br': (cmd_add_br, True, lambda n: n == 1 or n == 3),
483 'del-br': (cmd_del_br, True, 1),
484 'list-br': (cmd_list_br, False, 0),
485 'br-exists': (cmd_br_exists, False, 1),
486 'list-ports': (cmd_list_ports, False, 1),
487 'add-port': (cmd_add_port, True, 2),
488 'add-bond': (cmd_add_bond, True, lambda n: n >= 4),
489 'del-port': (cmd_del_port, True, lambda n: n == 1 or n == 2),
490 'port-to-br': (cmd_port_to_br, False, 1),
491 'br-to-vlan': (cmd_br_to_vlan, False, 1),
492 'br-to-parent': (cmd_br_to_parent, False, 1),
493 'list-ifaces': (cmd_list_ifaces, False, 1),
494 'iface-to-br': (cmd_iface_to_br, False, 1)}
496 # Break up commands at -- boundaries.
497 def split_commands(args):
503 commands.append(command)
508 commands.append(command)
511 def check_command(args):
512 command, args = args[0], args[1:]
513 if command not in cmdTable:
514 sys.stderr.write("%s: unknown command '%s' (use --help for help)\n"
518 function, is_mutator, nargs = cmdTable[command]
519 if callable(nargs) and not nargs(len(args)):
520 sys.stderr.write("%s: '%s' command does not accept %d arguments (use --help for help)\n" % (argv0, command, len(args)))
522 elif not callable(nargs) and len(args) != nargs:
523 sys.stderr.write("%s: '%s' command takes %d arguments but %d were supplied (use --help for help)\n" % (argv0, command, nargs, len(args)))
526 def run_command(cfg, args):
527 command, args = args[0], args[1:]
528 function, need_lock, nargs = cmdTable[command]
529 return function(cfg, *args)
532 # Parse command line.
534 options, args = getopt.getopt(sys.argv[1:], "c:t:hV",
542 except getopt.GetoptError, msg:
543 sys.stderr.write("%s: %s (use --help for help)\n" % (argv0, msg))
548 for opt, optarg in options:
549 if opt == "-c" or opt == "--config":
551 vswitchd_conf = optarg
552 elif opt == "-t" or opt == "--target":
553 global vswitchd_target
554 vswitchd_target = optarg
555 elif opt == "--no-reload":
556 global reload_vswitchd
557 reload_vswitchd = False
558 elif opt == "-h" or opt == "--help":
560 elif opt == "-V" or opt == "--version":
562 elif opt == "--no-syslog":
564 enable_syslog = False
565 elif opt == "--oneline":
568 raise RuntimeError("unhandled option %s" % opt)
571 syslog.openlog("ovs-vsctl")
572 log("Called as %s" % ' '.join(sys.argv[1:]))
574 # Break arguments into a series of commands.
575 commands = split_commands(args)
577 sys.stderr.write("%s: missing command name (use --help for help)\n"
581 # Check command syntax.
583 for command in commands:
584 check_command(command)
585 if cmdTable[command[0]][1]:
589 cfg = cfg_read(vswitchd_conf, need_lock)
590 for command in commands:
591 output = run_command(cfg, command)
595 print '\\n'.join([str(s).replace('\\', '\\\\')
601 cfg_save(cfg, vswitchd_conf)
604 if __name__ == "__main__":
608 sys.stderr.write("%s: %s\n" % (argv0, msg.msg))