common.py 4.15 KB
import logging
from time import (
    time,
    sleep,
    )
from sqlalchemy import exc
from opensipkd.tcp.connection import join_ip_port
from .iso8583.read_conf import name_conf
from .iso8583.connection import conn_mgr


log = logging.getLogger(__name__)


# Daftar job dari web request, berisi iso request
# key: ip:port, value: list of iso
web_request = {}

# Daftar job yang sedang diproses, yaitu menunggu iso response
# key: ip:port, value: list of stan (bit 11)
web_process = {}

# Daftar job yang sudah selesai, berisi iso response
# key: ip:port, value: dict of (key: stan, value: iso)
web_response = {}

# Nilai timeout dalam detik saat menunggu ISO-8583 response
ISO_TIMEOUT = 30


def append_web_process(ip_port, iso):
    stan = iso.get_stan()
    if ip_port in web_process:
        web_process[ip_port].append(stan)
    else:
        web_process[ip_port] = [stan]


def append_web_response(ip_port, stan, iso):
    if ip_port in web_response:
        web_response[ip_port][stan] = iso
    else:
        web_response[ip_port] = {stan: iso}


# Abstract class. Inherit, please.
class BaseView:
    def __init__(self, request):
        self.request = request

    def get_name(self):  # Override, please
        return 'unknown'

    def get_allowed_ip(self):  # Override, please
        return ['0.0.0.0']

    def validate(self):
        allowed_ip = self.get_allowed_ip()
        if '0.0.0.0' in allowed_ip:
            return
        if self.request.client_addr not in allowed_ip:
            raise self.not_found_error(self.request.client_addr)

    def log_prefix(self):
        ip = self.request.client_addr
        name = self.get_name()
        mem_id = id(self.request)
        return f'{ip} {name} {mem_id}'

    def log_receive(self, msg):
        prefix = self.log_prefix()
        path = self.request.path
        msg = f'{prefix} Receive {path} {msg}'
        log.info(msg)

    def log_send(self, msg):
        prefix = self.log_prefix()
        msg = f'{prefix} Send {msg}'
        log.info(msg)

    def log_debug(self, msg):
        prefix = self.log_prefix()
        msg = f'{prefix} {msg}'
        log.debug(msg)


# Abstract class. Inherit, please.
class BaseIsoView(BaseView):
    # Get ISO8583 connection
    def get_connection(self):
        conf = self.get_iso_conf()
        found_conn = None
        for ip_port, conn in conn_mgr:
            ip, port = ip_port.split(':')
            if conf['ip'] != ip:
                continue
            port = int(port)
            if conf['port'] != port:
                continue
            found_conn = conn
        if not found_conn:
            raise self.not_found_error(conf['name'])
        if not found_conn.running:
            raise self.not_running_error(conf['name'])
        return found_conn

    def send_iso(self, conn, iso):
        ip_port = join_ip_port(conn.conf['ip'], conn.conf['port'])
        if ip_port in web_request:
            web_request[ip_port].append(iso)
        else:
            web_request[ip_port] = [iso]
        stan = iso.get_stan()
        awal = time()
        while True:
            sleep(0.001)
            if time() - awal > ISO_TIMEOUT:
                raise self.timeout_error()
            if ip_port not in web_response:
                continue
            result = web_response[ip_port]
            if stan in result:
                iso = result[stan]
                del result[stan]
                return iso

    def get_iso_conf_name(self):  # Override, please
        return 'bjb'

    def get_iso_conf(self):
        name = self.get_iso_conf_name()
        return name_conf[name]

    def not_found_error(self, hostname):
        msg = f'Host {hostname} tidak ditemukan di konfigurasi'
        return Exception(msg)

    def not_running_error(self, hostname):
        msg = f'Host {hostname} belum terhubung'
        return Exception(msg)

    def timeout_error(self):
        return Exception('Timeout')


SQL_CHECK = dict(
    postgresql='SELECT 1',
    oracle='SELECT 1 FROM dual')


def db_check(engine):
    sql = SQL_CHECK[engine.dialect.name]
    try:
        engine.execute(sql)
    except exc.DBAPIError as e:
        if e.connection_invalidated:
            log.debug('Database connection problem')