Esempio n. 1
0
 def __init__(self, host, user='******', passwd='admin', port=8080):
     (self.host, self.port) = (host, port)
     self.log = logging.getLogger('pytomcat.Tomcat')
     self.name = 'Tomcat at {0}:{1}'.format(host, port)
     self.jmx = JMXProxyConnection(host, user, passwd, port)
     self.mgr = ManagerConnection(host, user, passwd, port)
Esempio n. 2
0
class Tomcat:
    progress_callback = None

    def __init__(self, host, user = '******', passwd = 'admin', port = 8080):
        self.log = logging.getLogger('pytomcat.Tomcat')
        self.name = 'Tomcat at {0}:{1}'.format(host,port)
        self.jmx = JMXProxyConnection(host, user, passwd, port)
        self.mgr = ManagerConnection(host, user, passwd, port)

    def memory_info(self):
        '''
        Get memory pools, sizes and allocation information

        >>> t.memory_info()
        { 'HeapMemory': {'max': 129957888, 'init': 0, 'used': 16853056, 'committed': 85000192}, ... }
        '''
        data = self.jmx.query('java.lang:type=Memory*,*')
        meminfo = {}

        generic = data.pop('java.lang:type=Memory')
        for k in [ 'NonHeapMemoryUsage', 'HeapMemoryUsage' ]:
            meminfo.update({ k.replace('Usage',''): generic[k] })

        for k, v in data.iteritems():
            if k.startswith('java.lang:type=MemoryPool,'):
                name = k.replace('java.lang:type=MemoryPool,name=','')
                meminfo.update({ name: v['Usage']})

        return meminfo

    def memory_usage(self):
        '''
        Get memory pool usage as percentage of allowed maximum.

        >>> t.memory_usage()
        { 'HeapMemory': 10, 'NonHeapMemory': 15, ... }
        '''
        usage = {}
        for k, v in self.memory_info().iteritems():
            usage[k] = 100 * v['used'] / v['max']

        return usage
        
    def find_pools_over(self, percentage):
        return list(k for k, v in self.memory_usage().iteritems() if v > percentage)

    def run_gc(self):
        '''
        Invoke Garbage Collector to (hopefully) reclaim memory
        '''
        return self.jmx.invoke('java.lang:type=Memory', 'gc')

    def dump_all_threads(self):
        return self.jmx.invoke('java.lang:type=Threading', 'dumpAllThreads', 'true', 'true')

    def vhosts(self):
        '''
        Return configured vhosts

        >>> for k, v in t.vhosts().items():
        ...     print 'Host: {0}'.format(v['name'])
        ...
        Host: localhost
        >>>
        '''
        return self.jmx.query('Catalina:type=Host,*')

    def deployers(self):
        '''
        Return available application deployers (each configured vhost
        has its own deployer)
        '''
        return self.jmx.query('Catalina:type=Deployer,*')

    def has_cluster(self):
        '''
        Return true if this instance of Tomcat is member of a cluster
        '''
        return len(self.jmx.query('Catalina:type=Cluster')) > 0

    def server_status(self):
        '''
        Return the state name of the Tomcat server component.

        >>> t.server_status()
        'STARTED'

        See Also:
        http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/Lifecycle.html
        http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/LifecycleState.html
        '''
        return self.jmx.get('Catalina:type=Server', 'stateName')

    @property
    def _restarter(self):
        try:
            return self._restarter_obj
        except AttributeError:
            self._restarter_obj = self._find_restarter()
            return self._restarter_obj

    def _find_restarter(self):
        restarters = [ _JSWRestarter(self), _YAJSWRestarter(self) ]
        for r in restarters:
            if r.detect():
                return r
        return None

    def can_restart(self):
        '''
        Return true is this instance of Tomcat can be restarted remotely.
        This will only work if Tomcat is launched by a supported wrapper
        which exposes this functionality via JMX.
        e.g. Java Service Wrapper http://wrapper.tanukisoftware.com or
        Yet Another Java Service Wrapper http://yajsw.sourceforge.net/

        >>> t.can_restart()
        True
        '''
        return self._restarter != None

    def restart(self, timeout=600):
        '''
        Restart this instance of Tomcat.
        Restarting will only work if Tomcat is launched by a supported wrapper
        which exposes restarting functionality via JMX.

        >>> t.restart()
        '''
        def started():
            try:
                return self.server_status() == 'STARTED'
            except: 
                return False
        if not self.can_restart():
            raise TomcatError('{0} does not support remote restarting'
                              .format(self.name))
        self._restarter.restart()
        if not wait_until(lambda: not started(), timeout):
            raise TomcatError('Timed out waiting for {0} to shut down'
                              .format(self.name))
        if not wait_until(started, timeout):
            raise TomcatError('Timed out waiting for {0} to boot up'
                              .format(self.name))

    def cluster_name(self):
        '''
        Return the name of the cluster
        '''
        return self.jmx.get('Catalina:type=Cluster', 'clusterName')

    def cluster_members(self):
        '''
        Return all members of the cluster

        >>> map(lambda x: x['hostname'], t.cluster_members().values())
        ['192.168.56.101', '192.168.56.102', '192.168.56.103']
        '''
        invalid_ips = [ '0.0.0.0', '255.255.255.255' ]
        def is_valid(m):
            return ( m['hostname'] not in invalid_ips )
        m = self.jmx.query('Catalina:type=Cluster,component=Member,*')
        return dict((k, v) for k, v in m.iteritems() if is_valid(v))

    def active_members(self):
        '''
        Return only active members of the cluster
        '''
        def is_active(m):
            return ( m['ready'] and not m['failing'] and not m['suspect'] )

        m = self.cluster_members()
        return dict((k, v) for k, v in m.iteritems() if is_active(v))

    def list_webapps(self, app='*', vhost='*'):
        '''
        List webapps running on the specified host

        >>> for v in t.list_webapps().values():
        ...     print '{baseName:<20} {path:<20} {stateName}'.format(**v)
        ...
        manager              /manager             STARTED
        docs                 /docs                STARTED
        ROOT                 None                 STARTED
        examples             /examples            STARTING
        host-manager         /host-manager        STOPPED
        >>>

        See Also:
        http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/Lifecycle.html
        http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/LifecycleState.html
        '''
        def sanitize_name(name):
            return '/' if name == None else name
        rv = self.jmx.query(
                   'Catalina:j2eeType=WebModule,name=//{0}/{1},*'
                   .format(vhost, re.sub('^/', '', app)))
        return dict((sanitize_name(v['name']),v) for k, v in rv.iteritems())

    def find_managers(self, app='*', vhost='*'):
        '''
        TODO
        '''
        def extract_context(mgr_id):
            # FIXME: depends on the exact ordering of parts in the object ID
            return re.match(
                       '^Catalina:type=Manager,context=(.+?),host=(.+?)',
                       mgr_id).group(1)
        rv = self.jmx.query(
                   'Catalina:type=Manager,context={0},host={1}'
                   .format(app, vhost))
        return dict((extract_context(k),v) for k, v in rv.iteritems())

    def _list_session_ids(self, mgr_obj_id):
        ids = self.jmx.invoke(mgr_obj_id, 'listSessionIds')
        if ids == None:
            return []
        else:
            return ids.rstrip().split(' ')

    def list_sessions(self, app='*', vhost='*'):
        '''
        TODO
        '''
        rv = {}
        for k, v in self.find_managers(app, vhost).iteritems():
            if v['activeSessions'] > 0:
                rv[k] = self._list_session_ids(v['objectName'])
            else:
                rv[k] = []
        return rv

    def undeploy_old_versions(self, vhost=None):
        '''
        Invoke 'checkUndeploy' on 'Catalina:type=Deployer' to undeploy old
        versions of webapps that share the same path. The distinction is made
        by comparing the 'webappVersion' property. Webapps must have no active
        sessions in order to be undeployed by this call.

        NB! As of Tomcat 7.0.37 versions are compared as strings
            thus 2 > 10, however 02 < 10

        >>> t.undeploy_old_versions('localhost') # Undeploy from localhost
        >>> t.undeploy_old_versions() # Undeploy from all configured vhosts
        '''
        if vhost == None:
            deployers = self.deployers().keys()
        else:
            deployers = [ 'Catalina:type=Deployer,host={0}'.format(vhost) ]

        for d in deployers:
            self.jmx.invoke(d, 'checkUndeploy')

    def find_connectors(self):
        '''
        Return the list of active Tomcat connector names.

        >>> t.find_connectors()
        ['Connector[HTTP/1.1-8080]', 'Connector[AJP/1.3-8009]']
        '''
        return self.jmx.invoke('Catalina:type=Service', 'findConnectors')

    def max_heap(self):
        return self.jmx.get('java.lang:type=Memory', 'HeapMemoryUsage', 'max')

    def max_nonheap(self):
        return self.jmx.get('java.lang:type=Memory', 'NonHeapMemoryUsage', 'max')

    def deploy(self, filename, context=None, vhost='localhost'):
        '''
        Deploy a Web application archive (WAR)

        >>> t.deploy('/tmp/myapp.war')
        '''
        (ctx, path, version) = parse_warfile(filename)
        if context == None:
            context = ctx
        return self.mgr.deploy(filename, context, vhost)

    def undeploy(self, context, vhost='localhost'):
        '''
        Remove a web application from the server

        >>> t.undeploy('/myapp')
        '''
        self.mgr.undeploy(context, vhost)

    def _expire_session(self, mgr_obj_id, session_id):
        self.jmx.invoke(mgr_obj_id, 'expireSession', session_id)

    def expire_sessions(self, app, vhost = '*'):
        '''
        Forcefully expire ALL active sessions in a webapp

        >>> t.expire_sessions('/manager')
        '''
        sessions = self.list_sessions(app, vhost)
        if len(sessions) <= 0:
            raise TomcatError("Unable to find context '{0}' from vhost '{1}'"
                              .format(app, vhost))

        mgrs = self.find_managers(app, vhost)
        for ctx, ids in sessions.iteritems():
            for id in ids:
                if not ctx in mgrs:
                    raise TomcatError(
                              "Unable to find manager for context '{0}' from vhost '{1}'"
                              .format(app, vhost))
                self._expire_session(mgrs[ctx]['objectName'], id)

    def set_progress_callback(self, callback):
        self.progress_callback = callback
        self.mgr.progress_callback = callback
Esempio n. 3
0
class Tomcat:
    progress_callback = None

    def __init__(self, host, user='******', passwd='admin', port=8080):
        (self.host, self.port) = (host, port)
        self.log = logging.getLogger('pytomcat.Tomcat')
        self.name = 'Tomcat at {0}:{1}'.format(host, port)
        self.jmx = JMXProxyConnection(host, user, passwd, port)
        self.mgr = ManagerConnection(host, user, passwd, port)

    def memory_info(self):
        '''
        Get memory pools, sizes and allocation information

        >>> t.memory_info()
        { 'HeapMemory': {'max': 129957888, 'init': 0, 'used': 16853056, 'committed': 85000192}, ... }
        '''
        data = self.jmx.query('java.lang:type=Memory*,*')
        meminfo = {}

        generic = data.pop('java.lang:type=Memory')
        for k in ['NonHeapMemoryUsage', 'HeapMemoryUsage']:
            meminfo.update({k.replace('Usage', ''): generic[k]})

        for k, v in data.iteritems():
            if k.startswith('java.lang:type=MemoryPool,'):
                name = k.replace('java.lang:type=MemoryPool,name=', '')
                meminfo.update({name: v['Usage']})

        return meminfo

    def memory_usage(self):
        '''
        Get memory pool usage as percentage of allowed maximum.

        >>> t.memory_usage()
        { 'HeapMemory': 10, 'NonHeapMemory': 15, ... }
        '''
        usage = {}
        for k, v in self.memory_info().iteritems():
            usage[k] = 100 * v['used'] / v['max']

        return usage

    def find_pools_over(self, percentage):
        return list(k for k, v in self.memory_usage().iteritems()
                    if v > percentage)

    def run_gc(self):
        '''
        Invoke Garbage Collector to (hopefully) reclaim memory
        '''
        return self.jmx.invoke('java.lang:type=Memory', 'gc')

    def dump_all_threads(self):
        return self.jmx.invoke('java.lang:type=Threading', 'dumpAllThreads',
                               'true', 'true')

    def vhosts(self):
        '''
        Return configured vhosts

        >>> for k, v in t.vhosts().items():
        ...     print 'Host: {0}'.format(v['name'])
        ...
        Host: localhost
        >>>
        '''
        return self.jmx.query('Catalina:type=Host,*')

    def deployers(self):
        '''
        Return available application deployers (each configured vhost
        has its own deployer)
        '''
        return self.jmx.query('Catalina:type=Deployer,*')

    def has_cluster(self):
        '''
        Return true if this instance of Tomcat is member of a cluster
        '''
        return len(self.jmx.query('Catalina:type=Cluster')) > 0

    def server_status(self):
        '''
        Return the state name of the Tomcat server component.

        >>> t.server_status()
        'STARTED'

        See Also:
        http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/Lifecycle.html
        http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/LifecycleState.html
        '''
        return self.jmx.get('Catalina:type=Server', 'stateName')

    @property
    def _restarter(self):
        try:
            return self._restarter_obj
        except AttributeError:
            self._restarter_obj = self._find_restarter()
            return self._restarter_obj

    def _find_restarter(self):
        restarters = [_JSWRestarter(self), _YAJSWRestarter(self)]
        for r in restarters:
            if r.detect():
                return r
        return None

    def can_restart(self):
        '''
        Return true is this instance of Tomcat can be restarted remotely.
        This will only work if Tomcat is launched by a supported wrapper
        which exposes this functionality via JMX.
        e.g. Java Service Wrapper http://wrapper.tanukisoftware.com or
        Yet Another Java Service Wrapper http://yajsw.sourceforge.net/

        >>> t.can_restart()
        True
        '''
        return self._restarter != None

    def restart(self, timeout=600):
        '''
        Restart this instance of Tomcat.
        Restarting will only work if Tomcat is launched by a supported wrapper
        which exposes restarting functionality via JMX.

        >>> t.restart()
        '''
        def started():
            try:
                return self.server_status() == 'STARTED'
            except:
                return False

        if not self.can_restart():
            raise TomcatError('{0} does not support remote restarting'.format(
                self.name))
        self._restarter.restart()
        if not wait_until(lambda: not started(), timeout):
            raise TomcatError('Timed out waiting for {0} to shut down'.format(
                self.name))
        if not wait_until(started, timeout):
            raise TomcatError('Timed out waiting for {0} to boot up'.format(
                self.name))

    def cluster_name(self):
        '''
        Return the name of the cluster
        '''
        return self.jmx.get('Catalina:type=Cluster', 'clusterName')

    def cluster_members(self):
        '''
        Return all members of the cluster

        >>> map(lambda x: x['hostname'], t.cluster_members().values())
        ['192.168.56.101', '192.168.56.102', '192.168.56.103']
        '''
        invalid_ips = ['0.0.0.0', '255.255.255.255']

        def is_valid(m):
            return (m['hostname'] not in invalid_ips)

        m = self.jmx.query('Catalina:type=Cluster,component=Member,*')
        return dict((k, v) for k, v in m.iteritems() if is_valid(v))

    def active_members(self):
        '''
        Return only active members of the cluster
        '''
        def is_active(m):
            return (m['ready'] and not m['failing'] and not m['suspect'])

        m = self.cluster_members()
        return dict((k, v) for k, v in m.iteritems() if is_active(v))

    def list_webapps(self, app='*', vhost='*'):
        '''
        List webapps running on the specified host

        >>> for v in t.list_webapps().values():
        ...     print '{baseName:<20} {path:<20} {stateName}'.format(**v)
        ...
        manager              /manager             STARTED
        docs                 /docs                STARTED
        ROOT                 None                 STARTED
        examples             /examples            STARTING
        host-manager         /host-manager        STOPPED
        >>>

        See Also:
        http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/Lifecycle.html
        http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/LifecycleState.html
        '''
        def sanitize_name(name):
            return '/' if name == None else name

        rv = self.jmx.query(
            'Catalina:j2eeType=WebModule,name=//{0}/{1},*'.format(
                vhost, re.sub('^/', '', app)))
        return dict((sanitize_name(v['name']), v) for k, v in rv.iteritems())

    def find_managers(self, app='*', vhost='*'):
        '''
        TODO
        '''
        def extract_context(mgr_id):
            # FIXME: depends on the exact ordering of parts in the object ID
            return re.match('^Catalina:type=Manager,context=(.+?),host=(.+?)',
                            mgr_id).group(1)

        rv = self.jmx.query(
            'Catalina:type=Manager,context={0},host={1}'.format(app, vhost))
        return dict((extract_context(k), v) for k, v in rv.iteritems())

    def _list_session_ids(self, mgr_obj_id):
        ids = self.jmx.invoke(mgr_obj_id, 'listSessionIds')
        if ids == None:
            return []
        else:
            return ids.rstrip().split(' ')

    def list_sessions(self, app='*', vhost='*'):
        '''
        TODO
        '''
        rv = {}
        for k, v in self.find_managers(app, vhost).iteritems():
            if v['activeSessions'] > 0:
                rv[k] = self._list_session_ids(v['objectName'])
            else:
                rv[k] = []
        return rv

    def undeploy_old_versions(self, vhost=None):
        '''
        Invoke 'checkUndeploy' on 'Catalina:type=Deployer' to undeploy old
        versions of webapps that share the same path. The distinction is made
        by comparing the 'webappVersion' property. Webapps must have no active
        sessions in order to be undeployed by this call.

        NB! As of Tomcat 7.0.37 versions are compared as strings
            thus 2 > 10, however 02 < 10

        >>> t.undeploy_old_versions('localhost') # Undeploy from localhost
        >>> t.undeploy_old_versions() # Undeploy from all configured vhosts
        '''
        if vhost == None:
            deployers = self.deployers().keys()
        else:
            deployers = ['Catalina:type=Deployer,host={0}'.format(vhost)]

        for d in deployers:
            self.jmx.invoke(d, 'checkUndeploy', timeout=20)

    def find_connectors(self):
        '''
        Return the list of active Tomcat connector names.

        >>> t.find_connectors()
        ['Connector[HTTP/1.1-8080]', 'Connector[AJP/1.3-8009]']
        '''
        return self.jmx.invoke('Catalina:type=Service', 'findConnectors')

    def max_heap(self):
        return self.jmx.get('java.lang:type=Memory', 'HeapMemoryUsage', 'max')

    def max_nonheap(self):
        return self.jmx.get('java.lang:type=Memory', 'NonHeapMemoryUsage',
                            'max')

    def deploy(self, filename, context=None, vhost='localhost'):
        '''
        Deploy a Web application archive (WAR)

        >>> t.deploy('/tmp/myapp.war')
        '''
        (ctx, path, version) = parse_warfile(filename)
        if context == None:
            context = ctx
        return self.mgr.deploy(filename, context, vhost)

    def undeploy(self, context, vhost='localhost'):
        '''
        Remove a web application from the server

        >>> t.undeploy('/myapp')
        '''
        self.mgr.undeploy(context, vhost)

    def _expire_session(self, mgr_obj_id, session_id):
        self.jmx.invoke(mgr_obj_id, 'expireSession', session_id)

    def expire_sessions(self, app, vhost='*'):
        '''
        Forcefully expire ALL active sessions in a webapp

        >>> t.expire_sessions('/manager')
        '''
        sessions = self.list_sessions(app, vhost)
        if len(sessions) <= 0:
            raise TomcatError(
                "Unable to find context '{0}' from vhost '{1}'".format(
                    app, vhost))

        mgrs = self.find_managers(app, vhost)
        for ctx, ids in sessions.iteritems():
            for id in ids:
                if not ctx in mgrs:
                    raise TomcatError(
                        "Unable to find manager for context '{0}' from vhost '{1}'"
                        .format(app, vhost))
                self._expire_session(mgrs[ctx]['objectName'], id)

    def set_progress_callback(self, callback):
        self.progress_callback = callback
        self.mgr.progress_callback = callback
Esempio n. 4
0
 def __init__(self, host, user = '******', passwd = 'admin', port = 8080):
     self.log = logging.getLogger('pytomcat.Tomcat')
     self.name = 'Tomcat at {0}:{1}'.format(host,port)
     self.jmx = JMXProxyConnection(host, user, passwd, port)
     self.mgr = ManagerConnection(host, user, passwd, port)