blob: 3ff22a9dfd8230313500bc9718b6db50a69a07ac [file] [log] [blame]
Sean Dague97fcc7b2014-06-16 17:24:14 -04001#!/usr/bin/env python
2#
3# Copyright 2014 Hewlett-Packard Development Company, L.P.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# 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, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17"""Dump the state of the world for post mortem."""
18
19import argparse
20import datetime
Chris Dent57d79672016-02-23 15:38:43 +000021from distutils import spawn
Sean Dague737e9422015-05-12 19:51:39 -040022import fnmatch
Sean Dague97fcc7b2014-06-16 17:24:14 -040023import os
24import os.path
Ian Wienand99440f92015-07-01 06:14:01 +100025import subprocess
Sean Dague97fcc7b2014-06-16 17:24:14 -040026import sys
27
28
29def get_options():
30 parser = argparse.ArgumentParser(
31 description='Dump world state for debugging')
32 parser.add_argument('-d', '--dir',
33 default='.',
34 help='Output directory for worlddump')
Sean Dagueac9313e2015-07-27 13:33:30 -040035 parser.add_argument('-n', '--name',
36 default='',
37 help='Additional name to tag into file')
Sean Dague97fcc7b2014-06-16 17:24:14 -040038 return parser.parse_args()
39
40
Sean Dagueac9313e2015-07-27 13:33:30 -040041def filename(dirname, name=""):
Sean Dague97fcc7b2014-06-16 17:24:14 -040042 now = datetime.datetime.utcnow()
Sean Dagueac9313e2015-07-27 13:33:30 -040043 fmt = "worlddump-%Y-%m-%d-%H%M%S"
44 if name:
45 fmt += "-" + name
46 fmt += ".txt"
47 return os.path.join(dirname, now.strftime(fmt))
Sean Dague97fcc7b2014-06-16 17:24:14 -040048
49
50def warn(msg):
51 print "WARN: %s" % msg
52
53
Sean Dague60a14052015-05-11 14:53:39 -040054def _dump_cmd(cmd):
55 print cmd
56 print "-" * len(cmd)
57 print
Ian Wienand99440f92015-07-01 06:14:01 +100058 try:
59 subprocess.check_call(cmd, shell=True)
Ihar Hrachyshka190b29d2016-02-11 13:42:21 +010060 print
Ihar Hrachyshka7976aac2016-03-03 15:30:49 +010061 except subprocess.CalledProcessError as e:
62 print "*** Failed to run '%(cmd)s': %(err)s" % {'cmd': cmd, 'err': e}
Sean Dague60a14052015-05-11 14:53:39 -040063
64
Chris Dent57d79672016-02-23 15:38:43 +000065def _find_cmd(cmd):
66 if not spawn.find_executable(cmd):
67 print "*** %s not found: skipping" % cmd
68 return False
69 return True
70
71
Sean Dague60a14052015-05-11 14:53:39 -040072def _header(name):
73 print
74 print name
75 print "=" * len(name)
76 print
77
78
fumihiko kakuma60994012016-03-08 20:55:01 +090079# This method gets a max openflow version supported by openvswitch.
80# For example 'ovs-ofctl --version' displays the following:
81#
82# ovs-ofctl (Open vSwitch) 2.0.2
83# Compiled Dec 9 2015 14:08:08
84# OpenFlow versions 0x1:0x4
85#
fumihiko kakuma2bd25682016-04-05 10:33:50 +090086# The above shows that openvswitch supports from OpenFlow10 to OpenFlow13.
fumihiko kakuma60994012016-03-08 20:55:01 +090087# This method gets max version searching 'OpenFlow versions 0x1:0x'.
88# And return a version value converted to an integer type.
89def _get_ofp_version():
90 process = subprocess.Popen(['ovs-ofctl', '--version'], stdout=subprocess.PIPE)
91 stdout, _ = process.communicate()
92 find_str = 'OpenFlow versions 0x1:0x'
93 offset = stdout.find(find_str)
94 return int(stdout[offset + len(find_str):-1]) - 1
95
96
Sean Dague97fcc7b2014-06-16 17:24:14 -040097def disk_space():
98 # the df output
Sean Dague60a14052015-05-11 14:53:39 -040099 _header("File System Summary")
100
Sean Dague97fcc7b2014-06-16 17:24:14 -0400101 dfraw = os.popen("df -Ph").read()
102 df = [s.split() for s in dfraw.splitlines()]
103 for fs in df:
104 try:
105 if int(fs[4][:-1]) > 95:
106 warn("Device %s (%s) is %s full, might be an issue" % (
107 fs[0], fs[5], fs[4]))
108 except ValueError:
109 # if it doesn't look like an int, that's fine
110 pass
111
112 print dfraw
113
114
Sean Dague2da606d2015-08-06 10:02:43 -0400115def ebtables_dump():
Sean Dague5c5e0862015-11-09 14:08:15 -0500116 tables = ['filter', 'nat', 'broute']
Sean Dague2da606d2015-08-06 10:02:43 -0400117 _header("EB Tables Dump")
Chris Dent57d79672016-02-23 15:38:43 +0000118 if not _find_cmd('ebtables'):
119 return
Sean Dague5c5e0862015-11-09 14:08:15 -0500120 for table in tables:
121 _dump_cmd("sudo ebtables -t %s -L" % table)
Sean Dague2da606d2015-08-06 10:02:43 -0400122
123
Sean Dague168b7c22015-05-07 08:57:28 -0400124def iptables_dump():
125 tables = ['filter', 'nat', 'mangle']
Sean Dague60a14052015-05-11 14:53:39 -0400126 _header("IP Tables Dump")
127
Sean Dague168b7c22015-05-07 08:57:28 -0400128 for table in tables:
Sean Dague60a14052015-05-11 14:53:39 -0400129 _dump_cmd("sudo iptables --line-numbers -L -nv -t %s" % table)
130
131
Ihar Hrachyshka72c34ee2016-01-30 16:18:01 +0100132def _netns_list():
133 process = subprocess.Popen(['ip', 'netns'], stdout=subprocess.PIPE)
134 stdout, _ = process.communicate()
135 return stdout.split()
136
137
Sean Dague60a14052015-05-11 14:53:39 -0400138def network_dump():
139 _header("Network Dump")
140
141 _dump_cmd("brctl show")
142 _dump_cmd("arp -n")
Ihar Hrachyshka72c34ee2016-01-30 16:18:01 +0100143 ip_cmds = ["addr", "link", "route"]
144 for cmd in ip_cmds + ['netns']:
145 _dump_cmd("ip %s" % cmd)
146 for netns_ in _netns_list():
147 for cmd in ip_cmds:
148 args = {'netns': netns_, 'cmd': cmd}
149 _dump_cmd('sudo ip netns exec %(netns)s ip %(cmd)s' % args)
Sean Dague168b7c22015-05-07 08:57:28 -0400150
151
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100152def ovs_dump():
153 _header("Open vSwitch Dump")
154
Chris Dent57d79672016-02-23 15:38:43 +0000155 # NOTE(cdent): If we're not using neutron + ovs these commands
156 # will not be present so
157 if not _find_cmd('ovs-vsctl'):
158 return
159
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100160 # NOTE(ihrachys): worlddump is used outside of devstack context (f.e. in
161 # grenade), so there is no single place to determine the bridge names from.
162 # Hardcode for now.
163 bridges = ('br-int', 'br-tun', 'br-ex')
fumihiko kakuma60994012016-03-08 20:55:01 +0900164 ofctl_cmds = ('show', 'dump-ports-desc', 'dump-ports', 'dump-flows')
165 ofp_max = _get_ofp_version()
166 vers = 'OpenFlow10'
167 for i in range(ofp_max + 1):
168 vers += ',OpenFlow1' + str(i)
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100169 _dump_cmd("sudo ovs-vsctl show")
fumihiko kakuma60994012016-03-08 20:55:01 +0900170 for ofctl_cmd in ofctl_cmds:
171 for bridge in bridges:
172 args = {'vers': vers, 'cmd': ofctl_cmd, 'bridge': bridge}
173 _dump_cmd("sudo ovs-ofctl --protocols=%(vers)s %(cmd)s %(bridge)s" % args)
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100174
175
Sean Dague97fcc7b2014-06-16 17:24:14 -0400176def process_list():
Sean Dague60a14052015-05-11 14:53:39 -0400177 _header("Process Listing")
178 _dump_cmd("ps axo "
179 "user,ppid,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,args")
Sean Dague97fcc7b2014-06-16 17:24:14 -0400180
181
Sean Dague737e9422015-05-12 19:51:39 -0400182def compute_consoles():
183 _header("Compute consoles")
184 for root, dirnames, filenames in os.walk('/opt/stack'):
185 for filename in fnmatch.filter(filenames, 'console.log'):
186 fullpath = os.path.join(root, filename)
187 _dump_cmd("sudo cat %s" % fullpath)
188
189
Joe Gordon2ebe9932015-06-07 16:57:34 +0900190def guru_meditation_report():
191 _header("nova-compute Guru Meditation Report")
Ian Wienand3a9df1d2015-07-01 06:18:47 +1000192
193 try:
194 subprocess.check_call(["pgrep","nova-compute"])
195 except subprocess.CalledProcessError:
196 print "Skipping as nova-compute does not appear to be running"
197 return
198
Kashyap Chamarthy88725452015-09-14 13:17:56 +0200199 _dump_cmd("kill -s USR2 `pgrep nova-compute`")
Joe Gordon2ebe9932015-06-07 16:57:34 +0900200 print "guru meditation report in nova-compute log"
201
202
Sean Dague97fcc7b2014-06-16 17:24:14 -0400203def main():
204 opts = get_options()
Sean Dagueac9313e2015-07-27 13:33:30 -0400205 fname = filename(opts.dir, opts.name)
Sean Dague97fcc7b2014-06-16 17:24:14 -0400206 print "World dumping... see %s for details" % fname
207 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
208 with open(fname, 'w') as f:
209 os.dup2(f.fileno(), sys.stdout.fileno())
210 disk_space()
211 process_list()
Sean Dague60a14052015-05-11 14:53:39 -0400212 network_dump()
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100213 ovs_dump()
Sean Dague168b7c22015-05-07 08:57:28 -0400214 iptables_dump()
Sean Dague2da606d2015-08-06 10:02:43 -0400215 ebtables_dump()
Sean Dague737e9422015-05-12 19:51:39 -0400216 compute_consoles()
Joe Gordon2ebe9932015-06-07 16:57:34 +0900217 guru_meditation_report()
Sean Dague97fcc7b2014-06-16 17:24:14 -0400218
219
220if __name__ == '__main__':
221 try:
222 sys.exit(main())
223 except KeyboardInterrupt:
224 sys.exit(1)