# -*- coding: utf-8 -*-
"""
Python Class Library for the HPE SimpliVity Rest API

Copyright (c) 2019 Hewlett Packard Enterprise

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    https://www.gnu.org/licenses/gpl-3.0.en.html

Requirements:
    requests
    datetime

"""

import requests
import datetime

DEBUG = False


class SimpliVity:
    """SvtC
    class SimpliVity
    routines for the SimpliVity RestAPI
    """
    def __init__(self, url):
        self.url = url          # base URL
        self.access_token = ''    # session access token

    def connect(self, hms_username, hms_password):
        response = requests.post(self.url+'oauth/token', auth=('simplivity', ''), verify=False, data={
                'grant_type': 'password',
                'username': hms_username,
                'password': hms_password})
        if(response.status_code == 200):
            self.access_token = response.json()['access_token']
            return response
        else:
            raise SvtError('SimpliVity.connect', response.status_code, response.json())

    def GetTask(self, task_id):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        response = requests.get(self.url+'tasks/'+task_id, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetTask', response.status_code, response.json())

    """ Certificate Operations ########################################################################"""

    def GetCertificate(self):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.9+json'}
        response = requests.get(self.url+'certificates', verify=False, headers=headers).json()
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetCertificicate', response.status_code, response.json())

    def PostCertificate(self, certificate):
        print("PostCertificate is not yet implemented")
        return 0

    def DeleteCertificate(self, certid):
        print("DeleteCertificate is not yet implemented")
        return 0

    """ Host Operations ########################################################################"""

    def GetHost(self, name=None):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        if name:
            url = self.url+'hosts?show_optional_fields=true&name='+name
        else:
            url = self.url+'hosts'
        response = requests.get(url, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetHost', response.status_code, response.json())

    def GetHostId(self, name):
        x = self.GetHost(name)['hosts']
        for i in range(len(x)):
            z = x[i]
            if z['state'] == 'ALIVE':
                return z['id']
        return 0

    def GetHostMetrics(self, name, timerange='43200', resolution='Minute', timeOffset='0'):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        url = self.url + 'hosts/'+self.GetHostId(name)+'/metrics?range='+timerange + \
            '&resolution='+resolution+'&offset='+timeOffset+'&show_optional_fields=true'
        response = requests.get(url, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetHostMetric', response.status_code, response.json())

    def GetHostCapacity(self, name, timerange='43200', resolution='Minute', timeOffset='0'):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        url = self.url + 'hosts/'+self.GetHostId(name)+'/capacity?range='+timerange + \
            '&resolution='+resolution+'&offset='+timeOffset+'&show_optional_fields=true'
        response = requests.get(url, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetHostCapacity', response.status_code, response.json())

    def GetHostHardware(self, name):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        url = self.url + 'hosts/'+self.GetHostId(name)+'/hardware'
        response = requests.get(url, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetHostHardware', response.status_code, response.json())

    def ShutdownOVC(self, host_id, ha_wait=True):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        response = requests.post(self.url+'hosts/'+host_id+'/shutdown_virtual_controller',
                                 verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.ShutdownOVC', response.status_code, response.json())

    def CancelShutdownOVC(self, host_id):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        response = requests.post(self.url+'hosts/'+host_id +
                                 '/virtual_controller_shutdown_status', verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.CancelShutdownOVC', response.status_code, response.json())

    def GetOVCShutdownStatus(self, host_id):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        response = requests.post(self.url+'hosts/'+host_id +
                                 '/cancel_virtual_controller_shutdown', verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetOVCShutdownStatus', response.status_code, response.json())

    """ VM Operations #######################################################################"""

    def GetVM(self, vmname=None):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        if vmname:
            url = self.url+'virtual_machines?show_optional_fields=true&name='+vmname
        else:
            url = self.url+'virtual_machines?show_optional_field=true'
        response = requests.get(url, verify=False, headers=headers)
        tmp = response.json()
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetVM', response.status_code, response.json())

    def GetVMId(self, vmname):
        x = self.GetVM(vmname)['virtual_machines']
        for z in x:
            if z['state'] == 'ALIVE':
                return z['id']
        raise SvtError('SimpliVity.GetVMId', 555, 'VM '+vmname+' is not ALIVE')

    def GetVMMetric(self, vmname, timerange='43200', resolution='Minute', timeOffset='0'):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        vmid = self.GetVMId(vmname)
        url = self.url + 'virtual_machines/' + \
            self.GetVMId(vmname)+'/metrics?range='+timerange+'&resolution=' + \
            resolution+'&offset='+timeOffset+'&show_optional_fields=true'
        response = requests.get(url, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetVMMetric', response.status_code, response.json())

    def SetVMPolicy(self, vmname, policyname):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.9+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.9+json"
        body = '{"virtual_machine_id":"'+self.GetVMId(vmname)+'",\
                 "policy_id":"'+self.GetPolicyId(policyname)+'"}'
        response = requests.post(self.url+'virtual_machinces/set_policy', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetVMPolicy', response.status_code, response.json())

    def VMmove(self, name, destination):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        body = '{"virtual_machine_name":"'+name+'",\
                 "destination_datastore_id":"'+self.GetDataStoreId(destination)+'"}'
        response = requests.post(self.url+'virtual_machines/'+self.GetVMId(name) +
                                 '/move', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.VMmove', response.status_code, response.json())

    def VMclone(self, name, cloneName, appConsistent=False, consistencyType='None'):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        body = '{"virtual_machine_name":"' + cloneName + '",\
                 "app_consistent":"' + appConsistent + '",\
                 "consistency_type":"' + consistencyType + '"}'
        response = requests.post(self.url+'virtual_machines/'+self.GetVMId(name) +
                                 '/clone', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.VMclone', response.status_code, response.json())

    def VMcredentials(self, name, username, password):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.9+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.9+json"
        body = '{"guest_username":"' + username + '",\
                 "guest_password":"' + password + '"}'
        response = requests.post(self.url+'virtual_machines/'+self.GetVMId(name) +
                                 '/validate_backup_credentials', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.VMcredentials', response.status_code, response.json())

    """ DataStore Operations ################################################################"""

    def GetDataStore(self, name=None):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        if name:
            url = self.url+'datastores?show_optional_fields=true&name='+name
        else:
            url = self.url+'datastores?show_optional_field=true'
        response = requests.get(url, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetDataStore', response.status_code, response.json())

    def GetDataStoreId(self, name):
        return self.GetDataStore(name)['datastores'][0]['id']

    def NewDataStore(self, name, cluster, policy, size):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        size = size * 1024 * 1024 * 1024
        body = '{"name":"'+name+'",\
                "omnistack_cluster_id":"'+self.GetClusterId(cluster)+'",\
                "policy_id":"'+self.GetPolicyId(policy)+'",\
                "size":"'+str(size)+'"}'
        response = requests.post(self.url+'datastores', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.NewDataStore', response.status_code, response.json())

    def RemoveDataStore(self, name):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        response = requests.delete(self.url+'datastores/'+self.GetDataStoreId(name), verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.RemoveDataStore', response.status_code, response.json())

    def ResizeDataStore(self, name, size):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        body = '{"size":"'+str(size*1024*1024*1024)+'"}'
        response = requests.post(self.url+'datastores/'+self.GetDataStoreId(name) +
                                 '/resize', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.ResizeDataStore', response.status_code, response.json())

    def SetDataStorePolicy(self, name, policy):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        body = '{"policy_id":"' + self.GetPolicyId(policy) + '"}'
        respones = requests.post(self.url+'datastores/'+self.GetDataStoreId(name) +
                                 '/set_policy', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.SetDataStorePolicy', response.status_code, response.json())

    """ Cluster Operations ###################################################################"""

    def GetCluster(self, name=None):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        if name:
            url = self.url+'omnistack_clusters?show_optional_fields=true&name='+name
        else:
            url = self.url+'omnistack_clusters?show_optional_fields=true'
        response = (requests.get(url, verify=False, headers=headers))
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetCluster', response.status_code, response.json())

    def GetClusterId(self, name):
        return self.GetCluster(name)['omnistack_clusters'][0]['id']

    def GetClusterMetric(self, name, timerange='43200', resolution='Minute', timeOffset='0'):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        url = self.url + 'omnistack_clusters/' + \
            self.GetClusterId(name)+'/metrics?range='+timerange+'&resolution=' + \
            resolution+'&offset='+timeOffset+'&show_optional_fields=true'
        response = requests.get(url, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetClusterMetric', response.status_code, response.json())

    def GetClusterThroughput(self):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        response = requests.get(self.url+'omnistack_clusters/throughput', verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetClusterThroughput', response.status_code, response.json())

    def SetClusterTimeZone(self, name, timezone):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        body = '{"time_zone":"' + timezone + '"}'
        response = requests.get(self.url+'omnistack_clusters/set_time_zone', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.SetClusterTimeZone', response.status_code, response.json())

    """ Backup & Restore #####################################################################"""

    def GetBackups(self, past_hours=None, vmname=None, listOffset=None, listLimit=None):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        url = self.url+'backups?show_optional_fields=true'
        if past_hours:
            createdAfter = (datetime.datetime.now() - datetime.timedelta(hours=past_hours)
                            ).isoformat(timespec='seconds')+'Z'
            url = url + '&created_after='+createdAfter
        if vmname:
            url = url + '&virtual_machine_name='+vmname
        if listOffset:
            url = url + '&offset='+listOffset
        if listLimit:
            url = url + '&limit='+listLimit
        response = requests.get(url, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetBackups', response.status_code, response.json())

    def GetVMLastBackup(self, vmname):
        bck = self.GetBackups(vmname=vmname)['backups']
        return (sorted(bck, key=lambda bck: bck['created_at'], reverse=True))[0]

    def BackupVM(self, name, destination, retention=0, appConsistent=False, consistencyType='None'):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        bname = name + '_' + str(datetime.datetime.now().isoformat(timespec='seconds'))
        body = '{\
                "backup_name":"' + bname + '",\
                "destination_id":"' + self.GetClusterId(destination) + '",\
                "app_consistent":"' + appConsistent + ',\
                "consistency_type":"' + consistencyType + '",\
                "retention":' + retention + '}'
        response = requests.post(self.url+'virtual_machines/'+self.GetVMId(name) +
                                 '/backup', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.BackupVM', response.status_code, response.json())

    def RestoreVM(self, vmname, destination, bckid, restore=False):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.5+json"
        if restore:
            cmd = self.url+'backups/'+bckid+'/restore?restore_original=true'
            response = requests.post(cmd, verify=False, headers=headers)
        else:
            body = '{\
                    "virtual_machine_name":"' + vmname + '",\
                    "dastastore_id":"' + self.GetDataStoreId(destination)+'}'
            response = requests.post(self.url+'backups/'+bckid+'/restore?restore_original=false',
                                     data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.RestoreVM', response.status_code, response.json())

    def GetBackupId(self, vmname, bckname):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        response = requests.get(self.url+'backups?show_optional_fields=true&virtual_machine_name=' +
                                vmname+'&name='+bckname, verify=False, headers=headers)
        if(response.status_code == 200):
            x = response.json()
            return x['backups'][0]['id']
        else:
            raise SvtError('SimpliVity.GetBackupId', response.status_code, response.json())

    def DeleteBackup(self, bckid):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        body = '{\
                "backup_id":["' + bckid + '"]}'
        response = requests.post(self.url+'backups/delete', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.DeleteBackup', response.status_code, response.json())

    """ Policy Operations ####################################################################"""

    def GetPolicy(self, policyname=None):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        if policyname:
            url = self.url+'policies?show_optional_fields=true&name='+policyname
        else:
            url = self.url+'policies?show_optional_field=true'
        response = requests.get(url, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.GetPolicy', response.status_code, response.json())

    def GetPolicyId(self, policyname):
        return(self.GetPolicy(policyname)['policies'][0]['id'])

    def DefinePolicy(self, policyname):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        body = '{\
               "name":"' + policyname + '"\
              }'
        response = requests.post(self.url+'policies', data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.DefinePolicy', response.status_code, response.json())

    def DeletePolicy(self, policyname):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        response = requests.delete(self.url+'policies/'+self.GetPolicyId(policyname), verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.DeletePolicy', response.status_code, response.json())

    def AddPolicyRule(self, policy_id, destination, frequency=1440, retention=1440, days='All',
                      endTime='00:00', startTime='00:00', replace=False, appConsistent="false", consistencyType='NONE'):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        headers['Content-Type'] = "application/vnd.simplivity.v1.7+json"
        body = '[ \n {\n\
    "destination_id": "' + destination + '",\n\
    "frequency": ' + str(frequency) + ',\n\
    "retention": ' + str(retention) + ',\n\
    "days": "' + days + '",\n\
    "start_time": "' + startTime + '",\n\
    "end_time": "' + endTime + '",\n\
    "application_consistent": ' + str(appConsistent) + ',\n\
    "consistency_type": "' + consistencyType + '"\n\
 }\n]'
        response = requests.post(self.url+'policies/'+policy_id+'/rules?replace_all_rules=' +
                                 str(replace), data=body, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.AddPolicyRule', response.status_code, response.json())

    def DeletePolicyRule(self, policy_id, rule_id):
        headers = {'Authorization':  'Bearer ' + self.access_token, 'Accept': 'application/vnd.simplivity.v1.7+json'}
        response = requests.delete(self.url+'policies/'+policy_id+'/rules/'+rule_id, verify=False, headers=headers)
        if(response.status_code == 200):
            return response.json()
        else:
            raise SvtError('SimpliVity.RestoreVM', response.status_code, response.json())


class SvtError(Exception):
    """ Base class for SimpliVityClass Errors """
    def __init__(self, expression, status, message):
        self.expression = expression
        self.message = message
        self.status = status
