1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import os
16 import sys
17 import json
18
19 from spacewalk.server import rhnSQL
20 from spacewalk.server.importlib.backendOracle import SQLBackend
21 from spacewalk.server.importlib.contentSourcesImport import ContentSourcesImport
22 from spacewalk.satellite_tools.satCerts import verify_certificate_dates
23 from spacewalk.satellite_tools.syncLib import log, log2
24 from spacewalk.server.importlib.importLib import ContentSource, ContentSourceSsl
25
26 import constants
30 """Class managing CDN repositories, connected channels etc."""
31
32 - def __init__(self, local_mount_point=None, client_cert_id=None):
67
68
70 self.repository_to_channels = {}
71 for channel in self.content_source_mapping:
72 for source in self.content_source_mapping[channel]:
73 relative_url = source['relative_url']
74 if relative_url in self.repository_to_channels:
75 self.repository_to_channels[relative_url].append(channel)
76 else:
77 self.repository_to_channels[relative_url] = [channel]
78
79 for channel in self.kickstart_metadata:
80 for tree in self.kickstart_metadata[channel]:
81 tree_label = tree['ks_tree_label']
82 if tree_label in self.kickstart_source_mapping:
83 relative_url = self.kickstart_source_mapping[tree_label][0]['relative_url']
84 if relative_url in self.repository_to_channels:
85 self.repository_to_channels[relative_url].append(channel)
86 else:
87 self.repository_to_channels[relative_url] = [channel]
88
90 sql = """
91 select cs.label, cs.source_url, csssl.ssl_ca_cert_id,
92 csssl.ssl_client_cert_id, csssl.ssl_client_key_id
93 from rhnContentSource cs inner join
94 rhnContentSourceSsl csssl on cs.id = csssl.content_source_id
95 where cs.org_id is null
96 and cs.label like :prefix || '%%'
97 """
98
99 if client_cert_id:
100 sql += " and csssl.ssl_client_cert_id = :client_cert_id"
101 query = rhnSQL.prepare(sql)
102 query.execute(prefix=constants.MANIFEST_REPOSITORY_DB_PREFIX, client_cert_id=client_cert_id)
103 rows = query.fetchall_dict() or []
104 cdn_repositories = {}
105
106 for row in rows:
107 label = row['label']
108 if label in cdn_repositories:
109 cdn_repository = cdn_repositories[label]
110 else:
111 cdn_repository = CdnRepository(label, row['source_url'])
112 cdn_repositories[label] = cdn_repository
113
114
115 ssl_set = CdnRepositorySsl(row['ssl_ca_cert_id'], row['ssl_client_cert_id'], row['ssl_client_key_id'])
116 cdn_repository.add_ssl_set(ssl_set)
117
118
119 for cdn_repository in cdn_repositories.values():
120 self.repository_tree.add_repository(cdn_repository)
121
122 - def get_content_sources_regular(self, channel_label, source=False):
123 if channel_label in self.content_source_mapping:
124 return [x for x in self.content_source_mapping[channel_label]
125 if source or x['pulp_content_category'] != "source"]
126 else:
127 return []
128
129 - def get_content_sources_kickstart(self, channel_label):
130 repositories = []
131 if channel_label in self.kickstart_metadata:
132 for tree in self.kickstart_metadata[channel_label]:
133 tree_label = tree['ks_tree_label']
134 if tree_label in self.kickstart_source_mapping:
135
136
137
138 repository = self.kickstart_source_mapping[tree_label][0]
139 repository['ks_tree_label'] = tree_label
140 repositories.append(repository)
141 else:
142 log2(1, 1, "WARNING: Can't find repository for kickstart tree in mappings: %s"
143 % tree_label, stream=sys.stderr)
144 return repositories
145
146 - def get_content_sources(self, channel_label, source=False):
147 sources = self.get_content_sources_regular(channel_label, source=source)
148 kickstart_sources = self.get_content_sources_kickstart(channel_label)
149 return sources + sorted(kickstart_sources)
150
152 """Checks if all repositories for channel are available."""
153 if no_kickstarts:
154 sources = self.get_content_sources_regular(channel_label)
155 else:
156 sources = self.get_content_sources(channel_label)
157
158
159 if not sources:
160 return False
161
162 for source in sources:
163 if not self.check_repository_availability(source['relative_url'], channel_label=channel_label):
164 if source.get('ks_tree_label', None):
165
166 log2(0, 0, "WARNING: kickstart tree '%s' is unavailable" % source['ks_tree_label'],
167 stream=sys.stderr)
168 self.excluded_urls.append(source['relative_url'])
169 else:
170 return False
171 return True
172
174 try:
175 crypto_keys = self.get_repository_crypto_keys(relative_url)
176 except CdnRepositoryNotFoundError:
177 log2(1, 1, "ERROR: No SSL certificates were found for repository '%s'" % relative_url, stream=sys.stderr)
178 return False
179
180
181 if not crypto_keys:
182 if channel_label:
183 log2(1, 1, "ERROR: No valid SSL certificates were found for repository '%s'"
184 " required for channel '%s'." % (relative_url, channel_label), stream=sys.stderr)
185 else:
186 log2(1, 1, "ERROR: No valid SSL certificates were found for repository '%s'." % relative_url,
187 stream=sys.stderr)
188 return False
189
190
191 if self.local_mount_point and not os.path.isfile(os.path.join(
192 self.local_mount_point, relative_url[1:], "repodata/repomd.xml")):
193 return False
194
195 return True
196
197 - def get_content_sources_import_batch(self, channel_label, backend, repos=None):
198 batch = []
199
200
201 if not repos:
202 sources = self.get_content_sources(channel_label)
203 for source in sources:
204 if 'ks_tree_label' in source:
205 content_source = self._create_content_source_obj(source['ks_tree_label'],
206 source['relative_url'], backend)
207 else:
208 content_source = self._create_content_source_obj(source['pulp_repo_label_v2'],
209 source['relative_url'], backend)
210 batch.append(content_source)
211
212 else:
213 for index, repo in enumerate(repos):
214 repo_label = "%s-%d" % (channel_label, index)
215 content_source = self._create_content_source_obj(repo_label, repo, backend)
216 batch.append(content_source)
217
218 return batch
219
220 - def _create_content_source_obj(self, label, source_url, backend):
221 type_id = backend.lookupContentSourceType('yum')
222 content_source = ContentSource()
223 content_source['label'] = label
224 content_source['source_url'] = source_url
225 content_source['org_id'] = None
226 content_source['type_id'] = type_id
227 content_source['ssl-sets'] = []
228 repository = self.repository_tree.find_repository(source_url)
229 for ssl_set in repository.get_ssl_sets():
230 content_source_ssl = ContentSourceSsl()
231 content_source_ssl['ssl_ca_cert_id'] = ssl_set.get_ca_cert()
232 content_source_ssl['ssl_client_cert_id'] = ssl_set.get_client_cert()
233 content_source_ssl['ssl_client_key_id'] = ssl_set.get_client_key()
234 content_source['ssl-sets'].append(content_source_ssl)
235 return content_source
236
245
247 backend = SQLBackend()
248 self.unlink_all_repos(channel_label, custom_only=True)
249 repos = self.list_associated_repos(channel_label)
250 changed = 0
251 if delete_repos:
252 for to_delete in delete_repos:
253 if to_delete in repos:
254 repos.remove(to_delete)
255 log(0, "Removing repository '%s' from channel." % to_delete)
256 changed += 1
257 else:
258 log2(0, 0, "WARNING: Repository '%s' is not attached to channel." % to_delete, stream=sys.stderr)
259 if add_repos:
260 for to_add in add_repos:
261 if to_add not in repos:
262 repos.append(to_add)
263 log(0, "Attaching repository '%s' to channel." % to_add)
264 changed += 1
265 else:
266 log2(0, 0, "WARNING: Repository '%s' is already attached to channel." % to_add, stream=sys.stderr)
267
268
269 if repos:
270 content_sources_batch = self.get_content_sources_import_batch(
271 channel_label, backend, repos=sorted(repos))
272 for content_source in content_sources_batch:
273 content_source['channels'] = [channel_label]
274 importer = ContentSourcesImport(content_sources_batch, backend)
275 importer.run()
276 else:
277
278 self.unlink_all_repos(channel_label)
279 return changed
280
281 @staticmethod
283 sql = """
284 delete from rhnChannelContentSource ccs
285 where ccs.channel_id = (select id from rhnChannel where label = :label)
286 """
287 if custom_only:
288 sql += """
289 and ccs.source_id in (select id from rhnContentSource where id = ccs.source_id and org_id is not null)
290 """
291 h = rhnSQL.prepare(sql)
292 h.execute(label=channel_label)
293 rhnSQL.commit()
294
295 @staticmethod
297 h = rhnSQL.prepare("""
298 select cs.source_url
299 from rhnChannel c inner join
300 rhnChannelContentSource ccs on c.id = ccs.channel_id inner join
301 rhnContentSource cs on ccs.source_id = cs.id
302 where c.label = :label
303 and cs.org_id is null
304 """)
305 h.execute(label=channel_label)
306 paths = [r['source_url'] for r in h.fetchall_dict() or []]
307 return paths
308
309 @staticmethod
311 h = rhnSQL.prepare("""
312 select cs.source_url
313 from rhnContentSource cs inner join
314 rhnContentSourceSsl csssl on cs.id = csssl.content_source_id
315 where cs.label like :prefix || '%%'
316 and csssl.ssl_client_cert_id = :client_cert_id
317 """)
318 h.execute(prefix=constants.MANIFEST_REPOSITORY_DB_PREFIX, client_cert_id=crypto_key_id)
319 paths = [r['source_url'] for r in h.fetchall_dict() or []]
320 return paths
321
322 @staticmethod
332
333 @staticmethod
335 if 'pulp_repo_label_v2' in source:
336 return source['pulp_repo_label_v2']
337 elif 'ks_tree_label' in source:
338 return source['ks_tree_label']
339 else:
340 raise InvalidContentSourceType()
341
343 if relative_path in self.repository_to_channels:
344 return self.repository_to_channels[relative_path]
345 else:
346 return []
347
350 """Class representing activated CDN repositories in tree structure.
351 Leafs contains CdnRepository instances.
352 Allows us to match direct CDN URLs without variables (coming from mapping)
353 to CDN URLs with variables (coming from manifest and having SSL keys/certs assigned)"""
354
355 VARIABLES = ['$releasever', '$basearch']
356
359
361 """Add new CdnRepository to tree."""
362
363 url = repository.get_url()
364 path = [x for x in url.split('/') if x]
365 node = self.root
366 for part in path[:-1]:
367 if part not in node:
368 node[part] = {}
369 node = node[part]
370
371 node[path[-1]] = repository
372
400
401 @staticmethod
403 """Splits repository URL, removes redundant characters and returns list with directory names."""
404 path = []
405 for part in url.split('/'):
406 if part == '..':
407 if path:
408 del path[-1]
409 else:
410
411 path.append(part)
412 elif part and part != '.':
413 path.append(part)
414
415 return path
416
428
432
433
434 -class InvalidContentSourceType(Exception):
436
439 """Class representing CDN repository."""
440
442 self.label = label
443 self.url = url
444 self.ssl_sets = []
445
446
448 self.ssl_sets.append(ssl_set)
449
452
455
458
461 """Class representing single SSL certificate, key set for single CDN repository"""
462
463 - def __init__(self, ca_cert, client_cert, client_key):
464 self.ca_cert = int(ca_cert)
465 self.client_cert = int(client_cert)
466 self.client_key = int(client_key)
467
470
472 return self.client_cert
473
475 return self.client_key
476
478 ssl_query = rhnSQL.prepare("""
479 select description, key, org_id from rhnCryptoKey where id = :id
480 """)
481 keys = {}
482 ssl_query.execute(id=self.ca_cert)
483 row = ssl_query.fetchone_dict()
484 keys['ca_cert'] = (str(row['description']), str(row['key']), row['org_id'])
485 ssl_query.execute(id=self.client_cert)
486 row = ssl_query.fetchone_dict()
487 keys['client_cert'] = (str(row['description']), str(row['key']), row['org_id'])
488 ssl_query.execute(id=self.client_key)
489 row = ssl_query.fetchone_dict()
490 keys['client_key'] = (str(row['description']), str(row['key']), row['org_id'])
491
492
493 if check_dates:
494 failed = 0
495 for key in (keys['ca_cert'], keys['client_cert']):
496 if not verify_certificate_dates(key[1]):
497 log(1, "WARNING: Problem with dates in certificate '%s'. "
498 "Please check validity of this certificate." % key[0])
499 failed += 1
500 if failed:
501 return {}
502 return keys
503