aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile14
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control15
-rw-r--r--debian/copyright0
-rw-r--r--debian/pyversions1
-rwxr-xr-xdebian/rules7
-rw-r--r--sos/__init__.py.in74
-rw-r--r--sos/plugins/__init__.py155
-rw-r--r--sos/plugins/as7.py179
-rw-r--r--sos/plugins/dpkg.py (renamed from sos/__init__.py)32
-rw-r--r--sos/policies/Makefile20
-rw-r--r--sos/policies/__init__.py7
-rw-r--r--sos/policies/debian.py156
-rw-r--r--sos/policies/ubuntu.py31
-rw-r--r--sos/policies/windows.py10
-rw-r--r--sos/sosreport.py3
-rw-r--r--sos/utilities.py12
-rw-r--r--tests/option_tests.py34
20 files changed, 562 insertions, 195 deletions
diff --git a/.gitignore b/.gitignore
index 790380d2..0176f5ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ tags
buildjar/
gpgkeys/rhsupport.*
rpm-build/*
+sos/__init__.py
diff --git a/Makefile b/Makefile
index 1e1b249a..88a6532d 100644
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,9 @@
NAME = sos
VERSION = $(shell echo `awk '/^Version:/ {print $$2}' sos.spec`)
RELEASE = $(shell echo `awk '/^Release:/ {gsub(/\%.*/,""); print $2}' sos.spec`)
-REPO = http://svn.fedorahosted.org/svn/sos
+REPO = http://github.com/sosreport
-SUBDIRS = po sos sos/plugins
+SUBDIRS = po sos sos/plugins sos/policies
PYFILES = $(wildcard *.py)
# OS X via brew
# MSGCAT = /usr/local/Cellar/gettext/0.18.1.1/bin/msgcat
@@ -33,7 +33,7 @@ ZIP_DEST = $(SRC_BUILD)/$(ARCHIVE_NAME)
build:
for d in $(SUBDIRS); do make -C $$d; [ $$? = 0 ] || exit 1 ; done
-install:
+install: updateversion
mkdir -p $(DESTDIR)/usr/sbin
mkdir -p $(DESTDIR)/usr/share/man/man1
mkdir -p $(DESTDIR)/usr/share/man/man5
@@ -47,9 +47,11 @@ install:
install -m644 LICENSE README TODO $(DESTDIR)/usr/share/$(NAME)/.
install -m644 $(NAME).conf $(DESTDIR)/etc/$(NAME).conf
install -m644 gpgkeys/rhsupport.pub $(DESTDIR)/usr/share/$(NAME)/.
- sed 's/@SOSVERSION@/$(VERSION)/g' < sos/__init__.py > sos/__init__.py
for d in $(SUBDIRS); do make DESTDIR=`cd $(DESTDIR); pwd` -C $$d install; [ $$? = 0 ] || exit 1; done
+updateversion:
+ sed 's/@SOSVERSION@/$(VERSION)/g' sos/__init__.py.in > sos/__init__.py
+
$(NAME)-$(VERSION).tar.gz: clean gpgkey
@mkdir -p $(ARCHIVE_DIR)
@tar -cv sosreport sos doc man po sos.conf TODO LICENSE README sos.spec Makefile | tar -x -C $(ARCHIVE_DIR)
@@ -58,7 +60,7 @@ $(NAME)-$(VERSION).tar.gz: clean gpgkey
@tar Ccvzf $(RPM_BUILD_DIR) $(RPM_BUILD_DIR)/$(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
clean:
- @rm -fv *~ .*~ changenew ChangeLog.old $(NAME)-$(VERSION).tar.gz sosreport.1.gz
+ @rm -fv *~ .*~ changenew ChangeLog.old $(NAME)-$(VERSION).tar.gz sosreport.1.gz sos.conf.5.gz
@rm -rf rpm-build
@for i in `find . -iname *.pyc`; do \
rm $$i; \
@@ -85,7 +87,7 @@ po: clean
cp $(PO_DIR)/sos_en.properties $(PO_DIR)/sos_en_US.properties
cp $(PO_DIR)/sos_en.properties $(PO_DIR)/sos.properties
-as7: po
+as7: po updateversion
cp -r sos/* $(SRC_BUILD)/sos/
find $(SRC_BUILD)/sos/plugins/ -not -name "*as7.py" -not -name "*__init__.py" -type f -delete
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 00000000..a788b652
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+sosreport (2.3) unstable; urgency=low
+
+ * Initial release.
+
+ -- Adam Stokes <adam.stokes@canonical.com> Mon, 20 Feb 2012 16:41:39 +0000
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 00000000..45a4fb75
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+8
diff --git a/debian/control b/debian/control
new file mode 100644
index 00000000..1625c69e
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,15 @@
+Source: sosreport
+Maintainer: Adam Stokes <adam.stokes@canonical.com>
+Section: python
+Priority: optional
+Standards-Version: 3.9.2
+Build-Depends: debhelper (>= 8), python-support, python (>=2.7), gettext
+
+Package: sosreport
+Architecture: any
+Depends: ${python:Depends}, ${misc:Depends}
+Description: A set of tools to gather troubleshooting information from a system
+ Sos is a set of tools that gathers information about system
+ hardware and configuration. The information can then be used for
+ diagnostic purposes and debugging. Sos is commonly used to help
+ support technicians and developers.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/debian/copyright
diff --git a/debian/pyversions b/debian/pyversions
new file mode 100644
index 00000000..3ad2293e
--- /dev/null
+++ b/debian/pyversions
@@ -0,0 +1 @@
+2.7-
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 00000000..3e0dfd04
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,7 @@
+#!/usr/bin/make -f
+
+DH_ALWAYS_EXCLUDE=.git
+
+%:
+ dh $@
+
diff --git a/sos/__init__.py.in b/sos/__init__.py.in
new file mode 100644
index 00000000..f703a834
--- /dev/null
+++ b/sos/__init__.py.in
@@ -0,0 +1,74 @@
+## Copyright 2010 Red Hat, Inc.
+## Author: Adam Stokes <astokes@fedoraproject.org>
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+"""
+This module houses the i18n setup and message function. The default is to use
+gettext to internationalize messages. If the client calls set_i18n and passes a
+path to a resource bundle the _ method will be changed to use java
+ResourceBundle code to present messages.
+"""
+
+__version__ = "@SOSVERSION@"
+
+import gettext
+gettext_dir = "/usr/share/locale"
+gettext_app = "sos"
+
+gettext.bindtextdomain(gettext_app, gettext_dir)
+
+def _default(msg):
+ return gettext.dgettext(gettext_app, msg)
+
+_sos = _default
+
+def _get_classloader(jarfile):
+ """Makes a new classloader loaded with the jarfile. This is useful since it
+ seems very difficult to get jars added to the correct classpath for
+ ResourceBundle.getBundle to find."""
+ from java.net import URLClassLoader, URL
+ from java.io import File
+ import jarray
+
+ file_ = File(jarfile)
+ ary = jarray.array([file_.toURL()], URL)
+ classloader = URLClassLoader.newInstance(ary)
+ return classloader
+
+def set_i18n(path=None, basename="sos.po.sos"):
+ """Use this method to change the default i18n behavior from gettext to java
+ ResourceBundle.getString. This is really only useful when using jython.
+ Path is expected to be the path to a jarfile that contains the translation
+ files (.properties)"""
+
+ # Since we are trying to modify the module-level _sos variable
+ # we have to declare it global
+ global _sos
+
+ try:
+ from java.util import ResourceBundle, Locale
+
+ rb = ResourceBundle.getBundle(basename,
+ Locale.getDefault(), _get_classloader(path))
+
+ def _java(msg):
+ try:
+ return rb.getString(msg).encode('utf-8')
+ except:
+ return msg
+ _sos = _java
+ except:
+ pass
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
index ebb76ea3..a1d38f45 100644
--- a/sos/plugins/__init__.py
+++ b/sos/plugins/__init__.py
@@ -39,7 +39,12 @@ from time import time
from itertools import *
from collections import deque
import logging
+import urllib2
+try:
+ import json
+except ImportError:
+ import simplejson as json
def commonPrefix(l1, l2, common = None):
"""
@@ -299,14 +304,16 @@ class Plugin(object):
any of the option names is returned."""
def _check(key):
- if hasattr(key, "__iter__"):
+ if hasattr(optionname, "__iter__"):
return key in optionname
else:
return key == optionname
for name, parms in izip(self.optNames, self.optParms):
if _check(name):
- return parms['enabled']
+ val = parms['enabled']
+ if val != None:
+ return val
for key, value in self.cInfo.get('global_plugin_options', {}).iteritems():
if _check(key):
@@ -437,6 +444,8 @@ class Plugin(object):
if not (status == 127 or status == 32512): # if not command_not_found
outfn_strip = outfn[len(self.cInfo['cmddir'])+1:]
self.archive.add_string(shout, outfn)
+ if root_symlink:
+ self.archive.add_link(outfn, root_symlink)
else:
self.soslog.debug("could not run command: %s" % exe)
outfn = None
@@ -601,10 +610,152 @@ class RedHatPlugin(object):
"""Tagging class to indicate that this plugin works with Red Hat Linux"""
pass
+class UbuntuPlugin(object):
+ """Tagging class to indicate that this plugin works with Ubuntu Linux"""
+ pass
+
+class DebianPlugin(object):
+ """Tagging class to indicate that this plugin works with Debian Linux"""
+ pass
+
class IndependentPlugin(object):
"""Tagging class that indicates this plugin can run on any platform"""
pass
+class AS7Mixin(object):
+ """A mixin class that adds some helpful methods for AS7 related plugins"""
+
+ class Request(object):
+
+ def __init__(self, resource, operation="read-resource", parameters=None):
+ self.resource = resource
+ self.operation = operation
+ if parameters:
+ self.parameters = parameters
+ else:
+ self.parameters = {}
+
+ def url_parts(self):
+ """Generator function to split a url into (key, value) tuples. The url
+ should contain an even number of pairs. In the case of / the generator
+ will immediately stop iteration."""
+ parts = self.resource.strip("/").split("/")
+
+ if parts == ['']:
+ raise StopIteration
+
+ while parts:
+ yield (parts.pop(0), parts.pop(0))
+
+ def get_jboss_home(self):
+ return self.getOption(('home', 'as7_home')) or os.getenv("JBOSS_HOME", None)
+
+ def query(self, request_obj):
+ try:
+ return self.query_java(request_obj)
+ except Exception, e:
+ self.addAlert("JBOSS API call failed, falling back to HTTP: %s" % e)
+ return self.query_http(request_obj)
+
+ def _get_opt(self, first, second, default=None):
+ val = self.getOption(first)
+ if val:
+ return val
+ val = self.getOption(second)
+ if val:
+ return val
+ return default
+
+ def query_java(self, request_obj):
+ from org.jboss.dmr import ModelNode
+ controller_client = self.getOption('controller_client_proxy')
+ if not controller_client:
+ raise AttributeError("Controller Client is not available")
+
+ request = ModelNode()
+ request.get("operation").set(request_obj.operation)
+
+ for key, val in request_obj.url_parts():
+ request.get('address').add(key,val)
+
+ if request_obj.parameters:
+ for key, value in request_obj.parameters.iteritems():
+ request.get(key).set(value)
+
+ return controller_client.execute(request).toJSONString(True)
+
+ def query_http(self, request_obj, postdata=None):
+ host = self._get_opt('host', 'as7_host')
+ port = self._get_opt('port', 'as7_port')
+
+ username = self._get_opt('user', 'as7_user')
+ password = self._get_opt('pass', 'as7_pass')
+
+ uri = "http://%s:%s/management" % (host,port)
+
+ json_data = {'operation': request_obj.operation,
+ 'address': []}
+
+ for key, val in request_obj.url_parts():
+ json_data['address'].append({key:val})
+
+ for key, val in request_obj.parameters.iteritems():
+ json_data[key] = val
+
+ postdata = json.dumps(json_data)
+ headers = {'Content-Type': 'application/json',
+ 'Accept': 'application/json'}
+
+ opener = urllib2.build_opener()
+
+ if username and password:
+ passwd_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
+ passwd_manager.add_password(realm="ManagementRealm",
+ uri=uri,
+ user=username,
+ passwd=password)
+ digest_auth_handler = urllib2.HTTPDigestAuthHandler(passwd_manager)
+ basic_auth_handler = urllib2.HTTPBasicAuthHandler(passwd_manager)
+
+ opener.add_handler(digest_auth_handler)
+ opener.add_handler(basic_auth_handler)
+
+ req = urllib2.Request(uri, data=postdata, headers=headers)
+
+ try:
+ resp = opener.open(req)
+ return resp.read()
+ except Exception, e:
+ err_msg = "Could not query url: %s; error: %s" % (uri, e)
+ self.addAlert(err_msg)
+ return err_msg
+
+ def set_domain_info(self, parameters=None):
+ """This function will add host controller and server instance
+ name data if it is present to the desired resource. This is to support
+ domain-mode operation in AS7"""
+ host_controller_name = self.getOption("as7_host_controller_name")
+ server_name = self.getOption("as7_server_name")
+
+ if host_controller_name and server_name:
+ if not parameters:
+ parameters = {}
+
+ parameters['host'] = host_controller_name
+ parameters['server'] = server_name
+
+ return parameters
+
+
+ def resource_to_file(self, resource=None, parameters=None, operation='read-resource', outfile=None):
+ parameters = self.set_domain_info(parameters)
+
+ r = self.Request(resource=resource,
+ parameters=parameters,
+ operation=operation)
+ self.addStringAsFile(self.query(r), filename=outfile)
+
+
def import_plugin(name):
"""Import name as a module and return a list of all classes defined in that
module"""
diff --git a/sos/plugins/as7.py b/sos/plugins/as7.py
index c31696ef..e56d91dd 100644
--- a/sos/plugins/as7.py
+++ b/sos/plugins/as7.py
@@ -7,38 +7,10 @@ import tempfile
from xml.etree import ElementTree
from itertools import chain
-try:
- import json
-except ImportError:
- import simplejson as json
-
-from sos.plugins import Plugin, IndependentPlugin
+from sos.plugins import Plugin, IndependentPlugin, AS7Mixin
from sos.utilities import DirTree, find, checksum
-class Request(object):
-
- def __init__(self, resource, operation="read-resource", parameters=None):
- self.resource = resource
- self.operation = operation
- if parameters:
- self.parameters = parameters
- else:
- self.parameters = {}
-
- def url_parts(self):
- """Generator function to split a url into (key, value) tuples. The url
- should contain an even number of pairs. In the case of / the generator
- will immediately stop iteration."""
- parts = self.resource.strip("/").split("/")
-
- if parts == ['']:
- raise StopIteration
-
- while parts:
- yield (parts.pop(0), parts.pop(0))
-
-
-class AS7(Plugin, IndependentPlugin):
+class AS7(Plugin, IndependentPlugin, AS7Mixin):
"""JBoss related information
"""
@@ -69,29 +41,18 @@ class AS7(Plugin, IndependentPlugin):
self.addAlert(msg)
def __getJbossHome(self):
- """
- Will attempt to locate the JBoss installation dir in either jboss.home or
- scrape it from the environment variable JBOSS_HOME.
- Returns:
- True JBOSS_HOME is set and the path exists. False otherwise.
- """
- if self.getOption("home"):
- ## Prefer this value first over the ENV
- self.__jbossHome=self.getOption(("home", "as7_home"))
- self.addAlert("INFO: The JBoss installation directory supplied to SOS is " +
- self.__jbossHome)
- elif os.environ.get("JBOSS_HOME"):
- self.__jbossHome=os.environ.get("JBOSS_HOME")
- self.addAlert("INFO: The JBoss installation directory (i.e. JBOSS_HOME) from the environment is " +
- self.__jbossHome)
- else:
+ self.__jbossHome = self.get_jboss_home()
+ if not self.__jbossHome:
self.addAlert("ERROR: The JBoss installation directory was not supplied.\
The JBoss SOS plug-in cannot continue.")
return False
+ self.addAlert("INFO: The JBoss installation directory supplied to SOS is " +
+ self.__jbossHome)
return True
+
def __getMd5(self, file):
"""Returns the MD5 sum of the specified file."""
@@ -122,8 +83,8 @@ class AS7(Plugin, IndependentPlugin):
return manifest
def __getStdJarInfo(self):
- found = False
jar_info_list = []
+
for jarFile in find("*.jar", self.__jbossHome):
checksum = self.__getMd5(jarFile)
manifest = self.__getManifest(jarFile)
@@ -131,8 +92,8 @@ class AS7(Plugin, IndependentPlugin):
if manifest:
manifest = manifest.strip()
jar_info_list.append((path, checksum, manifest))
- found = True
- if found:
+
+ if jar_info_list:
jar_info_list.sort()
self.addStringAsFile("\n".join([
"%s\n%s\n%s\n" % (name, checksum, manifest)
@@ -141,118 +102,21 @@ class AS7(Plugin, IndependentPlugin):
else:
self.addAlert("WARN: No jars found in JBoss system path (" + self.__jbossHome + ").")
- def query(self, request_obj):
- try:
- return self.query_java(request_obj)
- except Exception, e:
- self.addAlert("JBOSS API call failed, falling back to HTTP: %s" % e)
- return self.query_http(request_obj)
-
- def query_java(self, request_obj):
- from org.jboss.dmr import ModelNode
- controller_client = self.getOption('controller_client_proxy')
- if not controller_client:
- raise AttributeError("Controller Client is not available")
-
- request = ModelNode()
- request.get("operation").set(request_obj.operation)
-
- for key, val in request_obj.url_parts():
- request.get('address').add(key,val)
-
- if request_obj.parameters:
- for key, value in request_obj.parameters.iteritems():
- request.get(key).set(value)
-
- return controller_client.execute(request).toJSONString(True)
-
- def query_http(self, request_obj, postdata=None):
- host = self.getOption(('host', 'as7_host'))
- port = self.getOption(('port', 'as7_port'))
-
- username = self.getOption(('user', 'as7_user'), None)
- password = self.getOption(('pass', 'as7_pass'), None)
-
- uri = "http://%s:%s/management" % (host,port)
-
- json_data = {'operation': request_obj.operation,
- 'address': []}
-
- for key, val in request_obj.url_parts():
- json_data['address'].append({key:val})
-
- for key, val in request_obj.parameters.iteritems():
- json_data[key] = val
-
- postdata = json.dumps(json_data)
- headers = {'Content-Type': 'application/json',
- 'Accept': 'application/json'}
-
- opener = urllib2.build_opener()
-
- if username and password:
- passwd_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
- passwd_manager.add_password(realm="ManagementRealm",
- uri=uri,
- user=username,
- passwd=password)
- digest_auth_handler = urllib2.HTTPDigestAuthHandler(passwd_manager)
- basic_auth_handler = urllib2.HTTPBasicAuthHandler(passwd_manager)
-
- opener.add_handler(digest_auth_handler)
- opener.add_handler(basic_auth_handler)
-
- req = urllib2.Request(uri, data=postdata, headers=headers)
-
- try:
- resp = opener.open(req)
- return resp.read()
- except Exception, e:
- err_msg = "Could not query url: %s; error: %s" % (uri, e)
- self.addAlert(err_msg)
- return err_msg
-
- def _set_domain_info(self, parameters=None):
- """This function will add host controller and server instance
- name data if it is present to the desired resource. This is to support
- domain-mode operation in AS7"""
- host_controller_name = self.getOption("as7_host_controller_name")
- server_name = self.getOption("as7_server_name")
-
- if host_controller_name and server_name:
- if not parameters:
- parameters = {}
-
- parameters['host'] = host_controller_name
- parameters['server'] = server_name
-
- return parameters
-
-
- def _resource_to_file(self, resource=None, parameters=None, operation='read-resource', outfile=None):
- parameters = self._set_domain_info(parameters)
-
- r = Request(resource=resource,
- parameters=parameters,
- operation=operation)
- self.addStringAsFile(self.query(r), filename=outfile)
-
-
def get_online_data(self):
"""
This function co-locates calls to the management api that gather
information from a running system.
"""
- self._resource_to_file(resource="/",
+ self.resource_to_file(resource="/",
parameters={"recursive": "true"},
outfile="configuration.json")
- self._resource_to_file(resource="/core-service/service-container",
+ self.resource_to_file(resource="/core-service/service-container",
operation="dump-services",
outfile="dump-services.json")
- self._resource_to_file(resource="/subsystem/modcluster",
+ self.resource_to_file(resource="/subsystem/modcluster",
operation="read-proxies-configuration",
outfile="cluster-proxies-configuration.json")
- self._resource_to_file(resource="/core-service/platform-mbean/type/threading",
+ self.resource_to_file(resource="/core-service/platform-mbean/type/threading",
operation="dump-all-threads",
parameters={"locked-synchronizers": "true",
"locked-monitors": "true"},
@@ -275,14 +139,19 @@ class AS7(Plugin, IndependentPlugin):
## First get everything in the conf dir
confDir = os.path.join(path, "configuration")
self.addForbiddenPath(os.path.join(confDir, 'mgmt-users.properties'))
-
- self.doCopyFileOrDir(confDir, sub=(self.__jbossHome, 'JBOSSHOME'))
+ self.addForbiddenPath(os.path.join(confDir, 'application-users.properties'))
for logFile in find("*.log", path):
self.addCopySpecLimit(logFile,
self.getOption("logsize"),
sub=(self.__jbossHome, 'JBOSSHOME'))
+ for xml in find("*.xml", path):
+ self.addCopySpec(xml, sub=(self.__jbossHome, 'JBOSSHOME'))
+
+ for prop in find("*.properties", path):
+ self.addCopySpec(prop, sub=(self.__jbossHome, 'JBOSSHOME'))
+
deployment_info = self.__get_deployment_info(confDir)
deployments = self.__get_deployments(path)
for deployment in deployments:
@@ -337,8 +206,6 @@ class AS7(Plugin, IndependentPlugin):
def setup(self):
- ## We need to know where JBoss is installed and if we can't find it we
- ## must exit immediately.
if not self.__getJbossHome():
self.exit_please()
@@ -347,11 +214,9 @@ class AS7(Plugin, IndependentPlugin):
except urllib2.URLError:
pass
- ## Generate hashes of the stock Jar files for the report.
if self.getOption("stdjar"):
self.__getStdJarInfo()
- ## Generate a Tree for JBOSS_HOME
tree = DirTree(self.__jbossHome).as_string()
self.addStringAsFile(tree, "jboss_home_tree.txt")
@@ -377,7 +242,7 @@ class AS7(Plugin, IndependentPlugin):
r"=(.*)",
r'=********')
-# Remove PW from -ds.xml files
+# Remove PW from -ds.xml files
tmp = os.path.join(path, "deployments")
for dsFile in find("*-ds.xml", tmp):
self.doRegexSub(dsFile,
diff --git a/sos/__init__.py b/sos/plugins/dpkg.py
index 8d45216a..47c2c9e4 100644
--- a/sos/__init__.py
+++ b/sos/plugins/dpkg.py
@@ -1,7 +1,4 @@
-## Copyright 2010 Red Hat, Inc.
-## Author: Adam Stokes <astokes@fedoraproject.org>
-
-## This program is free software; you can redistribute it and/or modify
+### This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
@@ -15,24 +12,11 @@
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-__version__ = "@SOSVERSION@"
-
-try:
- from java.util import ResourceBundle
-
- rb = ResourceBundle.getBundle("sos.po.sos")
-
- def _sos(msg):
- try:
- return rb.getString(msg).encode('utf-8')
- except:
- return msg
-except:
- import gettext
- gettext_dir = "/usr/share/locale"
- gettext_app = "sos"
-
- gettext.bindtextdomain(gettext_app, gettext_dir)
+from sos.plugins import Plugin, DebianPlugin, UbuntuPlugin
- def _sos(msg):
- return gettext.dgettext(gettext_app, msg)
+class dpkg(Plugin, DebianPlugin, UbuntuPlugin):
+ """dpkg information
+ """
+ def setup(self):
+ self.addCopySpec("/var/log/dpkg.log")
+ self.collectExtOutput("/usr/bin/dpkg-query -W -f='${Package}-${Version}-${Architecture}\n' \*", root_symlink = "installed-debs")
diff --git a/sos/policies/Makefile b/sos/policies/Makefile
new file mode 100644
index 00000000..8fd6a1bf
--- /dev/null
+++ b/sos/policies/Makefile
@@ -0,0 +1,20 @@
+PYTHON=python
+PACKAGE = $(shell basename `pwd`)
+PYFILES = $(wildcard *.py)
+PYVER := $(shell $(PYTHON) -c 'import sys; print "%.3s" %(sys.version)')
+PYSYSDIR := $(shell $(PYTHON) -c 'import sys; print sys.prefix')
+PYLIBDIR = $(PYSYSDIR)/lib/python$(PYVER)
+PKGDIR = $(PYLIBDIR)/site-packages/sos/$(PACKAGE)
+
+all:
+ echo "nada"
+
+clean:
+ rm -f *.pyc *.pyo *~
+
+install:
+ mkdir -p $(DESTDIR)/$(PKGDIR)
+ for p in $(PYFILES) ; do \
+ install -m 755 $$p $(DESTDIR)/$(PKGDIR)/$$p; \
+ done
+ $(PYTHON) -c "import compileall; compileall.compile_dir('$(DESTDIR)/$(PKGDIR)', 1, '$(PYDIR)', 1)"
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
index a2234016..ad6539c0 100644
--- a/sos/policies/__init__.py
+++ b/sos/policies/__init__.py
@@ -24,8 +24,11 @@ def load(cache={}):
for policy in import_policy(module):
if policy.check():
cache['policy'] = policy()
- return policy()
- return GenericPolicy()
+
+ if 'policy' not in cache:
+ cache['policy'] = GenericPolicy()
+
+ return cache['policy']
class PackageManager(object):
diff --git a/sos/policies/debian.py b/sos/policies/debian.py
new file mode 100644
index 00000000..6d11482d
--- /dev/null
+++ b/sos/policies/debian.py
@@ -0,0 +1,156 @@
+from __future__ import with_statement
+
+from sos import _sos as _
+from sos.plugins import DebianPlugin, IndependentPlugin
+from sos.policies import PackageManager, Policy
+from sos.utilities import shell_out
+
+import os
+import sys
+import re
+
+class DebianPackageManager(PackageManager):
+
+ def _get_deb_list(self):
+ pkg_list = shell_out(["dpkg-query",
+ "-W",
+ "-f=",
+ "'${Package}|${Version}\\n' \*"]).splitlines()
+ self._debs = {}
+ for pkg in pkg_list:
+ name, version = pkg.split("|")
+ self._debs[name] = {
+ 'name': name,
+ 'version': version
+ }
+
+ def allPkgsByName(self, name):
+ return fnmatch.filter(self.allPkgs().keys(), name)
+
+ def allPkgsByNameRegex(self, regex_name, flags=None):
+ reg = re.compile(regex_name, flags)
+ return [pkg for pkg in self.allPkgs().keys() if reg.match(pkg)]
+
+ def pkgByName(self, name):
+ try:
+ self.AllPkgsByName(name)[-1]
+ except Exception:
+ return None
+
+ def allPkgs(self):
+ if not self._debs:
+ self._debs = self._get_deb_list()
+ return self._debs
+
+ def pkgNVRA(self, pkg):
+ fields = pkg.split("-")
+ version, release, arch = fields[-3:]
+ name = "-".join(fields[:-3])
+ return (name, version, release, arch)
+
+class DebianPolicy(Policy):
+ def __init__(self):
+ super(DebianPolicy, self).__init__()
+ self.reportName = ""
+ self.ticketNumber = ""
+ self.package_manager = DebianPackageManager()
+
+ def validatePlugin(self, plugin_class):
+ "Checks that the plugin will execute given the environment"
+ return issubclass(plugin_class, DebianPlugin) or issubclass(plugin_class, IndependentPlugin)
+
+ @classmethod
+ def check(self):
+ """This method checks to see if we are running on Debian.
+ It returns True or False."""
+ if os.path.isfile('/etc/debian_version'):
+ return True
+ return False
+
+ def preferedArchive(self):
+ from sos.utilities import TarFileArchive
+ return TarFileArchive
+
+ def getPreferredHashAlgorithm(self):
+ checksum = "md5"
+ try:
+ fp = open("/proc/sys/crypto/fips_enabled", "r")
+ except:
+ return checksum
+
+ fips_enabled = fp.read()
+ if fips_enabled.find("1") >= 0:
+ checksum = "sha256"
+ fp.close()
+ return checksum
+
+ def pkgByName(self, name):
+ return self.package_manager.pkgByName(name)
+
+ def runlevelDefault(self):
+ try:
+ with open("/etc/inittab") as fp:
+ pattern = r"id:(\d{1}):initdefault:"
+ text = fp.read()
+ return int(re.findall(pattern, text)[0])
+ except:
+ return 3
+
+ def kernelVersion(self):
+ return self.release
+
+ def hostName(self):
+ return self.hostname
+
+ def debianVersion(self):
+ try:
+ fp = open("/etc/debian_version").read()
+ if "wheezy/sid" in fp:
+ return 6
+ fp.close()
+ except:
+ pass
+ return False
+
+ def isKernelSMP(self):
+ return self.smp
+
+ def getArch(self):
+ return self.machine
+
+ def preWork(self):
+ # this method will be called before the gathering begins
+
+ localname = self.hostName()
+
+ if not self.commons['cmdlineopts'].batch and not self.commons['cmdlineopts'].silent:
+ try:
+ self.reportName = raw_input(_("Please enter your first initial and last name [%s]: ") % localname)
+ self.reportName = re.sub(r"[^a-zA-Z.0-9]", "", self.reportName)
+
+ self.ticketNumber = raw_input(_("Please enter the case number that you are generating this report for: "))
+ self.ticketNumber = re.sub(r"[^0-9]", "", self.ticketNumber)
+ self._print()
+ except:
+ self._print()
+ sys.exit(0)
+
+ if len(self.reportName) == 0:
+ self.reportName = localname
+
+ if self.commons['cmdlineopts'].customerName:
+ self.reportName = self.commons['cmdlineopts'].customerName
+ self.reportName = re.sub(r"[^a-zA-Z.0-9]", "", self.reportName)
+
+ if self.commons['cmdlineopts'].ticketNumber:
+ self.ticketNumber = self.commons['cmdlineopts'].ticketNumber
+ self.ticketNumber = re.sub(r"[^0-9]", "", self.ticketNumber)
+
+ return
+
+ def packageResults(self, archive_filename):
+ self._print(_("Creating compressed archive..."))
+
+ def get_msg(self):
+ msg_dict = {"distro": "Debian"}
+ return self.msg % msg_dict
diff --git a/sos/policies/ubuntu.py b/sos/policies/ubuntu.py
new file mode 100644
index 00000000..7d61d054
--- /dev/null
+++ b/sos/policies/ubuntu.py
@@ -0,0 +1,31 @@
+from __future__ import with_statement
+from sos import _sos as _
+from sos.plugins import UbuntuPlugin, IndependentPlugin
+from sos.policies.debian import DebianPolicy, DebianPackageManager
+from sos.utilities import shell_out
+
+import os
+
+class UbuntuPolicy(DebianPolicy):
+ def __init__(self):
+ super(UbuntuPolicy, self).__init__()
+
+ def validatePlugin(self, plugin_class):
+ "Checks that the plugin will execute given the environment"
+ return issubclass(plugin_class, UbuntuPlugin) or issubclass(plugin_class, IndependentPlugin)
+
+ @classmethod
+ def check(self):
+ """This method checks to see if we are running on Ubuntu.
+ It returns True or False."""
+ if os.path.isfile('/etc/lsb-release'):
+ try:
+ with open('/etc/lsb-release', 'r') as fp:
+ return "Ubuntu" in fp.read()
+ except:
+ return False
+ return False
+
+ def get_msg(self):
+ msg_dict = {"distro": "Ubuntu"}
+ return self.msg % msg_dict
diff --git a/sos/policies/windows.py b/sos/policies/windows.py
index 64e780bf..abb46494 100644
--- a/sos/policies/windows.py
+++ b/sos/policies/windows.py
@@ -13,6 +13,7 @@
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
import os
import time
+import platform
from sos.policies import PackageManager, Policy
from sos.utilities import shell_out
@@ -23,10 +24,13 @@ class WindowsPolicy(Policy):
@classmethod
def check(class_):
+ is_windows = False
try:
- return "Windows" in shell_out("ver")
- except Exception, e:
- return False
+ from java.lang import System
+ is_windows = "win" in System.getProperty('os.name').lower()
+ except:
+ is_windows = "win" in platform.system().lower()
+ return is_windows
def is_root(self):
if "S-1-16-12288" in shell_out("whoami /groups"):
diff --git a/sos/sosreport.py b/sos/sosreport.py
index 08f11fd6..fb2622ab 100644
--- a/sos/sosreport.py
+++ b/sos/sosreport.py
@@ -429,7 +429,8 @@ class SoSReport(object):
for plugin_class in plugin_classes:
if not self.policy.validatePlugin(plugin_class):
self.soslog.debug(_("plugin %s does not validate, skipping") % plug)
- self._skip(plugin_class, _("does not validate"))
+ if self.opts.verbosity > 0:
+ self._skip(plugin_class, _("does not validate"))
continue
if plugin_class.requires_root and not self._is_root:
diff --git a/sos/utilities.py b/sos/utilities.py
index e91586de..55fab228 100644
--- a/sos/utilities.py
+++ b/sos/utilities.py
@@ -217,6 +217,8 @@ class ImporterHelper(object):
try:
path_to_zip, tail = self._get_path_to_zip(path)
zf = zipfile.ZipFile(path_to_zip)
+ # the path will have os separators, but the zipfile will always have '/'
+ tail = tail.replace(os.path.sep, "/")
root_names = [name for name in zf.namelist() if tail in name]
candidates = self._get_plugins_from_list(root_names)
zf.close()
@@ -296,6 +298,9 @@ class Archive(object):
name = os.path.split(self._name)[-1]
return os.path.join(name, src.lstrip(os.sep))
+ def add_link(self, dest, link_name):
+ pass
+
class TarFileArchive(Archive):
@@ -329,6 +334,13 @@ class TarFileArchive(Archive):
tar_info.mtime = time.time()
self.tarfile.addfile(tar_info, StringIO(content))
+ def add_link(self, dest, link_name):
+ tar_info = tarfile.TarInfo(name=self.prepend(link_name))
+ tar_info.type = tarfile.SYMTYPE
+ tar_info.linkname = dest
+ tar_info.mtime = time.time()
+ self.tarfile.addfile(tar_info, None)
+
def open_file(self, name):
try:
self.tarfile.close()
diff --git a/tests/option_tests.py b/tests/option_tests.py
new file mode 100644
index 00000000..03804dec
--- /dev/null
+++ b/tests/option_tests.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+import unittest
+
+from sos.plugins import Plugin
+
+class GlobalOptionTest(unittest.TestCase):
+
+ def setUp(self):
+ self.commons = {
+ 'global_plugin_options': {
+ 'test_option': 'foobar',
+ 'baz': None,
+ 'empty_global': True,
+ },
+ }
+ self.plugin = Plugin(self.commons)
+ self.plugin.optNames = ['baz', 'empty']
+ self.plugin.optParms = [{'enabled': False}, {'enabled': None}]
+
+ def test_simple_lookup(self):
+ self.assertEquals(self.plugin.getOption('test_option'), 'foobar')
+
+ def test_multi_lookup(self):
+ self.assertEquals(self.plugin.getOption(('not_there', 'test_option')), 'foobar')
+
+ def test_cascade(self):
+ self.assertEquals(self.plugin.getOption(('baz')), False)
+
+ def test_none_should_cascade(self):
+ self.assertEquals(self.plugin.getOption(('empty', 'empty_global')), True)
+
+if __name__ == "__main__":
+ unittest.main()