#!/usr/bin/python

import threading
import time
import socket
import logging
import eventlet

from sqlalchemy import Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from cinder_drbd_volume_driver import drbdapi

from cinder.openstack.common import rpc
from cinder.openstack.common.rpc import dispatcher
from oslo.config import cfg

logging.basicConfig(filename='/tmp/drbd-agent.log',level=logging.DEBUG)

CONF = cfg.CONF
CONF(project='cinder')
CONF.import_opt('connection', 'cinder.openstack.common.db.sqlalchemy.session', group='database')

ktia_opts = [
        cfg.StrOpt('drbdapi_dsn',
                default=None,
                help='Databaser connection string for DRBD API'),
]

CONF.register_opts(ktia_opts)
print "drbdapi_dsn: " + CONF.drbdapi_dsn
drbdapi.initConnection(CONF.drbdapi_dsn)

dbcon = CONF.database.connection

QUEUE_NAME = 'drbd-agent-'
MYQUEUE_NAME = QUEUE_NAME+socket.gethostname()

eventlet.monkey_patch()

## innen kozosbe kene

Base = declarative_base()

class DBKTIAVolume(Base):
        __tablename__ = 'ktiavolume'

        id = Column(String(36), primary_key=True)
        replicated = Column(Integer)
        status = Column(Integer)
        statusdesc = Column(String(255))
        remotehost = Column(String(255))
        remoteport = Column(Integer)
        primaryreconf = Column(Boolean)
        secondaryreconf = Column(Boolean)

        def __init__(self, id, replicated, status, statusdesc, remotehost, remoteport, primaryreconf, secondaryreconf):
                self.id = id
                self.replicated = replicated
                self.status = status
                self.statusdesc = statusdesc
                self.remotehost = remotehost
                self.remoteport = remoteport
                self.primaryreconf = primaryreconf
                self.secondaryreconf = secondaryreconf

        def __repr__(self):
           return "<KTIAVolume('%s','%s','%s','%s','%s')>" % (self.id, self.replicated, self.status, self.statusdesc, self.remotehost)

class DBConnection(object):
        def __init__(self, url):
                self.engine = create_engine(url)
                Base.metadata.create_all(self.engine)
                Session = sessionmaker(bind=self.engine)
                self.session = Session()

class ContextClass:
        def to_dict(self):
                return {'user': 'wtf'}

## idaig

class ChangeManager():
        def __init__(self, name):
                self.name = name

	def noneToSyncOrAsync(self, syncType, id, role):
		print "Setting replication state none -> " + syncType + " (id: " + id + ", role: " + role + ")"
		device = drbdapi.DRBDDevice.getDRBDbyCinderId(id)
		db = DBConnection(dbcon)
		ktiavolume = db.session.query(DBKTIAVolume).filter(DBKTIAVolume.id == id).first()
		if(role == 'secondary'):
			print "This is the remote host, setting up DRBDDevice (1G, datavg)"
			device.setUpRemote('1G', 'datavg')
			ktiavolume.secondaryreconf = False
			db.session.commit()
		else:
			print "This is the primary host, checking DRBDDevice state"
			currentState = device.getState()
			if(currentState['status']['replState'] == 'DISCONNECTED' or currentState['status']['replState'] == 'IN-SYNC'):
				print "Device state is disconnected or in-sync, setting to " + syncType
				device.setReplType(syncType)
				ktiavolume.primaryreconf = False
				db.session.commit()
				rpccontext = ContextClass()
				secondaryTopic = QUEUE_NAME + ktiavolume.remotehost
		                print "Sending message to remote host on queue " + secondaryTopic
				message = {
					'method': 'change', 
					'args': { 
						'old': 'NonRepl', 
						'new': syncType, 
						'id': id,
						'primary_host': 'TODO',
						'secondary_host': ktiavolume.remotehost, 
						'role': 'secondary'
					} 
				}
                		connection = rpc.create_connection(new=True)
                        	rpc.fanout_cast(rpccontext, secondaryTopic, message)
	
	def syncOrAsyncToNone(self, syncType, id, host, role):
		print "Setting replication state " + syncType + " -> none (id: " + id + ", host: " + host + ", role: " + role + ")"
		device = drbdapi.DRBDDevice.getDRBDbyCinderId(id)
		db = DBConnection(dbcon)
		ktiavolume = db.session.query(DBKTIAVolume).filter(DBKTIAVolume.id == id).first()
		if(role == 'primary'):
			print "This was the primary host, updating DRBDDevice replType to NoneRepl"
			device.setReplType('NoneRepl') # TODO NonRepl vs NoneRepl
			ktiavolume.primaryreconf = False
			db.session.commit()
		else:
			print "This was the remote host, purging DRBDDevice"
			device.purgeRemote()
			ktiavolume.secondaryreconf = False
			db.session.commit()
			rpccontext = ContextClass()
			primaryTopic = QUEUE_NAME + host
			print "Sending message to primary host on queue " + primaryTopic
		        message = {
				'method': 'change', 
				'args': { 
					'old': 'NonRepl', 
					'new': syncType, 
					'id': id,
					'primary_host': host,
					'secondary_host': 'TODO',
					'role': 'primary'
				} 
			}
                	connection = rpc.create_connection(new=True)
                        rpc.fanout_cast(rpccontext, primaryTopic, message)
	
	def noneToAsync(self, id, role):
		self.noneToSyncOrAsync('AsyncRepl', id, role)

	def asyncToNone(self, id, host, role):
		self.syncOrAsyncToNone('AsyncRepl', id, host, role)

	def noneToSync(self, id, role):
		self.noneToSyncOrAsync('SyncRepl', id, role)
	
	def syncToNone(self, id, host, role):
		self.syncOrAsyncToNone('SyncRepl', id, host, role)
	
	def syncToAsync(self, id):
		pass		
	
	def asyncToSync(self, id):
		pass	
	
	def syncToLiveMigrate(self, id):
		pass
	
	def liveMigrateToSync(self, id):
		pass
 
	def change(self, ctxt, **kwargs):
		id = kwargs['id']
		role = kwargs['role']
		host = kwargs['primary_host']
		print kwargs['id'] + ' ' + kwargs['role'] + '  ' + kwargs['old'] + ' ' + kwargs['new']
		if(kwargs['old'] == 'NonRepl' and kwargs['new'] == 'AsyncRepl'):
			self.noneToAsync(id, role)
		if(kwargs['old'] == 'AsyncRepl' and kwargs['new'] == 'NonRepl'):
			self.asyncToNone(id, host, role)
		if(kwargs['old'] == 'NonRepl' and kwargs['new'] == 'SyncRepl'):
			self.noneToSync(id, role)
		if(kwargs['old'] == 'SyncRepl' and kwargs['new'] == 'NonRepl'):
			self.syncToNone(id, host, role)
		if(kwargs['old'] == 'SyncRepl' and kwargs['new'] == 'AsyncRepl'):
			self.syncToAsync(id)
		if(kwargs['old'] == 'AsyncRepl' and kwargs['new'] == 'SyncRepl'):
			self.asyncToSync(id)
		if(kwargs['old'] == 'SyncRepl' and kwargs['new'] == 'LiveMigrate'):
			self.syncToLiveMigrate(id)
		if(kwargs['old'] == 'LiveMigrate' and kwargs['new'] == 'SyncRepl'):
			self.liveMigrateToSync(id)

class MQListenerThread(threading.Thread):
	def __init__(self):
		threading.Thread.__init__(self)
		self.daemon = True

	def run(self):
		print "Consuming queue " + MYQUEUE_NAME
		self.manager = ChangeManager("manager")
                self.dispatcher = dispatcher.RpcDispatcher((self.manager,))
		self.connection = rpc.create_connection(new=True)
		self.connection.create_consumer(MYQUEUE_NAME, self.dispatcher, fanout=True)
		self.connection.consume()
	
class PollingThread(threading.Thread):
	def __init__(self):
		threading.Thread.__init__(self)
		self.daemon = True

	def run(self):
		while True:
			time.sleep(60)

if __name__ == '__main__':
	MQListenerThread().start()
	PollingThread().start()
	while threading.active_count() > 0:
		time.sleep(1)

