blob: 72a257fa8c753e86963943095f25be49cd50f417 [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
Sean Dague97fcc7b2014-06-16 17:24:14 -040079def disk_space():
80 # the df output
Sean Dague60a14052015-05-11 14:53:39 -040081 _header("File System Summary")
82
Sean Dague97fcc7b2014-06-16 17:24:14 -040083 dfraw = os.popen("df -Ph").read()
84 df = [s.split() for s in dfraw.splitlines()]
85 for fs in df:
86 try:
87 if int(fs[4][:-1]) > 95:
88 warn("Device %s (%s) is %s full, might be an issue" % (
89 fs[0], fs[5], fs[4]))
90 except ValueError:
91 # if it doesn't look like an int, that's fine
92 pass
93
94 print dfraw
95
96
Sean Dague2da606d2015-08-06 10:02:43 -040097def ebtables_dump():
Sean Dague5c5e0862015-11-09 14:08:15 -050098 tables = ['filter', 'nat', 'broute']
Sean Dague2da606d2015-08-06 10:02:43 -040099 _header("EB Tables Dump")
Chris Dent57d79672016-02-23 15:38:43 +0000100 if not _find_cmd('ebtables'):
101 return
Sean Dague5c5e0862015-11-09 14:08:15 -0500102 for table in tables:
103 _dump_cmd("sudo ebtables -t %s -L" % table)
Sean Dague2da606d2015-08-06 10:02:43 -0400104
105
Sean Dague168b7c22015-05-07 08:57:28 -0400106def iptables_dump():
107 tables = ['filter', 'nat', 'mangle']
Sean Dague60a14052015-05-11 14:53:39 -0400108 _header("IP Tables Dump")
109
Sean Dague168b7c22015-05-07 08:57:28 -0400110 for table in tables:
Sean Dague60a14052015-05-11 14:53:39 -0400111 _dump_cmd("sudo iptables --line-numbers -L -nv -t %s" % table)
112
113
Ihar Hrachyshka72c34ee2016-01-30 16:18:01 +0100114def _netns_list():
115 process = subprocess.Popen(['ip', 'netns'], stdout=subprocess.PIPE)
116 stdout, _ = process.communicate()
117 return stdout.split()
118
119
Sean Dague60a14052015-05-11 14:53:39 -0400120def network_dump():
121 _header("Network Dump")
122
123 _dump_cmd("brctl show")
124 _dump_cmd("arp -n")
Ihar Hrachyshka72c34ee2016-01-30 16:18:01 +0100125 ip_cmds = ["addr", "link", "route"]
126 for cmd in ip_cmds + ['netns']:
127 _dump_cmd("ip %s" % cmd)
128 for netns_ in _netns_list():
129 for cmd in ip_cmds:
130 args = {'netns': netns_, 'cmd': cmd}
131 _dump_cmd('sudo ip netns exec %(netns)s ip %(cmd)s' % args)
Sean Dague168b7c22015-05-07 08:57:28 -0400132
133
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100134def ovs_dump():
135 _header("Open vSwitch Dump")
136
Chris Dent57d79672016-02-23 15:38:43 +0000137 # NOTE(cdent): If we're not using neutron + ovs these commands
138 # will not be present so
139 if not _find_cmd('ovs-vsctl'):
140 return
141
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100142 # NOTE(ihrachys): worlddump is used outside of devstack context (f.e. in
143 # grenade), so there is no single place to determine the bridge names from.
144 # Hardcode for now.
145 bridges = ('br-int', 'br-tun', 'br-ex')
146 _dump_cmd("sudo ovs-vsctl show")
147 for bridge in bridges:
148 _dump_cmd("sudo ovs-ofctl show %s" % bridge)
149 for bridge in bridges:
150 _dump_cmd("sudo ovs-ofctl dump-flows %s" % bridge)
151
152
Sean Dague97fcc7b2014-06-16 17:24:14 -0400153def process_list():
Sean Dague60a14052015-05-11 14:53:39 -0400154 _header("Process Listing")
155 _dump_cmd("ps axo "
156 "user,ppid,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,args")
Sean Dague97fcc7b2014-06-16 17:24:14 -0400157
158
Sean Dague737e9422015-05-12 19:51:39 -0400159def compute_consoles():
160 _header("Compute consoles")
161 for root, dirnames, filenames in os.walk('/opt/stack'):
162 for filename in fnmatch.filter(filenames, 'console.log'):
163 fullpath = os.path.join(root, filename)
164 _dump_cmd("sudo cat %s" % fullpath)
165
166
Joe Gordon2ebe9932015-06-07 16:57:34 +0900167def guru_meditation_report():
168 _header("nova-compute Guru Meditation Report")
Ian Wienand3a9df1d2015-07-01 06:18:47 +1000169
170 try:
171 subprocess.check_call(["pgrep","nova-compute"])
172 except subprocess.CalledProcessError:
173 print "Skipping as nova-compute does not appear to be running"
174 return
175
Kashyap Chamarthy88725452015-09-14 13:17:56 +0200176 _dump_cmd("kill -s USR2 `pgrep nova-compute`")
Joe Gordon2ebe9932015-06-07 16:57:34 +0900177 print "guru meditation report in nova-compute log"
178
179
Sean Dague97fcc7b2014-06-16 17:24:14 -0400180def main():
181 opts = get_options()
Sean Dagueac9313e2015-07-27 13:33:30 -0400182 fname = filename(opts.dir, opts.name)
Sean Dague97fcc7b2014-06-16 17:24:14 -0400183 print "World dumping... see %s for details" % fname
184 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
185 with open(fname, 'w') as f:
186 os.dup2(f.fileno(), sys.stdout.fileno())
187 disk_space()
188 process_list()
Sean Dague60a14052015-05-11 14:53:39 -0400189 network_dump()
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100190 ovs_dump()
Sean Dague168b7c22015-05-07 08:57:28 -0400191 iptables_dump()
Sean Dague2da606d2015-08-06 10:02:43 -0400192 ebtables_dump()
Sean Dague737e9422015-05-12 19:51:39 -0400193 compute_consoles()
Joe Gordon2ebe9932015-06-07 16:57:34 +0900194 guru_meditation_report()
Sean Dague97fcc7b2014-06-16 17:24:14 -0400195
196
197if __name__ == '__main__':
198 try:
199 sys.exit(main())
200 except KeyboardInterrupt:
201 sys.exit(1)