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.
26 if argv0.find('/') >= 0:
27 argv0 = argv0[argv0.rfind('/') + 1:]
29 DEFAULT_VSWITCHD_CONF = "@sysconfdir@/ovs-vswitchd.conf"
30 VSWITCHD_CONF = DEFAULT_VSWITCHD_CONF
32 DEFAULT_VSWITCHD_TARGET = "@RUNDIR@/ovs-vswitchd.pid"
33 VSWITCHD_TARGET = DEFAULT_VSWITCHD_TARGET
35 RELOAD_VSWITCHD = True
37 class Error(Exception):
38 def __init__(self, msg):
39 Exception.__init__(self)
42 # XXX Most of the functions below should be integrated into a
43 # VSwitchConfiguration object with logically named fields and methods
44 # instead of this mishmash of functionality.
46 # Locks 'filename' for writing.
47 def cfg_lock(filename):
52 lastSlash = filename.rfind('/')
53 prefix = filename[:lastSlash]
54 suffix = filename[lastSlash + 1:]
55 lock_name = "%s/.%s.~lock~" % (prefix, suffix)
57 lock_name = ".%s.~lock~" % filename
60 # Try to open an existing lock file.
62 f = open(lock_name, 'r')
64 if e.errno != errno.ENOENT:
67 # Try to create a new lock file.
69 fd = os.open(lock_name, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0600)
71 if e.errno != errno.EEXIST:
73 # Someone else created the lock file, try again.
77 fcntl.flock(f, fcntl.LOCK_EX)
80 # Read the ovs-vswitchd.conf file named 'filename' and return its contents as a
81 # dictionary that maps from string keys to lists of string values. (Even
82 # singleton values are represented as lists.)
83 def cfg_read(filename, lock=False):
89 f = open('/dev/stdin')
93 sys.stderr.write("%s: could not open %s (%s)\n"
94 % (argv0, filename, e.strerror))
98 rx = re.compile('([-._@$:+a-zA-Z0-9]+)(?:[ \t\r\n\v]*)=(?:[ \t\r\n\v]*)(.*)$')
101 if len(line) == 0 or line[0] == '#':
104 match = rx.match(line)
108 key, value = match.groups()
111 cfg[key].append(value)
114 def do_cfg_save(cfg, file):
115 for key in sorted(cfg.keys()):
116 for value in sorted(cfg[key]):
117 file.write("%s=%s\n" % (key, value))
120 target = VSWITCHD_TARGET
122 if stat.S_ISREG(s.st_mode):
123 pid = read_first_line_of_file(target)
124 target = "@RUNDIR@/ovs-vswitchd.%s.ctl" % pid
126 if not stat.S_ISSOCK(s.st_mode):
127 raise Error("%s is not a Unix domain socket, cannot reload" % target)
128 f = open(target, "r+")
129 f.write("vswitchd/reload\n")
134 def cfg_save(cfg, filename):
136 do_cfg_save(cfg, sys.stdout)
138 tmp_name = filename + ".~tmp~"
139 f = open(tmp_name, 'w')
142 os.rename(tmp_name, filename)
146 # Returns a set of the immediate subsections of 'section' within 'cfg'. For
147 # example, if 'section' is "bridge" and keys bridge.a, bridge.b, bridge.b.c,
148 # and bridge.c.x.y.z exist, returns set(['a', 'b', 'c']).
149 def cfg_get_subsections(cfg, section):
152 if key.startswith(section + "."):
153 dot = key.find(".", len(section) + 1)
156 subsections.add(key[len(section) + 1:dot])
159 # Returns True if 'cfg' contains a key whose single value is 'true'. Otherwise
161 def cfg_get_bool(cfg, name):
162 return name in cfg and cfg[name] == ['true']
164 # If 'cfg' has a port named 'port' configured with an implicit VLAN, returns
165 # that VLAN number. Otherwise, returns 0.
166 def get_port_vlan(cfg, port):
168 return int(cfg["vlan.%s.tag" % port][0])
169 except (ValueError, KeyError):
172 # Returns all the ports within 'bridge' in 'cfg'. If 'vlan' is nonnegative,
173 # the ports returned are only those configured with implicit VLAN 'vlan'.
174 def get_bridge_ports(cfg, bridge, vlan):
176 for port in cfg["bridge.%s.port" % bridge]:
177 if vlan < 0 or get_port_vlan(cfg, port) == vlan:
181 # Returns all the interfaces within 'bridge' in 'cfg'. If 'vlan' is
182 # nonnegative, the interfaces returned are only those whose ports are
183 # configured with implicit VLAN 'vlan'.
184 def get_bridge_ifaces(cfg, bridge, vlan):
186 for port in get_bridge_ports(cfg, bridge, vlan):
187 ifaces.extend(cfg.get("bonding.%s.slave" % port, [port]))
190 # Returns the first line of the file named 'name', with the trailing new-line
191 # (if any) stripped off.
192 def read_first_line_of_file(name):
195 file = open(name, 'r')
196 return file.readline().rstrip('\n')
201 # Returns a bridge ID constructed from the MAC address of network device
202 # 'netdev', in the format "8000.000102030405".
203 def get_bridge_id(netdev):
205 hwaddr = read_first_line_of_file("/sys/class/net/%s/address" % netdev)
206 return "8000.%s" % (hwaddr.replace(":", ""))
208 return "8000.002320ffffff"
210 # Returns a list of 3-tuples based on 'cfg'. Each 3-tuple represents
211 # one real bridge or one fake bridge and has the form (bridge, parent,
212 # vlan), where 'bridge' is the real or fake bridge name, 'parent' is
213 # the same as 'bridge' for a real bridge or the name of the containing
214 # bridge for a fake bridge, and 'vlan' is 0 for a real bridge or a
215 # VLAN number for a fake bridge.
216 def get_bridge_info(cfg):
217 real_bridges = [(br, br, 0) for br in get_real_bridges(cfg)]
219 for linux_bridge, ovs_bridge, vlan in real_bridges:
220 for iface in get_bridge_ifaces(cfg, ovs_bridge, -1):
221 if cfg_get_bool(cfg, "iface.%s.fake-bridge" % iface):
222 fake_bridges.append((iface, ovs_bridge,
223 get_port_vlan(cfg, iface)))
224 return real_bridges + fake_bridges
226 # Returns the real bridges configured in 'cfg'.
227 def get_real_bridges(cfg):
228 return cfg_get_subsections(cfg, "bridge")
230 # Returns the fake bridges configured in 'cfg'.
231 def get_fake_bridges(cfg):
232 return [bridge for bridge, parent, vlan in get_bridge_info(cfg)
235 # Returns all the real and fake bridges configured in 'cfg'.
236 def get_all_bridges(cfg):
237 return [bridge for bridge, parent, vlan in get_bridge_info(cfg)]
239 # Returns the parent bridge and VLAN of real or fake 'bridge' in
240 # 'cfg', where the parent bridge and VLAN are as defined in the
241 # description of get_bridge_info(). Raises an error if no bridge
242 # named 'bridge' exists in 'cfg'.
243 def find_bridge(cfg, bridge):
244 for br, parent, vlan in get_bridge_info(cfg):
247 raise Error("no bridge named %s" % bridge)
249 def del_matching_keys(cfg, pattern):
250 for key in [key for key in cfg.keys() if fnmatch.fnmatch(key, pattern)]:
253 # Deletes anything related to a port named 'port' from 'cfg'. No port
254 # named 'port' need actually exist; this function will clean up
256 def del_port(cfg, port):
257 # The use of [!0-9] keeps an interface of 'eth0' from matching
258 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
260 for iface in cfg.get('bonding.%s.slave' % port, [port]):
261 del_matching_keys(cfg, 'iface.%s.[!0-9]*' % iface)
262 # Yes, this "port" setting applies to interfaces, not ports, *sigh*.
263 del_matching_keys(cfg, 'port.%s.ingress-policing*' % iface)
264 del_matching_keys(cfg, 'bonding.%s.[!0-9]*' % port)
265 del_matching_keys(cfg, 'vlan.%s.[!0-9]*' % port)
266 for key in cfg.keys():
267 if fnmatch.fnmatch(key, 'bridge.*.port'):
268 cfg[key] = [s for s in cfg[key] if s != port]
271 print """%(argv0)s: ovs-vswitchd management utility
272 usage: %(argv0)s [OPTIONS] COMMAND [ARG...]
275 add-br BRIDGE create a new bridge named BRIDGE
276 add-br BRIDGE PARENT VLAN create new fake bridge BRIDGE in PARENT on VLAN
277 del-br BRIDGE delete BRIDGE and all of its ports
278 list-br print the names of all the bridges
279 br-exists BRIDGE test whether BRIDGE exists
282 list-ports BRIDGE print the names of all the ports on BRIDGE
283 add-port BRIDGE PORT add network device PORT to BRIDGE
284 add-bond BRIDGE PORT IFACE... add new bonded port PORT in BRIDGE from IFACES
285 del-port BRIDGE PORT delete PORT (which may be bonded) from BRIDGE
286 port-to-br PORT print name of bridge that contains PORT
287 A bond is considered to be a single port.
289 Interface commands (a bond consists of multiple interfaces):
290 list-ifaces BRIDGE print the names of all the interfaces on BRIDGE
291 iface-to-br IFACE print name of bridge that contains IFACE
292 A bond is considered to consist of interfaces.
295 -c, --config=FILE set configuration file
296 (default: %(config)s)
297 -t, --target=PIDFILE|SOCKET set ovs-vswitchd target
298 (default: %(target)s)
299 --no-reload do not make ovs-vswitchd reload its configuration
300 -h, --help display this help message and exit
301 -V, --version display version information and exit
302 Report bugs to bugs@openvswitch.org.""" % {'argv0': argv0,
303 'config': DEFAULT_VSWITCHD_CONF,
304 'target': DEFAULT_VSWITCHD_TARGET}
308 print "ovs-vsctl (Open vSwitch) @VERSION@"
311 def check_conflicts(cfg, name, op):
312 bridges = get_bridge_info(cfg)
313 if name in [bridge for bridge, parent, vlan in bridges]:
314 raise Error("%s because a bridge named %s already exists" % (op, name))
316 for bridge, parent, vlan in bridges:
317 if name in get_bridge_ports(cfg, parent, vlan):
318 raise Error("%s because a port named %s already exists on bridge %s" % (op, name, bridge))
319 if name in get_bridge_ifaces(cfg, parent, vlan):
320 raise Error("%s because an interface named %s already exists on bridge %s" % (op, name, bridge))
322 def cmd_add_br(bridge, parent=None, vlan=None):
323 cfg = cfg_read(VSWITCHD_CONF, True)
325 check_conflicts(cfg, bridge, "cannot create a bridge named %s" % bridge)
328 if parent in get_fake_bridges(cfg):
329 raise Error("cannot create bridge with fake bridge as parent")
330 if parent not in get_real_bridges(cfg):
331 raise Error("parent bridge %s does not exist" % bridge)
333 if int(vlan) < 0 or int(vlan) > 4095:
336 raise Error("invalid VLAN number %s" % vlan)
338 # Create fake bridge internal port.
339 cfg['iface.%s.internal' % bridge] = ['true']
340 cfg['iface.%s.fake-bridge' % bridge] = ['true']
341 cfg['vlan.%s.tag' % bridge] = [vlan]
343 # Add fake bridge port to parent.
344 cfg['bridge.%s.port' % parent].append(bridge)
346 cfg['bridge.%s.port' % bridge] = [bridge]
347 cfg_save(cfg, VSWITCHD_CONF)
349 def cmd_del_br(bridge):
350 cfg = cfg_read(VSWITCHD_CONF, True)
351 parent, vlan = find_bridge(cfg, bridge)
354 for port in set(get_bridge_ports(cfg, parent, vlan) + [bridge]):
357 del_matching_keys(cfg, 'bridge.%s.[!0-9]*' % bridge)
358 cfg_save(cfg, VSWITCHD_CONF)
361 cfg = cfg_read(VSWITCHD_CONF)
362 for bridge in get_all_bridges(cfg):
365 def cmd_br_exists(bridge):
366 cfg = cfg_read(VSWITCHD_CONF)
367 if bridge not in get_all_bridges(cfg):
370 def cmd_list_ports(bridge):
371 cfg = cfg_read(VSWITCHD_CONF)
372 parent, vlan = find_bridge(cfg, bridge)
373 for port in get_bridge_ports(cfg, parent, vlan):
377 def do_add_port(cfg, bridge, parent, port, vlan):
378 check_conflicts(cfg, port, "cannot create a port named %s" % port)
379 cfg['bridge.%s.port' % parent].append(port)
381 cfg['vlan.%s.tag' % port] = [vlan]
383 def cmd_add_port(bridge, port):
384 cfg = cfg_read(VSWITCHD_CONF, True)
385 parent, vlan = find_bridge(cfg, bridge)
386 do_add_port(cfg, bridge, parent, port, vlan)
387 cfg_save(cfg, VSWITCHD_CONF)
389 def cmd_add_bond(bridge, port, *slaves):
390 cfg = cfg_read(VSWITCHD_CONF, True)
391 parent, vlan = find_bridge(cfg, bridge)
392 do_add_port(cfg, bridge, parent, port, vlan)
393 cfg['bonding.%s.slave' % port] = list(slaves)
394 cfg_save(cfg, VSWITCHD_CONF)
396 def cmd_del_port(bridge, port):
397 cfg = cfg_read(VSWITCHD_CONF, True)
398 parent, vlan = find_bridge(cfg, bridge)
399 if port not in get_bridge_ports(cfg, parent, vlan):
400 if port in get_bridge_ports(cfg, parent, -1):
401 raise Error("bridge %s does not have a port %s (although its parent bridge %s does)" % (bridge, port, parent))
403 raise Error("bridge %s does not have a port %s" % (bridge, port))
405 cfg_save(cfg, VSWITCHD_CONF)
407 def cmd_port_to_br(port):
408 cfg = cfg_read(VSWITCHD_CONF)
409 for bridge, parent, vlan in get_bridge_info(cfg):
410 if port != bridge and port in get_bridge_ports(cfg, parent, vlan):
413 raise Error("no port named %s" % port)
415 def cmd_list_ifaces(bridge):
416 cfg = cfg_read(VSWITCHD_CONF)
417 parent, vlan = find_bridge(cfg, bridge)
418 for iface in get_bridge_ifaces(cfg, parent, vlan):
422 def cmd_iface_to_br(iface):
423 cfg = cfg_read(VSWITCHD_CONF)
424 for bridge, parent, vlan in get_bridge_info(cfg):
425 if iface != bridge and iface in get_bridge_ifaces(cfg, parent, vlan):
428 raise Error("no interface named %s" % iface)
431 # Parse command line.
433 options, args = getopt.gnu_getopt(sys.argv[1:], "c:t:hV",
439 except getopt.GetoptError, msg:
440 sys.stderr.write("%s: %s (use --help for help)\n" % (argv0, msg))
444 for opt, optarg in options:
445 if opt == "-c" or opt == "--config":
447 VSWITCHD_CONF = optarg
448 elif opt == "-t" or opt == "--target":
449 global VSWITCHD_TARGET
451 optarg = '@RUNDIR@/' + optarg
452 VSWITCHD_TARGET = optarg
453 elif opt == "--no-reload":
454 global RELOAD_VSWITCHD
455 RELOAD_VSWITCHD = False
456 elif opt == "-h" or opt == "--help":
458 elif opt == "-V" or opt == "--version":
461 raise RuntimeError("unhandled option %s" % opt)
465 sys.stderr.write("%s: missing command name (use --help for help)\n"
469 commands = {'add-br': (cmd_add_br, lambda n: n == 1 or n == 3),
470 'del-br': (cmd_del_br, 1),
471 'list-br': (cmd_list_br, 0),
472 'br-exists': (cmd_br_exists, 1),
473 'list-ports': (cmd_list_ports, 1),
474 'add-port': (cmd_add_port, 2),
475 'add-bond': (cmd_add_bond, lambda n: n >= 4),
476 'del-port': (cmd_del_port, 2),
477 'port-to-br': (cmd_port_to_br, 1),
478 'list-ifaces': (cmd_list_ifaces, 1),
479 'iface-to-br': (cmd_iface_to_br, 1)}
482 if command not in commands:
483 sys.stderr.write("%s: unknown command '%s' (use --help for help)\n"
487 function, nargs = commands[command]
488 if callable(nargs) and not nargs(len(args)):
489 sys.stderr.write("%s: '%s' command does not accept %d arguments (use --help for help)\n" % (argv0, command, len(args)))
491 elif not callable(nargs) and len(args) != nargs:
492 sys.stderr.write("%s: '%s' command takes %d arguments but %d were supplied (use --help for help)\n" % (argv0, command, nargs, len(args)))
498 if __name__ == "__main__":
502 sys.stderr.write("%s: %s\n" % (argv0, msg.msg))