#!/usr/bin/env python
#
# VM Backup extension
#
# Copyright 2014 Microsoft Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import array
import base64
import os
import os.path
import re
import json
import string
import subprocess
import sys
import time
import shlex
import traceback
import datetime
import random
try:
    import ConfigParser as ConfigParsers
except ImportError:
    import configparser as ConfigParsers
from threading import Thread
from time import sleep
from os.path import join
from mounts import Mounts
from mounts import Mount
from patch import *
from fsfreezer import FsFreezer
from common import CommonVariables
from parameterparser import ParameterParser
from Utils import HandlerUtil
from Utils import SizeCalculation
from Utils import Status
from freezesnapshotter import FreezeSnapshotter
from backuplogger import Backuplogger
from blobwriter import BlobWriter
from taskidentity import TaskIdentity
from MachineIdentity import MachineIdentity
import ExtensionErrorCodeHelper
from PluginHost import PluginHost
from PluginHost import PluginHostResult
import platform
from workloadPatch import WorkloadPatch

#Main function is the only entrence to this extension handler

def main():
    global MyPatching,backup_logger,hutil,run_result,run_status,error_msg,freezer,freeze_result,snapshot_info_array,total_used_size,size_calculation_failed, patch_class_name, orig_distro, configSeqNo
    try:
        run_result = CommonVariables.success
        run_status = 'success'
        error_msg = ''
        freeze_result = None
        snapshot_info_array = None
        total_used_size = 0
        size_calculation_failed = False
        HandlerUtil.waagent.LoggerInit('/dev/console','/dev/stdout')
        hutil = HandlerUtil.HandlerUtility(HandlerUtil.waagent.Log, HandlerUtil.waagent.Error, CommonVariables.extension_name)
        backup_logger = Backuplogger(hutil)
        MyPatching, patch_class_name, orig_distro = GetMyPatching(backup_logger)
        hutil.patching = MyPatching
        configSeqNo = -1
        for a in sys.argv[1:]:
            if re.match("^([-/]*)(disable)", a):
                disable()
            elif re.match("^([-/]*)(uninstall)", a):
                uninstall()
            elif re.match("^([-/]*)(install)", a):
                install()
            elif re.match("^([-/]*)(enable)", a):
                enable()
            elif re.match("^([-/]*)(update)", a):
                update()
            elif re.match("^([-/]*)(daemon)", a):
                daemon()
            elif re.match("^([-/]*)(seqNo:)", a):
                try:
                    configSeqNo = int(a.split(':')[1])
                except:
                    configSeqNo = -1
    except Exception as e:
        sys.exit(0)

def install():
    global hutil,configSeqNo
    hutil.do_parse_context('Install', configSeqNo)
    hutil.do_exit(0, 'Install','success','0', 'Install Succeeded')

def status_report_to_file(file_report_msg):
    global backup_logger,hutil
    hutil.write_to_status_file(file_report_msg)
    backup_logger.log("file status report message:",True)
    backup_logger.log(file_report_msg,True)

def status_report_to_blob(blob_report_msg):
    global backup_logger,hutil,para_parser
    UploadStatusAndLog = hutil.get_strvalue_from_configfile('UploadStatusAndLog','True')        
    if(UploadStatusAndLog == None or UploadStatusAndLog == 'True'):
        try:
            if(para_parser is not None and para_parser.statusBlobUri is not None and para_parser.statusBlobUri != ""):
                blobWriter = BlobWriter(hutil)
                if(blob_report_msg is not None):
                    blobWriter.WriteBlob(blob_report_msg,para_parser.statusBlobUri)
                    backup_logger.log("blob status report message:",True)
                    backup_logger.log(blob_report_msg,True)
                else:
                    backup_logger.log("blob_report_msg is none",True)
        except Exception as e:
            err_msg='cannot write status to the status blob'+traceback.format_exc()
            backup_logger.log(err_msg, True, 'Warning')

def get_status_to_report(status, status_code, message, snapshot_info = None):
    global MyPatching,backup_logger,hutil,para_parser,total_used_size,size_calculation_failed
    blob_report_msg = None
    file_report_msg = None
    try:
        if total_used_size == -1 :
            sizeCalculation = SizeCalculation.SizeCalculation(patching = MyPatching , logger = backup_logger , para_parser = para_parser)
            total_used_size,size_calculation_failed = sizeCalculation.get_total_used_size()
            number_of_blobs = len(para_parser.includeLunList)
            maximum_possible_size = number_of_blobs * 1099511627776
            if(total_used_size>maximum_possible_size):
                total_used_size = maximum_possible_size
            backup_logger.log("Assertion Check, total size : {0} ,maximum_possible_size : {1}".format(total_used_size,maximum_possible_size),True)
        if(para_parser is not None):
            blob_report_msg, file_report_msg = hutil.do_status_report(operation='Enable',status=status,\
                    status_code=str(status_code),\
                    message=message,\
                    taskId=para_parser.taskId,\
                    commandStartTimeUTCTicks=para_parser.commandStartTimeUTCTicks,\
                    snapshot_info=snapshot_info,\
                    total_size = total_used_size,\
                    failure_flag = size_calculation_failed)
    except Exception as e:
        err_msg='cannot get status report parameters , Exception %s, stack trace: %s' % (str(e), traceback.format_exc())
        backup_logger.log(err_msg, True, 'Warning')
    return blob_report_msg, file_report_msg

def exit_with_commit_log(status,result,error_msg, para_parser):
    global backup_logger
    backup_logger.log(error_msg, True, 'Error')
    if(para_parser is not None and para_parser.logsBlobUri is not None and para_parser.logsBlobUri != ""):
        backup_logger.commit(para_parser.logsBlobUri)
    blob_report_msg, file_report_msg = get_status_to_report(status, result, error_msg, None)
    status_report_to_file(file_report_msg)
    status_report_to_blob(blob_report_msg)
    sys.exit(0)

def exit_if_same_taskId(taskId):
    global backup_logger,hutil,para_parser
    trans_report_msg = None
    taskIdentity = TaskIdentity()
    last_taskId = taskIdentity.stored_identity()
    if(taskId == last_taskId):
        backup_logger.log("TaskId is same as last, so skip with Processed Status, current:" + str(taskId) + "== last:" + str(last_taskId), True)
        status=CommonVariables.status_success 
        hutil.SetExtErrorCode(ExtensionErrorCodeHelper.ExtensionErrorCodeEnum.SuccessAlreadyProcessedInput)
        status_code=CommonVariables.SuccessAlreadyProcessedInput
        message='TaskId AlreadyProcessed nothing to do'
        backup_logger.log(message, True)
        sys.exit(0)

def freeze_snapshot(timeout):
    try:
        global hutil,backup_logger,run_result,run_status,error_msg,freezer,freeze_result,para_parser,snapshot_info_array,g_fsfreeze_on, workload_patch
        canTakeCrashConsistentSnapshot = can_take_crash_consistent_snapshot(para_parser)
        freeze_snap_shotter = FreezeSnapshotter(backup_logger, hutil, freezer, g_fsfreeze_on, para_parser, canTakeCrashConsistentSnapshot)
        backup_logger.log("Calling do snapshot method", True, 'Info')
        run_result, run_status, snapshot_info_array = freeze_snap_shotter.doFreezeSnapshot()
        if (canTakeCrashConsistentSnapshot == True and run_result != CommonVariables.success and run_result != CommonVariables.success_appconsistent):
            if (snapshot_info_array is not None and snapshot_info_array !=[] and check_snapshot_array_fail() == False and len(snapshot_info_array) == 1):