← Back to team overview

openerp-community-reviewer team mailing list archive

[Merge] lp:~camptocamp/server-env-tools/7.0-monitoring into lp:server-env-tools

 

Alexandre Fayolle - camptocamp has proposed merging lp:~camptocamp/server-env-tools/7.0-monitoring into lp:server-env-tools.

Requested reviews:
  Server Environment And Tools Core Editors (server-env-tools-core-editors)

For more details, see:
https://code.launchpad.net/~camptocamp/server-env-tools/7.0-monitoring/+merge/215138

new addon: server_monitoring

This addon logs information in the OpenERP database, which can be used to monitor the health of the instance:

* database information (table size, number of reads, inserts, updates, deletes...)
* process information (cpu time, memory, rpc calls...)
-- 
https://code.launchpad.net/~camptocamp/server-env-tools/7.0-monitoring/+merge/215138
Your team Server Environment And Tools Core Editors is requested to review the proposed merge of lp:~camptocamp/server-env-tools/7.0-monitoring into lp:server-env-tools.
=== added directory 'server_monitoring'
=== added file 'server_monitoring/__init__.py'
--- server_monitoring/__init__.py	1970-01-01 00:00:00 +0000
+++ server_monitoring/__init__.py	2014-04-10 10:25:03 +0000
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Author: Alexandre Fayolle
+#    Copyright 2014 Camptocamp SA
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import server_monitoring

=== added file 'server_monitoring/__openerp__.py'
--- server_monitoring/__openerp__.py	1970-01-01 00:00:00 +0000
+++ server_monitoring/__openerp__.py	2014-04-10 10:25:03 +0000
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Author: Alexandre Fayolle
+#    Copyright 2014 Camptocamp SA
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+{'name': 'Server Monitoring',
+ 'version': '0.1',
+ 'category': 'Connector',
+ 'depends': ['base',
+             ],
+ 'author': 'Camptocamp',
+ 'license': 'AGPL-3',
+ 'description': """
+Server Monitoring
+=================
+
+This module allows in-database logging of some statistics in order to monitor
+the health of an openerp instance.
+
+Database indicators are logged (number of rows, table size, number of reads,
+number of updates...), with a cron running each week by default.
+
+Some process indicators are logged (cpu time, memory) together with information
+about the different XMLRPC calls made to the server (user, model, method).
+
+Two crons are provided to cleanup old logs from the database.
+""",
+ 'data': [
+     'server_monitoring_view.xml',
+     'server_monitoring_data.xml',
+     'security/ir.model.access.csv',
+     ],
+ 'test':[],
+ 'installable': True,
+ 'application': False,
+}
+

=== added directory 'server_monitoring/security'
=== added file 'server_monitoring/security/ir.model.access.csv'
--- server_monitoring/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ server_monitoring/security/ir.model.access.csv	2014-04-10 10:25:03 +0000
@@ -0,0 +1,8 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_server_monitor_class_instance_count,access_server_monitor_class_instance_count,model_server_monitor_class_instance_count,base.group_no_one,1,0,1,0
+access_server_monitor_process,access_server_monitor_process,model_server_monitor_process,base.group_no_one,1,0,1,0
+access_server_monitor_model_row_count,access_server_monitor_model_row_count,model_server_monitor_model_row_count,base.group_no_one,1,0,1,0
+access_server_monitor_model_table_size,access_server_monitor_model_table_size,model_server_monitor_model_table_size,base.group_no_one,1,0,1,0
+access_server_monitor_model_table_activity_read,access_server_monitor_model_table_activity_read,model_server_monitor_model_table_activity_read,base.group_no_one,1,0,1,0
+access_server_monitor_model_table_activity_update,access_server_monitor_model_table_activity_update,model_server_monitor_model_table_activity_update,base.group_no_one,1,0,1,0
+access_server_monitor_database,access_server_monitor_database,model_server_monitor_database,base.group_no_one,1,0,1,0

=== added file 'server_monitoring/server_monitoring.py'
--- server_monitoring/server_monitoring.py	1970-01-01 00:00:00 +0000
+++ server_monitoring/server_monitoring.py	2014-04-10 10:25:03 +0000
@@ -0,0 +1,408 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Author: Alexandre Fayolle
+#    Copyright 2014 Camptocamp SA
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+"""
+Monitor openerp instance.
+
+The measures are stored in database.
+cleanup cron (2 different for db and process monitoring)
+
+* database monitoring:
+  cron for capturing data
+  add timestamp
+
+* process monitoring
+
+  TODO: log process start / end
+  cron log
+  RPC request log
+
+  -> to log: timestamp, pid, database, cpu time, memory, uid, model, method, return state, pooler state(?), session id (?)
+
+TODO: visualize
+TODO: export
+
+"""
+
+import logging
+import gc
+from operator import itemgetter
+import types
+import os
+import threading
+import resource
+import datetime
+
+import psutil
+
+from openerp.osv import orm, fields, osv
+from openerp import pooler
+from openerp import SUPERUSER_ID
+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
+
+_logger = logging.getLogger(__name__)
+
+
+class bigint(fields.integer):
+    _type = 'int8'
+
+fields.bigint = bigint
+orm.FIELDS_TO_PGTYPES[bigint] = 'int8'
+
+
+BLACKLIST = (
+    type, tuple, dict, list, set, frozenset,
+    property,
+    classmethod,
+    staticmethod,
+    types.FunctionType,
+    types.ClassType,
+    types.ModuleType, types.FunctionType, types.MethodType,
+    types.MemberDescriptorType, types.GetSetDescriptorType,
+    )
+
+## COUNTED = frozenset((
+##     column_info, LRUNode,
+##     browse_record,
+
+##     ))
+
+class ClassInstanceCount(orm.Model):
+    _name = 'server.monitor.class.instance.count'
+    _columns = {
+        'name': fields.text('Class name', readonly=True),
+        'count': fields.bigint('Instance count', readonly=True),
+        'measure_id': fields.many2one('server.monitor.process',
+                                      'Measure',
+                                      readonly=True,
+                                      ondelete='cascade'),
+        }
+
+
+
+def _monkey_patch_object_proxy_execute():
+    orig_execute_cr = osv.object_proxy.execute_cr
+    def execute_cr(self, cr, uid, obj, method, *args, **kw):
+        result = orig_execute_cr(self, cr, uid, obj, method, *args, **kw)
+        monitor_obj = pooler.get_pool(cr.dbname)['server.monitor.process']
+        context = {}
+        monitor_obj.log_measure(cr, uid, obj, method, 'rpc call',
+                                False, False, context)
+        return result
+    osv.object_proxy.execute_cr = execute_cr
+
+
+class ServerMonitorProcess(orm.Model):
+    def __init__(self, pool, cr):
+        super(ServerMonitorProcess, self).__init__(pool, cr)
+        _monkey_patch_object_proxy_execute()
+
+    _name = 'server.monitor.process'
+    _columns = {
+        'name': fields.datetime('Timestamp', readonly=True),
+        'pid': fields.integer('Process ID', readonly=True, group_operator='count'),
+        'thread': fields.text('Thread ID', readonly=True),
+        'cpu_time': fields.float('CPU time', readonly=True, group_operator='max'),
+        'memory': fields.float('Memory', readonly=True, group_operator='max'),
+        'uid': fields.many2one('res.users', 'User', readonly=True),
+        'model': fields.many2one('ir.model', 'Model', readonly=True),
+        'method': fields.text('Method', readonly=True),
+        'status': fields.text('RPC status', readonly=True),
+        #'pooler_state(?)': fields.XXX('Pooler state', readonly=True),
+        'sessionid': fields.text('Session ID', readonly=True),
+        'info': fields.text('Information'),
+        'class_count_ids': fields.one2many('server.monitor.class.instance.count',
+                                           'measure_id',
+                                           'Class counts',
+                                           readonly=True),
+        }
+    _order = 'name DESC'
+
+    def _default_pid(self, cr, uid, context):
+        return os.getpid()
+
+    def _default_cpu_time(self, cr, uid, context):
+        r = resource.getrusage(resource.RUSAGE_SELF)
+        cpu_time = r.ru_utime + r.ru_stime
+        return cpu_time
+
+    def _default_memory(self, cr, uid, context):
+        rss, vms = psutil.Process(os.getpid()).get_memory_info()
+        return vms
+
+    def _default_uid(self, cr, uid, context):
+        return uid
+
+    def _default_thread(self, cr, uid, context):
+        return threading.current_thread().name
+
+    def _class_count(self, cr, uid, context):
+        counts = {}
+        if context.get('_x_no_class_count'):
+            return []
+        if context.get('_x_no_gc_collect'):
+            gc.collect()
+            gc.collect()
+        for obj in gc.get_objects():
+            if isinstance(obj, BLACKLIST):
+                continue
+            try:
+                cls = obj.__class__
+            except:
+                if isinstance(obj, types.ClassType):
+                    cls = types.ClassType
+                else:
+                    print obj, type(obj)
+            name = '%s.%s' % (cls.__module__, cls.__name__)
+            try:
+                counts[name] += 1
+            except KeyError:
+                counts[name] = 1
+        info = []
+        for name, count in sorted(counts.items(), key=itemgetter(1), reverse=True):
+            if count < 2:
+                break
+            info.append({'name': name,  'count': count})
+        return [(0, 0, val) for val in info]
+
+
+
+    _defaults = {
+        'name': fields.datetime.now,
+        'class_count_ids': _class_count,
+        'pid': _default_pid,
+        'cpu_time': _default_cpu_time,
+        'memory': _default_memory,
+        'uid': _default_uid,
+        'thread': _default_thread,
+        }
+
+    def log_measure(self, cr, uid,
+                    model_name, method_name, info,
+                    with_class_count=True,
+                    gc_collect=True,
+                    context=None):
+        if context is None:
+            context = {}
+        ctx = context.copy()
+        ctx.update({
+            '_x_no_class_count': not with_class_count,
+            '_x_no_gc_collect': not gc_collect,
+            })
+        fields = self._defaults.keys()
+        defaults = self.default_get(cr, uid, fields, context=ctx)
+        model_obj = self.pool['ir.model']
+        model_id = model_obj.search(cr, uid, [('name', '=', model_name)], context=context)
+        if model_id:
+            model_id = model_id[0]
+        else:
+            model_id = 0
+        values = {'model': model_id,
+                  'method': method_name,
+                  'info': info,
+                  }
+        defaults.update(values)
+
+        self.create(cr, SUPERUSER_ID, defaults, context=context)
+        return True
+
+    def cleanup(self, cr, uid, age, context=None):
+        now = datetime.datetime.now()
+        delta = datetime.timedelta(days=age)
+        ids = self.search(cr, uid,
+                          [('name', '<', (now - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))],
+                          context=context)
+        _logger.debug('Process monitor cleanup: removing %d records', len(ids))
+        self.unlink(cr, uid, ids, context=context)
+
+
+class IrCron(orm.Model):
+    _inherit = 'ir.cron'
+    def _callback(self, cr, uid, model_name, method_name, args, job_id):
+        super(IrCron, self)._callback(cr, uid, model_name, method_name, args, job_id)
+        monitor_obj = self.pool['server.monitor.process']
+        context = {}
+        monitor_obj.log_measure(cr, uid, model_name, method_name, 'cron job',
+                                False, False, context)
+
+class ModelRowCount(orm.Model):
+    _name = 'server.monitor.model.row.count'
+    _columns = {
+        'name': fields.text('Table name', readonly=True),
+        'count': fields.bigint('row count', readonly=True),
+        'measure_id': fields.many2one('server.monitor.database',
+                                      'Measure',
+                                      ondelete='cascade',
+                                      readonly=True),
+        'timestamp': fields.related('measure_id', 'name', string='timestamp', type='datetime', store=True),
+        }
+    _order = 'timestamp DESC, count DESC'
+
+class ModelTableSize(orm.Model):
+    _name = 'server.monitor.model.table.size'
+    _columns = {
+        'name': fields.text('Table name', readonly=True),
+        'size': fields.bigint('Size (bytes)', readonly=True),
+        'hsize': fields.text('Size', readonly=True),
+        'measure_id': fields.many2one('server.monitor.database',
+                                      'Measure',
+                                      ondelete='cascade', readonly=True),
+        'timestamp': fields.related('measure_id', 'name', string='timestamp', type='datetime', store=True),
+        }
+    _order = 'timestamp DESC, size DESC'
+
+class ModelTableActivityRead(orm.Model):
+    _name = 'server.monitor.model.table.activity.read'
+    _columns = {
+        'name': fields.text('Table name'),
+        'disk_reads': fields.bigint('Disk reads (heap blocks)', readonly=True),
+        'cache_reads': fields.bigint('Cache reads', readonly=True),
+        'total_reads': fields.bigint('Total reads', readonly=True),
+        'measure_id': fields.many2one('server.monitor.database',
+                                      'Measure',
+                                      ondelete='cascade', readonly=True),
+        'timestamp': fields.related('measure_id', 'name', string='timestamp', type='datetime', store=True),
+        }
+    _order = 'timestamp DESC, total_reads DESC'
+
+class ModelTableActivityUpdate(orm.Model):
+    _name = 'server.monitor.model.table.activity.update'
+    _columns = {
+        'name': fields.text('Table name', readonly=True),
+        'seq_scan': fields.bigint('Seq scans', readonly=True),
+        'idx_scan': fields.bigint('Idx scans', readonly=True),
+        'lines_read_total': fields.bigint('Tot lines read', readonly=True),
+        'num_insert': fields.bigint('Inserts', readonly=True),
+        'num_update': fields.bigint('Updates', readonly=True),
+        'num_delete': fields.bigint('Deletes', readonly=True),
+        'measure_id': fields.many2one('server.monitor.database',
+                                      'Measure',
+                                      ondelete='cascade',
+                                      readonly=True),
+        'timestamp': fields.related('measure_id', 'name', string='timestamp', type='datetime', store=True),
+        }
+    _order = 'timestamp DESC, num_update DESC'
+
+
+class ServerMonitorDatabase(orm.Model):
+    _name = 'server.monitor.database'
+    _columns = {
+        'name': fields.datetime('Timestamp', readonly=True),
+        'info': fields.text('Information'),
+        'table_nb_row_ids': fields.one2many('server.monitor.model.row.count',
+                                            'measure_id',
+                                            'Model row counts',
+                                            readonly=True),
+        'table_size_ids': fields.one2many('server.monitor.model.table.size',
+                                          'measure_id',
+                                          'Model table size',
+                                          readonly=True),
+        'table_activity_read_ids': fields.one2many('server.monitor.model.table.activity.read',
+                                                   'measure_id',
+                                                   'Model table read activity',
+                                                   readonly=True),
+        'table_activity_update_ids': fields.one2many('server.monitor.model.table.activity.update',
+                                                     'measure_id',
+                                                     'Model table update activity',
+                                                     readonly=True),
+        }
+    _order = 'name DESC'
+
+
+    def _model_row_count(self, cr, uid, context):
+        res = []
+        query = ("SELECT schemaname || '.' || relname as name, n_live_tup as count "
+                 "FROM pg_stat_user_tables "
+                 "ORDER BY n_live_tup DESC")
+        cr.execute(query)
+        for val in cr.dictfetchall():
+            res.append((0, 0, val))
+        return res
+
+    def _model_table_size(self, cr, uid, context):
+        res = []
+        query = ("SELECT nspname || '.' || relname AS name, "
+                 "        pg_size_pretty(pg_total_relation_size(C.oid)) AS hsize, "
+                 "        pg_total_relation_size(C.oid) AS size "
+                 "FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) "
+                 "WHERE nspname NOT IN ('pg_catalog', 'information_schema') "
+                 "  AND C.relkind <> 'i' "
+                 "  AND nspname !~ '^pg_toast' "
+                 "ORDER BY pg_total_relation_size(C.oid) DESC"
+                 )
+        cr.execute(query)
+        for val in cr.dictfetchall():
+            res.append((0, 0, val))
+        return res
+
+    def _model_table_activity_read(self, cr, uid, context):
+        res = []
+        query = ("SELECT schemaname || '.' || relname as name, "
+                 "       heap_blks_read as disk_reads, "
+                 "       heap_blks_hit as cache_reads, "
+                 "       heap_blks_read + heap_blks_hit as total_reads "
+                 "FROM pg_statio_user_tables "
+                 "ORDER BY heap_blks_read + heap_blks_hit DESC"
+                 )
+        cr.execute(query)
+        for val in cr.dictfetchall():
+            res.append((0, 0, val))
+        return res
+
+    def _model_table_activity_update(self, cr, uid, context):
+        res = []
+        query = ("SELECT schemaname || '.' || relname as name, "
+                 "       seq_scan, "
+                 "       idx_scan, "
+                 "       idx_tup_fetch + seq_tup_read as lines_read_total, "
+                 "       n_tup_ins as num_insert, "
+                 "       n_tup_upd as num_update, "
+                 "       n_tup_del as num_delete "
+                 "FROM pg_stat_user_tables "
+                 "ORDER BY n_tup_upd + n_tup_ins + n_tup_del desc")
+        cr.execute(query)
+        for val in cr.dictfetchall():
+            res.append((0, 0, val))
+        return res
+
+
+    _defaults = {
+        'name': fields.datetime.now,
+        'table_nb_row_ids': _model_row_count,
+        'table_size_ids': _model_table_size,
+        'table_activity_read_ids': _model_table_activity_read,
+        'table_activity_update_ids': _model_table_activity_update,
+        }
+
+    def log_measure(self, cr, uid, context=None):
+        fields = self._defaults.keys()
+        defaults = self.default_get(cr, uid, fields, context=context)
+        self.create(cr, uid, defaults, context=context)
+        return True
+
+    def cleanup(self, cr, uid, age, context=None):
+        now = datetime.datetime.now()
+        delta = datetime.timedelta(days=age)
+        ids = self.search(cr, uid,
+                          [('name', '<', (now - delta).strftime(DEFAULT_SERVER_DATETIME_FORMAT))],
+                          context=context)
+        _logger.debug('Database monitor cleanup: removing %d records', len(ids))
+        self.unlink(cr, uid, ids, context=context)

=== added file 'server_monitoring/server_monitoring_data.xml'
--- server_monitoring/server_monitoring_data.xml	1970-01-01 00:00:00 +0000
+++ server_monitoring/server_monitoring_data.xml	2014-04-10 10:25:03 +0000
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data noupdate="1">
+        <record forcecreate="True" id="ir_cron_monitoring_action" model="ir.cron">
+            <field name="name">Database monitoring</field>
+            <field eval="False" name="active"/>
+            <field name="user_id" ref="base.user_root"/>
+            <field name="interval_number">7</field>
+            <field name="interval_type">days</field>
+            <field name="numbercall">-1</field>
+            <field eval="False" name="doall"/>
+            <field eval="'server.monitor.database'" name="model"/>
+            <field eval="'log_measure'" name="function"/>
+            <field eval="'()'" name="args"/>
+            <field name="priority" eval='100'/>
+        </record>
+
+        <record forcecreate="True" id="ir_cron_monitoring_process_cleanup_action" model="ir.cron">
+            <field name="name">Process monitoring cleanup</field>
+            <field eval="True" name="active"/>
+            <field name="user_id" ref="base.user_root"/>
+            <field name="interval_number">1</field>
+            <field name="interval_type">days</field>
+            <field name="numbercall">-1</field>
+            <field eval="False" name="doall"/>
+            <field eval="'server.monitor.process'" name="model"/>
+            <field eval="'cleanup'" name="function"/>
+            <field eval="'(30,)'" name="args"/>
+            <field name="priority" eval='500'/>
+        </record>
+
+        <record forcecreate="True" id="ir_cron_monitoring_database_cleanup_action" model="ir.cron">
+            <field name="name">Database monitoring cleanup</field>
+            <field eval="True" name="active"/>
+            <field name="user_id" ref="base.user_root"/>
+            <field name="interval_number">1</field>
+            <field name="interval_type">days</field>
+            <field name="numbercall">-1</field>
+            <field eval="False" name="doall"/>
+            <field eval="'server.monitor.database'" name="model"/>
+            <field eval="'cleanup'" name="function"/>
+            <field eval="'(365,)'" name="args"/>
+            <field name="priority" eval='500'/>
+        </record>
+
+</data>
+</openerp>

=== added file 'server_monitoring/server_monitoring_view.xml'
--- server_monitoring/server_monitoring_view.xml	1970-01-01 00:00:00 +0000
+++ server_monitoring/server_monitoring_view.xml	2014-04-10 10:25:03 +0000
@@ -0,0 +1,342 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data>
+    <menuitem groups="base.group_no_one" 
+              id="server_monitoring" 
+              name="Server Monitoring" 
+              parent="base.menu_reporting" 
+              sequence="100"/>
+
+    <record model="ir.ui.view" id="server_monitor_database_tree_view">
+      <field name="name">Server Monitor Database form view</field>
+      <field name="model">server.monitor.database</field>
+      <field name="arch" type="xml">
+        <tree string="Server Monitor" version="7.0">
+          <field name='name'/>
+        </tree>
+    </field>
+
+    </record>
+    <record model="ir.ui.view" id="server_monitor_database_form_view">
+      <field name="name">Server Monitor Database tree view</field>
+      <field name="model">server.monitor.database</field>
+      <field name="arch" type="xml">
+        <form string="Server Monitor" version="7.0">
+          <sheet>
+            <field name="name"/>
+            <notebook>
+              <page string="Nb rows">
+                <group>
+                  <field name='table_nb_row_ids' nolabel="1">
+                    <tree>
+                      <field name='name'/>
+                      <field name='count'/>
+                    </tree>
+                  </field>
+                </group>
+              </page>
+              <page string="Table size">
+                <group>
+                  <field name='table_size_ids' nolabel="1">
+                    <tree>
+                      <field name='name'/>
+                      <field name='hsize'/>
+                    </tree>
+                  </field>
+                </group>
+              </page>
+              <page string="Table reads">
+                <group>
+                  <field name='table_activity_read_ids' nolabel="1">
+                    <tree>
+                      <field name='name'/>
+                      <field name='disk_reads'/>
+                      <field name='cache_reads'/>
+                      <field name='total_reads'/>
+                    </tree>
+                  </field>
+                </group>
+              </page>
+              <page string="Table updates">
+                <group>
+                  <field name='table_activity_update_ids' nolabel="1">
+                    <tree>
+                      <field name='name'/>
+                      <field name='seq_scan'/>
+                      <field name='idx_scan'/>
+                      <field name='lines_read_total'/>
+                      <field name='num_insert'/>
+                      <field name='num_update'/>
+                      <field name='num_delete'/>
+                    </tree>
+                  </field>
+                </group>
+              </page>
+            </notebook>
+            <group>
+              <field name='info' string="Information"/>
+            </group>
+          </sheet>
+        </form>
+      </field>
+    </record>
+
+    <record model="ir.ui.view" id="server_monitor_process_search_view">
+      <field name="name">Server Monitor Process search view</field>
+      <field name="model">server.monitor.process</field>
+      <field name="arch" type="xml">
+        <search string="Search Server Monitor Process" version="7.0">
+          <field name="model"/>
+          <field name="method"/>
+          <field name="uid"/>
+          <group expand="0" string="Group By...">
+            <filter string="date" domain="[]" context="{'group_by': 'name'}"/>
+            <filter string="PID" domain="[]" context="{'group_by': 'pid'}"/>
+            <filter string="model" domain="[]" context="{'group_by': 'model'}"/>
+            <filter string="method" domain="[]" context="{'group_by': 'method'}"/>
+          </group>
+        </search>
+    </field>
+    </record>
+
+    <record model="ir.ui.view" id="server_monitor_process_tree_view">
+      <field name="name">Server Monitor Process tree view</field>
+      <field name="model">server.monitor.process</field>
+      <field name="arch" type="xml">
+        <tree string="Server Monitor Process" version="7.0">
+          <field name='name'/>
+          <field name='pid'/>
+          <field name='cpu_time'/>
+          <field name='memory'/>
+          <field name='uid'/>
+          <field name='model'/>
+          <field name='method'/>
+        </tree>
+    </field>
+    </record>
+
+    <record model="ir.ui.view" id="server_monitor_process_form_view">
+      <field name="name">Server Monitor Process form view</field>
+      <field name="model">server.monitor.process</field>
+      <field name="arch" type="xml">
+        <form string="Server Monitor" version="7.0">
+          <sheet>
+            <field name="name"/>
+            <notebook>
+              <page string='call information'>
+                <group>
+                  <field name='pid'/>
+                  <field name='thread'/>
+                  <field name='cpu_time'/>
+                  <field name='memory'/>
+                  <field name='uid'/>
+                  <field name='model'/>
+                  <field name='method'/>
+                  <field name='status'/>
+                  <field name='sessionid'/>
+                </group>
+              </page>
+              <page string="Class count">
+                <group>
+                  <field name='class_count_ids' nolabel="1">
+                    <tree>
+                      <field name='name'/>
+                      <field name='count'/>
+                    </tree>
+                  </field>
+                </group>
+              </page>
+            </notebook>
+            <group>
+              <field name='info' string="Information"/>
+            </group>
+          </sheet>
+        </form>
+      </field>
+    </record>
+
+
+    <record model="ir.actions.act_window" id="server_monitor_process_info">
+      <field name="name">Process Info</field>
+      <field name="res_model">server.monitor.process</field>
+      <field name="view_mode">tree,form</field>
+    </record>
+
+    <menuitem name="Process Info" 
+              parent="server_monitoring" 
+              id="server_monitor_process_menu" 
+              action="server_monitor_process_info"
+              sequence="10"/>
+
+    <record model="ir.actions.act_window" id="server_monitor_database_info">
+      <field name="name">Database Info</field>
+      <field name="res_model">server.monitor.database</field>
+      <field name="view_mode">tree,form</field>
+    </record>
+    <menuitem name="Database Info"
+              parent="server_monitoring" 
+              id="server_monitor_database_menu" 
+              action="server_monitor_database_info" 
+              sequence="20"/>
+
+
+    <record model="ir.ui.view" id="server_monitor_model_row_count_search_view">
+      <field name="name">Server Monitor model row count search view</field>
+      <field name="model">server.monitor.model.row.count</field>
+      <field name="arch" type="xml">
+        <search string="Search Server Monitor Process" version="7.0">
+          <field name="timestamp"/>
+          <field name="name"/>
+          <field name="count"/>
+          <group expand="0" string="Group By...">
+            <filter string="timestamp" domain="[]" context="{'group_by': 'timestamp'}"/>
+            <filter string="name" domain="[]" context="{'group_by': 'name'}"/>
+          </group>
+        </search>
+    </field>
+    </record>
+
+    <record model="ir.ui.view" id="server_monitor_model_row_count_tree_view">
+      <field name="name">Server Monitor model row count tree view</field>
+      <field name="model">server.monitor.model.row.count</field>
+      <field name="arch" type="xml">
+        <tree string="Server Monitor Process" version="7.0">
+          <field name='timestamp'/>
+          <field name='name'/>
+          <field name='count'/>
+        </tree>
+    </field>
+    </record>
+
+
+
+    <record model="ir.actions.act_window" id="server_monitor_database_table_rows">
+      <field name="name">DB Rows</field>
+      <field name="res_model">server.monitor.model.row.count</field>
+      <field name="view_mode">tree</field>
+    </record>
+    <menuitem name="DB Rows" 
+              parent="server_monitoring" 
+              id="server_monitor_database_table_rows_menu" 
+              action="server_monitor_database_table_rows" sequence="30"/>
+
+
+    <record model="ir.ui.view" id="server_monitor_model_table_size_search_view">
+      <field name="name">Server Monitor model table size search view</field>
+      <field name="model">server.monitor.model.table.size</field>
+      <field name="arch" type="xml">
+        <search string="Search Server Monitor Table Row Count" version="7.0">
+          <field name="timestamp"/>
+          <field name="name"/>
+          <group expand="0" string="Group By...">
+            <filter string="timestamp" domain="[]" context="{'group_by': 'timestamp'}"/>
+            <filter string="name" domain="[]" context="{'group_by': 'name'}"/>
+          </group>
+        </search>
+    </field>
+    </record>
+    <record model="ir.ui.view" id="server_monitor_model_table_size_tree_view">
+      <field name="name">Server Monitor model table size tree view</field>
+      <field name="model">server.monitor.model.table.size</field>
+      <field name="arch" type="xml">
+        <tree string="Server Monitor DB Table Row Count" version="7.0">
+          <field name='timestamp'/>
+          <field name='name'/>
+          <field name='hsize'/>
+        </tree>
+    </field>
+    </record>
+    <record model="ir.actions.act_window" id="server_monitor_database_table_size">
+      <field name="name">DB Rows</field>
+      <field name="res_model">server.monitor.model.table.size</field>
+      <field name="view_mode">tree</field>
+    </record>
+    <menuitem name="DB Table Size" 
+              parent="server_monitoring" 
+              id="server_monitor_database_table_size_menu"
+              action="server_monitor_database_table_size"
+              sequence="40"/>
+
+    <record model="ir.ui.view" id="server_monitor_model_table_activity_read_search_view">
+      <field name="name">Server Monitor table activity updates search view</field>
+      <field name="model">server.monitor.model.table.activity.read</field>
+      <field name="arch" type="xml">
+        <search string="Search Server Monitor Table Activity read" version="7.0">
+          <field name="timestamp"/>
+          <field name="name"/>
+          <group expand="0" string="Group By...">
+            <filter string="timestamp" domain="[]" context="{'group_by': 'timestamp'}"/>
+            <filter string="name" domain="[]" context="{'group_by': 'name'}"/>
+          </group>
+        </search>
+    </field>
+    </record>
+    <record model="ir.ui.view" id="server_monitor_model_table_activity_read_tree_view">
+      <field name="name">Server Monitor table activity tree view</field>
+      <field name="model">server.monitor.model.table.activity.read</field>
+      <field name="arch" type="xml">
+        <tree string="Server Monitor DB Table activity" version="7.0">
+          <field name='timestamp'/>
+          <field name='name'/>
+          <field name='disk_reads'/>
+          <field name='cache_reads'/>
+          <field name='total_reads'/>
+        </tree>
+    </field>
+    </record>
+    <record model="ir.actions.act_window" id="server_monitor_database_table_activity_read">
+      <field name="name">DB Rows</field>
+      <field name="res_model">server.monitor.model.table.activity.read</field>
+      <field name="view_mode">tree</field>
+    </record>
+    <menuitem name="DB Reads"
+              parent="server_monitoring"
+              id="server_monitor_database_table_activity_read_menu"
+              action="server_monitor_database_table_activity_read"
+              sequence="50"/>
+
+
+    <record model="ir.ui.view" id="server_monitor_model_table_activity_update_search_view">
+      <field name="name">Server Monitor table activity updates search view</field>
+      <field name="model">server.monitor.model.table.activity.update</field>
+      <field name="arch" type="xml">
+        <search string="Search Server Monitor Table Activity updates" version="7.0">
+          <field name="timestamp"/>
+          <field name="name"/>
+          <group expand="0" string="Group By...">
+            <filter string="timestamp" domain="[]" context="{'group_by': 'timestamp'}"/>
+            <filter string="name" domain="[]" context="{'group_by': 'name'}"/>
+          </group>
+        </search>
+    </field>
+    </record>
+    <record model="ir.ui.view" id="server_monitor_model_table_activity_update_tree_view">
+      <field name="name">Server Monitor table activity updates tree view</field>
+      <field name="model">server.monitor.model.table.activity.update</field>
+      <field name="arch" type="xml">
+        <tree string="Server Monitor DB Table size" version="7.0">
+          <field name='timestamp'/>
+          <field name='name'/>
+          <field name='seq_scan'/>
+          <field name='idx_scan'/>
+          <field name='lines_read_total'/>
+          <field name='num_insert'/>
+          <field name='num_update'/>
+          <field name='num_delete'/>
+        </tree>
+    </field>
+    </record>
+    <record model="ir.actions.act_window" id="server_monitor_database_table_activity_update">
+      <field name="name">DB Updates</field>
+      <field name="res_model">server.monitor.model.table.activity.update</field>
+      <field name="view_mode">tree</field>
+    </record>
+    <menuitem name="DB Rows"
+              parent="server_monitoring"
+              id="server_monitor_database_table_activity_update_menu"
+              action="server_monitor_database_table_activity_update"
+              sequence="60"/>
+
+
+  </data>
+</openerp>


Follow ups