diff options
author | jhjaggars <jhjaggars@gmail.com> | 2012-02-15 08:37:42 -0800 |
---|---|---|
committer | jhjaggars <jhjaggars@gmail.com> | 2012-02-15 08:37:42 -0800 |
commit | 269547b4a9ecf6aa507b5ad1dd1e1c68529a1243 (patch) | |
tree | 80775693e8ff4eb89a9fb37c174f6ea339cda01e | |
parent | ebefea9b122ea5cf74c692b59991be08fdb5d47e (diff) | |
parent | 80e99a2d290b10ed908320c97ca11874aac422b0 (diff) | |
download | sos-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__.py | 145 | ||||
-rw-r--r-- | sos/plugins/as7.py | 174 | ||||
-rw-r--r-- | sos/utilities.py | 10 | ||||
-rw-r--r-- | tests/option_tests.py | 10 |
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() |