775 lines
37 KiB
Python
775 lines
37 KiB
Python
#!/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()) |