Make ovs-appctl easier to use and synchronize its interface with ovs-vsctl.
[sliver-openvswitch.git] / utilities / ovs-vsctl.in
1 #! @PYTHON@
2 # Copyright (c) 2009 Nicira Networks.                       -*- python -*-
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 import errno
17 import fcntl
18 import fnmatch
19 import getopt
20 import os
21 import re
22 import socket
23 import stat
24 import sys
25 import syslog
26
27 argv0 = sys.argv[0]
28 if argv0.find('/') >= 0:
29     argv0 = argv0[argv0.rfind('/') + 1:]
30
31 DEFAULT_VSWITCHD_CONF = "@sysconfdir@/ovs-vswitchd.conf"
32 VSWITCHD_CONF = DEFAULT_VSWITCHD_CONF
33
34 DEFAULT_VSWITCHD_TARGET = "ovs-vswitchd"
35 VSWITCHD_TARGET = DEFAULT_VSWITCHD_TARGET
36
37 RELOAD_VSWITCHD = True
38
39 SYSLOG = True
40
41 class Error(Exception):
42     def __init__(self, msg):
43         Exception.__init__(self)
44         self.msg = msg
45
46 def log(message):
47     if SYSLOG:
48         syslog.syslog(message)
49
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.
53
54 # Locks 'filename' for writing.
55 def cfg_lock(filename):
56     if filename == '-':
57         return
58
59     if '/' in filename:
60         lastSlash = filename.rfind('/')
61         prefix = filename[:lastSlash]
62         suffix = filename[lastSlash + 1:]
63         lock_name = "%s/.%s.~lock~" % (prefix, suffix)
64     else:
65         lock_name = ".%s.~lock~" % filename
66
67     while True:
68         # Try to open an existing lock file.
69         try:
70             f = open(lock_name, 'r')
71         except IOError, e:
72             if e.errno != errno.ENOENT:
73                 raise
74
75             # Try to create a new lock file.
76             try:
77                 fd = os.open(lock_name, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0600)
78             except OSError, e:
79                 if e.errno != errno.EEXIST:
80                     raise
81                 # Someone else created the lock file, try again.
82             os.close(fd)
83             continue
84     
85         fcntl.flock(f, fcntl.LOCK_EX)
86         return
87
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):
92     if lock:
93         cfg_lock(filename)
94
95     try:
96         if filename == '-':
97             f = open('/dev/stdin')
98         else:
99             f = open(filename)
100     except IOError, e:
101         sys.stderr.write("%s: could not open %s (%s)\n"
102                          % (argv0, filename, e.strerror))
103         sys.exit(1)
104
105     cfg = {}
106     rx = re.compile('([-._@$:+a-zA-Z0-9]+)(?:[ \t\r\n\v]*)=(?:[ \t\r\n\v]*)(.*)$')
107     for line in f:
108         line = line.strip()
109         if len(line) == 0 or line[0] == '#':
110             continue
111
112         match = rx.match(line)
113         if match == None:
114             continue
115
116         key, value = match.groups()
117         if key not in cfg:
118             cfg[key] = []
119         cfg[key].append(value)
120
121     global orig_cfg
122     orig_cfg = cfg_clone(cfg)
123
124     return cfg
125
126 # Returns a deep copy of 'cfg', which must be in the format returned
127 # by cfg_read().
128 def cfg_clone(cfg):
129     new = {}
130     for key in cfg:
131         new[key] = list(cfg[key])
132     return new
133
134 # Returns a list of all the configuration lines that are in 'a' but
135 # not in 'b'.
136 def cfg_subtract(a, b):
137     difference = []
138     for key in a:
139         for value in a[key]:
140             if key not in b or value not in b[key]:
141                 difference.append("%s=%s" % (key, value))
142     return difference
143
144 def do_cfg_save(cfg, file):
145     # Log changes.
146     added = cfg_subtract(cfg, orig_cfg)
147     removed = cfg_subtract(orig_cfg, cfg)
148     if added or removed:
149         log("configuration changes:")
150         for line in removed:
151             log("-%s\n" % line)
152         for line in added:
153             log("+%s\n" % line)
154
155     # Write changes.
156     for key in sorted(cfg.keys()):
157         for value in sorted(cfg[key]):
158             file.write("%s=%s\n" % (key, value))
159
160 def cfg_reload():
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)
165     s = os.stat(target)
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)
169     skt.connect(target)
170     f = os.fdopen(skt.fileno(), "r+")
171     f.write("vswitchd/reload\n")
172     f.flush()
173     f.readline()
174     f.close()
175
176 def cfg_save(cfg, filename):
177     if filename == '-':
178         do_cfg_save(cfg, sys.stdout)
179     else:
180         tmp_name = filename + ".~tmp~"
181         f = open(tmp_name, 'w')
182         do_cfg_save(cfg, f)
183         f.close()
184         os.rename(tmp_name, filename)
185         if RELOAD_VSWITCHD:
186             cfg_reload()
187
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):
192     subsections = set()
193     for key in cfg:
194         if key.startswith(section + "."):
195             dot = key.find(".", len(section) + 1)
196             if dot == -1:
197                 dot = len(key)
198             subsections.add(key[len(section) + 1:dot])
199     return subsections
200
201 # Returns True if 'cfg' contains a key whose single value is 'true'.  Otherwise
202 # returns False.
203 def cfg_get_bool(cfg, name):
204     return name in cfg and cfg[name] == ['true']
205
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):
209     try:
210         return int(cfg["vlan.%s.tag" % port][0])
211     except (ValueError, KeyError):
212         return 0
213
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):
217     ports = []
218     for port in cfg["bridge.%s.port" % bridge]:
219         if vlan < 0 or get_port_vlan(cfg, port) == vlan:
220             ports.append(port)
221     return ports
222
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):
227     ifaces = []
228     for port in get_bridge_ports(cfg, bridge, vlan):
229         ifaces.extend(cfg.get("bonding.%s.slave" % port, [port]))
230     return ifaces
231
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):
235     file = None
236     try:
237         file = open(name, 'r')
238         return file.readline().rstrip('\n')
239     finally:
240         if file != None:
241             file.close()
242
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):
246     try:
247         hwaddr = read_first_line_of_file("/sys/class/net/%s/address" % netdev)
248         return "8000.%s" % (hwaddr.replace(":", ""))
249     except:
250         return "8000.002320ffffff"
251
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)]
260     fake_bridges = []
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
267
268 # Returns the real bridges configured in 'cfg'.
269 def get_real_bridges(cfg):
270     return cfg_get_subsections(cfg, "bridge")
271
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)
275             if bridge != parent]
276
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)]
280
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):
287         if br == bridge:
288             return parent, vlan
289     raise Error("no bridge named %s" % bridge)
290
291 def del_matching_keys(cfg, pattern):
292     for key in [key for key in cfg.keys() if fnmatch.fnmatch(key, pattern)]:
293         del cfg[key]
294
295 # Deletes anything related to a port named 'port' from 'cfg'.  No port
296 # named 'port' need actually exist; this function will clean up
297 # regardless.
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
301     # interfaces.
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]
311
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):
317             return bridge
318     return None
319
320 def usage():
321     print """%(argv0)s: ovs-vswitchd management utility
322 usage: %(argv0)s [OPTIONS] COMMAND [ARG...]
323
324 Bridge commands:
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
332   
333 Port commands:
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.
340
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.
345
346 General options:
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}
358     sys.exit(0)
359
360 def version():
361     print "ovs-vsctl (Open vSwitch) @VERSION@"
362     sys.exit(0)
363
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))
368
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))
374     
375 def cmd_add_br(cfg, bridge, parent=None, vlan=None):
376     check_conflicts(cfg, bridge, "cannot create a bridge named %s" % bridge)
377     
378     if parent and vlan:
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)
383         try:
384             if int(vlan) < 0 or int(vlan) > 4095:
385                 raise ValueError
386         except ValueError:
387             raise Error("invalid VLAN number %s" % vlan)
388
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]
393
394         # Add fake bridge port to parent.
395         cfg['bridge.%s.port' % parent].append(bridge)
396     else:
397         cfg['bridge.%s.port' % bridge] = [bridge]
398
399 def cmd_del_br(cfg, bridge):
400     parent, vlan = find_bridge(cfg, bridge)
401     if vlan == 0:
402         vlan = -1
403     for port in set(get_bridge_ports(cfg, parent, vlan) + [bridge]):
404         del_port(cfg, port)
405     if vlan < 0: 
406         del_matching_keys(cfg, 'bridge.%s.[!0-9]*' % bridge)
407
408 def cmd_list_br(cfg):
409     return get_all_bridges(cfg)
410
411 def cmd_br_exists(cfg, bridge):
412     if bridge not in get_all_bridges(cfg):
413         sys.exit(2)
414
415 def cmd_list_ports(cfg, bridge):
416     ports = []
417     parent, vlan = find_bridge(cfg, bridge)
418     for port in get_bridge_ports(cfg, parent, vlan):
419         if port != bridge:
420             ports.append(port)
421     return ports
422
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)
426     if vlan > 0:
427         cfg['vlan.%s.tag' % port] = [vlan]
428
429 def cmd_add_port(cfg, bridge, port):
430     parent, vlan = find_bridge(cfg, bridge)
431     do_add_port(cfg, bridge, parent, port, vlan)
432
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)
437
438 def cmd_del_port(cfg, *args):
439     if len(args) == 2:
440         bridge, port = 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))
445             else:
446                 raise Error("bridge %s does not have a port %s" % (bridge, port))
447     else:
448         port, = args
449         if not port_to_bridge(cfg, port):
450             raise Error("no port %s on any bridge" % port)
451     del_port(cfg, port)
452
453 def cmd_port_to_br(cfg, port):
454     bridge = port_to_bridge(cfg, port)
455     if bridge:
456         return (bridge,)
457     else:
458         raise Error("no port named %s" % port)
459
460 def cmd_list_ifaces(cfg, bridge):
461     ifaces = []
462     parent, vlan = find_bridge(cfg, bridge)
463     for iface in get_bridge_ifaces(cfg, parent, vlan):
464         if iface != bridge:
465             ifaces.append(iface)
466     return ifaces
467
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):
471             return (bridge,)
472     raise Error("no interface named %s" % iface)
473
474 def cmd_br_to_vlan(cfg, bridge):
475     parent, vlan = find_bridge(cfg, bridge)
476     return (vlan,)
477
478 def cmd_br_to_parent(cfg, bridge):
479     parent, vlan = find_bridge(cfg, bridge)
480     return (parent,)
481     
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)}
495
496 # Break up commands at -- boundaries.
497 def split_commands(args):
498     commands = []
499     command = []
500     for arg in args:
501         if arg == '--':
502             if command:
503                 commands.append(command)
504             command = []
505         else:
506             command.append(arg)
507     if command:
508         commands.append(command)
509     return commands
510
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"
515                          % (argv0, command))
516         sys.exit(1)
517
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)))
521         sys.exit(1)
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)))
524         sys.exit(1)
525
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)
530
531 def main():
532     # Parse command line.
533     try:
534         options, args = getopt.getopt(sys.argv[1:], "c:t:hV",
535                                       ["config=",
536                                        "target=",
537                                        "no-reload",
538                                        "no-syslog",
539                                        "oneline",
540                                        "help",
541                                        "version"])
542     except getopt.GetoptError, msg:
543         sys.stderr.write("%s: %s (use --help for help)\n" % (argv0, msg))
544         sys.exit(1)
545
546     # Handle options.
547     oneline = False
548     for opt, optarg in options:
549         if opt == "-c" or opt == "--config":
550             global VSWITCHD_CONF
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":
559             usage()
560         elif opt == "-V" or opt == "--version":
561             version()
562         elif opt == "--no-syslog":
563             global SYSLOG
564             SYSLOG = False
565         elif opt == "--oneline":
566             oneline = True
567         else:
568             raise RuntimeError("unhandled option %s" % opt)
569
570     if SYSLOG:
571         syslog.openlog("ovs-vsctl")
572         log("Called as %s" % ' '.join(sys.argv[1:]))
573
574     # Break arguments into a series of commands.
575     commands = split_commands(args)
576     if not commands:
577         sys.stderr.write("%s: missing command name (use --help for help)\n"
578                          % argv0)
579         sys.exit(1)
580
581     # Check command syntax.
582     need_lock = False
583     for command in commands:
584         check_command(command)
585         if cmdTable[command[0]][1]:
586             need_lock = True
587
588     # Execute commands.
589     cfg = cfg_read(VSWITCHD_CONF, need_lock)
590     for command in commands:
591         output = run_command(cfg, command)
592         if oneline:
593             if output == None:
594                 output = ()
595             print '\\n'.join([str(s).replace('\\', '\\\\')
596                               for s in output])
597         elif output != None:
598             for line in output:
599                 print line
600     if need_lock:
601         cfg_save(cfg, VSWITCHD_CONF)
602     sys.exit(0)
603
604 if __name__ == "__main__":
605     try:
606         main()
607     except Error, msg:
608         sys.stderr.write("%s: %s\n" % (argv0, msg.msg))
609         sys.exit(1)