[MIG] base external dbsource

* Migration of base_external_dbsource to odoo 9.0

* Fixing test error

* Moving test from yaml to python

* Fixing pylint error in test class

* Placeholder added to connection string text zone

* improving test coverage
This commit is contained in:
Gervais Naoussi
2016-09-08 09:24:27 -04:00
committed by Pedro M. Baeza
parent faadee69aa
commit 7d935f2e94
11 changed files with 253 additions and 147 deletions

View File

@@ -0,0 +1,82 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
=========================
External Database Sources
=========================
This module allows you to define connections to foreign databases using ODBC,
Oracle Client or SQLAlchemy.
Installation
============
No installation required.
Configuration
=============
Database sources can be configured in Settings > Configuration -> Data sources.
Depending on the database, you need:
* to install unixodbc and python-pyodbc packages to use ODBC connections.
* to install FreeTDS driver (tdsodbc package) and configure it through ODBC toconnect to Microsoft SQL Server.
* to install and configure Oracle Instant Client and cx_Oracle python library to connect to Oracle.
Usage
=====
To use this module:
* Go to Settings > Database Structure > Database Sources
* Click on Create to enter the following information:
* Datasource name 
* Pasword
* Connector: Choose the database to which you want to connect
* Connection string : Specify how to connect to database
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/9.0 for server-tools
Known issues / Roadmap
======================
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback `here <https://github.com/OCA/
server-tools/issues/new?body=module:%20
base_external_dbsource%0Aversion:%20
9.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Credits
=======
Contributors
------------
* Daniel Reis <dreis.pt@hotmail.com>
* Maxime Chambreuil <maxime.chambreuil@savoirfairelinux.com>
* Gervais Naoussi <gervaisnaoussi@gmail.com>
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.

View File

@@ -19,6 +19,4 @@
# #
############################################################################## ##############################################################################
from . import base_external_dbsource from . import models
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -1,48 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## # Copyright <2011> <Daniel Reis, Maxime Chambreuil, Savoir-faire Linux>
# # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
# Daniel Reis, 2011
# Additional contributions by Maxime Chambreuil, Savoir-faire Linux
#
# 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': 'External Database Sources', 'name': 'External Database Sources',
'version': '8.0.1.3.0', 'version': '9.0.1.0.0',
'category': 'Tools', 'category': 'Tools',
'description': """ 'author': "Daniel Reis,Odoo Community Association (OCA)",
This module allows you to define connections to foreign databases using ODBC, 'website': 'https://github.com/OCA/server-tools',
Oracle Client or SQLAlchemy. 'license': 'AGPL-3',
Database sources can be configured in Settings > Configuration -> Data sources.
Depending on the database, you need:
* to install unixodbc and python-pyodbc packages to use ODBC connections.
* to install FreeTDS driver (tdsodbc package) and configure it through ODBC to
connect to Microsoft SQL Server.
* to install and configure Oracle Instant Client and cx_Oracle python library
to connect to Oracle.
Contributors
============
* Maxime Chambreuil <maxime.chambreuil@savoirfairelinux.com>
""",
'author': 'Daniel Reis',
'website': 'http://launchpad.net/addons-tko',
'images': [ 'images': [
'images/screenshot01.png', 'images/screenshot01.png',
], ],
@@ -50,14 +15,11 @@ Contributors
'base', 'base',
], ],
'data': [ 'data': [
'base_external_dbsource_view.xml', 'views/base_external_dbsource.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
], ],
'demo': [ 'demo': [
'base_external_dbsource_demo.xml', 'demo/base_external_dbsource.xml',
],
'test': [
'test/dbsource_connect.yml',
], ],
'installable': True, 'installable': True,
} }

View File

@@ -1,15 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="base.external.dbsource" id="demo_postgre">
<field name="name">PostgreSQL local</field>
<field name="conn_string">dbname='postgres' password=%s</field>
<field name="password">postgresql</field>
<field name="connector">postgresql</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<odoo>
<record model="base.external.dbsource" id="demo_postgre">
<field name="name">PostgreSQL local</field>
<field name="conn_string">dbname='postgres' password=%s</field>
<field name="password">postgresql</field>
<field name="connector">postgresql</field>
</record>
</odoo>

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Daniel Reis
# 2011
#
# 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 base_external_dbsource

View File

@@ -21,8 +21,9 @@
import os import os
import logging import logging
from openerp.osv import orm, fields import psycopg2
from openerp.tools.translate import _ from openerp import models, fields, api, _
from openerp.exceptions import Warning as UserError
import openerp.tools as tools import openerp.tools as tools
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -62,58 +63,60 @@ except:
_logger.info('Oracle libraries not available. Please install "cx_Oracle"\ _logger.info('Oracle libraries not available. Please install "cx_Oracle"\
python package.') python package.')
import psycopg2
CONNECTORS.append(('postgresql', 'PostgreSQL')) CONNECTORS.append(('postgresql', 'PostgreSQL'))
class base_external_dbsource(orm.Model): class BaseExternalDbsource(models.Model):
_name = "base.external.dbsource" _name = "base.external.dbsource"
_description = 'External Database Sources' _description = 'External Database Sources'
_columns = { name = fields.Char('Datasource name', required=True, size=64)
'name': fields.char('Datasource name', required=True, size=64), conn_string = fields.Text('Connection string', help="""
'conn_string': fields.text('Connection string', help=""" Sample connection strings:
Sample connection strings: - Microsoft SQL Server:
- Microsoft SQL Server: mssql+pymssql://username:%s@server:port/dbname?charset=utf8
mssql+pymssql://username:%s@server:port/dbname?charset=utf8 - MySQL: mysql://user:%s@server:port/dbname
- MySQL: mysql://user:%s@server:port/dbname - ODBC: DRIVER={FreeTDS};SERVER=server.address;Database=mydb;UID=sa
- ODBC: DRIVER={FreeTDS};SERVER=server.address;Database=mydb;UID=sa - ORACLE: username/%s@//server.address:port/instance
- ORACLE: username/%s@//server.address:port/instance - PostgreSQL:
- PostgreSQL: dbname='template1' user='dbuser' host='localhost' port='5432' \
dbname='template1' user='dbuser' host='localhost' port='5432' password=%s password=%s
- SQLite: sqlite:///test.db - SQLite: sqlite:///test.db
"""), """)
'password': fields.char('Password', size=40), password = fields.Char('Password', size=40)
'connector': fields.selection(CONNECTORS, 'Connector', connector = fields.Selection(CONNECTORS, 'Connector', required=True,
required=True, help="If a connector is missing from the\
help="If a connector is missing from the\
list, check the server log to confirm\ list, check the server log to confirm\
that the required components were\ that the required components were\
detected."), detected.")
}
def conn_open(self, cr, uid, id1): @api.multi
def conn_open(self):
"""The connection is open here."""
self.ensure_one()
# Get dbsource record # Get dbsource record
data = self.browse(cr, uid, id1) # data = self.browse(cr, uid, id1)
# Build the full connection string # Build the full connection string
connStr = data.conn_string connStr = self.conn_string
if data.password: if self.password:
if '%s' not in data.conn_string: if '%s' not in self.conn_string:
connStr += ';PWD=%s' connStr += ';PWD=%s'
connStr = connStr % data.password connStr = connStr % self.password
# Try to connect # Try to connect
if data.connector == 'cx_Oracle': if self.connector == 'cx_Oracle':
os.environ['NLS_LANG'] = 'AMERICAN_AMERICA.UTF8' os.environ['NLS_LANG'] = 'AMERICAN_AMERICA.UTF8'
conn = cx_Oracle.connect(connStr) conn = cx_Oracle.connect(connStr)
elif data.connector == 'pyodbc': elif self.connector == 'pyodbc':
conn = pyodbc.connect(connStr) conn = pyodbc.connect(connStr)
elif data.connector in ('sqlite', 'mysql', 'mssql'): elif self.connector in ('sqlite', 'mysql', 'mssql'):
conn = sqlalchemy.create_engine(connStr).connect() conn = sqlalchemy.create_engine(connStr).connect()
elif data.connector == 'postgresql': elif self.connector == 'postgresql':
conn = psycopg2.connect(connStr) conn = psycopg2.connect(connStr)
return conn return conn
def execute(self, cr, uid, ids, sqlquery, sqlparams=None, metadata=False, @api.multi
def execute(self, sqlquery, sqlparams=None, metadata=False,
context=None): context=None):
"""Executes SQL and returns a list of rows. """Executes SQL and returns a list of rows.
@@ -131,10 +134,10 @@ Sample connection strings:
{ 'cols': [ 'col_a', 'col_b', ...] { 'cols': [ 'col_a', 'col_b', ...]
, 'rows': [ (a0, b0, ...), (a1, b1, ...), ...] } , 'rows': [ (a0, b0, ...), (a1, b1, ...), ...] }
""" """
data = self.browse(cr, uid, ids) # data = self.browse(cr, uid, ids)
rows, cols = list(), list() rows, cols = list(), list()
for obj in data: for obj in self:
conn = self.conn_open(cr, uid, obj.id) conn = obj.conn_open()
if obj.connector in ["sqlite", "mysql", "mssql"]: if obj.connector in ["sqlite", "mysql", "mssql"]:
# using sqlalchemy # using sqlalchemy
cur = conn.execute(sqlquery, sqlparams) cur = conn.execute(sqlquery, sqlparams)
@@ -154,15 +157,17 @@ Sample connection strings:
else: else:
return rows return rows
def connection_test(self, cr, uid, ids, context=None): @api.multi
for obj in self.browse(cr, uid, ids, context): def connection_test(self):
"""Test of connection."""
for obj in self:
conn = False conn = False
try: try:
conn = self.conn_open(cr, uid, obj.id) conn = self.conn_open()
except Exception as e: except Exception as e:
raise orm.except_orm(_("Connection test failed!"), raise UserError(_("Connection test failed: \
_("Here is what we got instead:\n %s") Here is what we got instead:\n %s") % tools.ustr(e))
% tools.ustr(e))
finally: finally:
try: try:
if conn: if conn:
@@ -171,5 +176,5 @@ Sample connection strings:
# ignored, just a consequence of the previous exception # ignored, just a consequence of the previous exception
pass pass
# TODO: if OK a (wizard) message box should be displayed # TODO: if OK a (wizard) message box should be displayed
raise orm.except_orm(_("Connection test succeeded!"), raise UserError(_("Connection test succeeded: \
_("Everything seems properly set up!")) Everything seems properly set up!"))

View File

@@ -1,9 +0,0 @@
-
Connect to local Postgres.
-
!python {model: base.external.dbsource}: |
from openerp.osv.orm import except_orm
try:
self.connection_test(cr, uid, [ref("demo_postgre")])
except except_orm as e:
assert e.value == u'Everything seems properly set up!'

View File

@@ -0,0 +1,3 @@
# -*- encoding: utf-8 -*-
from . import test_create_dbsource

View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
from openerp.exceptions import Warning as UserError
from openerp.tests import common
import logging
class TestCreateDbsource(common.TransactionCase):
"""Test class for base_external_dbsource."""
def test_create_dbsource(self):
"""source creation should succeed."""
dbsource = self.env.ref('base_external_dbsource.demo_postgre')
try:
dbsource.connection_test()
except UserError as e:
logging.warning("Log = " + str(e))
self.assertTrue(u'Everything seems properly set up!' in str(e))
def test_create_dbsource_failed(self):
"""source creation without connection string should failed."""
dbsource = self.env.ref('base_external_dbsource.demo_postgre')
# Connection without connection_string
dbsource.conn_string = ""
try:
dbsource.connection_test()
except UserError as e:
logging.warning("Log = " + str(e))
self.assertTrue(u'Here is what we got instead:' in str(e))
def test_create_dbsource_without_connector_failed(self):
"""source creation with other connector should failed."""
dbsource = self.env.ref('base_external_dbsource.demo_postgre')
# Connection to mysql
try:
dbsource.connector = "mysql"
dbsource.connection_test()
except ValueError as e:
logging.warning("Log = " + str(e))
self.assertTrue(u'Wrong value for' in str(e))
# Connection to mysql
try:
dbsource.connector = "pyodbc"
dbsource.connection_test()
except ValueError as e:
logging.warning("Log = " + str(e))
self.assertTrue(u'Wrong value for' in str(e))
# Connection to oracle
try:
dbsource.connector = "cx_Oracle"
dbsource.connection_test()
except ValueError as e:
logging.warning("Log = " + str(e))
self.assertTrue(u'Wrong value for' in str(e))

View File

@@ -1,8 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<openerp> <odoo>
<data> <!-- DBSource -->
<!-- DBSource -->
<record model="ir.ui.view" id="view_dbsource_tree"> <record model="ir.ui.view" id="view_dbsource_tree">
<field name="name">base.external.dbsource.tree</field> <field name="name">base.external.dbsource.tree</field>
@@ -11,32 +9,32 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="External DB Sources"> <tree string="External DB Sources">
<field name="name"/> <field name="name"/>
<field name="connector"/> <field name="connector"/>
<field name="conn_string"/> <field name="conn_string"/>
</tree> </tree>
</field> </field>
</record> </record>
<record model="ir.ui.view" id="view_dbsource_form"> <record model="ir.ui.view" id="view_dbsource_form">
<field name="name">base.external.dbsource.form</field> <field name="name">base.external.dbsource.form</field>
<field name="model">base.external.dbsource</field> <field name="model">base.external.dbsource</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="External DB Source" version="7.0"> <form string="External DB Source">
<sheet> <sheet>
<group>
<group> <group>
<field name="name"/> <group>
<field name="name"/>
</group>
<group>
<field name="password" password="True"/>
</group>
</group> </group>
<group> <group col="1">
<field name="password" password="True"/> <field name="connector"/>
<field name="conn_string" placeholder="Please check the tooltip for connection string examples"/>
<button name="connection_test" string="Test Connection" type="object" icon="gtk-network"/>
</group> </group>
</group> </sheet>
<group col="1">
<field name="connector"/>
<field name="conn_string"/>
<button name="connection_test" string="Test Connection" type="object" icon="gtk-network"/>
</group>
</sheet>
</form> </form>
</field> </field>
</record> </record>
@@ -49,12 +47,5 @@
<field name="view_id" ref="view_dbsource_tree"/> <field name="view_id" ref="view_dbsource_tree"/>
</record> </record>
<menuitem name="Database Sources" <menuitem name="Database Sources" id="menu_dbsource" parent="base.next_id_9" action="action_dbsource"/>
id="menu_dbsource" </odoo>
parent="base.next_id_9"
action="action_dbsource"/>
</data>
</openerp>