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.

247 lines
8.1 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 sys
  16. from os import path
  17. import shutil
  18. import os
  19. from calculate.lib.utils.files import listDirectory, readFile, readLinesFile, \
  20. makeDirectory, removeDir
  21. from package_tools import Git
  22. from update import UpdateError
  23. from calculate.lib.cl_lang import setLocalTranslate, _
  24. setLocalTranslate('cl_update3', sys.modules[__name__])
  25. DEFAULT_BRANCH = "master"
  26. class RepositoryStorage(object):
  27. directory = '/tmp'
  28. def __init__(self, directory):
  29. self.directory = directory
  30. makeDirectory(directory)
  31. def __iter__(self):
  32. git = Git()
  33. for dn in listDirectory(self.directory, onlyDir=True, fullPath=True):
  34. if git.is_git(dn):
  35. yield ProfileRepository(path.basename(dn), self)
  36. def get_profiles(self, url, branch=DEFAULT_BRANCH):
  37. return []
  38. class ProfileStorage(RepositoryStorage):
  39. def get_profiles(self, url, branch=DEFAULT_BRANCH):
  40. rep = self.get_repository(url, branch)
  41. if rep:
  42. return rep.get_profiles()
  43. return None
  44. def get_repository(self, url, branch=DEFAULT_BRANCH):
  45. return None
  46. class LocalStorage(ProfileStorage):
  47. """
  48. Локальное хранилище репозиториев, при запросе по урлу смотрит, доступные
  49. репозитории если находит подходящий - возвращает его профили
  50. """
  51. def get_repository(self, url, branch=DEFAULT_BRANCH):
  52. for rep in self:
  53. if rep.is_like(url, branch):
  54. return rep
  55. class CacheStorage(ProfileStorage):
  56. """
  57. Хранилище репозиториев, при запросе по урлу смотрит, доступные
  58. репозитории если находит подходящий - возвращает его профили,
  59. если не находит - скачивает и возвращает профили
  60. """
  61. def get_repository(self, url, branch=DEFAULT_BRANCH):
  62. for rep in self:
  63. if rep.is_like(url, branch):
  64. return rep
  65. else:
  66. return ProfileRepository.clone(url, self, branch)
  67. class RepositoryStorageSet(RepositoryStorage):
  68. """
  69. Набор хранилищ репозиториев
  70. """
  71. def __init__(self):
  72. self.storages = [LocalStorage('/var/lib/layman'),
  73. CacheStorage('/var/calculate/tmp/update')]
  74. def get_profiles(self, url, branch=DEFAULT_BRANCH):
  75. """
  76. Получить профили из указанного репозитория
  77. """
  78. for storage in self.storages:
  79. profiles = storage.get_profiles(url, branch)
  80. if profiles is not None:
  81. return profiles
  82. return None
  83. def __iter__(self):
  84. for storage in self.storages:
  85. for rep in storage:
  86. yield rep
  87. def get_repository(self, url, branch=DEFAULT_BRANCH):
  88. """
  89. Получить репозиторий по параметрам
  90. """
  91. for rep in self:
  92. if rep.is_like(url, branch):
  93. return rep
  94. return None
  95. def is_local(self, url, branch=DEFAULT_BRANCH):
  96. """
  97. Проверить является ли репозиторий с указанными параметрами
  98. локальным
  99. """
  100. rep = self.get_repository(url, branch)
  101. if rep and isinstance(rep.storage, LocalStorage):
  102. return True
  103. return False
  104. def __repr__(self):
  105. return "Repository set"
  106. class Profile(object):
  107. """
  108. Профиль репозитория
  109. """
  110. available_arch = ["amd64", "x86"]
  111. def __init__(self, repository, profile, arch):
  112. self.repository = repository
  113. self.profile = profile
  114. self.arch = arch
  115. @property
  116. def path(self):
  117. return path.join(self.repository.directory,"profiles", self.profile)
  118. @classmethod
  119. def from_string(cls, repository, s):
  120. parts = filter(None, s.split())
  121. if len(parts) == 3 and parts[0] in cls.available_arch:
  122. return Profile(repository, parts[1], parts[0])
  123. return None
  124. def __repr__(self):
  125. return "<Profile (%s) %s:%s from %s>" % (self.arch,
  126. self.repository.repo_name,
  127. self.profile,
  128. self.repository.directory)
  129. class ProfileRepository(object):
  130. """
  131. Репозиторий либо скачивается, либо берется из кэша
  132. """
  133. def __init__(self, name, storage):
  134. self._storage = storage
  135. self.name = name
  136. @property
  137. def storage(self):
  138. return self._storage
  139. @storage.setter
  140. def storage(self, storage):
  141. if storage.directory != self._storage.directory:
  142. newpath = path.join(storage.directory, self.name)
  143. if self.directory == newpath:
  144. return
  145. try:
  146. if path.exists(newpath):
  147. removeDir(newpath)
  148. shutil.move(self.directory, newpath)
  149. self._storage = storage
  150. except OSError as e:
  151. raise UpdateError(_("Failed to move the profile: %s") %
  152. str(e))
  153. @classmethod
  154. def clone(cls, url, storage, branch=DEFAULT_BRANCH):
  155. name = path.basename(url)
  156. if name.endswith(".git"):
  157. name = name[:-4]
  158. git = Git()
  159. rpath = path.join(storage.directory, name)
  160. if path.exists(rpath):
  161. removeDir(rpath)
  162. git.cloneRepository(url, rpath, branch)
  163. pr = cls(name, storage)
  164. repo_name = pr.repo_name
  165. if name != repo_name:
  166. rpath_new = path.join(storage.directory, repo_name)
  167. if not path.exists(rpath_new):
  168. os.rename(rpath, rpath_new)
  169. pr = cls(repo_name, storage)
  170. return pr
  171. @property
  172. def repo_name(self):
  173. return readFile(path.join(self.directory,
  174. "profiles/repo_name")).strip()
  175. def is_like(self, url, branch=DEFAULT_BRANCH):
  176. if self.url == url and (branch is None or self.branch == branch):
  177. return True
  178. return False
  179. @property
  180. def directory(self):
  181. """
  182. Получить локальную директорию на данные репозитория
  183. """
  184. return path.join(self.storage.directory, self.name)
  185. @property
  186. def url(self):
  187. git = Git()
  188. return git.get_url(self.directory, "origin")
  189. @property
  190. def branch(self):
  191. git = Git()
  192. return git.getBranch(self.directory)
  193. def sync(self):
  194. """
  195. Синхронизировать репозиторий
  196. """
  197. git = Git()
  198. if not git.pullRepository(self.directory, quiet_error=True):
  199. git.resetRepository(self.directory, to_origin=True)
  200. git.pullRepository(self.directory, quiet_error=True)
  201. def get_profiles(self):
  202. """
  203. Получить список профилей репозитория
  204. """
  205. profiles_desc = path.join(self.directory, "profiles/profiles.desc")
  206. return filter(None, (Profile.from_string(self, line)
  207. for line in readLinesFile(profiles_desc)))
  208. def __repr__(self):
  209. return "<ProfileRepository %s url=%s>" % (self.directory, self.url)