Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

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