aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhjaggars <jhjaggars@gmail.com>2012-02-15 08:37:42 -0800
committerjhjaggars <jhjaggars@gmail.com>2012-02-15 08:37:42 -0800
commit269547b4a9ecf6aa507b5ad1dd1e1c68529a1243 (patch)
tree80775693e8ff4eb89a9fb37c174f6ea339cda01e
parentebefea9b122ea5cf74c692b59991be08fdb5d47e (diff)
parent80e99a2d290b10ed908320c97ca11874aac422b0 (diff)
downloadsos-269547b4a9ecf6aa507b5ad1dd1e1c68529a1243.tar.gz
Merge pull request #23 from jhjaggars/master
Several bug fixes for AS7 plugin and base functionality
-rw-r--r--sos/plugins/__init__.py145
-rw-r--r--sos/plugins/as7.py174
-rw-r--r--sos/utilities.py10
-rw-r--r--tests/option_tests.py10
4 files changed, 183 insertions, 156 deletions
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
index 872e684d..576196de 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):
"""
@@ -306,7 +311,9 @@ class Plugin(object):
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
@@ -605,6 +614,140 @@ 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..9c052dd5 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,6 +139,7 @@ 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.addForbiddenPath(os.path.join(confDir, 'application-users.properties'))
self.doCopyFileOrDir(confDir, sub=(self.__jbossHome, 'JBOSSHOME'))
@@ -283,6 +148,9 @@ class AS7(Plugin, IndependentPlugin):
self.getOption("logsize"),
sub=(self.__jbossHome, 'JBOSSHOME'))
+ for xml in find("*.xml", path):
+ self.addCopySpec(xml)
+
deployment_info = self.__get_deployment_info(confDir)
deployments = self.__get_deployments(path)
for deployment in deployments:
@@ -337,8 +205,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 +213,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 +241,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/utilities.py b/sos/utilities.py
index e91586de..015c6c8e 100644
--- a/sos/utilities.py
+++ b/sos/utilities.py
@@ -296,6 +296,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 +332,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
index 06f048a4..03804dec 100644
--- a/tests/option_tests.py
+++ b/tests/option_tests.py
@@ -10,9 +10,13 @@ class GlobalOptionTest(unittest.TestCase):
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')
@@ -20,5 +24,11 @@ class GlobalOptionTest(unittest.TestCase):
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()