#!/usr/bin/env python
#
# Copyright 2006-2016 Hunan Kylin, Inc.  All Rights Reserved.
# 
# NOTICE: ALL INFORMATION CONTAINED HEREIN IS, AND REMAINS THE PROPERTY OF
# HUNAN KYLIN, INC. AND ITS SUPPLIERS, IF ANY. THE INTELLECTUAL AND
# TECHNICAL CONCEPTS CONTAINED HEREIN ARE PROPRIETARY TO HUNAN KYLIN, INC.
# AND ITS SUPPLIERS AND MAY BE COVERED BY U.S. AND FOREIGN PATENTS, PATENTS IN
# PROCESS, AND ARE PROTECTED BY TRADE SECRET OR COPYRIGHT LAW. DISSEMINATION
# OF THIS INFORMATION OR REPRODUCTION OF THIS MATERIAL IS STRICTLY FORBIDDEN
# UNLESS PRIOR WRITTEN PERMISSION IS OBTAINED FROM HUNAN KYLIN, INC.
#
# -*- coding: utf-8 -*-

#
# Main source file for the "ksvd-support-report" executable.
# Creates a tgz file (tar-ball) of KSVD log files, and other
# information useful for diagnosing KSVD or system problems.
# Optionally creates a .zip file as well, or in place of
# the .tgz file.
#

import sys
import errno
from os.path import join, dirname, abspath
import datetime
import shutil

# Find our python libraries
sys.path.append(join(dirname(dirname(abspath(sys.argv[0]))), 'etc/python'))

import KsvdUtil
from KsvdUtil import xl_
import KsvdApp
import KsvdEnv
import KsvdNetConfig
import KsvdScreen



class KsvdSupportReportApp(KsvdApp.KsvdApp):
	#
	# Initialize the application framework
	#
	def __init__(self, appArgs):
		KsvdApp.KsvdApp.__init__(
			self,
			appArgs)

	#
	# Options other than the standard ones (-v, -V, -i, -l)
	#
	def AddOptions(self):
		self.parser.add_option(
			'-s', '--simplename',
			action = 'store_true',
			default = False,
			help = xl_('Overwrite old logs by using constant, simple name for them.'))
		self.parser.add_option(
			'-z', '--zip',
			action = 'store_true',
			default = False,
			help = xl_('Attempt to create .zip file.'))
		self.parser.add_option(
			'-n', '--notar',
			action = 'store_true',
			default = False,
			help = xl_('Do not attempt to create .tgz file.'))
		self.parser.add_option(
			'-d', '--destination',
			help = xl_('Absolute path to place support logs.'),
			default = '/tmp',
			metavar = 'PATH')
		self.parser.add_option(
			'-t', '--tmparea',
			help = xl_('Absolute path of temporary work area to form logs.'),
			default = '/tmp',
			metavar = 'PATH')
		self.parser.add_option(
			'-u', '--user',
			help = xl_('User\'s data to gather. Default = All Users'),
			action = 'append',
			metavar = 'USER',
			default = [],
			type = 'str')
		self.parser.add_option(
			'-k', '--ksvdday',
			help = xl_('how many days of ksvd logs you want. Default is All ksvd logs'),
			default = '',
			metavar = 'NUM')

	#
	# Construct the support report
	#
	def InvokeApp(self):
		# Ensure destination path exists
		absDestination = abspath(self.options.destination)
		if not KsvdUtil.PathExists(absDestination):
			KsvdScreen.PrintError(xl_('Destination path does not exist.'))
			return errno.ENOENT

		# Get basic pointers to variables
		ksvdHomeDir = KsvdEnv.GetMCHomeDir()
		if len(ksvdHomeDir):
			if not KsvdUtil.PathExists(ksvdHomeDir):
				ksvdHomeDir = ''

		ksvdLibDir = KsvdEnv.GetEnvUniqb('VARPREFIX')
		ksvdNetLogDir = KsvdEnv.GetEnvUniqb('NETLOGDIR')
		ksvdLogDir = KsvdEnv.GetEnvUniqb('PRODLOGDIR')
		ksvdRunDir = KsvdEnv.GetEnvUniqb('PRODRUNDIR')
		ksvdEtcDir = KsvdEnv.GetEnvUniqb('ETCPATH')
		ksvdBinDir = KsvdEnv.GetEnvUniqb('BINPATH')

		# Lists of Log directories and files to gather
		# (attempt to avoid duplicates here)
		dirsToGather = [
#			ksvdLogDir,
			ksvdNetLogDir,
			ksvdLibDir + '/login.log',
			ksvdLibDir + '/mc',
			'/etc/sysconfig',
			'/etc/network',
			ksvdEtcDir + '/python']
		if len(ksvdHomeDir):
			dirsToGather += [
#				ksvdHomeDir + '/logs',
				ksvdHomeDir + '/.ksvd',
				ksvdHomeDir + '/.ksvd-local',
				ksvdHomeDir + '/db']

		filesToGather = [
			'/var/log/messages',
			'/var/log/syslog',
			'/var/log/boot.log',
			'/var/log/dmesg',
			'/var/log/kern.log',
			ksvdLibDir + '/settings.node',
			ksvdLibDir + '/KsvdNetConfig.pkl',
			ksvdLibDir + '/KsvdOrigNetConfig.pkl',
			ksvdRunDir + '/ksvd-network.cookie',
			ksvdRunDir + '/ksvd-mount.cookie',
			ksvdBinDir + '/ksvd-support-report',
			ksvdBinDir + '/ksvd-auto-config',
			ksvdBinDir + '/ksvd-tap-control',
			ksvdBinDir + '/rc.ksvd-ovs-network',
			ksvdBinDir + '/ksvd-dump-net-config',
			ksvdBinDir + '/postinstall.sh',
			ksvdBinDir + '/preremove.sh',
			ksvdBinDir + '/rc.ksvd',
			'/proc/meminfo',
			'/proc/cpuinfo',
			'/proc/partitions',
			'/proc/version',
			'/proc/uptime',
			'/proc/swaps',
			'/etc/networks',
			'/etc/inittab',
			'/etc/hostname',
			'/etc/passwd',
			'/etc/group',
			'/etc/resolv.conf',
			'/etc/redhat-release',
			'/etc/lsb-release',
			'/etc/kylin-release',
			'/etc/centos-release',
			'/etc/system-release',
			'/etc/pam.d/net-sf-jpam',
			'/etc/pam.d/uniqb-gauth',
			'/etc/lilo.conf',
			'/boot/grub/grub.cfg']

		if len(self.options.ksvdday):
			filesToGather += KsvdEnv.SystemCall(
				['find', ksvdLogDir, '-mtime', '-' + self.options.ksvdday, '-type', 'f'],
				returnStdOut = True).split()
			filesToGather += KsvdEnv.SystemCall(
				['find', ksvdHomeDir + '/logs', '-mtime', '-' + self.options.ksvdday, '-type', 'f'],
				returnStdOut = True).split()
		else:
			dirsToGather += [ksvdLogDir]
			dirsToGather += [ksvdHomeDir + '/logs']

		if len(ksvdHomeDir):
			# User Directories:
			# PAM or ksvd users?
			PAMUser = KsvdEnv.GetEnvFromSettingsCluster('KSVD_USE_PAM')
			if len(PAMUser) and PAMUser.lower()[0] == 'y':
				# Pam users, may be different directory structures.  Grep to find all the files we want
				# (Might this be a better way to find the Ksvd Pseudo users too?)
				ksvdUserPaths = KsvdEnv.SystemCall(
					['find', dirname(ksvdHomeDir), '-name', 'settings.local'],
					returnStdOut = True).split()
				for userPath in ksvdUserPaths:
					if (not len(self.options.user)) or (os.path.basename(userPath) in self.options.user):
						gatherDir = dirname(userPath)
						filesToGather += [gatherDir + '/macaddr']
						filesToGather += [gatherDir + '/.session-info']
						filesToGather += [gatherDir + '/settings.local']
						filesToGather += [gatherDir + '/settings.local.policy']
						filesToGather += [gatherDir + '/ksvd.last_boot']
						filesToGather += [gatherDir + '/uniqb.txt']
						filesToGather += [gatherDir + '/uniqb.txt.prev']

				ksvdOrgsDir = ksvdHomeDir + '/ksvd-orgs'
				filesToGather += KsvdEnv.SystemCall(
					['find', dirname(ksvdOrgsDir), '-name', 'macaddr'],
					returnStdOut = True).split()
				filesToGather += KsvdEnv.SystemCall(
					['find', dirname(ksvdOrgsDir), '-name', '.session-info'],
					returnStdOut = True).split()
				filesToGather += KsvdEnv.SystemCall(
					['find', dirname(ksvdOrgsDir), '-name', 'settings.local'],
					returnStdOut = True).split()
				filesToGather += KsvdEnv.SystemCall(
					['find', dirname(ksvdOrgsDir), '-name', 'settings.local.policy'],
					returnStdOut = True).split()
				filesToGather += KsvdEnv.SystemCall(
					['find', dirname(ksvdOrgsDir), '-name', 'ksvd.last_boot'],
					returnStdOut = True).split()
				filesToGather += KsvdEnv.SystemCall(
					['find', dirname(ksvdOrgsDir), '-name', 'uniqb.txt'],
					returnStdOut = True).split()
				filesToGather += KsvdEnv.SystemCall(
					['find', dirname(ksvdOrgsDir), '-name', 'uniqb.txt.prev'],
					returnStdOut = True).split()
			else:
				# Ksvd pseudo users, known directory structure.
				ksvdLocalUserDir = ksvdHomeDir + '/ksvd-orgs'
				for ksvdOrg in KsvdUtil.ListDir(ksvdLocalUserDir):
					ksvdUsersDir = ksvdLocalUserDir + '/' + ksvdOrg + '/users/'
					for ksvdDomain in KsvdUtil.ListDir(ksvdUsersDir):
						for ksvdUser in KsvdUtil.ListDir(ksvdLocalUserDir + '/' + ksvdOrg + '/users/' + ksvdDomain):
							if ksvdUser in self.options.user:
								for ksvdUserImage in KsvdUtil.ListDir(ksvdLocalUserDir + '/' + ksvdOrg + '/users/' + ksvdDomain + '/' + ksvdUser):
									gatherDir = ksvdLocalUserDir + '/' + ksvdOrg + '/users/' + ksvdDomain + '/' + ksvdUser + '/' + ksvdUserImage
									filesToGather += [gatherDir + '/macaddr']
									filesToGather += [gatherDir + '/.session-info']
									filesToGather += [gatherDir + '/settings.local']
									filesToGather += [gatherDir + '/settings.local.policy']
									filesToGather += [gatherDir + '/ksvd.last_boot']
									if len(self.options.ksvdday):
										filesToGather += KsvdEnv.SystemCall(
											['find', dirname(ksvdOrgsDir), '-name', 'uniqb.txt*', '-mtime', '-' + self.options.ksvdday, '-type', 'f'],
											returnStdOut = True).split()
									else:
										filesToGather += [gatherDir + '/uniqb.txt']
										filesToGather += [gatherDir + '/uniqb.txt.prev']
				# --user default is all users.
				if not len(self.options.user):
					ksvdOrgsDir = ksvdHomeDir + '/ksvd-orgs'
					filesToGather += KsvdEnv.SystemCall(
						['find', dirname(ksvdOrgsDir), '-name', 'macaddr'],
						returnStdOut = True).split()
					filesToGather += KsvdEnv.SystemCall(
						['find', dirname(ksvdOrgsDir), '-name', '.session-info'],
						returnStdOut = True).split()
					filesToGather += KsvdEnv.SystemCall(
						['find', dirname(ksvdOrgsDir), '-name', 'settings.local'],
						returnStdOut = True).split()
					filesToGather += KsvdEnv.SystemCall(
						['find', dirname(ksvdOrgsDir), '-name', 'settings.local.policy'],
						returnStdOut = True).split()
					filesToGather += KsvdEnv.SystemCall(
						['find', dirname(ksvdOrgsDir), '-name', 'ksvd.last_boot'],
						returnStdOut = True).split()
					if len(self.options.ksvdday):
						filesToGather += KsvdEnv.SystemCall(
							['find', dirname(ksvdOrgsDir), '-name', 'uniqb.txt*', '-mtime', '-' + self.options.ksvdday, '-type', 'f'],
							returnStdOut = True).split()
					else:
						filesToGather += KsvdEnv.SystemCall(
							['find', dirname(ksvdOrgsDir), '-name', 'uniqb.txt'],
							returnStdOut = True).split()
						filesToGather += KsvdEnv.SystemCall(
							['find', dirname(ksvdOrgsDir), '-name', 'uniqb.txt.prev'],
							returnStdOut = True).split()
			# Pools:
			ksvdPools = ksvdHomeDir + '/.ksvd-pools'
			for ksvdPool in filter(
					lambda x: KsvdUtil.IsDir(ksvdPools + '/' + x),
					KsvdUtil.ListDir(ksvdPools)):
				ksvdPoolDir = ksvdPools + '/' + ksvdPool
				filesToGather += [ksvdPoolDir + '/settings.local.policy']
				filesToGather += [ksvdPoolDir + '/vb.pool.title']
				for ksvdPoolImage in filter(
						lambda x: KsvdUtil.IsDir(ksvdPoolDir + '/' + x) and x.isdigit(),
						KsvdUtil.ListDir(ksvdPoolDir)):
					gatherDir = ksvdPoolDir + '/' + ksvdPoolImage
					filesToGather += [gatherDir + '/uniqb.txt']
					filesToGather += [gatherDir + '/uniqb.txt.prev']
					filesToGather += [gatherDir + '/ksvd.last_boot']
					filesToGather += [gatherDir + '/macaddr']

		# Output of these commands is gathered:
		cmdsToGather = [
			['hostname'],
			['ifconfig', '-a'],
			['route', '-n'],
			['netstat', '-in'],
#			['netstat', '-t'],
			['env'],
			['ps', '-ef'],
			['ps', 'aux'],
			['df', '-h'],
			['lspci'],
			['lspci', '-vv'],
			['lsusb'],
			['lsusb', '-v'],
			['mount'],
			['date'],
			['uname', '-a'],
			['runlevel'],
			['vmstat'],
			['iostat'],
			['free', '-m'],
			['brctl', 'show'],
			['sestatus'],
			['iptables', '-L', '-v', '-n', '--line-numbers'],
			['top', '-n1', '-b'],
			['chkconfig', '--list'],
			['dmesg'],
			['ethtool', '-i'],
			['lsmod']]
		distro, distrover = KsvdEnv.DetermineOS()
		if distro in [KsvdEnv.RHEL, KsvdEnv.KYLINOS, KsvdEnv.SUSE]:
			cmdsToGather += [
				['rpm', '-q', '-a']]
		elif distro == KsvdEnv.UBUNTU:
			cmdsToGather += [
				['dpkg-query', '-l']]
		if KsvdNetConfig.IsOVSNetworkingStarted():
			cmdsToGather += [
				['ovs-vsctl', 'show'],
				['ovs-vsctl', 'list', 'Interface'],
				['ovs-vsctl', 'list', 'Port'],
				['ksvd-dump-net-config']]
		else:
			cmdsToGather += [
				['brctl', 'show']]

		# Setup some things
		origPath = KsvdUtil.GetPath()
		KsvdUtil.SetPath(self.options.tmparea, create=True)
		reportName = 'KSVD-Support-Report'
		if not self.options.simplename:
			currNetCfg = KsvdNetConfig.GatherNetworkConfiguration(
				baseConfig = False,
				beSilent = True)
			if currNetCfg and len(currNetCfg.publicAddr):
				hostName = currNetCfg.publicAddr
			else:
				hostName = KsvdEnv.SystemCall(['hostname'], returnStdOut = True)
			dateTime = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
			reportName += '_' + hostName + '_' + dateTime
		reportPath = join(absDestination, reportName)
		tmpReportArea = join(self.options.tmparea, reportName)
		KsvdUtil.RemoveDir(tmpReportArea)
		KsvdUtil.MakePath(tmpReportArea)

		# Gather Directory trees
		for gatherDir in dirsToGather:
			destDir = tmpReportArea + gatherDir
			if KsvdUtil.PathExists(gatherDir) and not KsvdUtil.PathExists(destDir):
				shutil.copytree(gatherDir, destDir)

		# Gather individual files
		for gatherFile in filesToGather:
			if KsvdUtil.FileExists(gatherFile):
				# FIXME: In some cases, the file is not readable by root (NFS mounts with root_squash).
				# This should be handled here (but not in this release):
				# filePerms = oct(os.stat(tmpReportArea + gatherFile)[ST_MODE])[-3:]
				KsvdUtil.MakePathFor(tmpReportArea + gatherFile)
				shutil.copy(gatherFile, tmpReportArea + gatherFile)

		# Gather misc system information
		tmpSystemInfoPath = tmpReportArea + '/system-info'
		KsvdUtil.MakePath(tmpSystemInfoPath)
		for cmd in cmdsToGather:
			if KsvdUtil.FileExists(KsvdEnv.FileDB(cmd[0])):
				cmdOutput = KsvdEnv.SystemCall(cmd, returnStdOut = True)
				KsvdUtil.EchoToFile(
					'Output from \"' + ' '.join(cmd) + '\" command:' + '\n---\n' + cmdOutput + '\n',
					tmpSystemInfoPath + '/' + '_'.join(cmd) + '.txt')

		# Fold the report up into a tarball
		tarExists = False
		if not self.options.notar:
			cmd = [
				'tar',
				'-czf',
				reportPath + '.tgz',
				reportName]
			if not KsvdEnv.SystemCall(cmd):
				tarExists = True

		# If the zip utility exists,
		# fold the report up into a zipfile
		zipExists = False
		if self.options.zip:
			cmd = ['zip', '-r', reportPath + '.zip', reportName]
			if not KsvdEnv.SystemCall(cmd):
				zipExists = True

		# Remove the temporary dir, but leave the archives.
		KsvdUtil.RemoveDir(tmpReportArea)
		KsvdUtil.SetPath(origPath)

		# Report where the archives are
		if not tarExists and not zipExists:
			KsvdScreen.PrintError(
				xl_('Unable to create either a .zip or .tgz file.'
					'\nThese files may be too large to fit on drive.'))
			return errno.EFBIG
		finalMessage = xl_(
			'Report Archives have been created and can be found in:\n')
		if tarExists:
			finalMessage += '\n ' + reportPath + '.tgz'
		if zipExists:
			finalMessage += '\n ' + reportPath + '.zip'
		KsvdScreen.PrintMessage(finalMessage)

		# Exit
		return 0

# Run this program, that's all.
KsvdSupportReportApp(appArgs = sys.argv).RunApp()





