You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1037 lines
42 KiB

  1. #-*- coding: utf-8 -*-
  2. # Copyright 2014 Calculate Ltd. http://www.calculate-linux.org
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import random
  16. import sys
  17. from os import path
  18. import os
  19. import time
  20. from calculate.core.server.gen_pid import search_worked_process
  21. from calculate.lib.cl_template import SystemIni
  22. from calculate.lib.datavars import DataVarsError
  23. from calculate.lib.utils.tools import AddonError
  24. from calculate.lib.utils.colortext.palette import TextState
  25. from calculate.lib.utils.colortext import get_color_print
  26. from calculate.update.emerge_parser import RevdepPercentBlock
  27. from calculate.update.datavars import DataVarsUpdate
  28. from calculate.update.update_info import UpdateInfo
  29. from calculate.lib.cl_log import log
  30. import re
  31. from itertools import chain
  32. from collections import MutableSet
  33. from calculate.lib.utils.portage import (Git, Layman, EmergeLog, GitError,
  34. EmergeLogNamedTask, PackageList,
  35. PackageInformation,
  36. get_packages_files_directory,
  37. get_manifest_files_directory,
  38. get_remove_list)
  39. Colors = TextState.Colors
  40. from calculate.lib.utils.files import (getProgPath, STDOUT, removeDir,
  41. PercentProgress, process, getRunCommands,
  42. readFile, listDirectory)
  43. import emerge_parser
  44. import logging
  45. from emerge_parser import EmergeParser, EmergeCommand, EmergeError, EmergeCache
  46. from calculate.lib.cl_lang import (setLocalTranslate, getLazyLocalTranslate,
  47. RegexpLocalization, _)
  48. setLocalTranslate('cl_update3', sys.modules[__name__])
  49. __ = getLazyLocalTranslate(_)
  50. class UpdateError(AddonError):
  51. """Update Error"""
  52. class OverlayOwnCache(MutableSet):
  53. """
  54. Сет оверлеев с интегрированным кэшем
  55. """
  56. def __init__(self, initvalue=()):
  57. pass
  58. def __get_overlays(self):
  59. own_cache_value = SystemIni().getVar('update', 'own_cache') or ""
  60. return [x.strip() for x in own_cache_value.split(',') if x.strip()]
  61. def __write_overlays(self, overlays):
  62. if not overlays:
  63. SystemIni().delVar('update', 'own_cache')
  64. else:
  65. SystemIni().setVar('update', {'own_cache': ",".join(overlays)})
  66. def __contains__(self, item):
  67. return item in self.__get_overlays()
  68. def __iter__(self):
  69. return iter(self.__get_overlays())
  70. def __len__(self):
  71. return len(self.__get_overlays())
  72. def __append_value(self, overlays, value):
  73. if value not in overlays:
  74. overlays.append(value)
  75. self.__write_overlays(overlays)
  76. def add(self, value):
  77. overlays = self.__get_overlays()
  78. self.__append_value(overlays, value)
  79. def discard(self, value):
  80. overlays = self.__get_overlays()
  81. if value in overlays:
  82. overlays.remove(value)
  83. self.__write_overlays(overlays)
  84. class Update(object):
  85. """Основной объект для выполнения действий связанных с обновлением системы
  86. """
  87. def init(self):
  88. commandLog = path.join(self.clVars.Get('core.cl_log_path'),
  89. 'lastcommand.log')
  90. emerge_parser.CommandExecutor.logfile = commandLog
  91. self.color_print = get_color_print()
  92. self.emerge_cache = EmergeCache()
  93. if self.clVars.Get('cl_env_debug_set') == 'off':
  94. EmergeCache.logger.logger.setLevel(logging.WARNING)
  95. self.emerge_cache.check_list = (
  96. self.emerge_cache.check_list +
  97. map(emerge_parser.GitCheckvalue,
  98. self.clVars.Get('update.cl_update_rep_path')))
  99. self.update_map = {}
  100. def _syncRepository(self, name, url, rpath, revision, branch,
  101. cb_progress=None):
  102. """
  103. Синхронизировать репозитори
  104. """
  105. dv = self.clVars
  106. git = Git()
  107. info_outdate = False
  108. try:
  109. self.stash_cache(rpath, name)
  110. if not git.checkExistsRep(rpath):
  111. if revision == "last":
  112. git.cloneRepository(url, rpath, branch,
  113. cb_progress=cb_progress)
  114. else:
  115. git.cloneRevRepository(url, rpath, branch, revision,
  116. cb_progress=cb_progress)
  117. info_outdate = True
  118. else:
  119. # если нужно обновиться до конкретной ревизии
  120. if revision != "last":
  121. if revision == git.getCurrentCommit(rpath):
  122. if git.getBranch(rpath) == branch:
  123. return False
  124. # получить изменения из удаленного репозитория
  125. git.fetchRepository(rpath, cb_progress=cb_progress)
  126. # если текущая ветка не соответствует нужной
  127. repInfo = git.getStatusInfo(rpath)
  128. if repInfo['branch'] != branch:
  129. # меняем ветку
  130. info_outdate = True
  131. git.checkoutBranch(rpath, branch)
  132. if revision == "last":
  133. if git.resetRepository(rpath, to_origin=True):
  134. # если не удалось сбросить
  135. repInfo = git.getStatusInfo(rpath)
  136. if repInfo.get("files", False):
  137. raise GitError("Failed to reset git")
  138. info_outdate = True
  139. else:
  140. git.resetRepository(rpath, to_rev=revision)
  141. info_outdate = True
  142. if info_outdate:
  143. dv.Set('cl_update_outdate_set', 'on', force=True)
  144. finally:
  145. self.unstash_cache(rpath, name)
  146. return True
  147. def setAutocheckParams(self, status, interval, update_other, cleanpkg):
  148. """
  149. Настроить параметры автопроверки обновлений
  150. """
  151. onoff = lambda x: "on" if x else "off"
  152. self.clVars.Write('cl_update_autocheck_set', onoff(status), True)
  153. self.clVars.Write('cl_update_autocheck_interval', interval, True)
  154. self.clVars.Write('cl_update_other_set', onoff(update_other), True)
  155. self.clVars.Write('cl_update_cleanpkg_set', onoff(cleanpkg), True)
  156. return True
  157. def checkSchedule(self, interval, status):
  158. """
  159. Проверить по расписанию необходимость запуска команды
  160. """
  161. if not status:
  162. self.printWARNING(_("Updates autocheck is not enabled"))
  163. return False
  164. last_check = SystemIni().getVar('update', 'last_check') or ""
  165. re_interval = re.compile("^(\d+)\s*(hours?|days?|weeks?)?", re.I)
  166. interval_match = re_interval.search(interval)
  167. MINUTE = 60
  168. HOUR = MINUTE * 60
  169. DAY = HOUR * 24
  170. WEEK = DAY * 7
  171. if interval_match:
  172. if interval_match.group(2):
  173. suffix_map = {'h': HOUR, 'd': DAY, 'w': WEEK}
  174. k = suffix_map.get(interval_match.group(2).lower()[0], HOUR)
  175. else:
  176. k = HOUR
  177. est = int(interval_match.group(1)) * k
  178. else:
  179. est = 3 * HOUR
  180. if last_check:
  181. if last_check.isdigit():
  182. if (time.time() - int(last_check)) < (est - 10 * MINUTE):
  183. self.printWARNING(_("Please wait for the update time"))
  184. return False
  185. self.mark_schedule()
  186. return True
  187. def checkRun(self, wait_update):
  188. """
  189. Проверить повторный запуск
  190. """
  191. update_running = lambda: any(os.getpid() != x
  192. for x in search_worked_process('update', dv))
  193. dv = self.clVars
  194. if update_running():
  195. if not wait_update:
  196. raise UpdateError(_("Update is already running. "
  197. "Try to run later."))
  198. else:
  199. self.startTask(_("Waiting for another update to be complete"))
  200. while update_running():
  201. self.pauseProcess()
  202. while update_running():
  203. time.sleep(0.3)
  204. self.resumeProcess()
  205. time.sleep(random.random()*3)
  206. self.endTask()
  207. if self.clVars.Get('cl_chroot_status') == 'off':
  208. emerge_running = lambda: any("/usr/bin/emerge" in x
  209. for x in getRunCommands(True))
  210. if emerge_running():
  211. if not wait_update:
  212. raise UpdateError(_("Emerge is running. "
  213. "Try to run later."))
  214. else:
  215. self.startTask(_("Waiting for emerge to be complete"))
  216. while emerge_running():
  217. time.sleep(1)
  218. self.endTask()
  219. return True
  220. def syncRepositories(self, repname, clean_on_error=True):
  221. """
  222. Синхронизировать репозитории
  223. """
  224. dv = self.clVars
  225. url, rpath, revision, branch = (
  226. dv.Select(["cl_update_rep_url", "cl_update_rep_path",
  227. "cl_update_rep_rev", "cl_update_branch_name"],
  228. where="cl_update_rep_name", eq=repname, limit=1))
  229. if not url or not rpath:
  230. raise UpdateError(_("Configuration variables for repositories "
  231. "are not setup"))
  232. self.addProgress()
  233. if clean_on_error:
  234. try:
  235. if not self._syncRepository(repname, url, rpath, revision, branch,
  236. cb_progress=self.setProgress):
  237. return "skip"
  238. layman = Layman(dv.Get('cl_update_layman_installed'),
  239. dv.Get('cl_update_layman_make'))
  240. if repname != "portage":
  241. layman.add(repname, url, rpath)
  242. return True
  243. except GitError as e:
  244. if e.addon:
  245. self.printWARNING(str(e.addon))
  246. self.printWARNING(str(e))
  247. self.endTask(False)
  248. self.startTask(
  249. _("Re-fetching the {name} repository").format(name=repname))
  250. self.addProgress()
  251. try:
  252. rpath_new = "%s_new" % rpath
  253. self._syncRepository(repname, url, rpath_new, revision,
  254. branch, cb_progress=self.setProgress)
  255. removeDir(rpath)
  256. os.rename(rpath_new, rpath)
  257. except OSError as e:
  258. raise UpdateError(_("Failed to modify the "
  259. "{repname} repository").format(
  260. repname=repname)+_(": ")+str(e))
  261. finally:
  262. if path.exists(rpath_new):
  263. removeDir(rpath_new)
  264. else:
  265. if not self._syncRepository(repname, url, rpath, revision, branch):
  266. return "skip"
  267. layman = Layman(dv.Get('cl_update_layman_installed'),
  268. dv.Get('cl_update_layman_make'))
  269. if repname != "portage":
  270. layman.add(repname, url, rpath)
  271. return True
  272. metadata_cache_names = ("metadata/md5-cache", "metadata/cache")
  273. def stash_cache(self, rpath, name):
  274. """
  275. Спрятать кэш
  276. """
  277. if name in ("portage",):
  278. return
  279. if not name in OverlayOwnCache():
  280. for cachename in self.metadata_cache_names:
  281. cachedir = path.join(rpath, cachename)
  282. if path.exists(cachedir):
  283. try:
  284. cachedir_s = path.join(path.dirname(rpath),
  285. path.basename(
  286. cachename) + ".stash")
  287. if path.exists(cachedir_s):
  288. removeDir(cachedir_s)
  289. os.rename(cachedir, cachedir_s)
  290. except BaseException as e:
  291. pass
  292. def unstash_cache(self, rpath, name):
  293. """
  294. Извлеч кэш
  295. """
  296. if name in ("portage",):
  297. return
  298. cachenames = self.metadata_cache_names
  299. if not name in OverlayOwnCache():
  300. if any(path.exists(path.join(rpath, x)) for x in cachenames):
  301. for cachename in cachenames:
  302. cachedir_s = path.join(path.dirname(rpath),
  303. path.basename(cachename)+".stash")
  304. if path.exists(cachedir_s):
  305. try:
  306. removeDir(cachedir_s)
  307. except BaseException as e:
  308. pass
  309. OverlayOwnCache().add(name)
  310. else:
  311. for cachename in cachenames:
  312. cachedir = path.join(rpath, cachename)
  313. cachedir_s = path.join(path.dirname(rpath),
  314. path.basename(cachename)+".stash")
  315. if path.exists(cachedir_s):
  316. try:
  317. os.rename(cachedir_s, cachedir)
  318. except BaseException as e:
  319. pass
  320. else:
  321. if all(not path.exists(path.join(rpath, x)) for x in cachenames):
  322. OverlayOwnCache().discard(name)
  323. def syncLaymanRepository(self, repname):
  324. """
  325. Обновить репозиторий через layman
  326. """
  327. layman = getProgPath('/usr/bin/layman')
  328. if not layman:
  329. raise UpdateError(_("The Layman tool is not found"))
  330. rpath = self.clVars.Select('cl_update_other_rep_path',
  331. where='cl_update_other_rep_name', eq=repname,
  332. limit=1)
  333. laymanname = path.basename(rpath)
  334. self.stash_cache(rpath, laymanname)
  335. try:
  336. if Git.is_git(rpath):
  337. self.addProgress()
  338. p = PercentProgress(layman, "-s", laymanname, part=1, atty=True)
  339. for perc in p.progress():
  340. self.setProgress(perc)
  341. else:
  342. p = process(layman, "-s", repname, stderr=STDOUT)
  343. if p.failed():
  344. raise UpdateError(
  345. _("Failed to update the {rname} repository").format(rname=repname),
  346. addon=p.read())
  347. finally:
  348. self.unstash_cache(rpath, laymanname)
  349. return True
  350. def regenCache(self, repname):
  351. """
  352. Обновить кэш метаданных репозитория
  353. """
  354. egenCache = getProgPath('/usr/bin/egencache')
  355. if not egenCache:
  356. raise UpdateError(_("The Portage tool is not found"))
  357. if repname in self.clVars.Get('cl_update_rep_name'):
  358. path_rep = self.clVars.Select('cl_update_rep_path',
  359. where='cl_update_rep_name',
  360. eq=repname, limit=1)
  361. repo_name = readFile(
  362. path.join(path_rep, "profiles/repo_name")).strip()
  363. if repo_name != repname:
  364. self.printWARNING(
  365. _("Repository '{repo_name}' called '{repname}'"
  366. " in cl_update_rep_name").format(
  367. repo_name=repo_name, repname=repname))
  368. raise UpdateError(_("Failed to update the cache of the {rname} "
  369. "repository").format(rname=repname))
  370. cpu_num = self.clVars.Get('hr_cpu_num')
  371. if repname in OverlayOwnCache():
  372. self.printWARNING(
  373. _("Repository %s has its own cache") % repname.capitalize())
  374. else:
  375. self.startTask(_("Updating the %s repository cache") %
  376. repname.capitalize())
  377. p = process(egenCache, "--repo=%s" % repname, "--update",
  378. "--jobs=%s" % cpu_num, stderr=STDOUT)
  379. if p.failed():
  380. raise UpdateError(_("Failed to update the cache of the {rname} "
  381. "repository").format(rname=repname),
  382. addon=p.read())
  383. return True
  384. def emergeMetadata(self):
  385. """
  386. Выполнить egencache и emerge --metadata
  387. """
  388. emerge = getProgPath("/usr/bin/emerge")
  389. if not emerge:
  390. raise UpdateError(_("The Emerge tool is not found"))
  391. self.addProgress()
  392. p = PercentProgress(emerge, "--ask=n", "--metadata", part=1, atty=True)
  393. for perc in p.progress():
  394. self.setProgress(perc)
  395. if p.failed():
  396. data = p.read()
  397. with open('/var/log/calculate/failed-metadata-%d.log' % time.time(),
  398. 'w') as f:
  399. f.write(data+p.alldata)
  400. raise UpdateError(_("Failed to update metadata"), addon=data)
  401. return True
  402. def eixUpdate(self):
  403. """
  404. Выполенине eix-update для репозиторием
  405. eix-update выполнятется только для тех репозиториев, которые
  406. обновлялись, если cl_update_eixsync_force==auto, либо
  407. все, если cl_update_eixupdate_force==force
  408. """
  409. eixupdate = getProgPath("/usr/bin/eix-update")
  410. if not eixupdate:
  411. raise UpdateError(_("The Eix tool is not found"))
  412. self.addProgress()
  413. countRep = len(self.clVars.Get('main.cl_portdir_overlay'))+1
  414. p = PercentProgress(eixupdate, "-F", part=countRep or 1, atty=True)
  415. for perc in p.progress():
  416. self.setProgress(perc)
  417. if p.failed():
  418. raise UpdateError(_("Failed to update eix cache"), addon=p.read())
  419. return True
  420. def is_binary_pkg(self, pkg, binary=None):
  421. """
  422. Является ли пакет бинарным
  423. """
  424. if binary:
  425. return True
  426. if 'PN' in pkg and pkg['PN'].endswith('-bin'):
  427. return True
  428. if binary is not None:
  429. return binary
  430. if "binary" in pkg and pkg['binary']:
  431. return True
  432. return False
  433. def _printEmergePackage(self, pkg, binary=False, num=1, max_num=1):
  434. """
  435. Вывод сообщения сборки пакета
  436. """
  437. self.endTask()
  438. _print = self.color_print
  439. one = _print("{0}", num)
  440. two = _print("{0}", max_num)
  441. part = _("({current} of {maximum})").format(current=one,
  442. maximum=two)
  443. _print = _print.foreground(Colors.DEFAULT)
  444. if self.is_binary_pkg(pkg,binary):
  445. _colorprint = _print.foreground(Colors.PURPLE)
  446. else:
  447. _colorprint = _print.foreground(Colors.GREEN)
  448. PackageInformation.add_info(pkg)
  449. name = ""
  450. if pkg.info['DESCRIPTION']:
  451. name = _(pkg.info['DESCRIPTION'])
  452. name = name[:1].upper() + name[1:]
  453. if not name:
  454. name = str(pkg)
  455. self.printSUCCESS(
  456. _("{part} {package}").format(part=part, package=name))
  457. self.startTask(
  458. _("Emerging {package}").format(package=_colorprint(str(pkg))))
  459. def _printInstallPackage(self, pkg, binary=False):
  460. """
  461. Вывод сообщения установки пакета
  462. """
  463. self.endTask()
  464. _print = self.color_print
  465. if self.is_binary_pkg(pkg,binary):
  466. _print = _print.foreground(Colors.PURPLE)
  467. else:
  468. _print = _print.foreground(Colors.GREEN)
  469. #print listDirectory('/var/db/pkg/%s' % pkg['CATEGORY'])
  470. pkg_key = "{CATEGORY}/{PF}".format(**pkg)
  471. if pkg_key in self.update_map:
  472. self.startTask(_("Installing {pkg} [{oldver}]").format(
  473. pkg=_print(str(pkg)), oldver=self.update_map[ pkg_key]))
  474. else:
  475. self.startTask(_("Installing %s") % (_print(str(pkg))))
  476. def _printFetching(self, fn):
  477. """
  478. Вывод сообщения о скачивании
  479. """
  480. self.endTask()
  481. self.startTask(_("Fetching binary packages"))
  482. def _printUninstallPackage(self, pkg, num=1, max_num=1):
  483. """
  484. Вывод сообщения удаления пакета
  485. """
  486. self.endTask()
  487. _print = self.color_print
  488. one = _print("{0}", num)
  489. two = _print("{0}", max_num)
  490. part = _(" ({current} of {maximum})").format(current=one,
  491. maximum=two)
  492. _print = _print.foreground(Colors.LIGHT_RED)
  493. self.startTask(
  494. _("Unmerging{part} {package}").format(part=part,
  495. package=_print(str(pkg))))
  496. def emergelike(self, cmd, *params):
  497. """
  498. Запуск команды, которая подразумевает выполнение emerge
  499. """
  500. cmd_path = getProgPath(cmd)
  501. if not cmd_path:
  502. raise UpdateError(_("Failed to find the %s command") % cmd)
  503. with EmergeParser(
  504. emerge_parser.CommandExecutor(cmd_path, params)) as emerge:
  505. self._startEmerging(emerge)
  506. return True
  507. def revdep_rebuild(self, cmd, *params):
  508. """
  509. Запуск revdep-rebulid
  510. """
  511. cmd_path = getProgPath(cmd)
  512. if not cmd_path:
  513. raise UpdateError(_("Failed to find the %s command") % cmd)
  514. with EmergeParser(
  515. emerge_parser.CommandExecutor(cmd_path, params)) as emerge:
  516. revdep = RevdepPercentBlock(emerge)
  517. self.addProgress()
  518. revdep.add_observer(self.setProgress)
  519. revdep.action = lambda x: (
  520. self.endTask(), self.startTask(_("Assigning files to packages"))
  521. if "Assign" in revdep else None)
  522. self._startEmerging(emerge)
  523. return True
  524. def _display_pretty_package_list(self, pkglist, remove_list=False):
  525. """
  526. Отобразить список пакетов в "удобочитаемом" виде
  527. """
  528. _print = self.color_print
  529. ebuild_color = TextState.Colors.GREEN
  530. binary_color = TextState.Colors.PURPLE
  531. remove_color = TextState.Colors.LIGHT_RED
  532. flag_map = {"updating":
  533. _print.foreground(TextState.Colors.LIGHT_CYAN)("U"),
  534. "reinstall":
  535. _print.foreground(TextState.Colors.YELLOW)("rR"),
  536. "new":
  537. _print.foreground(TextState.Colors.LIGHT_GREEN)("N"),
  538. "newslot":
  539. _print.foreground(TextState.Colors.LIGHT_GREEN)("NS"),
  540. "downgrading": (
  541. _print.foreground(TextState.Colors.LIGHT_CYAN)("U") +
  542. _print.foreground(TextState.Colors.LIGHT_BLUE)("D"))}
  543. for pkg in sorted([PackageInformation.add_info(x) for x in
  544. pkglist],
  545. key=lambda y: y['CATEGORY/PN']):
  546. install_flag = ""
  547. if remove_list:
  548. pkgcolor = _print.foreground(remove_color)
  549. else:
  550. for flag in flag_map:
  551. if pkg[flag]:
  552. install_flag = "(%s) " % flag_map[flag]
  553. break
  554. if self.is_binary_pkg(pkg):
  555. pkgcolor = _print.foreground(binary_color)
  556. else:
  557. pkgcolor = _print.foreground(ebuild_color)
  558. if pkg.info['DESCRIPTION']:
  559. fullname = "%s " % _(pkg.info['DESCRIPTION'])
  560. fullname = fullname[:1].upper()+fullname[1:]
  561. else:
  562. fullname = ""
  563. shortname = pkgcolor("%s-%s" % (pkg["CATEGORY/PN"], pkg["PVR"]))
  564. if "SIZE" in pkg and pkg['SIZE'] and pkg["SIZE"] != "0 kB":
  565. size = " (%s)" % pkg["SIZE"]
  566. else:
  567. size = ""
  568. mult = _print.bold("*")
  569. self.printDefault(
  570. "&nbsp;{mult} {fullname}{flag}{shortname}{size}".format(
  571. mult=mult, fullname=fullname, shortname=shortname, size=size,
  572. flag=install_flag))
  573. def _display_install_package(self, emerge, emergelike=False):
  574. """
  575. Отобразить список устанавливаемых пакетов
  576. """
  577. # подробный список пакетов
  578. _print = self.color_print
  579. if emergelike:
  580. self.printPre(str(emerge.install_packages))
  581. else:
  582. pkglist = emerge.install_packages.list
  583. self.printSUCCESS(_print(
  584. _("Listing packages for installation")))
  585. self._display_pretty_package_list(pkglist)
  586. if emerge.install_packages.remove_list:
  587. self.printSUCCESS(_print(
  588. _("Listing packages for removal")))
  589. self._display_pretty_package_list(
  590. emerge.install_packages.remove_list, remove_list=True)
  591. if len(emerge.install_packages.list) > 0:
  592. install_mess = (_("{count} packages will be installed").format(
  593. count=len(emerge.install_packages.list)) + ", ")
  594. else:
  595. install_mess = ""
  596. if str(emerge.download_size) != "0 kB":
  597. self.printSUCCESS(_("{install}{size} will be downloaded").format(
  598. install=install_mess,
  599. size=str(emerge.download_size)))
  600. def _display_remove_list(self, emerge):
  601. """
  602. Отобразить список удаляемых пакетов
  603. """
  604. # подробный список пакетов
  605. if self.clVars.Get('cl_update_emergelist_set') == 'on':
  606. self.printPre(self._emerge_translate(
  607. emerge.uninstall_packages.verbose_result))
  608. else:
  609. _print = self.color_print
  610. pkglist = emerge.uninstall_packages.list
  611. self.printSUCCESS(_print.bold(
  612. _("Listing packages for removal")))
  613. self._display_pretty_package_list(pkglist, remove_list=True)
  614. def getCacheOnWorld(self, params, packages, check=False):
  615. """
  616. Получить список обновляемых пакетов @world из кэша
  617. """
  618. if "@world" in packages:
  619. from calculate.update.utils.cl_update import ClUpdateAction
  620. elog = EmergeLog(
  621. EmergeLogNamedTask(ClUpdateAction.log_names['premerge']))
  622. if check and (elog.list or elog.remove_list):
  623. self.emerge_cache.drop_cache(
  624. "Some packages was installed or removed")
  625. return params, packages
  626. installed_pkgs = elog.list
  627. new_packages = self.emerge_cache.get_cached_package_list()
  628. if new_packages is not None:
  629. return "-1O", ["=%s" % x for x in new_packages
  630. if not str(x) in installed_pkgs]
  631. return params, packages
  632. def updateCache(self, pkg_list):
  633. """
  634. Обновить кэш. Оставить отметку в emerge.log о том, выполнено действие
  635. premerge
  636. """
  637. self.emerge_cache.set_cache(pkg_list)
  638. from calculate.update.utils.cl_update import ClUpdateAction
  639. elog = EmergeLog(
  640. EmergeLogNamedTask(ClUpdateAction.log_names['premerge']))
  641. elog.mark_end_task(),
  642. def mark_schedule(self):
  643. """
  644. Установить отметку о запуске запланированной проверки
  645. """
  646. SystemIni().setVar('update', {'last_check': str(int(time.time()))})
  647. def premerge(self, param, *packages):
  648. """
  649. Вывести информацию об обновлении
  650. """
  651. deo = self.clVars.Get('cl_emerge_default_opts')
  652. param, packages = self.getCacheOnWorld(param, packages, check=True)
  653. param = [param, "-pv"]
  654. if not packages:
  655. self.printSUCCESS(_("Installed packages are up to date"))
  656. self.set_need_update(False)
  657. return True
  658. with EmergeParser(EmergeCommand(list(packages), emerge_default_opts=deo,
  659. extra_params=param)) as emerge:
  660. try:
  661. emerge.run()
  662. if "@world" in packages:
  663. if emerge.install_packages.remove_list:
  664. self.emerge_cache.drop_cache(
  665. "List has packages for remove")
  666. else:
  667. self.updateCache(emerge.install_packages.list)
  668. if not emerge.install_packages.list:
  669. self.printSUCCESS(_("The system is up to date"))
  670. self.set_need_update(False)
  671. return True
  672. emergelike = self.clVars.Get('cl_update_emergelist_set') == 'on'
  673. self._display_install_package(emerge, emergelike)
  674. except EmergeError:
  675. self.set_need_update(False)
  676. self.emerge_cache.drop_cache("Emerge error")
  677. self._display_install_package(emerge, emergelike=True)
  678. self._display_error(emerge.prepare_error)
  679. raise
  680. if self.clVars.Get('cl_update_pretend_set') == 'on':
  681. # установить кэш: есть обновления
  682. self.set_need_update()
  683. return True
  684. self.set_need_update(False)
  685. answer = self.askConfirm(
  686. _("Would you like to merge these packages?"), "yes")
  687. if answer == "no":
  688. raise KeyboardInterrupt
  689. return "yes"
  690. return True
  691. def set_need_update(self, val=True):
  692. """
  693. Установить флаг: есть обновления
  694. """
  695. UpdateInfo.set_update_ready(val)
  696. return True
  697. def _emerge_translate(self, s):
  698. """
  699. Перевести текст из emerge
  700. """
  701. return RegexpLocalization('cl_emerge').translate(str(s))
  702. def setUpToDateCache(self):
  703. """
  704. Установить кэш - "нет пакетов для обновления"
  705. """
  706. self.updateCache(PackageList([]))
  707. return True
  708. def _startEmerging(self, emerge):
  709. """
  710. Настроить и выполнить emerge
  711. """
  712. if emerge.install_packages and emerge.install_packages.list:
  713. for pkg in emerge.install_packages.list:
  714. rv = pkg.get('REPLACING_VERSIONS', '')
  715. if rv:
  716. self.update_map["{CATEGORY}/{PF}".format(**pkg)] = \
  717. rv.partition(":")[0]
  718. emerge.command.send("yes\n")
  719. emerge.emerging.add_observer(self._printEmergePackage)
  720. emerge.installing.add_observer(self._printInstallPackage)
  721. emerge.uninstalling.add_observer(self._printUninstallPackage)
  722. emerge.fetching.add_observer(self._printFetching)
  723. try:
  724. emerge.run()
  725. except EmergeError:
  726. self.emerge_cache.drop_cache("Emerge error")
  727. if emerge.emerging_error:
  728. self._display_error(emerge.emerging_error.log)
  729. else:
  730. self._display_error(emerge.prepare_error)
  731. raise
  732. def _display_error(self, error):
  733. lines_num = int(self.clVars.Get('update.cl_update_lines_limit'))
  734. error = "<br/>".join(str(error).split('<br/>')[-lines_num:])
  735. self.printPre(self._emerge_translate(error))
  736. def emerge(self, param, *packages):
  737. """
  738. Выполнить сборку пакета
  739. """
  740. deo = self.clVars.Get('cl_emerge_default_opts')
  741. if not packages:
  742. packages = [param]
  743. extra_params = None
  744. else:
  745. param, packages = self.getCacheOnWorld(param, packages)
  746. if not packages:
  747. return True
  748. extra_params = [param]
  749. with EmergeParser(EmergeCommand(list(packages), emerge_default_opts=deo,
  750. extra_params=extra_params)) as emerge:
  751. try:
  752. emerge.question.action = lambda x: False
  753. emerge.run()
  754. if not emerge.install_packages.list:
  755. return True
  756. except EmergeError:
  757. self.emerge_cache.drop_cache("Emerge error")
  758. self._display_error(emerge.prepare_error)
  759. raise
  760. self._startEmerging(emerge)
  761. return True
  762. def depclean(self):
  763. """
  764. Выполнить очистку системы от лишних пакетов
  765. """
  766. deo = self.clVars.Get('cl_emerge_default_opts')
  767. emerge = None
  768. try:
  769. emerge = EmergeParser(EmergeCommand(["--depclean"],
  770. emerge_default_opts=deo))
  771. outdated_kernel = False
  772. try:
  773. emerge.question.action = lambda x: False
  774. emerge.run()
  775. if not emerge.uninstall_packages.list:
  776. UpdateInfo(self.clVars).outdated_kernel = False
  777. return True
  778. kernel_pkg = self.clVars.Get('cl_update_kernel_pkg')
  779. if any(("%s-%s" % (x['CATEGORY/PN'], x['PVR'])) == kernel_pkg
  780. for x in emerge.uninstall_packages.list):
  781. pkglist = [
  782. "=%s-%s" % (x['CATEGORY/PN'], x['PVR']) for x in
  783. emerge.uninstall_packages.list
  784. if ("%s-%s" % (x['CATEGORY/PN'],
  785. x['PVR'])) != kernel_pkg]
  786. emerge.command.send('n\n')
  787. emerge.close()
  788. emerge = None
  789. if not pkglist:
  790. UpdateInfo(self.clVars).outdated_kernel = True
  791. return True
  792. emerge = EmergeParser(
  793. EmergeCommand(pkglist,
  794. extra_params=["--unmerge", '--ask=y'],
  795. emerge_default_opts=deo))
  796. emerge.question.action = lambda x: False
  797. emerge.run()
  798. outdated_kernel = True
  799. else:
  800. outdated_kernel = False
  801. self._display_remove_list(emerge)
  802. except EmergeError:
  803. self._display_error(emerge.prepare_error)
  804. raise
  805. if (self.askConfirm(
  806. _("Would you like to unmerge these packages?")) != 'yes'):
  807. return False
  808. UpdateInfo(self.clVars).outdated_kernel = outdated_kernel
  809. self._startEmerging(emerge)
  810. finally:
  811. if emerge:
  812. emerge.close()
  813. return True
  814. def update_task(self, task_name):
  815. """
  816. Декоратор для добавления меток запуска и останова задачи
  817. """
  818. def decor(f):
  819. def wrapper(*args, **kwargs):
  820. logger = EmergeLog(EmergeLogNamedTask(task_name))
  821. logger.mark_begin_task()
  822. ret = f(*args, **kwargs)
  823. if ret:
  824. logger.mark_end_task()
  825. return ret
  826. return wrapper
  827. return decor
  828. def migrateCacheRepository(self, url, branch):
  829. rep_set = self.clVars.Get('cl_update_profile_storage')
  830. rep = rep_set.get_repository(url, branch)
  831. if rep:
  832. rep.storage = rep_set.storages[0]
  833. self.clVars.Invalidate('cl_update_profile_storage')
  834. return True
  835. def reconfigureProfileVars(self):
  836. """
  837. Синхронизировать репозитории
  838. """
  839. dv = self.clVars
  840. profile_dv = dv.Get('cl_update_profile_datavars')
  841. try:
  842. if not profile_dv:
  843. raise UpdateError(
  844. _("Failed to use the new profile. Try again."))
  845. for var_name in ('cl_update_rep_rev',
  846. 'cl_update_rep_path',
  847. 'cl_update_rep_url',
  848. 'cl_update_rep_name',
  849. 'cl_update_branch_name',
  850. 'cl_profile_system',
  851. 'cl_update_layman_storage',
  852. 'cl_update_rep'):
  853. dv.Set(var_name, profile_dv.Get(var_name), force=True)
  854. except DataVarsError:
  855. raise UpdateError(_("Wrong profile"))
  856. return True
  857. def setProfile(self):
  858. profile = self.clVars.Select('cl_profile_path',
  859. where='cl_profile_shortname',
  860. eq=self.clVars.Get('cl_update_profile_system'), limit=1)
  861. if not profile:
  862. raise UpdateError(_("Failed to determine profile %s") %
  863. self.clVars.Get('cl_update_profile_system'))
  864. profile_path = path.relpath(profile, '/etc/portage')
  865. try:
  866. for rm_fn in filter(path.exists,
  867. ('/etc/make.profile', '/etc/portage/make.profile')):
  868. os.unlink(rm_fn)
  869. os.symlink(profile_path, '/etc/portage/make.profile')
  870. except (OSError,IOError) as e:
  871. raise UpdateError(_("Failed to set the profile: %s")%str(e))
  872. return True
  873. def applyProfileTemplates(self,useClt=None,cltFilter=False,useDispatch=True):
  874. """
  875. Наложить шаблоны из профиля
  876. """
  877. from calculate.lib.cl_template import (Template,TemplatesError,
  878. ProgressTemplate)
  879. dv = DataVarsUpdate()
  880. try:
  881. dv.importUpdate()
  882. dv.flIniFile()
  883. dv.Set('cl_action','merge',force=True)
  884. dv.Set('cl_templates_locate', self.clVars.Get('cl_update_templates_locate'))
  885. dv.Set("cl_chroot_path",'/', True)
  886. dv.Set("cl_root_path",'/', True)
  887. for copyvar in ("cl_dispatch_conf", "cl_verbose_set"):
  888. dv.Set(copyvar,self.clVars.Get(copyvar),True)
  889. # определение каталогов содержащих шаблоны
  890. useClt = useClt in ("on",True)
  891. self.addProgress()
  892. nullProgress = lambda *args,**kw:None
  893. dispatch = self.dispatchConf if useDispatch else None
  894. clTempl = ProgressTemplate(nullProgress, dv,
  895. cltObj=useClt,
  896. cltFilter=cltFilter,
  897. printSUCCESS=self.printSUCCESS,
  898. printWARNING=self.printWARNING,
  899. askConfirm=self.askConfirm,
  900. dispatchConf=dispatch,
  901. printERROR=self.printERROR)
  902. try:
  903. clTempl.applyTemplates()
  904. if clTempl.hasError():
  905. if clTempl.getError():
  906. raise TemplatesError(clTempl.getError())
  907. finally:
  908. if clTempl:
  909. if clTempl.cltObj:
  910. clTempl.cltObj.closeFiles()
  911. clTempl.closeFiles()
  912. finally:
  913. dv.close()
  914. return True
  915. def cleanpkg(self):
  916. """
  917. Очистить distfiles и pkgdir от устаревших пакетов
  918. """
  919. portdirs = ([self.clVars.Get('cl_portdir')] +
  920. self.clVars.Get('cl_portdir_overlay'))
  921. pkgfiles = get_packages_files_directory(*portdirs)
  922. distdirfiles = get_manifest_files_directory(*portdirs)
  923. distdir = self.clVars.Get('install.cl_distfiles_path')
  924. pkgdir = self.clVars.Get('cl_pkgdir')
  925. skip_files = ["/metadata.dtd", "/Packages"]
  926. try:
  927. if self.clVars.Get('client.os_remote_auth'):
  928. skip_files += ['portage_lockfile']
  929. except DataVarsError:
  930. pass
  931. logger = log("update_cleanpkg.log",
  932. filename="/var/log/calculate/update_cleanpkg.log",
  933. formatter="%(asctime)s - %(clean)s - %(message)s")
  934. for cleantype, filelist in (("packages",
  935. get_remove_list(pkgdir, list(pkgfiles), depth=4)),
  936. ("distfiles",
  937. get_remove_list(distdir, list(distdirfiles), depth=1))):
  938. removelist = []
  939. for fn in filelist:
  940. try:
  941. if not any(fn.endswith(x) for x in skip_files):
  942. os.unlink(fn)
  943. removelist.append(path.basename(fn))
  944. except OSError:
  945. pass
  946. removelist_str = ",".join(removelist)
  947. if removelist_str:
  948. logger.info(removelist_str, extra={'clean': cleantype})
  949. if cleantype == "packages":
  950. try:
  951. os.system('/usr/sbin/emaint binhost -f &>/dev/null')
  952. for dn in listDirectory(pkgdir, fullPath=True):
  953. if path.isdir(dn) and not listDirectory(dn):
  954. os.rmdir(dn)
  955. except OSError:
  956. pass
  957. return True