#!/usr/bin/python2.7
# vim: tabstop=4 softtabstop=4 shiftwidth=4 textwidth=80 smarttab expandtab
"""
* Copyright (C) 2015  Sangoma Technologies Corp.
* All Rights Reserved.
*
* Author
* Johnny Ma <jma@sangoma.com>
*
* This code is Sangoma Technologies Confidential Property.
* Use of and access to this code is covered by a previously executed
* non-disclosure agreement between Sangoma Technologies and the Recipient.
* This code is being supplied for evaluation purposes only and is not to be
* used for any other purpose.
"""

import os
import sys
import pyodbc
import re
import logging
from logging.handlers import SysLogHandler


CONN_STR = "DRIVER={mysql};DATABASE=freeswitch;server=localhost;user=root;pwd=;port=3306;"
ENGINE_REGEX = r"ENGINE=(InnoDB|MyISAM|MEMORY)"
TABLE_ENGINE_UPDATE_METRICS =  {
        "aliases"                               : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "calls"                                 : {"OldEngine" : "MyISAM", "NewEngine" : "MEMORY"},
        "channels"                              : {"OldEngine" : "InnoDB", "NewEngine" : "MEMORY"},
        "complete"                              : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "interfaces"                            : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "nat"                                   : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "registrations"                         : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "sip_authentication"                    : {"OldEngine" : "MyISAM", "NewEngine" : "MEMORY"},
        "sip_dialogs"                           : {"OldEngine" : "MyISAM", "NewEngine" : "MEMORY"},
        "sip_presence"                          : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "sip_recovery"                          : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "sip_registrations"                     : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "sip_shared_appearance_dialogs"         : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "sip_shared_appearance_subscriptions"   : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "sip_subscriptions"                     : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "sip_thru_subscriptions"                : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"},
        "tasks"                                 : {"OldEngine" : "MyISAM", "NewEngine" : "InnoDB"}}



class TableEngineLogger:

    formatter = logging.Formatter('[%(name)s] [%(levelname)s] %(message)s')
    logger = logging.getLogger('sng-update-table-engine')
    logger.setLevel(logging.INFO)

    syslog_handler = SysLogHandler('/dev/log')
    syslog_handler.setFormatter(formatter)
    logger.addHandler(syslog_handler)



class DBTable:

    def __init__(self, conn, cursor, name, logger):
        self._conn = conn
        self._cursor = cursor
        self._name = name
        self._logger = logger

        try:
            self._cursor.execute("show create table %s;" % self._name)
        except:
            raise


    def _get_ddl(self):
        rows = None

        try:
            self._cursor.execute("show create table %s;" % self._name)
            rows = self._cursor.fetchone()
        except:
            return None

        for row in rows:
            if row.startswith("CREATE TABLE"):
                return row

        return None


    def get_engine(self):
        myddl = self._get_ddl()
        if myddl is None:
            return None
        current_engine = re.search(ENGINE_REGEX, myddl, re.IGNORECASE).group()
        return current_engine[(current_engine.find('=')+1):]


    def is_empty(self):
        rows = None

        try:
            self._cursor.execute("select * from %s;" % self._name)
            rows = self._cursor.fetchone()
        except:
            return None

        if rows is None:
            return True
        else:
            return False


    def rename_to(self, new_name):
        try:
            self._cursor.execute("rename table %s to %s;" % (self._name, new_name))
            self._conn.commit()
        except:
            pass

        self._name = new_name


    @staticmethod
    def create_table(conn, cursor, ddl):
        try:
            cursor.execute(ddl)
            conn.commit()
        except:
            pass


    @staticmethod
    def copy_table(conn, cursor, from_tbl, to_tbl):
        try:
            cursor.execute("insert into %s select * from %s;" % (to_tbl, from_tbl))
            conn.commit()
        except:
            pass


    def update_engine(self, new_engine):
        # get DDL of table xxx
        my_ddl = self._get_ddl()
        if my_ddl is None:
            return

        # rename xxx to xxx_old
        new_tbl = self._name
        old_tbl = self._name + "_old"
        self.rename_to(old_tbl)

        # modify DDL to new engine
        my_ddl = my_ddl.replace("ENGINE=%s" % self.get_engine(), "ENGINE=%s" % new_engine)
        print my_ddl

        # create table xxx with new engine
        DBTable.create_table(self._conn, self._cursor, my_ddl)

        # copy records from xxx_old to xxx
        DBTable.copy_table(self._conn, self._cursor, old_tbl, new_tbl)

        # drop xxx_old
        self.drop()


    def drop(self):
        deleted = 0

        try:
            deleted = self._cursor.execute("delete from %s;" % self._name).rowcount
            self._cursor.execute("drop table %s;" % self._name)
            self._conn.commit()
        except:
            return 0

        return deleted


    def execute_update_logic(self, new_engine):
        # if table exists but already using the new engine, we
        # don't need to do anything either
        current_engine = self.get_engine()
        if current_engine.lower() == new_engine.lower():
            self._logger.info("Table %s is alredy with new engine %s. Skip" % (self._name, new_engine))
            return

        self._logger.info("Table %s(%s) needs to be updated to engine %s." % (self._name,
                current_engine, new_engine))

        if self.is_empty():
            self._logger.info("Empty table %s is dropped." % self._name)
            self.drop()
        else:
            self._logger.info("Non-empty table %s is updated to new engine %s." % (self._name, new_engine))
            self.update_engine(new_engine)



## main() ##
conn = None
logger = TableEngineLogger.logger

try:
    conn = pyodbc.connect(CONN_STR)
except:
    logger.error("Connection to MySQL server cannot be established.")
    sys.exit(1)

if conn is None:
    logger.error("Connection to MySQL server cannot be established.")
    sys.exit(1)

cursor = conn.cursor()

for table in TABLE_ENGINE_UPDATE_METRICS:
    try:
        table_to_update = DBTable(conn, cursor, table, logger)
    except:
        # if table doesn't exist, we don't need to do anything;
        # the table will be created by nsc with a new engine
        logger.info("Table %s doesn't exist!" % table)
        continue

    table_to_update.execute_update_logic(TABLE_ENGINE_UPDATE_METRICS[table]["NewEngine"])

cursor.close()
conn.close()

