blob: 3d7912a2ec680e4b29c9d8cb7597e00f52441d66 [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
Eyale7361772016-04-05 16:18:56 +030017
Sean Dague97fcc7b2014-06-16 17:24:14 -040018"""Dump the state of the world for post mortem."""
19
20import argparse
21import datetime
Chris Dent57d79672016-02-23 15:38:43 +000022from distutils import spawn
Sean Dague737e9422015-05-12 19:51:39 -040023import fnmatch
Sean Dague97fcc7b2014-06-16 17:24:14 -040024import os
25import os.path
Ian Wienand99440f92015-07-01 06:14:01 +100026import subprocess
Sean Dague97fcc7b2014-06-16 17:24:14 -040027import sys
28
29
30def get_options():
31 parser = argparse.ArgumentParser(
32 description='Dump world state for debugging')
33 parser.add_argument('-d', '--dir',
34 default='.',
35 help='Output directory for worlddump')
Sean Dagueac9313e2015-07-27 13:33:30 -040036 parser.add_argument('-n', '--name',
37 default='',
38 help='Additional name to tag into file')
Sean Dague97fcc7b2014-06-16 17:24:14 -040039 return parser.parse_args()
40
41
Sean Dagueac9313e2015-07-27 13:33:30 -040042def filename(dirname, name=""):
Sean Dague97fcc7b2014-06-16 17:24:14 -040043 now = datetime.datetime.utcnow()
Sean Dagueac9313e2015-07-27 13:33:30 -040044 fmt = "worlddump-%Y-%m-%d-%H%M%S"
45 if name:
46 fmt += "-" + name
47 fmt += ".txt"
48 return os.path.join(dirname, now.strftime(fmt))
Sean Dague97fcc7b2014-06-16 17:24:14 -040049
50
51def warn(msg):
Eyale7361772016-04-05 16:18:56 +030052 print("WARN: %s" % msg)
Sean Dague97fcc7b2014-06-16 17:24:14 -040053
54
Sean Dague60a14052015-05-11 14:53:39 -040055def _dump_cmd(cmd):
Eyale7361772016-04-05 16:18:56 +030056 print(cmd)
57 print("-" * len(cmd))
58 print()
Ian Wienand99440f92015-07-01 06:14:01 +100059 try:
60 subprocess.check_call(cmd, shell=True)
Eyale7361772016-04-05 16:18:56 +030061 print()
Ihar Hrachyshka7976aac2016-03-03 15:30:49 +010062 except subprocess.CalledProcessError as e:
Eyale7361772016-04-05 16:18:56 +030063 print("*** Failed to run '%(cmd)s': %(err)s" % {'cmd': cmd, 'err': e})
Sean Dague60a14052015-05-11 14:53:39 -040064
65
Chris Dent57d79672016-02-23 15:38:43 +000066def _find_cmd(cmd):
67 if not spawn.find_executable(cmd):
Eyale7361772016-04-05 16:18:56 +030068 print("*** %s not found: skipping" % cmd)
Chris Dent57d79672016-02-23 15:38:43 +000069 return False
70 return True
71
72
Sean Dague60a14052015-05-11 14:53:39 -040073def _header(name):
Eyale7361772016-04-05 16:18:56 +030074 print()
75 print(name)
76 print("=" * len(name))
77 print()
Sean Dague60a14052015-05-11 14:53:39 -040078
79
fumihiko kakuma60994012016-03-08 20:55:01 +090080# This method gets a max openflow version supported by openvswitch.
81# For example 'ovs-ofctl --version' displays the following:
82#
83# ovs-ofctl (Open vSwitch) 2.0.2
84# Compiled Dec 9 2015 14:08:08
85# OpenFlow versions 0x1:0x4
86#
87# The above shows that openvswitch supports from OpenFlow11 to OpenFlow13.
88# This method gets max version searching 'OpenFlow versions 0x1:0x'.
89# And return a version value converted to an integer type.
90def _get_ofp_version():
91 process = subprocess.Popen(['ovs-ofctl', '--version'], stdout=subprocess.PIPE)
92 stdout, _ = process.communicate()
93 find_str = 'OpenFlow versions 0x1:0x'
94 offset = stdout.find(find_str)
95 return int(stdout[offset + len(find_str):-1]) - 1
96
97
Sean Dague97fcc7b2014-06-16 17:24:14 -040098def disk_space():
99 # the df output
Sean Dague60a14052015-05-11 14:53:39 -0400100 _header("File System Summary")
101
Sean Dague97fcc7b2014-06-16 17:24:14 -0400102 dfraw = os.popen("df -Ph").read()
103 df = [s.split() for s in dfraw.splitlines()]
104 for fs in df:
105 try:
106 if int(fs[4][:-1]) > 95:
107 warn("Device %s (%s) is %s full, might be an issue" % (
108 fs[0], fs[5], fs[4]))
109 except ValueError:
110 # if it doesn't look like an int, that's fine
111 pass
112
Eyale7361772016-04-05 16:18:56 +0300113 print(dfraw)
Sean Dague97fcc7b2014-06-16 17:24:14 -0400114
115
Sean Dague2da606d2015-08-06 10:02:43 -0400116def ebtables_dump():
Sean Dague5c5e0862015-11-09 14:08:15 -0500117 tables = ['filter', 'nat', 'broute']
Sean Dague2da606d2015-08-06 10:02:43 -0400118 _header("EB Tables Dump")
Chris Dent57d79672016-02-23 15:38:43 +0000119 if not _find_cmd('ebtables'):
120 return
Sean Dague5c5e0862015-11-09 14:08:15 -0500121 for table in tables:
122 _dump_cmd("sudo ebtables -t %s -L" % table)
Sean Dague2da606d2015-08-06 10:02:43 -0400123
124
Sean Dague168b7c22015-05-07 08:57:28 -0400125def iptables_dump():
126 tables = ['filter', 'nat', 'mangle']
Sean Dague60a14052015-05-11 14:53:39 -0400127 _header("IP Tables Dump")
128
Sean Dague168b7c22015-05-07 08:57:28 -0400129 for table in tables:
Sean Dague60a14052015-05-11 14:53:39 -0400130 _dump_cmd("sudo iptables --line-numbers -L -nv -t %s" % table)
131
132
Ihar Hrachyshka72c34ee2016-01-30 16:18:01 +0100133def _netns_list():
134 process = subprocess.Popen(['ip', 'netns'], stdout=subprocess.PIPE)
135 stdout, _ = process.communicate()
136 return stdout.split()
137
138
Sean Dague60a14052015-05-11 14:53:39 -0400139def network_dump():
140 _header("Network Dump")
141
142 _dump_cmd("brctl show")
143 _dump_cmd("arp -n")
Ihar Hrachyshka72c34ee2016-01-30 16:18:01 +0100144 ip_cmds = ["addr", "link", "route"]
145 for cmd in ip_cmds + ['netns']:
146 _dump_cmd("ip %s" % cmd)
147 for netns_ in _netns_list():
148 for cmd in ip_cmds:
149 args = {'netns': netns_, 'cmd': cmd}
150 _dump_cmd('sudo ip netns exec %(netns)s ip %(cmd)s' % args)
Sean Dague168b7c22015-05-07 08:57:28 -0400151
152
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100153def ovs_dump():
154 _header("Open vSwitch Dump")
155
Chris Dent57d79672016-02-23 15:38:43 +0000156 # NOTE(cdent): If we're not using neutron + ovs these commands
157 # will not be present so
158 if not _find_cmd('ovs-vsctl'):
159 return
160
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100161 # NOTE(ihrachys): worlddump is used outside of devstack context (f.e. in
162 # grenade), so there is no single place to determine the bridge names from.
163 # Hardcode for now.
164 bridges = ('br-int', 'br-tun', 'br-ex')
fumihiko kakuma60994012016-03-08 20:55:01 +0900165 ofctl_cmds = ('show', 'dump-ports-desc', 'dump-ports', 'dump-flows')
166 ofp_max = _get_ofp_version()
167 vers = 'OpenFlow10'
168 for i in range(ofp_max + 1):
169 vers += ',OpenFlow1' + str(i)
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100170 _dump_cmd("sudo ovs-vsctl show")
fumihiko kakuma60994012016-03-08 20:55:01 +0900171 for ofctl_cmd in ofctl_cmds:
172 for bridge in bridges:
173 args = {'vers': vers, 'cmd': ofctl_cmd, 'bridge': bridge}
174 _dump_cmd("sudo ovs-ofctl --protocols=%(vers)s %(cmd)s %(bridge)s" % args)
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100175
176
Sean Dague97fcc7b2014-06-16 17:24:14 -0400177def process_list():
Sean Dague60a14052015-05-11 14:53:39 -0400178 _header("Process Listing")
179 _dump_cmd("ps axo "
180 "user,ppid,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,args")
Sean Dague97fcc7b2014-06-16 17:24:14 -0400181
182
Sean Dague737e9422015-05-12 19:51:39 -0400183def compute_consoles():
184 _header("Compute consoles")
185 for root, dirnames, filenames in os.walk('/opt/stack'):
186 for filename in fnmatch.filter(filenames, 'console.log'):
187 fullpath = os.path.join(root, filename)
188 _dump_cmd("sudo cat %s" % fullpath)
189
190
Joe Gordon2ebe9932015-06-07 16:57:34 +0900191def guru_meditation_report():
192 _header("nova-compute Guru Meditation Report")
Ian Wienand3a9df1d2015-07-01 06:18:47 +1000193
194 try:
195 subprocess.check_call(["pgrep","nova-compute"])
196 except subprocess.CalledProcessError:
Eyale7361772016-04-05 16:18:56 +0300197 print("Skipping as nova-compute does not appear to be running")
Ian Wienand3a9df1d2015-07-01 06:18:47 +1000198 return
199
Kashyap Chamarthy88725452015-09-14 13:17:56 +0200200 _dump_cmd("kill -s USR2 `pgrep nova-compute`")
Eyale7361772016-04-05 16:18:56 +0300201 print("guru meditation report in nova-compute log")
Joe Gordon2ebe9932015-06-07 16:57:34 +0900202
203
Sean Dague97fcc7b2014-06-16 17:24:14 -0400204def main():
205 opts = get_options()
Sean Dagueac9313e2015-07-27 13:33:30 -0400206 fname = filename(opts.dir, opts.name)
Eyale7361772016-04-05 16:18:56 +0300207 print("World dumping... see %s for details" % fname)
Sean Dague97fcc7b2014-06-16 17:24:14 -0400208 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
209 with open(fname, 'w') as f:
210 os.dup2(f.fileno(), sys.stdout.fileno())
211 disk_space()
212 process_list()
Sean Dague60a14052015-05-11 14:53:39 -0400213 network_dump()
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100214 ovs_dump()
Sean Dague168b7c22015-05-07 08:57:28 -0400215 iptables_dump()
Sean Dague2da606d2015-08-06 10:02:43 -0400216 ebtables_dump()
Sean Dague737e9422015-05-12 19:51:39 -0400217 compute_consoles()
Joe Gordon2ebe9932015-06-07 16:57:34 +0900218 guru_meditation_report()
Sean Dague97fcc7b2014-06-16 17:24:14 -0400219
220
221if __name__ == '__main__':
222 try:
223 sys.exit(main())
224 except KeyboardInterrupt:
225 sys.exit(1)