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

DOCUMENTATION = '''


---
module: oracle_ovm
short_description: Manage VM:s in Oracle Virtual Manager 3.3
description:
    - Best way to use this module in task playbooks is to delegate_to: 127.0.0.1
version_added: "0.0.2"
options:
    ovmHostname:
        description:
            - Hostname/Ipadress to OVM host
        required: true
    ovmPort:
        description:
            - Port that OVM listen on
        required: true
        default: 7002
    user:
        description:
            - Username for logon to the OVM Web Gui
        required: true
        default: admin
    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"]
    vmName:
        description:
            - Name on VM:en in OVM
        required: true
    bootOrder
        description:
            - The ordered list of boot devices to use when booting the Vm.
        required: true
    cpuCount
       description:
            - The number of CPUs assigned to the Vm. The CPU count limit value will be increased if this value exceeds it. See #getCpuCountLimit().
        default:
    cpuCountLimit
       description:
            - The maximum number of CPUs that can be allocated to the Vm.
    cpuPriority
       description:
            - The CPU priority value for scheduling. It can be an integer from 1 to 100 with 100 being the highest priority and 1 the lowest.
    cpuUtilizationCap
       description:
            - The maximum CPU utilization value, which is used by the Xen credit scheduler. Valid values are from 0 to 100; default is 100. Any value less than 0 will not be allowed.
    highAvailability
       description:
            - The Vm high availability flag. If set to true, this indicates to the ServerPool master that the Vm should be restarted on a new Server in the ServerPool in the event that the Server that it is currently on stops.
    hugePagesEnabled
       description:
            - 
        required: true
        default:
    keymapName
       description:
            - The name of the keymap for the Vm. If a keymap has not been specified for the Vm, this will be the ServerPool's keymap. If the Vm is not associated with a ServerPool, this will be the OVM Manager's default keymap.
    memory
       description:
            - The amount of memory in MB which the Vm needs to run.
    memoryLimit
       description:
            - The maximum amount of memory in MB that the Vm will be allowed to use. This is the upper limit that the memory can be adjusted to when the Vm is running. By default, it is the same as the Vm memory
    networkInstallPath
       description:
            - The path to install PV (paravirtualized) guests over the network. The format of the path in the form of:
    osType
       description:
            - The operating system type in use by the Vm. The list of supported operating system types can be retrieved by calling OvmWsApi#vmGetSupportedOsTypes() or the corresponding REST methods.
    serverId
       description:
            - The id of the Server to which the Vm is assigned. This value is read-only. The Vm's Server id should be modified by calling 
    vmDomainType
       description:
            - The VmDomainType of the Vm.
    vmMouseType
       description:
            - The type of mouse in use by the Vm.
    vmStartPolicy
       description:
            - The policy that will be used to start the Vm. This value will override the value of the VmStartPolicy in the ServerPool to which the Vm belongs.
    vmIpAdress
       description: Ipadress to configure the vmtemplate
            - 
    vmGateway
       description:
            - Gate to configure the vmtemplate
    vmDnsServer
       description:
            - DNS Server to configure the vmtemplate
    vmRootPassword
       description:
            - Root password to configure the vmtemplate
    vmCloneDefinition
       description:
            - Which CloneDefinition that should be use
    vmTemplate
       description:
            - Name of VmTemplate that should be cloned
    serverPool
       description:
            - Which ServerPool


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

EXAMPLES = '''
# Create new VM
oracle_ovm: ovmHostname: 192.168.1.100 ovmPort: 7002 user: admin password: Welcome1 vmName: vm state: present vmCloneDefinition: custvmoel7 serverPool: srvpool vmIpAdress: 192.168.1.188 vmGateway: 192.168.1.1 vmDnsServer: 192.168.1.1 vmRootPassword: oracle vmCloneDefinition: custvmoel7 memory: 4096 memoryLimit: 4096 cpuCount: 4 cpuCountLimit: 4 vmTemplate: OVM_OL7U1_x86_64_PVHVM
'''


try:
    import requests
    requests.packages.urllib3.disable_warnings()
    import time
    import json
except ImportError:
    requests_exists = False
else:
    requests_exists = True
"""Return jobStatus of job send to Oracle Cloud
"""


def getVmInfoById(module,VmId, restSession):
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/{vmId}'.format(vmId=VmId, hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    vmInfo = restSession.get(url)
    vmInfoJson = json.loads(vmInfo.text)
    return vmInfoJson
    
def getVmInfoByName(module, restSession):
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/id'.format(hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    vmResults = restSession.get(url)
    
    for obj in vmResults.json():
    	if 'name' in obj.keys():
            if obj['name']==module.params['vmName']:
                return obj

def vmExists(module, restSession):
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/id'.format(hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    vmResults = restSession.get(url)
    
    for obj in vmResults.json():
    	if 'name' in obj.keys():
            if obj['name']==module.params['vmName']:
                return True

    return False            
            
def getVmCloneDefinitionIdFromName(module, restSession, name):

        url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/VmCloneDefinition/id'.format(hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
        vmResults = restSession.get(url)

        for obj in vmResults.json():
                if 'name' in obj.keys():
                         if obj['name']==name:
                                return obj['value']

def getIdFromName(restSession ,module,resource,obj_name):
        baseUri = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest'.format(hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
        uri=baseUri+'/'+resource+'/id'
        r=restSession.get(uri)
        for obj in r.json():
           if 'name' in obj.keys():
            if obj['name']==obj_name:
                return obj
        raise Exception('Failed to find id for {name}'.format(name=obj_name))                            
                
def waitForJob(joburi,restSession, module):
        while True:
            time.sleep(1)
            r=restSession.get(joburi)
            job=r.json()
            if job['summaryDone']:
                print '{name}: {runState}'.format(name=job['name'], runState=job['jobRunState'])
                if job['jobRunState'].upper() == 'FAILURE':
                    raise Exception('Job failed: {error}'.format(error=job['error']))
                elif job['jobRunState'].upper() == 'SUCCESS':
                    if 'resultId' in job:
                        return job['resultId']
                    break
                else:
                    break
                
def configVmAfterStartup(module, restSession):
    jsonVmInfo = getVmInfoByName(module, restSession)
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/{vmId}/sendMessage'.format(vmId=jsonVmInfo["value"], hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    payLoad =   [
                {
                 "key": "com.oracle.linux.network.hostname",
                 "value": module.params["vmName"] 
                 },
                {
                 "key": "com.oracle.linux.network.device.0",
                 "value": "eth0"
                 },
                {
                 "key": "com.oracle.linux.network.onboot.0",
                 "value": "yes"
                 },
                {
                 "key": "com.oracle.linux.network.ipaddr.0",
                 "value": module.params["vmIpAdress"]
                 },
                {
                 "key": "com.oracle.linux.network.netmask.0",
                 "value": "255.255.255.0"
                 },
                {
                 "key": "com.oracle.linux.network.gateway.0",
                 "value": module.params["vmGateway"]
                 },
                {
                 "key": "com.oracle.linux.network.dns-servers.0",
                 "value": module.params["vmDnsServer"]
                 },
                {
                 "key": "com.oracle.linux.selinux.mode",
                 "value": "disabled"
                 },
                ]

    result  = restSession.put(url,data=json.dumps(payLoad))
    jsonResultModifyVm = json.loads(result.text)
    waitModifyJob = waitForJob(jsonResultModifyVm['id']['uri'],restSession, module)
    # Sleep for 10 sec so Oracle Temple can configure network
    time.sleep(10)

    payLoad = [
                    {
                     "key": "com.oracle.linux.root-password",
                     "value": module.params["vmRootPassword"]
                     },                  
                ]

    result  = restSession.put(url,data=json.dumps(payLoad))
    
    jsonResultModifyVm = json.loads(result.text)
    waitModifyJob = waitForJob(jsonResultModifyVm['id']['uri'],restSession, module)

def getJobInfo(restSession, jobID, module):
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Job/{jobId}'.format(jobId=jobID,hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    
    resultJobInfo = restSession.get(url)
    return json.loads(resultJobInfo.text)
    
def startVm(restSession,module):
    vmInfo = getVmInfoById(module,getVmInfoByName(module, restSession)['value'], restSession)
    
    if vmInfo["vmRunState"] == 'STOPPED': 
        url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/{vmId}/start'.format(vmId=vmInfo['id']['value'], hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
        startResult  = restSession.put(url)
        jsonResultStart = json.loads(startResult.text)
        waitModifyJob = waitForJob(jsonResultStart['id']['uri'],restSession, module)
        
def stopVm(restSession,module):
    vmInfo = getVmInfoById(module,getVmInfoByName(module, restSession)['value'], restSession)
    
    if vmInfo["vmRunState"] == 'RUNNING': 
        url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/{vmId}/stop'.format(vmId=vmInfo['id']['value'], hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
        startResult  = restSession.put(url)
        jsonResultStart = json.loads(startResult.text)
        waitModifyJob = waitForJob(jsonResultStart['id']['uri'],restSession, module)        
                
def getVirtualDiskInfoFromvmDiskMappingId(restSession, module, VmDiskMappingId):
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/VmDiskMapping/{vmDiskMappingId}/VirtualDisk'.format(vmDiskMappingId=VmDiskMappingId,hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    virtualDiskInfo = restSession.get(url)
    
    return json.loads(virtualDiskInfo.text)

def getVmDiskMappingInfo(restSession, module, vmDiskMappingId):
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/VmDiskMapping/{VmDiskMappingId}'.format(VmDiskMappingId=vmDiskMappingId, hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    
    vmDiskMappingResult = restSession.get(url)
    vmDiskMappingJson = json.loads(vmDiskMappingResult.text)
    
    return vmDiskMappingJson

def getVirtualDiskByIdInfo(restSession, module, virtualDiskId):
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/VirtualDisk/{VirtualDiskId}'.format(VirtualDiskId=virtualDiskId, hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    
    virtualDiskResult = restSession.get(url)
    virtualDiskJson = json.loads(virtualDiskResult.text)
    
    return virtualDiskJson

def delVmDiskMapping(restSession, module, vmDiskMappingId):
    vmInfo = getVmInfoById(module,getVmInfoByName(module, restSession)['value'], restSession)
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/{VmId}/VmDiskMapping/{VmDiskMappingId}'.format(VmId=vmInfo['id']['value'], VmDiskMappingId=vmDiskMappingId, hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    
    delVmDiskMappingResult = restSession.delete(url)
    delVmDiskMappingJson = json.loads(delVmDiskMappingResult.text)
    
    waitModifyJob = waitForJob(delVmDiskMappingJson['id']['uri'],restSession, module)        

def delVirtualDisk(restSession, module, virtualDisk):
    #virtualDiskInfo = getVirtualDiskByIdInfo(restSession, module, virtualDiskId)
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Repository/{repositoryId}/VirtualDisk/{virtualDiskId}'.format(repositoryId=virtualDisk['repositoryId']['value'], virtualDiskId=virtualDisk['id']['value'], hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    #module.exit_json(msg=url, changed=True)
    delVirtualDiskResult = restSession.delete(url)
    #module.exit_json(msg=delVirtualDiskResult.text, changed=True)
    delVirtualDiskJson = json.loads(delVirtualDiskResult.text)
    
    waitModifyJob = waitForJob(delVirtualDiskJson['id']['uri'],restSession, module)        

def setNameForVirtualDisk(restSession, module, newName, virtualDiskInfo):
    data = {"id":{
                  "type":"com.oracle.ovm.mgr.ws.model.VirtualDisk",
                  "value": virtualDiskInfo['id']['value']
                  },
            "name": newName,
            "generation": virtualDiskInfo['generation']
            }
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/VirtualDisk/{virtualDiskId}'.format(virtualDiskId=virtualDiskInfo['id']['value'], hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    resultOfRenameOfdisk  = restSession.put(url,data=json.dumps(data))
    jsonData = json.loads(resultOfRenameOfdisk.text)
    waitJob = waitForJob(jsonData['id']['uri'],restSession, module)
    
def cloneVm(module,restSession):
    
    if not vmExists(module, restSession):
        url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/{vmId}/clone'.format(vmId=getIdFromName(restSession ,module,'Vm',module.params['vmTemplate'])['value'] ,hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    #    module.exit_json(msg=json.dumps(url), status=False)
        payLoad={
               'vmCloneDefinitionId': getVmCloneDefinitionIdFromName(module, restSession, module.params['vmCloneDefinition']), 
               'serverPoolId': getIdFromName(restSession, module, 'ServerPool', module.params['serverPool'])['value'],
               'createTemplate': False
            } 
    #    module.exit_json(msg=json.dumps(payLoad), status=False)
        resultOfCloneRequest  = restSession.put(url, params=payLoad)
        jsonData = json.loads(resultOfCloneRequest.text)
        # Get JobId from jsonData and get newvmid from jobinfo
        waitJob = waitForJob(jsonData['id']['uri'],restSession, module)
        
        jobInfo = getJobInfo(restSession, jsonData['id']['value'], module)
    #    module.exit_json(msg=json.dumps(jobInfo), status=False)
        newVmId = jobInfo['resultId']['value']
        
        configVm(module, restSession, newVmId)
        
        jsonVmInfo1 = getVmInfoByName(module, restSession)
        jsonVmInfo = getVmInfoById(module,jsonVmInfo1['value'], restSession)
    #    module.exit_json(msg=json.dumps(jsonVmInfo), status=False)
        
        for obj in jsonVmInfo['vmDiskMappingIds']:
            virtualDiskInfo = getVirtualDiskInfoFromvmDiskMappingId(restSession, module, obj['value'])
            kk = setNameForVirtualDisk(restSession, module, 'system_' + module.params['vmName'], virtualDiskInfo)
        
        startVm(restSession, module)
        
        configVmAfterStartup(module, restSession)
        module.exit_json(msg='vm exists', changed=True)
    else:
        module.exit_json(msg='vm exists', changed=False)

    
def configVm(module, restSession,newVmId):
    jsonVmInfo = getVmInfoById(module,newVmId, restSession)
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/{vmId}'.format(vmId=jsonVmInfo['id']['value'], hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
       
    data = {"id":{"type":"com.oracle.ovm.mgr.ws.model.Vm",
                  "value": jsonVmInfo['id']['value']
                 },
            "memory": module.params["memory"],
            "memoryLimit": module.params["memoryLimit"],
            "name": module.params["vmName"],
            "description": module.params["description"],
            "generation": jsonVmInfo['generation'],
            "cpuCount": module.params["cpuCount"],
            "cpuCountLimit": module.params["cpuCountLimit"],
            "vmDomainType": module.params["vmDomainType"],
           }
    
    resultModifyVm  = restSession.put(url, data=json.dumps(data), params={"vmId": jsonVmInfo['id']['value']})
    jsonResultModifyVm = json.loads(resultModifyVm.text)
    waitModifyJob = waitForJob(jsonResultModifyVm['id']['uri'],restSession, module)

def createVm(module, restSession):

    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm'.format(hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
       
    data = {
            "repositoryId": {"type": "com.oracle.ovm.mgr.ws.model.Repository",
                             "value": "0004fb00000300008564bbdbe309abfb"},
            "serverPoolId": {"type": "com.oracle.ovm.mgr.ws.model.ServerPool",
                             "value":"0004fb000002000047a9ab9d92c370b8"},
            "vmDomainType": "XEN_HVM"
           }
    
    resultCreateVm  = restSession.post(url, data=json.dumps(data))
    jsonResultCreateVm = json.loads(resultCreateVm.text)
    #module.fail_json(msg=resultCreateVm.status_code)
    waitModifyJob = waitForJob(jsonResultCreateVm['id']['uri'],restSession, module)

    jobInfo = getJobInfo(restSession, jsonResultCreateVm['id']['value'], module)
    #module.exit_json(msg=json.dumps(jobInfo), status=False)
    newVmId = jobInfo['resultId']['value']

    configVm(module, restSession, newVmId)

def deleteVm(module, restSession):
    
    if vmExists(module, restSession):
        vmInfo = getVmInfoById(module,getVmInfoByName(module, restSession)['value'],restSession)
        #module.exit_json(msg=json.dumps(vmInfo), changed=True)
        if vmInfo['vmRunState'] == 'STOPPED':
            # First Remove DiskMappingToVm
            # Second Remove VirtalDisk
            for obj in vmInfo["vmDiskMappingIds"]:
                vmDiskInfo = getVirtualDiskInfoFromvmDiskMappingId(restSession, module, obj['value'])
                #virtualDiskIds.append(vmDiskInfo)
                delVmDiskMapping(restSession, module, obj['value'])
                delVirtualDisk(restSession, module, vmDiskInfo)
                
            # When Diskmapping & Disk are removed we can delete vm
            
            url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Vm/{vmId}'.format(vmId=vmInfo['id']['value'], hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
            
            resultDeleteVm = restSession.delete(url)
            jsonresultDeleteVm = json.loads(resultDeleteVm.text)
            waitModifyJob = waitForJob(jsonresultDeleteVm['id']['uri'],restSession, module)
            module.exit_json(msg="Vm deleted", changed=True)
        else:
            module.exit_json(msg="Vm not stopped", changed=False)
    else:
        module.exit_json(msg="Vm not exists", changed=False)
def testAuth(restSession, module):
    url = 'https://{hostName}:{portNr}/ovm/core/wsapi/rest/Job'.format(hostName=module.params["ovmHostname"],portNr=module.params["ovmPort"])
    resultPing = restSession.get(url)
    if resultPing.status_code == 200:
        return
    elif resultPing.status_code == 401:
        module.fail_json(msg="Wrong username/password")
    else:
        module.fail_json(msg=resultPing.status_code)
def main():
    msg = ['']
    module = AnsibleModule(
        argument_spec = dict(
            ovmHostname		    = dict(required=True),
            ovmPort		    = dict(required=True),
            user                    = dict(required=True),
            password                = dict(required=True),
            state                   = dict(choices=["present","absent","start", "stop", "restart","scale","ping","createvm"]),
            vmName		    = dict(required=True),
            description             = dict(required=False),
            bootOrder               = dict(required=False),
            cpuCount                = dict(required=False),
            cpuCountLimit           = dict(required=False),
            cpuPriority             = dict(required=False),
            cpuUtilizationCap       = dict(required=False),
            highAvailability        = dict(required=False),
            hugePagesEnabled        = dict(required=False),
            keymapName              = dict(required=False),
            memory                  = dict(required=False),
            memoryLimit             = dict(required=False),
            networkInstallPath      = dict(required=False),
            osType                  = dict(required=False),
            serverId                = dict(required=False),
            vmDomainType            = dict(required=False),
            vmMouseType             = dict(required=False),
            vmStartPolicy           = dict(required=False),
            vmIpAdress              = dict(required=False),
            vmGateway               = dict(required=False),
            vmDnsServer             = dict(required=False),
            vmRootPassword          = dict(required=False),
            vmCloneDefinition       = dict(required=False),
            vmTemplate              = dict(required=False),
            serverPool              = dict(required=False),
        ),
        supports_check_mode=False
    )
    
    restSession = requests.Session()
    restSession.auth = (module.params["user"], module.params["password"])
    restSession.headers.update({'Accept': 'application/json', 'Content-Type': 'application/json'})
    restSession.verify=False
    
    if module.params["state"] == "present":
        cloneVm(module, restSession)
    if module.params["state"] == "absent":
        deleteVm(module, restSession)
    elif module.params["state"] == "start":
        startVm(restSession,module)
        module.exit_json(msg='vm started', changed=True)
    elif module.params["state"] == "stop":
        stopVm(restSession,module)
        module.exit_json(msg='vm stopped', changed=True)
    elif module.params["state"] == "ping":
        testAuth(restSession,module)
        module.exit_json(msg='pong', changed=False)
    elif module.params["state"] == "createvm":
        createVm(module, restSession)
        module.exit_json(msg='CreateVm', changed=True)
    else:
        module.fail_json(msg="Wrong state")
    
from ansible.module_utils.basic import *
if __name__ == '__main__':
    main()

