ovs-vsctl: Add the ability to perform multiple operations in a single run.
[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 = "@RUNDIR@/ovs-vswitchd.pid"
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     s = os.stat(target)
163     if stat.S_ISREG(s.st_mode):
164         pid = read_first_line_of_file(target)
165         target = "@RUNDIR@/ovs-vswitchd.%s.ctl" % pid
166         s = os.stat(target)
167     if not stat.S_ISSOCK(s.st_mode):
168         raise Error("%s is not a Unix domain socket, cannot reload" % target)
169     skt = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
170     skt.connect(target)
171     f = os.fdopen(skt.fileno(), "r+")
172     f.write("vswitchd/reload\n")
173     f.flush()
174     f.readline()
175     f.close()
176
177 def cfg_save(cfg, filename):
178     if filename == '-':
179         do_cfg_save(cfg, sys.stdout)
180     else:
181         tmp_name = filename + ".~tmp~"
182         f = open(tmp_name, 'w')
183         do_cfg_save(cfg, f)
184         f.close()
185         os.rename(tmp_name, filename)
186         if RELOAD_VSWITCHD:
187             cfg_reload()
188
189 # Returns a set of the immediate subsections of 'section' within 'cfg'.  For
190 # example, if 'section' is "bridge" and keys bridge.a, bridge.b, bridge.b.c,
191 # and bridge.c.x.y.z exist, returns set(['a', 'b', 'c']).
192 def cfg_get_subsections(cfg, section):
193     subsections = set()
194     for key in cfg:
195         if key.startswith(section + "."):
196             dot = key.find(".", len(section) + 1)
197             if dot == -1:
198                 dot = len(key)
199             subsections.add(key[len(section) + 1:dot])
200     return subsections
201
202 # Returns True if 'cfg' contains a key whose single value is 'true'.  Otherwise
203 # returns False.
204 def cfg_get_bool(cfg, name):
205     return name in cfg and cfg[name] == ['true']
206
207 # If 'cfg' has a port named 'port' configured with an implicit VLAN, returns
208 # that VLAN number.  Otherwise, returns 0.
209 def get_port_vlan(cfg, port):
210     try:
211         return int(cfg["vlan.%s.tag" % port][0])
212     except (ValueError, KeyError):
213         return 0
214
215 # Returns all the ports within 'bridge' in 'cfg'.  If 'vlan' is nonnegative,
216 # the ports returned are only those configured with implicit VLAN 'vlan'.
217 def get_bridge_ports(cfg, bridge, vlan):
218     ports = []
219     for port in cfg["bridge.%s.port" % bridge]:
220         if vlan < 0 or get_port_vlan(cfg, port) == vlan:
221             ports.append(port)
222     return ports
223
224 # Returns all the interfaces within 'bridge' in 'cfg'.  If 'vlan' is
225 # nonnegative, the interfaces returned are only those whose ports are
226 # configured with implicit VLAN 'vlan'.
227 def get_bridge_ifaces(cfg, bridge, vlan):
228     ifaces = []
229     for port in get_bridge_ports(cfg, bridge, vlan):
230         ifaces.extend(cfg.get("bonding.%s.slave" % port, [port]))
231     return ifaces
232
233 # Returns the first line of the file named 'name', with the trailing new-line
234 # (if any) stripped off.
235 def read_first_line_of_file(name):
236     file = None
237     try:
238         file = open(name, 'r')
239         return file.readline().rstrip('\n')
240     finally:
241         if file != None:
242             file.close()
243
244 # Returns a bridge ID constructed from the MAC address of network device
245 # 'netdev', in the format "8000.000102030405".
246 def get_bridge_id(netdev):
247     try:
248         hwaddr = read_first_line_of_file("/sys/class/net/%s/address" % netdev)
249         return "8000.%s" % (hwaddr.replace(":", ""))
250     except:
251         return "8000.002320ffffff"
252
253 # Returns a list of 3-tuples based on 'cfg'.  Each 3-tuple represents
254 # one real bridge or one fake bridge and has the form (bridge, parent,
255 # vlan), where 'bridge' is the real or fake bridge name, 'parent' is
256 # the same as 'bridge' for a real bridge or the name of the containing
257 # bridge for a fake bridge, and 'vlan' is 0 for a real bridge or a
258 # VLAN number for a fake bridge.
259 def get_bridge_info(cfg):
260     real_bridges = [(br, br, 0) for br in get_real_bridges(cfg)]
261     fake_bridges = []
262     for linux_bridge, ovs_bridge, vlan in real_bridges:
263         for iface in get_bridge_ifaces(cfg, ovs_bridge, -1):
264             if cfg_get_bool(cfg, "iface.%s.fake-bridge" % iface):
265                 fake_bridges.append((iface, ovs_bridge,
266                                      get_port_vlan(cfg, iface)))
267     return real_bridges + fake_bridges
268
269 # Returns the real bridges configured in 'cfg'.
270 def get_real_bridges(cfg):
271     return cfg_get_subsections(cfg, "bridge")
272
273 # Returns the fake bridges configured in 'cfg'.
274 def get_fake_bridges(cfg):
275     return [bridge for bridge, parent, vlan in get_bridge_info(cfg)
276             if bridge != parent]
277
278 # Returns all the real and fake bridges configured in 'cfg'.
279 def get_all_bridges(cfg):
280     return [bridge for bridge, parent, vlan in get_bridge_info(cfg)]
281
282 # Returns the parent bridge and VLAN of real or fake 'bridge' in
283 # 'cfg', where the parent bridge and VLAN are as defined in the
284 # description of get_bridge_info().  Raises an error if no bridge
285 # named 'bridge' exists in 'cfg'.
286 def find_bridge(cfg, bridge):
287     for br, parent, vlan in get_bridge_info(cfg):
288         if br == bridge:
289             return parent, vlan
290     raise Error("no bridge named %s" % bridge)
291
292 def del_matching_keys(cfg, pattern):
293     for key in [key for key in cfg.keys() if fnmatch.fnmatch(key, pattern)]:
294         del cfg[key]
295
296 # Deletes anything related to a port named 'port' from 'cfg'.  No port
297 # named 'port' need actually exist; this function will clean up
298 # regardless.
299 def del_port(cfg, port):
300     # The use of [!0-9] keeps an interface of 'eth0' from matching
301     # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
302     # interfaces.
303     for iface in cfg.get('bonding.%s.slave' % port, [port]):
304         del_matching_keys(cfg, 'iface.%s.[!0-9]*' % iface)
305         # Yes, this "port" setting applies to interfaces, not ports, *sigh*.
306         del_matching_keys(cfg, 'port.%s.ingress-policing*' % iface)
307     del_matching_keys(cfg, 'bonding.%s.[!0-9]*' % port)
308     del_matching_keys(cfg, 'vlan.%s.[!0-9]*' % port)
309     for key in cfg.keys():
310         if fnmatch.fnmatch(key, 'bridge.*.port'):
311             cfg[key] = [s for s in cfg[key] if s != port]
312
313 # Returns the name of the (real or fake) bridge in 'cfg' that contains
314 # port 'port', or None if there is no such port.
315 def port_to_bridge(cfg, port):
316     for bridge, parent, vlan in get_bridge_info(cfg):
317         if port != bridge and port in get_bridge_ports(cfg, parent, vlan):
318             return bridge
319     return None
320
321 def usage():
322     print """%(argv0)s: ovs-vswitchd management utility
323 usage: %(argv0)s [OPTIONS] COMMAND [ARG...]
324
325 Bridge commands:
326   add-br BRIDGE               create a new bridge named BRIDGE
327   add-br BRIDGE PARENT VLAN   create new fake bridge BRIDGE in PARENT on VLAN
328   del-br BRIDGE               delete BRIDGE and all of its ports
329   list-br                     print the names of all the bridges
330   br-exists BRIDGE            test whether BRIDGE exists
331   br-to-vlan BRIDGE           print the VLAN which BRIDGE is on
332   br-to-parent BRIDGE         print the parent of BRIDGE
333   
334 Port commands:
335   list-ports BRIDGE           print the names of all the ports on BRIDGE
336   add-port BRIDGE PORT        add network device PORT to BRIDGE
337   add-bond BRIDGE PORT IFACE...  add new bonded port PORT in BRIDGE from IFACES
338   del-port [BRIDGE] PORT      delete PORT (which may be bonded) from BRIDGE
339   port-to-br PORT             print name of bridge that contains PORT
340 A bond is considered to be a single port.
341
342 Interface commands (a bond consists of multiple interfaces):
343   list-ifaces BRIDGE          print the names of all the interfaces on BRIDGE
344   iface-to-br IFACE           print name of bridge that contains IFACE
345 A bond is considered to consist of interfaces.
346
347 General options:
348   --no-syslog                 do not write mesages to syslog
349   -c, --config=FILE           set configuration file
350                               (default: %(config)s)
351   -t, --target=PIDFILE|SOCKET set ovs-vswitchd target
352                               (default: %(target)s)
353   --no-reload                 do not make ovs-vswitchd reload its configuration
354   -h, --help                  display this help message and exit
355   -V, --version               display version information and exit
356 Report bugs to bugs@openvswitch.org.""" % {'argv0': argv0,
357                                            'config': DEFAULT_VSWITCHD_CONF,
358                                            'target': DEFAULT_VSWITCHD_TARGET}
359     sys.exit(0)
360
361 def version():
362     print "ovs-vsctl (Open vSwitch) @VERSION@"
363     sys.exit(0)
364
365 def check_conflicts(cfg, name, op):
366     bridges = get_bridge_info(cfg)
367     if name in [bridge for bridge, parent, vlan in bridges]:
368         raise Error("%s because a bridge named %s already exists" % (op, name))
369
370     for bridge, parent, vlan in bridges:
371         if name in get_bridge_ports(cfg, parent, vlan):
372             raise Error("%s because a port named %s already exists on bridge %s" % (op, name, bridge))
373         if name in get_bridge_ifaces(cfg, parent, vlan):
374             raise Error("%s because an interface named %s already exists on bridge %s" % (op, name, bridge))
375     
376 def cmd_add_br(cfg, bridge, parent=None, vlan=None):
377     check_conflicts(cfg, bridge, "cannot create a bridge named %s" % bridge)
378     
379     if parent and vlan:
380         if parent in get_fake_bridges(cfg):
381             raise Error("cannot create bridge with fake bridge as parent")
382         if parent not in get_real_bridges(cfg):
383             raise Error("parent bridge %s does not exist" % bridge)
384         try:
385             if int(vlan) < 0 or int(vlan) > 4095:
386                 raise ValueError
387         except ValueError:
388             raise Error("invalid VLAN number %s" % vlan)
389
390         # Create fake bridge internal port.
391         cfg['iface.%s.internal' % bridge] = ['true']
392         cfg['iface.%s.fake-bridge' % bridge] = ['true']
393         cfg['vlan.%s.tag' % bridge] = [vlan]
394
395         # Add fake bridge port to parent.
396         cfg['bridge.%s.port' % parent].append(bridge)
397     else:
398         cfg['bridge.%s.port' % bridge] = [bridge]
399
400 def cmd_del_br(cfg, bridge):
401     parent, vlan = find_bridge(cfg, bridge)
402     if vlan == 0:
403         vlan = -1
404     for port in set(get_bridge_ports(cfg, parent, vlan) + [bridge]):
405         del_port(cfg, port)
406     if vlan < 0: 
407         del_matching_keys(cfg, 'bridge.%s.[!0-9]*' % bridge)
408
409 def cmd_list_br(cfg):
410     return get_all_bridges(cfg)
411
412 def cmd_br_exists(cfg, bridge):
413     if bridge not in get_all_bridges(cfg):
414         sys.exit(2)
415
416 def cmd_list_ports(cfg, bridge):
417     ports = []
418     parent, vlan = find_bridge(cfg, bridge)
419     for port in get_bridge_ports(cfg, parent, vlan):
420         if port != bridge:
421             ports.append(port)
422     return ports
423
424 def do_add_port(cfg, bridge, parent, port, vlan):
425     check_conflicts(cfg, port, "cannot create a port named %s" % port)
426     cfg['bridge.%s.port' % parent].append(port)
427     if vlan > 0:
428         cfg['vlan.%s.tag' % port] = [vlan]
429
430 def cmd_add_port(cfg, bridge, port):
431     parent, vlan = find_bridge(cfg, bridge)
432     do_add_port(cfg, bridge, parent, port, vlan)
433
434 def cmd_add_bond(cfg, bridge, port, *slaves):
435     parent, vlan = find_bridge(cfg, bridge)
436     do_add_port(cfg, bridge, parent, port, vlan)
437     cfg['bonding.%s.slave' % port] = list(slaves)
438
439 def cmd_del_port(cfg, *args):
440     if len(args) == 2:
441         bridge, port = args
442         parent, vlan = find_bridge(cfg, bridge)
443         if port not in get_bridge_ports(cfg, parent, vlan):
444             if port in get_bridge_ports(cfg, parent, -1):
445                 raise Error("bridge %s does not have a port %s (although its parent bridge %s does)" % (bridge, port, parent))
446             else:
447                 raise Error("bridge %s does not have a port %s" % (bridge, port))
448     else:
449         port, = args
450         if not port_to_bridge(cfg, port):
451             raise Error("no port %s on any bridge" % port)
452     del_port(cfg, port)
453
454 def cmd_port_to_br(cfg, port):
455     bridge = port_to_bridge(cfg, port)
456     if bridge:
457         return (bridge,)
458     else:
459         raise Error("no port named %s" % port)
460
461 def cmd_list_ifaces(cfg, bridge):
462     ifaces = []
463     parent, vlan = find_bridge(cfg, bridge)
464     for iface in get_bridge_ifaces(cfg, parent, vlan):
465         if iface != bridge:
466             ifaces.append(iface)
467     return ifaces
468
469 def cmd_iface_to_br(cfg, iface):
470     for bridge, parent, vlan in get_bridge_info(cfg):
471         if iface != bridge and iface in get_bridge_ifaces(cfg, parent, vlan):
472             return (bridge,)
473     raise Error("no interface named %s" % iface)
474
475 def cmd_br_to_vlan(cfg, bridge):
476     parent, vlan = find_bridge(cfg, bridge)
477     return (vlan,)
478
479 def cmd_br_to_parent(cfg, bridge):
480     parent, vlan = find_bridge(cfg, bridge)
481     return (parent,)
482     
483 cmdTable = {'add-br': (cmd_add_br, True, lambda n: n == 1 or n == 3),
484             'del-br': (cmd_del_br, True, 1),
485             'list-br': (cmd_list_br, False, 0),
486             'br-exists': (cmd_br_exists, False, 1),
487             'list-ports': (cmd_list_ports, False, 1),
488             'add-port': (cmd_add_port, True, 2),
489             'add-bond': (cmd_add_bond, True, lambda n: n >= 4),
490             'del-port': (cmd_del_port, True, lambda n: n == 1 or n == 2),
491             'port-to-br': (cmd_port_to_br, False, 1),
492             'br-to-vlan': (cmd_br_to_vlan, False, 1),
493             'br-to-parent': (cmd_br_to_parent, False, 1),
494             'list-ifaces': (cmd_list_ifaces, False, 1),
495             'iface-to-br': (cmd_iface_to_br, False, 1)}
496
497 # Break up commands at -- boundaries.
498 def split_commands(args):
499     commands = []
500     command = []
501     for arg in args:
502         if arg == '--':
503             if command:
504                 commands.append(command)
505             command = []
506         else:
507             command.append(arg)
508     if command:
509         commands.append(command)
510     return commands
511
512 def check_command(args):
513     command, args = args[0], args[1:]
514     if command not in cmdTable:
515         sys.stderr.write("%s: unknown command '%s' (use --help for help)\n"
516                          % (argv0, command))
517         sys.exit(1)
518
519     function, is_mutator, nargs = cmdTable[command]
520     if callable(nargs) and not nargs(len(args)):
521         sys.stderr.write("%s: '%s' command does not accept %d arguments (use --help for help)\n" % (argv0, command, len(args)))
522         sys.exit(1)
523     elif not callable(nargs) and len(args) != nargs:
524         sys.stderr.write("%s: '%s' command takes %d arguments but %d were supplied (use --help for help)\n" % (argv0, command, nargs, len(args)))
525         sys.exit(1)
526
527 def run_command(cfg, args):
528     command, args = args[0], args[1:]
529     function, need_lock, nargs = cmdTable[command]
530     return function(cfg, *args)
531
532 def main():
533     # Parse command line.
534     try:
535         options, args = getopt.getopt(sys.argv[1:], "c:t:hV",
536                                       ["config=",
537                                        "target=",
538                                        "no-reload",
539                                        "no-syslog",
540                                        "oneline",
541                                        "help",
542                                        "version"])
543     except getopt.GetoptError, msg:
544         sys.stderr.write("%s: %s (use --help for help)\n" % (argv0, msg))
545         sys.exit(1)
546
547     # Handle options.
548     oneline = False
549     for opt, optarg in options:
550         if opt == "-c" or opt == "--config":
551             global VSWITCHD_CONF
552             VSWITCHD_CONF = optarg
553         elif opt == "-t" or opt == "--target":
554             global VSWITCHD_TARGET
555             if optarg[0] != '/':
556                 optarg = '@RUNDIR@/' + optarg
557             VSWITCHD_TARGET = optarg
558         elif opt == "--no-reload":
559             global RELOAD_VSWITCHD
560             RELOAD_VSWITCHD = False
561         elif opt == "-h" or opt == "--help":
562             usage()
563         elif opt == "-V" or opt == "--version":
564             version()
565         elif opt == "--no-syslog":
566             global SYSLOG
567             SYSLOG = False
568         elif opt == "--oneline":
569             oneline = True
570         else:
571             raise RuntimeError("unhandled option %s" % opt)
572
573     if SYSLOG:
574         syslog.openlog("ovs-vsctl")
575         log("Called as %s" % ' '.join(sys.argv[1:]))
576
577     # Break arguments into a series of commands.
578     commands = split_commands(args)
579     if not commands:
580         sys.stderr.write("%s: missing command name (use --help for help)\n"
581                          % argv0)
582         sys.exit(1)
583
584     # Check command syntax.
585     need_lock = False
586     for command in commands:
587         check_command(command)
588         if cmdTable[command[0]][1]:
589             need_lock = True
590
591     # Execute commands.
592     cfg = cfg_read(VSWITCHD_CONF, need_lock)
593     for command in commands:
594         output = run_command(cfg, command)
595         if oneline:
596             if output == None:
597                 output = ()
598             print '\\n'.join([str(s).replace('\\', '\\\\')
599                               for s in output])
600         elif output != None:
601             for line in output:
602                 print line
603     if need_lock:
604         cfg_save(cfg, VSWITCHD_CONF)
605     sys.exit(0)
606
607 if __name__ == "__main__":
608     try:
609         main()
610     except Error, msg:
611         sys.stderr.write("%s: %s\n" % (argv0, msg.msg))
612         sys.exit(1)