diff --git a/update/package_tools.py b/update/package_tools.py index e299ed6..7d9b879 100644 --- a/update/package_tools.py +++ b/update/package_tools.py @@ -889,12 +889,22 @@ class EmergeLog: """ Получить список измений по логу, от последней записи маркера """ - log_data = SavableIterator(readLinesFile(self.emerge_log)) + log_data = SavableIterator(iter(readLinesFile(self.emerge_log))) for line in log_data.save(): if self.emerge_task.has_marker(line): log_data.save() return log_data.restore() + def get_last_time(self): + """ + Получить время послдней записи маркера + """ + last_line = "" + for line in readLinesFile(self.emerge_log): + if self.emerge_task.has_marker(line): + last_line = line + return last_line + @property def list(self): if self._list is None: diff --git a/update/update.py b/update/update.py index 9ffec82..17f64c4 100644 --- a/update/update.py +++ b/update/update.py @@ -28,7 +28,7 @@ from calculate.lib.utils.tools import AddonError from calculate.lib.utils.colortext.palette import TextState from calculate.lib.utils.colortext import get_color_print from calculate.update.emerge_parser import RevdepPercentBlock -import math +import re from package_tools import Git, Layman,\ EmergeLogNamedTask, EmergeLog, GitError, \ @@ -114,6 +114,47 @@ class Update: dv.Set('cl_update_outdate_set', 'on', force=True) return True + def setAutocheckParams(self, status, interval): + """ + Настроить параметры автопроверки обновлений + """ + status = "on" if status else "off" + self.clVars.Write('cl_update_autocheck_set', status, True) + self.clVars.Write('cl_update_autocheck_interval', interval, True) + return True + + def checkSchedule(self, interval, status): + """ + Проверить по расписанию необходимость запуска команды + """ + if not status: + self.printWARNING(_("Update autocheck is not enabled")) + return False + line = EmergeLog(EmergeLogNamedTask("schedule")).get_last_time() + re_interval = re.compile("^(\d+)\s*(hours?|days?|weeks?)?", re.I) + interval_match = re_interval.search(interval) + MINUTE = 60 + HOUR = MINUTE * 60 + DAY = HOUR * 24 + WEEK = DAY * 7 + if interval_match: + if interval_match.group(2): + suffix_map = {'h': HOUR, 'd': DAY, 'w': WEEK} + k = suffix_map.get(interval_match.group(2).lower()[0], HOUR) + else: + k = HOUR + est = int(interval_match.group(1)) * k + else: + est = 3 * HOUR + if line: + linetime = line.partition(":")[0] + if linetime.isdigit(): + if (time.time() - int(linetime)) < (est - 10 * MINUTE): + self.printWARNING(_("Update time is not yet come")) + return False + self.mark_schedule() + return True + def checkRun(self, wait_update): """ Проверить повторный запуск @@ -481,8 +522,14 @@ class Update: _("Listing packages for removal"))) self._display_pretty_package_list( emerge.install_packages.remove_list, remove_list=True) + if len(emerge.install_packages.list) > 0: + install_mess = (_("{count} packages will be installed").format( + count=len(emerge.install_packages.list)) + ", ") + else: + install_mess = "" if str(emerge.download_size) != "0 kB": - self.printSUCCESS(_("{size} will be downloaded").format( + self.printSUCCESS(_("{install}{size} will be downloaded").format( + install=install_mess, size=str(emerge.download_size))) def _display_remove_list(self, emerge): @@ -530,6 +577,12 @@ class Update: EmergeLogNamedTask(ClUpdateAction.log_names['premerge'])) elog.mark_end_task(), + def mark_schedule(self): + from calculate.update.utils.cl_update import ClUpdateAction + elog = EmergeLog( + EmergeLogNamedTask(ClUpdateAction.log_names['schedule'])) + elog.mark_end_task(), + def premerge(self, param, *packages): """ Вывести информацию об обновлении diff --git a/update/utils/cl_setup_update.py b/update/utils/cl_setup_update.py new file mode 100644 index 0000000..730baf4 --- /dev/null +++ b/update/utils/cl_setup_update.py @@ -0,0 +1,45 @@ +#-*- coding: utf-8 -*- + +# Copyright 2010-2013 Calculate Ltd. http://www.calculate-linux.org +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +from calculate.core.server.func import Action, Tasks +from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate +from calculate.lib.utils.files import FilesError +from calculate.update.update import UpdateError +from calculate.update.package_tools import GitError + +setLocalTranslate('cl_update3', sys.modules[__name__]) +__ = getLazyLocalTranslate(_) + + +class ClSetupUpdateAction(Action): + """ + Действие для настройки параметров автопроверки обновлений + """ + # ошибки, которые отображаются без подробностей + native_error = (FilesError, UpdateError, GitError) + + successMessage = __("Updates autocheck settings configured!") + failedMessage = __("Failed to configure the updates autocheck settings!") + interruptMessage = __("Configuration manually interrupted") + + + # список задач для дейсвия + tasks = [ + {'name': 'set_variables', + 'method': 'Update.setAutocheckParams(cl_update_autocheck_set,' + 'cl_update_autocheck_interval)'} + ] diff --git a/update/utils/cl_update.py b/update/utils/cl_update.py index 50e8ae2..f2fbae4 100644 --- a/update/utils/cl_update.py +++ b/update/utils/cl_update.py @@ -36,7 +36,7 @@ class ClUpdateAction(Action): native_error = (FilesError, UpdateError, GitError, EmergeError) successMessage = None - failedMessage = __("Update failed") + failedMessage = None interruptMessage = __("Update manually interrupted") def was_installed(pkg, task_name): @@ -53,7 +53,8 @@ class ClUpdateAction(Action): def pkg_color(text): return text - log_names = {'premerge': "check updates", + log_names = {'schedule': "schedule", + 'premerge': "check updates", 'python_updater': "update python modules", 'perl_cleaner': "update perl modules", 'kernel_modules': "update kernel modules", @@ -208,6 +209,12 @@ class ClUpdateAction(Action): # список задач для дейсвия tasks = [ + {'name': 'check_schedule', + 'method': 'Update.checkSchedule(cl_update_autocheck_interval,' + 'cl_update_autocheck_set)', + 'condition': lambda Get: ( + Get('cl_update_autocheck_schedule_set') == 'on'), + }, {'name': 'check_run', 'method': 'Update.checkRun(cl_update_wait_another_set)'}, {'name': 'reps_synchronization', @@ -287,6 +294,14 @@ class ClUpdateAction(Action): ] } ] + emerge_tasks + [ + {'name':'failed', + 'error':__("Update failed"), + 'depend': (Tasks.failed() & Tasks.hasnot("interrupt") & + (Tasks.hasnot("check_schedule") | + Tasks.success_all("check_schedule")))}, + {'name': 'failed', + 'depend': Tasks.failed_all("check_schedule") + }, # сообщение удачного завершения при обновлении ревизии {'name': 'success_rev', 'message': __("System update finished!"), @@ -297,5 +312,5 @@ class ClUpdateAction(Action): {'name': 'success_world', 'message': __("World rebuild finished!"), 'condition': lambda Get: Get('cl_rebuild_world_set') == 'on' - } + }, ] diff --git a/update/variables/update.py b/update/variables/update.py index c6b891c..a0634ac 100644 --- a/update/variables/update.py +++ b/update/variables/update.py @@ -57,7 +57,7 @@ class VariableClUpdateWorld(Variable): class VariableClRebuildWorldSet(Variable): """ - List of action update world, rebuild world, + List of action update world, rebuild world, """ type = "bool" opt = ["--rebuild-world"] @@ -70,7 +70,7 @@ class VariableClRebuildWorldSet(Variable): class VariableClUpdateRevSet(Variable): """ - List of action update world, rebuild world, + List of action update world, rebuild world, """ type = "bool" opt = ["--update-rev"] @@ -241,7 +241,7 @@ class VariableClUpdateSyncRep(Variable): orderList = self.Get('cl_update_rep_name') return sorted(value,key=lambda x: (orderList.index(x) if x in orderList else -1),reverse=True) - + def get(self): return list(reversed(self.Get('cl_update_rep_name'))) @@ -283,7 +283,7 @@ class VariableClUpdateMetadataForce(Variable): #untrusted = True def init(self): - self.help = ("'force' - " + _("force the update ebuilds metadata") + + self.help = ("'force' - " + _("force the update ebuilds metadata") + ",\n'skip' - " + _("skip the ebuild metadata update") + ",\n'auto' - " + _("update metadata if it is outdated")) self.label = _("Update metadata") @@ -873,3 +873,50 @@ class VariableClUpdateProfileSyncSet(Variable): def init(self): self.label = _("Synchronize repositories") self.help = _("synchronize repositories") + + +class VariableClUpdateAutocheckSet(Variable): + """ + Выполнять/невыполнять автопроверку обновлений + """ + type = "bool" + value = "on" + opt = ["-a", "--autocheck"] + + def init(self): + self.label = _("Automatically check updates") + self.help = _("automatically check updates") + + +class VariableClUpdateAutocheckInterval(Variable): + """ + Интервал выполнения проверки + """ + type = "choiceedit" + opt = ["-I", "--autocheck-interval"] + metavalue = "INTERVAL" + + def init(self): + self.label = _("Interval for the updates checking") + self.help = _("set interval for the updates checking") + + def choice(self): + return [["1h",_("hourly")], + ["3h",_("every three hours")], + ["6h",_("every six hours")], + ["12h",_("every twenty hours")], + ["1d",_("daily")]] + + +class VariableClUpdateAutocheckScheduleSet(Variable): + """ + Запустить проверку только если со времени последнего запуска прошло + необходимое время + """ + type = "bool" + value = "off" + opt = ["--schedule"] + + def init(self): + self.label = _("Consider auto-check schedule") + self.help = "consider auto-check schedule" diff --git a/update/wsdl_update.py b/update/wsdl_update.py index 5e8d36e..53af585 100644 --- a/update/wsdl_update.py +++ b/update/wsdl_update.py @@ -24,6 +24,7 @@ from calculate.update.update import Update,UpdateError from calculate.update.package_tools import GitError from utils.cl_update import ClUpdateAction from utils.cl_update_profile import ClUpdateProfileAction +from utils.cl_setup_update import ClSetupUpdateAction from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate setLocalTranslate('cl_update3',sys.modules[__name__]) __ = getLazyLocalTranslate(_) @@ -72,6 +73,7 @@ class Wsdl(WsdlBase): 'cl_update_eixupdate_force', 'cl_update_sync_only_set', 'cl_update_wait_another_set', + 'cl_update_autocheck_schedule_set', 'cl_templates_locate', 'cl_verbose_set', 'cl_dispatch_conf'), next_label=_("Update"))]}, @@ -120,4 +122,38 @@ class Wsdl(WsdlBase): )], 'brief': {'next': __("Perform"), 'name': __("Set the profile")}}, + # + # Настроить автопроверку обновлений + # + { + # идентификатор метода + 'method_name': "setup_update", + # категория метода + 'category': __('Configuration'), + # заголовок метода + 'title': __("Updates"), + # иконка для графической консоли + 'image': 'calculate-update', + # метод присутствует в графической консоли + 'gui': True, + # консольная команда + 'command': 'cl-setup-update', + # права для запуска метода + 'rights': ['update'], + # объект содержащий модули для действия + 'logic': {'Update': Update}, + # описание действия + 'action': ClSetupUpdateAction, + # объект переменных + 'datavars': "update", + 'native_error': (VariableError, DataVarsError, + InstallError, UpdateError, GitError), + # значения по умолчанию для переменных этого метода + 'setvars': {'cl_action!': 'merge'}, + # описание груп (список лямбда функций) + 'groups': [ + lambda group: group(_("Updates autocheck settings"), + normal=('cl_update_autocheck_set', + 'cl_update_autocheck_interval'), + next_label=_("Perform"))]}, ]