Hello community,

here is the log from the commit of package hanadb_exporter for openSUSE:Factory 
checked in at 2019-12-11 12:13:39
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/hanadb_exporter (Old)
 and      /work/SRC/openSUSE:Factory/.hanadb_exporter.new.4691 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "hanadb_exporter"

Wed Dec 11 12:13:39 2019 rev:6 rq:755692 version:0.5.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/hanadb_exporter/hanadb_exporter.changes  
2019-11-13 13:29:16.511757138 +0100
+++ 
/work/SRC/openSUSE:Factory/.hanadb_exporter.new.4691/hanadb_exporter.changes    
    2019-12-11 12:13:57.964531091 +0100
@@ -1,0 +2,14 @@
+Tue Dec 10 09:17:42 UTC 2019 - Xabier Arbulu <[email protected]>
+
+- Version 0.5.3 Improve metrics collection if some of the rows
+result is None. Before, if any result was None the result was
+not exported. Now, only metrics with None value are ommitted 
+
+-------------------------------------------------------------------
+Mon Dec  9 08:21:18 UTC 2019 - Xabier Arbulu <[email protected]>
+
+- Version 0.5.2 Add the option to use the hanadb_exporter with
+the stored user key. This gives the option to avoid the plain
+user/password strings usage. 
+
+-------------------------------------------------------------------

Old:
----
  hanadb_exporter-0.5.1.tar.gz

New:
----
  hanadb_exporter-0.5.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ hanadb_exporter.spec ++++++
--- /var/tmp/diff_new_pack.MXYiDI/_old  2019-12-11 12:13:59.020530794 +0100
+++ /var/tmp/diff_new_pack.MXYiDI/_new  2019-12-11 12:13:59.024530793 +0100
@@ -26,7 +26,7 @@
 %endif
 
 Name:           hanadb_exporter
-Version:        0.5.1
+Version:        0.5.3
 Release:        0
 Summary:        SAP HANA database metrics exporter
 License:        Apache-2.0

++++++ hanadb_exporter-0.5.1.tar.gz -> hanadb_exporter-0.5.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hanadb_exporter-0.5.1/README.md 
new/hanadb_exporter-0.5.3/README.md
--- old/hanadb_exporter-0.5.1/README.md 2019-11-06 12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/README.md 2019-12-10 12:20:40.946509434 +0100
@@ -51,9 +51,9 @@
 zypper in python3-PyHDB
 ```
 
-## Configuring and running the exporter
+## Configuring the exporter
 
-1. Create the `config.json` configuration file.
+Create the `config.json` configuration file.
 An example of `config.json` available in 
[config.json.example](config.json.example). Here the most
 important items in the configuration file:
   - `exposition_port`: Port where the prometheus exporter will be exposed 
(8001 by default).
@@ -61,6 +61,7 @@
   - `timeout`: Timeout to connect to the database. After this time the app 
will fail (even in daemon mode).
   - `hana.host`: Address of the SAP HANA database.
   - `hana.port`: Port where the SAP HANA database is exposed.
+  - `hana.userkey`: Stored user key. This is the secure option if you don't 
want to have the password in the configuration file. The `userkey` and 
`user/password` are self exclusive being the first the default if both options 
are set.
   - `hana.user`: An existing user with access right to the SAP HANA database.
   - `hana.password`: Password of an existing user.
   - `logging.config_file`: Python logging system configuration file (by 
default WARN and ERROR level messages will be sent to the syslog)
@@ -70,14 +71,44 @@
 
 Using the default [configuration file](./logging_config.ini), it will redirect 
the logs to the file assigned in the [json configuration 
file](./config.json.example) and to the syslog (only logging level up to 
WARNING).
 
-2. Start the exporter by running the following command:
+### Using the stored user key
+
+This is the recommended option if we want to keep the database secure (for 
development environments the `user/password` with `SYSTEM` user can be used as 
it's faster to setup).
+To use the `userkey` option the `dbapi` must be installed (usually stored in 
`/hana/shared/PRD/hdbclient/hdbcli-N.N.N.tar.gz` and installable with pip3).
+It cannot be used from other different client (the key is stored in the client 
itself). This will raise the `hdbcli.dbapi.Error: (-10104, 'Invalid value for 
KEY')` error.
+For that a new stored user key must be created with the user that is running 
python. For that (please, notice that the `hdbclient` is the same as the 
`dbapi` python package):
+```
+/hana/shared/PRD/hdbclient/hdbuserstore set yourkey host:30013@SYSTEMDB 
hanadb_exporter pass
+```
+
+Some tips:
+- Set `SYSTEMDB` as default database, this way the exporter will know where to 
get the tenants data.
+- Don't use the stored user key created for the backup as this is created 
using the sidadm user.
+- The usage of a user with access only to the monitoring tables is recommended 
instead of using SYSTEM user.
+- If a user with monitoring role is used the user must exist in all the 
databases (SYSTEMDB+tenants).
+
+### Create a new user with monitoring role
+Run the next commands to create a user with moniroting roles (**the commands 
must be executed in all the databases**):
+```
+su - prdadm
+hdbsql -u SYSTEM -p pass -d SYSTEMDB #(PRD for the tenant in this example)
+CREATE USER HANADB_EXPORTER_USER PASSWORD MyExporterPassword NO 
FORCE_FIRST_PASSWORD_CHANGE;
+CREATE ROLE HANADB_EXPORTER_ROLE;
+GRANT MONITORING TO HANADB_EXPORTER_ROLE;
+GRANT HANADB_EXPORTER_ROLE TO HANADB_EXPORTER_USER;
+```
+
+
+## Running the exporter
+
+Start the exporter by running the following command:
 ```
 hanadb_exporter -c config.json -m metrics.json
 # Or
 python3 hanadb_exporter/main.py -c config.json -m metrics.json
 ```
 
-## Running as a daemon
+### Running as a daemon
 The hanadb_exporter can be executed using `systemd`. For that, the best option 
is to install the
 project using a rpm package. This can be done following the next steps (this 
example is for tumbleweed):
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hanadb_exporter-0.5.1/hanadb_exporter/__init__.py 
new/hanadb_exporter-0.5.3/hanadb_exporter/__init__.py
--- old/hanadb_exporter-0.5.1/hanadb_exporter/__init__.py       2019-11-06 
12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/hanadb_exporter/__init__.py       2019-12-10 
12:20:40.946509434 +0100
@@ -8,4 +8,4 @@
 :since: 2019-05-09
 """
 
-__version__ = "0.5.0"
+__version__ = "0.5.3"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hanadb_exporter-0.5.1/hanadb_exporter/db_manager.py 
new/hanadb_exporter-0.5.3/hanadb_exporter/db_manager.py
--- old/hanadb_exporter-0.5.1/hanadb_exporter/db_manager.py     2019-11-06 
12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/hanadb_exporter/db_manager.py     2019-12-10 
12:20:40.946509434 +0100
@@ -17,14 +17,20 @@
 RECONNECTION_INTERVAL = 15
 
 
+class UserKeyNotSupportedError(ValueError):
+    """
+    User key not supported error
+    """
+
+
 class DatabaseManager(object):
     """
     Manage the connection to a multi container HANA system
     """
 
     TENANT_DATA_QUERY =\
-"""SELECT SQL_PORT FROM SYS_DATABASES.M_SERVICES
-WHERE (SERVICE_NAME='indexserver' and COORDINATOR_TYPE= 'MASTER')"""
+"""SELECT DATABASE_NAME,SQL_PORT FROM SYS_DATABASES.M_SERVICES
+WHERE COORDINATOR_TYPE='MASTER'"""
 
     def __init__(self):
         self._logger = logging.getLogger(__name__)
@@ -38,24 +44,50 @@
         data = self._system_db_connector.query(self.TENANT_DATA_QUERY)
         formatted_data = utils.format_query_result(data)
         for tenant_data in formatted_data:
-            yield int(tenant_data['SQL_PORT'])
+            if tenant_data['DATABASE_NAME'] != 'SYSTEMDB':
+                yield tenant_data['DATABASE_NAME'], 
int(tenant_data['SQL_PORT'])
 
-    def _connect_tenants(self, host, user, password):
+    def _connect_tenants(self, host, connection_data):
         """
         Connect to the tenants
 
         Args:
             host (str): Host of the HANA database
-            user (str): System database user name (SYSTEM usually)
-            password (str): System database user password
+            connection_data (dict): Data retrieved from _get_connection_data
         """
-        for tenant_port in self._get_tenants_port():
+        for database, tenant_port in self._get_tenants_port():
             conn = hdb_connector.HdbConnector()
+            # If userkey is used database name must be added to connect to 
tenants
+            if connection_data.get('userkey'):
+                connection_data['databaseName'] = database
             conn.connect(
-                host, tenant_port, user=user, password=password, 
RECONNECT='FALSE')
+                host, tenant_port, **connection_data)
             self._db_connectors.append(conn)
 
-    def start(self, host, port, user, password, multi_tenant=True, 
timeout=600):
+    def _get_connection_data(self, userkey, user, password):
+        """
+        Check that provided user data is valid. user/password pair or userkey 
must be provided
+        """
+        if userkey:
+            if hdb_connector.API == 'pyhdb':
+                raise UserKeyNotSupportedError(
+                    'userkey usage is not supported with pyhdb connector, 
hdbcli must be installed')
+            self._logger.info('stored user key %s will be used to connect to 
the database', userkey)
+            if user or password:
+                self._logger.warn(
+                    'userkey will be used to create the connection. 
user/password are omitted')
+        elif user and password:
+            self._logger.info('user/password combination will be used to 
connect to the databse')
+        else:
+            raise ValueError(
+                'Provided user data is not valid. userkey or user/password 
pair must be provided')
+
+        return {'userkey': userkey,
+                'user': user,
+                'password': password,
+                'RECONNECT': 'FALSE'}
+
+    def start(self, host, port, **kwargs):
         """
         Start de database manager. This will open a connection with the System 
database and
         retrieve the current environemtn tenant databases data
@@ -63,30 +95,39 @@
         Args:
             host (str): Host of the HANA database
             port (int): Port of the System database (3XX13 when XX is the 
instance number)
+            userkey (str): User stored key
             user (str): System database user name (SYSTEM usually)
             password (str): System database user password
             multi_tenant (bool): Connect to all tenants checking the data in 
the System database
             timeout (int, opt): Timeout in seconds to connect to the System 
database
         """
+        connection_data = self._get_connection_data(
+            kwargs.get('userkey', None), kwargs.get('user', ''), 
kwargs.get('password', ''))
         current_time = time.time()
-        timeout = current_time + timeout
+        timeout = current_time + kwargs.get('timeout', 600)
         while current_time <= timeout:
             try:
-                self._system_db_connector.connect(
-                    host, port, user=user, password=password, 
RECONNECT='FALSE')
+                # parameters are passed using kwargs to the connect method
+                # pyhdb only uses 'user' and `password`
+                # dbapi uses 'user', 'password', 'userkey' and other optional 
params
+                self._system_db_connector.connect(host, port, 
**connection_data)
                 self._db_connectors.append(self._system_db_connector)
                 break
             except hdb_connector.connectors.base_connector.ConnectionError as 
err:
                 self._logger.error(
                     'the connection to the system database failed. error 
message: %s', str(err))
+                # This conditions is used to stop the exporter if the provided 
userkey is not valid
+                if 'Invalid value for KEY' in str(err):
+                    raise 
hdb_connector.connectors.base_connector.ConnectionError(
+                        'provided userkey is not valid. Check if dbapi is 
installed correctly')
                 time.sleep(RECONNECTION_INTERVAL)
                 current_time = time.time()
         else:
             raise hdb_connector.connectors.base_connector.ConnectionError(
                 'timeout reached connecting the System database')
 
-        if multi_tenant:
-            self._connect_tenants(host, user, password)
+        if kwargs.get('multi_tenant', True):
+            self._connect_tenants(host, connection_data)
 
     def get_connectors(self):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hanadb_exporter-0.5.1/hanadb_exporter/main.py 
new/hanadb_exporter-0.5.3/hanadb_exporter/main.py
--- old/hanadb_exporter-0.5.1/hanadb_exporter/main.py   2019-11-06 
12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/hanadb_exporter/main.py   2019-12-10 
12:20:40.946509434 +0100
@@ -92,7 +92,9 @@
         dbs = db_manager.DatabaseManager()
         dbs.start(
             hana_config['host'], hana_config.get('port', 30013),
-            hana_config['user'], hana_config['password'],
+            user=hana_config.get('user', ''),
+            password=hana_config.get('password', ''),
+            userkey=hana_config.get('userkey', None),
             multi_tenant=config.get('multi_tenant', True),
             timeout=config.get('timeout', 600))
     except KeyError as err:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hanadb_exporter-0.5.1/hanadb_exporter/prometheus_exporter.py 
new/hanadb_exporter-0.5.3/hanadb_exporter/prometheus_exporter.py
--- old/hanadb_exporter-0.5.1/hanadb_exporter/prometheus_exporter.py    
2019-11-06 12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/hanadb_exporter/prometheus_exporter.py    
2019-12-10 12:20:40.946509434 +0100
@@ -111,16 +111,18 @@
                     if column_name.lower() == metric.value.lower():
                         metric_value = column_value
             if metric_value is None:
-                raise ValueError(
-                    'Specified value in metrics.json for metric'
-                    ' "{}": ({}) not found in the query result'.format(
-                        metric.name, metric.value))
+                self._logger.warn(
+                    'Specified value in metrics.json for metric "%s": (%s) not 
found or it is '\
+                    'invalid (None) in the query result',
+                    metric.name, metric.value)
+                continue
             elif len(labels) != len(metric.labels):
                 # Log when a label(s) specified in metrics.json is not found 
in the query result
-                raise ValueError(
-                    'One or more label(s) specified in metrics.json'
-                    ' for metric: "{}" is not found in the the query 
result'.format(
-                        metric.name))
+                self._logger.warn(
+                    'One or more label(s) specified in metrics.json '
+                    'for metric "%s" that are not found in the query result',
+                    metric.name)
+                continue
             else:
                 # Add sid, insnr and database_name labels
                 combined_labels = self.metadata_labels + labels
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hanadb_exporter-0.5.1/hanadb_exporter.changes 
new/hanadb_exporter-0.5.3/hanadb_exporter.changes
--- old/hanadb_exporter-0.5.1/hanadb_exporter.changes   2019-11-06 
12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/hanadb_exporter.changes   2019-12-10 
12:20:40.950507434 +0100
@@ -1,4 +1,18 @@
 -------------------------------------------------------------------
+Tue Dec 10 09:17:42 UTC 2019 - Xabier Arbulu <[email protected]>
+
+- Version 0.5.3 Improve metrics collection if some of the rows
+result is None. Before, if any result was None the result was
+not exported. Now, only metrics with None value are ommitted 
+
+-------------------------------------------------------------------
+Mon Dec  9 08:21:18 UTC 2019 - Xabier Arbulu <[email protected]>
+
+- Version 0.5.2 Add the option to use the hanadb_exporter with
+the stored user key. This gives the option to avoid the plain
+user/password strings usage. 
+
+-------------------------------------------------------------------
 Wed Nov 6 12:48:03 UTC 2019 - Diego Akechi <[email protected]>
 
 - Version 0.5.1 Add the SAP HANA current alerts rating metric.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hanadb_exporter-0.5.1/hanadb_exporter.spec 
new/hanadb_exporter-0.5.3/hanadb_exporter.spec
--- old/hanadb_exporter-0.5.1/hanadb_exporter.spec      2019-11-06 
12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/hanadb_exporter.spec      2019-12-10 
12:20:40.950507434 +0100
@@ -26,7 +26,7 @@
 %endif
 
 Name:           hanadb_exporter
-Version:        0.5.1
+Version:        0.5.3
 Release:        0
 Summary:        SAP HANA database metrics exporter
 License:        Apache-2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hanadb_exporter-0.5.1/tests/db_manager_test.py 
new/hanadb_exporter-0.5.3/tests/db_manager_test.py
--- old/hanadb_exporter-0.5.1/tests/db_manager_test.py  2019-11-06 
12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/tests/db_manager_test.py  2019-12-10 
12:20:40.950507434 +0100
@@ -44,15 +44,21 @@
         self._db_manager._system_db_connector = mock.Mock()
         self._db_manager._system_db_connector.query.return_value = 'result'
         ports = ['30040', '30041']
-        mock_format_query.return_value = [{'SQL_PORT': ports[0]}, {'SQL_PORT': 
ports[1]}]
-
-        for i, port in enumerate(self._db_manager._get_tenants_port()):
-            assert port == int(ports[i])
+        dbs = ['PRD', 'QAS']
+        mock_format_query.return_value = [
+            {'DATABASE_NAME': dbs[0], 'SQL_PORT': ports[0]},
+            {'DATABASE_NAME': dbs[1], 'SQL_PORT': ports[1]},
+            {'DATABASE_NAME': 'SYSTEMDB', 'SQL_PORT': '30013'}]
+
+        for i, data in enumerate(self._db_manager._get_tenants_port()):
+            assert data[0] == dbs[i]
+            assert data[1] == int(ports[i])
 
     @mock.patch('hanadb_exporter.db_manager.hdb_connector.HdbConnector')
     def test_connect_tenants(self, mock_hdb):
 
-        self._db_manager._get_tenants_port = mock.Mock(return_value=[1, 2, 3])
+        self._db_manager._get_tenants_port = mock.Mock(return_value=[
+            ('db1', 1), ('db2', 2),('db3', 3)])
 
         mock_conn1 = mock.Mock()
         mock_conn2 = mock.Mock()
@@ -60,24 +66,121 @@
 
         mock_hdb.side_effect = [mock_conn1, mock_conn2, mock_conn3]
 
-        self._db_manager._connect_tenants('10.10.10.10', 'SYSTEM', 'pass')
+        connection_data = {'mock_data': 'data'}
+
+        self._db_manager._connect_tenants('10.10.10.10', connection_data)
 
         assert mock_hdb.call_count == 3
 
-        mock_conn1.connect.assert_called_once_with(
-            '10.10.10.10', 1, user='SYSTEM', password='pass', 
RECONNECT='FALSE')
-        mock_conn2.connect.assert_called_once_with(
-            '10.10.10.10', 2, user='SYSTEM', password='pass', 
RECONNECT='FALSE')
-        mock_conn3.connect.assert_called_once_with(
-            '10.10.10.10', 3, user='SYSTEM', password='pass', 
RECONNECT='FALSE')
+        mock_conn1.connect.assert_called_once_with('10.10.10.10', 1, 
**connection_data)
+        mock_conn2.connect.assert_called_once_with('10.10.10.10', 2, 
**connection_data)
+        mock_conn3.connect.assert_called_once_with('10.10.10.10', 3, 
**connection_data)
 
         assert self._db_manager._db_connectors == [mock_conn1, mock_conn2, 
mock_conn3]
 
+    @mock.patch('hanadb_exporter.db_manager.hdb_connector.HdbConnector')
+    def test_connect_tenants_userkey(self, mock_hdb):
+
+        self._db_manager._get_tenants_port = mock.Mock(return_value=[
+            ('db1', 1), ('db2', 2),('db3', 3)])
+
+        mock_conn1 = mock.Mock()
+        mock_conn2 = mock.Mock()
+        mock_conn3 = mock.Mock()
+
+        mock_hdb.side_effect = [mock_conn1, mock_conn2, mock_conn3]
+
+        connection_data = {'mock_data': 'data', 'userkey': 'userkey'}
+        updated_connection_data = [
+            {'mock_data': 'data', 'userkey': 'userkey', 'databaseName': 'db1'},
+            {'mock_data': 'data', 'userkey': 'userkey', 'databaseName': 'db2'},
+            {'mock_data': 'data', 'userkey': 'userkey', 'databaseName': 'db3'}
+        ]
+
+        self._db_manager._connect_tenants('10.10.10.10', connection_data)
+
+        assert mock_hdb.call_count == 3
+
+        mock_conn1.connect.assert_called_once_with('10.10.10.10', 1, 
**updated_connection_data[0])
+        mock_conn2.connect.assert_called_once_with('10.10.10.10', 2, 
**updated_connection_data[1])
+        mock_conn3.connect.assert_called_once_with('10.10.10.10', 3, 
**updated_connection_data[2])
+
+        assert self._db_manager._db_connectors == [mock_conn1, mock_conn2, 
mock_conn3]
+
+    def test_get_connection_data_invalid_data(self):
+
+        with pytest.raises(ValueError) as err:
+            self._db_manager._get_connection_data(None, '', '')
+        assert 'Provided user data is not valid. userkey or user/password pair 
must be provided' \
+            in str(err.value)
+
+        with pytest.raises(ValueError) as err:
+            self._db_manager._get_connection_data(None, 'user', '')
+        assert 'Provided user data is not valid. userkey or user/password pair 
must be provided' \
+            in str(err.value)
+
+        with pytest.raises(ValueError) as err:
+            self._db_manager._get_connection_data(None, '', 'pass')
+        assert 'Provided user data is not valid. userkey or user/password pair 
must be provided' \
+            in str(err.value)
+
+    @mock.patch('hanadb_exporter.db_manager.hdb_connector')
+    def test_get_connection_data_not_supported(self, mock_api):
+
+        mock_api.API = 'pyhdb'
+        with pytest.raises(db_manager.UserKeyNotSupportedError) as err:
+            self._db_manager._get_connection_data('userkey', '', '')
+        assert 'userkey usage is not supported with pyhdb connector, hdbcli 
must be installed' \
+            in str(err.value)
+
+    @mock.patch('hanadb_exporter.db_manager.hdb_connector')
+    @mock.patch('logging.Logger.warn')
+    @mock.patch('logging.Logger.info')
+    def test_get_connection_data_userkey(self, logger,logger_warn, mock_api):
+
+        mock_api.API = 'dbapi'
+        connection_data = self._db_manager._get_connection_data('userkey', '', 
'')
+        assert connection_data == {
+            'userkey': 'userkey', 'user': '', 'password': '', 'RECONNECT': 
'FALSE'}
+        logger.assert_called_once_with(
+            'stored user key %s will be used to connect to the database', 
'userkey')
+        assert logger_warn.call_count == 0
+
+    @mock.patch('hanadb_exporter.db_manager.hdb_connector')
+    @mock.patch('logging.Logger.warn')
+    @mock.patch('logging.Logger.info')
+    def test_get_connection_data_userkey_warn(self, logger,logger_warn, 
mock_api):
+
+        mock_api.API = 'dbapi'
+        connection_data = self._db_manager._get_connection_data('userkey', 
'user', '')
+        assert connection_data == {
+            'userkey': 'userkey', 'user': 'user', 'password': '', 'RECONNECT': 
'FALSE'}
+        logger.assert_called_once_with(
+            'stored user key %s will be used to connect to the database', 
'userkey')
+        logger_warn.assert_called_once_with(
+            'userkey will be used to create the connection. user/password are 
omitted')
+
+    @mock.patch('hanadb_exporter.db_manager.hdb_connector')
+    @mock.patch('logging.Logger.info')
+    def test_get_connection_data_pass(self, logger, mock_api):
+
+        mock_api.API = 'dbapi'
+        connection_data = self._db_manager._get_connection_data(None, 'user', 
'pass')
+        assert connection_data == {
+            'userkey': None, 'user': 'user', 'password': 'pass', 'RECONNECT': 
'FALSE'}
+        logger.assert_called_once_with(
+            'user/password combination will be used to connect to the databse')
+
     
@mock.patch('hanadb_exporter.db_manager.hdb_connector.connectors.base_connector')
     @mock.patch('logging.Logger.error')
     @mock.patch('time.sleep')
     @mock.patch('time.time')
     def test_start_timeout(self, mock_time, mock_sleep, mock_logger, 
mock_exception):
+
+        self._db_manager._get_connection_data = mock.Mock()
+        connection_data = {'mock_data': 'data'}
+        self._db_manager._get_connection_data.return_value = connection_data
+
         mock_exception.ConnectionError = Exception
         mock_time.side_effect = [0, 1, 2, 3]
         self._db_manager._system_db_connector = mock.Mock()
@@ -87,16 +190,17 @@
             mock_exception.ConnectionError('err'),
             mock_exception.ConnectionError('err')]
 
+        # Method under test
         with pytest.raises(mock_exception.ConnectionError) as err:
             self._db_manager.start(
-                '10.10.10.10', 30013, 'user', 'pass', multi_tenant=False, 
timeout=2)
+                '10.10.10.10', 30013, user='user', password='pass', 
multi_tenant=False, timeout=2)
 
         assert 'timeout reached connecting the System database' in 
str(err.value)
 
         self._db_manager._system_db_connector.connect.assert_has_calls([
-            mock.call('10.10.10.10', 30013, user='user', password='pass', 
RECONNECT='FALSE'),
-            mock.call('10.10.10.10', 30013, user='user', password='pass', 
RECONNECT='FALSE'),
-            mock.call('10.10.10.10', 30013, user='user', password='pass', 
RECONNECT='FALSE')
+            mock.call('10.10.10.10', 30013, **connection_data),
+            mock.call('10.10.10.10', 30013, **connection_data),
+            mock.call('10.10.10.10', 30013, **connection_data)
         ])
 
         mock_sleep.assert_has_calls([
@@ -115,9 +219,47 @@
 
     
@mock.patch('hanadb_exporter.db_manager.hdb_connector.connectors.base_connector')
     @mock.patch('logging.Logger.error')
+    @mock.patch('time.time')
+    def test_start_invalid_key(self, mock_time, mock_logger, mock_exception):
+
+        self._db_manager._get_connection_data = mock.Mock()
+        connection_data = {'mock_data': 'data'}
+        self._db_manager._get_connection_data.return_value = connection_data
+
+        mock_exception.ConnectionError = Exception
+        mock_time.side_effect = [0, 1, 2, 3]
+        self._db_manager._system_db_connector = mock.Mock()
+        self._db_manager._connect_tenants = mock.Mock()
+
+        self._db_manager._system_db_connector.connect.side_effect = [
+            mock_exception.ConnectionError('Error: Invalid value for KEY')]
+
+        # Method under test
+        with pytest.raises(mock_exception.ConnectionError) as err:
+            self._db_manager.start(
+                '10.10.10.10', 30013, user='user', password='pass', 
multi_tenant=False, timeout=2)
+
+        assert 'provided userkey is not valid. Check if dbapi is installed 
correctly' in str(err.value)
+
+        self._db_manager._system_db_connector.connect.assert_called_once_with(
+            '10.10.10.10', 30013, **connection_data)
+
+        mock_logger.assert_called_once_with(
+            'the connection to the system database failed. error message: %s',
+            'Error: Invalid value for KEY')
+
+        self._db_manager._connect_tenants.assert_not_called()
+
+    
@mock.patch('hanadb_exporter.db_manager.hdb_connector.connectors.base_connector')
+    @mock.patch('logging.Logger.error')
     @mock.patch('time.sleep')
     @mock.patch('time.time')
     def test_start_correct(self, mock_time, mock_sleep, mock_logger, 
mock_exception):
+
+        self._db_manager._get_connection_data = mock.Mock()
+        connection_data = {'mock_data': 'data'}
+        self._db_manager._get_connection_data.return_value = connection_data
+
         mock_exception.ConnectionError = Exception
         mock_time.side_effect = [0, 1, 2, 3]
         self._db_manager._system_db_connector = mock.Mock()
@@ -129,12 +271,12 @@
             None]
 
         self._db_manager.start(
-            '10.10.10.10', 30013, 'user', 'pass', multi_tenant=False, 
timeout=2)
+            '10.10.10.10', 30013, user='user', password='pass', 
multi_tenant=False, timeout=2)
 
         self._db_manager._system_db_connector.connect.assert_has_calls([
-            mock.call('10.10.10.10', 30013, user='user', password='pass', 
RECONNECT='FALSE'),
-            mock.call('10.10.10.10', 30013, user='user', password='pass', 
RECONNECT='FALSE'),
-            mock.call('10.10.10.10', 30013, user='user', password='pass', 
RECONNECT='FALSE')
+            mock.call('10.10.10.10', 30013, **connection_data),
+            mock.call('10.10.10.10', 30013, **connection_data),
+            mock.call('10.10.10.10', 30013, **connection_data)
         ])
 
         mock_sleep.assert_has_calls([
@@ -155,6 +297,11 @@
     @mock.patch('time.sleep')
     @mock.patch('time.time')
     def test_start_correct_multitenant(self, mock_time, mock_sleep, 
mock_logger, mock_exception):
+
+        self._db_manager._get_connection_data = mock.Mock()
+        connection_data = {'mock_data': 'data'}
+        self._db_manager._get_connection_data.return_value = connection_data
+
         mock_exception.ConnectionError = Exception
         mock_time.side_effect = [0, 1, 2, 3]
         self._db_manager._system_db_connector = mock.Mock()
@@ -166,12 +313,12 @@
             None]
 
         self._db_manager.start(
-            '10.10.10.10', 30013, 'user', 'pass', multi_tenant=True, timeout=2)
+            '10.10.10.10', 30013, user='user', password='pass', 
multi_tenant=True, timeout=2)
 
         self._db_manager._system_db_connector.connect.assert_has_calls([
-            mock.call('10.10.10.10', 30013, user='user', password='pass', 
RECONNECT='FALSE'),
-            mock.call('10.10.10.10', 30013, user='user', password='pass', 
RECONNECT='FALSE'),
-            mock.call('10.10.10.10', 30013, user='user', password='pass', 
RECONNECT='FALSE')
+            mock.call('10.10.10.10', 30013, **connection_data),
+            mock.call('10.10.10.10', 30013, **connection_data),
+            mock.call('10.10.10.10', 30013, **connection_data)
         ])
 
         mock_sleep.assert_has_calls([
@@ -185,7 +332,7 @@
         ])
 
         assert self._db_manager._db_connectors == 
[self._db_manager._system_db_connector]
-        
self._db_manager._connect_tenants.assert_called_once_with('10.10.10.10', 
'user', 'pass')
+        
self._db_manager._connect_tenants.assert_called_once_with('10.10.10.10', 
connection_data)
 
 
     def test_get_connectors(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hanadb_exporter-0.5.1/tests/main_test.py 
new/hanadb_exporter-0.5.3/tests/main_test.py
--- old/hanadb_exporter-0.5.1/tests/main_test.py        2019-11-06 
12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/tests/main_test.py        2019-12-10 
12:20:40.950507434 +0100
@@ -137,7 +137,8 @@
         mock_setup_logging.assert_called_once_with(config)
         mock_db_manager.assert_called_once_with()
         db_instance.start.assert_called_once_with(
-            '10.10.10.10', 1234, 'user', 'pass', multi_tenant=True, 
timeout=600)
+            '10.10.10.10', 1234, user='user', password='pass',
+            userkey=None, multi_tenant=True, timeout=600)
         db_instance.get_connectors.assert_called_once_with()
         mock_exporters.assert_called_once_with(
             connectors='connectors', metrics_file='metrics')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hanadb_exporter-0.5.1/tests/prometheus_exporter_test.py 
new/hanadb_exporter-0.5.3/tests/prometheus_exporter_test.py
--- old/hanadb_exporter-0.5.1/tests/prometheus_exporter_test.py 2019-11-06 
12:59:54.036643763 +0100
+++ new/hanadb_exporter-0.5.3/tests/prometheus_exporter_test.py 2019-12-10 
12:20:40.950507434 +0100
@@ -180,10 +180,12 @@
         assert metric_obj == mock_gauge_instance
 
     @mock.patch('hanadb_exporter.prometheus_exporter.core')
-    @mock.patch('logging.Logger.error')
-    def test_manage_gauge_incorrect_label(self, mock_logger, mock_core):
+    @mock.patch('logging.Logger.warn')
+    @mock.patch('logging.Logger.debug')
+    def test_manage_gauge_incorrect_label(self, logger_debug, logger_warn, 
mock_core):
 
         mock_gauge_instance = mock.Mock()
+        mock_gauge_instance.samples = []
         mock_core.GaugeMetricFamily = mock.Mock()
         mock_core.GaugeMetricFamily.return_value = mock_gauge_instance
 
@@ -195,28 +197,30 @@
         mock_metric.value = 'column3'
 
         formatted_query = [
-            {'column1': 'data1', 'column2': 'data2', 'column3': 'data3'},
-            {'column1': 'data4', 'column2': 'data5', 'column3': 'data6'},
-            {'column1': 'data7', 'column2': 'data8', 'column3': 'data9'}
+            {'column1': 'data1', 'column2': 'data2', 'column3': 'data3'}
         ]
 
-        with pytest.raises(ValueError) as err:
-            metric_obj = self._collector._manage_gauge(mock_metric, 
formatted_query)
+        metric_obj = self._collector._manage_gauge(mock_metric, 
formatted_query)
 
         mock_core.GaugeMetricFamily.assert_called_once_with(
             'name', 'description', None,
             ['sid', 'insnr', 'database_name', 'column4', 'column5'], 'mb')
 
-        assert('One or more label(s) specified in metrics.json'
-               ' for metric: "{}" is not found in the the query result'.format(
-                'name') in str(err.value))
+        logger_warn.assert_called_once_with(
+            'One or more label(s) specified in metrics.json '
+            'for metric "%s" that are not found in the query result',
+            'name')
+
+        assert mock_gauge_instance.call_count == 0
+        logger_debug.assert_called_once_with('%s \n', [])
 
     @mock.patch('hanadb_exporter.prometheus_exporter.core')
-    @mock.patch('logging.Logger.error')
-    def test_manage_gauge_incorrect_value(self, mock_logger, mock_core):
+    @mock.patch('logging.Logger.warn')
+    @mock.patch('logging.Logger.debug')
+    def test_manage_gauge_incorrect_value(self, logger_debug, logger_warn, 
mock_core):
 
         mock_gauge_instance = mock.Mock()
-        mock_gauge_instance.samples = 'samples'
+        mock_gauge_instance.samples = []
         mock_core.GaugeMetricFamily = mock.Mock()
         mock_core.GaugeMetricFamily.return_value = mock_gauge_instance
 
@@ -229,20 +233,27 @@
 
         formatted_query = [
             {'column1': 'data1', 'column2': 'data2', 'column3': 'data3'},
-            {'column1': 'data4', 'column2': 'data5', 'column3': 'data6'},
-            {'column1': 'data7', 'column2': 'data8', 'column3': 'data9'}
+            {'column1': 'data4', 'column4': None, 'column3': 'data6'}
         ]
 
-        with pytest.raises(ValueError) as err:
-            metric_obj = self._collector._manage_gauge(mock_metric, 
formatted_query)
+        metric_obj = self._collector._manage_gauge(mock_metric, 
formatted_query)
 
         mock_core.GaugeMetricFamily.assert_called_once_with(
             'name', 'description', None,
             ['sid', 'insnr', 'database_name', 'column1', 'column2'], 'mb')
 
-        assert('Specified value in metrics.json for metric'
-               ' "{}": ({}) not found in the query result'.format(
-                'name', 'column4') in str(err.value))
+        logger_warn.assert_has_calls([
+            mock.call(
+                'Specified value in metrics.json for metric "%s": (%s) not 
found or it is '\
+                'invalid (None) in the query result',
+                'name', 'column4'),
+            mock.call(
+                'Specified value in metrics.json for metric "%s": (%s) not 
found or it is '\
+                'invalid (None) in the query result',
+                'name', 'column4')
+        ])
+        assert mock_gauge_instance.call_count == 0
+        logger_debug.assert_called_once_with('%s \n', [])
 
     def test_reconnect_connected(self):
         self._mock_connector.isconnected.return_value = True


Reply via email to