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