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.

850 lines
27 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015-2016 Mir Calculate. 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 hashlib
  16. import os
  17. from os import path
  18. import re
  19. import sys
  20. from calculate.lib.utils.colortext.palette import TextState
  21. from calculate.lib.utils.tools import ignore
  22. from calculate.lib.utils.git import Git, GitError
  23. from calculate.lib.utils.portage import (EmergePackage, PackageList,
  24. EmergeUpdateInfo,
  25. EmergeRemoveInfo)
  26. Colors = TextState.Colors
  27. import pexpect
  28. from calculate.lib.utils.files import (getProgPath, readLinesFile,
  29. listDirectory,
  30. writeFile, readFile)
  31. from calculate.lib.utils.colortext.output import XmlOutput
  32. from calculate.lib.utils.colortext.converter import (ConsoleCodes256Converter,
  33. XmlConverter)
  34. from calculate.lib.cl_log import log
  35. from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate, _
  36. setLocalTranslate('cl_update3', sys.modules[__name__])
  37. __ = getLazyLocalTranslate(_)
  38. linux_term_env = {'TERM': 'linux'}
  39. class EmergeError(Exception):
  40. """
  41. Ошибка при сборке пакетов
  42. """
  43. class EmergeNeedRootError(EmergeError):
  44. pass
  45. class CommandExecutor(object):
  46. """
  47. Запуск программы для объекта Emerge
  48. """
  49. logfile = '/var/log/calculate/lastcommand.log'
  50. def __init__(self, cmd, params, env=None, cwd=None, logfile=None):
  51. self.cwd = cwd
  52. self.env = env or dict(os.environ)
  53. self.env.update({'EINFO_QUIET': 'NO'})
  54. self.env.update(linux_term_env)
  55. self.cmd = cmd
  56. self.params = params
  57. self.child = None
  58. if logfile:
  59. self.logfile = logfile
  60. def get_command(self):
  61. return [self.cmd] + list(self.params)
  62. def execute(self):
  63. if self.child is None:
  64. command_data = self.get_command()
  65. self.child = pexpect.spawn(command_data[0], command_data[1:],
  66. logfile=open(self.logfile, 'w'),
  67. env=self.env, cwd=self.cwd, timeout=None)
  68. return self.child
  69. def close(self):
  70. if self.child is not None:
  71. self.child.close()
  72. self.child = None
  73. def success(self):
  74. if self.child:
  75. if self.child.isalive():
  76. self.child.wait()
  77. return self.child.exitstatus == 0
  78. return False
  79. def failed(self):
  80. return not self.success()
  81. def send(self, s):
  82. if self.child:
  83. self.child.send(s)
  84. class EmergeCommand(CommandExecutor):
  85. """
  86. Запуск emerge для последующего анализирования
  87. """
  88. # параметры по умолчанию
  89. default_params = ["-av", "--color=y", "--nospinner"]
  90. emerge_cmd = getProgPath("/usr/bin/emerge")
  91. def __init__(self, packages, extra_params=None, env=None, cwd=None,
  92. logfile=None, emerge_default_opts=None, use=""):
  93. extra_params = extra_params or []
  94. if env is None:
  95. if emerge_default_opts is None:
  96. env = {'CLEAN_DELAY': '0'}
  97. else:
  98. env = {
  99. 'CLEAN_DELAY': '0',
  100. 'EMERGE_DEFAULT_OPTS': re.sub(
  101. r'(?:^|\s)(--columns)(?=\s|$)', '',
  102. emerge_default_opts)
  103. }
  104. if use:
  105. env["USE"] = use
  106. env.update(os.environ)
  107. params = self.default_params + extra_params + packages
  108. super(EmergeCommand, self).__init__(self.emerge_cmd, params=params,
  109. env=env, cwd=cwd, logfile=logfile)
  110. def Chroot(chroot_path, obj):
  111. """
  112. Преобразовать команду (экземпляр объекта) в chroot
  113. :param obj: экземпляр команды
  114. :param chroot_path: путь для chroot
  115. :return:
  116. """
  117. old_get_command = obj.get_command
  118. def get_command():
  119. chrootCmd = '/usr/bin/chroot'
  120. bashCmd = '/bin/bash'
  121. bash_command = (
  122. "env-update &>/dev/null;"
  123. "source /etc/profile &>/dev/null;"
  124. "{cmd}".format(cmd=" ".join(old_get_command())))
  125. return [chrootCmd, chroot_path, bashCmd, "-c", bash_command]
  126. obj.get_command = get_command
  127. return obj
  128. def Linux32(obj):
  129. """
  130. Преобразовать команду (экземпляр объекта) в вызов под linux32
  131. :param obj: экземпляр команды
  132. :return:
  133. """
  134. old_get_command = obj.get_command
  135. def get_command():
  136. return ["/usr/bin/linux32"] + old_get_command()
  137. obj.get_command = get_command
  138. return obj
  139. class InfoBlockInterface(object):
  140. """
  141. Интерфейс для информационного блока
  142. """
  143. action = None
  144. token = None
  145. result = None
  146. text_converter = ConsoleCodes256Converter(XmlOutput())
  147. def get_block(self, child):
  148. pass
  149. def add_element(self, element):
  150. """
  151. :type element: InfoBlockInterface
  152. """
  153. pass
  154. class EmergeInformationBlock(InfoBlockInterface):
  155. _color_block = "(?:\033\[[^m]+?m)?"
  156. _new_line = "(?:\r*\n)"
  157. end_token = ["\n"]
  158. re_block = None
  159. re_match_type = type(re.match("", ""))
  160. re_type = type(re.compile(""))
  161. def __init__(self, parent):
  162. """
  163. :type parent: InfoBlockInterface
  164. """
  165. self.result = None
  166. self.text_converter = parent.text_converter
  167. self.parent = parent
  168. self.parent.add_element(self)
  169. self.children = []
  170. def add_element(self, element):
  171. """
  172. :type element: InfoBlockInterface
  173. """
  174. self.children.append(element)
  175. def __str__(self):
  176. if type(self.result) == self.re_match_type:
  177. return self.result.group()
  178. else:
  179. return self.result or ""
  180. def __nonzero__(self):
  181. return bool(self.result)
  182. def __len__(self):
  183. if self.result is None:
  184. return 0
  185. else:
  186. return len(self.result)
  187. def __contains__(self, item):
  188. if self.result is None:
  189. return False
  190. else:
  191. return item in str(self)
  192. def _get_text(self, result):
  193. """
  194. Получить результат из регулярки и преобразовать его через self.converter
  195. """
  196. if result:
  197. return self.text_converter.transform(result.rstrip())
  198. return ""
  199. def get_block(self, child):
  200. try:
  201. token = child.match
  202. if type(self.end_token) == self.re_type:
  203. child.expect(self.end_token)
  204. match = child.match.group()
  205. else:
  206. child.expect_exact(self.end_token)
  207. match = child.match
  208. self.get_data(self.re_block.search(
  209. token + child.before + match))
  210. except pexpect.EOF:
  211. child.buffer = "".join(
  212. [x for x in (child.before, child.after, child.buffer)
  213. if type(x) == str])
  214. def get_data(self, match):
  215. self.result = self._get_text(match.group(1))
  216. class InstallPackagesBlock(EmergeInformationBlock):
  217. """
  218. Блок emerge содержащий список пакетов для установки
  219. """
  220. list = PackageList([])
  221. remove_list = PackageList([])
  222. block_packages = False
  223. _new_line = EmergeInformationBlock._new_line
  224. _color_block = EmergeInformationBlock._color_block
  225. token = "\n["
  226. end_token = ["\r\n\r", "\n\n"]
  227. re_block = re.compile(r"((?:^\[.*?{nl})+)".format(nl=_new_line),
  228. re.MULTILINE)
  229. re_blocks = re.compile(r"\[{c}blocks{c} {c}b".format(c=_color_block))
  230. def get_data(self, match):
  231. super(InstallPackagesBlock, self).get_data(match)
  232. list_block = XmlConverter().transform(self.result).split('\n')
  233. self.list = PackageList(map(EmergeUpdateInfo, list_block))
  234. self.remove_list = PackageList(map(EmergeRemoveInfo, list_block))
  235. self.block_packages = any(self.re_blocks.search(x) for x in list_block)
  236. class UninstallPackagesBlock(EmergeInformationBlock):
  237. """
  238. Блок emerge содержащий список удаляемых пакетов
  239. """
  240. list = PackageList([])
  241. verbose_result = ""
  242. _new_line = EmergeInformationBlock._new_line
  243. _color_block = EmergeInformationBlock._color_block
  244. token = ["Calculating removal order",
  245. "These are the packages that would be unmerged"]
  246. end_token = re.compile("All selected packages:.*\n")
  247. re_block = re.compile(
  248. r"(?:{token}).*?{nl}(.*){nl}All selected packages: (.*?){nl}".
  249. format(token="|".join(token),
  250. nl=_new_line, c=_color_block), re.DOTALL)
  251. def get_data(self, match):
  252. re_clean = re.compile(
  253. "^.*?({token}).*?{c}{nl}".format(token="|".join(self.token),
  254. nl=self._new_line,
  255. c=self._color_block), re.DOTALL)
  256. verbose_result = re_clean.sub("", match.group(1))
  257. self.verbose_result = self._get_text(verbose_result)
  258. self.result = self._get_text(match.group(2))
  259. list_block = XmlConverter().transform(self.result).split()
  260. self.list = PackageList(map(EmergePackage, list_block))
  261. class GroupEmergeInformationBlock(EmergeInformationBlock):
  262. """
  263. Группа блоков
  264. """
  265. def get_block(self, child):
  266. self.children_get_block(child)
  267. self.result = True
  268. def children_get_block(self, child):
  269. for block in self.children:
  270. block.get_block(child)
  271. def children_action(self, child):
  272. for block in (x for x in self.children if x.result and x.action):
  273. if block.action(child) is False:
  274. return False
  275. def action(self, child):
  276. self.children_action(child)
  277. return False
  278. class FinishEmergeGroup(GroupEmergeInformationBlock):
  279. """
  280. Блок завершения команды
  281. """
  282. token = pexpect.EOF
  283. # регуляреное выражение, определяющее содержит ли блок
  284. # сообщения об ошибках
  285. re_failed = re.compile(
  286. r"Fetch instructions for \S+:|"
  287. r"The following.*are necessary to proceed|"
  288. r"!!! Multiple package .* slot have been pulled|"
  289. r"no ebuilds to satisfy|"
  290. r"Dependencies could not be completely resolved due to",
  291. re.MULTILINE)
  292. def get_block(self, child):
  293. if child.isalive():
  294. child.wait()
  295. if child.exitstatus != 0 or self.re_failed.search(child.before):
  296. self.children_get_block(child)
  297. else:
  298. self.result = True
  299. class PrepareErrorBlock(EmergeInformationBlock):
  300. """
  301. Блок информации с ошибками при получении списка устанавливаемых пакетов
  302. """
  303. token = None
  304. re_drop = re.compile("news items need reading|"
  305. "Use eselect news|"
  306. "Calculating dependencies|"
  307. "to read news items|"
  308. "Local copy of remote index is up-to-date|"
  309. "These are the packages that would be merged|"
  310. "Process finished with exit code")
  311. re_multi_empty_line = re.compile("(?:<br/>){3,}", re.DOTALL)
  312. re_strip_br = re.compile("^(?:<br/>)+|(?:<br/>)+$", re.DOTALL)
  313. def remove_needless_data(self, data):
  314. return "\n".join([x for x in data.split('\n')
  315. if not self.re_drop.search(x)])
  316. def strip_br(self, data):
  317. return self.re_strip_br.sub(
  318. "",
  319. self.re_multi_empty_line.sub("<br/><br/>", data))
  320. def get_block(self, child):
  321. self.result = self.strip_br(
  322. self._get_text(self.remove_needless_data(child.before)))
  323. def action(self, child):
  324. raise EmergeError(_("Emerge failed"))
  325. class DownloadSizeBlock(EmergeInformationBlock):
  326. """
  327. Размер скачиваемых обновлений
  328. """
  329. token = "Size of downloads:"
  330. re_block = re.compile(r"Size of downloads:\s(\S+\s\S+)")
  331. def __str__(self):
  332. if self.result:
  333. return self.result
  334. else:
  335. return "0 kB"
  336. class SkippedPackagesBlock(EmergeInformationBlock):
  337. """
  338. Размер скачиваемых обновлений
  339. """
  340. token = "The following update has been skipped"
  341. end_token = ["For more information, see the MASKED"]
  342. re_block = re.compile(
  343. r"(The following update has.*?)(?=For more information)", re.S)
  344. def __str__(self):
  345. if self.result:
  346. return self.result
  347. else:
  348. return ""
  349. class QuestionGroup(GroupEmergeInformationBlock):
  350. """
  351. Группа блоков разбора вопросов от emerge
  352. """
  353. token = "Would you like"
  354. end_token = ["]", "\n"]
  355. _color_block = EmergeInformationBlock._color_block
  356. re_block = re.compile(
  357. "(Would you.*)\[{c}Yes{c}/{c}No{c}".format(c=_color_block))
  358. def get_block(self, child):
  359. try:
  360. before = child.before
  361. token = child.match
  362. if type(self.end_token) == self.re_type:
  363. child.expect(self.end_token)
  364. match = child.match.group()
  365. else:
  366. child.expect_exact(self.end_token)
  367. match = child.match
  368. data = token + child.before + match
  369. child.before = before
  370. for block in self.children:
  371. child.match = re.search(block.token, data)
  372. block.get_block(child)
  373. if block.result:
  374. break
  375. except pexpect.EOF:
  376. child.buffer = "".join(
  377. [x for x in (child.before, child.after, child.buffer)
  378. if type(x) == str])
  379. class QuestionChangeConfigBlock(GroupEmergeInformationBlock):
  380. """
  381. Вопрос об изменении конфигурационных файлов
  382. """
  383. token = "Would you like to add these changes to your config files"
  384. def get_block(self, child):
  385. if child.match:
  386. self.result = self.token
  387. self.children_get_block(child)
  388. def action(self, child):
  389. if self.result:
  390. child.send("no\n")
  391. if child.isalive():
  392. child.wait()
  393. self.children_action(child)
  394. class QuestionBlock(EmergeInformationBlock):
  395. """
  396. Блок вопроса
  397. """
  398. default_answer = "yes"
  399. token = "Would you"
  400. def get_block(self, child):
  401. if child.match:
  402. self.result = self.token
  403. def action(self, child):
  404. if self.result:
  405. child.send("%s\n" % self.default_answer)
  406. return False
  407. class NeedRootBlock(EmergeInformationBlock):
  408. """
  409. Пользователь не явеляется root
  410. """
  411. token = "This action requires superuser access"
  412. def get_data(self, child):
  413. self.result = True
  414. def action(self, child):
  415. raise EmergeNeedRootError(_("This action requires superuser access"))
  416. class NotifierInformationBlock(EmergeInformationBlock):
  417. """
  418. Информационный блок поддерживающий observing
  419. """
  420. def __init__(self, parent):
  421. super(NotifierInformationBlock, self).__init__(parent)
  422. self.observers = []
  423. def get_data(self, match):
  424. self.result = match
  425. def add_observer(self, f):
  426. self.observers.append(f)
  427. def clear_observers(self):
  428. self.observers = []
  429. def remove_observer(self, f):
  430. if f in self.observers:
  431. self.observers.remove(f)
  432. def notify(self, observer, groups):
  433. observer(groups)
  434. def action(self, child):
  435. if self.result and self.observers:
  436. groups = self.result.groups()
  437. for observer in self.observers:
  438. self.notify(observer, groups)
  439. class EmergingPackage(NotifierInformationBlock):
  440. """
  441. Запуск устанавливаемого пакета
  442. ObserverFunc: (package, num=number, max_num=number, binary=binary)
  443. """
  444. _color_block = EmergeInformationBlock._color_block
  445. token = ">>> Emerging "
  446. re_block = re.compile(
  447. "Emerging (binary )?\({c}(\d+){c} "
  448. "of {c}(\d+){c}\) {c}([^\s\033]+){c}".format(c=_color_block))
  449. def notify(self, observer, groups):
  450. observer(EmergePackage(groups[3]), num=groups[1], max_num=groups[2],
  451. binary=bool(groups[0]))
  452. class UnemergingPackage(NotifierInformationBlock):
  453. """
  454. Запуск устанавливаемого пакета
  455. ObserverFunc: (package, num=number, max_num=number)
  456. """
  457. _color_block = EmergeInformationBlock._color_block
  458. token = ">>> Unmerging"
  459. re_block = re.compile(
  460. r"Unmerging (?:\({c}(\d+){c} "
  461. r"of {c}(\d+){c}\) )?(\S+)\.\.\.".format(c=_color_block))
  462. def notify(self, observer, groups):
  463. observer(EmergePackage(groups[2]), num=groups[0], max_num=groups[1])
  464. class FetchingTarball(NotifierInformationBlock):
  465. """
  466. Происходит скачивание архивов
  467. """
  468. token = "Saving to:"
  469. re_block = re.compile("Saving to:\s*[‘'](\S+)?['’]")
  470. def notify(self, observer, groups):
  471. observer(groups[0])
  472. class InstallingPackage(NotifierInformationBlock):
  473. """
  474. Запуск устанавливаемого пакета
  475. ObserverFunc: (package, binary=binary)
  476. """
  477. _color_block = EmergeInformationBlock._color_block
  478. binary = None
  479. token = ">>> Installing "
  480. re_block = re.compile(
  481. "Installing \({c}(\d+){c} "
  482. "of {c}(\d+){c}\) {c}([^\s\033]+){c}".format(c=_color_block))
  483. def notify(self, observer, groups):
  484. strpkg = str(EmergePackage(groups[2]))
  485. binary = bool(self.binary and strpkg in self.binary)
  486. observer(EmergePackage(groups[2]), binary=binary)
  487. def mark_binary(self, package):
  488. if self.binary is None:
  489. self.binary = []
  490. self.binary.append(str(package))
  491. class EmergeingErrorBlock(EmergeInformationBlock):
  492. """
  493. Блок содержит информацию об ошибке во время сборки пакета
  494. """
  495. token = ["* ERROR: ", " * \033[39;49;00mERROR: "]
  496. end_token = "Working directory:"
  497. re_block = re.compile("ERROR: (\S*) failed \([^)]+\).*?"
  498. "The complete build log is located at '([^']+)",
  499. re.DOTALL)
  500. package = ""
  501. def get_data(self, match):
  502. self.result = self._get_text(match.group(2).rstrip())
  503. self.package = match.group(1)
  504. @property
  505. def log(self):
  506. return self.text_converter.transform(readFile(self.result))
  507. def action(self, child):
  508. raise EmergeError(_("Failed to emerge %s") % self.package)
  509. class RevdepPercentBlock(NotifierInformationBlock):
  510. """
  511. Блок определния статуса revdep-rebuild
  512. """
  513. token = "Collecting system binaries"
  514. end_token = [re.compile("Assigning files to packages|"
  515. "All prepared. Starting rebuild")]
  516. re_block = re.compile("\[\s(\d+)%\s\]")
  517. action = None
  518. def notify(self, observer, groups):
  519. percent = int(groups[0])
  520. observer(percent)
  521. def get_block(self, child):
  522. expect_result = [self.re_block]+self.end_token
  523. try:
  524. while True:
  525. index = child.expect(expect_result)
  526. if index == 0:
  527. for observer in self.observers:
  528. self.notify(observer, child.match.groups())
  529. else:
  530. self.result = child.match
  531. break
  532. except pexpect.EOF:
  533. self.result = ""
  534. class EmergeParser(InfoBlockInterface):
  535. """
  536. Парсер вывода emerge
  537. """
  538. def __init__(self, command, run=False):
  539. self.command = command
  540. self.elements = {}
  541. self.install_packages = InstallPackagesBlock(self)
  542. self.uninstall_packages = UninstallPackagesBlock(self)
  543. self.question_group = QuestionGroup(self)
  544. self.change_config_question = QuestionChangeConfigBlock(
  545. self.question_group)
  546. self.question = QuestionBlock(self.question_group)
  547. self.finish_block = FinishEmergeGroup(self)
  548. self.need_root = NeedRootBlock(self)
  549. self.prepare_error = PrepareErrorBlock(self.finish_block)
  550. self.change_config_question.add_element(self.prepare_error)
  551. self.download_size = DownloadSizeBlock(self)
  552. self.skipped_packages = SkippedPackagesBlock(self)
  553. self.emerging_error = EmergeingErrorBlock(self)
  554. self.installing = InstallingPackage(self)
  555. self.uninstalling = UnemergingPackage(self)
  556. self.emerging = EmergingPackage(self)
  557. self.fetching = FetchingTarball(self)
  558. self.emerging.add_observer(self.mark_binary)
  559. self.emerging.add_observer(self.skip_fetching)
  560. if run:
  561. self.run()
  562. def mark_binary(self, package, binary=False, **kw):
  563. if binary:
  564. self.installing.mark_binary(package)
  565. def skip_fetching(self, *argv, **kw):
  566. self.fetching.action = lambda child: None
  567. def add_element(self, element):
  568. """
  569. :type element: InfoBlockInterface
  570. """
  571. if element.token:
  572. if type(element.token) == list:
  573. for token in element.token:
  574. self.elements[token] = element
  575. else:
  576. self.elements[element.token] = element
  577. def run(self):
  578. """
  579. Запустить команду
  580. """
  581. child = self.command.execute()
  582. while True:
  583. index = child.expect_exact(self.elements.keys())
  584. element = self.elements.values()[index]
  585. element.get_block(child)
  586. if element.action:
  587. if element.action(child) is False:
  588. break
  589. def close(self):
  590. self.command.close()
  591. def __enter__(self):
  592. return self
  593. def __exit__(self, *exc_info):
  594. self.close()
  595. class MtimeCheckvalue(object):
  596. def __init__(self, *fname):
  597. self.fname = fname
  598. def value_func(self, fn):
  599. return str(int(os.stat(fn).st_mtime))
  600. def get_check_values(self, file_list):
  601. for fn in file_list:
  602. if path.exists(fn) and not path.isdir(fn):
  603. yield fn, self.value_func(fn)
  604. else:
  605. for k, v in self.get_check_values(
  606. listDirectory(fn, fullPath=True)):
  607. yield k, v
  608. def checkvalues(self):
  609. return self.get_check_values(self.fname)
  610. class Md5Checkvalue(MtimeCheckvalue):
  611. def value_func(self, fn):
  612. return hashlib.md5(readFile(fn)).hexdigest()
  613. class GitCheckvalue(object):
  614. def __init__(self, rpath):
  615. self.rpath = rpath
  616. self.git = Git()
  617. def checkvalues(self):
  618. with ignore(GitError):
  619. if self.git.is_git(self.rpath):
  620. yield self.rpath, Git().getCurrentCommit(self.rpath)
  621. class EmergeCache(object):
  622. """
  623. Кэш пакетов
  624. """
  625. cache_file = '/var/lib/calculate/calculate-update/world.cache'
  626. # список файлов проверяемый по mtime на изменения
  627. check_list = [MtimeCheckvalue('/etc/make.conf',
  628. '/etc/portage',
  629. '/etc/make.profile'),
  630. Md5Checkvalue('/var/lib/portage/world',
  631. '/var/lib/portage/world_sets')]
  632. logger = log("emerge-cache",
  633. filename="/var/log/calculate/emerge-cache.log",
  634. formatter="%(asctime)s - %(levelname)s - %(message)s")
  635. def __init__(self):
  636. self.files_control_values = {}
  637. self.pkg_list = PackageList([])
  638. def set_cache(self, package_list):
  639. """
  640. Установить в кэш список пакетов
  641. """
  642. with writeFile(self.cache_file) as f:
  643. for fn, val in self.get_control_values().items():
  644. f.write("{fn}={val}\n".format(fn=fn, val=val))
  645. f.write('\n')
  646. for pkg in package_list:
  647. f.write("%s\n" % str(pkg))
  648. self.logger.info("Setting cache (%d packages)" % len(package_list))
  649. def drop_cache(self, reason=None):
  650. if path.exists(self.cache_file):
  651. with ignore(OSError):
  652. os.unlink(self.cache_file)
  653. self.logger.info("Droping cache. Reason: %s" % reason)
  654. else:
  655. self.logger.info("Droping empty cache. Reason: %s" % reason)
  656. def get_cached_package_list(self):
  657. self.read_cache()
  658. if not path.exists(self.cache_file):
  659. self.logger.info("Requesting empty cache")
  660. if self.check_actuality():
  661. return self.pkg_list
  662. return None
  663. def check_actuality(self):
  664. """
  665. Кэш считается актуальным если ни один из файлов не менялся
  666. """
  667. if self.get_control_values() == self.files_control_values:
  668. self.logger.info(
  669. "Getting actuality cache (%d packages)" % len(self.pkg_list))
  670. return True
  671. else:
  672. reason = "Unknown"
  673. for k, v in self.get_control_values().items():
  674. if k in self.files_control_values:
  675. if v != self.files_control_values[k]:
  676. reason = "%s was modified" % k
  677. else:
  678. reason = "Checksum of file %s is not exist" % k
  679. self.logger.info("Failed to get cache. Reason: %s" % reason)
  680. return False
  681. def read_cache(self):
  682. self.files_control_values = {}
  683. cache_file_lines = readLinesFile(self.cache_file)
  684. for line in cache_file_lines:
  685. if "=" not in line:
  686. break
  687. k, v = line.split('=')
  688. self.files_control_values[k] = v.strip()
  689. self.pkg_list = PackageList(cache_file_lines)
  690. def get_control_values(self):
  691. def generate():
  692. for obj in self.check_list:
  693. for check_value in obj.checkvalues():
  694. yield check_value
  695. return dict(generate())