#!/usr/bin/python3
# Copyright (c) 2009-2018 Canonical Ltd
#
# AUTHOR:
# Michael Vogt <mvo@ubuntu.com>
# Balint Reczey <rbalint@ubuntu.com>
#
# unattended-upgrade-shutdown - helper that checks if a
# unattended-upgrade is in progress and waits until it exists
#
# This file is part of unattended-upgrades
#
# unattended-upgrades 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 2 of the License, or (at
# your option) any later version.
#
# unattended-upgrades 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.
#
# You should have received a copy of the GNU General Public License
# along with unattended-upgrades; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

from cmath import atan
from time import timezone
import dbus
import signal
import datetime
import logging
import logging.config
import os.path
import os
import sys
import getpass
import configparser
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib
from optparse import OptionParser, Values
from threading import Event 
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.executors.pool import ThreadPoolExecutor,ProcessPoolExecutor
from apt.progress.base import AcquireProgress
import random
import subprocess
# from pytz import utc
from gettext import gettext as _
import gettext
import apt
import apt_pkg
import json
#deprecated
UNATTENDED_UPGRADE_CONFIG_FILE_PATH="/var/lib/unattended-upgrades/unattended-upgrade.conf"
UNATTENDED_UPGRADE_TIMESTAMP = "/var/lib/unattended-upgrades/unattended-upgrades-timestamp"


UNATTENDED_UPGRADE_POLICY_FILE_PATH="/var/lib/unattended-upgrades/unattended-upgrades-policy.conf"
LOG_PATH = "/var/log/kylin-unattended-upgrades/unattended-upgrades-shutdown.log"
TIMESTAMP_PATH="/var/lib/kylin-software-properties/template/kylin-source-status"
ACTION_INSTALL = 1
ACTION_CHECK_RESOLVER = 3
ACTION_DOWNLOADONLY = 4
        
def get_random_time(time_interval):
    now = datetime.datetime.now()
    try:
        start_time = datetime.datetime.strptime(time_interval.split("-")[0],"%H:%M")
        end_time = datetime.datetime.strptime(time_interval.split("-")[1],"%H:%M")
        start=datetime.datetime(now.year,now.month,now.day,start_time.hour,start_time.minute,0,0)
        end=datetime.datetime(now.year,now.month,now.day,end_time.hour,end_time.minute,0,0)
        time_diff = int((end-start).total_seconds())
        if time_diff<0:
            time_diff=time_diff+86400
        delta = random.randint(0,time_diff)
        actual_time = start+datetime.timedelta(seconds=delta)
        time_diff = int((actual_time - now).total_seconds())
        if time_diff<0:
            return actual_time+datetime.timedelta(seconds=86400)
        return actual_time
    except Exception as e:
        logging.error(_("illegal time format:%s")%e)
        return now+datetime.timedelta(seconds=random.randint(10,86400))

def ReadValueFromFile(file,section,option):
    config=configparser.ConfigParser(allow_no_value=True)
    config.optionxform = str
    try:
        config.read(file)
        value = config[section][option]
    except Exception as e:
        logging.error(_("read config file error:%s")%e)
        return ''
    return value

def WriteValueToFile(file,section,option,value):
    config=configparser.ConfigParser(allow_no_value=True)
    config.optionxform = str
    config.add_section(section)
    config.set(section,option,value)
    config.write(open(file,"w"))
  
def signal_term_handler(signal,frame):
    # type: (int, object) -> None
    logging.warning("SIGTERM received, will stop")
    os._exit(1)

def Predownload():
    logging.info(_("predownload task start"))
    if unattended_upgrades_shutdown.Download():
        now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        unattended_upgrades_shutdown.update_timestamp('predownload',now)
        return 0
    else:
        return 1
    
def Download():
    logging.info(_("download task start"))
    if unattended_upgrades_shutdown.Download():
        now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        unattended_upgrades_shutdown.update_timestamp('download',now)
        return 0
    else:
        return 1
    
def Install():
    logging.info(_("install task start"))
    if unattended_upgrades_shutdown.Install():
        now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        unattended_upgrades_shutdown.update_timestamp('install',now)
        return 0 
    else:
        return 1
    
def Upgrade():
    logging.info(_("upgrade task start"))
    if unattended_upgrades_shutdown.Install():
        return 0 
    else:
        return 1     

class FetchProgress(AcquireProgress):
    def __init__(self) -> None:
        super().__init__()
     
    def fetch(self, item: apt_pkg.AcquireItemDesc) -> None:
        logging.debug("%s [%d%%]"%(item.description,self.current_bytes*100/self.total_bytes))
        return super().fetch(item)
    
    def fail(self, item: apt_pkg.AcquireItemDesc) -> None:
        logging.error("package fetch failed:%s"%item.description)
        return super().fail(item)
    
    def ims_hit(self, item: apt_pkg.AcquireItemDesc) -> None:
        return super().ims_hit(item)
    
    def media_change(self, media: str, drive: str) -> bool:
        return super().media_change(media, drive)
    
    def pulse(self, owner: apt_pkg.Acquire) -> bool:
        return super().pulse(owner)
    
    def start(self) -> None:
        logging.info("download start")
        return super().start()
   
    def stop(self) -> None:
        logging.info("download finished")
        return super().stop()
           
class AutoUpgradePolicy():
    def __init__(self) -> None:
        self.autoupgradepolicy = {}
        if os.path.exists(UNATTENDED_UPGRADE_POLICY_FILE_PATH):
            config=configparser.ConfigParser(allow_no_value=True)
            config.optionxform = str
            config.read(UNATTENDED_UPGRADE_POLICY_FILE_PATH)
            for option in config.options('autoUpgradePolicy'):
                self.autoupgradepolicy.update({option:config['autoUpgradePolicy'][option]})
        logging.info(_("auto upgrade policy:"))
        for key in self.autoupgradepolicy.keys():
            logging.debug("%s:%s"%(key,self.autoupgradepolicy[key]))
        self.timestamp = {}
        if os.path.exists(UNATTENDED_UPGRADE_TIMESTAMP):
            logging.info(_("loading update time stamp"))
            config=configparser.ConfigParser(allow_no_value=True)
            config.optionxform = str
            config.read(UNATTENDED_UPGRADE_TIMESTAMP)
            if 'TimeStamp' in config.sections():                
                for option in config.options('TimeStamp'):
                    self.timestamp.update({option:config.get('TimeStamp',option)})
            logging.info(_("last run time:"))
            for key in self.timestamp:       
                logging.info("%s:%s"%(key,self.timestamp[key]))
                
    def UpdatePolicy(self):
        if os.path.exists(UNATTENDED_UPGRADE_POLICY_FILE_PATH):
            config=configparser.ConfigParser(allow_no_value=True)
            config.optionxform = str
            config.read(UNATTENDED_UPGRADE_POLICY_FILE_PATH)
            for option in config.options('autoUpgradePolicy'):
                self.autoupgradepolicy.update({option:config['autoUpgradePolicy'][option]})
        logging.info(_("auto upgrade policy:"))
        for key in self.autoupgradepolicy.keys():
            logging.debug("%s:%s"%(key,self.autoupgradepolicy[key]))    
                    
    def UpdateTimeStamp(self,task_id,last_run_time):
        logging.debug(_("update timestamp:%s %s")%(task_id,last_run_time))
        self.timestamp.update({task_id:last_run_time})
        if os.path.exists(UNATTENDED_UPGRADE_TIMESTAMP):
            config=configparser.ConfigParser(allow_no_value=True)
            config.optionxform = str
            config.read(UNATTENDED_UPGRADE_TIMESTAMP)
            if 'TimeStamp' in config.sections():
                config.set('TimeStamp',task_id,last_run_time)  
                with open(UNATTENDED_UPGRADE_TIMESTAMP,'w') as f:
                    config.write(f)
                
    def GetTimeStamp(self,task_id):
        if task_id in self.timestamp.keys():
            return self.timestamp[task_id]
        else:
            return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        
    def SetOptionValue(self,option,value):
        self.autoupgradepolicy.update({option:value})
   
    def GetOptionValue(self,option):
        try:
            return self.autoupgradepolicy[option]
        except Exception:
            return '' 
                                                         
class UnattendedUpgradesShutdown():
    def __init__(self, options):
        # type: (Values) -> None
        self.options = options
        self.init_events_flags()
        self.init_policy_config()
        self.init_scheduler()
        self.init_dbus_connections() 
    
    def init_events_flags(self):
        logging.info(_("init events and flags"))
        self.update_detect_status = False
        self.update_detect_event = Event()
        self.update_list = []
        self.resolve_depend_status = False
        self.resolve_depend_status_event = Event()
        self.remove_pkgs = []
        self.install_finish_status = False
        self.install_finish_status_event = Event()
        self.install_finish_group = []
        self.backup_start_result = False
        self.backup_start_event = Event()
        self.backup_finish_result = False
        self.backup_finish_event = Event()
    
    def init_policy_config(self):
        logging.info(_("init policy config"))
        self.autoupgradepolicy = AutoUpgradePolicy()
    
    def init_scheduler(self):
        jobstores = {'default': MemoryJobStore()}
        executors = {'default':ThreadPoolExecutor(1),'processpool':ProcessPoolExecutor(1)}
        job_defaults = {'misfire_grace_time':3600,'coalesce':True,'max_instances':1}
        self.background_scheduler = BackgroundScheduler(jobstores=jobstores,executors=executors,job_defaults=job_defaults,timezone="Asia/Shanghai")
        updatedays = 1
        try:
            updatedays = int(self.autoupgradepolicy.GetOptionValue('updateDays'))
        except Exception as e:
            logging.error(_("get update days error:%s")%e)
        try:
            if self.autoupgradepolicy.GetOptionValue('autoUpgradeState') == 'on':
                if self.autoupgradepolicy.GetOptionValue('downloadMode') == 'timing':
                    random_time = self.get_next_run_time('download')
                    self.background_scheduler.add_job(Download,trigger='interval',days=updatedays,\
                            start_date=random_time,id='download',replace_existing=True)                        
                if self.autoupgradepolicy.GetOptionValue('installMode') == 'timing':
                    random_time = self.get_next_run_time('install')
                    self.background_scheduler.add_job(Install,trigger='interval',days=updatedays,\
                        start_date=random_time,id='install',replace_existing=True)
            if self.autoupgradepolicy.GetOptionValue('preDownload') == 'on':
                random_time = self.get_next_run_time('predownload')
                self.background_scheduler.add_job(Predownload,trigger='interval',days=updatedays,\
                    start_date=random_time,id='predownload',replace_existing=True)                
        except Exception as e:
            logging.error(_("job initial error:%s")%e)
        self.background_scheduler.start()    
        with open(LOG_PATH,'a+') as f:
            self.background_scheduler.print_jobs(out=f)
        joblist = self.background_scheduler.get_jobs()
        for job in joblist:    
            logging.debug(_("initial job:%s,next run time:%s")%(job.id,job.next_run_time))   
    
    def load_systemupgrade_dbus_config(self):
        logging.debug(_("init system upgrade dbus connections"))
        self.update_proxy = self.system_bus.get_object('com.kylin.systemupgrade','/com/kylin/systemupgrade',follow_name_owner_changes=True)
        self.update_interface = dbus.Interface(self.update_proxy,dbus_interface='com.kylin.systemupgrade.interface')
        self.update_proxy.connect_to_signal('UpdateDetectFinished',self.update_detect_finished_handler)                                        
        self.update_proxy.connect_to_signal('UpdateFixBrokenStatus',self.update_fix_broken_status)
        self.update_proxy.connect_to_signal('UpdateDependResloveStatus',self.update_depend_resolve_status)                                           
        self.update_proxy.connect_to_signal('UpdateDloadAndInstStaChanged',self.update_download_install_status)
        self.update_proxy.connect_to_signal('UpdateInstallFinished',self.update_install_finished)   
        self.update_proxy.connect_to_signal('ChangeUpgradePolicy',self.change_upgrade_policy)                                                
    
    def load_strategy_dbus_config(self):
        logging.debug(_("init strategy dbus connections"))
        self.upgrade_strategy_proxy = self.system_bus.get_object('com.kylin.UpgradeStrategies','/com/kylin/UpgradeStrategies',follow_name_owner_changes=True)
        self.upgrade_strategy_interface = dbus.Interface(self.upgrade_strategy_proxy,dbus_interface='com.kylin.UpgradeStrategies.interface') 
        self.upgrade_strategy_proxy.connect_to_signal("PropertyChanged",self.property_changed_handler)
        self.upgrade_strategy_proxy.connect_to_signal("UpgradeAllNow",self.upgrade_all_now_handler) 
        
    def load_backup_dbus_config(self):
        logging.debug(_("init backup dbus connections"))
        self.backup_proxy = self.system_bus.get_object('com.kylin.backup','/',follow_name_owner_changes=True)
        self.backup_interface = dbus.Interface(self.backup_proxy,dbus_interface='com.kylin.backup.manager')                                                                                                                                      
        self.backup_proxy.connect_to_signal('sendStartBackupResult',self.backup_start_handler)                                       
        self.backup_proxy.connect_to_signal('sendBackupResult',self.backup_result_handler)                                          
        self.backup_proxy.connect_to_signal('sendRate',self.send_rate_handler) 
    
    def load_sys_dbus_config(self):
        logging.debug(_("load sys dbus config"))
        self.sys_proxy = self.system_bus.get_object('org.freedesktop.DBus','/org/freedesktop/DBus')
        self.sys_proxy.connect_to_signal('NameOwnerChanged',self.name_owner_changed)
         
    def init_dbus_connections(self):
        logging.debug(_("loading dbus configures..."))
        DBusGMainLoop(set_as_default=True)
        self.loop = GLib.MainLoop()
        self.system_bus = dbus.SystemBus()
        self.load_sys_dbus_config()
        self.load_systemupgrade_dbus_config()
        self.load_strategy_dbus_config()
        self.load_backup_dbus_config()
                                                 
    def name_owner_changed(self,busname,oldname,newname):
        if (busname == 'com.kylin.UpgradeStrategies'):
            logging.debug("name owner changed:%s,%s,%s"%(busname,oldname,newname))
        elif(busname == 'com.kylin.systemupgrade'):
            logging.debug("name owner changed:%s,%s,%s"%(busname,oldname,newname))
        elif(busname == 'com.kylin.backup'):
            logging.debug("name owner changed:%s,%s,%s"%(busname,oldname,newname))
        else:
            pass
    
    def change_upgrade_policy(self):
        logging.debug("change upgrade policy")
        self.autoupgradepolicy.UpdatePolicy()
        autoupgradestate = self.autoupgradepolicy.GetOptionValue('autoUpgradeState')
        self.property_changed_handler('autoUpgradeState',autoupgradestate)
            
    def property_changed_handler(self,property, value): 
        logging.info(_("property change:%s:%s")%(property,value))    
        self.autoupgradepolicy.SetOptionValue(property,value)             
        self.ExecutePolicy(property,value)
        with open(LOG_PATH,'a+') as f:
            self.background_scheduler.print_jobs(out=f)
        joblist = self.background_scheduler.get_jobs()
        for job in joblist:    
            logging.debug(_("job:%s,next run time:%s")%(job.id,job.next_run_time))    
    
    def upgrade_all_now_handler(self):
        logging.info(_("upgrade all now sinal received"))
        delta = random.randint(0,int(self.autoupgradepolicy.GetOptionValue('randomRange')))
        run_date = datetime.datetime.now() + datetime.timedelta(minutes=delta)
        self.background_scheduler.add_job(Upgrade,'date', run_date = run_date,id="upgrade",\
            max_instances=1,replace_existing=True)  
        with open(LOG_PATH,'a+') as f:
            self.background_scheduler.print_jobs(out=f) 
        joblist = self.background_scheduler.get_jobs()
        for job in joblist:    
           logging.debug(_("job:%s,next run time:%s")%(job.id,job.next_run_time))    
                                       
    def update_detect_finished_handler(self,success,updatelist,error_status,error_cause):
        logging.info(_("update detect finished:sucess:%s,updatelist:%s,error_status:%s,error_cause:%s")\
            %(success,",".join(updatelist),error_status,error_cause))
        self.update_detect_status = success
        self.update_list = []
        if success:
            try:
                for update_group in updatelist:
                    json_file_path = ("/var/lib/kylin-system-updater/json/%s.json"%(update_group))
                    if os.path.exists(json_file_path):
                        with open(json_file_path,'r') as f:
                            data = json.load(f)
                            for key in data['upgrade_list'].keys():
                                if key in ["total_download_size","total_install_size"]:
                                    pass
                                else:
                                    self.update_list.append(key)
                            for key in data['install_list'].keys():
                                if key in ["total_download_size","total_install_size"]:
                                    pass
                                else:
                                    self.update_list.append(key)
            except Exception as e:
                logging.error(e)  
        self.update_detect_event.set()
        
    def update_fix_broken_status(self,resolver_status,remove_status,remove_pkgs,pkg_raw_description,delete_desc,error_string,error_desc):
        logging.info(_("update fix broken status:resolver_status:%s,remove_status:%s,error_string:%s,error_desc:%s")%(resolver_status,remove_status,error_string,error_desc))
        #logging.info(remove_pkgs,pkg_raw_description,delete_desc)
        self.update_detect_status = False
        self.update_list = []
        self.update_detect_event.set()
            
    def update_depend_resolve_status(self,resolver_status,remove_status,remove_pkgs,pkg_raw_description,delete_description,error_string,error_desc):
        logging.info(_("update depend resolve status:%s,remove status:%s,remove pkgs:%s,pkg raw description:%s,delete_descrition:%s,error string:%s,error desc:%s")\
            %(resolver_status,remove_status,",".join(remove_pkgs),",".join(pkg_raw_description),",".join(delete_description),error_string,error_desc))
        self.resolve_depend_status = resolver_status
        self.remove_pkgs = remove_pkgs
        self.resolve_depend_status_event.set()
        
    def update_download_install_status(self,group,progress,status,details):
        logging.debug(_("%s update progress:%d,status:%s,details:%s")%(",".join(group),progress,status,details))
        
    def update_install_finished(self,success,group,error_string,error_desc):
        logging.info(_("update install finisih success:%s,group:%s,error string:%s,error desc:%s")\
            %(success,",".join(group),error_string,error_desc))
        self.install_finish_status = success
        self.install_finish_group=group
        self.install_finish_status_event.set()        
    
    def backup_start_handler(self,result):
        logging.debug(_("backup start result:%d")%result)
        if result == 30:    
            self.backup_start_result = True    
        else:                    
            self.backup_start_result = False
            self.backup_finish_event.set()
        self.backup_start_event.set()
                    
    def backup_result_handler(self,result):
        logging.debug(_("backup result:%s")%result)
        if result:                    
            self.backup_finish_result = True 
        else:
            self.backup_finish_result = False 
        self.backup_finish_event.set()   
        
    def send_rate_handler(self,sta,pro):
        logging.debug(_("backup status:%d,progress:%d")%(sta,pro))
    
    def run(self):
        if self.options.wait_for_signal:
            logging.debug(_("Waiting for signal to start operation "))
            self.loop.run()
        elif options.download_only:
            logging.debug(_("runing a download job"))
            return self.Download()
        elif options.install_only:
            logging.debug(_("runing an install job"))
            return self.Install()
        elif options.upgrade:
            logging.debug(_("runing an upgrade job"))
            return self.Install()
        else:
            logging.info(_("illegal options"))
        return True
    
    def print_jobs(self):
        with open(LOG_PATH,'a+') as f:
            self.background_scheduler.print_jobs(out=f)
    
    def list_jobs(self):
        joblist = self.background_scheduler.get_jobs()
        for job in joblist:    
            logging.debug(_("job:%s,next run time:%s")%(job.id,job.next_run_time))
            
    def remove_job(self,job_id):
        if self.background_scheduler.get_job(job_id):
            self.background_scheduler.remove_job(job_id)   
             
    def update_timestamp(self,task_id,timestamp):
        self.autoupgradepolicy.UpdateTimeStamp(task_id,timestamp)
 
    def get_next_run_time(self,task_id):
        now = datetime.datetime.now()
        try:
            option = 'downloadTime'
            if task_id == 'download':
                option = 'downloadTime'
            elif task_id == 'install':
                option = 'installTime'
            elif task_id == 'predownload':
                option = 'preDownloadTime'
            else:
                pass
            last_run_date=datetime.datetime.strptime(self.autoupgradepolicy.GetTimeStamp(task_id),"%Y-%m-%d %H:%M:%S")
            next_run_date=last_run_date+datetime.timedelta(days=float(self.autoupgradepolicy.GetOptionValue('updateDays')))
            time_interval = self.autoupgradepolicy.GetOptionValue(option)
            start_time = datetime.datetime.strptime(time_interval.split("-")[0],"%H:%M")
            end_time = datetime.datetime.strptime(time_interval.split("-")[1],"%H:%M")
            start=datetime.datetime(next_run_date.year,next_run_date.month,next_run_date.day,start_time.hour,start_time.minute,0,0)
            end=datetime.datetime(next_run_date.year,next_run_date.month,next_run_date.day,end_time.hour,end_time.minute,0,0)
            time_diff = int((end-start).total_seconds())
            delta=0
            if time_diff<0:
                delta = random.randint(time_diff,0)       
            else:
                delta = random.randint(0,time_diff)     
            nextdate = start+datetime.timedelta(seconds=delta)
            if ((now-nextdate).total_seconds())>0:                     
                return datetime.datetime(now.year,now.month,now.day,nextdate.hour,nextdate.minute,nextdate.second,0)
            else:
                return nextdate
        except Exception as e:
            logging.error(_("illegal time format:%s")%e)
            return now+datetime.timedelta(seconds=random.randint(0,86400))
        
    def ExecutePolicy(self,property,value):
        updatedays = 1
        try:
            updatedays = int(self.autoupgradepolicy.GetOptionValue('updateDays'))
        except Exception as e:
            logging.error(e)
        try:      
            if property == 'autoUpgradeState':
                if value == 'on':
                    if self.autoupgradepolicy.GetOptionValue('downloadMode') == 'timing':
                        random_time = self.get_next_run_time("download")
                        self.background_scheduler.add_job(Download,trigger='interval',days=updatedays,\
                            start_date=random_time,id='download',replace_existing=True)
                    if self.autoupgradepolicy.GetOptionValue('installMode') == 'timing':
                        random_time = self.get_next_run_time("install")
                        self.background_scheduler.add_job(Install,trigger='interval',days=updatedays,\
                            start_date=random_time,id='install',replace_existing=True)
                elif value == 'off':
                    self.remove_job('download')
                    self.remove_job('install')
                else:
                    pass                 
            elif property == 'downloadMode':
                if value == 'timing':
                    if self.autoupgradepolicy.GetOptionValue('autoUpgradeState') == 'on':
                        random_time = self.get_next_run_time("download")
                        self.background_scheduler.add_job(Download,trigger='interval',days=updatedays,\
                            start_date=random_time,id='download',replace_existing=True)
                elif value == 'manual':
                    self.remove_job('download')
                else:
                    pass    
            elif property == 'downloadTime':  
                if self.autoupgradepolicy.GetOptionValue('autoUpgradeState') == 'on' and \
                    self.autoupgradepolicy.GetOptionValue('downloadMode') == 'timing': 
                    random_time = self.get_next_run_time("download")
                    self.background_scheduler.add_job(Download,trigger='interval',days=updatedays,\
                        start_date=random_time,id='download',replace_existing=True)      
            elif property == 'installMode':  
                if value == 'timing':
                    if self.autoupgradepolicy.GetOptionValue('autoUpgradeState') == 'on':
                        random_time = self.get_next_run_time("install")
                        self.background_scheduler.add_job(Install,trigger='interval',days=updatedays,\
                            start_date=random_time,id='install',replace_existing=True)
                elif value == 'manual':
                    self.remove_job('install')
                elif value == 'bshutdown':
                    self.remove_job('install')
                else:
                    pass
            elif property == 'installTime':  
                if self.autoupgradepolicy.GetOptionValue('autoUpgradeState') == 'on' and \
                    self.autoupgradepolicy.GetOptionValue('installMode') == 'timing':
                    random_time = self.get_next_run_time("install")
                    self.background_scheduler.add_job(Install,trigger='interval',days=updatedays,\
                        start_date=random_time,id='install',replace_existing=True)
            elif property == 'preDownload':  
                if value == 'on':
                    random_time = self.get_next_run_time("predownload")
                    self.background_scheduler.add_job(Predownload,trigger='interval',days=updatedays,\
                        start_date=random_time,id='predownload',replace_existing=True)           
                elif value == 'off':
                    self.remove_job('predownload')
                else:
                    pass 
            elif property == 'preDownloadTime':  
                if self.autoupgradepolicy.GetOptionValue('preDownload') == 'on':
                    random_time = self.get_next_run_time("predownload")
                    self.background_scheduler.add_job(Predownload,trigger='interval',days=updatedays,\
                        start_date=random_time,id='predownload',replace_existing=True)        
            elif property == 'updateDays':
                if self.autoupgradepolicy.GetOptionValue('preDownload') == 'on':
                    random_time = self.get_next_run_time("predownload")
                    self.background_scheduler.add_job(Predownload,trigger='interval',days=updatedays,\
                        start_date=random_time,id='predownload',replace_existing=True) 
                if self.autoupgradepolicy.GetOptionValue('autoUpgradeState') == 'on':
                    if self.autoupgradepolicy.GetOptionValue('downloadMode') == 'timing': 
                        random_time = self.get_next_run_time("download")
                        self.background_scheduler.add_job(Download,trigger='interval',days=updatedays,\
                            start_date=random_time,id='download',replace_existing=True) 
                    if self.autoupgradepolicy.GetOptionValue('installMode') == 'timing':
                        random_time = self.get_next_run_time("install")
                        self.background_scheduler.add_job(Install,trigger='interval',days=updatedays,\
                            start_date=random_time,id='install',replace_existing=True)           
            else:
                logging.info(_("other options:%s:%s")%(property,value))
        except Exception as e:
            logging.error(_("policy execute error:%s")%e) 
    
    def Backup(self): 
        logging.info(_("start backup"))
        backup_partition_status = self.backup_interface.Mount_backup_partition()
        logging.info(_("backup partition status:%d")%backup_partition_status)
        if backup_partition_status not in [0,5]:
            logging.error(_("backup partition error:%d")%backup_partition_status)
            return False
        status_code,result = self.backup_interface.getBackupState()
        logging.debug(_("backup state code:%d,%s")%(status_code,result))
        if result == 0 and status_code == 99:
            pass
        else:   
            return False
        backup_name = _("unattended upgrades")
        create_note = ''
        inc_note = ''
        userName=getpass.getuser()
        uid=os.getuid()
        self.backup_finish_event.clear()
        self.backup_start_result =True
        self.backup_finish_result=True
        self.backup_interface.autoBackUpForSystemUpdate_noreturn(backup_name,create_note,inc_note,userName,uid)
        self.backup_finish_event.wait()
        logging.debug(_("backup start result:%s,backup result:%s")%(self.backup_start_result,self.backup_finish_result))
        if (self.backup_start_result and self.backup_finish_result):              
            return True
        else:
            return False
        
    def Download(self):
        logging.debug(_("start download"))
        self.update_detect_event.clear()
        self.UpdateDetect()
        self.update_detect_event.wait()
        logging.debug(_("update detect finish:%s,%s")%(self.update_detect_status,",".join(self.update_list)))
        if self.update_detect_status and len(self.update_list)>0:
            pass
        # elif not self.update_detect_status and 'kylin-system-updater' in self.update_list:
        #     logging.info(_("self update finished"))
        else:
            logging.info(_("no pkgs to download"))
            return False
        cache = apt.Cache()
        for pkg in self.update_list:
            try:
                package = cache[pkg]
                if not package.installed:
                    package.mark_install()
                else:
                    package.mark_upgrade()
            except Exception as e:
                logging.error(e)
                return False
        list = apt_pkg.SourceList()
        list.read_main_list()
        recs = cache._records
        pm = apt_pkg.PackageManager(cache._depcache)
        fetcher = apt_pkg.Acquire(FetchProgress())
        try:
            pm.get_archives(fetcher, list, recs)
        except Exception as e:
            logging.error(e)
        res = fetcher.run()
        logging.debug("fetch.run() result: %s", res)
        if res == 0:
            return True
        else:
            return False
        
    def Install(self):
        logging.debug(_("start install"))
        self.update_detect_event.clear()
        self.UpdateDetect()
        self.update_detect_event.wait()
        logging.debug(_("update detect finish:%s,%s")%(self.update_detect_status,",".join(self.update_list)))
        if self.update_detect_status and len(self.update_list)>0:
            pass
        # elif not self.update_detect_status and 'kylin-system-updater' in self.update_list:
        #     logging.info(_("self update finished"))
        else:
            return False
        self.resolve_depend_status_event.clear()
        self.DistUpgradeAll(False)
        self.resolve_depend_status_event.wait()
        logging.debug(_("resolve dependency status:%s,%s")%(self.resolve_depend_status,",".join(self.remove_pkgs)))
        if self.resolve_depend_status and len(self.remove_pkgs)==0:
            pass
        else:
            return False
        needbackup = self.autoupgradepolicy.GetOptionValue('backupbeforeinstall')
        logging.debug(_("checking if need backup:%s")%needbackup)
        if needbackup == 'on':
            if self.Backup():
                logging.debug(_("backup success"))
            else:
                logging.debug(_("backup failed"))
                return False    
        self.install_finish_status_event.clear()
        self.DistUpgradeAll(True)
        self.install_finish_status_event.wait()        
        logging.debug(_("install finish status:%s,%s")%(self.install_finish_status,",".join(self.install_finish_group)))
        if self.install_finish_status and len(self.install_finish_group)>0:
            self.reboot_if_required()
        else:
            return False
        return True        
      
    def UpdateDetect(self):
        return self.update_interface.UpdateDetect()
    
    def DistUpgradeAll(self,is_install):
        return self.update_interface.DistUpgradeAll(is_install) 
    
    def mount_backup_partition(self):
        return self.backup_interface.Mount_backup_partition()
    
    def get_backup_state(self):
        return self.backup_interface.getBackupState()

    def get_backup_comment_for_systemupdate(self):
        return self.backup_interface.getBackupCommentForSystemUpdate()
    
    def auto_backup_for_system_update_noreturn(self,timeStamp,create_note,inc_note,userName,uid):
        self.backup_interface.autoBackUpForSystemUpdate_noreturn(timeStamp,create_note,inc_note,userName,uid)
        return        
    
    def reboot_if_required(self):
        needreboot = self.autoupgradepolicy.GetOptionValue('automaticReboot')
        when = self.autoupgradepolicy.GetOptionValue('automaticRebootTime')
        logging.debug(_("check if need reboot:%s when:%s")%(needreboot,when))
        if needreboot == "on":
            try:
                output = subprocess.check_output(["/sbin/shutdown","-r",when],shell=False)
                logging.debug(output)
            except Exception as e:
                logging.error(_("Failed to issue shutdown: %s")%e)
                return False
  
if __name__ == "__main__":
    gettext.bindtextdomain("unattended-upgrades","/usr/share/locale")
    gettext.textdomain("unattended-upgrades")
    parser = OptionParser()
    parser.add_option("", "--debug",
                      action="store_true", dest="debug",
                      default=False,help="print debug messages")   
    parser.add_option("", "--download-only",
                      action="store_true", dest="download_only",
                      default=False,help="only download without install")
    parser.add_option("", "--install-only",
                      action="store_true", dest="install_only",
                      default=False,help="only install without download")
    parser.add_option("", "--upgrade",
                      action="store_true", dest="upgrade",
                      default=False,help="upgrade all packages") 
    parser.add_option("", "--wait-for-signal",
                      action="store_true", dest="wait_for_signal",
                      default=False,
                      help="wait for TERM signal before starting operation")
    (options, args) = parser.parse_args()
    logdir = "/var/log/kylin-unattended-upgrades/"
    if not os.path.exists(logdir):
        os.makedirs(logdir)
    logfile = os.path.join(logdir, "unattended-upgrades-shutdown.log")
    if options.debug:
        logging.basicConfig(format='%(asctime)s-%(name)s-%(levelname)s-%(message)s',datefmt='%Y-%m-%d,%H:%M:%S',level=logging.DEBUG,stream=sys.stdout)
    else:
        logging.basicConfig(format='%(asctime)s-%(name)s-%(levelname)s-%(message)s',datefmt='%Y-%m-%d,%H:%M:%S',level=logging.DEBUG,filename=logfile) 
    signal.signal(signal.SIGTERM, signal_term_handler)
    signal.signal(signal.SIGHUP, signal.SIG_IGN)
    logging.info(_("unattended upgrade start options:%s")%(" ".join(sys.argv)))
    unattended_upgrades_shutdown = UnattendedUpgradesShutdown(options)
    sys.exit(unattended_upgrades_shutdown.run())    