Merge "citrix" branch into "master.
[sliver-openvswitch.git] / xenserver / usr_sbin_brctl
1 #! /usr/bin/python
2 #
3 # Copyright (c) 2009 Nicira Networks.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at:
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 import getopt
18 import os
19 import re
20 import subprocess
21 import sys
22
23 argv0 = sys.argv[0]
24
25 BRCTL = "/usr/lib/vswitch/xs-original/brctl"
26 VSWITCHD_CONF = "/etc/ovs-vswitchd.conf"
27
28 # Execute the real brctl program, passing the same arguments that were passed
29 # to us.
30 def delegate():
31     os.execl(BRCTL, BRCTL, *sys.argv[1:])
32     # execl should never return.  We only arrive here if brctl failed to exec.
33     sys.exit(1)
34
35 # Read the ovs-vswitchd.conf file named 'filename' and return its contents as a
36 # dictionary that maps from string keys to lists of string values.  (Even
37 # singleton values are represented as lists.)
38 def cfg_read(filename):
39     try:
40         f = open(filename)
41     except IOError, e:
42         sys.stderr.write("%s: could not open %s (%s)\n"
43                          % (argv0, filename, e.strerror))
44         sys.exit(1)
45
46     cfg = {}
47     rx = re.compile('([-._@$:+a-zA-Z0-9]+)(?:[ \t\r\n\v]*)=(?:[ \t\r\n\v]*)(.*)$')
48     for line in f:
49         line = line.strip()
50         if len(line) == 0 or line[0] == '#':
51             continue
52
53         match = rx.match(line)
54         if match == None:
55             continue
56
57         key, value = match.groups()
58         if key not in cfg:
59             cfg[key] = []
60         cfg[key].append(value)
61     return cfg
62
63 # Returns a set of the immediate subsections of 'section' within 'cfg'.  For
64 # example, if 'section' is "bridge" and keys bridge.a, bridge.b, bridge.b.c,
65 # and bridge.c.x.y.z exist, returns set(['a', 'b', 'c']).
66 def cfg_get_subsections(cfg, section):
67     subsections = set()
68     for key in cfg:
69         if key.startswith(section + "."):
70             dot = key.find(".", len(section) + 1)
71             if dot == -1:
72                 dot = len(key)
73             subsections.add(key[len(section) + 1:dot])
74     return subsections
75
76 # Returns True if 'cfg' contains a key whose single value is 'true'.  Otherwise
77 # returns False.
78 def cfg_get_bool(cfg, name):
79     return name in cfg and cfg[name] == ['true']
80
81 # If 'cfg' has a port named 'port' configured with an implicit VLAN, returns
82 # that VLAN number.  Otherwise, returns 0.
83 def get_port_vlan(cfg, port):
84     try:
85         return int(cfg["vlan.%s.tag" % port][0])
86     except (ValueError, KeyError):
87         return 0
88
89 # Returns all the ports within 'bridge' in 'cfg'.  If 'vlan' is nonnegative,
90 # the ports returned are only those configured with implicit VLAN 'vlan'.
91 def get_bridge_ports(cfg, bridge, vlan):
92     ports = []
93     for port in cfg["bridge.%s.port" % bridge]:
94         if vlan < 0 or get_port_vlan(cfg, port) == vlan:
95             ports.append(port)
96     return ports
97
98 # Returns all the interfaces within 'bridge' in 'cfg'.  If 'vlan' is
99 # nonnegative, the interfaces returned are only those whose ports are
100 # configured with implicit VLAN 'vlan'.
101 def get_bridge_ifaces(cfg, bridge, vlan):
102     ifaces = []
103     for port in get_bridge_ports(cfg, bridge, vlan):
104         ifaces.extend(cfg.get("bonding.%s.slave" % port, [port]))
105     return ifaces
106
107 # Returns the first line of the file named 'name', with the trailing new-line
108 # (if any) stripped off.
109 def read_first_line_of_file(name):
110     file = None
111     try:
112         file = open(name, 'r')
113         return file.readline().rstrip('\n')
114     finally:
115         if file != None:
116             file.close()
117
118 # Returns a bridge ID constructed from the MAC address of network device
119 # 'netdev', in the format "8000.000102030405".
120 def get_bridge_id(netdev):
121     try:
122         hwaddr = read_first_line_of_file("/sys/class/net/%s/address" % netdev)
123         return "8000.%s" % (hwaddr.replace(":", ""))
124     except:
125         return "8000.002320ffffff"
126
127 def cmd_show():
128     print "bridge name\tbridge id\t\tSTP enabled\tinterfaces"
129     cfg = cfg_read(VSWITCHD_CONF)
130
131     # Find all the bridges.
132     real_bridges = [(br, br, 0) for br in cfg_get_subsections(cfg, "bridge")]
133     fake_bridges = []
134     for linux_bridge, ovs_bridge, vlan in real_bridges:
135         for iface in get_bridge_ifaces(cfg, ovs_bridge, -1):
136             if cfg_get_bool(cfg, "iface.%s.fake-bridge" % iface):
137                 fake_bridges.append((iface, ovs_bridge,
138                                      get_port_vlan(cfg, iface)))
139     bridges = real_bridges + fake_bridges
140
141     # Find all the interfaces on each bridge.
142     for linux_bridge, ovs_bridge, vlan in bridges:
143         bridge_ports = get_bridge_ports(cfg, ovs_bridge, vlan)
144         if linux_bridge in bridge_ports:
145             bridge_ports.remove(linux_bridge)
146         bridge_ports.sort()
147         bridge_id = get_bridge_id(linux_bridge)
148         first_port = ""
149         if bridge_ports:
150             first_port = bridge_ports[0]
151         print "%s\t\t%s\t%s\t\t%s" % (linux_bridge, bridge_id, "no", first_port)
152         for port in bridge_ports[1:]:
153             print "\t\t\t\t\t\t\t%s" % port
154
155 def main():
156     # Parse the command line.
157     try:
158         options, args = getopt.gnu_getopt(sys.argv[1:],
159                                           "hV", ["help", "version"])
160     except getopt.GetoptError, msg:
161         sys.stderr.write("%s: %s (use --help for help)\n" % (argv0, msg))
162         sys.exit(1)
163
164     # Handle command-line options.
165     for opt, optarg in options:
166         if opt == "-h" or opt == "--help":
167             delegate()
168         elif opt == "-V" or opt == "--version":
169             subprocess.call([BRCTL, "--version"])
170             print "Open vSwitch brctl wrapper"
171             sys.exit(0)
172
173     # Execute commands.  Most commands are delegated to the brctl binary that
174     # we are wrapping, but we implement the "show" command ourselves.
175     if args and args[0] == "show":
176         cmd_show()
177     else:
178         delegate()
179
180 if __name__ == "__main__":
181     main()