blob: f931f11b2e63e365984aafc698319eb191c4d181 [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 kakuma578459f2016-04-07 08:15:45 +090079def _bridge_list():
80 process = subprocess.Popen(['ovs-vsctl', 'list-br'], stdout=subprocess.PIPE)
81 stdout, _ = process.communicate()
82 return stdout.split()
83
84
fumihiko kakuma60994012016-03-08 20:55:01 +090085# This method gets a max openflow version supported by openvswitch.
86# For example 'ovs-ofctl --version' displays the following:
87#
88# ovs-ofctl (Open vSwitch) 2.0.2
89# Compiled Dec 9 2015 14:08:08
90# OpenFlow versions 0x1:0x4
91#
fumihiko kakuma2bd25682016-04-05 10:33:50 +090092# The above shows that openvswitch supports from OpenFlow10 to OpenFlow13.
fumihiko kakuma60994012016-03-08 20:55:01 +090093# This method gets max version searching 'OpenFlow versions 0x1:0x'.
94# And return a version value converted to an integer type.
95def _get_ofp_version():
96 process = subprocess.Popen(['ovs-ofctl', '--version'], stdout=subprocess.PIPE)
97 stdout, _ = process.communicate()
98 find_str = 'OpenFlow versions 0x1:0x'
99 offset = stdout.find(find_str)
100 return int(stdout[offset + len(find_str):-1]) - 1
101
102
Sean Dague97fcc7b2014-06-16 17:24:14 -0400103def disk_space():
104 # the df output
Sean Dague60a14052015-05-11 14:53:39 -0400105 _header("File System Summary")
106
Sean Dague97fcc7b2014-06-16 17:24:14 -0400107 dfraw = os.popen("df -Ph").read()
108 df = [s.split() for s in dfraw.splitlines()]
109 for fs in df:
110 try:
111 if int(fs[4][:-1]) > 95:
112 warn("Device %s (%s) is %s full, might be an issue" % (
113 fs[0], fs[5], fs[4]))
114 except ValueError:
115 # if it doesn't look like an int, that's fine
116 pass
117
118 print dfraw
119
120
Sean Dague2da606d2015-08-06 10:02:43 -0400121def ebtables_dump():
Sean Dague5c5e0862015-11-09 14:08:15 -0500122 tables = ['filter', 'nat', 'broute']
Sean Dague2da606d2015-08-06 10:02:43 -0400123 _header("EB Tables Dump")
Chris Dent57d79672016-02-23 15:38:43 +0000124 if not _find_cmd('ebtables'):
125 return
Sean Dague5c5e0862015-11-09 14:08:15 -0500126 for table in tables:
127 _dump_cmd("sudo ebtables -t %s -L" % table)
Sean Dague2da606d2015-08-06 10:02:43 -0400128
129
Sean Dague168b7c22015-05-07 08:57:28 -0400130def iptables_dump():
131 tables = ['filter', 'nat', 'mangle']
Sean Dague60a14052015-05-11 14:53:39 -0400132 _header("IP Tables Dump")
133
Sean Dague168b7c22015-05-07 08:57:28 -0400134 for table in tables:
Sean Dague60a14052015-05-11 14:53:39 -0400135 _dump_cmd("sudo iptables --line-numbers -L -nv -t %s" % table)
136
137
Ihar Hrachyshka72c34ee2016-01-30 16:18:01 +0100138def _netns_list():
139 process = subprocess.Popen(['ip', 'netns'], stdout=subprocess.PIPE)
140 stdout, _ = process.communicate()
141 return stdout.split()
142
143
Sean Dague60a14052015-05-11 14:53:39 -0400144def network_dump():
145 _header("Network Dump")
146
147 _dump_cmd("brctl show")
148 _dump_cmd("arp -n")
Ihar Hrachyshka72c34ee2016-01-30 16:18:01 +0100149 ip_cmds = ["addr", "link", "route"]
150 for cmd in ip_cmds + ['netns']:
151 _dump_cmd("ip %s" % cmd)
152 for netns_ in _netns_list():
153 for cmd in ip_cmds:
154 args = {'netns': netns_, 'cmd': cmd}
155 _dump_cmd('sudo ip netns exec %(netns)s ip %(cmd)s' % args)
Sean Dague168b7c22015-05-07 08:57:28 -0400156
157
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100158def ovs_dump():
159 _header("Open vSwitch Dump")
160
Chris Dent57d79672016-02-23 15:38:43 +0000161 # NOTE(cdent): If we're not using neutron + ovs these commands
162 # will not be present so
163 if not _find_cmd('ovs-vsctl'):
164 return
165
fumihiko kakuma578459f2016-04-07 08:15:45 +0900166 bridges = _bridge_list()
fumihiko kakuma60994012016-03-08 20:55:01 +0900167 ofctl_cmds = ('show', 'dump-ports-desc', 'dump-ports', 'dump-flows')
168 ofp_max = _get_ofp_version()
169 vers = 'OpenFlow10'
fumihiko kakuma578459f2016-04-07 08:15:45 +0900170 for i in range(1, ofp_max + 1):
fumihiko kakuma60994012016-03-08 20:55:01 +0900171 vers += ',OpenFlow1' + str(i)
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100172 _dump_cmd("sudo ovs-vsctl show")
fumihiko kakuma60994012016-03-08 20:55:01 +0900173 for ofctl_cmd in ofctl_cmds:
174 for bridge in bridges:
175 args = {'vers': vers, 'cmd': ofctl_cmd, 'bridge': bridge}
176 _dump_cmd("sudo ovs-ofctl --protocols=%(vers)s %(cmd)s %(bridge)s" % args)
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100177
178
Sean Dague97fcc7b2014-06-16 17:24:14 -0400179def process_list():
Sean Dague60a14052015-05-11 14:53:39 -0400180 _header("Process Listing")
181 _dump_cmd("ps axo "
182 "user,ppid,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,args")
Sean Dague97fcc7b2014-06-16 17:24:14 -0400183
184
Sean Dague737e9422015-05-12 19:51:39 -0400185def compute_consoles():
186 _header("Compute consoles")
187 for root, dirnames, filenames in os.walk('/opt/stack'):
188 for filename in fnmatch.filter(filenames, 'console.log'):
189 fullpath = os.path.join(root, filename)
190 _dump_cmd("sudo cat %s" % fullpath)
191
192
Joe Gordon2ebe9932015-06-07 16:57:34 +0900193def guru_meditation_report():
194 _header("nova-compute Guru Meditation Report")
Ian Wienand3a9df1d2015-07-01 06:18:47 +1000195
196 try:
197 subprocess.check_call(["pgrep","nova-compute"])
198 except subprocess.CalledProcessError:
199 print "Skipping as nova-compute does not appear to be running"
200 return
201
Kashyap Chamarthy88725452015-09-14 13:17:56 +0200202 _dump_cmd("kill -s USR2 `pgrep nova-compute`")
Joe Gordon2ebe9932015-06-07 16:57:34 +0900203 print "guru meditation report in nova-compute log"
204
205
Sean Dague97fcc7b2014-06-16 17:24:14 -0400206def main():
207 opts = get_options()
Sean Dagueac9313e2015-07-27 13:33:30 -0400208 fname = filename(opts.dir, opts.name)
Sean Dague97fcc7b2014-06-16 17:24:14 -0400209 print "World dumping... see %s for details" % fname
210 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
211 with open(fname, 'w') as f:
212 os.dup2(f.fileno(), sys.stdout.fileno())
213 disk_space()
214 process_list()
Sean Dague60a14052015-05-11 14:53:39 -0400215 network_dump()
Ihar Hrachyshkac1b7cb12016-02-11 13:50:46 +0100216 ovs_dump()
Sean Dague168b7c22015-05-07 08:57:28 -0400217 iptables_dump()
Sean Dague2da606d2015-08-06 10:02:43 -0400218 ebtables_dump()
Sean Dague737e9422015-05-12 19:51:39 -0400219 compute_consoles()
Joe Gordon2ebe9932015-06-07 16:57:34 +0900220 guru_meditation_report()
Sean Dague97fcc7b2014-06-16 17:24:14 -0400221
222
223if __name__ == '__main__':
224 try:
225 sys.exit(main())
226 except KeyboardInterrupt:
227 sys.exit(1)