#!/usr/bin/python
# -*- coding: utf-8 -*-

DOCUMENTATION = '''
---
module: oracle_cloud_dbpaas
short_description: Manage Oracle Instance Services in the Oracle Cloud
description:
    - Best way to use this module in task playbooks is to delegate_to: 127.0.0.1
version_added: "0.1.0"
options:
    identityDomain:
        description:
            - Name of the identity domain housing the Database as a Service subscription or trial to which the request applies.
        required: true
        default:     
    user:
        description:
            - 
        required: true
        default:
    password:
        description:
            - 
        required: true
        default:    
    state:
        description:
            - The intended state of the Oracle Cloud Instance
                - present = Create New Cloud Service
                - absent = Delete Existen Cloud Service
                - start = Start Existen Cloud Service VM
                - stop  = Stop Existen Cloud Service VM
                - restart = Restart Existen Cloud Service VM
                - scale = Scale Up or Down Cloud Service VM
        required: true
        choices=["present","absent","start", "stop", "restart","scale"]
    serviceName:
        description:
            - A string containing the name for service instance.
        required: true
        default:
    level:
        description:
            - A string containing the service level for the service instance:
                - PAAS: The Oracle Database Cloud Service service level
                - BASIC: The Oracle Database Cloud Service - Virtual Image service level
        required: false
        default: PAAS
        choices=["PAAS","BASIC"]
    subscriptionType:
        description:
            - A string containing the billing frequency for the service instance; either MONTHLY or HOURLY.
        required: false
        default:
    version:
        description:
            - A string containing the Oracle Database version for the service instance:
                - 12.1.0.2
                - 11.2.0.4
        required: false
        default: 12.1.0.2
        choices=["12.1.0.2","11.2.0.4"]
    edition:
        description:
            - A string containing the database edition for the service instance:
                - SE: Standard Edition
                - EE: Enterprise Edition
                - EE_HP–Enterprise Edition: High Performance
                - EE_EP–Enterprise Edition: Extreme Performance
        required: false
        default: "EE"
        choices=["SE","EE","EE_HP","EE_EP"]
    description:
        description:
            - A string containing a description of the service instance, if desired.
        required: false
        default:
    shape:
        description:
            - A string containing the Oracle Compute Cloud shape to scale the service instance up to.
        required: false
        default: oc3
        choices=["oc3","oc4","oc5","oc6","oc7","oc1m","oc2m","oc3m","oc4m","oc5m"]
    vmPublicKey:
        description:
            - A string containing the fully qualified name of an SSH public key already uploaded to Oracle Compute Cloud service. This string has the form:
            - Either this parameter or the vmPublicKey parameter must be provided, but not both of them.
        required: false
        default:
    vmPublicKeyText:
        description:
            - A string containing the text of an SSH public key. This key is added to Oracle Compute Cloud Service as part of the instance creation operation.
            - Either this parameter or the vmPublicKey parameter must be provided, but not both of them.
        required: false
        default:
    usableStorage:
        description:
            - A string containing the number of GB of usable data storage for the Oracle Database server .
        required: false
        default:
    adminPassword:
        description:
            - A string containing the administrator password for the service instance.
        required: false
        default:
    sid:
        description:
            - A string containing the SID for the database instance.
        required: false
        default:
    pdbName:
        description:
            - A string containing the name for the default PDB (pluggable database).
            - Include this parameter only if the level is "PAAS", the version is "12.1.0.2" and the edition is EE, EE_HP or EE_EP.
        required: false
        default:    
    backupDestination:
        description:
            - A string containing the backup configuration for the service instance:
                - NONE: Configure no backups.
                - DISK: Configure backups to local storage on the service instance.
                - BOTH: Configure backups to local storage on the service instance and to an Oracle Storage Cloud container.
            - Include this parameter only if the level is "PAAS".
        required: false
        default: NONE
        choices=["NONE","DISK","BOTH"]
    cloudStorageContainer:
        description:
            - A string containing the Oracle Storage Cloud container for backups. This string has the form: instance-id_domain\/container
        required: false
        default:
    cloudStorageUser:
        description:
            - The user name of an Oracle Cloud user with read/write access to the specified
        required: false
        default:
    cloudStoragePwd:
        description:
            - A string containing the password of the specified cloudStorageUser.
        required: false
        default:

    hostname:
        description:
            - The Oracle database host
        required: false
        default: localhost

    
notes:
    - required=True needs to be installed
requirements: [ "request" ]
author: Björn Ahl, bjorn.ahl@gmail.com, @TwittAhl
'''

EXAMPLES = '''
# Stop VM in Oracle Cloud
oracle_cloud_dbpaas: identityDomain: domain1 user: oracle_cloud_admin_user password: oracle_cloud_admin_password serviceName: Oracle Cloud Service Name state: stop

# start VM in Oracle Cloud
oracle_cloud_dbpaas: identityDomain: domain1 user: oracle_cloud_admin_user password: oracle_cloud_admin_password serviceName: Oracle Cloud Service Name state: start

# restart VM in Oracle Cloud
oracle_cloud_dbpaas: identityDomain: domain1 user: oracle_cloud_admin_user password: oracle_cloud_admin_password serviceName: Oracle Cloud Service Name state: restart

# Delete Cloud Service in Oracle Cloud (Service that you want to delete need to be stopped state)
oracle_cloud_dbpaas: identityDomain: domain1 user: oracle_cloud_admin_user password: oracle_cloud_admin_password serviceName: Oracle Cloud Service Name state: absent

# Scale Up/Down Cloud Service
oracle_cloud_dbpaas: identityDomain: domain1 user: oracle_cloud_admin_user password: oracle_cloud_admin_password serviceName: Oracle Cloud Service Name state: scale shape: oc5/oc6/oc1m/oc2m/oc3m..

# Create new Oracle Cloud Service
oracle_cloud_dbpaas: identityDomain: domain1 user: oracle_cloud_admin_user password: oracle_cloud_admin_password serviceName: Oracle Cloud Service Name state: present serviceName: "vm1" level: PAAS subscriptionType: MONTHLY version: "12.1.0.2" edition: EE description: test01 shape: oc3 vmPublicKey: "/Compute-domain/user@test.se/host"  usableStorage: "30" adminPassword: "Pa55_word" sid: "cdb1"pdbName: "pdb1" backupDestination: "NONE"

'''


try:
    import requests
    import time
except ImportError:
    requests_exists = False
else:
    requests_exists = True
"""Return jobStatus of job send to Oracle Cloud
"""
def dbpaasJobStatus(url, restSession):
    returnData = restSession.get(url)
    jobStatus = json.loads(returnData.text)
    
    return jobStatus['job_status']

def getServiceInformation(module, restSession):
    url = 'https://dbaas.oraclecloud.com/paas/service/dbcs/api/v1.1/instances/' + module.params["identityDomain"] + "/" + module.params["serviceName"]
    r = restSession.get(url)
    ServiceInformation = json.loads(r.text)
    
    return ServiceInformation

def dbpaasExists(module, restSession):
    url = 'https://dbaas.oraclecloud.com/paas/service/dbcs/api/v1.1/instances/' + module.params["identityDomain"] 
    r = restSession.get(url)
    parsed_json = json.loads(r.text)
    
    if any(x['service_name']==module.params["serviceName"] for x in parsed_json['services']):
        return True
    else:
        return False    

def scaleUpOrDownService(module, restSession):
    serviceInformation = getServiceInformation(module, restSession)
    
    if serviceInformation["shape"] != module.params["shape"]:
        payLoad = {}
        payLoad["shape"] = module.params["shape"] 
        url = 'https://dbaas.oraclecloud.com/paas/service/dbcs/api/v1.1/instances/' + module.params["identityDomain"] + "/" + module.params["serviceName"]
        returnHeaders = restSession.put(url, data=json.dumps(payLoad))
        if returnHeaders.status_code == 202:
            url = returnHeaders.headers['Location']
            while dbpaasJobStatus(url,restSession) in ('InProgress','WaitingOnResource',"Maintenance"):
                time.sleep(5)
            message = 'Service %s successfully scaled' % (module.params["serviceName"])
            module.exit_json(msg=message, changed=True)
        else:
            module.fail_json(msg="Wrong ResponseCode")
    elif serviceInformation["shape"] == module.params["shape"]:
        message = 'Service %s in same shape' % (module.params["serviceName"])
        module.exit_json(msg=message, changed=False)
        
        
def deleteService(module,restSession):

    serviceInformation = getServiceInformation(module, restSession)

    if dbpaasExists(module, restSession) and serviceInformation["status"] == "Stopped":
        if module.check_mode:
            message = 'Service %s successfully will be deleted' % (module.params["serviceName"])
            module.exit_json(msg=message, changed=True)
        else:
            url = 'https://dbaas.oraclecloud.com/paas/service/dbcs/api/v1.1/instances/' + module.params["identityDomain"] + "/" + module.params["serviceName"]
            returnHeaders = restSession.delete(url)
            if returnHeaders.status_code == 202:
                url = returnHeaders.headers['Location']
                while dbpaasJobStatus(url,restSession) in ('InProgress','WaitingOnResource',"Terminating"):
                    time.sleep(5)
                message = 'Service %s successfully will be deleted' % (module.params["serviceName"])
                module.exit_json(msg=message, changed=True)
            else:
                module.fail_json(msg="Wrong ResponseCode")
    elif not dbpaasExists(module, restSession):
        message = 'Service %s not exists' % (module.params["serviceName"])
        module.exit_json(msg=message, changed=False)
    else:
        module.fail_json(msg="Service in wrong state")


def createService(module,restSession):
    payload = {}
    payload["serviceName"] = module.params["serviceName"]
    payload["version"] = module.params["version"]
    payload["level"] = module.params["level"]
    payload["edition"] = module.params["edition"]
    payload["subscriptionType"] = module.params["subscriptionType"]
    payload["description"] = module.params["description"]
    payload["shape"] = module.params["shape"]
    payload["vmPublicKey"] = module.params["vmPublicKey"]
    payload["parameters"] = []
    if module.params["level"] == 'BASIC':
        payload["parameters"].append(dict(type="db",adminPassword=module.params["adminPassword"],usableStorage=module.params["usableStorage"],sid=module.params["sid"],pdb=module.params["pdbName"],failoverDatabase="no")) #,backupDestination=module.params["backupDestination"]))
    elif module.params["level"] == 'PAAS':
        payload["parameters"].append(dict(type="db",adminPassword=module.params["adminPassword"],usableStorage=module.params["usableStorage"],sid=module.params["sid"],pdb=module.params["pdbName"],failoverDatabase="no",backupDestination=module.params["backupDestination"]))

    
    if not dbpaasExists(module, restSession):
        if module.check_mode:
            message = 'Service %s successfully will be created' % (module.params["serviceName"])
            module.exit_json(msg=message, changed=True)
        else:
            url = 'https://dbaas.oraclecloud.com/paas/service/dbcs/api/v1.1/instances/' + module.params["identityDomain"]
            returnHeaders = restSession.post(url, data=json.dumps(payload))
            
            if returnHeaders.status_code == 202:
                url = returnHeaders.headers['Location']
                while dbpaasJobStatus(url,restSession) in ('InProgress','WaitingOnResource'):
                    time.sleep(5)
                message = 'Service %s successfully created' % (module.params["serviceName"])
                module.exit_json(msg=message, changed=True)                
            else:
                module.fail_json(msg="Wrong ResponseCode")
    else:
        message = 'Service %s already exists' % (module.params["serviceName"])
        module.exit_json(msg=message, changed=False)


def setChangeLifecycleState(module, restSession):
    payLoad = {}
    payLoad["lifecycleState"] = module.params["state"] 
    url = "https://dbaas.oraclecloud.com/paas/service/dbcs/api/v1.1/instances/" + module.params["identityDomain"]  + "/" + module.params["serviceName"]
    returnHeaders = restSession.post(url, data=json.dumps(payLoad))
    if returnHeaders.status_code == 202:
        url = returnHeaders.headers['Location']
        while dbpaasJobStatus(url,restSession) in ('InProgress','WaitingOnResource'):
            time.sleep(5)
    else:
        module.fail_json(msg="Wrong ResponseCode")

def changeLifecycleState(module, restSession):

    serviceInformation = getServiceInformation(module, restSession)

    if module.params["state"] in ("stop","restart"):
        if serviceInformation["status"] == "Running":
            if module.check_mode:
                message = 'Service %s successfully %s' % (module.params["serviceName"], module.params["state"])
                module.exit_json(msg=message, changed=True)
            else:
                setChangeLifecycleState(module, restSession)
                message = 'Service %s successfully %s' % (module.params["serviceName"], module.params["state"])
                module.exit_json(msg=message, changed=True)
        else:
            msg[0] = 'Service %s already %s' % (module.params["serviceName"], module.params["state"])
            module.exit_json(msg=message, changed=False)
    elif module.params["state"] in ("start"):
        if serviceInformation["status"] == "Stopped":
            if module.check_mode:
                message = 'Service %s successfully %s' % (module.params["serviceName"], module.params["state"])
                module.exit_json(message, changed=True)
            else:
                setChangeLifecycleState(module, restSession)
                message = 'Service %s successfully %s' % (module.params["serviceName"], module.params["state"])
                module.exit_json(msg=message, changed=True)
        else:
            msg = 'Service %s already %s' % (module.params["serviceName"], module.params["state"])
            module.exit_json(msg=message, changed=False)
    else:
        null

    
def main():
    msg = ['']
    module = AnsibleModule(
        argument_spec = dict(
            identityDomain          = dict(required=True),
            user                    = dict(required=True),
            password                = dict(required=True),
            state                   = dict(choices=["present","absent","start", "stop", "restart","scale"]),
            serviceName             = dict(required=True),
            level                   = dict(default="PAAS", choices=["PAAS","BASIC"]),
            subscriptionType        = dict(default="MONTHLY", choices=["MONTHLY","HOURLY"]),
            version                 = dict(default="12.1.0.2", choices=["12.1.0.2","11.2.0.4"]),
            edition                 = dict(default="EE", choices=["SE","EE","EE_HP","EE_EP"]),
            description             = dict(required=False),
            shape                   = dict(default="oc3", choices=["oc3","oc4","oc5","oc6","oc7","oc1m","oc2m","oc3m","oc4m","oc5m"]),
            vmPublicKey             = dict(required=False),
            vmPublicKeyText         = dict(required=False),
            parameters              = dict(required=False),
            usableStorage           = dict(required=False),
            adminPassword           = dict(required=False),
            sid                     = dict(required=False),
            pdbName                 = dict(required=False),
            #failoverDatabase        = 'no',
            backupDestination       = dict(default="NONE", choices=["NONE","DISK","BOTH"]),
            cloudStorageContainer   = dict(required=False),
            cloudStorageUser        = dict(required=False),
            cloudStoragePwd         = dict(required=False),
        ),
        supports_check_mode=True
    )
    
    restSession = requests.Session()
    restSession.auth = (module.params["user"], module.params["password"])
    restSession.headers.update({'X-ID-TENANT-NAME':module.params["identityDomain"] ,'Content-Type':'application/json'})
    
    if module.params["state"] == "present":
        createService(module, restSession)
    elif module.params["state"] == "absent":
        deleteService(module, restSession)
    elif module.params["state"] in ("start", "stop", "restart"):
        changeLifecycleState(module, restSession)
    elif module.params["state"] == "scale":
        scaleUpOrDownService(module, restSession)
        
    
from ansible.module_utils.basic import *
if __name__ == '__main__':
    main()
