| Trees | Indices | Help |
|---|
|
|
1 #
2 # Copyright (c) 2008--2018 Red Hat, Inc.
3 #
4 # This software is licensed to you under the GNU General Public License,
5 # version 2 (GPLv2). There is NO WARRANTY for this software, express or
6 # implied, including the implied warranties of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
8 # along with this software; if not, see
9 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
10 #
11 # Red Hat trademarks are not licensed under GPLv2. No permission is
12 # granted to use or replicate Red Hat trademarks that are incorporated
13 # in this software or its documentation.
14 #
15 #
16 # Classes for generating repository metadata from RHN info.
17 #
18
19 import time
20 try:
21 # python 2
22 import StringIO
23 except ImportError:
24 # python3
25 import io as StringIO
26 import shutil
27 import os.path
28
29 from gzip import GzipFile
30 from gzip import write32u
31
32 from spacewalk.common.usix import LongType
33 from spacewalk.common import checksum
34 from spacewalk.common import rhnCache
35 from spacewalk.common.rhnLog import log_debug
36 from spacewalk.common.rhnConfig import CFG
37
38 import mapper
39 import view
40 from domain import RepoMD
41 from spacewalk.server import rhnChannel
42
43 # One meg
44 CHUNK_SIZE = 1048576
45
46 comps_mapping = {
47 'rhel-x86_64-client-5': 'rhn/kickstart/ks-rhel-x86_64-client-5/Client/repodata/comps-rhel5-client-core.xml',
48 'rhel-x86_64-client-vt-5': 'rhn/kickstart/ks-rhel-x86_64-client-5/VT/repodata/comps-rhel5-vt.xml',
49 'rhel-x86_64-client-workstation-5': 'rhn/kickstart/ks-rhel-x86_64-client-5/Workstation/repodata/comps-rhel5-client-workstation.xml',
50 'rhel-x86_64-server-5': 'rhn/kickstart/ks-rhel-x86_64-server-5/Server/repodata/comps-rhel5-server-core.xml',
51 'rhel-x86_64-server-vt-5': 'rhn/kickstart/ks-rhel-x86_64-server-5/VT/repodata/comps-rhel5-vt.xml',
52 'rhel-x86_64-server-cluster-5': 'rhn/kickstart/ks-rhel-x86_64-server-5/Cluster/repodata/comps-rhel5-cluster.xml',
53 'rhel-x86_64-server-cluster-storage-5': 'rhn/kickstart/ks-rhel-x86_64-server-5/ClusterStorage/repodata/comps-rhel5-cluster-st.xml',
54 }
55 for k in comps_mapping.keys():
56 for arch in ('i386', 'ia64', 's390x', 'ppc'):
57 comps_mapping[k.replace('x86_64', arch)] = comps_mapping[k].replace('x86_64', arch)
58
59
61
62 """
63 Representation of RHN channels as repository metadata.
64
65 This class can generate primary.xml, filelists.xml, and other.xml
66 """
67
69 self.channel_id = channel['id']
70 self.last_modified = channel['last_modified']
71
72 self.primary_prefix = "repomd_primary.xml"
73 self.other_prefix = "repomd_other.xml"
74 self.filelists_prefix = "repomd_filelists.xml"
75 self.updateinfo_prefix = "repomd_updateinfo.xml"
76
77 self._channel = None
78
79 cache = rhnCache.Cache()
80 self.cache = rhnCache.NullCache(cache)
81
83 """ Return a file-like object of the primarl.xml for this channel. """
84 ret = self.get_primary_cache()
85
86 if not ret:
87 viewobj = self.get_primary_view()
88
89 self.generate_files([viewobj])
90 ret = self.get_primary_cache()
91
92 return ret
93
95 """ Return a file-like object of the other.xml for this channel. """
96 ret = self.get_other_cache()
97
98 if not ret:
99 viewobj = self.get_other_view()
100
101 self.generate_files([viewobj])
102 ret = self.get_other_cache()
103
104 return ret
105
107 """ Return a file-like object of the filelists.xml for this channel. """
108 ret = self.get_filelists_cache()
109
110 if not ret:
111 viewobj = self.get_filelists_view()
112
113 self.generate_files([viewobj])
114 ret = self.get_filelists_cache()
115
116 return ret
117
119 """ Return a file-like object of the updateinfo.xml for the channel. """
120 ret = self.get_cache_file(self.updateinfo_prefix)
121
122 if not ret:
123 viewobj = self.get_cache_view(self.updateinfo_prefix,
124 view.UpdateinfoView)
125
126 viewobj.write_updateinfo()
127 viewobj.fileobj.close()
128 ret = self.get_cache_file(self.updateinfo_prefix)
129
130 return ret
131
134
136 cache_entry = self.get_cache_entry_name(cache_prefix)
137 ret = self.cache.get_file(cache_entry, self.last_modified)
138 return ret
139
141 cache_entry = self.get_cache_entry_name(cache_prefix)
142 ret = self.cache.set_file(cache_entry, self.last_modified)
143 viewobj = view_class(self.channel, ret)
144 return viewobj
145
147 return self.get_cache_file(self.primary_prefix)
148
150 return self.get_cache_file(self.other_prefix)
151
153 return self.get_cache_file(self.filelists_prefix)
154
157
160
163
165 """ Return a file-like object of the comps.xml/modules.yaml for the channel. """
166 if repomd_obj:
167 repomd_view = view.RepoMDView(repomd_obj)
168 return repomd_view.get_file()
169 elif func_name == 'get_comps_file' and self.channel.label in comps_mapping:
170 comps_view = view.RepoMDView(RepoMD(None,
171 os.path.join(CFG.mount_point, comps_mapping[self.channel.label])))
172 return comps_view.get_file()
173 else:
174 if self.channel.cloned_from_id is not None:
175 log_debug(1, "No comps/modules and no comps_mapping for [%s] cloned from [%s] trying to get comps from the original one."
176 % (self.channel.id, self.channel.cloned_from_id))
177 cloned_from_channel = rhnChannel.Channel().load_by_id(self.channel.cloned_from_id)
178 cloned_from_channel_label = cloned_from_channel._row['label']
179 func = getattr(Repository(rhnChannel.channel_info(cloned_from_channel_label)), func_name)
180 return func()
181 return None
182
185
188
190 for view in views:
191 view.write_start()
192
193 for package in self.channel.packages:
194 for view in views:
195 view.write_package(package)
196
197 for view in views:
198 view.write_end()
199 view.fileobj.close()
200
202 """ Late binding for the channel. """
203 if self._channel is None:
204 channel_mapper = mapper.get_channel_mapper()
205 self._channel = channel_mapper.get_channel(self.channel_id)
206 return self._channel
207
208 channel = property(__get_channel)
209
210
212
213 """ Decorator for Repositories adding gzip compression of the output. """
214
216 self.repository = repository
217
218 self.primary_prefix = self.repository.primary_prefix + ".gz"
219 self.other_prefix = self.repository.other_prefix + ".gz"
220 self.filelists_prefix = self.repository.filelists_prefix + ".gz"
221 self.updateinfo_prefix = self.repository.updateinfo_prefix + ".gz"
222
224 xml_file = self.repository.get_primary_xml_file()
225 return self.__get_compressed_file(xml_file)
226
228 """ Return gzipped other.xml file """
229 xml_file = self.repository.get_other_xml_file()
230 return self.__get_compressed_file(xml_file)
231
233 """ Return gzipped filelists.xml file """
234 xml_file = self.repository.get_filelists_xml_file()
235 return self.__get_compressed_file(xml_file)
236
238 """ Return gzipped updateinfo.xml file """
239 xml_file = self.repository.get_updateinfo_xml_file()
240 return self.__get_compressed_file(xml_file)
241
243 return getattr(self.repository, x)
244
246 string_file = StringIO.StringIO()
247 gzip_file = NoTimeStampGzipFile(mode="wb", fileobj=string_file)
248
249 shutil.copyfileobj(uncompressed_file, gzip_file)
250
251 gzip_file.close()
252
253 string_file.seek(0, 0)
254
255 return string_file
256
257
259
260 """ Decorator for Repositories adding caching. """
261
263 self.repository = repository
264
265 cache = rhnCache.Cache()
266 self.cache = rhnCache.NullCache(cache)
267
269 """ Return the cached primary metadata file, if it exists. """
270 return self._cached(self.primary_prefix,
271 self.repository.get_primary_xml_file)
272
276
280
284
286 """
287 Return the cached results if they are new enough, else get new results.
288
289 cache_prefix is a unique string that will identify the cached data.
290 fallback_method is the method to call if the cached data doesn't exist
291 or isn't new enough.
292 """
293 cache_entry = "%s-%s" % (cache_prefix, self.channel_id)
294 ret = self.cache.get_file(cache_entry, self.last_modified)
295 if ret:
296 log_debug(4, "Scored cache hit", self.channel_id)
297 else:
298 ret = fallback_method()
299 cache_file = self.cache.set_file(cache_entry, self.last_modified)
300
301 shutil.copyfileobj(ret, cache_file)
302
303 ret.close
304 cache_file.close()
305 ret = self.cache.get_file(cache_entry, self.last_modified)
306 return ret
307
309 return getattr(self.repository, x)
310
311
313
314 """
315 A repository that can provide repomd data.
316
317 A Metadata Repository is composed of a repository and a
318 CompressedRepository, as both are required to generate the repomd file.
319 """
320
322 self.repository = repository
323 self.compressed_repository = compressed_repository
324
325 self.repomd_prefix = "repomd.xml"
326
328 """ Return uncompressed repomd.xml file """
329
330 cache_entry = "%s-%s" % (self.repomd_prefix, self.channel_id)
331 ret = self.cache.get_file(cache_entry, self.last_modified)
332
333 if not ret:
334 # We need the time in seconds since the epoch for the xml file.
335 timestamp = int(time.mktime(time.strptime(self.last_modified,
336 "%Y%m%d%H%M%S")))
337
338 to_generate = []
339
340 if not self.repository.get_primary_cache():
341 to_generate.append(self.repository.get_primary_view())
342 if not self.repository.get_other_cache():
343 to_generate.append(self.repository.get_other_view())
344 if not self.repository.get_filelists_cache():
345 to_generate.append(self.repository.get_filelists_view())
346
347 self.repository.generate_files(to_generate)
348
349 primary = self.__compute_checksums(timestamp,
350 self.repository.get_primary_xml_file(),
351 self.compressed_repository.get_primary_xml_file())
352
353 filelists = self.__compute_checksums(timestamp,
354 self.repository.get_filelists_xml_file(),
355 self.compressed_repository.get_filelists_xml_file())
356
357 other = self.__compute_checksums(timestamp,
358 self.repository.get_other_xml_file(),
359 self.compressed_repository.get_other_xml_file())
360
361 updateinfo = self.__compute_checksums(timestamp,
362 self.repository.get_updateinfo_xml_file(),
363 self.compressed_repository.get_updateinfo_xml_file())
364
365 # Comps and modules might not exist on disc
366 comps = None
367 comps_file = None
368 modules = None
369 modules_file = None
370 try:
371 comps_file = self.repository.get_comps_file()
372 except IOError:
373 pass
374 try:
375 modules_file = self.repository.get_modules_file()
376 except IOError:
377 pass
378 if comps_file:
379 comps = self.__compute_open_checksum(timestamp, comps_file)
380 if modules_file:
381 modules = self.__compute_checksums(timestamp, modules_file)
382
383 ret = self.cache.set_file(cache_entry, self.last_modified)
384 repomd_view = view.RepoView(primary, filelists, other, updateinfo,
385 comps, modules, ret, self.__get_checksumtype())
386
387 repomd_view.write_repomd()
388 ret.close()
389 ret = self.cache.get_file(cache_entry, self.last_modified)
390
391 return ret
392
394 hash_computer = checksum.getHashlibInstance(self.__get_checksumtype(), False)
395
396 chunk = xml_file.read(CHUNK_SIZE)
397 while chunk:
398 hash_computer.update(chunk)
399 chunk = xml_file.read(CHUNK_SIZE)
400
401 return hash_computer.hexdigest()
402
404 template_hash = {}
405
406 template_hash['open_checksum'] = self.__get_file_checksum(xml_file)
407 template_hash['timestamp'] = timestamp
408
409 return template_hash
410
412 template_hash = self.__compute_open_checksum(timestamp, xml_file)
413
414 template_hash['gzip_checksum'] = self.__get_file_checksum(xml_gz_file)
415
416 return template_hash
417
420
423
424
426 """ Factory Method-ish function to create a repository from a channel. """
427 repository = Repository(channel)
428
429 compressed_repository = CompressedRepository(repository)
430 compressed_repository = CachedRepository(compressed_repository)
431
432 meta_repository = MetadataRepository(repository, compressed_repository)
433
434 return meta_repository
435
436
447
| Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Wed Mar 4 07:37:42 2020 | http://epydoc.sourceforge.net |