1
2 '''
3 # DESCRIPTION:
4 # Entropy Object Oriented Interface
5
6 Copyright (C) 2007-2009 Fabio Erculiani
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 '''
22
23 from __future__ import with_statement
24 import os
25 import sys
26 from entropy.core import Singleton
27 from entropy.output import TextInterface
28 from entropy.db import dbapi2
29 from entropy.client.interfaces.loaders import LoadersMixin
30 from entropy.client.interfaces.cache import CacheMixin
31 from entropy.client.interfaces.dep import CalculatorsMixin
32 from entropy.client.interfaces.methods import RepositoryMixin, MiscMixin, \
33 MatchMixin
34 from entropy.client.interfaces.fetch import FetchersMixin
35 from entropy.const import etpConst, etpCache, etpUi, const_debug_write
36 from entropy.core import SystemSettings, SystemSettingsPlugin
37 from entropy.misc import LogFile
38 from entropy.exceptions import SystemDatabaseError, RepositoryError
39
41
42 import entropy.tools as entropyTools
43
44 - def __init__(self, plugin_id, helper_interface):
48
50 """
51 This function collects available repositories configuration files
52 by filling internal dict() __repos_files and __repos_mtime.
53
54 @param system_settings: SystemSettings instance
55 @type system_settings: instance of SystemSettings
56 @return: None
57 @rtype: None
58 """
59
60 self.__repos_mtime = {
61 'repos_license_whitelist': {},
62 'repos_mask': {},
63 'repos_system_mask': {},
64 'repos_critical_updates': {},
65 }
66 self.__repos_files = {
67 'repos_license_whitelist': {},
68 'repos_mask': {},
69 'repos_system_mask': {},
70 'conflicting_tagged_packages': {},
71 'repos_critical_updates': {},
72 }
73
74 dmp_dir = etpConst['dumpstoragedir']
75 for repoid in system_settings['repositories']['order']:
76
77 repos_mask_setting = {}
78 repos_mask_mtime = {}
79 repos_lic_wl_setting = {}
80 repos_lic_wl_mtime = {}
81 repo_data = system_settings['repositories']['available'][repoid]
82 repos_sm_mask_setting = {}
83 repos_sm_mask_mtime = {}
84 confl_tagged = {}
85 repos_critical_updates_setting = {}
86 repos_critical_updates_mtime = {}
87
88 maskpath = os.path.join(repo_data['dbpath'],
89 etpConst['etpdatabasemaskfile'])
90 wlpath = os.path.join(repo_data['dbpath'],
91 etpConst['etpdatabaselicwhitelistfile'])
92 sm_path = os.path.join(repo_data['dbpath'],
93 etpConst['etpdatabasesytemmaskfile'])
94 ct_path = os.path.join(repo_data['dbpath'],
95 etpConst['etpdatabaseconflictingtaggedfile'])
96 critical_path = os.path.join(repo_data['dbpath'],
97 etpConst['etpdatabasecriticalfile'])
98
99 if os.access(maskpath, os.R_OK | os.F_OK):
100 repos_mask_setting[repoid] = maskpath
101 repos_mask_mtime[repoid] = dmp_dir + "/repo_" + \
102 repoid + "_" + etpConst['etpdatabasemaskfile'] + ".mtime"
103
104 if os.access(wlpath, os.R_OK | os.F_OK):
105 repos_lic_wl_setting[repoid] = wlpath
106 repos_lic_wl_mtime[repoid] = dmp_dir + "/repo_" + \
107 repoid + "_" + etpConst['etpdatabaselicwhitelistfile'] + \
108 ".mtime"
109
110 if os.access(sm_path, os.R_OK | os.F_OK):
111 repos_sm_mask_setting[repoid] = sm_path
112 repos_sm_mask_mtime[repoid] = dmp_dir + "/repo_" + \
113 repoid + "_" + etpConst['etpdatabasesytemmaskfile'] + \
114 ".mtime"
115 if os.access(ct_path, os.R_OK | os.F_OK):
116 confl_tagged[repoid] = ct_path
117
118 if os.access(critical_path, os.R_OK | os.F_OK):
119 repos_critical_updates_setting[repoid] = critical_path
120 repos_critical_updates_mtime[repoid] = dmp_dir + "/repo_" + \
121 repoid + "_" + etpConst['etpdatabasecriticalfile'] + \
122 ".mtime"
123
124 self.__repos_files['repos_mask'].update(repos_mask_setting)
125 self.__repos_mtime['repos_mask'].update(repos_mask_mtime)
126
127 self.__repos_files['repos_license_whitelist'].update(
128 repos_lic_wl_setting)
129 self.__repos_mtime['repos_license_whitelist'].update(
130 repos_lic_wl_mtime)
131
132 self.__repos_files['repos_system_mask'].update(
133 repos_sm_mask_setting)
134 self.__repos_mtime['repos_system_mask'].update(
135 repos_sm_mask_mtime)
136
137 self.__repos_files['conflicting_tagged_packages'].update(
138 confl_tagged)
139
140 self.__repos_files['repos_critical_updates'].update(
141 repos_critical_updates_setting)
142 self.__repos_mtime['repos_critical_updates'].update(
143 repos_critical_updates_mtime)
144
145 - def __run_post_branch_migration_hooks(self, sys_settings_instance):
146
147
148 if os.getuid() != 0:
149 return
150
151 old_branch_path = etpConst['etp_previous_branch_file']
152 current_branch = sys_settings_instance['repositories']['branch']
153
154 def write_current_branch(branch):
155 old_brf = open(old_branch_path, "w")
156 old_brf.write(current_branch+"\n")
157 old_brf.flush()
158 old_brf.close()
159
160 if not os.access(old_branch_path, os.F_OK | os.R_OK):
161 write_current_branch(current_branch)
162 return
163
164 old_f = open(old_branch_path, "r")
165 old_branch = old_f.readline().strip()
166 old_f.close()
167
168 if old_branch == current_branch:
169 return
170
171 repos, err = self._helper.run_repositories_post_branch_switch_hooks(
172 old_branch, current_branch)
173 if not err:
174 write_current_branch(current_branch)
175
176 - def __run_post_branch_upgrade_hooks(self, sys_settings_instance):
177
178
179 if os.getuid() != 0:
180 return
181
182
183
184
185 try:
186 update, remove, fine, spm_fine = \
187 self._helper.calculate_world_updates(critical_updates = False)
188 except RepositoryError:
189 return
190
191
192
193 if not update:
194 self._helper.run_repository_post_branch_upgrade_hooks()
195
197
198 parser_data = {}
199
200 mask_installed = []
201 mask_installed_keys = {}
202 while (self._helper.clientDbconn != None):
203 try:
204 self._helper.clientDbconn.validateDatabase()
205 except SystemDatabaseError:
206 break
207 mc_cache = set()
208 repos_mask_list = self.__repositories_system_mask(
209 system_settings_instance)
210 m_list = repos_mask_list + system_settings_instance['system_mask']
211 for atom in m_list:
212 m_ids, m_r = self._helper.clientDbconn.atomMatch(atom,
213 multiMatch = True)
214 if m_r != 0:
215 continue
216 mykey = self.entropyTools.dep_getkey(atom)
217 if mykey not in mask_installed_keys:
218 mask_installed_keys[mykey] = set()
219 for m_id in m_ids:
220 if m_id in mc_cache:
221 continue
222 mc_cache.add(m_id)
223 mask_installed.append(m_id)
224 mask_installed_keys[mykey].add(m_id)
225 break
226
227 parser_data.update({
228 'repos_installed': mask_installed,
229 'repos_installed_keys': mask_installed_keys,
230 })
231 return parser_data
232
234 data = {
235 'cache': {},
236 }
237 return data
238
240 """
241 Parser returning system packages mask metadata read from
242 packages.db.system_mask file inside the repository directory.
243 This file contains packages that should be always kept
244 installed, extending the already defined (in repository database)
245 set of atoms.
246 """
247 system_mask = []
248 for repoid in self.__repos_files['repos_system_mask']:
249 sys_settings_instance.validate_entropy_cache(
250 self.__repos_files['repos_system_mask'][repoid],
251 self.__repos_mtime['repos_system_mask'][repoid],
252 repoid = repoid)
253 system_mask += [x for x in \
254 self.entropyTools.generic_file_content_parser(
255 self.__repos_files['repos_system_mask'][repoid]) if x \
256 not in system_mask]
257 return system_mask
258
260 """
261 Parser that generates repository settings metadata.
262
263 @param sys_settings_instance: SystemSettings instance
264 @type sys_settings_instance: instance of SystemSettings
265 @return: parsed metadata
266 @rtype: dict
267 """
268
269
270 self.__setup_repos_files(sys_settings_instance)
271
272 data = {
273 'license_whitelist': {},
274 'mask': {},
275 'system_mask': [],
276 'critical_updates': {},
277 'conflicting_tagged_packages': {},
278 }
279
280
281 """
282 Parser returning licenses considered accepted by default
283 (= GPL compatibles) read from package.lic_whitelist.
284 """
285 for repoid in self.__repos_files['repos_license_whitelist']:
286 sys_settings_instance.validate_entropy_cache(
287 self.__repos_files['repos_license_whitelist'][repoid],
288 self.__repos_mtime['repos_license_whitelist'][repoid],
289 repoid = repoid)
290
291 data['license_whitelist'][repoid] = \
292 self.entropyTools.generic_file_content_parser(
293 self.__repos_files['repos_license_whitelist'][repoid])
294
295
296 """
297 Parser returning packages masked at repository level read from
298 packages.db.mask inside the repository database directory.
299 """
300 for repoid in self.__repos_files['repos_mask']:
301 sys_settings_instance.validate_entropy_cache(
302 self.__repos_files['repos_mask'][repoid],
303 self.__repos_mtime['repos_mask'][repoid], repoid = repoid)
304
305 data['mask'][repoid] = \
306 self.entropyTools.generic_file_content_parser(
307 self.__repos_files['repos_mask'][repoid])
308
309
310 data['system_mask'] = self.__repositories_system_mask(
311 sys_settings_instance)
312
313
314 """
315 Parser returning critical packages list metadata read from
316 packages.db.critical file inside the repository directory.
317 This file contains packages that should be always updated
318 before anything else.
319 """
320 for repoid in self.__repos_files['repos_critical_updates']:
321 sys_settings_instance.validate_entropy_cache(
322 self.__repos_files['repos_critical_updates'][repoid],
323 self.__repos_mtime['repos_critical_updates'][repoid],
324 repoid = repoid)
325 data['critical_updates'][repoid] = \
326 self.entropyTools.generic_file_content_parser(
327 self.__repos_files['repos_critical_updates'][repoid])
328
329
330
331 """
332 Parser returning packages that could have been installed because
333 they aren't in the same scope, but ending up creating critical
334 issues. You can see it as a configurable conflict map.
335 """
336
337 repoids = [x for x in sys_settings_instance['repositories']['order'] \
338 if x in self.__repos_files['conflicting_tagged_packages']]
339 for repoid in repoids:
340 filepath = self.__repos_files['conflicting_tagged_packages'].get(
341 repoid)
342 if os.access(filepath, os.R_OK | os.F_OK):
343 confl_f = open(filepath,"r")
344 content = confl_f.readlines()
345 confl_f.close()
346 content = [x.strip().rsplit("#", 1)[0].strip().split() for x \
347 in content if not x.startswith("#") and x.strip()]
348 for mydata in content:
349 if len(mydata) < 2:
350 continue
351 data['conflicting_tagged_packages'][mydata[0]] = mydata[1:]
352
353 return data
354
355
357
358 """
359 Parses Entropy client system configuration file.
360
361 @return dict data
362 """
363
364 data = {
365 'filesbackup': etpConst['filesbackup'],
366 'forcedupdates': etpConst['forcedupdates'],
367 'ignore_spm_downgrades': etpConst['spm']['ignore-spm-downgrades'],
368 'collisionprotect': etpConst['collisionprotect'],
369 'configprotect': etpConst['configprotect'][:],
370 'configprotectmask': etpConst['configprotectmask'][:],
371 'configprotectskip': etpConst['configprotectskip'][:],
372 }
373
374 cli_conf = etpConst['clientconf']
375 if not (os.path.isfile(cli_conf) and os.access(cli_conf, os.R_OK)):
376 return data
377
378 client_f = open(cli_conf,"r")
379 clientconf = [x.strip() for x in client_f.readlines() if \
380 x.strip() and not x.strip().startswith("#")]
381 client_f.close()
382 for line in clientconf:
383
384 split_line = line.split("|")
385 split_line_len = len(split_line)
386
387 if line.startswith("filesbackup|") and (split_line_len == 2):
388
389 compatopt = split_line[1].strip().lower()
390 if compatopt in ("disable", "disabled","false", "0", "no",):
391 data['filesbackup'] = False
392
393 if line.startswith("forcedupdates|") and (split_line_len == 2):
394
395 compatopt = split_line[1].strip().lower()
396 if compatopt in ("disable", "disabled","false", "0", "no",):
397 data['forcedupdates'] = False
398 else:
399 data['forcedupdates'] = True
400
401 elif line.startswith("ignore-spm-downgrades|") and \
402 (split_line_len == 2):
403
404 compatopt = split_line[1].strip().lower()
405 if compatopt in ("enable", "enabled", "true", "1", "yes"):
406 data['ignore_spm_downgrades'] = True
407
408 elif line.startswith("collisionprotect|") and (split_line_len == 2):
409
410 collopt = split_line[1].strip()
411 if collopt.lower() in ("0", "1", "2",):
412 data['collisionprotect'] = int(collopt)
413
414 elif line.startswith("configprotect|") and (split_line_len == 2):
415
416 configprotect = split_line[1].strip()
417 for myprot in configprotect.split():
418 data['configprotect'].append(
419 unicode(myprot,'raw_unicode_escape'))
420
421 elif line.startswith("configprotectmask|") and \
422 (split_line_len == 2):
423
424 configprotect = split_line[1].strip()
425 for myprot in configprotect.split():
426 data['configprotectmask'].append(
427 unicode(myprot,'raw_unicode_escape'))
428
429 elif line.startswith("configprotectskip|") and \
430 (split_line_len == 2):
431
432 configprotect = split_line[1].strip()
433 for myprot in configprotect.split():
434 data['configprotectskip'].append(
435 etpConst['systemroot']+unicode(myprot,
436 'raw_unicode_escape'))
437
438 return data
439
440 - def post_setup(self, system_settings_instance):
441 """
442 Reimplemented from SystemSettingsPlugin.
443 """
444
445 self.__run_post_branch_migration_hooks(system_settings_instance)
446
447
448 self.__run_post_branch_upgrade_hooks(system_settings_instance)
449
450
451 -class Client(Singleton, TextInterface, LoadersMixin, CacheMixin, CalculatorsMixin, \
452 RepositoryMixin, MiscMixin, MatchMixin, FetchersMixin):
453
454 - def init_singleton(self, indexing = True, noclientdb = 0,
455 xcache = True, user_xcache = False, repo_validation = True,
456 load_ugc = True, url_fetcher = None,
457 multiple_url_fetcher = None):
572
574 self.__instance_destroyed = True
575 if hasattr(self,'clientDbconn'):
576 if self.clientDbconn != None:
577 self.clientDbconn.closeDB()
578 del self.clientDbconn
579 if hasattr(self,'FileUpdates'):
580 del self.FileUpdates
581 if hasattr(self,'clientLog'):
582 self.clientLog.close()
583 if hasattr(self,'Cacher'):
584 self.Cacher.stop()
585 if hasattr(self,'SystemSettings') and \
586 hasattr(self,'sys_settings_client_plugin_id'):
587
588 if hasattr(self.SystemSettings,'remove_plugin'):
589 try:
590 self.SystemSettings.remove_plugin(
591 self.sys_settings_client_plugin_id)
592 except KeyError:
593 pass
594
595 self.close_all_repositories(mask_clear = False)
596 self.closeAllSecurity()
597 self.closeAllQA()
598
600 return self.__instance_destroyed
601
604