Skip to content

Commit

Permalink
Merge pull request #6 from thomgonzalez/tsg/multiple-connection
Browse files Browse the repository at this point in the history
Multiple connection
  • Loading branch information
rootsantiago authored Sep 27, 2022
2 parents 4335777 + 3d51e70 commit 89534cf
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 68 deletions.
36 changes: 21 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@ Before installing pyodbc you must install the packages from Debian/Ubuntu system

1.1 INSTALLING ON DEBIAN-BASED LINUX DISTRIBUTIONS.

Install python and odbc modules

```
sudo apt update
sudo apt install libpq-dev python-dev python3-dev
sudo apt install python3.5-dev python3.6-dev
sudo apt install unixodbc-dev
sudo apt install libpq-dev python-dev python3-dev python3.6-dev
sudo apt install build-essential
```

Install unixodbc

```
sudo apt install unixodbc-dev unixodbc
```

To connect to IBM iSeries systems we will need to download a .deb file, this library can be downloaded from the IBM page https://www.ibm.com/support/pages/ibm-i-access-client-solutions. So let's create an account, log in, and download the IBM i Access for Linux package.

Example installation instructions:
Copy the downloaded iseries odbc driver to the server and install:
```
sudo apt install ./ibm-iaccess-1.1.0.15-1.0.ppc64el.deb
```
Expand All @@ -35,28 +42,27 @@ pip install pyiaccess
Create a .env file in the root of your project

```
ISERIE_DSN = LIBRARY
ISERIE_HOST = HOST
ISERIE_USER = USER
ISERIE_PASSWORD = PASSWORD
IBMI_DSN = YOUR DATABASE
IBMI_HOST = YOUR IP
IBMI_USER = YOUR USER
IBMI_PASSWORD = YOUR PASSWORD
IBMI_PORT = YOUR PORT # Define or not is required
SFTP_PORT = 22
SFTP_REMOTE_PATH = "/home/repo/"
SFTP_HOST = YOUR IP
SFTP_USER = YOUR USER
SFTP_PASSWORD = YOUR PASSWORD
SFTP_PORT = 22 # Default 22 or change port
SFTP_REMOTE_PATH = "/home/"
```

Configuration of environment variables of the project or application

```python
import os
from pyiaccess.manage import set_env

# Define .env file with absolute or complete path.
path_env = '/home/user/proyect/.env'

# Define .env file with relative path of the project or application.
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
path_env = os.path.join(BASE_DIR, ".env")

# Load the environment variables.
set_env(path_env)
```
28 changes: 18 additions & 10 deletions pyiaccess/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,30 @@
from pyiaccess.helpers.common import generic_error


class ConnexionClient(object):
class IBMClient(object):
"""
Base class for database connection.
"""

_DRIVER = "IBM i Access ODBC Driver"

def __init__(self):
conn = "SYSTEM={};db2:DSN={};UID={};PWD={};DRIVER={};".format(
os.getenv("ISERIE_HOST"),
os.getenv("ISERIE_DSN"),
os.getenv("ISERIE_USER"),
os.getenv("ISERIE_PASSWORD"),
self._DRIVER,
def __init__(self, **kwargs):
self._IBMI_HOST = kwargs.get("hostname", None)
self._IBMI_DSN = kwargs.get("dsn", None)
self._IBMI_USER = kwargs.get("username", None)
self._IBMI_PASSWORD = kwargs.get("password", None)
self._IBMI_PORT = kwargs.get("port", None)

self.conn_str = (
f"SYSTEM={self._IBMI_HOST};db2:DSN={self._IBMI_DSN};UID={self._IBMI_USER};"
)
self._cnn = pyodbc.connect(conn)
if self._IBMI_PORT:
port = f"PORT={self._IBMI_PORT};"
self.conn_str += port
self.conn_str += f"PWD={self._IBMI_PASSWORD};DRIVER={self._DRIVER};"

def connect(self):
self._cnn = pyodbc.connect(self.conn_str)
self._cnn.autocommit = True

def close(self):
Expand Down Expand Up @@ -77,7 +85,7 @@ def execute_many(self, query, params):
return True

@generic_error
def call(self, usp_name, resultset):
def call(self, usp_name, resultset=False):
cursor = self._cnn.cursor()
cursor.execute(usp_name)

Expand Down
12 changes: 12 additions & 0 deletions pyiaccess/manage.py → pyiaccess/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import os
from dotenv import load_dotenv
from pathlib import Path
from pyiaccess.transfer import SFTPClient
from pyiaccess.db import IBMClient


def set_env(path_env):
Expand All @@ -10,3 +12,13 @@ def set_env(path_env):
"""
dotenv_path = Path(path_env)
load_dotenv(dotenv_path=dotenv_path)


def create_sftp(**kwargs):
sftp = SFTPClient(**kwargs)
return sftp


def create_db(**kwargs):
ibm = IBMClient(**kwargs)
return ibm
2 changes: 1 addition & 1 deletion pyiaccess/helpers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def func_wrapper(*args, **kwargs):
try:
return fn(*args, **kwargs)
except Exception as e:
error = "Connection error: {}".format_map(e)
error = f"Connection error: {e}"
raise error

return func_wrapper
33 changes: 17 additions & 16 deletions pyiaccess/transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,55 @@ class SFTPClient(object):
Class for SSH client
"""

def __init__(self):
def __init__(self, **kwargs):
self.ssh_client = paramiko.SSHClient()
self.ssh_client.load_host_keys(
os.path.expanduser(os.path.join("~", ".ssh", "known_hosts"))
)
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ISERIE_HOST = os.getenv("ISERIE_HOST")
self.ISERIE_USER = os.getenv("ISERIE_USER")
self.ISERIE_PASSWORD = os.getenv("ISERIE_PASSWORD")
self.SFTP_PORT = os.getenv("SFTP_PORT")
self.SFTP_REMOTE_PATH = os.getenv("SFTP_REMOTE_PATH")
self.SFTP_HOST = kwargs.get("hostname", None)
print(self.SFTP_HOST)
self.SFTP_USER = kwargs.get("username", None)
self.SFTP_PASSWORD = kwargs.get("password", None)
self.SFTP_PORT = kwargs.get("port", 22)
self.SFTP_REMOTE_PATH = kwargs.get("remote_path", None)

def connect(self):
try:
self.ssh_client.connect(
hostname=self.ISERIE_HOST,
username=self.ISERIE_USER,
password=self.ISERIE_PASSWORD,
hostname=self.SFTP_HOST,
username=self.SFTP_USER,
password=self.SFTP_PASSWORD,
port=self.SFTP_PORT,
)
msg = "{}: {}".format(self.ISERIE_HOST, "CONNECTED")
msg = f"{self.SFTP_HOST}: CONNECTED"
logging.info(msg)
except paramiko.AuthenticationException as exc:
print("Authentication Failed")
logging.error("Authentication Failed")
raise exc
except paramiko.SSHException as exc:
print("SSH Error")
logging.error("SSH Error")
raise exc
except Exception as exc:
print("Unknown Error: %s" % exc)
logging.error(f"Unknown Error: {exc}")
raise str(exc)

def list_files(self):
sftp = self.ssh_client.open_sftp()
sftp.chdir(self.SFTP_REMOTE_PATH)
filenames = []
for filename in sftp.listdir_attr():
filenames.append("{}".format(filename))
filenames.append(f"{filename}")
sftp.close()

return filenames

def upload(self, path_file):
local_file_path = path_file
remote_path = "{}{}".format(self.SFTP_REMOTE_PATH, ntpath.basename(path_file))
remote_path = f"{self.SFTP_REMOTE_PATH}{ntpath.basename(path_file)}"
ftp_client = self.ssh_client.open_sftp()
ftp_client.put(local_file_path, remote_path)
ftp_client.close()

msg = "TRANSFERRED: {}".format(remote_path)
msg = f"TRANSFERRED: {remote_path}"
logging.info(msg)
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
long_description = fh.read()

setuptools.setup(
name="pyiaccess",
version="0.1.4b1",
name="PyiAccess",
version="0.1.1b1",
author="Tomás Gonzalez",
author_email="[email protected]",
description="It is a library that connects to IBM Power Systems (IBM i), with pyodbc and SSH connection.",
Expand Down
41 changes: 41 additions & 0 deletions sftp_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import os
import unittest
from pathlib import Path
from pyiaccess.engine import set_env, create_sftp

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
path_env = os.path.join(BASE_DIR, ".back.env")
set_env(path_env)


class TestSFTPClient(unittest.TestCase):
def setUp(self):
hostname = os.getenv("SFTP_HOST", None)
username = os.getenv("SFTP_USER", None)
password = os.getenv("SFTP_PASSWORD", None)
port = os.getenv("SFTP_PORT", None)
remote_path = os.getenv("SFTP_REMOTE_PATH", None)

if not hostname and username and password:
raise Exception("Sorry, Undefined environment variables.")

self.path_file = os.path.join(BASE_DIR, "Clases.csv")

self.engine = create_sftp(
hostname=hostname,
username=username,
password=password,
port=port,
remote_path=remote_path,
)

def test_is_file(self):
self.assertEqual(Path(self.path_file).is_file(), True)

def test_upload_file(self):
self.engine.connect()
self.engine.upload(self.path_file)


if __name__ == "__main__":
unittest.main()
42 changes: 18 additions & 24 deletions tests.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
import os
import unittest
from pathlib import Path
from pyiaccess.manage import set_env
from pyiaccess.db import ConnexionClient
from pyiaccess.transfer import SFTPClient
from pyiaccess.engine import set_env, create_db

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
path_env = os.path.join(BASE_DIR, ".back.env")
set_env(path_env)


class TestDBClient(unittest.TestCase):

def setUp(self):
self.cnn = ConnexionClient()
hostname = os.getenv("IBMI_HOST", None)
dsn = os.getenv("IBMI_DSN", None)
username = os.getenv("IBMI_USER", None)
password = os.getenv("IBMI_PASSWORD", None)
port = os.getenv("IBMI_PORT", None)

self.engine = create_db(
hostname=hostname, dsn=dsn, username=username, password=password, port=port
)
self.engine.connect()

def test_connexion(self):
message = "Given object is not instance of ConnexionClient."
self.assertIsInstance(self.cnn, ConnexionClient, message)
self.assertIsInstance(self.engine, object, message)

def test_sql_selected_all(self):
sql = """SELECT * FROM TSANTIAGO.NF01"""
data = self.cnn.execute(sql)
data = self.engine.execute(sql)
columns = [column[0] for column in data.description]

results = []
Expand All @@ -34,7 +41,7 @@ def test_sql_select(self):
sql = """SELECT * FROM TSANTIAGO.NF01
WHERE NEMPL=?
"""
data = self.cnn.execute(sql, params=["215"])
data = self.engine.execute(sql, params=["215"])
columns = [column[0] for column in data.description]

results = []
Expand All @@ -46,11 +53,11 @@ def test_sql_select(self):

def test_sql_insert(self):
sql = """
INSERT INTO TSANTIAGO.TB_EMPLOYEE_UPD(CCIA, COCIA, NEMPL, CCOST)
INSERT INTO TSANTIAGO.TB_EMPLOYEE_UPD(CCIA, COCIA, NEMPL, CCOST)
VALUES (?,?,?,?)
"""
params = [1, "test2", "1", "0"]
data = self.cnn.execute(sql, params)
data = self.engine.execute(sql, params)

message = "First value and second value are not equal."
self.assertEqual(data.rowcount, 1, message)
Expand All @@ -62,24 +69,11 @@ def test_sql_update(self):
WHERE NEMPL=?
"""
params = ["demo", "1"]
data = self.cnn.execute(sql, params)
data = self.engine.execute(sql, params)

message = "Given object is not instance of data.rowcount"
self.assertIsInstance(data.rowcount, int, message)


class TestSFTPClient(unittest.TestCase):
def setUp(self):
self.path_file = os.path.join(BASE_DIR, "Clases.csv")

def test_is_file(self):
self.assertEqual(Path(self.path_file).is_file(), True)

def test_upload_file(self):
sftp = SFTPClient()
sftp.connect()
sftp.upload(self.path_file)


if __name__ == "__main__":
unittest.main()

0 comments on commit 89534cf

Please sign in to comment.