summaryrefslogtreecommitdiff
path: root/cloudinit/apport.py
diff options
context:
space:
mode:
authorChad Smith <chad.smith@canonical.com>2017-09-12 10:27:07 -0600
committerScott Moser <smoser@brickies.net>2017-09-15 15:46:02 -0400
commite626966ee7d339b53d2c8b14a8f2ff8e3fe892ee (patch)
treebba6a1a7d8d31b1fc790b7bff1ca94183c278b78 /cloudinit/apport.py
parentda1db792b2721d94ef85df8c136e78012c49c6e5 (diff)
downloadcloud-init-git-e626966ee7d339b53d2c8b14a8f2ff8e3fe892ee.tar.gz
cmdline: add collect-logs subcommand.
Add a new collect-logs sub command to the cloud-init CLI. This script will collect all logs pertinent to a cloud-init run and store them in a compressed tar-gzipped file. This tarfile can be attached to any cloud-init bug filed in order to aid in bug triage and resolution. A cloudinit.apport module is also added that allows apport interaction. Here is an example bug filed via ubuntu-bug cloud-init: LP: #1716975. Once the apport launcher is packaged in cloud-init, bugs can be filed against cloud-init with the following command: ubuntu-bug cloud-init LP: #1607345
Diffstat (limited to 'cloudinit/apport.py')
-rw-r--r--cloudinit/apport.py105
1 files changed, 105 insertions, 0 deletions
diff --git a/cloudinit/apport.py b/cloudinit/apport.py
new file mode 100644
index 00000000..221f341c
--- /dev/null
+++ b/cloudinit/apport.py
@@ -0,0 +1,105 @@
+# Copyright (C) 2017 Canonical Ltd.
+#
+# This file is part of cloud-init. See LICENSE file for license information.
+
+'''Cloud-init apport interface'''
+
+try:
+ from apport.hookutils import (
+ attach_file, attach_root_command_outputs, root_command_output)
+ has_apport = True
+except ImportError:
+ has_apport = False
+
+
+KNOWN_CLOUD_NAMES = [
+ 'Amazon - Ec2', 'AliYun', 'AltCloud', 'Azure', 'Bigstep', 'CloudSigma',
+ 'CloudStack', 'DigitalOcean', 'GCE - Google Compute Engine', 'MAAS',
+ 'NoCloud', 'OpenNebula', 'OpenStack', 'OVF', 'Scaleway', 'SmartOS',
+ 'VMware', 'Other']
+
+# Potentially clear text collected logs
+CLOUDINIT_LOG = '/var/log/cloud-init.log'
+CLOUDINIT_OUTPUT_LOG = '/var/log/cloud-init-output.log'
+USER_DATA_FILE = '/var/lib/cloud/instance/user-data.txt' # Optional
+
+
+def attach_cloud_init_logs(report, ui=None):
+ '''Attach cloud-init logs and tarfile from 'cloud-init collect-logs'.'''
+ attach_root_command_outputs(report, {
+ 'cloud-init-log-warnings':
+ 'egrep -i "warn|error" /var/log/cloud-init.log',
+ 'cloud-init-output.log.txt': 'cat /var/log/cloud-init-output.log'})
+ root_command_output(
+ ['cloud-init', 'collect-logs', '-t', '/tmp/cloud-init-logs.tgz'])
+ attach_file(report, '/tmp/cloud-init-logs.tgz', 'logs.tgz')
+
+
+def attach_hwinfo(report, ui=None):
+ '''Optionally attach hardware info from lshw.'''
+ prompt = (
+ 'Your device details (lshw) may be useful to developers when'
+ ' addressing this bug, but gathering it requires admin privileges.'
+ ' Would you like to include this info?')
+ if ui and ui.yesno(prompt):
+ attach_root_command_outputs(report, {'lshw.txt': 'lshw'})
+
+
+def attach_cloud_info(report, ui=None):
+ '''Prompt for cloud details if available.'''
+ if ui:
+ prompt = 'Is this machine running in a cloud environment?'
+ response = ui.yesno(prompt)
+ if response is None:
+ raise StopIteration # User cancelled
+ if response:
+ prompt = ('Please select the cloud vendor or environment in which'
+ ' this instance is running')
+ response = ui.choice(prompt, KNOWN_CLOUD_NAMES)
+ if response:
+ report['CloudName'] = KNOWN_CLOUD_NAMES[response[0]]
+ else:
+ report['CloudName'] = 'None'
+
+
+def attach_user_data(report, ui=None):
+ '''Optionally provide user-data if desired.'''
+ if ui:
+ prompt = (
+ 'Your user-data or cloud-config file can optionally be provided'
+ ' from {0} and could be useful to developers when addressing this'
+ ' bug. Do you wish to attach user-data to this bug?'.format(
+ USER_DATA_FILE))
+ response = ui.yesno(prompt)
+ if response is None:
+ raise StopIteration # User cancelled
+ if response:
+ attach_file(report, USER_DATA_FILE, 'user_data.txt')
+
+
+def add_bug_tags(report):
+ '''Add any appropriate tags to the bug.'''
+ if 'JournalErrors' in report.keys():
+ errors = report['JournalErrors']
+ if 'Breaking ordering cycle' in errors:
+ report['Tags'] = 'systemd-ordering'
+
+
+def add_info(report, ui):
+ '''This is an entry point to run cloud-init's apport functionality.
+
+ Distros which want apport support will have a cloud-init package-hook at
+ /usr/share/apport/package-hooks/cloud-init.py which defines an add_info
+ function and returns the result of cloudinit.apport.add_info(report, ui).
+ '''
+ if not has_apport:
+ raise RuntimeError(
+ 'No apport imports discovered. Apport functionality disabled')
+ attach_cloud_init_logs(report, ui)
+ attach_hwinfo(report, ui)
+ attach_cloud_info(report, ui)
+ attach_user_data(report, ui)
+ add_bug_tags(report)
+ return True
+
+# vi: ts=4 expandtab