← Back to team overview

credativ team mailing list archive

[Merge] lp:~0k.io/openupgrade-server/8.0-openupgrade-records-2 into lp:openupgrade-server/8.0

 

Valentin Lab has proposed merging lp:~0k.io/openupgrade-server/8.0-openupgrade-records-2 into lp:openupgrade-server/8.0.

Requested reviews:
  Stefan Rijnhart (Therp) (stefan-therp)

For more details, see:
https://code.launchpad.net/~0k.io/openupgrade-server/8.0-openupgrade-records-2/+merge/214768

This commit contains all the file of the ``openupgrade_records`` module.

You won't find the core or the doc code here, this was split up in:

core: https://code.launchpad.net/~0k.io/openupgrade-server/8.0-openupgrade-base-2
docs: https://code.launchpad.net/~0k.io/openupgrade-server/8.0-openupgrade-doc-2
-- 
https://code.launchpad.net/~0k.io/openupgrade-server/8.0-openupgrade-records-2/+merge/214768
Your team OpenUpgrade Committers is subscribed to branch lp:openupgrade-server/8.0.
=== added directory 'openerp/addons/openupgrade_records'
=== added file 'openerp/addons/openupgrade_records/__init__.py'
--- openerp/addons/openupgrade_records/__init__.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/__init__.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,2 @@
+import model
+import lib

=== added file 'openerp/addons/openupgrade_records/__openerp__.py'
--- openerp/addons/openupgrade_records/__openerp__.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/__openerp__.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    This module Copyright (C) 2012 OpenUpgrade community
+#    https://launchpad.net/~openupgrade-committers
+#
+#    Contributors:
+#    Therp BV <http://therp.nl>
+#
+#    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': 'OpenUpgrade Records',
+    'version': '0.2',
+    'category': 'Normal',
+    'description': """Allow OpenUpgrade records to be 
+stored in the database and compare with other servers.
+
+This module depends on OpenERP client lib:
+
+    easy_install openerp-client-lib
+
+""",
+    'author': 'OpenUpgrade Community',
+    'maintainer': 'OpenUpgrade Community',
+    'contributors': ['Therp BV'],
+    'website': 'https://launchpad.net/~openupgrade-committers',
+    'depends': [],
+    'init_xml': [],
+    'update_xml': [
+        'view/openupgrade_record.xml',
+        'view/comparison_config.xml',
+        'view/analysis_wizard.xml',
+        'view/generate_records_wizard.xml',
+        'view/install_all_wizard.xml',
+        'security/ir.model.access.csv',
+        ],
+    'demo_xml': [
+    ],
+    'test': [
+    ],
+    'installable': True,
+    'auto_install': False,
+    'external_dependencies': {
+        'python' : ['openerplib'],
+        },
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added file 'openerp/addons/openupgrade_records/__terp__.py'
--- openerp/addons/openupgrade_records/__terp__.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/__terp__.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    This module Copyright (C) 2012 OpenUpgrade community
+#    https://launchpad.net/~openupgrade-committers
+#
+#    Contributors:
+#    Therp BV <http://therp.nl>
+#
+#    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': 'OpenUpgrade Records',
+    'version': '0.2',
+    'category': 'Normal',
+    'description': """Allow OpenUpgrade records to be 
+stored in the database and compare with other servers.
+
+This module depends on OpenERP client lib:
+
+    easy_install openerp-client-lib
+
+""",
+    'author': 'OpenUpgrade Community',
+    'maintainer': 'OpenUpgrade Community',
+    'contributors': ['Therp BV'],
+    'website': 'https://launchpad.net/~openupgrade-committers',
+    'depends': [],
+    'init_xml': [],
+    'update_xml': [
+        'view/openupgrade_record.xml',
+        'view/comparison_config.xml',
+        'view/analysis_wizard.xml',
+        'view/generate_records_wizard.xml',
+        'view/install_all_wizard.xml',
+        'security/ir.model.access.csv',
+        ],
+    'demo_xml': [
+    ],
+    'test': [
+    ],
+    'installable': True,
+    'auto_install': False,
+    'external_dependencies': {
+        'python' : ['openerplib'],
+        },
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added directory 'openerp/addons/openupgrade_records/lib'
=== added file 'openerp/addons/openupgrade_records/lib/__init__.py'
--- openerp/addons/openupgrade_records/lib/__init__.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/lib/__init__.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,2 @@
+import apriori
+import compare

=== added file 'openerp/addons/openupgrade_records/lib/apriori.py'
--- openerp/addons/openupgrade_records/lib/apriori.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/lib/apriori.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,14 @@
+""" Encode any known changes to the database here
+to help the matching process
+"""
+
+renamed_modules = {
+    'account_coda': 'l10n_be_coda',
+    'base_crypt': 'auth_crypt',
+    'mrp_subproduct': 'mrp_byproduct',
+    'users_ldap': 'auth_ldap',
+    'wiki': 'document_page',
+    }
+
+renamed_models = {
+    }

=== added file 'openerp/addons/openupgrade_records/lib/compare.py'
--- openerp/addons/openupgrade_records/lib/compare.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/lib/compare.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,250 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    This module copyright (C) 2011 Therp BV (<http://therp.nl>).
+#
+#    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/>.
+#
+##############################################################################
+
+#####################################################################
+#   library providing a function to analyse two progressive database
+#   layouts from the OpenUpgrade server.
+#####################################################################
+
+import copy
+
+try:
+    from openerp.addons.openupgrade_records.lib import apriori
+except ImportError:
+    from openupgrade_records.lib import apriori
+
+keys = [
+    'module',
+    'mode',
+    'model',
+    'field',
+    'type',
+    'isfunction',
+    'relation',
+    'required',
+    'selection_keys',
+    'req_default',
+    'inherits',
+    ]
+
+def module_map(module):
+    return apriori.renamed_modules.get(
+        module, module)
+
+def compare_records(dict_old, dict_new, fields):
+    """
+    Check equivalence of two OpenUpgrade field representations
+    with respect to the keys in the 'fields' arguments.
+    Take apriori knowledge into account for mapped modules or
+    model names.
+    Return True of False.
+    """
+    for field in fields:
+        if field == 'module':
+            if (module_map(dict_old[field]) != dict_new[field]):
+                return False
+        elif field == 'model':
+            if (apriori.renamed_models.get(
+                    dict_old[field], dict_old[field]) != dict_new[field]):
+                return False
+        else:
+            if dict_old[field] != dict_new[field]:
+                return False
+    return True
+
+def search(item, item_list, fields):
+    """
+    Find a match of a dictionary in a list of similar dictionaries
+    with respect to the keys in the 'fields' arguments.
+    Return the item if found or None.
+    """
+    for i in item_list:
+        if not compare_records(item, i, fields):
+            continue
+        return i
+    return None
+
+def fieldprint(old, new, field, text, reprs):
+    fieldrepr = "%s (%s)" % (old['field'], old['type'])
+    repr = '%s / %s / %s' % (
+        old['module'].ljust(12), old['model'].ljust(24), fieldrepr.ljust(30))
+    if text:
+        reprs.setdefault(module_map(old['module']), []).append(
+            "%s: %s" % (repr, text))
+    else:
+        reprs.setdefault(module_map(old['module']), []).append(
+            "%s: %s is now \'%s\' ('%s')" % (
+                repr, field, new[field], old[field]))
+
+def report_generic(new, old, attrs, reprs):
+    for attr in attrs:
+        if attr == 'required':
+            if old[attr] != new['required'] and new['required']:
+                text = "now required"
+                if new['req_default']:
+                    text += ', default = %s' % new['req_default']
+                fieldprint(old, new, None, text, reprs)
+        elif attr == 'isfunction':
+            if old['isfunction'] != new['isfunction']:
+                if new['isfunction']:
+                    text = "now a function"
+                else:
+                    text = "not a function anymore"
+                fieldprint(old, new, None, text, reprs)
+        else:
+            if old[attr] != new[attr]:
+                fieldprint(old, new, attr, None, reprs)
+
+def compare_sets(old_records, new_records):
+    """
+    Compare a set of OpenUpgrade field representations.
+    Try to match the equivalent fields in both sets.
+    Return a textual representation of changes in a dictionary with
+    module names as keys. Special case is the 'general' key
+    which contains overall remarks and matching statistics.
+    """
+    reprs = {'general': []}
+
+    for record in old_records + new_records:
+        record['matched'] = False
+    origlen = len(old_records)
+    new_models = set([ column['model'] for column in new_records ])
+    old_models = set([ column['model'] for column in old_records ])
+
+    matched_direct = 0
+    matched_other_module = 0
+    matched_other_type = 0
+    matched_other_name = 0
+    in_obsolete_models = 0
+    
+    obsolete_models = []
+    for model in old_models:
+        if model not in new_models:
+            obsolete_models.append(model)
+            reprs['general'].append('obsolete model %s' % model)
+
+    for column in copy.copy(old_records):
+        if column['model'] in obsolete_models:
+            old_records.remove(column)
+            in_obsolete_models += 1
+
+    for model in new_models:
+        if model not in old_models:
+            reprs['general'].append('new model %s' % model)
+    
+    def match(match_fields, report_fields, warn=False):
+        count = 0
+        for column in copy.copy(old_records):
+            found = search(column, new_records, match_fields)
+            if found:
+                if warn:
+                    pass
+                    #print "Tentatively"
+                report_generic(found, column, report_fields, reprs)
+                old_records.remove(column)
+                new_records.remove(found)
+                count += 1
+        return count
+
+    matched_direct = match(
+        ['module', 'mode', 'model', 'field'],
+        ['relation', 'type', 'selection_keys', 'inherits', 'isfunction', 'required'])
+
+    # other module, same type and operation
+    matched_other_module = match(
+        ['mode', 'model', 'field', 'type'],
+        ['module', 'relation', 'selection_keys', 'inherits', 'isfunction', 'required'])
+
+    # other module, same operation, other type
+    matched_other_type = match(
+        ['mode', 'model', 'field'],
+        ['relation', 'type', 'selection_keys', 'inherits', 'isfunction', 'required'])
+
+    # fields with other names
+    #matched_other_name = match(
+    # ['module', 'type', 'relation'],
+    # ['field', 'relation', 'type', 'selection_keys',
+    #   'inherits', 'isfunction', 'required'], warn=True)
+
+    printkeys = [
+        'relation', 'required', 'selection_keys',
+        'req_default', 'inherits', 'mode'
+        ]
+    for column in old_records:
+        # we do not care about removed function fields
+        if not column['isfunction']:
+            if column['mode'] == 'create':
+                column['mode'] = ''
+            fieldprint(
+                column, None, None, "DEL " + ", ".join(
+                    [k + ': ' + str(column[k]) for k in printkeys if column[k]]
+                    ), reprs)
+
+    for column in new_records:
+        # we do not care about newly added function fields
+        if not column['isfunction']:
+            if column['mode'] == 'create':
+                column['mode'] = ''
+            fieldprint(
+                column, None, None, "NEW " + ", ".join(
+                    [k + ': ' + str(column[k]) for k in printkeys if column[k]]
+                    ), reprs)
+
+    for line in [
+        "# %d fields matched," % (origlen - len(old_records)),
+        "# Direct match: %d" % matched_direct,
+        "# Found in other module: %d" % matched_other_module,
+        "# Found with different type: %d" % matched_other_type,
+        "# Found with different name: %d" % matched_other_name,
+        "# In obsolete models: %d" % in_obsolete_models,
+        "# Not matched: %d" % len(old_records),
+        "# New columns: %d" % len(new_records),
+        ]:
+        reprs['general'].append(line)
+    return reprs
+
+def compare_xml_sets(old_records, new_records):
+    reprs = {}
+    match_fields = ['module', 'model', 'name']
+    for column in copy.copy(old_records):
+        found = search(column, new_records, match_fields)
+        if found:
+            old_records.remove(column)
+            new_records.remove(found)
+
+    for record in old_records:
+        record['old'] = True
+    for record in new_records:
+        record['new'] = True
+
+    sorted_records = sorted(
+        old_records + new_records,
+        key=lambda k: '%s%s%s' % (k['model'].ljust(128), 'old' in k, k['name'])
+    )
+    for entry in sorted_records:
+        if 'old' in entry:
+            content = 'DEL %s: %s' % (entry['model'],
+                                                          entry['name'])
+        elif 'new' in entry:
+            content = 'NEW %s: %s' % (entry['model'],
+                                                      entry['name'])
+        reprs.setdefault(module_map(entry['module']), []).append(content)
+    return reprs

=== added directory 'openerp/addons/openupgrade_records/model'
=== added file 'openerp/addons/openupgrade_records/model/__init__.py'
--- openerp/addons/openupgrade_records/model/__init__.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/__init__.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,6 @@
+import openupgrade_record
+import comparison_config
+import analysis_wizard
+import generate_records_wizard
+import install_all_wizard
+

=== added file 'openerp/addons/openupgrade_records/model/analysis_wizard.py'
--- openerp/addons/openupgrade_records/model/analysis_wizard.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/analysis_wizard.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    This module Copyright (C) 2012 OpenUpgrade community
+#    https://launchpad.net/~openupgrade-committers
+#
+#    Contributors:
+#    Therp BV <http://therp.nl>
+#
+#    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/>.
+#
+##############################################################################
+
+import os
+from openerp.osv import osv, fields
+
+try:
+    from openerp.addons.openupgrade_records.lib import compare
+    from openerp.addons.openupgrade_records.lib import apriori
+    from openerp.addons import get_module_path
+except ImportError:
+    from openupgrade_records.lib import compare
+    from openupgrade_records.lib import apriori
+    from addons import get_module_path
+
+class openupgrade_analysis_wizard(osv.osv_memory):
+    _name = 'openupgrade.analysis.wizard'
+    _description = 'OpenUpgrade Analysis Wizard'
+    _columns = {
+        'server_config': fields.many2one(
+            'openupgrade.comparison.config',
+            'Configuration', required=True),
+        'state': fields.selection(
+            [('init', 'Init'), ('ready', 'Ready')], 'State',
+            readonly=True),
+        'log': fields.text('Log'),
+        'write': fields.boolean(
+            'Write files',
+            help='Write analysis files to the module directories'
+            ),
+        }
+    _defaults = {
+        'state': lambda *a: 'init',
+        'write': lambda *a: True,
+        }
+
+    def get_communication(self, cr, uid, ids, context=None):
+        """
+        Retrieve both sets of database representations,
+        perform the comparison and register the resulting
+        change set
+        """
+        def write_file(
+            module, version, contents, filename='openupgrade_analysis.txt'):
+            module_path = get_module_path(module)
+            if not module_path:
+                return "ERROR: could not find module path:\n"
+            full_path = os.path.join(
+                module_path, 'migrations', version)
+            if not os.path.exists(full_path):
+                try:
+                    os.makedirs(full_path)
+                except os.error:
+                    return "ERROR: could not create migrations directory:\n"
+            logfile = os.path.join(full_path, filename)
+            try:
+                f = open(logfile, 'w')
+            except Exception:
+                return "ERROR: could not open file %s for writing:\n" % logfile
+            f.write(contents)
+            f.close()
+            return None
+
+        wizard = self.browse(cr, uid, ids[0], context=context)
+        # Retrieve connection and access methods
+        conf_obj = self.pool.get('openupgrade.comparison.config')
+        connection = conf_obj.get_connection(
+            cr, uid, [wizard.server_config.id], context=context)
+        remote_record_obj = connection.get_model('openupgrade.record')
+        local_record_obj = self.pool.get('openupgrade.record')
+
+        # Retrieve field representations and compare
+        remote_records = remote_record_obj.field_dump(context)
+        local_records = local_record_obj.field_dump(cr, uid, context)
+        modules_record = set([record['module'] for record in remote_records + local_records])
+        res = compare.compare_sets(remote_records, local_records)
+
+        # Retrieve xml id representations and compare
+        fields = ['module', 'model', 'name']
+        local_xml_record_ids = local_record_obj.search(
+            cr, uid, [('type', '=', 'xmlid')])
+        remote_xml_record_ids = remote_record_obj.search(
+            [('type', '=', 'xmlid')])
+        local_xml_records = [
+            dict([(field, x[field]) for field in fields])
+            for x in local_record_obj.read(
+                cr, uid, local_xml_record_ids, fields)
+            ]
+        remote_xml_records = [
+            dict([(field, x[field]) for field in fields])
+            for x in remote_record_obj.read(
+                remote_xml_record_ids, fields)
+            ]
+        modules_xml_records = set([record['module'] for record in remote_xml_records + local_xml_records])
+        res_xml = compare.compare_xml_sets(
+            remote_xml_records, local_xml_records)
+
+        # reorder and output the result
+        keys = ['general'] + list(modules_record & modules_xml_records)
+        module_obj = self.pool.get('ir.module.module')
+        module_ids = module_obj.search(
+            cr, uid, [('state', '=', 'installed')])
+        modules = dict([(x['name'], x) for x in module_obj.read(cr, uid, module_ids)])
+        general = ''
+        for key in keys:
+            contents = "---Fields in module '%s'---\n" % key
+            if key in res:
+                contents += '\n'.join([unicode(line) for line in sorted(res[key])])
+                if res[key]:
+                    contents += '\n'
+            contents += "---XML records in module '%s'---\n" % key
+            if key in res_xml:
+                contents += '\n'.join([unicode(line) for line in res_xml[key]])
+                if res_xml[key]:
+                    contents += '\n'
+            if key not in res and key not in res_xml:
+                contents += '-- nothing has changed in this module'
+            if key == 'general':
+                general += contents
+                continue
+            if key not in modules:
+                general += (
+                    "ERROR: module not in list of installed modules:\n"
+                    + contents)
+                continue
+            if wizard.write:
+                error = write_file(
+                    key, modules[key]['installed_version'], contents)
+                if error:
+                    general += error
+                    general += contents
+            else:
+                general += contents
+
+        # Store the general log in as many places as possible ;-)
+        if wizard.write and 'base' in modules:
+            write_file(
+                'base', modules['base']['installed_version'], general,
+                'openupgrade_general_log.txt')
+        self.pool.get('openupgrade.comparison.config').write(
+            cr, uid, wizard.server_config.id,
+            {'last_log': general})
+        self.write(cr, uid, ids, {'state': 'ready', 'log': general})
+
+        result = {
+            'name': self._description,
+            'view_type': 'form',
+            'view_mode': 'form',
+            'res_model': 'openupgrade.analysis.wizard',
+            'domain': [],
+            'context': context,
+            'type': 'ir.actions.act_window',
+            #'target': 'new',
+            'res_id': ids[0],
+            }
+        return result
+
+openupgrade_analysis_wizard()
+

=== added file 'openerp/addons/openupgrade_records/model/comparison_config.py'
--- openerp/addons/openupgrade_records/model/comparison_config.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/comparison_config.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    This module Copyright (C) 2012 OpenUpgrade community
+#    https://launchpad.net/~openupgrade-committers
+#
+#    Contributors:
+#    Therp BV <http://therp.nl>
+#
+#    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 openerp.osv import osv, fields
+import openerplib
+from openerp.tools.translate import _
+
+class openupgrade_comparison_config(osv.osv):
+    _name = 'openupgrade.comparison.config'
+    _columns = {
+        'name': fields.char('Name', size=64),
+        'server': fields.char('Server', size=64, required=True),
+        'port': fields.integer('Port', required=True),
+        'protocol': fields.selection(
+            [('http://', 'XML-RPC')],
+            # ('https://', 'XML-RPC Secure')], not supported by libopenerp
+            'Protocol', required=True),
+        'database': fields.char('Database', size=64, required=True),
+        'username': fields.char('Username', size=24, required=True),
+        'password': fields.char('Password', size=24, required=True, password=True),
+        'last_log': fields.text('Last log'),
+        }
+    _defaults = {
+        'port': lambda *a: 8069,
+        'protocol': lambda *a: 'http://',
+        }
+
+    def get_connection(self, cr, uid, ids, context=None):
+        if not ids:
+            raise osv.except_osv(
+                _("Cannot connect"), _("Invalid id passed."))
+        conf = self.read(cr, uid, ids[0], context=None)
+        return openerplib.get_connection(
+           hostname=conf['server'],
+           database=conf['database'],
+           login=conf['username'],
+           password=conf['password'],
+           port=conf['port'],
+           )
+
+    def test_connection(self, cr, uid, ids, context=None):
+        try:
+            connection = self.get_connection(cr, uid, [ids[0]], context)
+            user_model = connection.get_model("res.users")
+            ids = user_model.search([("login", "=", "admin")])
+            user_info = user_model.read(ids[0], ["name"])
+        except Exception, e:
+            raise osv.except_osv(
+                _("Connection failed."), unicode(e))
+        raise osv.except_osv(
+            _("Connection succesful."),
+            _("%s is connected.") % user_info["name"]
+            )
+    
+    def analyze(self, cr, uid, ids, context=None):
+        """
+        Run the analysis wizard
+        """
+        wizard_obj = self.pool.get('openupgrade.analysis.wizard')
+        wizard_id = wizard_obj.create(
+            cr, uid, {'server_config': ids[0]}, context)
+        result = {
+            'name': wizard_obj._description,
+            'view_type': 'form',
+            'view_mode': 'form',
+            'res_model': 'openupgrade.analysis.wizard',
+            'domain': [],
+            'context': context,
+            'type': 'ir.actions.act_window',
+            'target': 'new',
+            'res_id': wizard_id,
+            'nodestroy': True,
+            }
+        return result
+
+openupgrade_comparison_config()

=== added file 'openerp/addons/openupgrade_records/model/generate_records_wizard.py'
--- openerp/addons/openupgrade_records/model/generate_records_wizard.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/generate_records_wizard.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    This module Copyright (C) 2012 OpenUpgrade community
+#    https://launchpad.net/~openupgrade-committers
+#
+#    Contributors:
+#    Therp BV <http://therp.nl>
+#
+#    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/>.
+#
+##############################################################################
+
+import os
+from openerp.osv import osv, fields
+from openerp import pooler
+try:
+    from openerp.openupgrade import openupgrade_tools
+except ImportError:
+    from openupgrade import openupgrade_tools
+
+class generate_records_wizard(osv.osv_memory):
+    _name = 'openupgrade.generate.records.wizard'
+    _description = 'OpenUpgrade Generate Records Wizard'
+    _columns = {
+        'state': fields.selection([('init', 'init'), ('ready', 'ready')], 'State'),
+        }
+    _defaults = {
+        'state': lambda *a: 'init',
+        }
+
+    def generate(self, cr, uid, ids, context=None):
+        """
+        Main wizard step. Make sure that all modules are up-to-date,
+        then reinitialize all installed modules.
+        Equivalent of running the server with '-d <database> --init all'
+
+        The goal of this is to fill the records table.
+
+        TODO: update module list and versions, then update all modules?
+        """
+        # Truncate the records table
+        if (openupgrade_tools.table_exists(cr, 'openupgrade_attribute') and
+            openupgrade_tools.table_exists(cr, 'openupgrade_record')):
+            cr.execute(
+                'TRUNCATE openupgrade_attribute, openupgrade_record;'
+                )
+
+        # Need to get all modules in state 'installed'
+        module_obj = self.pool.get('ir.module.module')
+        module_ids = module_obj.search(
+            cr, uid, [('state', 'in', ['to install', 'to upgrade'])])
+        if module_ids:
+            cr.commit()
+            _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+        # Did we succeed above?
+        module_ids = module_obj.search(
+            cr, uid, [('state', 'in', ['to install', 'to upgrade'])])
+        if module_ids:
+            modules = module_obj.read(
+                cr, uid, module_ids, ['name'], context=context)
+            raise except_osv(
+                "Cannot reliably generate records", 
+                ("Cannot seem to install or upgrade modules " +
+                 ', '.join([x['name'] for x in modules])))
+        # Now reinitialize all installed modules
+        module_ids = module_obj.search(
+            cr, uid, [('state', '=', 'installed')])
+        module_obj.write(
+            cr, uid, module_ids, {'state': 'to install'})
+        cr.commit()
+        _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+        self.write(cr, uid, ids, {'state': 'ready'})
+        # and we are done
+        return True
+
+generate_records_wizard()
+

=== added file 'openerp/addons/openupgrade_records/model/install_all_wizard.py'
--- openerp/addons/openupgrade_records/model/install_all_wizard.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/install_all_wizard.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,113 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    This module Copyright (C) 2012 OpenUpgrade community
+#    https://launchpad.net/~openupgrade-committers
+#
+#    Contributors:
+#    Therp BV <http://therp.nl>
+#
+#    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/>.
+#
+##############################################################################
+
+import time
+import os
+from openerp.osv import osv, fields
+from openerp import pooler
+
+class install_all_wizard(osv.osv_memory):
+    _name = 'openupgrade.install.all.wizard'
+    _description = 'OpenUpgrade Install All Wizard'
+    _columns = {
+        'state': fields.selection([('init', 'init'), ('ready', 'ready')], 'State', readonly=True),
+        'to_install': fields.integer('Number of modules to install', readonly=True),
+        }
+    _defaults = {
+        'state': lambda *a: 'init',
+        }
+
+
+    def default_get(self, cr, uid, fields, context=None):
+        """
+        Update module list and retrieve the number
+        of installable modules
+        """
+        res = super(install_all_wizard, self).default_get(
+            cr, uid, fields, context=None)
+        module_obj = self.pool.get('ir.module.module')
+        update, add = module_obj.update_list(cr, uid,)
+        print "%s modules added" % add
+        module_ids = module_obj.search(
+            cr, uid, [('state', 'not in', ['installed', 'uninstallable', 'unknown'])])
+        res.update(
+            {'to_install': module_ids and len(module_ids) or False}
+            )
+        return res
+
+    def quirk_fiscalyear(self, cr, uid, ids, context=None):
+        """ 
+        Install account module first and create a fiscal year,
+        in order to prevent "No fiscal year defined" exception
+        during an upgrade or reinstallation of the account module.
+        
+        Refer to account_fiscalyear.find(), which is called as
+        a default function by the orm upon module upgrade.
+        """
+        module_obj = self.pool.get('ir.module.module')
+        pool = self.pool
+        # Retrieve status of the account module
+        account_module_id = module_obj.search(
+            cr, uid, [('name', '=', 'account')], context=context)[0]
+        state = module_obj.read(
+            cr, uid, account_module_id, ['state'], context=context)['state']
+        if state != 'installed':
+            # Cancel installation of other modules
+            module_ids = module_obj.search(
+                cr, uid, [('state', '=', 'to install')])
+            module_obj.write(cr, uid, module_ids, {'state': 'uninstalled'})
+            # Mark the module and its dependencies
+            module_obj.button_install(cr, uid, [account_module_id])
+            # Install account module
+            cr.commit()
+            _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+        # get or create today's fiscal year
+        fy_obj = pool.get('account.fiscalyear')
+        if not fy_obj.find(cr, uid, False, exception=False, context=context):
+            fy_obj.create(cr, uid, {
+                    'name': time.strftime('%Y'),
+                    'code': time.strftime('%Y'),
+                    'date_start': "%s-01-01" % time.strftime('%Y'),
+                    'date_stop': "%s-12-31" % time.strftime('%Y'),
+                    })
+        
+    def install_all(self, cr, uid, ids, context=None):
+        """
+        Main wizard step. Set all installable modules to install
+        and actually install them.
+        """
+        module_obj = self.pool.get('ir.module.module')
+        module_ids = module_obj.search(
+            cr, uid, [('state', 'not in', ['installed', 'uninstallable', 'unknown'])])
+        if module_ids:
+            module_obj.write(
+                cr, uid, module_ids, {'state': 'to install'})
+            cr.commit()
+            _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+            self.write(cr, uid, ids, {'state': 'ready'})
+        return True
+
+install_all_wizard()
+

=== added file 'openerp/addons/openupgrade_records/model/openupgrade_record.py'
--- openerp/addons/openupgrade_records/model/openupgrade_record.py	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/openupgrade_record.py	2014-04-08 14:03:57 +0000
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    This module Copyright (C) 2012 OpenUpgrade community
+#    https://launchpad.net/~openupgrade-committers
+#
+#    Contributors:
+#    Therp BV <http://therp.nl>
+#
+#    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 openerp.osv import osv, fields
+
+# Cannot use forward references in 6.0
+class openupgrade_record(osv.osv):
+    _name = 'openupgrade.record'
+openupgrade_record()
+
+class openupgrade_attribute(osv.osv):
+    _name = 'openupgrade.attribute'
+    _rec_name = 'name'
+    _columns = {
+        'name': fields.char(
+            'Name', size=24,
+            readonly=True,
+            ),
+        'value': fields.char(
+            'Value',
+            size=4096,
+            readonly=True,
+            ),
+        'record_id': fields.many2one(
+            'openupgrade.record', ondelete='CASCADE',
+            readonly=True,
+            ),
+        }
+openupgrade_attribute()
+
+class openupgrade_record(osv.osv):
+    _inherit = 'openupgrade.record'
+
+    _columns = {
+        'name': fields.char('Name', size=256, readonly=True),
+        'module': fields.char('Module', size=128, readonly=True),
+        'model': fields.char('Model', size=128, readonly=True),
+        'field': fields.char('Field', size=128, readonly=True),
+        'mode': fields.selection(
+            [('create', 'Create'), ('modify', 'Modify')],
+            'Mode',
+            help='Set to Create if a field is newly created '
+            'in this module. If this module modifies an attribute of an '
+            'exting field, set to Modify.',
+            readonly=True,
+             ),
+        'type': fields.selection(
+            [('field', 'Field'), ('xmlid', 'XML ID')],
+            'Type',
+            readonly=True,
+            ),
+        'attribute_ids': fields.one2many(
+            'openupgrade.attribute', 'record_id', 'Attributes',
+            readonly=True,
+            ),
+        }
+    def field_dump(self, cr, uid, context=None):
+        keys = [
+            'module',
+            'mode',
+            'model',
+            'field',
+            'type',
+            'isfunction',
+            'relation',
+            'required',
+            'selection_keys',
+            'req_default',
+            'inherits',
+            ]
+
+        template = dict([(x, False) for x in keys])
+        ids = self.search(cr, uid, [('type', '=', 'field')], context=context)
+        records = self.browse(cr, uid, ids, context=context)
+        data = []
+        for record in records:
+            repr = template.copy()
+            repr.update({
+                    'module': record.module,
+                    'model': record.model,
+                    'field': record.field,
+                    'mode': record.mode,
+                    })
+            repr.update(
+                dict([(x.name, x.value) for x in record.attribute_ids]))
+            data.append(repr)
+        return data
+
+openupgrade_record()

=== added directory 'openerp/addons/openupgrade_records/security'
=== added file 'openerp/addons/openupgrade_records/security/ir.model.access.csv'
--- openerp/addons/openupgrade_records/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/security/ir.model.access.csv	2014-04-08 14:03:57 +0000
@@ -0,0 +1,3 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+"access_openupgrade_record","openupgrade.record all","model_openupgrade_record",,1,0,0,0
+"access_openupgrade_attribute","openupgrade.attribute all","model_openupgrade_attribute",,1,0,0,0

=== added directory 'openerp/addons/openupgrade_records/view'
=== added file 'openerp/addons/openupgrade_records/view/analysis_wizard.xml'
--- openerp/addons/openupgrade_records/view/analysis_wizard.xml	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/analysis_wizard.xml	2014-04-08 14:03:57 +0000
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <record id="view_openupgrade_analysis_wizard_form" model="ir.ui.view">
+            <field name="name">view.openupgrade.analysis_wizard.form</field>
+            <field name="model">openupgrade.analysis.wizard</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="OpenUpgrade Analysis Wizard">
+                    <field name="server_config" readonly="1"/>
+                    <field name="state"/>
+                    <field name="log" colspan="4"
+                           attrs="{'invisible': [('state', '!=', 'ready')]}"/>
+                    <field name="write"                           
+                           attrs="{'readonly': [('state', '!=', 'init')]}"/>
+                    <button icon="gtk-close"
+                            special="cancel"
+                            string="Close"
+                            />
+                    <button icon="gtk-ok"
+                            string="Create" 
+                            name="get_communication"
+                            type="object"
+                            states="init"
+                            />
+                </form>
+            </field>
+        </record>
+    </data>
+</openerp>

=== added file 'openerp/addons/openupgrade_records/view/comparison_config.xml'
--- openerp/addons/openupgrade_records/view/comparison_config.xml	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/comparison_config.xml	2014-04-08 14:03:57 +0000
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <record id="view_openupgrade_comparison_config_tree" model="ir.ui.view">
+            <field name="name">view.openupgrade.comparison_config.tree</field>
+            <field name="model">openupgrade.comparison.config</field>
+            <field name="type">tree</field>
+            <field name="arch" type="xml">
+                <tree string="OpenUpgrade Comparison Config">
+                    <field name="name" select="1"/>
+                    <field name="protocol"/>
+                    <field name="server" select="1"/>
+                    <field name="port" select="1"/>
+                    <field name="database" select="1"/>
+                </tree>
+            </field>
+        </record>
+        <record id="view_openupgrade_comparison_config_form" model="ir.ui.view">
+            <field name="name">view.openupgrade.comparison_config.form</field>
+            <field name="model">openupgrade.comparison.config</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="OpenUpgrade Comparison Config">
+                    <field name="name"/>
+                    <field name="protocol"/>
+                    <field name="server"/>
+                    <field name="port"/>
+                    <field name="database"/>
+                    <field name="username"/>
+                    <field name="password" password="1"/>
+                    <button
+                        name="test_connection" 
+                        string="Test Connection"
+                        type="object" icon="gtk-network"
+                        colspan="2"
+                        />
+                    <newline/>
+                    <button 
+                        name="analyze"
+                        string="Perform Analysis"
+                        type="object" icon="gtk-execute"
+                        colspan="2"
+                        />
+                    <separator string="Last log" colspan="4"/>
+                    <field name="last_log" nolabel="1" colspan="4"/>
+                </form>
+            </field>
+        </record>
+        <record id="action_openupgrade_comparison_config_tree" model="ir.actions.act_window">
+            <field name="name">OpenUpgrade Comparison Configs</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">openupgrade.comparison.config</field>
+            <field name="view_type">form</field>
+        </record>
+        <menuitem
+            action="action_openupgrade_comparison_config_tree"
+            id="menu_openupgrade_comparison_config"
+            name="Comparison Configurations"
+            parent="menu_openupgrade"
+            />
+    </data>
+</openerp>

=== added file 'openerp/addons/openupgrade_records/view/generate_records_wizard.xml'
--- openerp/addons/openupgrade_records/view/generate_records_wizard.xml	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/generate_records_wizard.xml	2014-04-08 14:03:57 +0000
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <record id="view_openupgrade_generate_records_wizard_form" model="ir.ui.view">
+            <field name="name">view.openupgrade.generate_records_wizard.form</field>
+            <field name="model">openupgrade.generate.records.wizard</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="OpenUpgrade Generate Records Wizard">
+                    <group states="init" colspan="4">
+                        <label string="This will reinitialize all the modules installed on this database. Do not continue if you use this database in production."
+                               />
+                        <button icon="gtk-close"
+                                special="cancel"
+                                string="Cancel"
+                                />
+                        <button icon="gtk-ok"
+                                string="Continue" 
+                                name="generate"
+                                type="object"
+                                />
+                    </group>
+                    <group states="ready" colspan="4">
+                        <label string="Modules initialized and records created"
+                               />
+                        <field name="state" invisible="1"/>
+                        <button icon="gtk-close"
+                                special="cancel"
+                                string="Close"
+                            />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <record id="action_generate_records" model="ir.actions.act_window">
+            <field name="name">Generate Records</field>
+	    <field name="type">ir.actions.act_window</field>
+            <field name="res_model">openupgrade.generate.records.wizard</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">form,tree</field>
+            <field name="target">new</field>
+        </record>
+
+        <menuitem name="Generate Records"
+            id="menu_openupgrade_generate_records"
+            parent="menu_openupgrade"
+            action="action_generate_records"
+            sequence="15"/>
+
+    </data>
+</openerp>

=== added file 'openerp/addons/openupgrade_records/view/install_all_wizard.xml'
--- openerp/addons/openupgrade_records/view/install_all_wizard.xml	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/install_all_wizard.xml	2014-04-08 14:03:57 +0000
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <record id="view_openupgrade_install_all_wizard_form" model="ir.ui.view">
+            <field name="name">view.openupgrade.install_all_wizard.form</field>
+            <field name="model">openupgrade.install.all.wizard</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="OpenUpgrade Install All Modules Wizard">
+                    <group states="init" colspan="4">
+                        <label string="This will install all modules on the database. Do not continue if you use this database in production." colspan="4"
+                               />
+                        <field name="to_install"/>
+                        <newline/>
+                        <button icon="gtk-close"
+                                special="cancel"
+                                string="Cancel"
+                                />
+                        <button icon="gtk-ok"
+                                string="Continue" 
+                                name="install_all"
+                                type="object"
+                                />
+                    </group>
+                    <group states="ready" colspan="4">
+                        <label string="Modules installed"
+                               />
+                        <field name="state" invisible="1"/>
+                        <button icon="gtk-close"
+                                special="cancel"
+                                string="Close"
+                            />
+                    </group>
+                </form>
+            </field>
+        </record>
+
+        <record id="action_install_all" model="ir.actions.act_window">
+            <field name="name">Install All Modules</field>
+	    <field name="type">ir.actions.act_window</field>
+            <field name="res_model">openupgrade.install.all.wizard</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">form,tree</field>
+            <field name="target">new</field>
+        </record>
+
+        <menuitem name="Install All Modules"
+            id="menu_openupgrade_install_all"
+            parent="menu_openupgrade"
+            action="action_install_all"
+            sequence="14"/>
+
+    </data>
+</openerp>

=== added file 'openerp/addons/openupgrade_records/view/openupgrade_record.xml'
--- openerp/addons/openupgrade_records/view/openupgrade_record.xml	1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/openupgrade_record.xml	2014-04-08 14:03:57 +0000
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        <!-- Top level menu under 'Database structure' -->
+        <menuitem
+            id="menu_openupgrade"
+            name="OpenUpgrade Development"
+            parent="base.menu_administration"
+            sequence="99"
+            />
+        <record id="view_openupgrade_record_tree" model="ir.ui.view">
+            <field name="name">view.openupgrade.record.tree</field>
+            <field name="model">openupgrade.record</field>
+            <field name="type">tree</field>
+            <field name="arch" type="xml">
+                <tree string="OpenUpgrade Records">
+                    <field name="module" select="1"/>
+                    <field name="model" select="1"/>
+                    <field name="field" select="1"/>
+                    <field name="name" select="1"/>
+                    <field name="type" select="1"/>
+                    <field name="mode" select="1"/>
+                </tree>
+            </field>
+        </record>
+        <record id="view_openupgrade_record_form" model="ir.ui.view">
+            <field name="name">view.openupgrade.record.form</field>
+            <field name="model">openupgrade.record</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="OpenUpgrade Record">
+                    <field name="module" select="1"/>
+                    <field name="model" select="1"/>
+                    <field name="field" select="1"/>
+                    <field name="name" select="1"/>
+                    <field name="type" select="1"/>
+                    <field name="mode" select="1"/>
+                    <separator string="Attributes" colspan="4"/>
+                    <field name="attribute_ids" mode="tree,form" nolabel="1" colspan="4">
+                        <tree string="Attributes">
+                            <field name="name"/>
+                            <field name="value"/>
+                        </tree>
+                        <form string="Attribute">
+                            <field name="name"/>
+                            <field name="value"/>
+                        </form>
+                    </field>
+                </form>
+            </field>
+        </record>
+        <record id="action_openupgrade_record_tree" model="ir.actions.act_window">
+            <field name="name">OpenUpgrade Records</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">openupgrade.record</field>
+            <field name="view_type">form</field>
+        </record>
+        <menuitem
+            action="action_openupgrade_record_tree"
+            id="menu_openupgrade_records"
+            name="Records"
+            parent="menu_openupgrade"
+            />
+    </data>
+</openerp>


Follow ups