41 import pkg.client.query_parser as query_p |
41 import pkg.client.query_parser as query_p |
42 import pkg.fmri as fmri |
42 import pkg.fmri as fmri |
43 import pkg.misc as misc |
43 import pkg.misc as misc |
44 import pkg.p5i as p5i |
44 import pkg.p5i as p5i |
45 import pkg.search_errors as search_errors |
45 import pkg.search_errors as search_errors |
46 import pkg.variant as variant |
|
47 import pkg.nrlock |
46 import pkg.nrlock |
48 |
47 |
49 from pkg.client.imageplan import EXECUTED_OK |
48 from pkg.client.imageplan import EXECUTED_OK |
50 from pkg.client import global_settings |
49 from pkg.client import global_settings |
51 |
50 |
52 CURRENT_API_VERSION = 23 |
51 CURRENT_API_VERSION = 24 |
53 CURRENT_P5I_VERSION = 1 |
52 CURRENT_P5I_VERSION = 1 |
54 |
53 |
55 logger = global_settings.logger |
54 logger = global_settings.logger |
56 |
55 |
57 class ImageInterface(object): |
56 class ImageInterface(object): |
86 callbacks. cancel_state_callable is a function which the client |
85 callbacks. cancel_state_callable is a function which the client |
87 wishes to have called each time whether the operation can be |
86 wishes to have called each time whether the operation can be |
88 canceled changes. It can raise VersionException and |
87 canceled changes. It can raise VersionException and |
89 ImageNotFoundException.""" |
88 ImageNotFoundException.""" |
90 |
89 |
91 compatible_versions = set([23]) |
90 compatible_versions = set([CURRENT_API_VERSION]) |
92 |
91 |
93 if version_id not in compatible_versions: |
92 if version_id not in compatible_versions: |
94 raise api_errors.VersionException(CURRENT_API_VERSION, |
93 raise api_errors.VersionException(CURRENT_API_VERSION, |
95 version_id) |
94 version_id) |
96 |
95 |
241 self.log_operation_end(error=exc_value) |
240 self.log_operation_end(error=exc_value) |
242 self.__reset_unlock() |
241 self.__reset_unlock() |
243 self.__activity_lock.release() |
242 self.__activity_lock.release() |
244 raise |
243 raise |
245 |
244 |
246 |
245 def plan_install(self, pkg_list, refresh_catalogs=True, |
247 def plan_install(self, pkg_list, filters, refresh_catalogs=True, |
|
248 noexecute=False, verbose=False, update_index=True): |
246 noexecute=False, verbose=False, update_index=True): |
249 """Contructs a plan to install the packages provided in |
247 """Contructs a plan to install the packages provided in |
250 pkg_list. pkg_list is a list of packages to install. filters |
248 pkg_list. pkg_list is a list of packages to install. |
251 is a list of filters to apply to the actions of the installed |
249 refresh_catalogs controls whether the catalogs will |
252 packages. refresh_catalogs controls whether the catalogs will |
|
253 automatically be refreshed. noexecute determines whether the |
250 automatically be refreshed. noexecute determines whether the |
254 history will be recorded after planning is finished. verbose |
251 history will be recorded after planning is finished. verbose |
255 controls whether verbose debugging output will be printed to the |
252 controls whether verbose debugging output will be printed to the |
256 terminal. Its existence is temporary. It returns a boolean |
253 terminal. Its existence is temporary. It returns a boolean |
257 which tells the client whether there is anything to do. It can |
254 which tells the client whether there is anything to do. It can |
266 self.__refresh_publishers() |
263 self.__refresh_publishers() |
267 |
264 |
268 self.__img.make_install_plan(pkg_list, |
265 self.__img.make_install_plan(pkg_list, |
269 self.__progresstracker, |
266 self.__progresstracker, |
270 self.__check_cancelation, noexecute, |
267 self.__check_cancelation, noexecute, |
271 filters=filters, verbose=verbose) |
268 verbose=verbose) |
272 |
269 |
273 assert self.__img.imageplan |
270 assert self.__img.imageplan |
274 |
271 |
275 if self.__canceling: |
272 if self.__canceling: |
276 raise api_errors.CanceledException() |
273 raise api_errors.CanceledException() |
387 except api_errors.ImageNotFoundException: |
384 except api_errors.ImageNotFoundException: |
388 # Can't do anything in this |
385 # Can't do anything in this |
389 # case; so proceed. |
386 # case; so proceed. |
390 pass |
387 pass |
391 |
388 |
392 # XXX For now, strip the publisher prefix from FMRIs for |
389 self.__img.make_update_plan(self.__progresstracker, |
393 # packages that were installed from a publisher that was |
390 self.__check_cancelation, noexecute, |
394 # preferred at the time of installation. However, when |
391 verbose=verbose) |
395 # the image starts using prioritised publishers, this |
392 |
396 # will have to be removed. Newer versions of packages |
|
397 # should only be a valid match if offered the same |
|
398 # publisher since equivalence cannot be determined by |
|
399 # package name alone. |
|
400 ppub = self.__img.get_preferred_publisher() |
|
401 pkg_list = [ |
|
402 p.get_pkg_stem(anarchy=self.__img.is_pkg_preferred( |
|
403 p)) |
|
404 for p in self.__img.gen_installed_pkgs() |
|
405 ] |
|
406 |
|
407 self.__img.make_install_plan(pkg_list, |
|
408 self.__progresstracker, self.__check_cancelation, |
|
409 noexecute, verbose=verbose, multimatch_ignore=True) |
|
410 |
|
411 assert self.__img.imageplan |
393 assert self.__img.imageplan |
412 |
394 |
413 if self.__canceling: |
395 if self.__canceling: |
414 raise api_errors.CanceledException() |
396 raise api_errors.CanceledException() |
415 self.__set_can_be_canceled(False) |
397 self.__set_can_be_canceled(False) |
431 |
413 |
432 self.__plan_common_finish() |
414 self.__plan_common_finish() |
433 res = not self.__img.imageplan.nothingtodo() |
415 res = not self.__img.imageplan.nothingtodo() |
434 return res, opensolaris_image |
416 return res, opensolaris_image |
435 |
417 |
436 def plan_change_variant(self, variants, noexecute=False, |
418 def plan_change_varcets(self, variants=None, facets=None, |
437 verbose=False, be_name=None): |
419 noexecute=False, verbose=False, be_name=None): |
438 """Creates a plan to change the specified variants on an image. |
420 """Creates a plan to change the specified variants/facets on an image. |
439 There is option to refresh_catalogs since if we're changing |
421 verbose controls |
440 architectures, we have to download manifests that were |
|
441 previously uncached. noexecute determines whether the history |
|
442 will be recorded after planning is finished. verbose controls |
|
443 whether verbose debugging output will be printed to the |
422 whether verbose debugging output will be printed to the |
444 terminal. This function has two return values. The first is |
423 terminal. This function has two return values. The first is |
445 a boolean which tells the client whether there is anything to |
424 a boolean which tells the client whether there is anything to |
446 do. The third is either None, or an exception which indicates |
425 do. The third is either None, or an exception which indicates |
447 partial success. This is currently used to indicate a failure |
426 partial success. This is currently used to indicate a failure |
448 in refreshing catalogs. It can raise CatalogRefreshException, |
427 in refreshing catalogs. It can raise CatalogRefreshException, |
449 IpkgOutOfDateException, NetworkUnavailableException, |
428 IpkgOutOfDateException, NetworkUnavailableException, |
450 PlanCreationException and PermissionsException.""" |
429 PlanCreationException and PermissionsException.""" |
451 |
430 |
452 self.__plan_common_start("change-variant") |
431 self.__plan_common_start("change-variant") |
|
432 if not variants and not facets: |
|
433 raise ValueError, "Nothing to do" |
453 try: |
434 try: |
454 self.check_be_name(be_name) |
435 self.check_be_name(be_name) |
455 self.be_name = be_name |
436 self.be_name = be_name |
456 |
437 |
457 self.__refresh_publishers() |
438 self.__refresh_publishers() |
458 |
439 |
459 self.__img.image_change_variant(variants, |
440 self.__img.image_change_varcets(variants, |
|
441 facets, |
460 self.__progresstracker, |
442 self.__progresstracker, |
461 self.__check_cancelation, |
443 self.__check_cancelation, |
462 noexecute, verbose=verbose) |
444 noexecute, verbose=verbose) |
463 |
445 |
464 assert self.__img.imageplan |
446 assert self.__img.imageplan |
596 if self.__img.is_liveroot(): |
578 if self.__img.is_liveroot(): |
597 e = api_errors.ImageUpdateOnLiveImageException() |
579 e = api_errors.ImageUpdateOnLiveImageException() |
598 self.log_operation_end(error=e) |
580 self.log_operation_end(error=e) |
599 raise e |
581 raise e |
600 else: |
582 else: |
601 if self.__img.imageplan.actuators.reboot_needed() and \ |
583 if self.__img.imageplan.reboot_needed() and \ |
602 self.__img.is_liveroot(): |
584 self.__img.is_liveroot(): |
603 e = api_errors.RebootNeededOnLiveImageException() |
585 e = api_errors.RebootNeededOnLiveImageException() |
604 self.log_operation_end(error=e) |
586 self.log_operation_end(error=e) |
605 raise e |
587 raise e |
606 |
588 |
1051 def __set_history_PlanCreationException(self, e): |
1033 def __set_history_PlanCreationException(self, e): |
1052 if e.unmatched_fmris or e.multiple_matches or \ |
1034 if e.unmatched_fmris or e.multiple_matches or \ |
1053 e.missing_matches or e.illegal: |
1035 e.missing_matches or e.illegal: |
1054 self.log_operation_end(error=e, |
1036 self.log_operation_end(error=e, |
1055 result=history.RESULT_FAILED_BAD_REQUEST) |
1037 result=history.RESULT_FAILED_BAD_REQUEST) |
1056 elif e.constraint_violations: |
|
1057 self.log_operation_end(error=e, |
|
1058 result=history.RESULT_FAILED_CONSTRAINED) |
|
1059 else: |
1038 else: |
1060 self.log_operation_end(error=e) |
1039 self.log_operation_end(error=e) |
1061 |
1040 |
1062 def local_search(self, query_lst): |
1041 def local_search(self, query_lst): |
1063 """local_search takes a list of Query objects and performs |
1042 """local_search takes a list of Query objects and performs |
1086 get_manifest_path=\ |
1065 get_manifest_path=\ |
1087 self.__img.get_manifest_path, |
1066 self.__img.get_manifest_path, |
1088 gen_installed_pkg_names=\ |
1067 gen_installed_pkg_names=\ |
1089 self.__img.gen_installed_pkg_names, |
1068 self.__img.gen_installed_pkg_names, |
1090 case_sensitive=q.case_sensitive) |
1069 case_sensitive=q.case_sensitive) |
1091 excludes = [variant.Variants( |
|
1092 {"variant.arch": self.__img.get_arch()} |
|
1093 ).allow_action] |
|
1094 res = query.search( |
1070 res = query.search( |
1095 self.__img.gen_installed_pkgs, |
1071 self.__img.gen_installed_pkgs, |
1096 self.__img.get_manifest_path, excludes) |
1072 self.__img.get_manifest_path, |
|
1073 self.__img.list_excludes()) |
1097 except search_errors.InconsistentIndexException, e: |
1074 except search_errors.InconsistentIndexException, e: |
1098 raise api_errors.InconsistentIndexException(e) |
1075 raise api_errors.InconsistentIndexException(e) |
1099 # i is being inserted to track which query the results |
1076 # i is being inserted to track which query the results |
1100 # are for. None is being inserted since there is no |
1077 # are for. None is being inserted since there is no |
1101 # publisher being searched against. |
1078 # publisher being searched against. |
1265 self.__img.update_index_dir() |
1242 self.__img.update_index_dir() |
1266 self.log_operation_start("rebuild-index") |
1243 self.log_operation_start("rebuild-index") |
1267 if not os.path.isdir(self.__img.index_dir): |
1244 if not os.path.isdir(self.__img.index_dir): |
1268 self.__img.mkdirs() |
1245 self.__img.mkdirs() |
1269 try: |
1246 try: |
1270 excludes = [variant.Variants( |
|
1271 {"variant.arch": self.__img.get_arch()}).allow_action] |
|
1272 ind = indexer.Indexer(self.__img, self.__img.get_manifest, |
1247 ind = indexer.Indexer(self.__img, self.__img.get_manifest, |
1273 self.__img.get_manifest_path, |
1248 self.__img.get_manifest_path, |
1274 self.__progresstracker, excludes) |
1249 self.__progresstracker, self.__img.list_excludes()) |
1275 ind.rebuild_index_from_scratch( |
1250 ind.rebuild_index_from_scratch( |
1276 self.__img.gen_installed_pkgs()) |
1251 self.__img.gen_installed_pkgs()) |
1277 except search_errors.ProblematicPermissionsIndexException, e: |
1252 except search_errors.ProblematicPermissionsIndexException, e: |
1278 error = api_errors.ProblematicPermissionsIndexException(e) |
1253 error = api_errors.ProblematicPermissionsIndexException(e) |
1279 self.log_operation_end(error=error) |
1254 self.log_operation_end(error=error) |
1305 self.__img.add_publisher(pub, |
1280 self.__img.add_publisher(pub, |
1306 refresh_allowed=refresh_allowed, |
1281 refresh_allowed=refresh_allowed, |
1307 progtrack=self.__progresstracker) |
1282 progtrack=self.__progresstracker) |
1308 finally: |
1283 finally: |
1309 self.__img.cleanup_downloads() |
1284 self.__img.cleanup_downloads() |
|
1285 |
|
1286 def get_pub_search_order(self): |
|
1287 """Return current search order of publishers; includes |
|
1288 disabled publishers""" |
|
1289 return self.__img.cfg_cache.publisher_search_order |
|
1290 |
|
1291 def set_pub_search_after(self, being_moved_prefix, staying_put_prefix): |
|
1292 """Change the publisher search order so that being_moved is |
|
1293 searched after staying_put""" |
|
1294 self.__img.pub_search_after(being_moved_prefix, staying_put_prefix) |
|
1295 |
|
1296 def set_pub_search_before(self, being_moved_prefix, staying_put_prefix): |
|
1297 """Change the publisher search order so that being_moved is |
|
1298 searched before staying_put""" |
|
1299 self.__img.pub_search_before(being_moved_prefix, staying_put_prefix) |
1310 |
1300 |
1311 def get_preferred_publisher(self): |
1301 def get_preferred_publisher(self): |
1312 """Returns the preferred publisher object for the image.""" |
1302 """Returns the preferred publisher object for the image.""" |
1313 return self.get_publisher( |
1303 return self.get_publisher( |
1314 prefix=self.__img.get_preferred_publisher()) |
1304 prefix=self.__img.get_preferred_publisher()) |