#!/usr/pythoncontroller/python

import getopt
import sys
import os
import web
import json
import glob
from commonutils import *
import re 
import __builtin__

PIDFILE = "/var/run/storageAPI.pid"
MODPATH = "/usr/pythoncontroller"
sys.path.append(MODPATH)

GET_REQUESTS = ['Storagehandler', 'Datastore','HV','Drive','Node','NodeIOStat','VDisk', 'VDiskIOStat', 'Id', 'Tunnel','Controller']
POST_REQUESTS = ['Datastore','VDisk']
PUT_REQUESTS = ['Storagehandler','Drive','Node','VDisk','Datastore','MDFile','Tunnel','Controller'] #activate,deactivate,online,offline
DELETE_REQUESTS = ['VDisk','Datastore']

LOCALHOST = "127.0.0.1"
SHDict = {}
STORAGE_HANDLERS = []
if __name__ != "__main__":
    mods = []
    for node in glob.glob(MODPATH + "/*API.py*"):
        modname = os.path.basename(node).split('.')[0]
        if modname == 'storageAPI':
            continue
        if not modname in mods:
            mods.append(modname)

    for modname in mods:
        try:
            locals()[modname] = __import__(modname)
            print "Imported module %s" % modname
        except:
            print "Failed to load module %s" % modname
            continue
        shname = locals()[modname].getSHName()
        SHDict[shname] = locals()[modname]
        STORAGE_HANDLERS.append(shname)

def getStorageHandler(handler):
    return SHDict[handler]
            
storagehandlerstate = {
    0:'enable',
    1:'disable',
    2:'update'
    }

datastorestate = {
    0:'nodeadd',
    1:'noderemove',
    2:'degradedVDisks',
    3:'degradedNodes',
    4:'createLocal', 
    5:'deleteLocal',
    6:'colocationInfoVDisks',
    7:'rename',
    8:'renameLocal',
    9:'getParentSnapshotMapping',
    10:'getParentSnapshotMappingLocal',
    11:'epochEnable'
    }

drivestate = {
    0:'add',
    1:'remove',
    2:'prepare',
    3:'getLocalInfo',
    4:'sync',
    5:'getHealthInfo',
    6:'umount'
    }

nodestate = {
    0:'forget',
    1:'adjust',
    2:'deprecate', 
    3:'getLocalInfo',
    4:'emergencyFreeSpace',
    5:'listlocks',
    6:'cachevg',
    7:'cacheorphans'
    }

vdiskstate = {
    0:'nodeadd',
    1:'noderemove',
    2:'offline',
    3:'online',
    4:'resize',
    5:'snapshot',
    6:'repair',
    7:'deactivate',
    8:'membershiprepair',      
    9:'resynchstatus',
    10:'getLocalInfo',
    11:'startTransaction',
    12:'endTransaction',
    13:'failTransaction', 
    14:'activateLocal',
    15:'deactivateLocal',
    16:'createLocal',
    17:'onlineRefresh',
    18:'getCachedInfo',
    19:'startRemotereplicaLocal',
    20:'stopRemotereplicaLocal',
    21:'startRemotereplicate',
    22:'endRemotereplicate', 
    23:'setSnapReserve',
    24:'setSnapReserveLocal',
    25:'cleanup',
    26:'cleanupLocal',
    27:'createLocal',
    29:'onlineLocal',
    30:'offlineLocal',
    31:'enableSyncSlave',
    32:'disableSyncSlave',
    33:'setLocalMembership',
    34:'setLocalSize',
    35:'enableSyncMaster',
    36:'pauseLocal',
    37:'unpauseLocal',
    38:'setLocalSeqno',
    39:'snapshotLocal',
    40:'deleteLocal',
    41:'addCacheHostid',
    42:'removeCacheHostid',
    43:'maxResizeSize',
    44:'activate',

    # epoch
    45:'epochcreateLocal',
    46:'epochlist',
    47:'epochlistLocal',

    # dm-cache
    48:'cache',
    49:'acquireOrRelease',
    50:'acquireOrReleaseLocal',
    51:'cacheforget',
    52:'offlineLocalIncrementSeqno',
    53:'onlineIncrementSeqno',
    54:'resetCacheHostid',
    55:'resetCacheHostidLocal'
}

tunnelstate = {
    0:'prepare',
    1:'add',
    2:'remove'
}

controllerstate = {
    0:'prepare',
    1:'start',
    2:'stop'
}
urls = (
    '/(.*)', 'mainhandle'
)

# this function takes in the paramstring from the url and determines the following
# a) the storage handler
# b) a map of object type to uuids
# c) the main object type
def parseParams(paramstring):
    params = paramstring.split('/')
    index = 1
    objType = ''
    objTypeToUuid = {}
    while index < len(params):
        objType = params[index]
        if index + 1 < len(params):
             objTypeToUuid[params[index]] = params[index + 1]
             index += 2
        else:
             objTypeToUuid[params[index]] = ''
             index += 1
    return (params[0], objTypeToUuid, objType)  
             
def render_json(d):
    return json.dumps(d)

def getOptParams(json):
    try:
        return json.loads(web.data())
    except Exception, e:
        return {}

def testIP():
    if web.ctx.env['REMOTE_ADDR'] == LOCALHOST or re.search("^10.200.", web.ctx.env['REMOTE_ADDR']):
        return True
    elif os.environ.has_key('REMOTE_ADDR') and not web.ctx.env['REMOTE_ADDR'] in os.environ['REMOTE_ADDR'].split(','):
        return False
    else:
        return True

class mainhandle:
    def GET(self, paramstring):
        if not testIP():
            return render_json({"result":1,"Object type":"Unauthorised address"})
        optparams = getOptParams(json)
        sh, objTypeToUuid, objType = parseParams(paramstring)              
        if sh not in STORAGE_HANDLERS:
            print "Not in STORAGE_HANDLERS"
            return render_json({"result":1,"StorageHandle":"Not supported"})
        if objType not in GET_REQUESTS:
            print "Not in GET_REQUESTS"
            return render_json({"result":1,"Object type":"Not supported"})
        fparams = {}
        if objTypeToUuid[objType] != '':
            fparams['uuid'] = objTypeToUuid[objType]
        fparams.update(objTypeToUuid)
        fparams.update(optparams)
 
        result = {} 
        func_name = "get" + objType + "Info"
        storageHandler = getStorageHandler(sh)
        func = getattr(storageHandler,func_name)
        return render_json(func(fparams))

    def POST(self, paramstring):
        if not testIP():
            return render_json({"result":1,"Object type":"Unauthorised address"})
        sh, objTypeToUuid, objType = parseParams(paramstring)              
        if sh not in STORAGE_HANDLERS:
            return render_json({"result":1,"StorageHandle":"Not supported"})
        if objType not in POST_REQUESTS:
            return render_json({"result":1,"Object type":"Not supported"})

        storageHandler = getStorageHandler(sh)
        optparams = getOptParams(json)
        optparams.update(objTypeToUuid) 
        func_name = "create" + objType
        if not getattr(storageHandler, 'testParams')(optparams,func_name):
            return render_json({"result":1,"errorstring":"Insufficient args"})
        func = getattr(storageHandler,func_name)
        return render_json(func(optparams))

    def DELETE(self, paramstring):
        if not testIP():
            return render_json({"result":1,"Object type":"Unauthorised address"})
        sh, objTypeToUuid, objType = parseParams(paramstring)              
        if sh not in STORAGE_HANDLERS:
            return render_json({"result":1,"StorageHandle":"Not supported"})
        if objType not in DELETE_REQUESTS:
            return render_json({"result":1,"Object type":"Not supported"})
        
        fparams = {'uuid':objTypeToUuid[objType]}
        func_name = "delete" + objType
        optparams = getOptParams(json)
        fparams.update(optparams)
        fparams.update(objTypeToUuid)
        storageHandler = getStorageHandler(sh)
        if not getattr(storageHandler, 'testParams')(fparams,func_name):
            return render_json({"result":1,"errorstring":"Insufficient args"})
        func = getattr(storageHandler,func_name)
        return render_json(func(fparams))

    def PUT(self, paramstring):
        if not testIP():
            return render_json({"result":1,"Object type":"Unauthorised address"})
        sh, objTypeToUuid, objType = parseParams(paramstring)              
        if sh not in STORAGE_HANDLERS:
            return render_json({"result":1,"StorageHandle":"Not supported"})
        if objType not in PUT_REQUESTS:
            return render_json({"result":1,"Object type":"Not supported"})

        optparams = getOptParams(json)
        storageHandler = getStorageHandler(sh)
        optparams['uuid'] = objTypeToUuid[objType]
        optparams.update(objTypeToUuid)
        if not 'state' in optparams.keys():
            return render_json({"result":1,"errorstring":"State parameter not included"})
        if objType == 'Datastore':
            if int(optparams['state']) not in datastorestate.keys():
                return render_json({"result":1,"errorstring":"Incorrect ds state parameter"})
            func_name = datastorestate[int(optparams['state'])] + objType
        elif objType =='VDisk':
            if int(optparams['state']) not in vdiskstate.keys():
                return render_json({"result":1,"errorstring":"Incorrect vdisk state parameter"})
            func_name = vdiskstate[int(optparams['state'])] + objType
        elif objType =='Node':
            if int(optparams['state']) not in nodestate.keys():
                return render_json({"result":1,"errorstring":"Incorrect node state parameter"})
            func_name = nodestate[int(optparams['state'])] + objType
        elif objType =='Drive':
            if int(optparams['state']) not in drivestate.keys():
                return render_json({"result":1,"errorstring":"Incorrect drive state parameter"})
            func_name = drivestate[int(optparams['state'])] + objType
        elif objType =='Tunnel':
            if int(optparams['state']) not in tunnelstate.keys():
                return render_json({"result":1,"errorstring":"Incorrect tunnel state parameter"})
            func_name = tunnelstate[int(optparams['state'])] + objType
        elif objType =='Controller':
            if int(optparams['state']) not in controllerstate.keys():
                return render_json({"result":1,"errorstring":"Incorrect controller state parameter"})
            func_name = controllerstate[int(optparams['state'])] + objType
        elif objType =='Storagehandler':
            if int(optparams['state']) not in storagehandlerstate.keys():
                return render_json({"result":1,"errorstring":"Incorrect storagehandler state parameter"})
            func_name = storagehandlerstate[int(optparams['state'])] + objType
        if not getattr(storageHandler, 'testParams')(optparams,func_name):
            return render_json({"result":1,"errorstring":"Insufficient args"})
        func = getattr(storageHandler, func_name)
        return render_json(func(optparams))

if __name__ == "__main__":

    try:
        import diskutil
        if diskutil.in_maintenance_mode():
	    sys.exit("in maintenance mode, not starting storageAPI service")
    except ImportError:
        print "failed to import diskutil, can't check if in maintenance mode, " \
            "assuming this is a static HV"
    if isAlive(PIDFILE):
        print "Nodecontroller already running....exiting quietly"
        sys.exit(0)
    try:
        opts, args = getopt.getopt(sys.argv[1:], "s:fL:")
    except getopt.GetoptError, err:
        # print help information and exit:
        print str(err) # will print something like "option -a not recognized"
        usage()
        sys.exit(2)

    deamon = True
    for o, a in opts:
        if o == "-h":
            usage()
            exit(1)
        elif o == "-s":
            os.environ['REMOTE_ADDR'] = a
            sys.argv.pop()
            sys.argv.pop()
        elif o == "-f":
            deamon = False
            sys.argv.pop()
        elif o == "-L":
            __builtin__.LOG_LEVEL = int(a)
            sys.argv.pop()
            sys.argv.pop()

    if deamon:
        deamonize()

    app = web.application(urls,globals())
    app.run()
