可变系统更新方式保持不变且引入不可变系统的系统更新方式,软件包:kylin-system-updater-immutable

This commit is contained in:
wangsong 2023-12-04 11:37:56 +08:00
parent d0a8aac6f0
commit 8c788e68f0
64 changed files with 17204 additions and 3 deletions

38
.gitignore vendored Executable file
View File

@ -0,0 +1,38 @@
# Python
*venv*
__pycache__
.vscode
.pybuild
build
debian/.debhelper
debian/kylin-system-updater
debian/kylin-system-updater.debhelper.log
debian/kylin-system-updater.postinst.debhelper
debian/kylin-system-updater.prerm.debhelper
debian/kylin-system-updater.substvars
backend/po/kylin-system-updater.pot
system-updater.session.sql
build/scripts-3.8/
debian/files
plugin/libupgrade.so
plugin/*.o
plugin/moc*
# Misc
.*cache
plugin/.qmake.stash
plugin/Makefile
backend/po/kylin-system-updater.pot
plugin/upgrade.pro.user
#plugin
plugin/qrc_img.cpp
plugin/translations/bo.qm
plugin/translations/en_US.qm
plugin/translations/tr.qm
plugin/translations/zh_CN.qm
#notification
kylin-updatefinish-notify
notification/CMakeCache.txt
notification/CMakeFiles/
notification/Makefile
notification/cmake_install.cmake

View File

@ -1,4 +1,7 @@
all:updater
all:updater updater-immutable
updater:
cd backend && ./setup.py build
updater-immutable:
cd backend-immutable && ./setup.py build

View File

@ -0,0 +1,103 @@
# AlertWatcher.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2010 Mohamed Amine IL Idrissi
#
# Author: Mohamed Amine IL Idrissi <ilidrissiamine@gmail.com>
#
# 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 2 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import
import logging
from gi.repository import GObject
import dbus
from dbus.mainloop.glib import DBusGMainLoop
class AlertWatcher(GObject.GObject):
""" a class that checks for alerts and reports them, like a battery
or network warning """
__gsignals__ = {"network-alert": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_INT,)),
"battery-alert": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_BOOLEAN,)),
"network-3g-alert": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_BOOLEAN,
GObject.TYPE_BOOLEAN,)),
}
def __init__(self):
GObject.GObject.__init__(self)
DBusGMainLoop(set_as_default=True)
self.bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
# make it always connected if NM isn't available
self.network_state = 3
def check_alert_state(self):
try:
#network
obj = self.bus.get_object("org.freedesktop.NetworkManager",
"/org/freedesktop/NetworkManager")
obj.connect_to_signal(
"StateChanged",
self._on_network_state_changed,
dbus_interface="org.freedesktop.NetworkManager")
interface = dbus.Interface(obj, "org.freedesktop.DBus.Properties")
self.network_state = interface.Get(
"org.freedesktop.NetworkManager", "State")
self._network_alert(self.network_state)
# power
# obj = self.bus.get_object('org.freedesktop.UPower',
# '/org/freedesktop/UPower')
# obj.connect_to_signal("Changed", self._power_changed,
# dbus_interface="org.freedesktop.UPower")
# self._power_changed()
# 3g
# self._update_3g_state()
except dbus.exceptions.DBusException as e:
logging.error(str(e))
pass
def _on_network_state_changed(self, state):
self._network_alert(state)
# self._update_3g_state()
# def _update_3g_state(self):
# from .roam import NetworkManagerHelper
# nm = NetworkManagerHelper()
# on_3g = nm.is_active_connection_gsm_or_cdma()
# is_roaming = nm.is_active_connection_gsm_or_cdma_roaming()
# self._network_3g_alert(on_3g, is_roaming)
# def _network_3g_alert(self, on_3g, is_roaming):
# self.emit("network-3g-alert", on_3g, is_roaming)
def _network_alert(self, state):
self.network_state = state
self.emit("network-alert", state)
# def _power_changed(self):
# obj = self.bus.get_object("org.freedesktop.UPower",
# "/org/freedesktop/UPower")
# interface = dbus.Interface(obj, "org.freedesktop.DBus.Properties")
# on_battery = interface.Get("org.freedesktop.UPower", "OnBattery")
# self.emit("battery-alert", on_battery)

View File

@ -0,0 +1,753 @@
# DataAcquisition.py
# supervisory control and data acquisition
#!/usr/bin/python3
import os
import json
import dbus
import uuid
import time
import socket
import base64
import shutil
import hashlib
import logging
import tarfile
import requests
import datetime
import threading
import subprocess
from email import message
from datetime import datetime
from binascii import a2b_hex
from Crypto.PublicKey import RSA
from urllib import parse, request
from SystemUpdater.Core import enums
from Crypto.Cipher import PKCS1_OAEP
from json.decoder import JSONDecodeError
from dbus.exceptions import DBusException
from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig
LOCALTIDDIR = "/var/lib/kylin-system-updater/"
LOCALTIDFILE = "tidfile.conf"
MSGSNDDIR = "/var/lib/kylin-system-updater/sendinfos/"
SERVERID = "/var/lib/kylin-software-properties/config/serverID.conf"
SOURCE_PKGNAME = {
'Kylin System Updater': 'kylin-system-updater',
'Kylin Unattended Upgrade': 'unattended-upgrades'
}
class UpdateMsgCollector():
ACTION_DEFUALT_STATUS = -1
ACTION_UPDATE = 0
ACTION_INSTALL = 1
ACTION_INSTALL_DEB = 2
ACTION_CHECK_RESOLVER = 3
ACTION_DOWNLOADONLY = 4
ACTION_FIX_BROKEN = 5
ACTION_REMOVE_PACKAGES = 6
ACTION_FIX_INCOMPLETE = 7
ACTION_CLEAN = 8
ACTION_INSTALL_SHUTDOWN = 9
MODE_DEFAULT_STATUS = -1
#1、ACTION_INSTALL 安装的子类
#部分升级
MODE_INSTALL_PARTIAL = 0
#全部升级
MODE_INSTALL_ALL = 1
#系统全盘升级
MODE_INSTALL_SYSTEM = 2
#2、更新的子类
MODE_UPDATE_CACHE = 0
MODE_UPDATE_ALL = 1
mode_map = {
MODE_INSTALL_PARTIAL:"upgrade_system",
MODE_INSTALL_ALL:"upgrade_all",
MODE_INSTALL_PARTIAL:"upgrade_partial"
}
action_map = {
ACTION_CHECK_RESOLVER:enums.MONIT_DEPRESOLUT,
ACTION_INSTALL:enums.MONIT_INSTALL,
ACTION_INSTALL_DEB:enums.MONIT_INSTALLDEB,
10:enums.MONIT_FINISH,
"finish-updatedetect":enums.MONIT_DETECT,
"finish-download":enums.MONIT_DOWNLOAD,
"finish-downfinish":enums.MONIT_DOWNLOAD,
"finish-update":enums.MONIT_FINISH,
"finish-install":enums.MONIT_FINISH
}
messageType_map = {
"finish-updatedetect":"UpdateDetect",
ACTION_CHECK_RESOLVER:"DepResolution",
"finish-downfinish":"Downloaded",
ACTION_INSTALL:"Installing",
# InstallBackend.ACTION_CHECK_RESOLVER:"UpgradeFinish",
ACTION_INSTALL_DEB:"InstallerInfo",
10:"Background-Upgrade",
"finish-update":"UpdateInfos",
"finish-install":"InstallInfos"
}
def __init__(self, manager=None):
self.uuid = ''
self.status = ''
self.upgrade_mode = ''
self.upgrade_action = ''
self.UploadMessage = {}
self.PackageInfo = {}
self.UpdateInfos = {}
self.background_version = {}
self.background_upgradable = []
self.background_list = []
self.upgrade_list = []
self.waitSendList = []
self.cache = None
self.updateManager = manager
# 转换 & 加密
self.convertor = FormatConvert(self)
# 发送器
self.sender = MessageSend(self)
logging.info("Initialize Update MessageSend Collector to success...")
def GenUploadMessage(self, dict_message, local_uuid = ''):
UploadMessage = {}
# 获取将要上传的数据,获取东八区时间
UploadMessage['createTimeStamp'] = get_east_8_time()
try:
if "packageName" in dict_message.keys():
dict_message.pop("packageName")
for key in dict_message.keys():
UploadMessage[key] = dict_message[key]
if local_uuid != '':
UploadMessage['UUID'] = str(local_uuid)
else:
UploadMessage['UUID'] = str(uuid.uuid1())
serverid_configs = UpgradeConfig(datadir = "/var/lib/kylin-system-updater/json/", name = "serverID.conf")
tans_id = serverid_configs.getWithDefault("ID", "pushID", " ")
UploadMessage['transactionID'] = tans_id
logging.debug('Get current transactionID: %s.',tans_id)
snobj = dbus.SystemBus().get_object('org.freedesktop.activation', '/org/freedesktop/activation')
sninterface = dbus.Interface(snobj, dbus_interface='org.freedesktop.activation.interface')
retval,retid = sninterface.serial_number()
if(retid==0):
UploadMessage['serialNumber'] = str(retval)
else:
UploadMessage['serialNumber'] = ''
except Exception as e:
logging.error(str(e))
json_UploadMessage = self.convertor.dictConvertJson(UploadMessage)
logging.debug('Generate UploadMessage: %s.',json_UploadMessage)
self.UploadMessage = UploadMessage.copy()
UploadMessage.clear()
def GenPackageInfo(self, messageType, packageName):
PackageInfo = {}
PackageInfo['messageType'] = str(messageType)
PackageInfo['packageName'] = str(packageName)
key = str(packageName)+'_'+str(messageType)
# 获取本地tid
self.sender.GetLocalTid(key)
PackageInfo["tid"] = str(self.sender.localtid)
json_PackageInfo = self.convertor.dictConvertJson(PackageInfo)
logging.debug('Generate PackageInfo: %s.',json_PackageInfo)
self.PackageInfo = PackageInfo.copy()
PackageInfo.clear()
def setUploadMessage(self, KeyValue):
pass
def UpdateMsg(self, messageType, json_message, uuid = ''):
# para: messageType(消息类型): "UpdateInfos"、 "InstallInfos"、 "RemoveInfo"
# para: dict_message(数据内容): 必须包含 "packageName"、"source"", 采集器会进行检测
dict_message = self.convertor.JsonConvertDict(json_message)
if messageType == "":
messageType = "SystemUpdate"
if type(dict_message) != type(dict) and "appname" not in dict_message.keys():
raise AttributeError("'%s' object has no attribute '%s'" % ("dict message", "appname"))
# 生成UploadMessage与PackageInfo
try:
self.GenPackageInfo(messageType, "kylin-system-updater")
self.GenUploadMessage(dict_message, local_uuid = uuid)
except Exception as e:
logging.error(str(e))
# sha256
json_UploadMessage = self.convertor.dictConvertJson(self.UploadMessage)
json_PackageInfo = self.convertor.dictConvertJson(self.PackageInfo)
shaValue = self.convertor.Sha256Value(json_UploadMessage)
encodeMsg = self.convertor.EncodeRSAtoBase64(shaValue)
try:
self.sender.MsgSendToServer(json_UploadMessage, json_PackageInfo, encodeMsg)
except Exception as e:
logging.error(e)
try:
if messageType == "UpdateInfos":
# 获取系统激活码
Activation_code = GetActivationCode()
dict_message['RegisterNumber'] = Activation_code
if "appname" in self.UploadMessage.keys():
self.UploadMessage["packageName"] = self.UploadMessage["appname"]
self.UploadMessage.pop("appname")
json_UploadMessage = self.convertor.dictConvertJson(self.UploadMessage)
json_PackageInfo = self.convertor.dictConvertJson(self.PackageInfo)
shaValue = self.convertor.Sha256Value(json_UploadMessage)
encodeMsg = self.convertor.EncodeRSAtoBase64(shaValue)
self.sender.MsgSendToLogTransmit(json_UploadMessage, json_PackageInfo, encodeMsg)
except Exception as e:
logging.error(e)
def Generate_Msg(self, upgrade_list, mode):
try:
self.upgrade_list = upgrade_list
self.upgrade_mode = mode
self.UpdateInfos.update({"upgradeMode":self.mode_map.get(self.upgrade_mode, "default-mode")})
except DBusException as e:
logging.error(e)
def Upgrade_Process_Msg(self, action, dict_msg = {}):
if self.updateManager.configs_uncover.getWithDefault("SystemStatus", "upload_upgrade_log", False) == True:
tmp_dict = {}
tmp_dict.update(dict_msg)
try:
self.Init_UUID()
if 'error_string' in tmp_dict.keys() and 'error_desc' in tmp_dict.keys():
if tmp_dict['error_string'] == "下载失败" and tmp_dict['error_desc'] == "取消升级":
tmp_dict.update({"error_string":str(tmp_dict['error_string']+","+tmp_dict['error_desc'])})
tmp_dict.update({"error_desc":str(tmp_dict['error_string']+","+tmp_dict['error_desc'])})
self.UpdateInfos.update({"step":self.action_map.get(action, "")})
if self.upgrade_mode == self.MODE_INSTALL_SYSTEM:
self.UpdateInfos.update({"appname":"Upgrade System"})
tmp_dict.update(self.UpdateInfos)
json_file = json.dumps(tmp_dict.copy())
self.UpdateMsg(self.messageType_map.get(action, ""), json_file, self.uuid)
else:
if action == self.ACTION_INSTALL_DEB:
tmp_dict.update({"step":self.action_map.get(action, "")})
json_file = json.dumps(tmp_dict.copy())
self.UpdateMsg(self.messageType_map.get(action, ""), json_file, self.uuid)
else:
tmp_dict.update(self.UpdateInfos)
json_file = json.dumps(tmp_dict.copy())
self.UpdateMsg(self.messageType_map.get(action, ""), json_file, self.uuid)
except Exception as e:
logging.error(e)
tmp_dict.clear()
def make_background_version(self,pkg):
if pkg.is_installed == True:
self.background_version.update({pkg.name:pkg.installed.source_version})
else:
self.background_version.update({pkg.name:"Unknown"})
self.background_list.append(pkg.name)
def Init_UUID(self):
DMI_UUID_FILE = "/sys/devices/virtual/dmi/id/product_uuid1"
if os.path.isfile(DMI_UUID_FILE):
with open(DMI_UUID_FILE, 'r') as f:
data = f.read()
self.uuid = data.split()
if self.uuid == "":
args = ["/usr/sbin/dmidecode","-s","system-uuid"]
from subprocess import Popen, PIPE
p = Popen(args, stdout=PIPE, universal_newlines=True)
res = p.wait()
if res != 0:
self.uuid = ""
else:
self.uuid = p.stdout.readline().strip()
p.stdout.close()
def Msg_Clean(self):
self.UploadMessage = {}
self.PackageInfo = {}
self.UpdateInfos = {}
self.uuid = ""
def update_process(self, action,status,upgrade_group,error_code='',error_string='',error_desc=''):
MsgInfos = {}
MsgInfos.update({"appname":str(",".join(upgrade_group))})
MsgInfos.update({"status":str(status)})
MsgInfos.update({"error_code":str(error_code)})
MsgInfos.update({"error_string":str(error_string)})
MsgInfos.update({"error_desc":str(error_desc)})
if action == "finish-updatedetect":
if upgrade_group != [] or len(upgrade_group) != 0:
self.Upgrade_Process_Msg("finish-updatedetect", MsgInfos.copy())
elif action == "finish-downfinish":
self.Upgrade_Process_Msg("finish-downfinish", MsgInfos.copy())
MsgInfos.clear()
class FormatConvert():
def __init__(self, DataCollector):
#秘钥
self.publickey = UniqueKey()
self.collector = DataCollector
def dictConvertJson(self, dict_msg):
#字典转换为json格式字符串
json_file = ''
try:
json_file = json.dumps(dict_msg)
except JSONDecodeError as e:
logging.error(str(e))
return json_file
def JsonConvertDict(self, json_file):
# json格式字符串转换为字典
dict_file = {}
try:
dict_file = json.loads(json_file)
except JSONDecodeError as e:
logging.error(str(e))
return dict_file
def Sha256Value(self, json_file):
# 计算sha256值
hsobj = hashlib.sha256()
try:
hsobj.update(json_file.encode("utf-8"))
except ValueError as e:
logging.error("SHA256 value error: %s.",str(e))
except Exception as e:
logging.error(str(e))
return hsobj.hexdigest()
def EncodeRSAtoBase64(self, value):
# 将value进行RSA加密并base64转码
try:
# 计算hex值
value_hex = a2b_hex(value)
# 加载公钥,填充格式OAEP
uniqueKey = self.publickey.keyvalue.encode('utf-8')
uniqueKeyorig = base64.b64decode(uniqueKey) # 公钥文件
rsa_pubkey = RSA.importKey(uniqueKeyorig) # RSA公钥
oaep_pub = PKCS1_OAEP.new(rsa_pubkey) # OAEP填充
# 加密数据
encodemsg = oaep_pub.encrypt(value_hex)
# 加密数据Base64转码
enMsg = base64.b64encode(encodemsg)
except ValueError:
logging.error("Value error: %s.", value)
except TypeError:
logging.error("RSA key has no private half.")
return enMsg
class MessageSend():
ERR_PARA_FROMAT = 1
ERR_NO_LOACLTID = 2
ERR_ABNORMAL_SHA = 3
ERR_UPLOADMSG_SHA = 4
ERR_UPLOADMSG_CTS = 5
def __init__(self, DataCollector=None) -> None:
# self.convertor = FormatConvert()
if DataCollector == None:
self.collector = UpdateMsgCollector()
else:
self.collector = DataCollector
def MsgSendToServer(self, UploadMessage, PackageInfo, encodeMsg):
daqbus = dbus.SystemBus()
try:
daqobj = daqbus.get_object('com.kylin.daq', '/com/kylin/daq')
daqinterface = dbus.Interface(daqobj, dbus_interface='com.kylin.daq.interface')
except DBusException as e:
logging.error("kylin-daq service error: "+str(e))
return
try:
retval,retid = daqinterface.UploadMessage(PackageInfo, UploadMessage, encodeMsg)
except AttributeError:
logging.error("Call UploadMessage: Attribute Error.")
self.Send_finally(retval, retid, PackageInfo, UploadMessage, encodeMsg)
def MsgSendToLogTransmit(self, UploadMessage, PackageInfo, encodeMsg):
daqbus = dbus.SystemBus()
hedronbus = dbus.SystemBus()
messgae = {}
info = {}
hostname = ""
try:
daqobj = daqbus.get_object('org.log.sys_transmit', '/org/log/sys_transmit')
daqinterface = dbus.Interface(daqobj, dbus_interface='org.log.transmit')
except DBusException as e:
logging.warning("sys_transmit service error: "+str(e)) #主线默认不通过安全管控上传日志
return
dict_UploadMessage = self.collector.convertor.JsonConvertDict(UploadMessage)
dict_PackageInfo = self.collector.convertor.JsonConvertDict(PackageInfo)
messgae.update({"packageName": str(dictgetattr(dict_PackageInfo, "packageName", 'None'))})
messgae.update({"messageType": str(dictgetattr(dict_PackageInfo, "messageType", 'None'))})
for k in dict_UploadMessage.keys():
messgae.update({k: str(dictgetattr(dict_UploadMessage, k, 'unKnown'))})
if "status" in messgae.keys():
if messgae["status"] == "success":
messgae.update({"status": str("成功")})
else:
messgae.update({"status": str("失败")})
json_messgae = self.collector.convertor.dictConvertJson(messgae)
info.update({"time":str(dictgetattr(dict_UploadMessage, 'createTimeStamp', ' '))})
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
host_ip = s.getsockname()[0]
except:
host_ip = '0.0.0.0'
try:
hedronobj = hedronbus.get_object('com.kylin.kydevmonit.hedronclient', '/kydevmonit/hedronclient')
hedroninterface = dbus.Interface(hedronobj, dbus_interface='com.kylin.kydevmonit.hedronclient')
hostname = hedroninterface.get_current_user()
except Exception as err:
logging.warning(err)
hostname = ""
info.update({"hostname":str(hostname)})
info.update({"ip":str(host_ip)})
info.update({"name":str("kylin-system-updater")})
info.update({"lv":str("debug")})
info.update({"message":json_messgae})
json_info = self.collector.convertor.dictConvertJson(info)
logging.info("Send to sys_transmit: < %s >.",json_info)
try:
retval = daqinterface.log_transmit(json_info)
logging.info("Send retval: %s.", retval)
except Exception as err:
logging.error(err)
def Send_finally(self, retval, retid, json_PackageInfo, json_UploadMessage, encodeMsg):
# 根据发送结果进行处理
result = ''
PackageInfo = self.collector.convertor.JsonConvertDict(json_PackageInfo)
if retval != 0:
if retval == self.ERR_PARA_FROMAT:
result = "Parameter format error"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
elif retval == self.ERR_NO_LOACLTID:
result = "The tid value in packageInfo is abnormal, but the message is saved successfully"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
# 将返回的tid保存到本地
key = PackageInfo['packageName']+'_'+PackageInfo['messageType']
self.SaveTid(key, retid)
elif retval == self.ERR_ABNORMAL_SHA:
result = "Abnormal UploadedMessage Sha256"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
elif retval == self.ERR_UPLOADMSG_SHA:
result = "Description The UploadedMessageSha256 was decrypted incorrectly"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
elif retval == self.ERR_UPLOADMSG_CTS:
result = "The createTimeStamp field of UploadedMessage is abnormal"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
elif retval == self.ERR_UPLOADMSG_CTS:
result = "Invalid key included in \"uploadedMessage\" or \"packageInfo\": <@timestamp>,<_id>,<_index>,<_type>,<createTime>,<highlight>,<sn>,<sort>, check upload field"
logging.debug("Sent Status: false - packageName: %s : result: %s.", PackageInfo['packageName'], result)
else:
logging.debug("Sent Status: false - packageName: %s : retval: %s.", PackageInfo['packageName'], retval)
# 上传失败写入本地json
if retval != self.ERR_NO_LOACLTID or retval == self.ERR_NO_LOACLTID:
self.WriteToJson(PackageInfo['messageType'], json_PackageInfo, json_UploadMessage, encodeMsg)
elif retval == 0:
result = "Send to server success"
logging.debug("Sent Status: True - packageName: %s : result: %s.", PackageInfo['packageName'], result)
def GetLocalTid(self, key):
# 试图获取本地tid
try:
# 存放至数据库
tid = self.collector.updateManager.sqlite3_server.select_from_tid("tid",key)
if tid == "None" or tid == None:
self.localtid = ""
else:
self.localtid = tid
except Exception as e:
logging.error(str(e))
def SaveTid(self, key, localtid):
if len(localtid) == 0:
return
_localtid = str(localtid)
try:
# 写入数据库
self.collector.updateManager.sqlite3_server.insert_into_tid(key, _localtid)
except Exception as e:
logging.error(str(e))
def WriteToJson(self, messageType, json_PackageInfo, json_UploadMessage, encodeMsg):
#发送失败时写入本地json中定时发送
Msg = {}
Msg["PackageInfo"] = json_PackageInfo
Msg["UploadMessage"] = json_UploadMessage
Msg["encodeMsg"] = str(encodeMsg)
json_file = self.collector.convertor.dictConvertJson(Msg)
# 保存信息
try:
if not os.path.exists(MSGSNDDIR):
os.mkdir(MSGSNDDIR)
# 根据messageType保存信息
with open(MSGSNDDIR+messageType+".json","a") as f:
f.write(json_file)
f.write("\n")
except Exception as e:
logging.error(str(e))
def _TimedTransmission(self, file_path = MSGSNDDIR):
classify_list = [name for name in os.listdir(file_path) if name.endswith(".json")]
for f in classify_list:
# 循环发送每一个文件
self._ReadFromFile(os.path.join(file_path, f))
def _ReadFromFile(self, json_path):
new_lines = []
# 从本地文件读取
if not os.path.exists(json_path):
return
with open(json_path, "r+") as f:
lines = f.readlines()
# file is empty and path is exit -> remove file
if len(lines) == 0 and os.path.exists(json_path):
os.remove(json_path)
return
#send installinfo or updateinfo
for line in lines:
(retval,retid) = self._file_send_server(line)
if retval != 0: # success
new_lines.append(line)
if os.path.exists(json_path):
os.remove(json_path)
if len(new_lines) != 0:
with open(json_path, "w+") as f:
for line in lines:
f.write(line)
def _file_send_server(self, json):
UploadMessage = {}
PackageInfo = {}
encodeMsg = ''
dict_msg = self.collector.convertor.JsonConvertDict(json)
if 'UploadMessage' in dict_msg.keys():
UploadMessage = dict_msg['UploadMessage']
UploadMessage = self.collector.convertor.dictConvertJson(UploadMessage)
if 'PackageInfo' in dict_msg.keys():
PackageInfo = dict_msg['PackageInfo']
PackageInfo = self.collector.convertor.dictConvertJson(PackageInfo)
if 'encodeMsg' in dict_msg.keys():
encodeMsg = str(dict_msg['encodeMsg'])
if len(UploadMessage) == 0 or len(PackageInfo) == 0 or encodeMsg == '':
logging.error("Msg error")
return 6, ''
daqbus = dbus.SystemBus()
try:
daqobj = daqbus.get_object('com.kylin.daq', '/com/kylin/daq')
daqinterface = dbus.Interface(daqobj, dbus_interface='com.kylin.daq.interface')
except DBusException as e:
logging.error(str(e))
try:
retval,retid = daqinterface.UploadMessage(PackageInfo, UploadMessage, encodeMsg)
except AttributeError:
logging.error("Call UploadMessage: Attribute Error.")
return (retval,retid)
class UniqueKey():
keyvalue = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FR\
OEFNSUlCQ2dLQ0FRRUFzdW1NTFJEdlFNb0tEQkRJODRqSgpqc1A0Mk55V0pWVEZob2Jra3ZiT05j\
dExYTXVzRmo2TzJUblZYU3Z6VlVLSjRqZkpwT2l2WEphOVB5Z2wzYTRnClBzSU40enNCMEdOY0tr\
R3VsS2RrV2x6S3lWQ2xlTzhiQnN6SjkwbTc3cWF6YWg3a1A0TUl0WTVFczBpSkpiR0oKN1MxcERj\
MlJkNnVFQWJLaXJyRTFlNzlFTEd4am5VN2V5NWkyRDE2WWJoZEQwZ2lNa2RHR3piQXBKTWZWRVJR\
TQo1NXorMFVqdS8zSFJhNFY3b3p2TGRPRE5HUURaeWNJU0l3VHBLbFR3RjBxazdCNjVhTUlJenQ1\
dnhOK1lxYU1GClppZFRLNzcxNjdqNEExZ3F3MG45bjlybWVXUGRWZ3dudnRtVXp4Q1krNk05SXpK\
TDI3eWpRUTV1WGQ3RVdMT3IKbndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
class PHPServer(threading.Thread):
LOG_PATH = "/var/log/kylin-system-updater"
TMP_LOG_PATH = "/run/kylin-system-updater"
KYLIN_SOFTWARE_PROPERTIES_LOG = "/var/log/kylin-software-properties.log"
PINGBACK_INTERNET_URL = "http://archive1.kylinos.cn:32294/kylin-update-manager-server/main.php?"
PINGBACK_INTERNET_FILE_URL = "http://archive1.kylinos.cn:32294/kylin-update-manager-server/get_file.php?"
PINGBACK_INTRANET_URL = "http://archive.kylinos-intranet.cn/kylin-update-manager-server/main.php?"
PINGBACK_INTRANET_FILE_URL = "http://archive.kylinos-intranet.cn/kylin-update-manager-server/get_file.php?"
SYSTEM_VERSION_PATH = "/etc/kylin-version/kylin-system-version.conf"
def get_values(self, _appname="", _appversion="", _state='', _errorcode='', _errorstring=""):
self.appname = _appname
self.appversion = _appversion
self.status = _state
self.errorcode = _errorcode
self.errorstring = _errorstring
def run(self):
# 获取本机ip
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
host_ip = s.getsockname()[0]
except:
host_ip = 'Ip failed to get'
# 获取系统版本
with open('/etc/lsb-release', 'r') as f:
for line in f.readlines():
if line.strip().startswith('DISTRIB_DESCRIPTION='):
versions = line.strip().split('=')
if "V10" in line and "SP1" in versions[1]:
version = "V10SP1"
else:
version = "V10Pro"
break
# 获取软件版本
output = os.popen('dpkg -l|grep kylin-system-updater').readlines()
if output:
soft_version = output[0].strip().split()[2]
# 获取时间
nowtime = time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(time.time()))
# 获取Mac
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
host_mac = ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
# 获取序列号
if os.path.exists("/etc/.kyinfo"):
kyinfo_configs = UpgradeConfig(datadir = "/etc/", name = ".kyinfo")
key = kyinfo_configs.getWithDefault("servicekey", "key", "0")
else:
key = "0"
log_dir = ""
log_file_gzip = ""
try:
# 用于收集更新器更新日志
if self.status != "success":
nowtime = get_east_8_time()
os.makedirs(self.TMP_LOG_PATH, exist_ok=True)
log_dir = os.path.join(self.TMP_LOG_PATH, host_mac + "_" + nowtime)
log_file_gzip = log_dir + ".tar.gz"
os.makedirs(log_dir, exist_ok=True)
#get updater log
if os.path.exists("/var/log/kylin-system-updater/kylin-system-updater.log.1"):
shutil.copy("/var/log/kylin-system-updater/kylin-system-updater.log.1", log_dir)
if os.path.exists("/var/log/kylin-system-updater/kylin-system-updater.log.1.1.gz"):
shutil.copy("/var/log/kylin-system-updater/kylin-system-updater.log.1.1.gz", log_dir)
#get apt log
if os.path.exists("/var/log/apt/history.log"):
shutil.copy("/var/log/apt/history.log", log_dir)
if os.path.exists("/var/log/apt/term.log"):
shutil.copy("/var/log/apt/term.log", log_dir)
#get version file
if os.path.exists(self.SYSTEM_VERSION_PATH):
shutil.copy(self.SYSTEM_VERSION_PATH, log_dir)
gZipFile(log_dir, log_file_gzip)
header = {'Content-Type': "multipart/form-data", "Accept-Encoding": "gzip"}
try:
with open(log_file_gzip, "rb") as f:
requests.post(self.PINGBACK_INTRANET_FILE_URL + "filename=" + os.path.basename(log_file_gzip),
data=f.read(), headers=header, timeout = (3,3))
except:
with open(log_file_gzip, "rb") as f:
requests.post(self.PINGBACK_INTERNET_FILE_URL + "filename=" + os.path.basename(log_file_gzip),
data=f.read(), headers=header, timeout = (3,3))
else:
log_file_gzip = ""
kmg_tmp = {'ip': host_ip, 'version': version, 'soft_version': soft_version, 'datetime': nowtime,
'host_mac': host_mac, 'appname': self.appname, 'appversion': self.appversion, 'serial_number': key,
'state': self.status, 'filename': log_file_gzip, 'errorcode': self.errorcode, 'errorstring': self.errorstring}
kmg = parse.urlencode(kmg_tmp)
logging.debug("PHPServer UpdateInfos: %s .", kmg_tmp)
# 优先使用内网服务器,再使用外网
try:
url = self.PINGBACK_INTRANET_URL + kmg
req = request.urlopen(url=url, timeout=3)
logging.info("The Intranet log server is successfully accessed, pkgname:%s .",self.appname)
except:
url = self.PINGBACK_INTERNET_URL + kmg
req = request.urlopen(url=url, timeout=3)
logging.info("The external log server is successfully accessed, pkgname:%s .",self.appname)
except Exception as e:
logging.error("Failed to access the external log server: %s, pkgname:%s .", e, self.appname)
if os.path.isfile(log_file_gzip):
os.remove(log_file_gzip)
if os.path.isdir(log_dir):
shutil.rmtree(log_dir)
def PHPSeverSend(_appname="", _appversion="", _statue="", _errorcode="", _errorstring=""):
send_thread = PHPServer()
send_thread.get_values(_appname=_appname, _appversion=_appversion, _state=_statue, _errorcode=_errorcode, _errorstring=_errorstring)
send_thread.start()
def gZipFile(src, dst):
with tarfile.open(dst, "w:gz") as tar:
tar.add(src, arcname=os.path.basename(src))
def get_east_8_time():
import time
# UTC时间
utc_time = datetime.utcnow()
# 转时间字符串
utc_time = utc_time.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
time_suffix = utc_time.split(".")[1]
# 字符串转时间元祖
utc_time = time.strptime(utc_time, "%Y-%m-%d %H:%M:%S.%f")
# 时间元祖转时间戳
utc_time = time.mktime(utc_time)
# 生成东八区时间时间戳
now_time = utc_time + 8*60*60
# 时间戳转时间元祖
now_time = time.localtime(now_time)
# 时间元祖转字符串
now_time = time.strftime("%Y-%m-%d %H:%M:%S",now_time)
now_time = now_time + "." +time_suffix
return now_time
# return 0
def GetActivationCode():
Code = ''
try:
bus = dbus.SystemBus()
obj = bus.get_object('org.freedesktop.activation', '/org/freedesktop/activation')
interface = dbus.Interface(obj, dbus_interface='org.freedesktop.activation.interface')
retval,ret = interface.register_number()
if ret == 0 and len(retval) != 0:
Code = str(retval)
elif len(retval) == 0: # sh 查询
sh_retval = os.popen("kylin_gen_register").read().strip()
if len(sh_retval) != 0:
Code = str(sh_retval)
except Exception as e:
logging.error(str(e))
return ""
return Code
def dictgetattr(_dict, _key, default_value):
if type(_dict) != type(dict()):
return
if _key in _dict.keys():
return _dict[_key]
else :
return default_value
if __name__ == "__main__":
# 执行定时发送
ms = MessageSend()
ms._ReadFromFile("/var/lib/kylin-system-updater/sendinfos/testMsg.json")

View File

@ -0,0 +1,400 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
import logging
import sqlite3
from operator import itemgetter
from gettext import gettext as _
from optparse import OptionParser
DB_UPDATER = "/var/cache/kylin-update-manager/kylin-update-manager.db"
DB_UPGRADE = "/var/cache/kylin-system-updater/kylin-system-updater.db"
VER_DB = "/usr/share/kylin-system-updater/kylin-system-updater.db"
def dateMigration(options=None, old_db=None, old_db_cursor=None, new_db=None, new_db_cursor=None):
print(_("Loading Sqlite3Server..."))
if options==None:
old_path = DB_UPDATER
new_path = DB_UPGRADE
try:
if old_db==None and old_db_cursor==None:
old_db = sqlite3.connect(old_path, check_same_thread=False)
old_db_cursor = old_db.cursor()
if new_db==None and new_db_cursor==None:
new_db = sqlite3.connect(new_path, check_same_thread=False)
new_db_cursor = new_db.cursor()
except Exception as e:
print(e)
sql_commnd = ""
old_cfg_dict = {}
new_cfg_dict = {}
# step 1: 更新旧配置数据
try:
print("更新旧配置数据")
sql_commnd = "SELECT * FROM display where id=1"
old_db_cursor.execute(sql_commnd)
old_cfg = old_db_cursor.fetchone()
for od in old_db_cursor.description:
old_cfg_dict.update({str(od[0]):old_cfg[old_db_cursor.description.index(od)]})
new_db_cursor.execute(sql_commnd)
new_cfg = new_db_cursor.fetchone()
for od in new_db_cursor.description:
new_cfg_dict.update({str(od[0]):new_cfg[new_db_cursor.description.index(od)]})
if "download_limit" in new_cfg_dict.keys() and "download_limit_value" in new_cfg_dict.keys():
if new_cfg_dict['download_limit'] != None or new_cfg_dict['download_limit_value'] != None:
print("目标数据库有更新的配置项")
else:
sql_commnd = "UPDATE display set check_time='"+old_cfg_dict['check_time']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
sql_commnd = "UPDATE display set update_time='"+old_cfg_dict['update_time']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
sql_commnd = "UPDATE display set auto_check='"+old_cfg_dict['auto_check']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
sql_commnd = "UPDATE display set system_version='"+old_cfg_dict['system_version']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
if old_cfg_dict['auto_backup'] != None:
sql_commnd = "UPDATE display set auto_backup='"+old_cfg_dict['auto_backup']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
if 'download_limit' in old_cfg_dict.keys() and old_cfg_dict['download_limit'] != None:
sql_commnd = "UPDATE display set download_limit='"+old_cfg_dict['download_limit']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
if 'download_limit_value' in old_cfg_dict.keys() and old_cfg_dict['download_limit_value'] != None:
sql_commnd = "UPDATE display set download_limit_value='"+old_cfg_dict['download_limit_value']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
except Exception as e:
print(e)
print("更新配置文件错误")
return
# step 2: 更新installed
try:
print("更新installed")
update_record_dict = {}
tmp_update_record_dict = []
sql_commnd = "SELECT * FROM installed"
old_db_cursor.execute(sql_commnd)
update_record = old_db_cursor.fetchall()
sql_commnd = "SELECT * FROM updateinfos"
new_db_cursor.execute(sql_commnd)
new_update_record = new_db_cursor.fetchall()
for ur in update_record:
id,appname,version,time,description,icon,statue,keyword,errorcode = ur
if errorcode in range(200):
errorcode = 'null'
update_record_dict.clear()
update_record_dict.update({"appname":appname})
update_record_dict.update({"version":version})
update_record_dict.update({"time":time})
update_record_dict.update({"description":description})
update_record_dict.update({"icon":icon})
update_record_dict.update({"statue":statue})
update_record_dict.update({"keyword":'1'})
update_record_dict.update({"errorcode":errorcode})
tmp_update_record_dict.append(update_record_dict.copy())
for ur in new_update_record:
id,appname,version,description,date,status,keyword,errorcode = ur
if errorcode in range(200):
errorcode = 'null'
update_record_dict.clear()
update_record_dict.update({"appname":appname})
update_record_dict.update({"version":version})
update_record_dict.update({"time":date})
update_record_dict.update({"description":description})
update_record_dict.update({"icon":None})
update_record_dict.update({"statue":status})
update_record_dict.update({"keyword":'1'})
update_record_dict.update({"errorcode":errorcode})
tmp_update_record_dict.append(update_record_dict.copy())
# 按时间排序
tmp_update_record_dict = sorted(tmp_update_record_dict, key=itemgetter('time'))
print("更新installed success")
except Exception as e:
print(e)
print("更新安装记录错误")
return
try:
# 删除 tmp
# DeleteTable(options.new_path+':'+'tmp')
# 创建表
sql_commnd = "create table IF NOT EXISTS tmp('id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,\
'appname' TEXT,\
'version' TEXT,\
'description' TEXT,\
'date' TEXT,\
'status' TEXT,\
'keyword' TEXT,\
'errorcode' TEXT) "
new_db_cursor.execute(sql_commnd)
# 更新数据
for urd in tmp_update_record_dict:
new_db_cursor.execute(
"insert into tmp (appname, version, description, date, status, keyword, errorcode) values(?,"
"?,?,?,?,?,?)",
(urd['appname'], urd['version'], urd['description'], urd['time'], urd['statue'], urd['keyword'], urd['errorcode']))
new_db.commit()
# 删除updateinfos
sql_commnd = "drop table updateinfos"
new_db_cursor.execute(sql_commnd)
new_db.commit()
# 修改表名
sql_commnd = "alter table tmp rename to updateinfos"
new_db_cursor.execute(sql_commnd)
new_db.commit()
except Exception as e:
print(e)
print("安装记录迁移错误")
return
print("数据迁移成功.")
def CleanTable(db_table):
db_path, table_name = str(db_table).split(":")
if not os.path.isfile(db_path):
print("db path error.")
exit(-1)
print(_("Loading Sqlite3Server..."))
try:
db = sqlite3.connect(db_path, check_same_thread=False)
db_cursor = db.cursor()
sql_commnd = 'delete from '+table_name
db_cursor.execute(sql_commnd)
db.commit()
print("clean %s success."%table_name)
except Exception as e:
print(e)
print("clean %s error."%table_name)
def DeleteTable(db_table):
db_path, table_name = str(db_table).split(":")
if not os.path.isfile(db_path):
print("db path error.")
exit(-1)
print(_("Loading Sqlite3Server..."))
try:
db = sqlite3.connect(db_path, check_same_thread=False)
db_cursor = db.cursor()
sql_commnd = 'drop table '+table_name
db_cursor.execute(sql_commnd)
db.commit()
print("delete %s success."%table_name)
except Exception as e:
print("delete %s error: %s"%(table_name,e))
def _has_first_migration(new_db, new_db_cursor):
try:
sql_commnd = "select * from sqlite_master where type='table' and name='display';"
new_db_cursor.execute(sql_commnd)
retval = new_db_cursor.fetchone()
for rv in retval:
if "firstmigration" in str(rv):
return True
except Exception as e:
print(e)
return False
def _is_first_migration(new_db, new_db_cursor):
try:
sql_commnd = "select firstmigration from display;"
new_db_cursor.execute(sql_commnd)
retval = new_db_cursor.fetchone()
if "yes" in retval:
return True
else :
return False
except Exception as e:
print(e)
return False
def _is_display_exist_fields(field, db, db_cursor):
try:
sql_commnd = "select * from sqlite_master where type='table' and name='display';"
db_cursor.execute(sql_commnd)
retval = db_cursor.fetchone()
for rv in retval:
if field in str(rv):
return True
except Exception as e:
print(e)
return False
return False
def _is_updateinfos_exist_fields(field, db, db_cursor):
try:
sql_commnd = "select * from sqlite_master where type='table' and name='updateinfos';"
db_cursor.execute(sql_commnd)
retval = db_cursor.fetchone()
for rv in retval:
if field in str(rv):
return True
except Exception as e:
print(e)
return False
return False
def _add_display_fields(fields_default, default_table = True):
try:
if "=" not in fields_default:
print("format: field=value")
return False
field, value = fields_default.split('=')
# print(_("Loading Sqlite3Server..."))
db = sqlite3.connect(DB_UPGRADE, check_same_thread=False)
db_cursor = db.cursor()
if default_table:
if _is_display_exist_fields(field, db, db_cursor):
print("field %s is exist."%field)
return False
# 字段不存在,新增字段
sql_commnd = "alter table display add column "+field+" TEXT;"
db_cursor.execute(sql_commnd)
sql_commnd = "UPDATE display SET "+field+"='"+value+"'"
db_cursor.execute(sql_commnd)
db.commit()
else:
if _is_updateinfos_exist_fields(field, db, db_cursor):
print("field %s is exist."%field)
return False
# 字段不存在,新增字段
sql_commnd = "alter table updateinfos add column "+field+" TEXT;"
db_cursor.execute(sql_commnd)
db.commit()
except Exception as e:
print(e)
return False
print("Succeeded in adding field: '%s' "%field)
return True
def _add_new_table(table):
table = str(table).strip()
if "=" not in table:
return False
opt, fields = table.split('=')
try:
if fields == 'tid_search':
db = sqlite3.connect(DB_UPGRADE, check_same_thread=False)
db_cursor = db.cursor()
sql_commnd = "create table IF NOT EXISTS tid_search('id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,\
'key' TEXT,\
'tid' TEXT) "
db_cursor.execute(sql_commnd)
db.commit()
db.close()
except Exception as e:
print(e)
return False
def CopyData():
try:
# 判断新字段是否存在
if (os.path.exists(VER_DB) and os.path.exists(DB_UPGRADE)):
print(_("Loading Sqlite3Server..."))
try:
new_db = sqlite3.connect(DB_UPGRADE, check_same_thread=False)
new_db_cursor = new_db.cursor()
ver_db = sqlite3.connect(VER_DB, check_same_thread=False)
ver_db_cursor = ver_db.cursor()
except Exception as e:
print(e)
if (_has_first_migration(new_db, new_db_cursor)): # 存在 firstmigration
if (_is_first_migration(new_db, new_db_cursor)):
# 数据迁移
dateMigration(new_db=new_db, new_db_cursor=new_db_cursor)
sql_commnd = "UPDATE display SET firstmigration='false';"
new_db_cursor.execute(sql_commnd)
new_db.commit()
else:
print("No data migration is required ...")
else:# 不存在firstmigration
# 新增 firstmigration 字段
sql_commnd = "alter table display add column firstmigration text;"
new_db_cursor.execute(sql_commnd)
sql_commnd = "UPDATE display SET firstmigration='true';"
new_db_cursor.execute(sql_commnd)
#数据迁移
dateMigration(new_db=new_db, new_db_cursor=new_db_cursor)
sql_commnd = "UPDATE display SET firstmigration='false';"
new_db_cursor.execute(sql_commnd)
new_db.commit()
else :
print("Not found kylin-system-updater.db, ensure that \'kylin-system-updater\' is successfully installed ... ")
exit(-1)
except Exception as e:
print(e)
if __name__ == "__main__":
# Begin parsing of options
parser = OptionParser()
parser.add_option ("-d", "--debug", action="store_true", default=False,
help=_("Show debug messages"))
parser.add_option ("-o", "--old-path", dest="old_path",
help=_("Enter the old database address"))
parser.add_option ("-n", "--new-path", dest="new_path",
help=_("Enter the new database address"))
parser.add_option ("-c", "--clean-table", dest="clean_table",
help=_("Clear the table"))
parser.add_option ("-r", "--delete-table", dest="delete_table",
help=_("Delete the table"))
parser.add_option ("-m", "--data-migration", default=False, action="store_true",
dest="data_migration", help=_("data migration"))
parser.add_option ("-f", "--add-display-fields",
dest="add_display_fields", help=_("add display fields"))
parser.add_option ("-u", "--add-updateinfos-fields",
dest="add_updateinfos_fields", help=_("add updateinfos fields"))
parser.add_option ("-t", "--add-new-table",
dest="add_new_table", help=_("add new table"))
(options, args) = parser.parse_args()
if options.clean_table:
if ":" not in options.clean_table:
print("format error: <database:table>")
else:
CleanTable(str(options.clean_table))
if options.delete_table:
if ":" not in options.delete_table:
print("format error: <database:table>")
else:
DeleteTable(str(options.delete_table))
if options.add_display_fields:
_add_display_fields(str(options.add_display_fields))
if options.add_updateinfos_fields:
_add_display_fields(str(options.add_updateinfos_fields), default_table = False)
if options.add_new_table:
_add_new_table(str(options.add_new_table))
if options.data_migration:
CopyData()
exit(0)
if options.old_path or options.new_path:
# 检查文件
if not options.old_path or not options.new_path:
print("parameter error")
exit(-1)
if not os.path.isfile(options.old_path):
print("The source database file does not exist")
exit(-1)
if not os.path.isfile(options.new_path):
print("The destination database file does not exist")
exit(-1)
dateMigration(options)

View File

@ -0,0 +1,810 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
import re
import json
import yaml
import shutil
import sqlite3
import logging
import datetime
from operator import itemgetter
from gettext import gettext as _
from SystemUpdater.Core.errors import *
from SystemUpdater.Core.enums import *
# from SystemUpdater.Core.DataAcquisition import PHPSeverSend
from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig
# from SystemUpdater.Core.utils import get_config_patch
DB_FILE = os.path.join("/var/cache/kylin-system-updater/kylin-system-updater.db")
# UMDB_FILE = os.path.join("/var/cache/kylin-system-updater/kylin-system-updater.db")
DB_UPDATER = "/var/cache/kylin-update-manager/kylin-update-manager.db"
DB_UPGRADE = "/var/cache/kylin-system-updater/kylin-system-updater.db"
VER_DB = "/usr/share/kylin-system-updater/kylin-system-updater.db"
INSTALLED_LIST = [{"item": "errorcode", "type": "int", "default": "0"}]
DISPALY_LIST = []
class Sqlite3Server(object):
def __init__(self, updateManager, _no_DataMigration=False):
self.connect = None
self.window_main = updateManager
# self.config_path = get_config_patch()
self.init_sqlit()
# uncoverable配置文件
self.ucconfigs = UpgradeConfig(datadir = "/etc/kylin-version", name = "kylin-system-version.conf")
self._system_version_config()
#判断数据迁移
if not _no_DataMigration:
if (not os.path.exists(DB_UPDATER)):
logging.debug("Can not found database: %s, should ignore.",DB_UPDATER)
self.__need_DataMigration(mark_FirstMigration = True)
else:
self.__need_DataMigration(mark_FirstMigration = False)
self.current_purge_pkgs = []
self.init_metadata()
def init_metadata(self):
self.deb_metadata = {}
self.deb_metadata.update({"current_install_debfile":""})
self.deb_metadata.update({"absolute_path":""})
self.deb_metadata.update({"debname":""})
self.deb_metadata.update({"deblist":[]})
self.deb_metadata.update({"action":''})
self.deb_metadata.update({"mode":''})
self.deb_metadata.update({"source":''})
self.deb_metadata.update({"sender":''})
self.deb_metadata.update({"caller":''})
self.deb_metadata.update({"old_version":''})
self.deb_metadata.update({"new_version":''})
self.deb_metadata.update({"old_update_version":''})
self.deb_metadata.update({"new_update_version":''})
# Initialize the connection database and modify it to connect when using
def init_sqlit(self):
try:
logging.info(_("Initialize database files ..."))
if not os.path.isfile(DB_FILE):
if not os.path.isdir(os.path.dirname(DB_FILE)):
os.makedirs(os.path.dirname(DB_FILE))
shutil.copy("/usr/share/kylin-system-updater/kylin-system-updater.db", os.path.dirname(DB_FILE))
except Exception as e:
logging.error("Failed to initialize database files: %s", str(e))
#connect连接数据库
def connect_database(self):
try:
self.connect = sqlite3.connect(DB_FILE, check_same_thread=False)
self.cursor = self.connect.cursor()
except Exception as e:
logging.error("Failed to connect database: %s", str(e))
#disconnect连接数据库
def disconnect_database(self):
try:
if self.connect != None:
self.connect.close()
if self.cursor != None:
del self.cursor
except Exception as e:
logging.error("Failed to disconnect database: %s", str(e))
# 数据库表格中动态增加新的字段用于扩展
def insert_new_field(self):
if len(INSTALLED_LIST) == 0 and len(DISPALY_LIST) == 0:
return
self.cursor.execute("select sql from sqlite_master where name='installed'")
installed_sql = self.cursor.fetchone()[0]
pattern = re.compile(r'\"\w+\"')
installed_sql_list = pattern.findall(installed_sql)
for value in INSTALLED_LIST:
for field in installed_sql_list:
if value["item"] == str(field).strip("\""):
break
elif field == installed_sql_list[len(installed_sql_list) - 1]:
try:
if value["default"] != "":
sql = 'alter table installed add column "' + value["item"] + '" ' + value["type"] \
+ ' default ' + str(value["default"])
else:
sql = 'alter table installed add column "' + value["item"] + '" ' + value["type"]
self.cursor.execute(sql)
logging.info(_("installed table insert new field: %s"), value["item"])
except:
logging.error(_("installed table failed to insert a new field:"), value["item"], exc_info=True)
self.cursor.execute("select sql from sqlite_master where name='display'")
display_sql = self.cursor.fetchone()[0]
pattern = re.compile(r'\"\w+\"')
display_sql_list = pattern.findall(display_sql)
for value in DISPALY_LIST:
for field in display_sql_list:
if value["item"] == str(field).strip("\""):
break
elif field == display_sql_list[len(display_sql_list) - 1]:
try:
if value["default"] != "":
sql = 'alter table display add column "' + value["item"] + '" ' + value["type"] \
+ ' default ' + str(value["default"])
else:
sql = 'alter table installed add column "' + value["item"] + '" ' + value["type"]
self.cursor.execute(sql)
logging.info(_("display table insert new field %s"), value["item"])
except:
logging.error(_("display table failed to insert a new field %s"), value["item"], exc_info=True)
# 写入数据到installed表中
def insert_into_installed(self, *args, **kwargs):
self.connect_database()
self.cursor.execute(
"insert into installed (appname, version, time, description, icon, statue, keyword, errorcode) values(?,"
"?,?,?,?,?,?,?)", (args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]))
self.connect.commit()
logging.info("Database: Insert (%s=%s) To installed Complete ...", args[0], args[1])
self.disconnect_database()
# 写入数据到display表中
def insert_into_display(self, *args, **kwargs):
self.connect_database()
try:
sql = "update display set " + args[0] + "='" + args[1] + "' where id = 1"
self.cursor.execute(sql)
self.connect.commit()
except Exception as e:
logging.error("Insert error: %s.", str(e))
self.disconnect_database()
return False
logging.info("Database: Insert (%s=%s) To display Complete ...", args[0], args[1])
self.disconnect_database()
return True
# 写入数据到tid_search表中
def insert_into_tid(self, *args, **kwargs):
self.connect_database()
self.cursor.execute(
"insert into tid_search (key, tid) values(?,?)",
(args[0], args[1]))
self.connect.commit()
logging.info("Database: Insert (%s=%s) To tid_search Complete ...", args[0], args[1])
self.disconnect_database()
# 搜索tid_search表获取tid值
def select_from_tid(self, *args, **kwargs):
retval = ''
self.connect_database()
try:
sql = "select "+args[0]+" from tid_search where key='"+args[1]+"'"
self.cursor.execute(sql)
rets = self.cursor.fetchall()
if len(rets)!= 0:
if len(rets[0])!=0:
ret_first = rets[0]
retval = str(ret_first[0])
except Exception as e:
logging.error("Insert error: %s.", str(e))
self.disconnect_database()
logging.info("Database: Select tid_search data Complete...")
self.disconnect_database()
return retval
# 读出display表中数据
def select_from_display(self, *args, **kwargs):
self.connect_database()
try:
sql = "select "+args[0]+" from display"
self.cursor.execute(sql)
self.connect.commit()
retval = str(self.cursor.fetchone()[0])
except Exception as e:
logging.error("select error: %s.", str(e))
self.disconnect_database()
return "Error"
logging.info("Database: Search display Complete (%s) ...", args[0])
self.disconnect_database()
return retval
# 写入updateinfos表中
def insert_into_updateinfo(self, *args, **kwargs):
self.connect_database()
try:
self.cursor.execute(
"insert into updateinfos (appname, version, description, date, status, keyword, errorcode, appname_cn, status_cn, changelog) values(?,"
"?,?,?,?,?,?,?,?,?)",
(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]))
self.connect.commit()
except Exception as e:
logging.error("Insert error: %s.", str(e))
self.disconnect_database()
logging.info(_("Database: Insert To updateinfos Complete..."))
self.disconnect_database()
#get display data
def gen_display_mate(self)->dict:
mateData = {}
self.connect_database()
try:
sql = "pragma table_info({})".format("display")
self.cursor.execute(sql)
keys=[key[1] for key in self.cursor.fetchall()]
sql = "select * from display where id=1;"
self.cursor.execute(sql)
values = self.cursor.fetchone()
mateData = dict(zip(keys, values))
#
# self.connect.row_factory = self.dictFactory
# values = self.cursor.execute("select * from display where id=1;").fetchall()
except Exception as e:
logging.error("select error: %s.", str(e))
self.disconnect_database()
return mateData
def dictFactory(self,cursor,row):
"""将sql查询结果整理成字典形式"""
d={}
for index,col in enumerate(cursor.description):
d[col[0]]=row[index]
return d
def __need_DataMigration(self, mark_FirstMigration = False):
# mateData = self.gen_display_mate()
# if "firstmigration" in mateData.keys() and mateData["firstmigration"] == "true":
dm = DataMigration()
if (mark_FirstMigration):
dm.MarkFirstMigration()
else:
dm.CopyData()
def _check_upgrade_content(self,cache,content):
upgradeable_groups = []
upgradeable_pkgs = []
for cont in content:
if cont == '':
continue
if cont in cache:
upgradeable_pkgs.append(cont)
else:
upgradeable_groups.append(cont)
return upgradeable_pkgs,upgradeable_groups
# 接收更新列表与信息,生成数据并插入数据库中
def insert_info(self, success,matedata,error_code):
if success:
status = 'success'
status_cn = '成功'
else:
status = 'failed'
status_cn = '失败'
logging.info(matedata)
pkgappname = matedata.get("name",{}).get("zh_CN", "")
pkgversion = matedata.get("version", "")
pkgdescription = matedata.get("description", {}).get("zh_CN", "")
pkgchangelog = matedata.get("changelog", "")
pkgtimestr = datetime.datetime.strftime(datetime.datetime.now(), "%Y-%m-%d %H:%M:%S")
pkgstatus = str(status)
pkgkeyword = str('1')
pkgerrorcode = str(error_code)
pkgappnamecn = str('系统更新')
pkgstatuscn = str(status_cn)
errstr = get_error_description_from_enum(error_code)
self.insert_into_updateinfo(pkgappname, pkgversion, pkgdescription, pkgtimestr, \
pkgstatus, pkgkeyword, errstr, pkgappnamecn, pkgstatuscn, pkgchangelog)
# 系统升级完成 更新版本号
if status == "success":
logging.info("Complete system upgrade, refresh system version ...")
self._refresh_system_version(update_version = pkgversion)
# 获取group信息
def GetGroupmsg(self, appname):
jsonfile = appname+".json"
files = os.listdir(self.config_path) #获取文件夹中所有文件
if jsonfile in files: # 存在
# 读取组JSON文件
with open(self.config_path+jsonfile, "r") as f:
try :
data = json.load(f)
except json.JSONDecodeError as e:
logging.error(str(e))
try:
version = data['version']
if "=" in version:
version = version.split("=")[1].strip()
tmpdescription = data['description']
appname_cn = data['name']['zh_CN']
except Exception as e:
logging.error(str(e))
if "zh_CN" in tmpdescription and "en_US" in tmpdescription:
description = tmpdescription["zh_CN"] + ": " + tmpdescription["en_US"]
return (version,description,appname_cn)
else: # 不存在
return (None, None, None)
def refreshpkglist(self):
pkgs_install = []
pkgs_upgrade = []
pkgs_remove = []
for pkg in self.window_main.cache:
try:
if pkg.marked_install:
pkgs_install.append(pkg.name)
if pkg.marked_upgrade:
pkgs_upgrade.append(pkg.name)
elif pkg.marked_delete:
pkgs_remove.append(pkg.name)
except KeyError:
# pkg missing from fresh_cache can't be modified
pass
return pkgs_install,pkgs_upgrade,pkgs_remove
def _removal_of_marker(self):
try:
marker_path = "/var/cache/kylin-update-manager/ignoreOrDelay"
if os.path.exists(marker_path):
with open(marker_path, 'r+') as f:
line= f.readline()
if "2107" in line or "2203" in line:
f.seek(0)
f.truncate()
except Exception as e:
logging.error("Removing the upgrade success mark error: %s.",str(e))
# #查找数据库
# def find_msg_from_datebase(self, table, field, action = 'check', cid = 0):
# # 查询数据
# try:
# sql = "select "+field+" from "+table
# self.cursor.execute(sql)
# update_count = self.cursor.fetchone()[0]
# logging.info("%d history updates detected.", update_count)
# except Exception as e:
# logging.error("Check update error: %s", str(e))
def _system_version_config(self):
self.connect_database()
try:
sql = "select init_version from display where id=1"
self.cursor.execute(sql)
_is_init_verison = self.cursor.fetchone()[0]
if _is_init_verison == "yes" or _is_init_verison == "true":
logging.info("Need to refresh version ...")
self._initial_system_version()
sql = "update display set init_version = 'no'"
self.cursor.execute(sql)
self.connect.commit()
except Exception as e:
logging.error(str(e))
self.disconnect_database()
def _refresh_system_version(self, update_version = '', pseudo_version = False):
try:
#刷新版本号:update_version、os_version
default_update_version, os_version = self.get_default_version()
if not pseudo_version:
if len(os_version) != 0:
self.ucconfigs.setValue("SYSTEM","os_version",str(os_version),True)
if len(update_version) != 0:
self.ucconfigs.setValue("SYSTEM","update_version",str(update_version),True)
elif len(os_version) != 0 and len(update_version) == 0:
self.ucconfigs.setValue("SYSTEM","update_version",str(os_version),True)
else:
current_update_version, current_os_version = self.get_current_version()
if current_os_version != os_version:
os_version+='*'
self.ucconfigs.setValue("SYSTEM","os_version",str(os_version),True)
if current_update_version != update_version:
update_version+='*'
self.ucconfigs.setValue("SYSTEM","update_version",str(update_version),True)
except Exception as e:
logging.error("Refresh system version error: %s.",str(e))
def _initial_system_version(self):
try:
#刷新版本号:update_version、os_version
update_version, os_version = self.get_default_version()
if len(os_version) != 0:
self.ucconfigs.setValue("SYSTEM","os_version",str(os_version),True)
self.ucconfigs.setValue("SYSTEM","update_version",str(os_version),True)
except Exception as e:
logging.error("Initial system version error: %s.",str(e))
def get_default_version(self):
update_version = ""
os_version = ""
INPUT_CONFIG_PATH = self.config_path + 'kylin-update-desktop-system.json'
if os.path.isfile(INPUT_CONFIG_PATH): # 存在
# 读取JSON文件
with open(INPUT_CONFIG_PATH, "r") as f:
try :
data = json.load(f)
except json.JSONDecodeError as e:
logging.error(str(e))
try:
update_version = data['version']
if "=" in update_version:
update_version = update_version.split('=')[-1].strip()
except Exception as e:
logging.error("get_default_version error: %s .",str(e))
version_path = "/etc/os-release"
if os.path.isfile(version_path):
with open(version_path, "r+") as f:
lines = f.readlines()
for line in lines:
if "KYLIN_RELEASE_ID" in line:
os_version = line.split('=')[1]
os_version = os_version.strip().strip('"')
logging.info('Get default update_version: %s, os_version: %s .', update_version, os_version)
return str(update_version),str(os_version)
def get_current_version(self):
os_version = ''
update_version = ''
try:
if not os.path.exists("/etc/kylin-version/kylin-system-version.conf"):
logging.warning("System version file doesn't exist.")
return os_version,update_version
os_version = eval(str(self.window_main.sqlite3_server.ucconfigs.get("SYSTEM","os_version")))
update_version = str(self.window_main.sqlite3_server.ucconfigs.get("SYSTEM","update_version"))
except Exception as e:
logging.error(str(e))
return update_version,os_version
logging.info('Current os_version: %s, release_id: %s .', os_version, update_version)
return str(update_version),str(os_version)
def get_cn_appname(self, name):
try:
SC_DB_FILE = "/usr/share/kylin-software-center/data/uksc.db"
if os.path.isfile(SC_DB_FILE):
connect = sqlite3.connect(SC_DB_FILE, check_same_thread=False)
cursor = connect.cursor()
else:
logging.warning("software center database isn't exist .")
return ""
sql = "select display_name_cn from application where display_name='"+name+"'"
cursor.execute(sql)
connect.commit()
retval = cursor.fetchone()
connect.close()
if retval != None and len(retval) != 0:
return str(retval[0])
else:
return ''
except Exception as e:
logging.error(_("Failed to initialize the database: %s"), str(e))
return ''
def insert_upgrade_history(self, args, caller):
caller_list = ['kylin-unattended-upgrade', "d-feet", "root"]
_in_list = False
for cl in caller_list:
if caller in cl:
_in_list = True
if _in_list == False:
logging.warning("Caller \" %s \": Operation without permission...", caller)
return False
# {"appname":GLib.Variant("s", "kylin-system-updater"), "version":GLib.Variant("s", "0.0")}
# "description":GLib.Variant("s", "Update Manager for Kylin"), "date":GLib.Variant("s", "2022-07-27 15:23:51")
# "status":GLib.Variant("s", "failed"), "keyword":GLib.Variant("s", "1")
# "errorcode":GLib.Variant("s", "System upgrade is complete. "), "appname_cn":GLib.Variant("s", "音乐")
upgrade_info = {}
try:
for it in args:
upgrade_info[str(it)] = str(args[str(it)])
logging.info("upgrade_info: %s", upgrade_info)
if "appname" in upgrade_info.keys() and "version" in upgrade_info.keys() \
and "description" in upgrade_info.keys() \
and "date" in upgrade_info.keys() \
and "status" in upgrade_info.keys() \
and "keyword" in upgrade_info.keys() \
and "errorcode" in upgrade_info.keys() \
and "appname_cn" in upgrade_info.keys() \
and "status_cn" in upgrade_info.keys() \
and "changelog" in upgrade_info.keys():
appname = upgrade_info["appname"]
if "kylin-unattended-upgrade" == appname:
upgrade_info["appname"] = self.get_cn_appname(appname)
if upgrade_info["appname"] == "":
upgrade_info["appname"] = _("kylin-unattended-upgrade")
if appname in self.window_main.cache and upgrade_info["description"] == "":
pkg = self.window_main.cache[appname]
if pkg.is_installed:
upgrade_info["description"] = pkg.installed.description
self.insert_into_updateinfo( upgrade_info["appname"], upgrade_info["version"], \
upgrade_info["description"], \
upgrade_info["date"], \
upgrade_info["status"], \
upgrade_info["keyword"], \
upgrade_info["errorcode"], \
upgrade_info["appname_cn"], \
upgrade_info["status_cn"], \
upgrade_info["changelog"] )
else:
logging.warning("Incomplete field.")
return False
except Exception as e:
logging.error(e)
return False
class DataMigration():
def __init__(self):
pass
#connect连接数据库
def connect_database(self):
try:
self.connect = sqlite3.connect(DB_FILE, check_same_thread=False)
self.cursor = self.connect.cursor()
except Exception as e:
logging.error("Failed to connect database: %s", str(e))
#disconnect连接数据库
def disconnect_database(self):
try:
if self.connect != None:
self.connect.close()
if self.connect != None:
del self.cursor
except Exception as e:
logging.error("Failed to disconnect database: %s", str(e))
def MarkFirstMigration(self):
if (os.path.exists(DB_UPGRADE)):
try:
new_db = sqlite3.connect(DB_UPGRADE, check_same_thread=False)
new_db_cursor = new_db.cursor()
if (self._has_first_migration(new_db, new_db_cursor)): # 存在 firstmigration
if (self._is_first_migration(new_db, new_db_cursor)):
sql_commnd = "UPDATE display SET firstmigration='false';"
new_db_cursor.execute(sql_commnd)
new_db.commit()
else:# 不存在firstmigration
# 新增 firstmigration 字段
sql_commnd = "alter table display add column firstmigration text;"
new_db_cursor.execute(sql_commnd)
sql_commnd = "UPDATE display SET firstmigration='false';"
new_db_cursor.execute(sql_commnd)
new_db.commit()
except Exception as e:
logging.error("Failed to disconnect database: %s", str(e))
else :
logging.info("Not found kylin-system-updater.db, ensure that \'kylin-system-updater\' is successfully installed ... ")
def CopyData(self):
# 判断数据库是否存在
if (os.path.exists(VER_DB) and os.path.exists(DB_UPGRADE)):
try:
new_db = sqlite3.connect(DB_UPGRADE, check_same_thread=False)
new_db_cursor = new_db.cursor()
ver_db = sqlite3.connect(VER_DB, check_same_thread=False)
ver_db_cursor = ver_db.cursor()
if (self._has_first_migration(new_db, new_db_cursor)): # 存在 firstmigration
if (self._is_first_migration(new_db, new_db_cursor)):
# 数据迁移
if (self.dateMigration(new_db=new_db, new_db_cursor=new_db_cursor)):
sql_commnd = "UPDATE display SET firstmigration='false';"
new_db_cursor.execute(sql_commnd)
new_db.commit()
else:
logging.info("No data migration is required ...")
else:# 不存在firstmigration
# 新增 firstmigration 字段
sql_commnd = "alter table display add column firstmigration text;"
new_db_cursor.execute(sql_commnd)
sql_commnd = "UPDATE display SET firstmigration='yes';"
new_db_cursor.execute(sql_commnd)
#数据迁移
if (self.dateMigration(new_db=new_db, new_db_cursor=new_db_cursor)):
sql_commnd = "UPDATE display SET firstmigration='false';"
new_db_cursor.execute(sql_commnd)
new_db.commit()
except Exception as e:
logging.error("Failed to disconnect database: %s", str(e))
else :
logging.info("Not found kylin-system-updater.db, ensure that \'kylin-system-updater\' is successfully installed ... ")
def _has_first_migration(self, new_db, new_db_cursor)->bool:
try:
sql_commnd = "select * from sqlite_master where type='table' and name='display';"
new_db_cursor.execute(sql_commnd)
retval = new_db_cursor.fetchone()
for rv in retval:
if "firstmigration" in str(rv):
return True
except Exception as e:
logging.error("Failed to get field firstMigration: %s", str(e))
return False
def _is_first_migration(self, new_db, new_db_cursor):
try:
sql_commnd = "select firstmigration from display;"
new_db_cursor.execute(sql_commnd)
retval = new_db_cursor.fetchone()
if "true" in retval or "yes" in retval:
return True
except Exception as e:
logging.error("Failed to select firstMigration: %s", str(e))
return False
def _is_display_exist_fields(self, field, db, db_cursor):
try:
sql_commnd = "select * from sqlite_master where type='table' and name='display';"
db_cursor.execute(sql_commnd)
retval = db_cursor.fetchone()
for rv in retval:
if field in str(rv):
return True
except Exception as e:
logging.error("Failed to select %s: %s", field, str(e))
return False
def _add_display_fields(self, fields_default):
try:
if "=" not in fields_default:
print("format: field=value")
return False
field, value = fields_default.split('=')
print(_("Loading Sqlite3Server..."))
db = sqlite3.connect(DB_UPGRADE, check_same_thread=False)
db_cursor = db.cursor()
if self._is_display_exist_fields(field, db, db_cursor):
print("field %s is exist."%field)
return False
# 字段不存在,新增字段
sql_commnd = "alter table display add column "+field+" TEXT;"
db_cursor.execute(sql_commnd)
sql_commnd = "UPDATE display SET "+field+"='"+value+"'"
db_cursor.execute(sql_commnd)
db.commit()
except Exception as e:
print(e)
return False
print("Succeeded in adding field: '%s' "%field)
return True
def dateMigration(self,new_db,new_db_cursor,old_db=None,old_db_cursor=None)->bool:
sql_commnd = ""
update_record_dict = {}
tmp_update_record_dict = []
old_cfg_dict = {}
new_cfg_dict = {}
try:
# step 1: 数据库初始化
if old_db==None and old_db_cursor==None:
old_db = sqlite3.connect(DB_UPDATER, check_same_thread=False)
old_db_cursor = old_db.cursor()
# step 2: 获取更新数据
sql_commnd = "SELECT * FROM installed"
old_db_cursor.execute(sql_commnd)
update_record = old_db_cursor.fetchall()
for ur in update_record:
id,appname,version,time,description,icon,statue,keyword,errorcode = ur
if keyword == '2': #2本条数据是deb安装记录,不迁移
continue
if errorcode in range(200):
errorcode = 'null'
update_record_dict.clear()
update_record_dict.update({"appname":appname})
update_record_dict.update({"version":version})
update_record_dict.update({"time":time})
update_record_dict.update({"description":description})
update_record_dict.update({"icon":icon})
if statue == 'Success':
statue = 'success'
elif statue == 'Fail':
statue = 'failed'
update_record_dict.update({"statue":statue})
update_record_dict.update({"keyword":'1'})
update_record_dict.update({"errorcode":errorcode})
tmp_update_record_dict.append(update_record_dict.copy())
# 按时间排序
tmp_update_record_dict = sorted(tmp_update_record_dict, key=itemgetter('time'))
logging.info("Generate update records complete.")
# step 3: insert新生成的记录
sql_commnd = "create table IF NOT EXISTS tmp('id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,\
'appname' TEXT,\
'version' TEXT,\
'description' TEXT,\
'date' TEXT,\
'status' TEXT,\
'keyword' TEXT,\
'errorcode' TEXT,\
'appname_cn' TEXT,\
'status_cn' TEXT,\
'changelog' TEXT) "
new_db_cursor.execute(sql_commnd)
# 更新数据
for urd in tmp_update_record_dict:
new_db_cursor.execute(
"insert into tmp (appname, version, description, date, status, keyword, errorcode) values(?,"
"?,?,?,?,?,?)",
(urd['appname'], urd['version'], urd['description'], urd['time'], urd['statue'], urd['keyword'], urd['errorcode']))
new_db.commit()
# 备份updateinfos
new_db_cursor.execute(
"insert into tmp (appname, version, description, date, status, keyword, errorcode, appname_cn, status_cn, changelog) "
"SELECT appname, version, description, date, status, keyword, errorcode, appname_cn, status_cn, changelog FROM updateinfos")
new_db.commit()
# 删除updateinfos
sql_commnd = "drop table updateinfos"
new_db_cursor.execute(sql_commnd)
new_db.commit()
# 修改表名
sql_commnd = "alter table tmp rename to updateinfos"
new_db_cursor.execute(sql_commnd)
new_db.commit()
logging.info("Insert update records complete.")
# step 4: 更新配置转移
sql_commnd = "SELECT * FROM display where id=1"
old_db_cursor.execute(sql_commnd)
old_cfg = old_db_cursor.fetchone()
if not old_cfg:
logging.warning("Table 'display' is empty .")
else:
for od in old_db_cursor.description:
old_cfg_dict.update({str(od[0]):old_cfg[old_db_cursor.description.index(od)]})
if old_db != None:
old_db.close()
if old_db_cursor != None:
del old_db_cursor
new_db_cursor.execute(sql_commnd)
new_cfg = new_db_cursor.fetchone()
for od in new_db_cursor.description:
new_cfg_dict.update({str(od[0]):new_cfg[new_db_cursor.description.index(od)]})
sql_commnd = "UPDATE display set check_time='"+old_cfg_dict['check_time']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
sql_commnd = "UPDATE display set update_time='"+old_cfg_dict['update_time']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
sql_commnd = "UPDATE display set auto_check='"+old_cfg_dict['auto_check']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
sql_commnd = "UPDATE display set system_version='"+old_cfg_dict['system_version']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
if old_cfg_dict['auto_backup'] != None:
sql_commnd = "UPDATE display set auto_backup='"+old_cfg_dict['auto_backup']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
if 'download_limit' in old_cfg_dict.keys() and old_cfg_dict['download_limit'] != None:
sql_commnd = "UPDATE display set download_limit='"+old_cfg_dict['download_limit']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
if 'download_limit_value' in old_cfg_dict.keys() and old_cfg_dict['download_limit_value'] != None:
sql_commnd = "UPDATE display set download_limit_value='"+old_cfg_dict['download_limit_value']+"' Where id=1"
new_db_cursor.execute(sql_commnd)
new_db.commit()
except Exception as e:
logging.warning("Update table 'display' error: %s .",e)
logging.info("The data migration is complete.")
return True
def listtojsonstr(lists):
import json
jsonfile = json.dumps(lists)
return jsonfile

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
import os
from gettext import gettext as _
import apt
import logging
import fcntl
import apt_pkg
class LogInstallProgress(apt.progress.base.InstallProgress):
""" Install progress that writes to self.progress_log
(/var/run/unattended-upgrades.progress by default)
"""
def __init__(self,file):
# type: (str) -> None
apt.progress.base.InstallProgress.__init__(self)
self.output_logfd = None # type: int
self.filename=file
self.error_pkg=""
self.errormsg=""
# raise Exception("for test!!!")
def error(self,pkg, errormsg):
logging.error(("Install mode - dpkg, Install error: %s"), errormsg)
self.error_pkg=self.filename
self.errormsg = errormsg
def status_change(self, pkg, percent, status):
# type: (str, float, str) -> None
logging.info(("pkg:%s,percent:%s,status:%s"),pkg,percent,status)
with open(self.progress_log, "w") as f:
f.write(("%s")%percent)
f.write(_("当前进度: %s ,正在安装:%s,当前状态:%s") % (percent, pkg,status))
f.write(_("Progress: %s %% (%s)") % (percent, pkg))
def _fixup_fds(self):
# () -> None
required_fds = [0, 1, 2, # stdin, stdout, stderr
self.writefd,
self.write_stream.fileno(),
self.statusfd,
self.status_stream.fileno()
]
# ensure that our required fds close on exec
for fd in required_fds[3:]:
old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
# close all fds
try:
# PEP-446 implemented in Python 3.4 made all descriptors
# CLOEXEC, but we need to be able to pass writefd to dpkg
# when we spawn it
os.set_inheritable(self.writefd, True)
except AttributeError: # if we don't have os.set_inheritable()
pass
proc_fd = "/proc/self/fd"
if os.path.exists(proc_fd):
error_count = 0
for fdname in os.listdir(proc_fd):
try:
fd = int(fdname)
except Exception:
print("ERROR: can not get fd for %s" % fdname)
if fd in required_fds:
continue
try:
os.close(fd)
# print("closed: ", fd)
except OSError as e:
# there will be one fd that can not be closed
# as its the fd from pythons internal diropen()
# so its ok to ignore one close error
error_count += 1
if error_count > 1:
print("ERROR: os.close(%s): %s" % (fd, e))
def _redirect_stdin(self):
# type: () -> None
REDIRECT_INPUT = os.devnull
fd = os.open(REDIRECT_INPUT, os.O_RDWR)
os.dup2(fd, 0)
def _redirect_output(self):
# type: () -> None
# do not create log in dry-run mode, just output to stdout/stderr
if not apt_pkg.config.find_b("Debug::pkgDPkgPM", False):
logfd = self._get_logfile_dpkg_fd()
os.dup2(logfd, 1)
os.dup2(logfd, 2)
def _get_logfile_dpkg_fd(self):
# type: () -> int
logfd = os.open(
"/var/log/kylin-system-updater/kylin-system-updater.log.1", os.O_RDWR | os.O_APPEND | os.O_CREAT, 0o640)
try:
import grp
adm_gid = grp.getgrnam("adm").gr_gid
os.fchown(logfd, 0, adm_gid)
except (KeyError, OSError):
pass
return logfd
def update_interface(self):
# type: () -> None
# call super class first
apt.progress.base.InstallProgress.update_interface(self)
def _log_in_dpkg_log(self, msg):
# type: (str) -> None
logfd = self._get_logfile_dpkg_fd()
os.write(logfd, msg.encode("utf-8"))
os.close(logfd)
def finish_update(self):
# if error_status == 1:
# os._exit(1)
pass
def fork(self):
pid = os.fork()
if pid == 0:
self._fixup_fds()
self._redirect_stdin()
self._redirect_output()
return pid

View File

@ -0,0 +1,54 @@
#!/usr/bin/python3
# DistUpgradeConfigParser.py
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
import json
import logging
class Singleton(object):
def __init__(self, cls):
self._cls = cls
self._instance = {}
def __call__(self):
if self._cls not in self._instance:
self._instance[self._cls] = self._cls()
return self._instance[self._cls]
@Singleton
class JsonConfig():
NOW_UPDATE_CONFIG = '/usr/share/kylin-update-desktop-config/config/kylin-update-desktop-system.json'
def __init__(self):
self._configDate = None
self.read()
def read(self):
try:
with open(self.NOW_UPDATE_CONFIG,'r') as f:
self._configDate = json.load(f)
logging.info("Finished: refreshing Json Configuration File Successfully...")
except Exception as exc:
self._configDate = None
def getWithDefault(self,key1=None,key2=None,key3=None,key4=None,default=None):
try:
if self._configDate == None:
return default
if key4 != None:
return self._configDate[key1][key2][key3][key4]
elif key3 != None:
return self._configDate[key1][key2][key3]
elif key2 != None:
return self._configDate[key1][key2]
elif key1 != None:
return self._configDate[key1]
else:
return self._configDate
except Exception as e:
logging.warning(str(e))
return default
if __name__ == "__main__":
pass

View File

@ -0,0 +1,70 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#参考文档: https://cuiqingcai.com/6080.html
import os
path = '/var/log/kylin-system-updater/'
numlist = []
def get_FileSize(filePath):
fsize = os.path.getsize(filePath)
fsize = fsize / float(1024 * 1024)
return round(fsize, 2)
#日志回滚
def get_logfile():
if not os.path.exists(path):
os.makedirs(path)
return os.path.join(path, "kylin-system-updater.log.1")
#优先获取当前未写满100M的日志编号文件
for i in os.listdir(path):
if "kylin-system-updater.log." in os.path.basename(path + i):
numlist.append((path + i).split(".")[2])
if get_FileSize(path + i) < 100:
return path + i
#获取1-5未使用的最小数字的标号作为日志文件
for i in range(1, 6):
if str(i) not in numlist:
return(os.path.join(path, ("kylin-system-updater.log.%s") % i))
try:
#编号1-5日志文件均写满时删除第一个往前移动日志编号获取最后一个编号作为日志文件
if len(numlist) != 0:
os.remove(os.path.join(path, "kylin-system-updater.log.1"))
for i in range(2, 6):
os.rename(os.path.join(path, ("kylin-system-updater.log.%s") % i),
os.path.join(path, ("kylin-system-updater.log.%s") % (i - 1)))
return os.path.join(path, "kylin-system-updater.log.5")
#默认情况下未生成任何日志时使用编号1的日志文件
else:
return os.path.join(path, "kylin-system-updater.log.1")
except:
return os.path.join(path, "kylin-system-updater.log.1")
def upgrade_strategies_logfile():
if not os.path.exists(path):
os.makedirs(path)
#优先获取当前未写满100M的日志编号文件
for i in os.listdir(path):
if "upgrade-strategies-daemon.log." in os.path.basename(path + i):
numlist.append((path + i).split(".")[2])
if get_FileSize(path + i) < 10:
return path + i
#获取1-5未使用的最小数字的标号作为日志文件
for i in range(1, 6):
if str(i) not in numlist:
return(os.path.join(path, ("upgrade-strategies-daemon.log.%s") % i))
try:
#编号1-5日志文件均写满时删除第一个往前移动日志编号获取最后一个编号作为日志文件
if len(numlist) != 0:
os.remove(os.path.join(path, "upgrade-strategies-daemon.log.1"))
for i in range(2, 6):
os.rename(os.path.join(path, ("upgrade-strategies-daemon.log.%s") % i),
os.path.join(path, ("upgrade-strategies-daemon.log.%s") % (i - 1)))
return os.path.join(path, "upgrade-strategies-daemon.log.5")
#默认情况下未生成任何日志时使用编号1的日志文件
else:
return os.path.join(path, "upgrade-strategies-daemon.log.1")
except:
return os.path.join(path, "upgrade-strategies-daemon.log.1")

View File

@ -0,0 +1,88 @@
# MyCache.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2004-2008 Canonical
#
# Author: Michael Vogt <mvo@debian.org>
#
# 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 2 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
import gi
gi.require_version('OSTree', '1.0')
from gi.repository import OSTree
import logging
LOCAL_SUMMARY_DATA = "/var/ostree-summary"
class MyCache():
def __init__(self):
# sysroot
self.sysroot = OSTree.Sysroot.new_default()
self.sysroot.set_mount_namespace_in_use()
self.sysroot.initialize()
self.sysroot.load()
self.deployment = self.sysroot.get_booted_deployment()
self.cur_checksum = self.deployment.get_csum()
self.osname = self.deployment.get_osname()
oringin_f = self.deployment.get_origin()
refspec = oringin_f.get_string("origin","refspec")
ret,self.origin_remote,self.origin_ref = OSTree.parse_refspec(refspec)
self.available_checksum = self.cur_checksum
self.available_refs = self.origin_ref
def is_upgradable(self):
return self.available_checksum != self.deployment.get_osname()
def _initDepCache(self):
self._depcache.read_pinfile()
self._depcache.init()
def clear(self):
self._initDepCache()
def list_refs(self):
repo = self.sysroot.repo()
ret,all_refs = repo.list_refs(None,None)
refs = list(all_refs.keys())
return refs
def rollback_deployment(self):
repo = self.sysroot.repo()
out_pending,out_rollback = self.sysroot.query_deployments_for(self.osname)
if out_rollback:
rollback_checksum = self.deployment.get_csum()
ret,metadata = repo.load_variant(OSTree.ObjectType.COMMIT,rollback_checksum)
n_metadata = metadata[0]
return n_metadata
else:
return ""
def get_metadata(self,checksum):
repo = self.sysroot.repo()
ret,metadata = repo.load_variant(OSTree.ObjectType.COMMIT,checksum)
return metadata[0]

View File

@ -0,0 +1,580 @@
#!/usr/bin/python3
import apt
import apt_pkg
import fnmatch
import logging
import logging.handlers
import re
import os
import sys
import string
import subprocess
import json
try:
from typing import List
from typing import Union
except ImportError:
pass
from gettext import gettext as _
SYSTEM_UPDATER_CORE_LIB_PATH="/usr/share/kylin-system-updater/"
sys.path.append(SYSTEM_UPDATER_CORE_LIB_PATH)
from SystemUpdater.Core.utils import get_config_patch
ImportantListPath="/var/lib/kylin-software-properties/template/important.list"
SOURCESLIST = "/etc/apt/sources.list"
# no py3 lsb_release in debian :/
DISTRO_CODENAME = subprocess.check_output(
["lsb_release", "-c", "-s"], universal_newlines=True).strip() # type: str
DISTRO_DESC = subprocess.check_output(
["lsb_release", "-d", "-s"], universal_newlines=True).strip() # type: str
DISTRO_ID = subprocess.check_output(
["lsb_release", "-i", "-s"], universal_newlines=True).strip() # type: str
ARCHITECTUREMAP = ['arm64','amd64','armhf','i386','loongarch64','mips64el','sw64']
RELEASEOFFSET = 1
ORIGINOFFSET = 2
HTTPTYPE = "HTTP"
FTPTYPE = "FTP"
class UpdateListFilterCache(apt.Cache):
def __init__(self, window_main):
self.window_main = window_main
# whitelist
self.upgradeList = []
# 必须升级的包
self.installList = []
self.config_path = get_config_patch()
# 获取源属性
self.origin_property = OriginProperty()
self.origin_property.get_allowed_sources()
self.origin_property.get_allowed_origin()
self.allowed_origins = get_allowed_origins(self.origin_property.allow_origin)
self.allowed_origins = deleteDuplicatedElementFromList(self.allowed_origins)
logging.info(_("Allowed origins: %s"),
self.allowed_origins)
# self.blacklist = apt_pkg.config.value_list(
# "Kylin-system-updater::Package-Blacklist")
# self.blacklist = deleteDuplicatedElementFromList(self.blacklist)
# self.whitelist = apt_pkg.config.value_list(
# "Kylin-system-updater::Package-Whitelist")
# self.whitelist = deleteDuplicatedElementFromList(self.whitelist)
# self.strict_whitelist = apt_pkg.config.find_b(
# "Kylin-system-updater::Package-Whitelist-Strict", False)
def checkInCache(self):
logging.info("start Check in cache")
tmplist = []
cache = apt.Cache()
for i in self.upgradeList:
try:
cache[i]
tmplist.append(i)
except Exception as e:
pass
self.upgradeList = tmplist
def initLocalPackagesList(self):
jsonfiles = []
tmplist = []
# 获取importantlist 本次更新推送
with open(ImportantListPath, 'r') as f:
text = f.read()
importantList = text.split()
logging.info("importantList: %s",importantList)
f.close()
if not importantList:
logging.error("importantList is empty")
exit(-1)
# 获取/usr/share/kylin-update-desktop-config/data/下所有json文件
for root,dirs,files in os.walk(self.config_path):
pass
for i in files:
if ".json" in i:
jsonfiles.append(i.split('.')[0])
# 找到importantlist中对应的json文件
for i in importantList:
if i not in jsonfiles:
# 说明这个是单独的包,不在分组中
# 加入更新列表
if i not in self.upgradeList:
self.upgradeList.append(i)
else:
# 在分组中
# 获取每个对应json文件中的upgrade_list
if i in jsonfiles:
filepath = os.path.join(self.config_path, i)
filepath = filepath+".json"
with open(filepath, 'r') as f:
pkgdict = f.read()
jsonfile = json.loads(pkgdict)
tmplist = jsonfile['install_list']
for j in tmplist:
if j not in self.upgradeList:
self.upgradeList.append(j)
f.close()
# 更改传入包列表经过源过滤返回的pkg中进行版本调整
def check_in_allowed_origin(self, pkg_lists, _is_adjust):
new_upgrade_pkgs = []
adjust_candidate_pkgs = []
for pkg in pkg_lists:
try:
new_ver = ver_in_allowed_origin(pkg, self.allowed_origins)
if _is_adjust and len(new_ver) == 0:
logging.warning("< %s > did not find a suitable version..." % pkg.name)
continue
if len(new_ver) == 0:
continue
if not pkg.installed: # 判断安装列表
if pkg.candidate == new_ver[0] and pkg not in new_upgrade_pkgs:
new_upgrade_pkgs.append(pkg)
elif new_ver[0] != pkg.candidate and pkg not in new_upgrade_pkgs:
logging.info("adjusting candidate version: %s" % new_ver[0])
if _is_adjust == True:
pkg.candidate = new_ver[0]
adjust_candidate_pkgs.append(pkg.name+"="+pkg.candidate.version)
new_upgrade_pkgs.append(pkg)
else: # 判断升级列表
for nv in new_ver:
if nv > pkg.installed and nv != pkg.candidate:
logging.info("adjusting candidate version: %s" % nv)
if _is_adjust == True:
pkg.candidate = nv
adjust_candidate_pkgs.append(pkg.name+"="+pkg.candidate.version)
break
elif nv > pkg.installed and nv == pkg.candidate:
new_upgrade_pkgs.append(pkg)
break
elif _is_adjust == True:
logging.warning("< %s > did not find a suitable version..." % pkg.name)
except NoAllowedOriginError:
logging.error("Cannot found allowed version: %s", pkg.name)
continue
return (new_upgrade_pkgs, adjust_candidate_pkgs)
def is_pkgname_in_blacklist(self, pkgs):
blacklist_filter_pkgs = []
for pkg in pkgs:
if pkg.name in self.blacklist:
pass
else :
blacklist_filter_pkgs.append(pkg)
return blacklist_filter_pkgs
def is_pkgname_in_whitelist(self, pkgs):
whitelist_filter_upgrade_pkgs = []
for pkg in pkgs:
if pkg.name in self.upgradeList:
whitelist_filter_upgrade_pkgs.append(pkg)
else :
pkg.mark_keep()
return whitelist_filter_upgrade_pkgs
class OriginProperty():
def __init__(self):
# 包含了本地所有源 http & ftp
self.local_sourcelist = {"http":[],"ftp":[]}
# 经过解析后的本地源,获取所有的分发属性
self.local_origin = {"http":[],"ftp":[]}
# 允许的源列表
self.allow_sources = []
# 允许的源+属性
self.allow_origin = {"http":[],"ftp":[]}
# 加载本地所有源
self.init_local_origin()
# 进行属性解析
self.analytic_properties(self.local_sourcelist)
def init_local_origin(self):
http_origin = {}
ftp_orgin = {}
#apt policy
sh_retval = os.popen("apt-cache policy").read().split("\n")
# policy = [ rv for rv in sh_retval if "http" in rv or "ftp" in rv or "release" in rv or "origin" in rv]
for rv in sh_retval:
if "http" in rv:
http_origin['sources'] = rv
http_origin['release'] = sh_retval[sh_retval.index(rv) + RELEASEOFFSET]
http_origin['origin'] = sh_retval[sh_retval.index(rv) + ORIGINOFFSET]
self.local_sourcelist['http'].append(http_origin.copy())
elif "ftp" in rv:
ftp_orgin['sources'] = rv
ftp_orgin['release'] = sh_retval[sh_retval.index(rv) + RELEASEOFFSET]
ftp_orgin['origin'] = sh_retval[sh_retval.index(rv) + ORIGINOFFSET]
self.local_sourcelist['ftp'].append(ftp_orgin.copy())
def merge_origin(self, source_type, source_origin):
is_append = True
if source_type == HTTPTYPE:
if self.local_origin['http']:
for lo in self.local_origin['http']:
if lo['origin_source'] == source_origin['origin_source'] and lo['dist'] == source_origin['dist']:
lo['component'] = list(set(lo['component']).union(set(source_origin['component'])))
is_append = False
if is_append:
self.local_origin['http'].append(source_origin.copy())
else:
self.local_origin['http'].append(source_origin.copy())
elif source_type == FTPTYPE:
if self.local_origin['ftp']:
for lo in self.local_origin['ftp']:
if lo['origin_source'] == source_origin['origin_source'] and lo['dist'] == source_origin['dist']:
lo['component'] = list(set(lo['component']).union(set(source_origin['component'])))
is_append = False
if is_append:
self.local_origin['ftp'].append(source_origin.copy())
else:
self.local_origin['ftp'].append(source_origin.copy())
def analytic_properties(self, local_sourcelist):
http_origin = {"component":[],"release":{}}
ftp_orgin = {"component":[],"release":{}}
dist_list = []
# 经过解析后的本地源,获取所有的分发属性
for ls in local_sourcelist['http']:
for item in filter(not_empty, ls['sources'].split(' ')):
if item.isdigit():
http_origin['policy_priority'] = item
elif "http" in item:
http_origin['origin_source'] = item
elif "/" in item:
dist_list = item.split("/")
dist_list.pop()
http_origin['dist'] = "/".join(dist_list)
http_origin['component'].append(item.split("/")[1])
elif item not in ARCHITECTUREMAP and item != "Packages":
http_origin['component'].append(item)
release_list = ls['release'].split(',')
release_list = [ rl.strip() for rl in release_list ]
if "release" in release_list[0]:
release_list[0] = release_list[0].lstrip("release").strip()
for rl in release_list:
if "=" in rl:
self.generate_dict(http_origin['release'], rl)
for item in filter(not_empty, ls['origin'].split(' ')):
if "origin" not in ls['origin']:
break
elif "origin" != item:
http_origin['origin'] = item
self.merge_origin(HTTPTYPE, http_origin)
http_origin = {"component":[],"release":{}}
for ls in local_sourcelist['ftp']:
for item in filter(not_empty, ls['sources'].split(' ')):
if item.isdigit():
ftp_orgin['policy_priority'] = item
elif "ftp" in item:
ftp_orgin['origin_source'] = item
elif "/" in item:
ftp_orgin['dist'] = item.split("/")[0]
ftp_orgin['component'].append(item.split("/")[1])
elif item not in ARCHITECTUREMAP and item != "Packages":
ftp_orgin['component'].append(item)
release_list = ls['release'].split(',')
if "release " in release_list[0]:
release_list[0] = release_list[0].lstrip("release ")
for rl in release_list:
if "=" in rl:
self.generate_dict(ftp_orgin['release'], rl)
for item in filter(not_empty, ls['origin'].split(' ')):
if "origin" not in ls['origin']:
break
elif "origin" != item:
ftp_orgin['origin'] = item
self.merge_origin(FTPTYPE, ftp_orgin)
ftp_orgin = {"component":[],"release":{}}
def generate_dict(self, dict, item):
item = item.strip()
if item == "":
logging.warning("empty match string matches nothing")
return False
(what, value) = [ s for s in item.split("=")]
if what in ('o', 'origin'):
dict['origin'] = value
elif what in ("l", "label"):
dict['label'] = value
elif what in ("a", "suite", "archive"):
dict['archive'] = value
elif what in ("c", "component"):
dict['component'] = value
elif what in ("site",):
dict['site'] = value
elif what in ("n", "codename",):
dict['codename'] = value
else:
dict[what] = value
# raise UnknownMatcherError(
# "Unknown whitelist entry for matcher %s (value %s)" % (
# what, value))
def get_allowed_sources(self):
# 源地址,在本地源列表中查找. 源服务器下发source.list为允许的源, 本模块屏蔽了sources.list.d下的源
# 获取允许的源
try:
old_sources_list = apt_pkg.config.find("Dir::Etc::sourcelist")
old_sources_list_d = apt_pkg.config.find("Dir::Etc::sourceparts")
old_cleanup = apt_pkg.config.find("APT::List-Cleanup")
apt_pkg.config.set("Dir::Etc::sourcelist",
os.path.abspath(SOURCESLIST))
apt_pkg.config.set("Dir::Etc::sourceparts", "xxx")
apt_pkg.config.set("APT::List-Cleanup", "0")
slist = apt_pkg.SourceList()
slist.read_main_list()
self.allow_sources = slist.list
except Exception as e:
logging.error(str(e))
finally:
apt_pkg.config.set("Dir::Etc::sourcelist",
old_sources_list)
apt_pkg.config.set("Dir::Etc::sourceparts",
old_sources_list_d)
apt_pkg.config.set("APT::List-Cleanup",
old_cleanup)
def get_allowed_origin(self):
# 获取允许的源
# 生成源与属性
self.local_origin
self.allow_sources
self.allow_origin
try:
for item in self.allow_sources:
for lo in self.local_origin['http']:
if item.uri.strip('/') == lo['origin_source'].strip('/') and item.dist == lo['dist']:
self.allow_origin['http'].append(lo)
for lo in self.local_origin['ftp']:
if item.uri.strip('/') == lo['origin_source'].strip('/') and item.dist == lo['dist']:
self.allow_origin['ftp'].append(lo)
except Exception as e:
logging.error(str(e))
class UnattendUpgradeFilter():
def __init__(self) -> None:
pass
def GetAllowOrigins(self):
# 获取源属性
self.origin_property = OriginProperty()
self.origin_property.get_allowed_sources()
self.origin_property.get_allowed_origin()
self.allowed_origins = get_allowed_origins(self.origin_property.allow_origin)
self.allowed_origins = deleteDuplicatedElementFromList(self.allowed_origins)
logging.info(_("Allowed origins: %s"),
self.allowed_origins)
return self.allowed_origins
def ver_in_allowed_origin(pkg, allow_origin):
# type: (apt.Package, List[str]) -> apt.package.Version
allown_versions = []
versions = _get_priority_order(pkg)
# 获取每个优先级别中 允许源的最高版本
allown_versions = _get_allowed_list(versions, allow_origin)
return allown_versions
def _get_priority_order(pkg):
versions = []
for ver in pkg.versions:
if versions:
for v in versions:
if v.policy_priority >= ver.policy_priority and v == versions[-1]:
break
elif v.policy_priority >= ver.policy_priority and v != versions[-1]:
continue
else:
index = versions.index(v)
versions.insert(index,ver)
break
if v == versions[-1] and versions[-1].policy_priority >= ver.policy_priority:
versions.append(ver)
else:
versions.append(ver)
return versions
def _get_allowed_list(versions, allow_origin):
current_priority = -100
allown_versions = []
for ver in versions:
if current_priority != ver.policy_priority:
if is_in_allowed_origin(ver, allow_origin):
allown_versions.append(ver)
current_priority = ver.policy_priority
else:
continue
return allown_versions
def get_allowed_origins(allow_origin):
""" return a list of allowed origins
"""
allowed_origins = []
origin = ''
archive = ''
uri = ''
label = ''
for ao in (allow_origin['http']+allow_origin['ftp']):
if 'origin' in ao['release']:
origin = 'o='+ao['release']['origin']
else:
origin = 'o='
if 'archive' in ao['release']:
archive = 'a='+ao['release']['archive']
else:
archive = 'a='
if 'label' in ao['release']:
label = 'l='+ao['release']['label']
else:
label = 'l='
if 'origin_source' in ao:
uri = 'uri='+ao['origin_source']
else:
uri = 'uri='
allowed_origins.append(origin+","+archive+","+label+","+uri)
return allowed_origins
def get_allowed_origins_legacy():
# type: () -> List[str]
""" legacy support for old Allowed-Origins var """
allowed_origins = [] # type: List[str]
key = "Kylin-system-updater::Allowed-Origins"
try:
for s in apt_pkg.config.value_list(key):
# if there is a ":" use that as seperator, else use spaces
if re.findall(r'(?<!\\):', s):
(distro_id, distro_codename) = re.split(r'(?<!\\):', s)
else:
(distro_id, distro_codename) = s.split()
# unescape "\:" back to ":"
distro_id = re.sub(r'\\:', ':', distro_id)
# escape "," (see LP: #824856) - can this be simpler?
distro_id = re.sub(r'([^\\]),', r'\1\\,', distro_id)
distro_codename = re.sub(r'([^\\]),', r'\1\\,', distro_codename)
# convert to new format
allowed_origins.append("o=%s,a=%s" % (substitute(distro_id),
substitute(distro_codename)))
except ValueError:
logging.error(_("Unable to parse %s." % key))
raise
return allowed_origins
def substitute(line):
# type: (str) -> str
""" substitude known mappings and return a new string
Currently supported ${distro-release}
"""
mapping = {"distro_codename": get_distro_codename(),
"distro_id": get_distro_id()}
return string.Template(line).substitute(mapping)
def get_distro_codename():
# type: () -> str
return DISTRO_CODENAME
def get_distro_id():
# type: () -> str
return DISTRO_ID
def is_in_allowed_origin(ver, allowed_origins):
# type: (apt.package.Version, List[str]) -> bool
if not ver:
return False
for origin in ver.origins:
if is_allowed_origin(origin, allowed_origins):
return True
return False
def is_allowed_origin(origin, allowed_origins):
# type: (Union[apt.package.Origin, apt_pkg.PackageFile], List[str]) -> bool
for allowed in allowed_origins:
if match_whitelist_string(allowed, origin):
return True
return False
def match_whitelist_string(whitelist, origin):
# type: (str, Union[apt.package.Origin, apt_pkg.PackageFile]) -> bool
"""
take a whitelist string in the form "origin=Debian,label=Debian-Security"
and match against the given python-apt origin. A empty whitelist string
never matches anything.
"""
whitelist = whitelist.strip()
if whitelist == "":
logging.warning("empty match string matches nothing")
return False
res = True
# make "\," the html quote equivalent
whitelist = whitelist.replace("\\,", "%2C")
for token in whitelist.split(","):
# strip and unquote the "," back
(what, value) = [s.strip().replace("%2C", ",")
for s in token.split("=")]
# logging.debug("matching %s=%s against %s" % (
# what, value, origin))
# support substitution here as well
value = substitute(value)
# first char is apt-cache policy output, send is the name
# in the Release file
if what in ("o", "origin"):
match = fnmatch.fnmatch(origin.origin, value)
elif what in ("l", "label"):
match = fnmatch.fnmatch(origin.label, value)
elif what in ("a", "suite", "archive"):
match = fnmatch.fnmatch(origin.archive, value)
elif what in ("c", "component"):
match = fnmatch.fnmatch(origin.component, value)
elif what in ("site",):
match = fnmatch.fnmatch(origin.site, value)
elif what in ("n", "codename",):
match = fnmatch.fnmatch(origin.codename, value)
elif what in ("uri",):
match = True
else:
raise UnknownMatcherError(
"Unknown whitelist entry for matcher %s (token %s)" % (
what, token))
# update res
res = res and match
# logging.debug("matching %s=%s against %s" % (
# what, value, origin))
return res
def deleteDuplicatedElementFromList(list):
resultList = []
for item in list:
if not item in resultList:
resultList.append(item)
return resultList
def not_empty(s):
return s and s.strip()
class UnknownMatcherError(ValueError):
pass
class NoAllowedOriginError(ValueError):
pass

View File

@ -0,0 +1,402 @@
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# 脚本插件化执行管理
# TODO:
# 使能/失能 --配置文件--ok
# 错误码 --脚本/本程序--wait/add
# 进度同步 --线程获取--add
# 输出规范 --脚本/本程序(重定向日志文件、输出格式)--ok/ok
# 元数据类型 --描述信息、翻译(配置文件)--ok
# 运行等级 --(root/user)--wait
import subprocess
import os
from sre_compile import isstring
import threading
import yaml
import logging
from gi.repository import GObject
class pluginState():
PLUGIN_SUCCESS = 0
PLUGINERR_PLUGIN_NOT_EXIST = PLUGIN_SUCCESS - 1
PLUGINERR_PLUGIN_NOT_COMPLETED = PLUGINERR_PLUGIN_NOT_EXIST - 1
PLUGINERR_NO_AVAILABLE_YAML = PLUGINERR_PLUGIN_NOT_COMPLETED - 1
PLUGINERR_NOT_LOAD_ALL = PLUGINERR_NO_AVAILABLE_YAML - 1
PLUGINERR_PLUGIN_NOT_IN_LIST = PLUGINERR_NOT_LOAD_ALL - 1
PLUGINERR_PLUGIN_NOT_ENABLED = PLUGINERR_PLUGIN_NOT_IN_LIST - 1
PLUGINERR_PLUGIN_CONFIG_FAILED = PLUGINERR_PLUGIN_NOT_ENABLED - 1
PLUGINERR_LOG_PATH_NOT_EXIT = PLUGINERR_PLUGIN_CONFIG_FAILED - 1
PLUGINERR_CONFIG_NOT_COMPLETED = PLUGINERR_LOG_PATH_NOT_EXIT - 1
PLUGINERR_CMD_IS_NONE = PLUGINERR_CONFIG_NOT_COMPLETED - 1
PLUGINERR_LANGUAGE_NOT_SUPPORT = PLUGINERR_CMD_IS_NONE - 1
_numToInfo = {
PLUGIN_SUCCESS: 'success',
PLUGINERR_PLUGIN_NOT_EXIST: 'plugin path not exist',
PLUGINERR_PLUGIN_NOT_COMPLETED: 'plugin folder not completed',
PLUGINERR_NO_AVAILABLE_YAML: 'there is no available yaml',
PLUGINERR_NOT_LOAD_ALL: 'not run load_all',
PLUGINERR_PLUGIN_NOT_IN_LIST: 'plugin not in loaded plugin list',
PLUGINERR_PLUGIN_NOT_ENABLED: 'plugin not enabled',
PLUGINERR_PLUGIN_CONFIG_FAILED: 'plugin config failed',
PLUGINERR_LOG_PATH_NOT_EXIT: 'log path not exists',
PLUGINERR_CONFIG_NOT_COMPLETED: 'config not completed',
PLUGINERR_CMD_IS_NONE: 'cmd is none',
PLUGINERR_LANGUAGE_NOT_SUPPORT: 'not support this language',
}
_infoToNum = {
'success': PLUGIN_SUCCESS,
'plugin path not exist': PLUGINERR_PLUGIN_NOT_EXIST,
'plugin folder not completed': PLUGINERR_PLUGIN_NOT_COMPLETED,
'there is no available yaml': PLUGINERR_NO_AVAILABLE_YAML,
'not run load_all': PLUGINERR_NOT_LOAD_ALL,
'plugin not in loaded plugin list': PLUGINERR_PLUGIN_NOT_IN_LIST,
'plugin not enabled': PLUGINERR_PLUGIN_NOT_ENABLED,
'plugin config failed': PLUGINERR_PLUGIN_CONFIG_FAILED,
'log path not exists': PLUGINERR_LOG_PATH_NOT_EXIT,
'config not completed': PLUGINERR_CONFIG_NOT_COMPLETED,
'cmd is none': PLUGINERR_CMD_IS_NONE,
'not support this language': PLUGINERR_LANGUAGE_NOT_SUPPORT,
}
PLUGIN_MANAGER_PATH = "./" # 可修改
# 目录结构 FILE PATH
CFG_FILE = "conf.yaml"
CFG_EX_DIR = "conf.d/"
CFG_PATH = PLUGIN_MANAGER_PATH + CFG_FILE
CFG_EX_PATH = PLUGIN_MANAGER_PATH + CFG_EX_DIR
MOD_DIR = "modules/"
MOD_PATH = PLUGIN_MANAGER_PATH + MOD_DIR
MOD_AVAILABLE = "modules-available/"
MOD_ENABLED = "modules-enabled/"
MOD_AVAILABLE_PATH = MOD_PATH + MOD_AVAILABLE
MOD_ENABLED_PATH = MOD_PATH + MOD_ENABLED
PLUGIN_DIR = "script/"
PLUGIN_PATH = PLUGIN_MANAGER_PATH + PLUGIN_DIR
# 配置 日志路径
LOG_DIR_ROOT = '/var/log/kylin-system-updater/'
# 默认插件日志路径
PLUGIN_LOG_DIR = '/var/log/kylin-system-updater/plugin/'
# PLUGIN.YAML
PLUGIN_CONF_KEY_NAME = 'name'
PLUGIN_CONF_KEY_DESC = 'desc'
PLUGIN_CONF_KEY_EXEC = 'exec'
PLUGIN_CONF_KEY_LOGLEVEL = 'loglevel'
PLUGIN_CONF_KEY_RUNLEVEL = 'runlevel'
PLUGIN_CONF_KEY_LIST = [PLUGIN_CONF_KEY_NAME,PLUGIN_CONF_KEY_DESC,PLUGIN_CONF_KEY_EXEC,PLUGIN_CONF_KEY_LOGLEVEL,PLUGIN_CONF_KEY_RUNLEVEL]
# CONF.YAML AND CONF.D
MANAGER_CONF_KEY_LOGDIR = "logdir"
MANAGER_CONF_KEY_LIST = [MANAGER_CONF_KEY_LOGDIR, ]
FORMAT = "%(asctime)s [%(levelname)s]: %(message)s"
RUNLEVEL_LIST = ['ROOT', 'USER']
LOGLEVEL_LIST = ['DEBUG', 'INFO', 'NOTIFY', 'WARNING', 'ERROR', 'CRITICAL']
class LOADSTATE():
PLGNAME = 0x01
EXECCMD = 0x02
STATESUM = PLGNAME + EXECCMD
LANG_KEY_ZH_CN = 'zh_CN'
LANG_KEY_EN = 'en'
class LANGLIST():
LANG_EN = 0
LANG_ZH_CN = 1
class pluginClass(pluginState):
def __init__(self):
# 必须配置项
self.pluginName = None
self.execCmd = None
# 可选配置项
self.descList = [] # en / zh
self.logLevel = LOGLEVEL_LIST.index('DEBUG')
self.runLevel = RUNLEVEL_LIST.index('ROOT')
self.enabled = False
# 合成变量
self.cmd = None
self.logDir = PLUGIN_LOG_DIR # 插件日志路径
self.logPath = os.path.join(self.logDir, "default.log") # 插件日志文件
self.fifoName = "default-fifo" # 插件进度文件
# self.fifoPath = PLUGIN_PATH + self.fifoName
self.fifoPath = "/var/log/kylin-system-updater"+self.fifoName
# 记录变量
self.running = False # 是否在运行
self.process = 0 # 执行进度
self.loadState = 0 # 插件配置完成
logging.info("init finished.")
###################### 内部函数 ######################
# 1-配置指定字段
# 2-更新进度 (1/0.5s)
# 3-
######################
def _config_key(self, cfg, key):
if cfg == None or key == None or key not in cfg:
logging.warning("[PLUGIN]: key[%s] not in yaml.", key)
if key == PLUGIN_CONF_KEY_NAME:
if isstring(cfg[key]):
self.pluginName = cfg[key]
self.fifoName = cfg[key] + "-fifo"
self.loadState += LOADSTATE.PLGNAME
else:
logging.error("[PLUGIN]: name[%s] not string.", cfg[key])
elif key == PLUGIN_CONF_KEY_DESC:
langList = cfg[key]
descDict = {}
if langList == None or len(langList) == 0:
return
for i in range(len(langList)):
descDict = langList[i]
if LANG_KEY_EN in descDict:
self.descList.insert(LANGLIST.LANG_EN, descDict.pop(LANG_KEY_EN))
continue
elif LANG_KEY_ZH_CN in descDict:
self.descList.insert(LANGLIST.LANG_ZH_CN, descDict.pop(LANG_KEY_ZH_CN))
continue
elif key == PLUGIN_CONF_KEY_EXEC:
if isstring(cfg[key]):
self.execCmd = cfg[key]
self.loadState += LOADSTATE.EXECCMD
else:
logging.error("[PLUGIN]: execCmd[%s] not string.", cfg[key])
elif key == PLUGIN_CONF_KEY_LOGLEVEL:
loglevel = cfg[key].upper()
if loglevel in LOGLEVEL_LIST:
self.logLevel = LOGLEVEL_LIST.index(loglevel)
elif key == PLUGIN_CONF_KEY_RUNLEVEL:
runlevel = cfg[key].upper()
if runlevel in RUNLEVEL_LIST:
self.runLevel = RUNLEVEL_LIST.index(runlevel)
else:
logging.warning("[PLUGIN]: key[%s] not need config.", key)
def _update_process(self):
if not self.running:
logging.info("[PLUGIN]: plugin [%s] is not running.", self.pluginName)
return
if os.path.exists(self.fifoPath):
try:
fd = open(self.fifoPath, 'r', 1)
process = fd.readline()
self.process = int(process.strip("\n"))
except Exception as e:
logging.info("[PLUGIN]: get process err[%s].",e)
else:
logging.info("[PLUGIN]: fifo[%s] not exists.", self.fifoPath)
if self.process >= 100 or self.process < 0:
return
tmptimer = threading.Timer(0.5, function=self._update_process)
tmptimer.start()
###################### 外部函数 ######################
# 1-读取配置文件,并配置该插件
# 2-使能插件
# 3-失能插件
# 4-获取插件名称
# 5-获取进度
# 6-注册进度跟新回调
# 7-执行插件
# 8-获取描述信息
# 9-设置脚本日志路径
# TODO:
# 重配置该插件
######################
# 配置该插件
def plg_config(self, filePath):
if not os.path.exists(filePath):
logging.error("[PLUGIN]: [%s] not exist.", filePath)
return self.PLUGINERR_PLUGIN_CONFIG_FAILED
def plg_enable(self):
self.enabled = True
def plg_disable(self):
self.enabled = False
def plg_get_name(self):
return self.pluginName
def plg_get_process(self):
return self.process
def plg_get_desc(self):
# 获得语言变量
#TODO: 例如:中文繁体,如果不存在的话,显示中文简体
lang=os.getenv("LANG")
if LANG_KEY_EN in lang:
if len(self.descList) > LANGLIST.LANG_EN:
return self.descList[LANGLIST.LANG_EN]
else:
logging.error("[PLUGIN]: There is not a desc of the language[%s].", lang)
elif LANG_KEY_ZH_CN in lang:
if len(self.descList) > LANGLIST.LANG_ZH_CN:
return self.descList[LANGLIST.LANG_ZH_CN]
else:
logging.error("[PLUGIN]: There is not a desc of the language[%s].", lang)
else:
logging.error("[PLUGIN]: There is not a desc of the language[%s].", lang)
return
# 添加 update cmd
# 设置脚本日志路径
def plg_set_logDir(self, logPath):
if not os.path.exists(logPath):
try:
os.makedirs(logPath, mode=0o755)
except Exception as e:
logging.error("[PLUGIN]: create plugin log dir failed.[%s]", e)
return self.PLUGINERR_LOG_PATH_NOT_EXIT
self.logDir = logPath
if self.pluginName != None:
self.logPath = os.path.join(self.logDir, self.pluginName + ".log")
self.cmd = "bash " + self.execCmd + " fifoname=" + self.fifoName + " logpath=" + self.logPath + " loglevel=" + str(self.logLevel) + " modename=" + self.pluginName
def plg_run(self):
if not self.enabled:
logging.error("[PLUGIN]: [%s] not enabled.", self.pluginName)
return self.PLUGINERR_PLUGIN_NOT_ENABLED
self.running = True
tmptimer = threading.Timer(0.5, function=self._update_process)
tmptimer.start()
if self.cmd == None:
logging.error("[PLUGIN]: cmd is None.")
return self.PLUGINERR_CMD_IS_NONE, self._numToInfo(self.PLUGINERR_CMD_IS_NONE), self._numToInfo(self.PLUGINERR_CMD_IS_NONE)
logging.debug("[PLUGIN]: cmd[%s].",self.cmd)
try:
ret = subprocess.run(self.cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
except Exception as e:
logging.error("[PLUGIN]: subprocess run err[%s].", e)
self.running = False
logging.debug("[PLUGIN]: [%s] run finished ret[%d].",self.pluginName, ret.returncode)
return ret.returncode, ret.stdout.decode(), ret.stderr.decode()
def plg_reconfig(self):
pass
class pluginManagerClass(pluginState):
def __init__(self):
# 变量初始化
self.plgClassList = [] # 插件句柄
self.loaded = False
self.managerLogDir = LOG_DIR_ROOT # 管理器日志路径
# 日志配置初始化,试用updater的logger
# if not os.path.exists(self.managerLogDir):
# os.mkdir(self.managerLogDir, mode=0o755)
# logfile = os.path.join(self.managerLogDir, 'PluginManager.log.' + str(self.classNum))
# logging.basicConfig(format=FORMAT, level='DEBUG', datefmt='%m-%d,%H:%M:%S', filename=logfile, filemode='a')
# self.pluginLogDir = PLUGIN_LOG_DIR # 插件日志路径
# 将单个插件句柄添加到全局记录, 并使能
def _add_single_plugin(self, filePath, enable):
if not os.path.exists(filePath):
logging.debug("[PLUGIN]: [%s] not exist.", filePath)
return
singlePlgClass = pluginClass()
singlePlgClass.plg_config(filePath)
self.plgClassList.append(singlePlgClass)
if enable:
singlePlgClass.plg_enable()
singlePlgClass.plg_set_logDir(self.pluginLogDir)
def _remove_single_plugin(self, pluginClass):
if pluginClass in self.plgClassList:
logging.debug("[PLUGIN]: remove [%s].", pluginClass.plg_get_name())
pluginClass.remove(pluginClass)
pluginClass.plg_disable()
# 加载所有插件,读取所有配置
# 第一个执行
# 返回插件句柄列表
# TODO:加载指定插件, 读取指定配置
def reload_plugin(self, pluginName):
pass
# 通过句柄获取插件名称
def get_plugin_name(self, pluginClass):
if not self.loaded:
logging.error("[PLUGIN]: please run load_all first.")
return self.PLUGINERR_NOT_LOAD_ALL
if pluginClass not in self.plgClassList:
logging.error("[PLUGIN]: there is no this plugin in pluginList.")
return self.PLUGINERR_PLUGIN_NOT_IN_LIST
return pluginClass.plg_get_name()
# 运行指定插件
# pluginName, pluginClass 都指定时,以名称为准
def run_plugin(self, pluginName = None):
self.running = True
if pluginName == None or not os.path.isfile(pluginName):
logging.error("[PLUGIN]: [%s] Cann't found.",pluginName)
return True
cmd = "bash " + pluginName
try:
ret = subprocess.run(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
logging.info("[PLUGIN]: script[%s].",pluginName)
except Exception as e:
logging.error("[PLUGIN]: subprocess run err[%s].", e)
return True
self.running = False
if ret.returncode != 0:
logging.error("[PLUGIN]: code:%d, out:%s, err:%s",ret.returncode, ret.stdout.decode(), ret.stderr.decode())
logging.debug("[PLUGIN]: run finished returncode[%d], out[%s], err[%s]",ret.returncode, ret.stdout.decode(), ret.stderr.decode())
return (ret.returncode==0)
def connect_signal(self, plgclass, signal, handler):
if plgclass not in self.plgClassList:
logging.error("[PLUGIN]: there is no this plugin in pluginList.")
return self.PLUGINERR_PLUGIN_NOT_IN_LIST
return plgclass.connect(signal, handler)
def plugin_process_handler(obj, process):
logging.info("[PLUGIN]: ******* process [%d].", process)
# if __name__ == "__main__":
# pMClass = pluginManagerClass()
# plgList = pMClass.load_all("./")
# for everyPlg in iter(plgList):
# name = pMClass.get_plugin_name(everyPlg)
# print("name:", name)
# desc = pMClass.get_desc(everyPlg)
# print("desc:", desc)
# pMClass.connect_signal(everyPlg, "processChanged", plugin_process_handler)
# ret = pMClass.run_plugin(name)
# exit(0)

View File

@ -0,0 +1,543 @@
# UpdateList.py
from gettext import gettext as _
import logging
import os
import json
import yaml
from gi.repository import Gio
from .errors import *
from .enums import *
class UpdateList():
OUTPUT_JSON_PATH = '/var/lib/kylin-system-updater/json/'
def __init__(self,parent):
self.parent = parent
self.uuconfigs = self.parent.uuconfigs
self.push_groups = []
self.push_steps = []
self.push_singles = []
self.pkgs_data = {}
if 'XDG_CURRENT_DESKTOP' in os.environ:
self.current_desktop = os.environ.get('XDG_CURRENT_DESKTOP')
else:
self.current_desktop = ''
if 'XDG_DATA_DIRS' in os.environ and os.environ['XDG_DATA_DIRS']:
data_dirs = os.environ['XDG_DATA_DIRS']
else:
data_dirs = '/usr/local/share/:/usr/share/'
self.application_dirs = [os.path.join(base, 'applications')
for base in data_dirs.split(':')]
def get_push(self):
return self.push_groups + self.push_singles
def push_pop(self,content):
for cont in content:
if cont in self.push_singles:
self.push_singles.remove(cont)
if cont in self.push_groups:
self.push_groups.remove(cont)
def classify_content(self,content):
if content == []:
steps = []
if self.push_groups:
steps = self.push_steps
return self.push_singles,self.push_groups,steps
else:
singles = []
groups = []
steps = []
for cont in content:
if cont in self.push_singles:
singles.append(cont)
elif cont in self.push_groups:
groups.append(cont)
if groups != [] and self.push_steps != []:
steps = self.push_steps
return singles,groups,steps
def copy_pkgs_data(self,content):
updating_data = {}
for con in content:
updating_data.update({con:self.pkgs_data.get(con,{})})
return updating_data
def is_steps(self,content):
for cont in content:
if cont in self.push_singles:
return False
if self.push_steps == []:
return False
else:
return True
def _make_important_list(self,cache,pkgs_upgrade,important_list = []):
upgradeable_pkgs = []
# tmp = []
upgradeable_groups = []
logging.info("The Server Push List: %a",important_list)
for pkg_name in important_list:
if pkg_name in cache:
pkg_obj = cache[pkg_name]
if pkg_obj.is_installed:
if pkg_name in pkgs_upgrade:
pkgs_upgrade.remove(pkg_name)
upgradeable_pkgs.append(pkg_obj.name)
else:
upgradeable_pkgs.append(pkg_obj.name)
else:
upgradeable_groups.append(pkg_name)
logging.info("Push Single Packages: %a, Push Groups:%a",upgradeable_pkgs,upgradeable_groups)
return upgradeable_groups,upgradeable_pkgs
def _make_pkg_info_json(self,cache,pkgs_list):
total_download_size = 0
total_installed_size = 0
pkgs_info_json = {}
for pkg_name in pkgs_list:
pkg = cache[pkg_name]
cur_version = getattr(pkg.installed, "version", '')
new_version = getattr(pkg.candidate, "version", '')
download_size = getattr(pkg.candidate, "size", 0)
installed_size = getattr(pkg.candidate, "installed_size", 0)
total_download_size = total_download_size + download_size
total_installed_size = total_installed_size + installed_size
pkgs_info_json.update({pkg_name:{"cur_version":cur_version,"new_version":new_version,\
"download_size":str(download_size),"install_size":str(installed_size)}})
pkgs_info_json.update({"total_download_size":str(total_download_size)})
pkgs_info_json.update({"total_install_size":str(total_installed_size)})
return pkgs_info_json
def _check_pkg_in_cache(self,cache,pkgs_list):
new_pkgs_list = []
for pkg_name in pkgs_list:
#检查是否在cache 以及 是否安装检查
if pkg_name in cache and not cache[pkg_name].is_installed:
new_pkgs_list.append(pkg_name)
else:
pass
return new_pkgs_list
def _make_group_output_json(self,data,data_yaml,upgrade_pkgs_json,install_pkgs_json,output_path):
groups_base_info = {}
output_json = {}
output_config_name = output_path + data['package'] + '.json'
groups_base_info.update({"package":data['package']})
groups_base_info.update({"new_version":data['version']})
groups_base_info.update({"name":data['name']})
groups_base_info.update({"description":data['description']})
groups_base_info.update({"icon":data['icon']})
groups_base_info.update({"changelog":data_yaml['changelog']})
output_json.update(groups_base_info)
output_json.update({"upgrade_list":upgrade_pkgs_json})
output_json.update({"install_list":install_pkgs_json})
# output_json.update({"hold_list":hold_pkgs_list})
# output_json.update({"remove_list":remove_pkgs_list})
with open(output_config_name, 'w', encoding='utf-8') as f:
json.dump(output_json, f, ensure_ascii=False, indent=4)
logging.info("Generate Jsonfile(%s) to complete... ",output_config_name)
def _split_package_id(self,package):
"""Return the name, the version number and the release of the
specified package."""
if "=" in package:
name, version = package.split("=", 1)
release = None
elif "/" in package:
name, release = package.split("/", 1)
version = None
else:
name = package
version = release = None
return name, version, release
def _make_downgrade(self,cache,downgrade_pkgs):
output_downgrade = []
adjust_pkgs = []
for pkg_name, pkg_ver, pkg_rel in [self._split_package_id(pkg)
for pkg in downgrade_pkgs]:
try:
pkg = cache[pkg_name]
except KeyError:
logging.warning("Package %s isn't available",pkg_name)
continue
if not pkg.is_installed:
logging.warning("Package %s isn't installed",pkg_name)
if pkg_ver:
if pkg.installed and pkg.installed.version < pkg_ver:
logging.warning("The former version %s of %s is already installed",pkg.installed.version, pkg.name)
continue
elif pkg.installed and pkg.installed.version == pkg_ver:
logging.warning("The version %s of %s is already installed",pkg.installed.version, pkg.name)
continue
try:
pkg.candidate = pkg.versions[pkg_ver]
except KeyError:
logging.warning("The version %s of %s isn't available",pkg_ver, pkg_name)
continue
output_downgrade.append(pkg_name)
adjust_pkgs.append(pkg_name+'='+pkg_ver)
return output_downgrade,adjust_pkgs
def _get_downgrade_list(self,cache,data):
downgrade_pkgs = []
try:
downgrade_raw = data['force_install_list']
except Exception as e:
downgrade_raw = []
for pkg_name, pkg_ver, pkg_rel in [self._split_package_id(pkg)
for pkg in downgrade_raw]:
if pkg_name in cache:
downgrade_pkgs.append(pkg_name)
else:
logging.warning("Package %s isn't available",pkg_name)
continue
return downgrade_raw,downgrade_pkgs
def _make_groups_pkgs(self,cache,data,pkgs_upgrade = []):
upgrade_pkgs_list = data['upgrade_list']
new_install_list = self._check_pkg_in_cache(cache,data['install_list'])
new_upgrade_list = list(set(pkgs_upgrade) & set(upgrade_pkgs_list))
for pkg in new_upgrade_list:
pkgs_upgrade.remove(pkg)
for pkg in self.push_singles:
if pkg in new_install_list:
new_install_list.remove(pkg)
return new_install_list,new_upgrade_list
def _make_groups_upgrade(self,cache,group_list,is_openkylin,pkgs_install,pkgs_upgrade,output_path,config_path):
upgrade_list = []
install_list = []
if os.path.isdir(config_path) == False:
logging.warning("configPath(%s) is not exists...",config_path)
return
files = os.listdir(config_path)
for ifile in files:
if ifile.endswith('.json'):
with open(config_path+ifile,'r') as f:
try:
data = json.load(f)
except Exception as exc:
logging.error(exc)
raise UpdateBaseError(ERROR_LOAD_CONFIG_FAILED)
group_name = data['package']
with open(config_path + group_name + ".yaml", "r") as stream:
try:
data_yaml = yaml.safe_load(stream)
except Exception as exc:
logging.error(exc)
raise UpdateBaseError(ERROR_LOAD_CONFIG_FAILED)
if not group_name in group_list:
continue
if is_openkylin == True:
install_list,upgrade_list = pkgs_install,pkgs_upgrade
else:
install_list,upgrade_list = self._make_groups_pkgs(cache,data,pkgs_upgrade)
if len(install_list) == 0 and len(upgrade_list) == 0:
continue
upgrade_pkgs_json = self._make_pkg_info_json(cache,upgrade_list)
install_pkgs_json = self._make_pkg_info_json(cache,install_list)
self._make_group_output_json(data,data_yaml,upgrade_pkgs_json,install_pkgs_json,output_path)
# 读取策略下发 策略优先级最高
if self.uuconfigs.getWithDefault("updateStrategiesManager","strategiesState",False) == True:
install_type = self.uuconfigs.getWithDefault("updateStrategiesManager","installType",DEFAULT_INSTALL_TYPE)
if install_type == DEFAULT_INSTALL_TYPE:
install_type = data.setdefault("install_type",RUNTIME_INSTALL_TYPE)
else:
install_type = data.setdefault("install_type",RUNTIME_INSTALL_TYPE)
poweroff_install = install_type == POWEROFF_INSTALL_TYPE
progress_begin = 50
if poweroff_install == True:
logging.info("Turn on shutdown installation mode...")
progress_begin = 0
step_update = data.setdefault("step_update",{})
if step_update != {}:
if poweroff_install == False:
progress_begin = self._make_steps_runtime(progress_begin,step_update,install_list,upgrade_list)
else:
progress_begin = self._make_steps_poweroff(progress_begin,step_update,install_list,upgrade_list)
put_data = {group_name:{"progress_begin":progress_begin,"progress_end":100,"upgrade_list":upgrade_list,\
"install_list":install_list,"script":data.setdefault("script",{}),\
"install_type":install_type}}
self.push_groups.append(group_name)
self.pkgs_data.update(put_data)
logging.info("Group(%s) upgrade:%d install:%d",group_name,len(upgrade_list),len(install_list))
else:
pass
def _make_steps_poweroff(self,steps_begin,step_update,install_list,upgrade_list):
delete_step = []
for step_name in step_update:
step_data = step_update.get(step_name,{})
if step_data == {}:
continue
step_install = list(set(step_data.setdefault("install_list",[])) & set(install_list))
step_upgrade = list(set(step_data.setdefault("upgrade_list",[])) & set(upgrade_list))
if step_install == [] and step_upgrade == []:
delete_step.append(step_name)
continue
step_data["install_list"] = step_install
step_data["upgrade_list"] = step_upgrade
for step in delete_step:
step_update.pop(step)
for step_name in step_update:
step_data = step_update.get(step_name,{})
step_install = step_data["install_list"]
step_upgrade = step_data["upgrade_list"]
value = int((len(step_install) + len(step_upgrade))/(len(install_list) + len(upgrade_list)) * 50)
if value == 0:
value = 1
progress_end = steps_begin + value
step_data.update({"progress_begin":steps_begin})
step_data.update({"progress_end":progress_end})
steps_begin = progress_end
logging.info("Steps(%s) upgrade:%d install:%d",step_name,len(step_upgrade),len(step_install))
self.push_steps.append(step_name)
self.pkgs_data.update({step_name:step_data})
return steps_begin
def _make_steps_runtime(self,steps_begin,step_update,install_list,upgrade_list):
for step_name in step_update:
step_data = step_update.get(step_name,{})
if step_data == {}:
continue
step_install = list(set(step_data.setdefault("install_list",[])) & set(install_list))
step_upgrade = list(set(step_data.setdefault("upgrade_list",[])) & set(upgrade_list))
need_reboot = step_data.setdefault("need_reboot",False)
if step_install == [] and step_upgrade == []:
continue
step_data["install_list"] = step_install
step_data["upgrade_list"] = step_upgrade
value = int((len(step_install) + len(step_upgrade))/(len(install_list) + len(upgrade_list)) * 50)
if need_reboot == True:
step_data.update({"progress_begin":steps_begin})
step_data.update({"progress_end":100})
else:
progress_end = steps_begin + value
step_data.update({"progress_begin":steps_begin})
step_data.update({"progress_end":progress_end})
steps_begin = progress_end
logging.info("Steps(%s) upgrade:%d install:%d",step_name,len(step_upgrade),len(step_install))
self.push_steps.append(step_name)
self.pkgs_data.update({step_name:step_data})
if need_reboot == True:
break
return steps_begin
def _rate_application_for_package(self, application, pkg):
score = 0
desktop_file = os.path.basename(application.get_filename())
application_id = os.path.splitext(desktop_file)[0]
if application.should_show():
score += 1
if application_id == pkg.name:
score += 5
return score
def _file_is_application(self, file_path):
# WARNING: This is called often if there's a lot of updates. A poor
# performing call here has a huge impact on the overall performance!
if not file_path.endswith(".desktop"):
# First the obvious case: If the path doesn't end in a .desktop
# extension, this isn't a desktop file.
return False
file_path = os.path.abspath(file_path)
for app_dir in self.application_dirs:
if file_path.startswith(app_dir):
return True
return False
def get_application_for_package(self, pkg):
desktop_files = []
rated_applications = []
for installed_file in pkg.installed_files:
if self._file_is_application(installed_file):
desktop_files.append(installed_file)
for desktop_file in desktop_files:
try:
application = Gio.DesktopAppInfo.new_from_filename(
desktop_file)
application.set_desktop_env(self.current_desktop)
except Exception as e:
logging.warning("Error loading .desktop file %s: %s" %
(desktop_file, e))
continue
score = self._rate_application_for_package(application, pkg)
if score > 0:
rated_applications.append((score, application))
rated_applications.sort(key=lambda app: app[0], reverse=True)
if len(rated_applications) > 0:
return rated_applications[0][1]
else:
return None
def _make_single_upgrade(self,cache,pkg_list,output_path):
for pkg in pkg_list:
zh_name = ''
base_info = {}
output_json = {}
output_config_name = output_path + pkg + '.json'
pkg_cache = cache[pkg]
if pkg_cache.is_installed:
app = self.get_application_for_package(pkg_cache)
if app is not None:
zh_name = app.get_display_name()
pkgs_json = self._make_pkg_info_json(cache,[pkg])
en_name = getattr(pkg_cache.candidate, "summary", '')
description_str = getattr(pkg_cache.candidate, "description", '')
base_info.update({"package":pkg})
base_info.update({"cur_version":getattr(pkg_cache.installed, "version", '')})
base_info.update({"new_version":getattr(pkg_cache.candidate, "version", '')})
base_info.update({"name":{"zh_CN":zh_name,"en_US":en_name}})
base_info.update({"description":{"zh_CN":description_str,"en_US":description_str}})
base_info.update({"icon":''})
output_json.update(base_info)
if pkg_cache.is_installed:
output_json.update({"upgrade_list":pkgs_json})
output_json.update({"install_list":{}})
else:
output_json.update({"upgrade_list":{}})
output_json.update({"install_list":pkgs_json})
with open(output_config_name, 'w', encoding='utf-8') as f:
json.dump(output_json, f, ensure_ascii=False, indent=4)
logging.info("Generate Jsonfile(%s) to complete... ",output_config_name)
def _make_distupgrade(self,cache):
pkgs_upgrade = []
pkgs_install = []
if cache.get_changes():
cache.clear()
cache._depcache.upgrade(True)
for pkg in cache:
try:
if pkg.marked_install:
pkgs_install.append(pkg.name)
elif pkg.marked_upgrade:
pkgs_upgrade.append(pkg.name)
except KeyError:
pass
return pkgs_install,pkgs_upgrade
def _backup_current_serverid(self):
import shutil
SERVERIDPATH = "/var/lib/kylin-software-properties/config/serverID.conf"
BACKUPPATH = "/var/lib/kylin-system-updater/json/"
try:
if os.path.isfile(SERVERIDPATH):
dst = shutil.copy(SERVERIDPATH, BACKUPPATH)
logging.info("Backup serverID conf completed: %s...",dst)
else:
logging.info("ServerID conf is not exists...")
except Exception as e:
logging.error("%s",e)
def update_kylin(self,cache,important_data,is_openkylin = False):
system_install = []
system_upgrade = []
if is_openkylin == True:
system_install,system_upgrade = self._make_distupgrade(cache)
else:
if cache.get_changes():
cache.clear()
for pkg in cache:
if pkg.is_upgradable and pkg.is_installed:
system_upgrade.append(pkg.name)
logging.info("System all upgradeable packages:upgrade:%d install:%d ",len(system_upgrade),len(system_install))
group_list,single_pkgs = self._make_important_list(cache,system_upgrade,important_data)
make_empty_dir(self.OUTPUT_JSON_PATH)
if not group_list and not single_pkgs:
return
self.push_singles = single_pkgs
self._make_single_upgrade(cache,single_pkgs,self.OUTPUT_JSON_PATH)
self._make_groups_upgrade(cache,group_list,is_openkylin,\
system_install,system_upgrade,self.OUTPUT_JSON_PATH,get_config_patch())
self._backup_current_serverid()

View File

@ -0,0 +1,105 @@
#!/usr/bin/python3
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
# DistUpgradeConfigParser.py
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from configparser import NoOptionError
from configparser import ConfigParser as SafeConfigParser
import os.path
import os
import logging
class UpgradeConfig(SafeConfigParser):
def __init__(self, datadir="/var/lib/kylin-system-updater/", name="system-updater.conf",defaults_dir=None):
SafeConfigParser.__init__(self)
self.datadir = datadir
maincfg = os.path.join(datadir, name)
# defaults are read first
self.config_files = []
if defaults_dir:
self.config_files.append(os.path.join(datadir, defaults_dir))
# our config file
self.config_files += [maincfg]
for conf in self.config_files:
if not os.path.exists(conf):
logging.warning("Check: Conf(%s) not exits...",conf)
self.read(self.config_files, encoding='iso-8859-1')
logging.info("Initialize Upgrade ConfigFile(%s) to success",str(self.config_files))
def optionxform(self, optionstr):
return optionstr
def reRead(self):
self.read(self.config_files, encoding='iso-8859-1')
def setValue(self, section, option, value=None,is_write = True):
logging.info("SetValue Section:%s Option:%s Value:%s",section, option, value)
try:
self.reRead()
self.set(section, option,value)
except Exception as e:
logging.error("Error: setValue section:%s option:%s value:%s",section, option, value)
logging.error(str(e))
return False
if is_write == True:
with open(self.config_files[-1], 'w+') as configfile:
self.write(configfile)
return True
def getWithDefault(self, section, option, default,re_read=False):
try:
if re_read == True:
self.reRead()
if type(default) == bool:
return self.getboolean(section, option)
elif type(default) == float:
return self.getfloat(section, option)
elif type(default) == int:
return self.getint(section, option)
return self.get(section, option)
except Exception as e:
logging.error(str(e))
return default
def getlist(self, section, option):
try:
tmp = self.get(section, option)
except Exception as e:
logging.error(str(e))
return []
items = [x.strip() for x in tmp.split(" ")]
if '' in items and len(items):
return []
return items
def setListValue(self, section, option, value=None,is_write = True):
tmp = str(value).replace('[', '').replace(']', '')
tmp = tmp.replace("'", '').replace(',', '')
try:
self.set(section, option,tmp)
except Exception as e:
logging.error("setListValue section:%s option:%s",section, option)
logging.error(str(e))
return
if is_write == True:
with open(self.config_files[-1], 'w+') as configfile:
self.write(configfile)
def getListFromFile(self, section, option):
try:
filename = self.get(section, option)
except NoOptionError:
return []
p = os.path.join(self.datadir, filename)
if not os.path.exists(p):
logging.error("getListFromFile: no '%s' found" % p)
with open(p) as f:
items = [x.strip() for x in f]
return [s for s in items if not s.startswith("#") and not s == ""]
if __name__ == "__main__":
pass

View File

@ -0,0 +1,67 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""enums - Enumerates for apt daemon dbus messages"""
__all__ = (
#错误枚举
"ERROR_OTHER_PROGRESS_OCCUPATION","ERROR_NETWORK_FAILED","ERROR_CANCELLED","ERROR_NOT_ROLLBAK_DEPLOYMENT",
"ERROR_ORIGIN_IS_NONE",
# 方法
"get_error_description_from_enum"
)
import gettext
gettext.bindtextdomain('kylin-system-updater', '/usr/share/locale')
gettext.textdomain('kylin-system-updater')
_ = gettext.gettext
#系统更新的所有枚举
PRIORITY_UPGRADE_SUCCCESSED = "priority-upgrade-successed"
#apt update阶段出现的错误解析
#通用错误
ERROR_PROGRAM_EXCEPTION = "#0000"
ERROR_OTHER_PROGRESS_OCCUPATION = "#0001"
ERROR_NETWORK_FAILED = "#0002"
#检查更新错误 1
ERROR_ORIGIN_IS_NONE = "#0100"
#下载错误 2
ERROR_CANCELLED = "#0200"
#部署错误 3
ERROR_NOT_ROLLBAK_DEPLOYMENT = "#0300"
GLIB_ERROR_NETWORK_FAILED = "While fetching mirrorlist"
_DESCS_ERROR = {
ERROR_OTHER_PROGRESS_OCCUPATION: _("Other tasks are being updated and upgraded, please try again later."),
ERROR_NETWORK_FAILED: _("Network anomaly, can't check for updates!"),
ERROR_CANCELLED: _("_Cancel Upgrade"),
ERROR_NOT_ROLLBAK_DEPLOYMENT: _("There is no way to rollback to the previous version, there is nothing to rollback."),
ERROR_ORIGIN_IS_NONE: _("Check for update exceptions! Please verify that the system deployment is complete.")
}
def get_error_description_from_enum(enum):
"""Get a long description of an error.
:param enum: The transaction error enum, e.g. :data:`ERROR_NO_LOCK`.
:returns: The description string.
"""
try:
return _DESCS_ERROR[enum]
except KeyError:
return ''
# vim:ts=4:sw=4:et

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Exception classes"""
import logging
from .enums import *
class UpdateBaseError(Exception):
"""Internal error if a transaction could not be processed successfully."""
_dbus_error_name = "org.debian.apt.TransactionFailed"
def __init__(self, code,desc=None,*args):
self.code = code
if not desc:
self.desc = get_error_description_from_enum(self.code)
else:
self.desc = desc
Exception.__init__(self, *args)
def __str__(self):
return "%s" % \
(get_error_description_from_enum(self.code))
class UpdateProgressExit(Exception):
def __init__(self,*args):
Exception.__init__(self, *args)
logging.info("Update Progress wiil be Exit...")
class DebDeltaError(UpdateBaseError):
"""Internal error if a transaction could not be processed successfully."""
# vim:ts=4:sw=4:et

View File

@ -0,0 +1,13 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""Main loop for aptdaemon."""
__all__ = ("mainloop", "get_main_loop")
from gi.repository import GLib
mainloop = GLib.MainLoop()
def get_main_loop():
"""Return the glib main loop as a singleton."""
return mainloop

View File

@ -0,0 +1,198 @@
# UpdateManager.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
import json
import dbus,time,sys
import logging
from .Core.errors import *
from .Core.enums import *
from .Core.loop import mainloop
from gettext import gettext as _
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
from .Core.Database import Sqlite3Server
from .backend.BackendOstreeNext import UpdateBackend,DownloadBackend,DeployBackend,RollbackBackend
from .UpdateManagerDbus import UpdateManagerDbusController,UPDATER_DBUS_INTERFACE,UPDATER_DBUS_PATH,UPDATER_DBUS_SERVICE
from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig
from .Core.MyCache import MyCache
from .UpdatesAvailable import UpdatesAvailable
import subprocess
OUTPUT_JSON_PATH = '/var/cache/kylin-system-updater/json/'
SYSTEM_UPDATE_GROUPS = "kylin-update-desktop-system"
class UpdateManager():
def __init__(self,options):
try:
self.options = options
#FIXME: 如何解决值的保存
# self.cache = MyCache()
self.trans = UpdatesAvailable(self)
self.sqlite3_server = Sqlite3Server(self)
self.configs_uncover = UpgradeConfig(defaults_dir="system-updater-defaults.conf")
self.bus = dbus.SystemBus()
self.dbus_send = self._setup_dbus(self.bus)
except Exception as e:
logging.error(e)
def run(self):
"""Start the daemon and listen for calls."""
logging.info("Waiting for calls...")
try:
mainloop.run()
except KeyboardInterrupt:
self.dbus_send.Quit(None)
def start_cancel(self):
if self.trans:
self.trans.cancellable.cancel()
#进行更新的操作
def start_update(self):
try:
def _download_info(parent,fetched,requested,bytes_transferred,bytes_sec):
self.dbus_send.UpdateDownloadInfo(fetched,requested,bytes_transferred,10000,bytes_sec)
#更新回调
def _update_finished(parent,trans):
if trans.exit:
# 检查是否可以升级
if trans.is_upgradable:
self._make_meta_for_panel(trans.available_metadata,OUTPUT_JSON_PATH)
self.dbus_send.UpdateDetectFinishedNext(True,[SYSTEM_UPDATE_GROUPS],'')
else:
self.dbus_send.UpdateDetectFinishedNext(True,[],'')
else:
self.dbus_send.UpdateDetectFinishedNext(trans.exit,[],trans.error_code)
update_backend = UpdateBackend(self,self.options.sysroot,self.options.os)
update_backend.connect("transaction-done", _update_finished)
update_backend.connect("download-info", _download_info)
update_backend.run(self.trans)
except Exception as e:
logging.error(e)
def start_download(self):
try:
def _download_info(parent,fetched,requested,bytes_transferred,bytes_sec):
self.dbus_send.UpdateDownloadInfo(fetched,requested,bytes_transferred,10000,bytes_sec)
def _download_finished(parent,trans):
self.dbus_send.UpdateDownloadFinishedNext(trans.exit,[SYSTEM_UPDATE_GROUPS],trans.error_code)
# if trans.exit:
# self.dbus_send.RebootLogoutRequired("reboot")
download_backend = DownloadBackend(self,self.options.sysroot,self.options.os)
download_backend.connect("transaction-done", _download_finished)
download_backend.connect("download-info", _download_info)
download_backend.run(self.trans)
except Exception as e:
logging.error(e)
def start_deploy(self,device_status):
try:
def _deploy_finished(parent,trans):
self.dbus_send.DeployUpdatFinishedNext(trans.exit,[SYSTEM_UPDATE_GROUPS],trans.available_metadata,trans.error_code)
if trans.exit:
self._system_reboot_shutdown(device_status,self.options.prohibit_shutdown)
deploy_backend = DeployBackend(self,self.options.sysroot,self.options.os)
deploy_backend.connect("transaction-done", _deploy_finished)
deploy_backend.run(self.trans)
except Exception as e:
logging.error(e)
def start_rollback(self):
try:
def _rollback_finished(parent,trans):
self.dbus_send.RollbackSysFinishedNext(trans.exit,trans.available_refs,trans.error_code)
if trans.exit:
self._system_reboot_shutdown("reboot",self.options.prohibit_shutdown)
rollback_backend = RollbackBackend(self)
rollback_backend.connect("transaction-done", _rollback_finished)
rollback_backend.run(self.trans)
except Exception as e:
logging.error(e)
def _make_meta_for_panel(self,data,output_path):
groups_base_info = {}
output_json = {}
output_config_name = output_path + SYSTEM_UPDATE_GROUPS + '.json'
groups_base_info.update({"package":SYSTEM_UPDATE_GROUPS})
groups_base_info.update({"new_version":data.setdefault("new_version","")})
groups_base_info.update({"name":data.setdefault("name","")})
groups_base_info.update({"description":data.setdefault("description","")})
groups_base_info.update({"icon":data.setdefault("icon","")})
groups_base_info.update({"total_download_size":data.setdefault("total_download_size","")})
groups_base_info.update({"total_install_size":data.setdefault("total_install_size","")})
groups_base_info.update({"changelog":data.setdefault("changelog","")})
output_json.update(groups_base_info)
with open(output_config_name, 'w', encoding='utf-8') as f:
json.dump(output_json, f, ensure_ascii=False, indent=4)
logging.info("Generate Jsonfile(%s) to complete... ",output_config_name)
def _system_reboot_shutdown(self,status="reboot",prohibit_shutdown=False):
if status == "shutdown":
args = ["/sbin/shutdown","-h","now"]
else:
args = ["/sbin/shutdown","-r","now"]
if prohibit_shutdown:
pass
else:
logging.info("The device will be %s by run system command(%r)...",status,args)
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode != 0:
logging.info("The device has (%s) Failed...",status)
return False
def _setup_dbus(self,bus):
# check if there is another g-a-i already and if not setup one
# listening on dbus
try:
bus_name = dbus.service.BusName(UPDATER_DBUS_SERVICE,
bus,do_not_queue=True)
logging.info("Initiate dbus success ...")
return UpdateManagerDbusController(self,bus,bus_name)
except dbus.exceptions.NameExistsException:
if self.options.replace is False:
logging.critical("Another daemon is already running")
sys.exit(1)
logging.warning("Replacing already running daemon")
retry_reboot_times = 0
the_other_guy = bus.get_object(UPDATER_DBUS_SERVICE,
UPDATER_DBUS_PATH)
the_other_guy.Quit(dbus_interface=UPDATER_DBUS_INTERFACE,
timeout=300)
time.sleep(1)
while True:
retry_reboot_times = retry_reboot_times + 1
#当重试次数超过5次时退出程序
if retry_reboot_times > 5:
logging.critical("Reboot backend is Failed...")
sys.exit(1)
try:
bus_name = dbus.service.BusName(UPDATER_DBUS_SERVICE,
bus,
do_not_queue=True)
logging.warning("Replacing already running daemon to Success...")
return UpdateManagerDbusController(self,bus,bus_name)
except dbus.exceptions.NameExistsException:
the_other_guy = bus.get_object(UPDATER_DBUS_SERVICE,
UPDATER_DBUS_PATH)
the_other_guy.Quit(dbus_interface=UPDATER_DBUS_INTERFACE,
timeout=300)
logging.error("Dbus has not withdrawn and retry reboot times:%d...",retry_reboot_times)
time.sleep(1)

View File

@ -0,0 +1,448 @@
#!/usr/bin/python3
import os
import dbus
import time
import dbus.service
import logging
from gettext import gettext as _
from .Core.loop import mainloop
import SystemUpdater.Core.enums as enums
from SystemUpdater.Core.errors import *
from SystemUpdater.Core.enums import *
from xml.etree import ElementTree
import locale
from gettext import ngettext
from math import ceil
UPDATER_DBUS_INTERFACE = 'com.kylin.systemupgrade.interface'
UPDATER_DBUS_PATH = '/com/kylin/systemupgrade'
UPDATER_DBUS_SERVICE = 'com.kylin.systemupgrade'
RUN_UNATTENDED_UPGRADE = '/var/run/unattended-upgrades.pid'
SYSTEM_VERSION = '/etc/kylin-version/kylin-system-version.conf'
UPDATER_DBUS_PATH_UTILS = '/com/kylin/systemupgrade/utils'
#颜色设置
COLORLOG_SUFFIX = "\033[0m"
# Define some foreground colors
BLACK = 30
RED = 31
GREEN = 32
YELLOW = 33
BLUE = 34
MAGENTA = 35
CYAN = 36
WHITE = 37
#字体颜色
FRONT_COLOR_SEQ = "\033[1;%dm"
#背景颜色
BACK_COLOR_SEQ = "\033[%d;1m"
COLORLOG_PREFIX = FRONT_COLOR_SEQ % GREEN
COLORMETHOR_PREFIX = FRONT_COLOR_SEQ % CYAN
UU_UPGRADE_MODE_TIMING = 0
UU_UPGRADE_MODE_BEFORE_SHUTDOWN = 1
ACTION_DEFUALT_STATUS = -1
ACTION_DOWNLOADONLY = 1
ACTION_DEPLOY = 2
def humanize_size(bytes):
"""
Convert a given size in bytes to a nicer better readable unit
"""
if bytes < 1000 * 1000:
# to have 0 for 0 bytes, 1 for 0-1000 bytes and for 1 and above
# round up
size_in_kb = int(ceil(bytes / float(1000)))
# TRANSLATORS: download size of small updates, e.g. "250 kB"
return ngettext("%(size).0f kB", "%(size).0f kB", size_in_kb) % {
"size": size_in_kb}
else:
# TRANSLATORS: download size of updates, e.g. "2.3 MB"
return locale.format_string(_("%.1f MB"), bytes / 1000.0 / 1000.0)
def get_proc_from_dbus_name(dbus_name, bus=None):
"""Return a deferred that gets the id of process owning the given
system D-Bus name.
"""
if not bus:
bus = dbus.SystemBus()
bus_obj = bus.get_object("org.freedesktop.DBus",
"/org/freedesktop/DBus/Bus")
pid = bus_obj.GetConnectionUnixProcessID(dbus_name,
dbus_interface="org.freedesktop.DBus")
# proc = psutil.Process(int(pid))
with open("/proc/%s/status" % pid) as process:
values = [v for v in process.readlines() if v.startswith("Uid:")]
uid = int(values[0].split()[1])
#检查是否root用户执行
if uid == 0:
return "root"
# return proc.name()
return uid
#dbus 建立
class UpdateManagerDbusController(dbus.service.Object):
""" this is a helper to provide the UpdateManagerIFace """
ACTION_DEFUALT_STATUS = -1
ACTION_UPDATE = 0
ACTION_DOWNLOADONLY = 1
ACTION_DEPLOY = 2
ACTION_ROLLBACK = 3
RETURN_SUCCESS_CODE = 0
RETURN_SUCCESS_DESC = ""
RETURN_UNKNOWN_CODE = -1
RETURN_UNKNOWN_DESC = ""
RETURN_BUSY_STATE = 1
RETURN_BUSY_DESC = "In the process of updating or Upgrading..."
def __init__(self, parent,bus,bus_name,
object_path=UPDATER_DBUS_PATH):
dbus.service.Object.__init__(self, bus_name, object_path)
self.parent = parent
# self.cache = parent.cache
self.bus = bus
self.prohibit_list = []
self.database = self.parent.sqlite3_server
self.now_working = self.ACTION_DEFUALT_STATUS
self.transaction = None
if not self.parent.options.debug:
self.prohibit_list = ["dbus-send","gdbus"]
def _check_prohibit_user(self, sender_name):
if sender_name in self.prohibit_list:
raise dbus.exceptions.DBusException("ERROR: You are not allowed to perform this action.")
@dbus.service.method(UPDATER_DBUS_INTERFACE,
in_signature="", out_signature="",
sender_keyword="caller_name")
def Quit(self, caller_name):
"""Request a shutdown of the daemon."""
logging.info("Quitting was requested")
logging.debug("Quitting main loop...")
mainloop.quit()
logging.debug("Exit")
#获取后端现在的状态
@dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='s',out_signature='i',sender_keyword='sender')
def GetBackendStatus(self,user_lang,sender=None):
try:
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' GetBackendStatus and user_lang = %s sender_name:%s status:%d...',str(user_lang),sender_name,self.now_working)
return self.now_working
except Exception as e:
logging.error(str(e))
return self.now_working
#更新的dbus
@dbus.service.method(UPDATER_DBUS_INTERFACE,out_signature='b',sender_keyword='sender')
def CheckPopupOnPoweroffInstalled(self,sender=None):
try:
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' ChcekPopupOnPoweroffInstalled sender:%s...',sender_name)
return False
except Exception as e:
logging.error(str(e))
return False
# =============================== 检查更新 =============================== */
@dbus.service.method(UPDATER_DBUS_INTERFACE,out_signature='is',sender_keyword='sender')
def UpdateDetect(self,sender=None):
try:
#处于更新和升级中的话 不进行更新
if self.now_working != ACTION_DEFUALT_STATUS:
logging.warning('UpdateDetect In the process of Updating or Upgrading...')
return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC
else:
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UpdateDetect sender:%s...',sender_name)
self._check_prohibit_user(sender_name)
self.parent.start_update()
self.now_working = self.ACTION_UPDATE
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
except Exception as e:
logging.error(str(e))
return self.RETURN_UNKNOWN_CODE,str(e)
#更新 信号
def UpdateDetectFinishedNext(self,success,upgrade_group,error_code=''):
self.UpdateDetectFinished(success,upgrade_group,error_code,get_error_description_from_enum(error_code))
@dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='basss')
def UpdateDetectFinished(self, success, upgrade_group,error_code='',error_desc=''):
self.now_working = self.ACTION_DEFUALT_STATUS
logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " UpdateDetectFinished success = %r , upgrade_group = %a, error_code = %s , error_desc = %s ",\
success,upgrade_group,error_code,error_desc)
# =============================== 下载 =============================== */
@dbus.service.method(UPDATER_DBUS_INTERFACE,out_signature='is',sender_keyword='sender')
def UpdateDownloadAll(self,sender=None):
try:
#处于更新和升级中的话 不进行更新
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UpdateDownloadAll sender:%s...',sender_name)
self._check_prohibit_user(sender_name)
self.parent.start_download()
self.now_working = self.ACTION_DOWNLOADONLY
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
except Exception as e:
logging.error(str(e))
return self.RETURN_UNKNOWN_CODE,str(e)
# 下载信号
def UpdateDownloadFinishedNext(self,success,upgrade_group,error_code=''):
self.UpdateDownloadFinished(success,upgrade_group,error_code,get_error_description_from_enum(error_code))
@dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='basss')
def UpdateDownloadFinished(self, success, upgrade_group,error_code='',error_desc=''):
self.now_working = self.ACTION_DEFUALT_STATUS
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" UpdateDownloadFinished success = %r , upgrade_group = %a, error_code = %s , error_desc = %s ",\
success,upgrade_group, error_code,error_desc)
#发送下载包信息 fix bug 字节大小改成u 无符号32位
@dbus.service.signal(UPDATER_DBUS_INTERFACE, signature='iiuui')
def UpdateDownloadInfo(self,current_items, total_items, currenty_bytes, total_bytes, current_cps):
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" UpdateDownloadInfo current_items = %d, total_items = %d, currenty_bytes = %s, total_bytes = %s, current_cps = %s/s",
current_items, total_items, \
humanize_size(currenty_bytes), humanize_size(total_bytes),\
humanize_size(current_cps))
@dbus.service.method(UPDATER_DBUS_INTERFACE,out_signature='is',sender_keyword='sender')
def CancelDownload(self,sender=None):
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' CancelDownload ')
self.parent.start_cancel()
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
# =============================== 部署 =============================== */
@dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='s',out_signature='is',sender_keyword='sender')
def DeployLatestUpdate(self,mode,sender=None):
try:
#处于更新和升级中的话 不进行更新
if self.now_working != ACTION_DEFUALT_STATUS:
logging.warning('DeployLatestUpdate In the process of Updating or Upgrading...')
return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC
else:
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' DeployLatestUpdate sender:%s...',sender_name)
self._check_prohibit_user(sender_name)
self.parent.start_deploy(str(mode))
self.now_working = self.ACTION_DEPLOY
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
except Exception as e:
logging.error(str(e))
return self.RETURN_UNKNOWN_CODE,str(e)
def DeployUpdatFinishedNext(self,success,upgrade_group,metadata,error_code=''):
self.DeployUpdatFinished(success,upgrade_group,error_code,get_error_description_from_enum(error_code))
self.database.insert_info(success,metadata,error_code)
@dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='basss')
def DeployUpdatFinished(self, success, upgrade_group,error_code='',error_desc=''):
self.now_working = self.ACTION_DEFUALT_STATUS
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" DeployUpdatFinished success = %r , upgrade_group = %a, error_code = %s , error_desc = %s ",\
success,upgrade_group, error_code,error_desc)
# =============================== 回滚 =============================== */
@dbus.service.method(UPDATER_DBUS_INTERFACE,out_signature='is',sender_keyword='sender')
def RollbackSysVersion(self,mode,version,sender=None):
try:
#处于更新和升级中的话 不进行更新
if self.now_working != ACTION_DEFUALT_STATUS:
logging.warning('RollbackSysVersion In the process of Updating or Upgrading...')
return self.RETURN_BUSY_STATE,self.RETURN_BUSY_DESC
else:
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' RollbackSysVersion sender:%s...',sender_name)
self._check_prohibit_user(sender_name)
self.parent.start_rollback()
self.now_working = self.ACTION_ROLLBACK
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
except Exception as e:
logging.error(str(e))
return self.RETURN_UNKNOWN_CODE,str(e)
#向数据库display表中插入数据
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='ss', out_signature='is', sender_keyword='sender')
def InsertInstallState(self, item, value, sender=None):
try:
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' InsertInstallState, options:%svalue:%s, InsertInstallState sender: %s .' % (item, value, sender_name))
self.database.insert_into_display(item, value)
logging.info("Database insert to display successfullyoptions:%svalue:%s" % (item, value))
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
except Exception as e:
logging.error("Database insert to display failedoptions:%svalue:%s" % (item, value))
return self.RETURN_UNKNOWN_CODE,str(e)
def RollbackSysFinishedNext(self, success, version_name,error_code):
self.RollbackSysFinished(success,version_name,error_code,get_error_description_from_enum(error_code))
@dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='bsss')
def RollbackSysFinished(self, success, version_name,error_code='',error_desc=''):
self.now_working = self.ACTION_DEFUALT_STATUS
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" RollbackSysFinished success = %r , upgrade_group = %s, error_code = %s , error_desc = %s ",\
success,version_name, error_code,error_desc)
#重启和注销请求信号
@dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='s')
def RebootLogoutRequired(self,required_status=''):
logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " RebootLogoutRequired required_status = %s",required_status)\
WRITABLE_PROPERTIES = ()
# pylint: disable-msg=C0103,C0322
@dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE,
signature="sa{sv}as")
def PropertiesChanged(self, interface, changed_properties,
invalidated_properties):
"""The signal gets emitted if a property of the object's
interfaces changed.
:param property: The name of the interface.
:param changed_properties: A dictrionary of changed
property/value pairs
:param invalidated_properties: An array of property names which
changed but the value isn't conveyed.
:type interface: s
:type changed_properties: a{sv}
:type invalidated_properties: as
"""
logging.info("Emitting PropertiesChanged: %s, %s, %s" %
(interface, changed_properties, invalidated_properties))
# pylint: disable-msg=C0103,C0322
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
in_signature='', out_signature='s',
path_keyword='object_path',
connection_keyword='connection')
def Introspect(self, object_path, connection):
# Inject the properties into the introspection xml data
data = dbus.service.Object.Introspect(self, object_path, connection)
xml = ElementTree.fromstring(data)
for iface in xml.findall("interface"):
props = self._get_properties(iface.attrib["name"])
if not hasattr(props,'items'):
continue
for key, value in props.items():
attrib = {"name": key}
if key in self.WRITABLE_PROPERTIES:
attrib["access"] = "readwrite"
else:
attrib["access"] = "read"
if isinstance(value, dbus.String):
attrib["type"] = "s"
elif isinstance(value, dbus.UInt32):
attrib["type"] = "u"
elif isinstance(value, dbus.Int32):
attrib["type"] = "i"
elif isinstance(value, dbus.UInt64):
attrib["type"] = "t"
elif isinstance(value, dbus.Int64):
attrib["type"] = "x"
elif isinstance(value, dbus.Boolean):
attrib["type"] = "b"
elif isinstance(value, dbus.Struct):
attrib["type"] = "(%s)" % value.signature
elif isinstance(value, dbus.Dictionary):
attrib["type"] = "a{%s}" % value.signature
elif isinstance(value, dbus.Array):
attrib["type"] = "a%s" % value.signature
else:
raise Exception("Type %s of property %s isn't "
"convertable" % (type(value), key))
iface.append(ElementTree.Element("property", attrib))
new_data = ElementTree.tostring(xml, encoding="UTF-8")
return new_data
# pylint: disable-msg=C0103,C0322
@dbus.service.method(dbus.PROPERTIES_IFACE,
in_signature="ssv", out_signature="",
sender_keyword="sender")
def Set(self, iface, name, value, sender):
"""Set a property.
Only the user who intiaited the transaction is
allowed to modify it.
:param iface: The interface which provides the property.
:param name: The name of the property which should be modified.
:param value: The new value of the property.
:type iface: s
:type name: s
:type value: v
"""
logging.info("Set() was called: %s, %s" % (name, value))
return self._set_property(iface, name, value, sender)
# pylint: disable-msg=C0103,C0322
@dbus.service.method(dbus.PROPERTIES_IFACE,
in_signature="s", out_signature="a{sv}")
def GetAll(self, iface):
"""Get all available properties of the given interface."""
logging.info("GetAll() was called: %s" % iface)
return self._get_properties(iface)
# pylint: disable-msg=C0103,C0322
@dbus.service.method(dbus.PROPERTIES_IFACE,
in_signature="ss", out_signature="v")
def Get(self, iface, property):
"""Return the value of the given property provided by the given
interface.
"""
logging.info("Get() was called: %s, %s" % (iface, property))
return self._get_properties(iface)[property]
def _set_property(self, iface, name, value, sender):
"""Helper to set a property on the properties D-Bus interface."""
'''
if iface == UPDATER_DBUS_INTERFACE:
if name == "ShutdownInstall":
self.parent.configs_uncover.setValue("InstallMode","shutdown_install",str(bool(value)))
elif name == "P2pBootstrap":
self.parent.apt_p2p_config.set_bootstrap(str(value))
else:
raise dbus.exceptions.DBusException("Unknown or read only "
"property: %s" % name)
else:
raise dbus.exceptions.DBusException("Unknown interface: %s" %
iface)
'''
def _get_properties(self, iface):
"""Helper get the properties of a D-Bus interface."""
if iface == UPDATER_DBUS_INTERFACE:
return {
"CurentOriginRefs": dbus.String(""),
"RollbackDeployment":dbus.String(""),
}
else:
return {}

View File

@ -0,0 +1,101 @@
#!/usr/bin/python3
import os
import json
import logging
from gi.repository import Gio
from gettext import gettext as _
from SystemUpdater.Core.errors import *
from SystemUpdater.Core.enums import *
class UpdatesAvailable():
"""
Represent the (potentially partial) results of an unattended-upgrades
run
"""
# 更新的缓存数据保存位置
UPDATE_AVAILABLE_DATA = "/var/cache/kylin-system-updater/update_available_data.json"
def __init__(self,parent):
self.parent = parent
self.error_code = ''
self.exit = None
self.is_upgradable = False
self.available_checksum = None
self.available_refs = None
self.available_metadata = None
def cancelled():
logging.info("Task has been cancelled...")
self.cancellable = Gio.Cancellable.new()
self.cancellable.connect(cancelled)
def _output_resolver_pkgs(self,pkgs):
OUTPUT_JSON = "/var/lib/kylin-system-updater/json/requires_upgrade_pkgs.json"
try:
output_upgrade = {}
pkg_string = ''
for pkg in pkgs:
pkg_string = pkg_string + ' ' + str(pkg)
output_upgrade.update({"packages":pkg_string})
with open(OUTPUT_JSON, 'w', encoding='utf-8') as f:
json.dump(output_upgrade, f, ensure_ascii=False, indent=4)
logging.info("Generate Jsonfile(%s) to complete... ",OUTPUT_JSON)
logging.info("Update Packages list to config file...")
except Exception as exc:
logging.error(exc)
def _write_local(self):
try:
output_upgrade = {}
output_upgrade.update({"steps_queue":self.steps_queue})
output_upgrade.update({"_problem_resolver":self._problem_resolver})
output_upgrade.update({"updating_data":self.updating_data})
output_upgrade.update({"upgrade_singles":self.upgrade_singles})
output_upgrade.update({"upgrade_groups":self.upgrade_groups})
output_upgrade.update({"upgrade_steps":self.upgrade_steps})
output_upgrade.update({"device_status":self.device_status})
output_upgrade.update({"shutdown_install":self.shutdown_install})
output_upgrade.update({"signal_trigger":self.signal_trigger})
output_upgrade.update({"_available":self._available})
#6 产生JSON文件
with open(self.UPDATE_AVAILABLE_DATA, 'w', encoding='utf-8') as f:
json.dump(output_upgrade, f, ensure_ascii=False, indent=4)
logging.info("Write Upgrade data to local json(%s) in shutdown model...",self.UPDATE_AVAILABLE_DATA)
except Exception as exc:
logging.error(exc)
def read_local(self):
#读取组JSON文件
try:
with open(self.UPDATE_AVAILABLE_DATA,'r') as f:
data = json.load(f)
self.steps_queue = data["steps_queue"]
self._problem_resolver = data["_problem_resolver"]
self.updating_data = data["updating_data"]
self.upgrade_singles = data["upgrade_singles"]
self.upgrade_groups = data["upgrade_groups"]
self.upgrade_steps = data["upgrade_steps"]
self.device_status = data["device_status"]
self.shutdown_install = data["shutdown_install"]
self.signal_trigger = data["signal_trigger"]
self._available = data["_available"]
logging.info("Finished: reading upgrade data from the local file...")
except Exception as exc:
logging.error(exc)
def _get_available(self):
return self._available
def _set_available(self, state):
self._available = state
self._write_local()
available = property(_get_available, _set_available)

View File

@ -0,0 +1,101 @@
# UpdateManager.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
import os
import sys
import time
import dbus
import logging
import dbus.service
import traceback
from gettext import gettext as _
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib
DBusGMainLoop(set_as_default=True)
from .UpgradeStrategiesDbus import UpgradeStrategiesDbusController,UPDATER_DBUS_INTERFACE,UPDATER_DBUS_PATH,UPDATER_DBUS_SERVICE
from .Core.Database import Sqlite3Server
from .Core.loop import mainloop
from SystemUpdater.Core.UpdaterConfigParser import UpgradeConfig
STRATEGY_IDLE_INTERVAL = 2*60
STRATEGY_IDLE_TIMEOUT = 6*60
class UpgradeStrategies():
def __init__(self,options):
try:
self.options = options
#dbus
self.dbusController = self._setup_dbus()
#config
self.uuconfigs = UpgradeConfig(datadir = "/var/lib/unattended-upgrades/", name = "unattended-upgrades-policy.conf")
self.sqlite3_server = Sqlite3Server(self, _no_DataMigration=True)
#策略配置接口的超时退出机制
self.strategy_timestamp = 0
# GLib.timeout_add_seconds(STRATEGY_IDLE_INTERVAL,
# self._check_strategy_inactivity)
except Exception as e:
logging.error(e)
traceback.print_exc()
def run(self):
"""Start the daemon and listen for calls."""
logging.info("Waiting for calls...")
try:
mainloop.run()
except KeyboardInterrupt:
self.dbusController.Quit(None)
def _setup_dbus(self):
# check if there is another g-a-i already and if not setup one
# listening on dbus
bus = dbus.SystemBus()
try:
bus_name = dbus.service.BusName(UPDATER_DBUS_SERVICE,
bus,
do_not_queue=True)
logging.info("Initiate dbus success ...")
return UpgradeStrategiesDbusController(self, bus_name)
except dbus.exceptions.NameExistsException:
if self.options.replace is False:
logging.critical("Another daemon is already running")
sys.exit(1)
logging.warning("Replacing already running daemon")
retry_reboot_times = 0
the_other_guy = bus.get_object(UPDATER_DBUS_SERVICE,
UPDATER_DBUS_PATH)
the_other_guy.Quit(dbus_interface=UPDATER_DBUS_INTERFACE,
timeout=300)
time.sleep(1)
while True:
retry_reboot_times = retry_reboot_times + 1
#当重试次数超过5次时退出程序
if retry_reboot_times > 5:
logging.critical("Reboot backend is Failed...")
sys.exit(1)
try:
bus_name = dbus.service.BusName(UPDATER_DBUS_SERVICE,
bus,
do_not_queue=True)
logging.warning("Replacing already running daemon to Success...")
return UpgradeStrategiesDbusController(self, bus_name)
except dbus.exceptions.NameExistsException:
the_other_guy = bus.get_object(UPDATER_DBUS_SERVICE,
UPDATER_DBUS_PATH)
the_other_guy.Quit(dbus_interface=UPDATER_DBUS_INTERFACE,
timeout=300)
logging.error("Dbus has not withdrawn and retry reboot times:%d...",retry_reboot_times)
time.sleep(1)
def _check_strategy_inactivity(self):
logging.info("Checking for inactivity in Strategies daemon ...")
timestamp = self.strategy_timestamp
if timestamp == 0:
self.strategy_timestamp = time.time()
return True
#超时退出
if self.strategy_timestamp != 0 and time.time() - self.strategy_timestamp > STRATEGY_IDLE_TIMEOUT:
logging.warning("Quitting due to inactivity")
self.dbusController.Quit(None)
return False
return True

View File

@ -0,0 +1,610 @@
#!/usr/bin/python3
import os
import dbus
import dbus.service
import logging
import subprocess
from gettext import gettext as _
from .Core.loop import mainloop
from SystemUpdater.Core.utils import get_proc_from_dbus_name
UPDATER_DBUS_INTERFACE = 'com.kylin.UpgradeStrategies.interface'
UPDATER_DBUS_PATH = '/com/kylin/UpgradeStrategies'
UPDATER_DBUS_SERVICE = 'com.kylin.UpgradeStrategies'
RUN_UNATTENDED_UPGRADE = '/var/run/unattended-upgrades.pid'
SYSTEM_VERSION = '/etc/kylin-version/kylin-system-version.conf'
#颜色设置
COLORLOG_SUFFIX = "\033[0m"
# Define some foreground colors
BLACK = 30
RED = 31
GREEN = 32
YELLOW = 33
BLUE = 34
MAGENTA = 35
CYAN = 36
WHITE = 37
#字体颜色
FRONT_COLOR_SEQ = "\033[1;%dm"
#背景颜色
BACK_COLOR_SEQ = "\033[%d;1m"
COLORLOG_PREFIX = FRONT_COLOR_SEQ % GREEN
COLORMETHOR_PREFIX = FRONT_COLOR_SEQ % CYAN
UU_UPGRADE_MODE_AUTOMATIC_DOWNLOAD = 0
UU_UPGRADE_MODE_MANUAL = 1
UU_UPGRADE_MODE_AUTOMATIC_INSTALL = 2
UU_UPGRADE_MODE_BEFORE_SHUTDOWN = 3
#dbus 建立
class UpgradeStrategiesDbusController(dbus.service.Object):
""" this is a helper to provide the UpdateManagerIFace """
P2P_DEDAULT_PATH = "/etc/default/apt-p2p"
RETURN_SUCCESS_CODE = 0
RETURN_SUCCESS_DESC = ""
RETURN_UNKNOWN_CODE = -1
RETURN_UNKNOWN_DESC = ""
def __init__(self, parent, bus_name,
object_path=UPDATER_DBUS_PATH):
dbus.service.Object.__init__(self, bus_name, object_path)
self.parent = parent
self.bus = dbus.SystemBus()
self.transaction = None
def __check_change__(self, _config = None, _section = "", _option = "", _value = ""):
if _config == None:
return False
if _value == _config.getWithDefault(_section,_option,_value," "):
return True
return False
@dbus.service.method(UPDATER_DBUS_INTERFACE,
in_signature="", out_signature="",
sender_keyword="caller_name")
def Quit(self, caller_name):
"""Request a shutdown of the daemon."""
#如果在下载就请求 取消
logging.info("Quitting was requested")
logging.debug("Quitting main loop...")
mainloop.quit()
logging.debug("Exit")
## dbus接口: 开启或关闭预下载功能
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='s', out_signature='is',sender_keyword='sender')
def ChangingP2PStatus(self,_status,sender = None):
status = str(_status)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' ChangingP2PStatus, _status = %s , sender name: %s',status,sender_name)
if os.path.exists(self.P2P_DEDAULT_PATH):
if status == "enable":
with open(self.P2P_DEDAULT_PATH, 'w+') as configfile:
configfile.write("#enable=true\n")
#第一次进行检查是否已经开启
args = ["systemctl","is-enabled","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode != 0:
logging.info("Apt-p2p service is not runing and will be enable...")
#第二次进行重启
args = ["systemctl","enable","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Service Enable Execute successfully")
#第三次进行重启
args = ["systemctl","restart","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Restart Execute successfully")
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
else:
logging.error(str(p.stdout))
logging.error("Failed to execute reboot")
return self.RETURN_UNKNOWN_CODE,str(p.stdout)
else:
logging.error(str(p.stdout))
logging.error("Failed to execute enable")
return self.RETURN_UNKNOWN_CODE,str(p.stdout)
else:
logging.info("Apt-p2p service has been enabled and not need to reabled...")
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
elif status == "disable":
with open(self.P2P_DEDAULT_PATH, 'w+') as configfile:
configfile.write("enable=false\n")
args = ["systemctl","is-enabled","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Apt-p2p service is runing and will be disable...")
args = ["systemctl","disable","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Service disable Execute successfully")
args = ["systemctl","stop","apt-p2p.service"]
p = subprocess.run(args, stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Stop Execute successfully")
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
else:
logging.error(str(p.stdout))
logging.error("Failed to execute Stop")
return self.RETURN_UNKNOWN_CODE,str(p.stdout)
else:
logging.error(str(p.stdout))
logging.error("Failed to execute disable")
return self.RETURN_UNKNOWN_CODE,str(p.stdout)
else:
logging.info("Apt-p2p service has been disabled and not need to redisabled...")
return self.RETURN_SUCCESS_CODE,self.RETURN_SUCCESS_DESC
else:
logging.warning("error: input value _status=%s",status)
else:
logging.warning("apt-p2p function is not install...")
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='b', out_signature='b',sender_keyword='sender')
def SetUpgradeStrategyState(self,state,sender=None):
state=bool(state)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetUpdateStrategyState, upgrade strategy state is %r, sender name: %s .',state,sender_name)
try:
if state:
if not self.__check_change__(self.parent.uuconfigs, "updateStrategiesManager", "strategiesState", "True"):
self.parent.uuconfigs.setValue("updateStrategiesManager", "strategiesState", "True", True)
self.StrategyChanged("strategiesState","True")
else:
if not self.__check_change__(self.parent.uuconfigs, "updateStrategiesManager", "strategiesState", "False"):
self.parent.uuconfigs.setValue("updateStrategiesManager", "strategiesState", "False", True)
self.StrategyChanged("strategiesState","False")
except Exception as e:
logging.error(str(e))
return False
return True
## 设置更新周期
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='s', out_signature='b',sender_keyword='sender')
def SetUpgradeMode(self, mode, sender = None):
_mode = str(mode)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetUpgradeMode, mode: %s , sender:%s .',_mode,sender_name)
try:
if not self.__check_change__(self.parent.uuconfigs, "updateStrategiesManager", "installType", _mode):
self.parent.uuconfigs.setValue("updateStrategiesManager", "installType", _mode, True)
self.StrategyChanged("installType",_mode)
except Exception as e:
logging.error(str(e))
return False
return True
## dbus接口: 开启或关闭预下载功能
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='bs', out_signature='b',sender_keyword='sender')
def SetPreDownloadState(self, _state, _time, sender = None):
state = bool(_state)
time = str(_time)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetPreDownloadState, state is %r, time: %s, sender name: %s .',state,time,sender_name)
try:
if state:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "preDownload", "on"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "preDownload", "on", True)
self.PropertyChanged("preDownload","on")
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "preDownloadTime", time):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "preDownloadTime", time, True)
self.PropertyChanged("preDownloadTime",time)
else:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "preDownload", "off"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "preDownload", "off", True)
self.PropertyChanged("preDownload","off")
except Exception as e:
logging.error(str(e))
return False
return True
## 设置更新周期
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='i', out_signature='b',sender_keyword='sender')
def SetUpdateDays(self, days, sender = None):
_days = int(days)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetUpdateDays, days: %d , sender:%s .'\
,_days,sender_name)
try:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "updateDays", _days):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "updateDays", str(_days), True)
self.PropertyChanged("updateDays",str(_days))
except Exception as e:
logging.error(str(e))
return False
return True
# 设置自动更新时间
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='s', out_signature='bs',sender_keyword='sender')
def SetAutoUpgradeRandomRange(self,randomRange,sender=None):
_randomRange = str(randomRange)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetAutoUpgradeRandomRange will be set value %s, sender: %s .',\
_randomRange,sender_name)
try:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "randomRange", _randomRange):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "randomRange", _randomRange, True)
self.PropertyChanged("randomRange",_randomRange)
except Exception as e:
logging.error(str(e))
return True,"success"
## 设置是否开启自动重启,以及设置自动重启的时间
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='bs', out_signature='b',sender_keyword='sender')
def SetAutomaticReboot(self, status, reboot_time, sender = None):
_state = bool(status)
_reboot_time = str(reboot_time)
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetAutomaticReboot, status is %r, reboot_time: %s, sender name: %s .'\
,_state,_reboot_time,sender_name)
try:
if _state:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "automaticReboot", "on"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "automaticReboot", "on", True)
self.PropertyChanged("automaticReboot","on")
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "automaticRebootTime", _reboot_time):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "automaticRebootTime", _reboot_time, True)
self.PropertyChanged("automaticRebootTime",_reboot_time)
else:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "automaticReboot", "off"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "automaticReboot", "off", True)
self.PropertyChanged("automaticReboot","off")
except Exception as e:
logging.error(str(e))
return False
return True
## dbus接口: 开启关闭自动更新功能
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='b', out_signature='b')
def SetAutoUpgradeState(self, _state):
state = bool(_state)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetAutoUpgradeState, state is %r ...',state)
try:
if state:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "autoUpgradeState", "on"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "autoUpgradeState", "on", True)
self.parent.sqlite3_server.insert_into_display("autoupdate_allow", "true")
self.PropertyChanged("autoUpgradeState","on")
self.ButtonStatusChange("autoUpgradeStatus", "true")
else :
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "autoUpgradeState", "off"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "autoUpgradeState", "off", True)
self.parent.sqlite3_server.insert_into_display("autoupdate_allow", "false")
self.PropertyChanged("autoUpgradeState","off")
self.ButtonStatusChange("autoUpgradeStatus", "false")
except Exception as e:
logging.error(str(e))
return False
return True
## dbus接口: 设置自动更新策略
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='is', out_signature='b')
def SetAutoUpgradeMode(self, mode, time):
_time = str(time)
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' SetAutoUpgradeMode, mode is %s, time is %s ...',mode,_time)
try:
if mode == UU_UPGRADE_MODE_AUTOMATIC_DOWNLOAD:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "downloadMode", "timing"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "downloadMode", "timing", True)
self.PropertyChanged("downloadMode","timing")
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "downloadTime", str(_time)):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "downloadTime", str(_time), True)
self.PropertyChanged("downloadTime",str(_time))
self.ButtonStatusChange("autoUpgradeTime", str(_time))
elif mode == UU_UPGRADE_MODE_AUTOMATIC_INSTALL:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "installMode", "timing"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "installMode", "timing", True)
self.PropertyChanged("installMode","timing")
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "installTime", str(_time)):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "installTime", str(_time), True)
self.PropertyChanged("installTime",str(_time))
elif mode == UU_UPGRADE_MODE_BEFORE_SHUTDOWN:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "installTime", "bshutdown"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "installMode", "bshutdown", True)
self.PropertyChanged("installMode","bshutdown")
elif mode == UU_UPGRADE_MODE_MANUAL:
if not self.__check_change__(self.parent.uuconfigs, "autoUpgradePolicy", "downloadMode", "manual"):
self.parent.uuconfigs.setValue("autoUpgradePolicy", "downloadMode", "manual", True)
self.PropertyChanged("downloadMode","manual")
except Exception as e:
logging.error(str(e))
return False
return True
# # dbus接口改变apt下载速度
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='sb', out_signature='b',sender_keyword='sender')
def SetDownloadspeedMax(self, speed, set,sender = None):
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetDownloadspeedMax, speed:%s, set:%r, sender name: %s .'%(speed, set, sender_name))
#来重启Aptdeamon
if set:
with open("/etc/apt/apt.conf.d/80apt-download", "w+") as f:
try:
f.write("Acquire::http::Dl-Limit" + " \"" + "%s" % str(speed) + "\";\n")
f.write("Acquire::https::Dl-Limit" + " \"" + "%s" % str(speed) + "\";\n")
#更改数据库值
self.parent.sqlite3_server.insert_into_display("download_limit","true")
self.parent.sqlite3_server.insert_into_display("download_limit_value",str(speed))
#发送信号
self.ButtonStatusChange("speed" , str(speed))
return True
except Exception as e:
logging.error(e)
return False
else:
if os.path.exists("/etc/apt/apt.conf.d/80apt-download"):
os.remove("/etc/apt/apt.conf.d/80apt-download")
self.parent.sqlite3_server.insert_into_display("download_limit","false")
self.ButtonStatusChange("speed", "0")
return True
else:
self.parent.sqlite3_server.insert_into_display("download_limit","false")
self.ButtonStatusChange("speed", "0")
return True
# # dbus接口获取apt下载速度
@dbus.service.method(UPDATER_DBUS_INTERFACE, out_signature='bs',sender_keyword='sender')
def GetDownloadspeedLimitValue(self,sender = None):
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' GetDownloadspeedLimitValue sender: %s .', sender_name)
try:
download_limit = self.parent.sqlite3_server.select_from_display("download_limit")
if download_limit == "true":
download_limit_value = self.parent.sqlite3_server.select_from_display("download_limit_value")
return True,str(download_limit_value)
else:
return False,str("0")
except:
return False, "0"
# 是否允许关机前更新
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='ss', out_signature='bs', sender_keyword='sender')
def UnattendedUpgradeValue(self, operation, value="false", sender=None):
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UnattendedUpgradeValue sender:%s ', sender_name)
if operation.lower() != "get" and operation.lower() != "set":
return False, 'Please input [\"set\", \"value\"] to set. \nor [\"get\"] to get whether updates are allowed before shutdown.'
if operation == "set":
try:
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UnattendedUpgradeValue is going to %s [allow_unattended_upgrades_shutdown] value to %s.'%(operation,value))
self.parent.sqlite3_server.insert_into_display("allow_unattended_upgrades_shutdown", value.lower())
except Exception as e:
logging.error(str(e))
return False,str(e)
else:
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' UnattendedUpgradeValue is going to %s [allow_unattended_upgrades_shutdown] value.'%(operation))
try:
value = self.parent.sqlite3_server.select_from_display("allow_unattended_upgrades_shutdown")
logging.info("[allow_unattended_upgrades_shutdown] value is %s."%(value))
except Exception as e:
logging.error(str(e))
return False,str(e)
return True,"success"
# # 设置自动更新时间
# @dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='s', out_signature='bs',sender_keyword='sender')
# def SetAutoUpgradePeriod(self, period, sender = None):
# sender_name = get_proc_from_dbus_name(sender)
# logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetAutoUpgradePeriod will be set value %s, SetAutoUpgradePeriod sender: %s.'%(period, sender_name))
# try:
# self.parent.sqlite3_server.insert_into_display("update_period", period.lower())
# except Exception as e:
# logging.error(str(e))
# return True,"success"
# 获取数据库值
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='bss', out_signature='s')
def GetSetDatabaseInfo(self, gs, table, field):
Text = 'NULL'
try:
if gs: #get
if table == 'display':
Text = self.parent.sqlite3_server.select_from_display(str(field))
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' GetDatabaseInfo Table:%s Field:%s Text:%s',table,field,Text)
else: #set
if table == 'display' and "=" in field:
field, value = str(field).split("=")
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetDatabaseInfo Table:%s Field:%s', table, field)
self.parent.sqlite3_server.insert_into_display(field, value)
return "success"
except Exception as e:
logging.error(str(e))
return Text
return Text
## dbus接口: 发送立即更新的信号
@dbus.service.method(UPDATER_DBUS_INTERFACE, out_signature='b')
def AutoUpgradeAllNow(self):
logging.info(COLORMETHOR_PREFIX+'method'+COLORLOG_SUFFIX+' AutoUpgradeAllNow ...')
try:
self.UpgradeAllNow()
except Exception as e:
logging.error(str(e))
return False
return True
# kill 进程
@dbus.service.method(UPDATER_DBUS_INTERFACE, in_signature='i', out_signature='b')
def KillProcessSignal(self, pid):
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' KillProcessSignal is %d', pid)
try:
# 判断文件是否存在
if (os.path.exists(RUN_UNATTENDED_UPGRADE)):
os.kill(int(pid), 9)
logging.info('%s has been killed', pid)
else:
logging.warning('%s is not exist.', RUN_UNATTENDED_UPGRADE)
except Exception as e:
logging.error(str(e))
return False
return True
#设置数据库配置信息
@dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='ss',out_signature='b',sender_keyword='sender')
def DatabaseInfoSet(self,field_name,field_value,sender=None):
Status = False
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' SetDatabaseInfo,field_name:%s,field_value:%s,caller:%s .',\
field_name,field_value,sender_name)
Status = self.parent.sqlite3_server.insert_into_display(field_name,field_value)
return bool(Status)
#数据库获取配置信息
@dbus.service.method(UPDATER_DBUS_INTERFACE,in_signature='s',out_signature='s',sender_keyword='sender')
def DatabaseInfoGet(self,field_name,sender=None):
field_value = ''
sender_name = get_proc_from_dbus_name(sender)
logging.info(COLORMETHOR_PREFIX+'Method'+COLORLOG_SUFFIX+' GetDatabaseInfo field_name:%s caller:%s',field_name,sender_name)
field_value = self.parent.sqlite3_server.select_from_display(str(field_name))
logging.info("Get field_value:%s",field_value)
return field_value
#限速修改信号
@dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='ss')
def ButtonStatusChange(self, signal_types = '', value=''):
logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " ButtonStatusChange signal_types = %s, value = %s.",signal_types, value)
# dbus 信号:用于发送立即更新信号
@dbus.service.signal(UPDATER_DBUS_INTERFACE)
def UpgradeAllNow(self):
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" UpgradeAllNow")
# dbus 信号:用于发送自动更新配置更改信号
@dbus.service.signal(UPDATER_DBUS_INTERFACE)
def ChangeUpgradePolicy(self):
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" ChangeUpgradePolicy")
#限速修改信号
@dbus.service.signal(UPDATER_DBUS_INTERFACE,signature='ss')
def ButtonStatusChange(self, signal_types = '', value=''):
logging.info(COLORLOG_PREFIX + "Emitting"+ COLORLOG_SUFFIX + " ButtonStatusChange signal_types = %s, value = %s.",signal_types, value)
# signal属性发生改变
@dbus.service.signal(dbus_interface=UPDATER_DBUS_INTERFACE,
signature="ss")
def PropertyChanged(self, property, value):
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" PropertyChanged: ( %s, %s )" % (property, value))
# signal属性发生改变
@dbus.service.signal(dbus_interface=UPDATER_DBUS_INTERFACE,
signature="ss")
def StrategyChanged(self, property, value):
logging.info(COLORLOG_PREFIX + "Emitting" + COLORLOG_SUFFIX +" StrategyChanged: ( %s, %s )" % (property, value))
# WRITABLE_PROPERTIES = ()
# # pylint: disable-msg=C0103,C0322
# @dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE,
# signature="sa{sv}as")
# def PropertiesChanged(self, interface, changed_properties,
# invalidated_properties):
# """The signal gets emitted if a property of the object's
# interfaces changed.
# :param property: The name of the interface.
# :param changed_properties: A dictrionary of changed
# property/value pairs
# :param invalidated_properties: An array of property names which
# changed but the value isn't conveyed.
# :type interface: s
# :type changed_properties: a{sv}
# :type invalidated_properties: as
# """
# logging.info("Emitting PropertiesChanged: %s, %s, %s" %
# (interface, changed_properties, invalidated_properties))
# # pylint: disable-msg=C0103,C0322
# @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
# in_signature='', out_signature='s',
# path_keyword='object_path',
# connection_keyword='connection')
# def Introspect(self, object_path, connection):
# # Inject the properties into the introspection xml data
# data = dbus.service.Object.Introspect(self, object_path, connection)
# xml = ElementTree.fromstring(data)
# for iface in xml.findall("interface"):
# props = self._get_properties(iface.attrib["name"])
# for key, value in props.items():
# attrib = {"name": key}
# if key in self.WRITABLE_PROPERTIES:
# attrib["access"] = "readwrite"
# else:
# attrib["access"] = "read"
# if isinstance(value, dbus.String):
# attrib["type"] = "s"
# elif isinstance(value, dbus.UInt32):
# attrib["type"] = "u"
# elif isinstance(value, dbus.Int32):
# attrib["type"] = "i"
# elif isinstance(value, dbus.UInt64):
# attrib["type"] = "t"
# elif isinstance(value, dbus.Int64):
# attrib["type"] = "x"
# elif isinstance(value, dbus.Boolean):
# attrib["type"] = "b"
# elif isinstance(value, dbus.Struct):
# attrib["type"] = "(%s)" % value.signature
# elif isinstance(value, dbus.Dictionary):
# attrib["type"] = "a{%s}" % value.signature
# elif isinstance(value, dbus.Array):
# attrib["type"] = "a%s" % value.signature
# else:
# raise Exception("Type %s of property %s isn't "
# "convertable" % (type(value), key))
# iface.append(ElementTree.Element("property", attrib))
# new_data = ElementTree.tostring(xml, encoding="UTF-8")
# return new_data
# # pylint: disable-msg=C0103,C0322
# @dbus.service.method(dbus.PROPERTIES_IFACE,
# in_signature="ssv", out_signature="",
# sender_keyword="sender")
# def Set(self, iface, name, value, sender):
# """Set a property.
# Only the user who intiaited the transaction is
# allowed to modify it.
# :param iface: The interface which provides the property.
# :param name: The name of the property which should be modified.
# :param value: The new value of the property.
# :type iface: s
# :type name: s
# :type value: v
# """
# logging.info("Set() was called: %s, %s" % (name, value))
# return self._set_property(iface, name, value, sender)
# # pylint: disable-msg=C0103,C0322
# @dbus.service.method(dbus.PROPERTIES_IFACE,
# in_signature="s", out_signature="a{sv}")
# def GetAll(self, iface):
# """Get all available properties of the given interface."""
# logging.info("GetAll() was called: %s" % iface)
# return self._get_properties(iface)
# # pylint: disable-msg=C0103,C0322
# @dbus.service.method(dbus.PROPERTIES_IFACE,
# in_signature="ss", out_signature="v")
# def Get(self, iface, property):
# """Return the value of the given property provided by the given
# interface.
# """
# logging.info("Get() was called: %s, %s" % (iface, property))
# return self._get_properties(iface)[property]

View File

@ -0,0 +1,342 @@
#!/usr/bin/env python
import gi
import os,subprocess
gi.require_version('OSTree', '1.0')
from gi.repository import GLib, Gio, OSTree,GObject
from SystemUpdater.Core.errors import *
from SystemUpdater.Core.enums import *
from gettext import gettext as _
RETRY_LIMIT = 3
ORIGIN_REMOTE_NAME = "openkylin"
class BackendBaseOstree(GObject.GObject):
__gsignals__ = {
"transaction-done": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_PYOBJECT,)),
"download-info": (GObject.SIGNAL_RUN_FIRST,
GObject.TYPE_NONE,
( GObject.TYPE_INT,
GObject.TYPE_INT,
GObject.TYPE_INT,
GObject.TYPE_INT
))
}
def __init__(self,parent,sysroot_path=None,osname=None):
GObject.GObject.__init__(self)
if sysroot_path:
sysroot_path = Gio.File.new_for_path(sysroot_path)
self.sysroot = OSTree.Sysroot.new(sysroot_path)
self.sysroot.set_mount_namespace_in_use()
self.sysroot.initialize()
self.sysroot.load()
booted_deployment = self.sysroot.get_booted_deployment()
if booted_deployment:
self.osname = booted_deployment.get_osname()
else:
self.osname = osname
self.merge_deployment = self.sysroot.get_merge_deployment(self.osname)
if not self.merge_deployment:
pass
self.origin = self.merge_deployment.get_origin()
self._async_progress = OSTree.AsyncProgress.new()
self._async_progress.connect("changed",self._on_async_progress)
def run(self,trans):
logging.info("Processing transaction")
GLib.idle_add(self._run_transaction_idle,trans)
def _run_transaction_idle(self,trans):
"""Run the transaction"""
try:
self._try_lock()
self._run_transaction(self.sysroot,trans)
except GLib.Error as error:
logging.error(str(error))
trans.exit = False
self._make_error(error,trans)
except UpdateBaseError as excep:
trans.exit = False
trans.error_code = excep.code
except Exception as excep:
logging.error(str(excep))
trans.exit = False
else:
trans.exit = True
finally:
self._try_unlock()
self._async_progress.finish()
logging.info("Finished transaction")
self._emit_transaction_done(trans)
return False
def _run_transaction(self,sysroot,trans):
"""This method needs to be implemented by the backends."""
pass
# raise errors.TransactionFailed(enums.ERROR_NOT_SUPPORTED)
def _try_lock(self):
ret,out_acquired = self.sysroot.try_lock()
if not out_acquired:
raise UpdateBaseError(ERROR_OTHER_PROGRESS_OCCUPATION)
def _try_unlock(self):
self.sysroot.unlock()
def _emit_transaction_done(self,trans):
"""Emit the transaction-done signal.
Keyword argument:
trans -- the finished transaction
"""
# logging.debug("Emitting transaction-done: %d",)
self.emit("transaction-done",trans)
def _on_async_progress(self,obj):
start_time = obj.get_uint64("start-time")
bytes_transferred = obj.get_uint64("bytes-transferred")
fetched = obj.get_uint("fetched")
requested = obj.get_uint("requested")
# 下载速度
elapsed_secs=(GLib.get_monotonic_time()-start_time)/GLib.USEC_PER_SEC
bytes_sec = int(bytes_transferred / elapsed_secs)
if requested <= 2:
return
self.emit("download-info",fetched,requested,bytes_transferred,bytes_sec)
def _make_error(self,error,trans):
if error.code in (0,37):
trans.error_code = ERROR_NETWORK_FAILED
#取消
elif error.code == 19:
trans.cancellable.reset()
trans.error_code = ERROR_CANCELLED
elif error.code == 1:
#Remote "kylin" not found (1)
trans.exit = True
trans.is_upgradable = False
class UpdateBackend(BackendBaseOstree):
def __init__(self,parent,sysroot_path=None,osname=None):
"""Initialize a new AptWorker instance."""
BackendBaseOstree.__init__(self,parent,sysroot_path,osname)
def _run_transaction(self, sysroot,trans):
ostree_repo = sysroot.repo()
from_revision = self.merge_deployment.get_csum()
refspec = self.origin.get_string("origin","refspec")
ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec)
if origin_remote == None:
origin_remote = ORIGIN_REMOTE_NAME
# raise UpdateBaseError(ERROR_ORIGIN_IS_NONE)
g_options = GLib.Variant("a{sv}",
{ "refs":GLib.Variant("as",[origin_ref]),\
"n-network-retries":GLib.Variant("u",RETRY_LIMIT),\
"flags":GLib.Variant("i",OSTree.RepoPullFlags.COMMIT_ONLY),\
# "timestamp-check":GLib.Variant("b",True),\
"depth":GLib.Variant("i",1)
})
logging.info("Start pull metadata refs:%s...",origin_ref)
ret = ostree_repo.pull_with_options(origin_remote,g_options,self._async_progress,trans.cancellable)
origin_refspec = origin_remote+":"+origin_ref
ret,new_revision = ostree_repo.resolve_rev(origin_refspec,True)
new_ref = origin_ref
ret,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,new_revision)
n_metadata = metadata[0]
# 检查是否当前分支已经终止,切换到新的分支
if OSTree.COMMIT_META_KEY_ENDOFLIFE_REBASE in n_metadata:
new_ref = n_metadata[OSTree.COMMIT_META_KEY_ENDOFLIFE_REBASE]
# 拉取新分支的元数据
g_options = GLib.Variant("a{sv}",
{ "refs":GLib.Variant("as",[new_ref]),\
"n-network-retries":GLib.Variant("u",RETRY_LIMIT),\
"flags":GLib.Variant("i",OSTree.RepoPullFlags.COMMIT_ONLY),\
# "timestamp-check":GLib.Variant("b",True),\
"depth":GLib.Variant("i",1)
})
logging.info("From origin ref:%s To new refs:%s...",origin_ref,new_ref)
logging.info("Start pull metadata refs:%s...",new_ref)
ret = ostree_repo.pull_with_options(origin_remote,g_options,self._async_progress,trans.cancellable)
origin_refspec = origin_remote+":"+new_ref
ret,new_revision = ostree_repo.resolve_rev(origin_refspec,True)
ret,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,new_revision)
# 判断是否有新的更新
trans.is_upgradable = from_revision != new_revision
trans.available_checksum = new_revision
trans.available_refs = new_ref
trans.available_metadata = metadata[0]
logging.info("Update available refs:%s from revision:%s to %s ",from_revision,new_revision,new_ref)
class DownloadBackend(BackendBaseOstree):
def __init__(self,parent,sysroot_path=None,osname=None):
"""Initialize a new AptWorker instance."""
BackendBaseOstree.__init__(self,parent,sysroot_path,osname)
def _run_transaction(self, sysroot,trans):
ostree_repo = sysroot.repo()
refspec = self.origin.get_string("origin","refspec")
ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec)
if origin_remote == None:
origin_remote = ORIGIN_REMOTE_NAME
g_options = GLib.Variant("a{sv}",
{
"refs":GLib.Variant("as",[trans.available_refs]),\
"n-network-retries":GLib.Variant("u",RETRY_LIMIT),\
# "timestamp-check":GLib.Variant("b",True),\
# "low-speed-limit-bytes":GLib.Variant("u",10),\
# "low-speed-limit-seconds":GLib.Variant("u",20),\
"flags":GLib.Variant("i",OSTree.RepoPullFlags.NONE),\
"depth":GLib.Variant("i",1)
})
logging.info("start pull data from available_refs:%s...",trans.available_refs)
ostree_repo.pull_with_options(origin_remote,g_options,self._async_progress,trans.cancellable)
# 下载完成后检查下载的元数据的时间戳
# upgrader.check_timestamps(ostree_repo,from_revision,trans.available_checksum)
class DeployBackend(BackendBaseOstree):
def __init__(self,parent,sysroot_path=None,osname=None):
"""Initialize a new AptWorker instance."""
BackendBaseOstree.__init__(self,parent,sysroot_path,osname)
def _deployment_sanitycheck_true(self,rootfs_path):
try:
pid = os.fork()
if pid == 0:
os.chroot(rootfs_path)
os.chdir("/")
# 执行命令
result = subprocess.run(["/usr/bin/true"],capture_output=True, text=True)
os._exit(result.returncode)
else:
# 等待子进程结束
_, exit_code = os.waitpid(pid, 0)
logging.info("Command exited with status: %d",os.WEXITSTATUS(exit_code))
if os.WIFEXITED(exit_code) != 0:
logging.error("Command did not exit normally.")
except Exception as e:
logging.error(str(e))
def _run_transaction(self, sysroot,trans):
ostree_repo = sysroot.repo()
upgrader = OSTree.SysrootUpgrader.new(sysroot)
# 当前合并部署
refspec = self.origin.get_string("origin","refspec")
ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec)
#执行清理任何先前部分失败留下的数据。
ret = sysroot.prepare_cleanup(None)
# 复现旧部署orgin文件 以及修改refspec
origin_refspec = origin_remote+":"+trans.available_refs
origin_cp = upgrader.dup_origin()
origin_cp.set_string("origin","refspec",origin_refspec)
ret,new_revision = ostree_repo.resolve_rev(origin_refspec,True)
# 最终完成阶段在关机的时候
logging.info("start deploy available_refs:%s new_revision:%s...",trans.available_refs,new_revision)
ret,new_deployment = sysroot.stage_tree(None,new_revision,origin_cp,self.merge_deployment,None,None)
# 健全性检查
# rootfs_path = sysroot.get_deployment_dirpath(new_deployment)
# self._deployment_sanitycheck_true(rootfs_path)
ret,metadata = ostree_repo.load_variant(OSTree.ObjectType.COMMIT,new_revision)
trans.available_checksum = new_revision
trans.available_metadata = metadata[0]
def _start_plymouth_splash(self):
try:
logging.info("Running plymouth --splash")
if os.path.exists("/sbin/plymouthd"):
subprocess.run(["/sbin/plymouthd", "--mode=boot","--attach-to-session"],timeout=1)
if os.path.exists("/bin/plymouth"):
subprocess.Popen(["/bin/plymouth", "show-splash","--wait"])
subprocess.call(["/bin/plymouth","system-update","--progress=0"])
except Exception as exc:
logging.error(exc)
def _check_plymouth_state(self):
try:
args = ["/bin/plymouth","--ping"]
p = subprocess.run(args,timeout=2,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
if p.returncode == 0:
logging.info("Check: Plymouthd state start to success...")
else:
logging.info("Check: Plymouthd Failed to Boot and Restart boot up again...")
self._start_plymouth_splash()
except Exception as exc:
logging.error(exc)
def _message_to_plymouth(self,message):
subprocess.call(["/bin/plymouth", "message", "--text", message])
def _progress_to_plymouth(self,progress):
tmp = ("--progress=%d"%progress)
run_cmd = ["/bin/plymouth","system-update",tmp]
subprocess.call(run_cmd)
class RollbackBackend(BackendBaseOstree):
def __init__(self,parent,sysroot_path=None,osname=None):
"""Initialize a new AptWorker instance."""
BackendBaseOstree.__init__(self,parent,sysroot_path,osname)
def _run_transaction(self, sysroot,trans):
new_deployments = []
out_pending,out_rollback = sysroot.query_deployments_for(self.osname)
if out_rollback == None:
raise UpdateBaseError(ERROR_NOT_ROLLBAK_DEPLOYMENT)
rollback_csum = out_rollback.get_csum()
old_deployments = sysroot.get_deployments()
new_deployments.append(out_rollback)
# 部署的列表
for deploys in old_deployments:
if out_rollback.equal(deploys) != True:
new_deployments.append(deploys)
logging.info("start reollback checksum:%s...",rollback_csum)
ret = sysroot.write_deployments(new_deployments,None)
refspec = new_deployments.get_string("origin","refspec")
ret,origin_remote,origin_ref = OSTree.parse_refspec(refspec)
trans.available_refs = origin_ref

View File

@ -0,0 +1 @@
Dir::Bin::Methods::ftp "ftp";

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<vendor>Kylin System Updater</vendor>
<vendor_url>www.kylinos.cn</vendor_url>
<icon_name>kylin-system-updater</icon_name>
<!--Kylin Installer Config-->
<action id="cn.kylin.installer.action">
<_description>
Configuration items added for Kirin Installer
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
<action id="cn.kylin.uninstaller.action">
<_description>
Configuration items added for Kirin Uninstaller
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
<!--Kylin System Updater Config-->
<action id="cn.kylinos.KylinSystemUpdater.action">
<_description>
Configuration items added for Kirin Installer
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
<!--Kylin Software Center Config-->
<action id="cn.kylin.software.center.action">
<_description>
Configuration items added for Kylin Software Center
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
<!--Kylin Installer Config-->
<action id="cn.kylin.installer.self.action">
<_description>
Configuration items added for Kirin Installer
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_self_keep</allow_any>
<allow_inactive>auth_self_keep</allow_inactive>
<allow_active>auth_self_keep</allow_active>
</defaults>
</action>
<action id="cn.kylin.uninstaller.self.action">
<_description>
Configuration items added for Kirin Uninstaller
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_self_keep</allow_any>
<allow_inactive>auth_self_keep</allow_inactive>
<allow_active>auth_self_keep</allow_active>
</defaults>
</action>
<!--Kylin System Updater Config-->
<action id="cn.kylinos.KylinSystemUpdater.self.action">
<_description>
Configuration items added for Kirin Installer
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_self_keep</allow_any>
<allow_inactive>auth_self_keep</allow_inactive>
<allow_active>auth_self_keep</allow_active>
</defaults>
</action>
<!--Kylin Software Center Config-->
<action id="cn.kylin.software.center.self.action">
<_description>
Configuration items added for Kylin Software Center
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_self_keep</allow_any>
<allow_inactive>auth_self_keep</allow_inactive>
<allow_active>auth_self_keep</allow_active>
</defaults>
</action>
</policyconfig>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<vendor>Kylin System Updater Config Manager</vendor>
<vendor_url>www.kylinos.cn</vendor_url>
<icon_name>kylin-upgrade-strategies</icon_name>
<action id="com.kylin.UpgradeStrategies.action">
<_description>
system level settings
</_description>
<_message>
To Change the settings, you need to authenticate.
</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root can own the service -->
<policy user="root">
<allow own="com.kylin.UpgradeStrategies"/>
<allow send_interface="com.kylin.UpgradeStrategies.interface"/>
</policy>
<!-- Allow anyone to invoke methods on the interfaces -->
<policy context="default">
<allow send_destination="com.kylin.UpgradeStrategies"
send_interface="com.kylin.UpgradeStrategies.interface"/>
<allow send_destination="com.kylin.UpgradeStrategies"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="com.kylin.UpgradeStrategies"
send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>

View File

@ -0,0 +1,4 @@
[D-BUS Service]
Name=com.kylin.UpgradeStrategies
Exec=/usr/share/kylin-system-updater/kylin-upgrade-strategies
User=root

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root can own the service -->
<policy user="root">
<allow own="com.kylin.systemupgrade"/>
<allow send_interface="com.kylin.systemupgrade.interface"/>
</policy>
<!-- Allow anyone to invoke methods on the interfaces -->
<policy context="default">
<allow send_destination="com.kylin.systemupgrade"
send_interface="com.kylin.systemupgrade.interface"/>
<allow send_destination="com.kylin.systemupgrade"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="com.kylin.systemupgrade"
send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>

View File

@ -0,0 +1,14 @@
[whitelist]
key1 = /usr/bin/kylin-background-upgrade
key2 = /usr/bin/ukui-control-center
key3 = /usr/bin/kylin-installer
key4 = /usr/bin/kylin-uninstaller
key5 = /usr/bin/kylin-software-properties-service
key6 = /usr/bin/kylin-source-update
key7 = /usr/bin/kylin-source-manager
key8 = /usr/bin/kylin-unattended-upgrade
key9 = /usr/bin/kylin-software-center
key10 = /usr/bin/kylin-printer
key11 = /usr/bin/kylin-printer-applet
key12 = /usr/bin/hedron-client
key13 = /usr/bin/kylin-software-center-plugin-synchrodata

View File

@ -0,0 +1,25 @@
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Entries in this file show the compile time defaults.
# You can change settings by editing this file.
# Defaults can be restored by simply deleting this file.
#
# See systemd-sleep.conf(5) for details
[Sleep]
AllowSuspend=no
AllowHibernation=no
#AllowSuspendThenHibernate=yes
#AllowHybridSleep=yes
#SuspendMode=
#SuspendState=mem standby freeze
#HibernateMode=platform shutdown
#HibernateState=disk
#HybridSleepMode=suspend platform shutdown
#HybridSleepState=disk
#HibernateDelaySec=180min

View File

@ -0,0 +1,26 @@
#!/bin/sh
# a) it breaks if its not available
# b) the string we have here does not need it (because it has no vars)
eval_gettext() {
if [ -x /usr/bin/gettext ]; then
echo $(gettext "$1")
else
echo "$1"
fi
}
export TEXTDOMAIN=update-notifier
export TEXTDOMAINDIR=/usr/share/locale
case "$DPKG_MAINTSCRIPT_PACKAGE::$DPKG_MAINTSCRIPT_NAME" in
linux-image-extra*::postrm)
exit 0;;
esac
if [ "$0" = "/etc/kernel/postinst.d/update-notifier" ]; then
DPKG_MAINTSCRIPT_PACKAGE=linux-base
fi
# Wake the applet up
echo "*** $(eval_gettext "System logout required") ***" > /var/run/logout-required
echo "$DPKG_MAINTSCRIPT_PACKAGE" >> /var/run/logout-required.pkgs

View File

@ -0,0 +1,26 @@
#!/bin/sh
# a) it breaks if its not available
# b) the string we have here does not need it (because it has no vars)
eval_gettext() {
if [ -x /usr/bin/gettext ]; then
echo $(gettext "$1")
else
echo "$1"
fi
}
export TEXTDOMAIN=update-notifier
export TEXTDOMAINDIR=/usr/share/locale
case "$DPKG_MAINTSCRIPT_PACKAGE::$DPKG_MAINTSCRIPT_NAME" in
linux-image-extra*::postrm)
exit 0;;
esac
if [ "$0" = "/etc/kernel/postinst.d/update-notifier" ]; then
DPKG_MAINTSCRIPT_PACKAGE=linux-base
fi
# Wake the applet up
echo "*** $(eval_gettext "System restart required") ***" > /var/run/reboot-required
echo "$DPKG_MAINTSCRIPT_PACKAGE" >> /var/run/reboot-required.pkgs

View File

@ -0,0 +1,10 @@
/var/log/kylin-system-updater/kylin-system-updater.log.1
{
weekly
missingok
rotate 3
compress
notifempty
maxsize 10M
copytruncate
}

Binary file not shown.

View File

@ -0,0 +1,13 @@
[Unit]
Description=kylin-system-updater dbus daemon
StartLimitIntervalSec=0
[Service]
Type=dbus
Restart=always
RestartSec=3
BusName=com.kylin.systemupgrade
ExecStart=/usr/share/kylin-system-updater/kylin-system-updater
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,3 @@
[SYSTEM]
os_version =
update_version =

View File

@ -0,0 +1,12 @@
[Unit]
Description=Do something before lightdm started.
Conflicts=getty@tty7.service plymouth-quit.service
Before=lightdm.service
After=kylin-system-updater.service systemd-user-sessions.service getty@tty7.service plymouth-quit.service
[Service]
Type=oneshot
ExecStart=/usr/share/kylin-system-updater/kylin-upgrade-poweroff
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,18 @@
#此配置文件内的所有配置项,在重新安装时会被替换掉
[AutoUpgradeConfig]
upgradeInterval = 7
downloadRandom = 180
[TestsConfig]
check_device_power = True
force_dist_upgrade = False
[ShutdownInstall]
# 包含的类型: "success":安装成功 "failed":安装失败 "none":不弹窗,默认状态
finished_status = none
[main debian archive]
Origin=Debian
Label=Debian
delta_uri=http://10.41.116.28

View File

@ -0,0 +1,29 @@
[SystemStatus]
abnormal_reboot = False
backend_restart = False
frontend_restart = False
source_restart = False
upload_upgrade_log = True
upload_installer_log = False
[InstallMode]
shutdown_install = False
manual_install = False
auto_install = False
#目前仅静默更新使用 安装状态存在在检查更新阶段被刷新的情况 在版本号发生变化时切换
[LastUpdateInfo]
finished_status = success
update_version =
update_content =
# 保存上次安装的状态:
#1、在升级完成时刷新成功或者失败
#2、在检查更新阶段 若推送内容不为空 检查不需要更新 判断为系统为最新 也刷新状态
# last_install 字符串类型,"success":上次更新成功 非success时都认为失败例如 "#0202" 错误码
[statusForProperties]
last_install = success
uuid =
# 前端使用配置文件
[UpdateFrontendConf]
backup_exist = False

View File

@ -0,0 +1,44 @@
[autoUpgradePolicy]
#自动更新的开关
autoUpgradeState = off
#预下载开关
preDownload = off
# 预下载的时间为时间段 例如10:00-11:00
preDownloadTime = 09:00-10:00
#添加检查更新的周期 以天为单位
updateDays = 30
# timing 为定时下载 manaual手动下载
downloadMode = timing
# 下载的时间为时间段 例如10:00-11:00
downloadTime = 20:00-08:00
#安装存在定时timing 手动:manual 关机安装bshutdown
installMode = bshutdown
#安装也为时间段 例如00:00
installTime = 08:00-20:00
#立即更新随机波动范围(单位:分钟)
randomRange = 60
#是否开启自动重启 以及自动重启时间可以调节
automaticReboot = off
#自动重启时间的调节 now为立即重启 重启时间调节 例如00:00
automaticRebootTime = now
#更新前是否进行备份
backupbeforeinstall = on
#下发的策略管控
[updateStrategiesManager]
# 策略的开关 False:关 True
strategiesState = False
# 安装策略 default默认模式 runtime运行时安装 pre-poweroff关机安装
installType = default

View File

@ -0,0 +1,4 @@
[TimeStamp]
predownload = 2023-7-1 00:00:00
download = 2023-7-1 00:00:00
install = 2023-7-1 00:00:00

View File

@ -0,0 +1,657 @@
## DBUS接口
[TOC]
### 对应版本信息
| 软件包 | 目前版本 | 备注 |
| :------------------: | :-----------------------------: | :--: |
| kylin-system-updater | kylin-system-updater 1.4.16kord | |
| aptdaemon | 1.1.1+bzr982-0kylin32.3 | |
| | | |
### 描述
实现系统升级以python apt库和aptdeamon的形式
### Dbus接口信息
| 名称 | 含义 |
| -------------- | --------------------------------- |
| BUS类型 | SYSTEM BUS |
| DBUS名称 | com.kylin.systemupgrade |
| OBJECT路径 | /com/kylin/systemupgrade |
| INTERFACES名称 | com.kylin.systemupgrade.interface |
| 属性名称 | org.freedesktop.DBus.Properties |
### Apt-p2p配置项设置
### Dbus接口信息
| 名称 | 含义 |
| -------------- | --------------------------------- |
| BUS类型 | SYSTEM BUS |
| DBUS名称 | com.kylin.systemupgrade |
| OBJECT路径 | /com/kylin/systemupgrade |
| INTERFACES名称 | com.kylin.systemupgrade.interface |
| 属性名称 | org.freedesktop.DBus.Properties |
#### Get
- `简介:`获取属性的值
- `入参:` `s`iface要设置的属性的接口 `s`property要设置的属性名称
- `出参:` `Variant`变量
- `示例:`
```
#获取p2p的配置
Get(com.kylin.systemupgrade.interface,P2pBootstrap)
```
#### Set
- `简介:`设置属性的值
- `入参:` `s`iiface要设置的属性的接口 `s`iproperty要设置的属性名称 `Variant`value要设置的值
- `出参:`
- `示例:`
```
#设置p2p的配置
set("com.kylin.systemupgrade.interface","P2pBootstrap"GLib.Variant('s', "test"))
```
### 方法列表
| Method Name | Input Args | Output Args | means | 异步任务 |
| ------------------ | ---------- | ----------- | --------------------------------- | ------------------ |
| UpdateDetect | 无 | b | 更新cache产生组升级列表JSON文件 | |
| DistUpgradeAll | b | b | 全部升级 | |
| DistUpgradePartial | b,as | b | 部分升级 | |
| DistUpgradeSystem | b | b | 全盘升级 | |
| CancelDownload | 无 | b | 取消升级 | |
| InsertInstallState | ss | b | 向display表插入数据 | |
| GtDownloadspeedLimitValue | 无 | b | 获取当前限速 | |
| SetDownloadspeedMax | sb | b | 设置限速 | |
| GetBackendStatus | 无 | i | 控制获取后端状态 | |
| UnattendedUpgradeValue | ss | bs | 获取是否允许关机前更新 | |
| PurgePackages | as | b | 卸载软件包 | |
| InstalldebFile | ssbb | b | 安装本地deb包 | |
| DataBackendCollect | ss | b | | |
| CheckRebootRequired | s | b | 检查是否需要重启的方法,以及弹窗提示 | |
### Method分析
#### UpdateDetect
- `简介:`更新cache对象完成从之后拿到系统中所有可升级的包再经过源过滤、白名单等等的过滤最后输出当前`可升级的包以及分组JSON配置 输出目录: /var/lib/kylin-system-updater
- `入参:`
- `出参:`True or False 注意:不通过返回值来判断有没有执行成功 通过下面的信号
- `对应信号:`
- `UpdateDetectStatusChanged:` 更新的进度信息和状态信息
- `UpdateDetectFinished:`更新的完成的信号
#### DistUpgradePartial
- `简介:` 升级部分软件包或者分组
- `入参:` `b:` False模式只进行获取升级列表以及计算修复依赖关系以及计算是否存在删除的包`True模式:`直接进行安装的操作 注意必须选使用False模式获取升级列表以及计算依赖关系再进行True模式
`as:` 输入需要升级或者安装的分组 例如 升级系统组:["kylin-update-desktop-system"]
- `出参:`True or False 注意:不通过返回值来判断有没有执行成功 通过下面的信号
- `对应信号:`
- `UpdateDependResloveStatus:` 升级计算依赖修复反馈信号
- `UpdateDloadAndInstStaChanged:`升级安装过程的进度信号以及状态
- `UpdateInstallFinished:` 升级安装完成的信号
#### DistUpgradeAll
- `简介:`升级全部可升级的分组
- `入参:` `b:` False模式只进行获取升级列表以及计算修复依赖关系以及计算是否存在删除的包`True模式:`直接进行安装的操作 注意必须选使用False模式获取升级列表以及计算依赖关系再进行True模式
- `出参:`True or False 注意:不通过返回值来判断有没有执行成功 通过下面的信号
- `对应信号:`
- `UpdateDependResloveStatus:` 升级计算依赖修复反馈信号
- `UpdateDloadAndInstStaChanged:`升级安装过程的进度信号以及状态
- `UpdateInstallFinished:` 升级安装完成的信号
#### UpdateDownloadInfo
- `介绍:` 发送下载包信息信号
- `出参`: `i:`当前正在下载的项,`i:`所有下载的项,`i:`当前下载的字节,`i:`总的需要下载的字节,`i:`下载速度
- `示例:`
```sh
current_items = 1, total_items = 1, currenty_bytes = 45 kB, total_bytes = 45 kB, current_cps = 0 kB/s
```
#### GetBackendStatus
- `介绍:` 获取后端的状态,现在正在处理那些任务
- `入参:` `s:`当前用户的语言变量 例如传入语言环境变量`LANG` 的值`zh_CN.UTF-8` 就会将升级的语言切换为中文 同理其他也能相应设置
- `出参`: `i:`当前任务ID整型数字
- `状态示例列表:`
```python
ACTION_DEFUALT_STATUS = -1 #默认状态空闲状态
ACTION_UPDATE = 0 #处于更新cache状态
ACTION_INSTALL = 1 #包括部分升级、全部升级、q
ACTION_INSTALL_DEB = 2 #处于安装deb的状态
ACTION_CHECK_RESOLVER = 3 #处于计算依赖过程
ACTION_DOWNLOADONLY = 4 #单独下载软件包过程
ACTION_FIX_BROKEN = 5 #修复依赖的过程
ACTION_REMOVE_PACKAGES = 6 #卸载包的状态中
```
#### UnattendedUpgradeValue
- `介绍:` 设置或获取是否允许关机前更新
- `入参`: `s:`operation("get"/"set")`s:`value将要设置的值
- `示例:`
```sh
operation = "set", value = "false"
```
#### InstalldebFile
- `简介:`安装本地deb包
- `入参:` `source:(string)` 安装来源,`path:(string)`本地deb包绝对路径`_check_local_dep:(bool)`出现依赖问题时是否查询本路径下是否存在满足的包,`_auto_satisfy:(bool)`出现依赖问题时是否通过网络下载并安装依赖包
- `出参:`True or False
- `对应信号:`
- `InstalldebStatusChanged`:安装过程的进度信号以及状态
- `InstalldebFinished`:安装完成的信号
- `示例:`
```sh
source = 'kylin-installer', path = '/home/kylin/kylin-video_3.1.0-94.5_amd64.deb', _check_local_dep = 0, _auto_satisfy = 1
```
#### PurgePackages
- `简介:`卸载系统中的软件包
- `入参:` `as:` 需要卸载的包列表 `s:`当前用户的用户名 例如:kylin用户就传入`kylin`字符串
- `出参:`True or False 出参值不做任何参考意义 `注意:`其中False的时候表示后端正在处理其他任务会报错其中完成信号也会反馈结果故不采用方法的返回值来判断错误类型
- `对应信号:`
- `PurgePkgStatusChanged:`卸载过程的进度信号以及状态
- `PurgePackagesFinished:` 卸载完成的信号
- `示例:`
```sh
_purge_list = ['kylin-video','tree'] cur_user = 'kylin'
```
#### DataBackendCollect
- `介绍:` 后端数据采集
- `入参`: `messageType: ` 消息埋点(string) `uploadMessage: ` 上传数据(json格式字符串),必须包含的字段: "packageName"
- `示例:`
```sh
messageType = "UpdateInfos", uploadMessage = "{\"packageName\":\"kylin-system-updater\",\"source\":\"kylin-system-updater\",\"status\":\"True\",\"errorCode\":\"\",\"versionOld\":\"1.2.13.2kord\",\"versionNew\":\"1.2.17.1kord\"}"
messageType: 消息埋点(string) "UpdateInfos"、"InstallInfos"、"RemoveInfos"...
source: 安装来源 "kylin-installer"、"unattented-upgrade"、"kylin-software-center"、"kylin-system-updater"...
status: 安装或卸载状态 "True"/"False"
errorCode: 错误信息 ""/"..."
versionOld: 旧版本号 "1.2.13.2kord"
versionNew: 新版本号 "1.2.17.1kord"
```
#### CheckRebootRequired
- `介绍:` 检查是否需要重启的方法,以及弹窗提示
- `入参`: `s:`标识那个应用调的此接口如自动更新可填入字符串“autoyupgrade”
- `出参:`True or False 执行的结果
#### SetConfigValue
- `简介:`设置配置文件的值 配置文件的目录`/var/lib/kylin-system-updater/system-updater.conf`
- `入参:` `section`, `option`,` value` 不管布尔、列表的数据类型都转化成字符串类型来写入
- 例如传入"InstallModel","shutdown_install","False"
```
[InstallMode]
shutdown_install = True
manual_install = False
auto_install = False
pkgs_install =
pkgs_upgrade =
pkgs_remove =
```
- `出参:`True :设置值成功 False: 设置失败
#### GetConfigValue
- `简介:`获取配置文件的值 配置文件的目录`/var/lib/kylin-system-updater/system-updater.conf`
- `入参:` `section`,` option` 例如传入"InstallModel","shutdown_install"
- `出参:` `bool:`True :获取值成功 False: 获取失败 `Value:`值都以字符串类型来返回
```
[InstallMode]
shutdown_install = True
manual_install = False
auto_install = False
pkgs_install =
pkgs_upgrade =
pkgs_remove =
```
#### CheckInstallRequired
- `简介:`检查当前系统是否需要关机安装或者重启安装
- `入参:`
- `出参:` `int:` 类型返回值 表示当前系统是否需要安装 返回值数值含义如下。简要为0时不需要安装不为零时需要进行提示安装
- `1` 手动更新请求当前系统在关机时进行安装软件包
- `2` 自动更新请求当前系统在关机时进行安装软件包
- `0` 当前系统不需要进行安装
#### FixBrokenDepends
- `简介:` 修复当前的系统Apt环境收到`UpdateFixBrokenStatus`后进行调用类似于调用apt install -f 来进行修复
- `入参:`
- `出参:` True or False 执行的结果 无实际意义
- `对应信号:`
- `FixBrokenStatusChanged:` 修复依赖的状态信号 可不使用
#### MountSquashfsSource
- `简介:` 挂载离线源squashfs
- `入参:` `s:` 挂载文件的位置
- `出参:` `b:` True or False 执行的结果,`s:` 字符类型错误信息描述
### Signal列表
| Signal Name | Output Args | means |
| ---------------------------- | ----------- | ------------------------ |
| UpdateDetectStatusChanged | i,s | 更新进度信息以及状态信息 |
| UpdateDetectFinished | b,as,s,s | 更新完成信号 |
| UpdateDloadAndInstStaChanged | as,i,s,s | 升级的进度信号以及状态 |
| UpdateInstallFinished | b,as,s,s | 升级完成的信号 |
| UpdateDownloadInfo | i,i,u,u,i | 发送下载包信息信号 |
| UpdateDependResloveStatus | b,b,s | 更新依赖修复信息 |
| DistupgradeDependResloveStatus | b,s | 更新全盘修复信息 |
| Cancelable | b | 是否可取消 |
| UpdateSqlitSingle | | |
| FixBrokenStatusChanged | iiisss | 修复依赖的状态信号 |
| PurgePackagesFinished | iss | 卸载完成信号 |
| PurgePkgStatusChanged | bss | 卸载进度信息以及状态信息 |
| RebootLogoutRequired | s | 请求重启或者注销的信号 |
| UpdateInstallFinished | b,as,s,s | 下载完成的信号 |
### Signal分析
#### UpdateDetectStatusChanged
- `介绍:`更新的进度信息和状态信息
- `出参`:`i:`更新的进度信息从0-100%`s:`更新的状态信息,
- `示例:`
```sh
progress = 9 , status = 正在解决依赖关系
progress = 92 , status = 正在载入软件列表
progress = 92 , status = 完成
```
#### UpdateDetectFinished
- `介绍:`更新的完成的信号
- `出参`: `b:`更新是否成功,`as:`可升级的组列表,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
success = True , upgrade_group = ['kylin-update-desktop-system', 'tree', 'texinfo', 'kylin-update-manager', 'dnsmasq-base', 'vino', 'dpkg-dev', 'ghostscript', 'atril', 'wpasupplicant', 'eom', 'eom-common', 'fcitx-bin', 'fcitx-data', 'fcitx-frontend-gtk2', 'wps-office'], error_string = , error_desc =
error_string = 获取更新软件推送失败,请稍后再进行尝试更新 , error_desc = 推送服务器连接异常
```
#### UpdateDependResloveStatus
- `介绍:`升级计算依赖修复反馈信号
- `出参`: `b:`修复依赖关系是否成功,`b:`是否存在升级需要卸载的包,`as:`卸载的包列表,`as:`卸载的包的描述信息,`as:`卸载此包的原因 升级安装那些包导致的,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
UpdateDependResloveStatus:resolver_status = True , remove_status = True , remove_pkgs = ['kylin-burner-i18n'],pkg_raw_description = ['Sophisticated CD/DVD burning application - localizations files'] ,delete_desc = ['kylin-burner-i18n 将要被删除,由于升级 kylin-burner'],error_string = , error_desc =
```
#### UpdateFixBrokenStatus
- `介绍:`更新检查过程中是否需要修复系统Apt的环境提示是否存在卸载软件包的情况
- `出参`: `b:`是否能修复系统环境,`b:`是否存在修复需要卸载的包,`as:`卸载的包列表,`as:`卸载的包的描述信息,`as:`卸载此包的原因,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
UpdateDependResloveStatus:resolver_status = True , remove_status = True , remove_pkgs = ['kylin-burner-i18n'],pkg_raw_description = ['Sophisticated CD/DVD burning application - localizations files'] ,delete_desc = ['kylin-burner-i18n 将要被删除,由于升级 kylin-burner'],error_string = , error_desc =
```
#### UpdateDloadAndInstStaChanged
- `介绍:` 升级安装过程的进度信号以及状态
- `出参`: `as:`当前那些组在升级安装 `i:`更新的进度信息从0-100%`s:`更新的状态信息 `s:`下载的细节信息
- ` 示例:`
```sh
groups_list = ['kylin-update-desktop-system'] progress = 15 , status = 下载中 current_details = 下载中 tree
```
#### UpdateInstallFinished
- `介绍:` 升级安装完成的信号
- `出参`: `b:`升级是否成功,`as:`可升级的组列表,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
pdateInstallFinished success = True , upgrade_group = ['tree'], error_string = 系统升级完成。 , error_desc =
```
#### UpdateDownloadFinished
- `介绍:` 下载完成的信号
- `出参`: `b:`下载是否成功,`as:`可升级的组列表,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
pdateInstallFinished success = True , upgrade_group = ['tree'], error_string = 系统升级完成。 , error_desc =
```
#### FixBrokenStatusChanged
- `介绍:` 修复依赖过程中的状态反馈信号
- `出参`: `i:`修复依赖是否完成 `i:`修复依赖过程是否成功`注意:只有修复完成时,修复依赖是否成功才有意义``i:`修复的进度信息 `s:`修复的状态信息 `s:`产生错误的结果,`s:`产生错误的原因
- ` 示例:`
```sh
emit FixBrokenStatusChanged finished = False , success = True,progress = 66 , status = 正在应用更改,error_string = , error_desc =
```
-
#### PurgePkgStatusChanged
- `介绍:`卸载的进度信息和状态信息以及状态的细节信息
- `出参`:`i:`卸载的进度信息从0-100%`s:`卸载的状态信息,`s:`卸载的细节信息
- `示例:`
```sh
INFO:emit PurgePkgStatusChanged progress = 63 , status = 正在应用更改 ,current_details = 正在准备删除 kylin-video
INFO:emit PurgePkgStatusChanged progress = 76 , status = 正在应用更改 ,current_details = 正在卸载 kylin-video
```
#### PurgePackagesFinished
- `介绍:`卸载的完成的信号
- `出参`: `b:`卸载是否成功,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
#卸载完成
PurgePackagesFinished success = True , error_string = 卸载完成。 , error_desc =
#卸载失败
PurgePackagesFinished success = False , error_string = 软件包不存在 , error_desc = 检查包名的拼写是否正确,以及是否启用了相应的仓库。
PurgePackagesFinished success = False , error_string = 软件包没有安装 , error_desc = 不需要进行卸载。
#卸载失败 由于正在处理其他任务也同样会报错
PurgePackagesFinished success = False , error_string = 其他任务正在更新升级中,请稍后再卸载。 , error_desc =
```
#### InstalldebStatusChanged
- `介绍:`安装的进度信息和状态信息以及状态的细节信息
- `出参`:`i:`安装的进度信息从0-100%`s:`安装的状态信息,`s:`安装的细节信息
- `示例:`
```sh
InstalldebStatusChanged progress = 57 , status = 正在应用更改 ,current_details = 正在配置 python3-bandit
InstalldebStatusChanged progress = 57 , status = 正在应用更改 ,current_details = python3-bandit 已安装
```
#### InstalldebFinished
- `介绍:`安装的完成的信号
- `出参`: `b:`安装是否成功,`s:`产生错误的结果,`s:`产生错误的原因
- `示例:`
```sh
#安装完成
InstalldebFinished success = True , error_string = , error_desc =
#安装失败 缺少依赖的
InstalldebFinished success = False , error_string = bandit dependency is not satisfied , error_desc = python3-bandit
#安装失败 选择从网络拉依赖 网络断开 报网络错误
InstalldebFinished success = False , error_string = 下载软件包文件失败 , error_desc = 检查您的网络连接。
```
#### RebootLogoutRequired
- `介绍:`请求重启和注销的信号
- `出参`: `s:` "reboot" 表示重启 "logout"表示注销
- `示例:`
```sh
Emitting RebootLogoutRequired required_status = reboot
```
#### InstallDetectStatus
- `介绍:`下载安装前的状态检查
- `出参`: `b:`检查出错时为`False`,没有错误`success``s:`产生错误的码
- 错误码示例:
```python
ERROR_NOT_DISK_SPACE = "error-not-disk-space"
```
- `示例:`
```sh
#表示出现磁盘已满的错误z
InstallDetectStatus success = False , error_code = "error-not-disk-space"
```
后端日志:`/var/log/kylin-system-updater/kylin-system-updater.log.1`
### 更新过程报错信息总结
| 错误信息 | 错误原因 | 解决办法 |
| -------------------- | ------------------------------------------------------------ | ---------------------------------------- |
| 下载软件仓库信息失败 | 源存在问题使用apt update检查若存在报错则更新管理器无问题 | 检查源是否可以使用 |
| 无法访问源管理服务器 | 源管理服务器存在问题 | 源管理服务器是否可用或者检查源服务器配置 |
| 软件索引已经损坏 | 当前系统中cache被破坏apt无法使用 | 终端检查错误原因进行解决 |
| 无法初始化软件包信息 | apt存在某些问题 | 具体错误原因查看日志相应解决 |
| 无法获取组配置软件包 | 源中不存在kylin-update-desktop-config | 将此包放入源仓库或者写配置文件不强制安装 |
| 无法读取推送升级列表 | 读取推送列表出现问题 | 检查important.list是否存在 |
| 获取软件推送失败 | 老版本文案同 无法访问源管理服务器解决 | |
### 安装过程报错信息总结
| 错误信息 | 错误原因 | 解决办法 |
| ------------------ | ------------------------------ | ---------------------------------- |
| 软件包操作失败 | 被升级的软件包有问题 | 检查后端log日志查看那个包存在问题 |
| 下载软件包文件失败 | 网络原因的或者这个软件包的仓库 | 检查网络以及源仓库 |
| 磁盘空间不足 | 磁盘的空间不足 | 查看日志那些目录空间不足 |
| 不能计算升级 | 无法计算依赖关系 | 检查日志那个包出现的问题,相应解决 |
| | | |

View File

@ -0,0 +1,75 @@
#!/usr/bin/python3
from SystemUpdater.UpdateManager import UpdateManager
from gettext import gettext as _
import logging
from optparse import OptionParser
import dbus
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
import signal
import os
import sys
import gettext
from SystemUpdater.Core.LogManager import get_logfile as logfile
gettext.bindtextdomain('kylin-system-updater', '/usr/share/locale')
gettext.textdomain('kylin-system-updater')
_ = gettext.gettext
#定义日志的格式
FORMAT = "%(asctime)s [%(levelname)s]: %(message)s"
FORMAT_DEBUG = '%(asctime)-15s %(levelname)s(%(filename)s:%(lineno)d):%(message)s'
def signal_handler_term(signal, frame):
# type: (int, object) -> None
logging.warning("SIGTERM received, will stop")
app.dbus_send.Quit(None)
if __name__ == "__main__":
# Begin parsing of options
parser = OptionParser()
parser.add_option ("-d", "--debug", action="store_true", default=False,
help=_("Show debug messages"))
parser.add_option("-r", "--replace",
default=False,
action="store_true", dest="replace",
help=_("Quit and replace an already running "
"daemon"))
parser.add_option("", "--sysroot", default=None,
action="store", type="string", dest="sysroot",
help=_("Import sysroot path in the given path"))
parser.add_option("", "--os", default=None,
action="store", type="string", dest="os",
help=_("Import os name in the given string"))
parser.add_option("-p", "--prohibit-shutdown",
default=False,
action="store_true", dest="prohibit_shutdown",
help=_("close auto shutdown"))
parser.add_option("--no-check-network",
default=True,
action="store_false", dest="check_network",
help=_("Quit and close check network"))
(options, args) = parser.parse_args()
if os.getuid() != 0:
print(_("You need to be root to run this application"))
sys.exit(1)
# ensure that we are not killed when the terminal goes away e.g. on
# shutdown
signal.signal(signal.SIGHUP, signal.SIG_IGN)
signal.signal(signal.SIGINT,signal_handler_term)
if options.debug:
logging.basicConfig(format=FORMAT,level=logging.INFO,datefmt='%m-%d,%H:%M:%S')
else:
logging.basicConfig(format=FORMAT,level=logging.INFO,datefmt='%m-%d,%H:%M:%S',filename = logfile(),filemode = 'a')
logging.info('kylin-system-updater starting ...')
app = UpdateManager(options)
app.run()

View File

@ -0,0 +1,81 @@
#!/usr/bin/python3
from SystemUpdater.UpgradeStrategies import UpgradeStrategies
from gettext import gettext as _
import logging
from optparse import OptionParser
import dbus
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
import signal
import os
import sys
import gettext
from SystemUpdater.Core.LogManager import upgrade_strategies_logfile as logfile
gettext.bindtextdomain('kylin-system-updater', '/usr/share/locale')
gettext.textdomain('kylin-system-updater')
_ = gettext.gettext
#定义日志的格式
FORMAT = "%(asctime)s [%(levelname)s]: %(message)s"
FORMAT_DEBUG = '%(asctime)-15s %(levelname)s(%(filename)s:%(lineno)d):%(message)s'
def signal_handler_term(signal, frame):
# type: (int, object) -> None
logging.warning("SIGTERM received, will stop")
app.dbusController.Quit(None)
if __name__ == "__main__":
# Begin parsing of options
parser = OptionParser()
parser.add_option ("-d", "--debug", action="store_true", default=False,
help=_("Show debug messages"))
parser.add_option("-r", "--replace",
default=False,
action="store_true", dest="replace",
help=_("Quit and replace an already running "
"daemon"))
(options, args) = parser.parse_args()
if os.getuid() != 0:
print(_("You need to be root to run this application"))
sys.exit(1)
# set debconf to NON_INTERACTIVE
os.environ["DEBIAN_FRONTEND"] = "noninteractive"
os.environ["TERM"] = "xterm"
os.environ["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
#当不存在语言变量时 默认显示中文
if not "LANGUAGE" in os.environ:
os.environ["LANGUAGE"] = "zh_CN.UTF-8"
#当不存在语言变量时 默认显示中文
if not "LANG" in os.environ:
os.environ["LANG"] = "zh_CN.UTF-8"
#做一些规范处理
if os.environ["LANGUAGE"] == "en":
os.environ["LANGUAGE"] = "en_US.UTF-8"
if os.environ["LANGUAGE"] == "zh_CN:en" or os.environ["LANGUAGE"] == "zh_CN:zh":
os.environ["LANGUAGE"] = "zh_CN.UTF-8"
# ensure that we are not killed when the terminal goes away e.g. on
# shutdown
signal.signal(signal.SIGHUP, signal.SIG_IGN)
signal.signal(signal.SIGINT,signal_handler_term)
if options.debug:
logging.basicConfig(format=FORMAT,level=logging.INFO,datefmt='%m-%d,%H:%M:%S')
else:
logging.basicConfig(format=FORMAT,level=logging.DEBUG,datefmt='%m-%d,%H:%M:%S',filename = logfile(),filemode = 'a')
logging.info('Updater Config Manager Daemon(LANGUAGE:%s LANG:%s) starting ...',os.environ["LANGUAGE"],os.environ["LANG"])
app = UpgradeStrategies(options)
app.run()

View File

@ -0,0 +1,4 @@
#!/bin/bash
set -e
/usr/bin/dbus-monitor --system "type=method_call,interface='org.freedesktop.login1.Manager',path='/org/freedesktop/login1',member='Reboot'" "type=method_call,interface='org.freedesktop.login1.Manager',path='/org/freedesktop/login1',member='PowerOff'" >>/tmp/monitor-dbus-upgrade.log 2>&1 &

View File

@ -0,0 +1,6 @@
2021-09-16 XueYi Luo <luoxueyi@kylinos.cn>
* zh_CN.po: Updated Simplified Chinese translation.
* zh_HK.po: Updated translation for HongKong,china.
* zh_TW.po: Updated translation for Taiwan,China.

View File

@ -0,0 +1,28 @@
top_srcdir=`pwd`/..
DOMAIN=kylin-system-updater
PO_FILES := $(wildcard *.po)
CONTACT=sebastian.heinlein@web.de
XGETTEXT_ARGS = --msgid-bugs-address=$(CONTACT)
XGETTEXT_ARGS += --keyword=unicode_gettext:2 --keyword=unicode_ngettext:2,3
XGETTEXT_ARGS += --language=python
all: update-po
# update the pot
$(DOMAIN).pot:
XGETTEXT_ARGS="$(XGETTEXT_ARGS)" intltool-update -p -g $(DOMAIN)
# merge the new stuff into the po files
merge-po: $(PO_FILES)
XGETTEXT_ARGS="$(XGETTEXT_ARGS)" intltool-update -r -g $(DOMAIN);
# create mo from the pos
%.mo : %.po
mkdir -p mo/$(subst .po,,$<)/LC_MESSAGES/
msgfmt $< -o mo/$(subst .po,,$<)/LC_MESSAGES/$(DOMAIN).mo
# dummy target
update-po: $(DOMAIN).pot merge-po $(patsubst %.po,%.mo,$(wildcard *.po))

View File

@ -0,0 +1,10 @@
[encoding: UTF-8]
SystemUpdater/backend/BackendOstreeNext.py
SystemUpdater/backend/__init__.py
SystemUpdater/UpdateManager.py
SystemUpdater/Core/MyCache.py
SystemUpdater/Core/UpdateList.py
SystemUpdater/Core/OriginFilter.py
SystemUpdater/Core/Database.py
SystemUpdater/UpdateManagerDbus.py
SystemUpdater/Core/enums.py

View File

View File

@ -0,0 +1,238 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: kylinos.cn\n"
"POT-Creation-Date: 2012-06-14 00:53+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Unable to access the source management server, please try again later"
msgstr "འདྲི་རྩད་ཀྱི་འབྱུང་ཁུངས་དོ་དམ་ཞབས་ཞུའི་ཡོ་བྱད་ལ་འཚམས་འདྲི་བྱེད་ཐབས་མེད་པས་རྗེས་སུ་ཡང་བསྐྱར"
msgid "Access to the source management server timed out, please try again later"
msgstr "འཚམས་འདྲིའི་འབྱུང་ཁུངས་ཀྱི་དོ་དམ་ཞབས་ཞུའི་ཡོ་བྱད་དུས་ལས་བརྒལ་བས་རྗེས་སུ་ཡང་བསྐྱར་ཚོད་ལྟ་བྱེད་རོགས"
msgid "Check if your network requires authentication?"
msgstr "ཁྱེད་ཀྱི་དྲ་རྒྱར་ཞིབ་བཤེར་བྱེད་དགོས་སམ།"
msgid "Check your source public key signature"
msgstr "ཁྱེད་ཀྱི་འབྱུང་ཁུངས་ལ་ཞིབ་བཤེར་བྱས་ནས་མིང་རྟགས་བཀོད།"
msgid "update important list occur Exception"
msgstr "ལག་ཏུ་བླངས་ནས་རྒྱུན་ལྡན་མིན་པ་བྱུང་ན་རྗེས་སུ་ཡང་བསྐྱར་ཚོད་ལྟ་ཞིག་གནང་རོགས།"
msgid "You need to be root to run this application"
msgstr "ཁྱོད་ལ་rootཡི་དབང་ཚད་ལྟར་འཁོར་སྐྱོད་བྱེད་དགོས།"
msgid "There is an exception in the update package."
msgstr "ཁུག་མ་གསར་སྒྱུར་བྱས་པ་རྒྱུན་ལྡན་མིན་པ་ཞིག་རེད"
msgid "You request the removal of a system-essential package."
msgstr "ཁྱེད་ཀྱིས་མ་ལག་ཅིག་གི་དགོས་ངེས་ཀྱི་མཉེན་ཆས་ཁུག་མ་ཞིག་བསུབ་དགོས་པའི་བླང་བྱ་"
msgid "This update cannot detect the upgradeable package."
msgstr "ཐེངས་འདིའི་གསར་སྒྱུར་གྱིས་རིམ་འཕར་ཐུབ་པའི་མཉེན་ཆས་ཁུག་མར་ཞིབ་དཔྱད་ཚད་ལེན་བྱེད་ཐབས་མེད།"
msgid "read important list failed"
msgstr "རིམ་པ་སྤོར་བའི་རེའུ་མིག་ཀློག་ཐབས་བྲལ་བས་རྗེས་སུ་ཡང་བསྐྱར་ཚོད་ལྟ་ཞིག་བྱེད་རོགས།"
msgid "Priority Upgrade Package being updated"
msgstr "ཁག་བགོས་ཀྱི་བཀོད་སྒྲིག་གསར་སྒྱུར་བྱེད་བཞིན་ཡོད།"
msgid "Exceptions of Priority Upgrade."
msgstr "དམིགས་སུ་བཀར་ནས་དམིགས་སུ་བཀར་ནས་རིམ་པ"
msgid "Due to the presence of deleted packages."
msgstr "བསུབ་པའི་མཉེན་ཆས་ཁུག་མ་ཡོད་པའི་རྐྱེན་གྱིས།"
msgid "The system update configuration file is read abnormally, please check if the system update configuration file format is correct."
msgstr "མ་ལག་གསར་སྒྱུར་བཀོད་སྒྲིག་ཡིག་ཆ་རྒྱུན་ལྡན་མིན་པས། ཞིབ་བཤེར་མ་ལག་གི་བཀོད་སྒྲིག་ཡིག་ཆའི་རྣམ་གཞག་ཡང་དག་ཡིན་མིན་ལ་ཞིབ་བཤེར་གནང་རོགས།"
msgid "Installation progress: "
msgstr "སྒྲིག་སྦྱོར་མྱུར་ཚད་གཤམ་གསལ། "
msgid "Installation successful, about to shut down"
msgstr "སྒྲིག་སྦྱོར་ལེགས་འགྲུབ་བྱུང་བ་དང་འགག་སྒོ་ལས་སྒྲོལ་གྲབས་ཡོད་"
msgid "Installation failed, about to shut down"
msgstr "སྒྲིག་སྦྱོར་བྱས་ནས་ཕམ་ཉེས་བྱུང་ན་སྒོ་རྒྱག་ལ་ཉེ།"
msgid "groups JSON ConfigPkgs install failed"
msgstr "ཁག་བགོས་ཀྱིས་ཡིག་ཆ་སྒྲིག་སྦྱོར་བྱེད་ཐབས་མེད།"
msgid "Installtion timeout to exit Due to inactivity"
msgstr "སྒྲིག་སྦྱོར་བྱེད་སྐབས་ཕྱིར་འཐེན་བྱས་པའི་རྐྱེན་གྱིས་རེད།"
msgid "Command execution error"
msgstr "ཚགས་པར་འདོན་ནོར་ཤོར་བའི་བཀའ་ཕབ་པ།"
msgid "Unsupported architecture"
msgstr "སྒྲོམ་གཞི་དང་མི་མཐུན་པ།"
msgid "Other Error"
msgstr "ནོར་འཁྲུལ་གཞན་དག་བཅས་ཡིན"
msgid "dependency is not satisfied"
msgstr "འབྲེལ་བ་མི་ཚིམ་པར་བརྟེན་དགོས།"
msgid "dependency is not satisfied will download"
msgstr "འབྲེལ་བ་མི་ཚིམ་པར་བརྟེན་དགོས།"
msgid "Disk space is insufficient, please clean the disk and then upgrade"
msgstr "ཁབ་ལེན་གྱི་བར་སྟོང་མི་འདང་བས་ཁབ་ལེན་སྡེར་མ་གཙང་བཤེར་བྱས་རྗེས་རིམ་སྤར་གསར་སྒྱུར་བྱེད་རོགས།"
msgid "Network anomaly, can't check for updates!"
msgstr "དྲ་རྒྱ་རྒྱུན་ལྡན་མིན་པས་ཞིབ་བཤེར་གསར་སྒྱུར་བྱེད་ཐབས་མེད།"
msgid "Check for update exceptions!"
msgstr "རྒྱུན་ལྡན་མིན་པར་ཞིབ་བཤེར་བྱེད་པ།"
msgid "Check for update exceptions,fix system APT environment error."
msgstr "ཞིབ་བཤེར་གསར་སྒྱུར་མ་ལག་APTཡི་ཁོར་ཡུག་ལ་ནོར་འཁྲུལ་བྱུང་བ་རེད།"
msgid "The system APT environment is abnormal, please check the system APT environment."
msgstr "མ་ལག་APTཡི་ཁོར་ཡུག་རྒྱུན་ལྡན་མིན་པར་ཉམས་གསོ་བྱེད་པར་མ་ལག་APTཡི་ཁོར་ཡུག་ལ་ཞིབ་བཤེར་གནང་རོགས།"
msgid "Priority upgrade status exception."
msgstr "དམིགས་སུ་བཀར་ནས་རིམ་པ་འཕར་བའི་རྣམ་པ་རྒྱུན་ལྡན་མིན་པ"
msgid "Upgrade configuration acquisition exception."
msgstr "རིམ་སྤར་བཀོད་སྒྲིག་ལ་རྒྱུན་ལྡན་མིན་པའི་གྲུབ་འབྲས་ཐོབ་པ་རེད།"
msgid "Please check your network connection and retry."
msgstr "ཁྱེད་ཀྱི་དྲ་རྒྱ་འབྲེལ་མཐུད་བྱས་རྗེས་ཡང་བསྐྱར་ཚོད་ལྟ་ཞིག་བྱེད་རོགས།"
msgid "Please check your source list and retry."
msgstr "ཁྱེད་ཀྱི་འབྱུང་ཁུངས་རེའུ་མིག་ལ་ཞིབ་བཤེར་བྱས་རྗེས་ཡང་བསྐྱར་ཚོད་ལྟ་བྱོས།"
msgid "Checking network connection"
msgstr "དྲ་རྒྱ་སྦྲེལ་མཐུད་བྱེད་པར་ཞིབ་བཤེར་བྱ་དགོས།"
msgid "Updating Source Template"
msgstr "འབྱུང་ཁུངས་གསར་སྒྱུར་བྱེད་པའི་མ་དཔེའི་ནང་།"
msgid "Update Manager upgrade is complete, please restart the setting panel before performing the system update."
msgstr "དོ་དམ་ཡོ་བྱད་རིམ་སྤར་ལེགས་འགྲུབ་བྱུང་བ་དང་། གསར་བཅོས་བྱས་རྗེས་སླར་ཡང་མ་ལག་གསར་སྒྱུར་བྱེད་རོགས།"
msgid "Uninstallation completed"
msgstr "ཕབ་ལེན་ལེགས་འགྲུབ་བྱུང་བ།"
msgid "Package validation failed and installation was rejected."
msgstr "མཉེན་ཆས་ཚོད་ལྟས་ར་སྤྲོད་བྱས་ནས་ཕམ་ཁ་བྱུང་བས་སྒྲིག་སྦྱོར་དང་ལེན་མ་བྱས"
msgid "Other tasks are being updated and upgraded, please uninstall them later."
msgstr "ལས་འགན་གཞན་དག་གསར་སྒྱུར་རིམ་སྤོར་བྱེད་བཞིན་པའི་སྒང་ཡིན་"
#: ../aptdaemon/worker/aptworker.py:1353
msgid "The following packages have unmet dependencies:"
msgstr "གཤམ་གསལ་གྱི་མཉེན་ཆས་ཁུག་མ་ཡིད་ཚིམ་པའི་གཞན་རྟེན་གྱི་འབྲེལ་བ།"
#: ../aptdaemon/worker/aptworker.py:1406
msgid "but it is a virtual package"
msgstr "འོན་ཀྱང་དེ་ནི་རྟོག་བཟོའི་མཉེན་ཆས་ཁུག་མ་རེད།"
#: ../aptdaemon/worker/aptworker.py:1409
msgid "but it is not installed"
msgstr "但是 %s 没有安装"
#: ../aptdaemon/worker/aptworker.py:1411
msgid "but it is not going to be installed"
msgstr "但是无法安装 %s"
#. TRANSLATORS: %s is a version number
#: ../aptdaemon/worker/aptworker.py:1415
#, python-format
msgid "but %s is installed"
msgstr "但是 %s 已经安装"
#. TRANSLATORS: %s is a version number
#: ../aptdaemon/worker/aptworker.py:1419
#, python-format
msgid "but %s is to be installed"
msgstr "但是将要安装 %s"
#: ../SystemUpdater/Core/enums.py:763
msgid "Kylin System Updater"
msgstr "ཝེ།སྒྲིག་ཆས་གསར་སྒྱུར་བྱེད་དགོས།"
#: ../SystemUpdater/Core/enums.py:609
msgid "Kylin Installer"
msgstr "ཝེ།སྒྲིག་ཆས་སྒྲིག་སྦྱོར་བྱེད་དགོས།"
#: ../SystemUpdater/Core/enums.py:610
msgid "Kylin Uninstaller"
msgstr "ཝེ།བཏགས་ཆས་"
#: ../SystemUpdater/Core/enums.py:611
msgid "Kylin Background Upgrade"
msgstr "ཁ་རོག་གེར་གསར་སྒྱུར་བྱེད་པ།"
#: ../SystemUpdater/Core/enums.py:612
msgid "Kylin Software Center"
msgstr "མཉེན་ཆས་ཚོང་ཁང་།"
#: ../SystemUpdater/UpdateManagerDbus.py:355
msgid " requires authentication to uninstall software packages."
msgstr "མཉེན་ཆས་ཀྱི་ཁུག་མ་འདོན་པར་བདེན་དཔང་ར་སྤྲོད་བྱེད་དགོས།"
#. 验签失败,提权
#: ../SystemUpdater/UpdateManager.py:463
msgid " requires authentication to install software packages."
msgstr "མཉེན་ཆས་ཀྱི་ཁུག་མ་སྒྲིག་སྦྱོར་བྱེད་པར་བདེན་དཔང་ར་སྤྲོད་བྱེད"
#: ../SystemUpdater/Core/utils.py:750
msgid "Authentication success."
msgstr "བདེན་དཔང་ར་སྤྲོད་ལེགས་འགྲུབ་བྱུང་"
#: ../SystemUpdater/Core/utils.py:753
msgid "Authentication failure."
msgstr "བདེན་དཔང་ར་སྤྲོད་ཕམ་སོང་།"
#: ../SystemUpdater/Core/enums.py:101
msgid "Deb format exception, read local deb file error."
msgstr "མཉེན་ཆས་ཀྱི་ཁུག་མའི་རྣམ་གཞག་རྒྱུན་ལྡན་མིན་པས་ཕམ་ཁ་བླངས།"
#: ../SystemUpdater/Core/enums.py:102
msgid "Install deb error."
msgstr "མཉེན་ཆས་སྒྲིག་སྦྱོར་བྱས་པ་ཕམ་སོང་།"
msgid "Upgrade System"
msgstr "ཁྱོན་ཡོངས་ནས་རིམ་སྤར་བྱ་དགོས།"
msgid "kylin-unattended-upgrade"
msgstr "རང་འགུལ་གྱིས་གསར་སྒྱུར་བྱེད་དགོས།"
msgid "Please check the system time and synchronize the system time before updating."
msgstr "མ་ལག་གི་དུས་ཚོད་ལ་ཞིབ་བཤེར་གནང་རོགས། དུས་མཉམ་དུ་མ་ལག་གི་དུས་ཚོད་རྗེས་སུ་གསར་སྒྱུར་བྱེད་དགོས"
msgid "The package is unsigned, refuses to install."
msgstr "མཉེན་ཆས་ཀྱི་ཁུག་མར་མིང་རྟགས་མ་བཀོད་པས་སྒྲིག་སྦྱོར་དང་ལེན་མི་བྱེད།"
msgid "Program exception, please contact the administrator to solve."
msgstr "གོ་རིམ་ལག་བསྟར་རྒྱུན་ལྡན་མིན་པར་དོ་དམ་པ་དང་འབྲེལ་གཏུག་བྱས་ནས་ཐག་གཅོད"
msgid "Exceptions to running the check script."
msgstr "འཁོར་སྐྱོད་ཞིབ་བཤེར་གྱི་རྐང་པར་རྒྱུན་ལྡན་མིན་པ་བྱུང་།"
msgid "Application installation control policy not enabled ."
msgstr "ཉེར་སྤྱོད་སྒྲིག་སྦྱོར་དོ་དམ་ཚོད་འཛིན་བྱེད་འགོ་ཚུགས་མེད་པ།"
msgid "Installation failed! Application is not in the software whitelist list!"
msgstr "སྒྲིག་སྦྱོར་ཕམ་པ། མཉེན་ཆས་ནི་མིང་ཐོའི་རེའུ་མིག་དཀར་པོ་ན་ཡོད་དམ།"
msgid "Installation failed! Application is in the software blacklist list!"
msgstr "སྒྲིག་སྦྱོར་ཕམ་པ། མཉེན་ཆས་མིང་ཐོའི་ནང་དུ་ཡོད།"
msgid "Application installation in unknown mode ."
msgstr "ཉེར་སྤྱོད་སྒྲིག་སྦྱོར་དང་དོ་དམ་ཚོད་འཛིན་གྱི་ཐབས་ཇུས་མི་ཤེས་པ།"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
#!/bin/sh
#系统升级收集bug日志使用
if [ $(id -u) -eq 0 ]; then
echo "当前执行权限是root请使用普通权限来执行"
exit 1
fi
echo "系统升级收集BUG日志使用..."
#建立收集的log目录
mkdir updaterlog
#记录一些基本信息
date >> updaterlog/base-info
dpkg -l | grep kylin-system-updater >> updaterlog/base-info
echo $1 >> updaterlog/base-info
echo "记录BUG产生时间系统当前时间以及升级相关的版本信息"
cat updaterlog/base-info
cp /etc/apt/sources.list updaterlog || true
cp -r /etc/apt/apt.conf.d updaterlog || true
cp -r /var/log/syslog updaterlog || true
cp -r /usr/share/kylin-update-desktop-config/config/ updaterlog || true
cp -r /var/log/kylin-system-updater/ updaterlog || true
#收集apt的日志
cp -r /var/log/apt updaterlog || true
cp -r /var/log/dpkg.log updaterlog || true
cp -r /var/log/kylin-unattended-upgrades/ updaterlog || true
cp -r ~/.config/kylin-background-upgrade/ updaterlog || true
#激活
cp -r ~/.log/kylin-activation/ updaterlog || true
#收集前端日志
cp -r ~/.log/kylin-update-frontend-notifysend.log updaterlog >/dev/null 2>&1 || true
cp -r ~/.log/ukui-control-center.log updaterlog >/dev/null 2>&1 || true
cp -r ~/.log/ukui-notification-daemon.log updaterlog >/dev/null 2>&1 || true
cp -r ~/.config/ukui-session/ updaterlog >/dev/null 2>&1 || true
cp -r /tmp/kylin-updateresult-notify.log updaterlog >/dev/null 2>&1 || true
outputName="$(date +%m-%d,%H-%M-%S)-updaterLog.tar.gz"
#将所有的日志进行打包
tar -czvf updaterLog.tar.gz updaterlog >/dev/null
#删除收集的日志目录
rm -rf updaterlog
#将文件存储到桌面
if [ ! -d ~/桌面 ]; then
mv updaterLog.tar.gz ~/Desktop/$outputName
echo 输出位置:~/Desktop/$outputName
else
mv updaterLog.tar.gz ~/桌面/$outputName
echo 输出位置:~/桌面/$outputName
fi
echo "系统更新日志收集完毕..."
echo "\033[1;31m注意\033[0m 1、请确保Bug复现的时间与执行脚本收集日志时间相近以此能根据脚本执行时间快速定位到问题的相关日志..."
echo " 2、若Bug复现的时间与现在时间相差较远时请手动输入大概复现时间。例如 report-updater-bug 月-日,时-分"
echo "请将桌面下\033[5;32;49;1m $outputName \033[0m日志文件提交到禅道... "

View File

@ -0,0 +1,15 @@
[build_i18n]
domain=kylin-system-updater
# xml_files=[("share/metainfo/",
# ("data/update-manager.appdata.xml.in",)),
# ]
[sdist]
formats = bztar
[nosetests]
match=test
# [install]
# skip-build=0

21
backend-immutable/setup.py Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
from distutils.core import setup
from DistUtilsExtra.command import (
build_extra, build_i18n, build_help)
disabled = []
class CustomBuild(build_extra.build_extra):
def run(self):
build_extra.build_extra.run(self)
setup(
packages=[ 'SystemUpdater',
'SystemUpdater.backend',
'SystemUpdater.Core'
],
cmdclass={ "build": CustomBuild,
"build_i18n": build_i18n.build_i18n
# "build_help": build_help.build_help
}
)

18
debian/changelog vendored
View File

@ -1,3 +1,21 @@
kylin-system-updater (3.0.0.0-ok2) nile; urgency=medium
* BUG:无
* 需求号: 无
* 其他改动说明: 同时兼容可变系统更新包以及不可变系统更新包
* 其他改动影响域:系统更新
-- wangsong <wangsong@kylinos.cn> Tue, 12 Dec 2023 20:40:24 +0800
kylin-system-updater (3.0.0.0-ok1) nile; urgency=medium
* BUG:无
* 需求号: 无
* 其他改动说明: 引入基于不可变系统的系统更新
* 其他改动影响域:系统更新
-- wangsong <wangsong@kylinos.cn> Mon, 04 Dec 2023 11:36:30 +0800
kylin-system-updater (2.0.5.15-ok9) yangtze; urgency=medium
* BUG:issues/I8305P【控制面板】【更新】d-feet设置参数开启"自动更新"后日志中显示download time及install time可以下载更新包成功安装失败

28
debian/control vendored
View File

@ -43,9 +43,33 @@ Depends: ${python3:Depends},
python3-crypto,
sqlite3,
kylin-update-frontend
Breaks:
Recommends: python3-launchpadlib
Breaks: kylin-system-updater-immutable
Conflicts: kylin-system-updater-immutable
Suggests: gir1.2-dbusmenu-glib-0.4,
gir1.2-unity-5.0,
gir1.2-unity-5.0
Package: kylin-system-updater-immutable
Architecture: all
Depends: ${python3:Depends},
${misc:Depends},
policykit-1,
python3-dbus,
python3-psutil,
python3-gi (>= 3.8),
python3-yaml,
aptdaemon (>=1.1.1+bzr982-0kylin32.3k5.2),
python3-distro-info,
python3-apscheduler,
python3-crypto,
ostree,
gir1.2-ostree-1.0,
libostree-1-1,
sqlite3
Recommends: python3-launchpadlib
Breaks: kylin-system-updater
Conflicts: kylin-system-updater
Suggests: gir1.2-dbusmenu-glib-0.4,
gir1.2-unity-5.0
Description: dbus daemon that manages apt updates.
dbus daemon that manages apt updates. Provides DBUS interfaces to UKCC.

View File

@ -0,0 +1,41 @@
#!/usr/bin/dh-exec
#backend-immutable
backend-immutable/data/kylin-system-updater.db /usr/share/kylin-system-updater/
backend-immutable/report-updater-bug /usr/bin
backend-immutable/data/30kylin-system-updater /etc/apt/apt.conf.d/
backend-immutable/data/com.kylin.systemupgrade.conf /etc/dbus-1/system.d/
#backend-immutable/data/com.kylin.systemupgrade.limit /etc/dbus-1/conf/
backend-immutable/data/kylin-system-updater.service /usr/lib/systemd/system/
#backend-immutable/data/kylin-upgrade-poweroff.service /usr/lib/systemd/system/
backend-immutable/data/kylin-logout-required /usr/share/kylin-system-updater/
backend-immutable/data/kylin-reboot-required /usr/share/kylin-system-updater/
backend-immutable/kylin-system-updater /usr/share/kylin-system-updater/
backend-immutable/monitor-reboot /usr/share/kylin-system-updater/
backend-immutable/SystemUpdater/*.py /usr/share/kylin-system-updater/SystemUpdater/
backend-immutable/SystemUpdater/backend/*.py /usr/share/kylin-system-updater/SystemUpdater/backend/
backend-immutable/SystemUpdater/Core/*.py /usr/share/kylin-system-updater/SystemUpdater/Core/
backend-immutable/build/mo/* /usr/share/locale/
backend-immutable/data/system-updater-defaults.conf /var/lib/kylin-system-updater/
backend-immutable/data/system-updater-coverable.conf /var/lib/kylin-system-updater/
backend-immutable/data/inhibit-sleep.conf /var/lib/kylin-system-updater/
backend-immutable/data/unattended-upgrades-policy.conf /var/lib/unattended-upgrades/
backend-immutable/data/unattended-upgrades-timestamp /var/lib/unattended-upgrades/
backend-immutable/data/cn.kylinos.KylinSystemUpdater.policy /usr/share/polkit-1/actions/
backend-immutable/data/kylin-system-version.conf /etc/kylin-version/
#configDaemon
backend-immutable/kylin-upgrade-strategies /usr/share/kylin-system-updater/
backend-immutable/data/com.kylin.UpgradeStrategies.conf /etc/dbus-1/system.d/
backend-immutable/data/com.kylin.UpgradeStrategies.service /usr/share/dbus-1/system-services/
backend-immutable/data/cn.kylinos.UpgradeStrategies.policy /usr/share/polkit-1/actions/
backend-immutable/data/kylin-system-updater /etc/logrotate.d
#uu
unattended-upgrades/*.service /lib/systemd/system/
unattended-upgrades/notify /usr/bin/
unattended-upgrades/*.desktop /etc/xdg/autostart/
unattended-upgrades/kylin-unattended-upgrade /usr/bin
unattended-upgrades/kylin-unattended-upgrade-shutdown /usr/bin
unattended-upgrades/kylin-unattended-upgrades /etc/apt/apt.conf.d/
unattended-upgrades/logrotate.d/* /etc/logrotate.d/