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.

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