diff -Nru landscape-client-12.04.3/debian/changelog landscape-client-12.05/debian/changelog --- landscape-client-12.04.3/debian/changelog 2012-06-19 15:01:47.000000000 -0300 +++ landscape-client-12.05/debian/changelog 2012-06-19 14:56:02.000000000 -0300 @@ -1,3 +1,44 @@ +landscape-client (12.05-0ubuntu0.12.04) precise-proposed; urgency=low + + * New upstream release 12.05 (r561 in trunk) (LP: #1004678). + * Make all subpackages that depend on each other require the exact same + version, instead of >= $version. + * Added python-gi to client depends starting natty. + * Make change-packages also handle package holds (LP: #972489). + * Fix typo in glade file (LP: #983096). + * Tidy up apt facade (LP: #985493). + * Remove SmartFacade and its tests, no longer used (LP: #996269). + * Remove check for apt version, since we won't release this for + Hardy where that check matters (LP: #996837). + * Remove methods that were smart specific. We no longer use smart + (LP: #996841). + * Remove smart-update helper binary. We no longer use smart + (LP: #997408). + * Remove test-mixins that were useful only during the apt-to-smart + code migration. Now with smart gone, they are no longer necessary + (LP: #997760). + * Build the packages from precise onward, not just precise. + * Assorted packaging fixes: + - Switched to format 3.0 (quilt). + - Added source lintian overrides, with comments. + - Updated debian/rules: + - Added build-arch and build-indep dummy target. + - Build the GUI packages from precise onwards, and not just on precise. + - Re-enable dh_lintian. + - Strip the binaries (dh_strip), and also call dh_shlibdeps for the + automatic shlibs dependency. + - Added python-gi from natty onwards. + - Used __Choices instead of _Choices in landscape-common.templates, it's + better for translators. + - Updated standard version to 3.8.2, the version from Lucid (oldest one + we support) + - Added shlibs depends. + - Dropped deprecated ${Source-Version} and replaced it with + ${binary:Version} + - Require exact version of the sibling package instead of >=. + + -- Andreas Hasenack Tue, 19 Jun 2012 12:10:15 -0300 + landscape-client (12.04.3-0ubuntu1) precise; urgency=low * Warn on unicode entry into settings UI (LP: #956612). diff -Nru landscape-client-12.04.3/debian/control landscape-client-12.05/debian/control --- landscape-client-12.04.3/debian/control 2012-06-19 15:01:47.000000000 -0300 +++ landscape-client-12.05/debian/control 2012-06-01 17:52:18.000000000 -0300 @@ -4,12 +4,13 @@ Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Landscape Team Build-Depends: debhelper (>= 5), po-debconf, python-dev, lsb-release, gawk, python-twisted-core, python-distutils-extra -Standards-Version: 3.8.0 +Standards-Version: 3.8.2 XS-Python-Version: >= 2.4, << 2.8 Package: landscape-common Architecture: any Depends: ${python:Depends}, ${misc:Depends}, ${extra:Depends}, + ${shlibs:Depends}, python-gnupginterface, python-twisted-core, python-apt, @@ -33,9 +34,10 @@ Package: landscape-client Architecture: any Depends: ${python:Depends}, ${misc:Depends}, ${extra:Depends}, + ${shlibs:Depends}, python-twisted-web, python-twisted-names, - landscape-common (>= ${Source-Version}) + landscape-common (= ${binary:Version}) Suggests: ${extra:Suggests} Description: The Landscape administration system client Landscape is a web-based tool for managing Ubuntu systems. This @@ -48,8 +50,8 @@ Package: landscape-client-ui Architecture: any Depends: ${python:Depends}, ${misc:Depends}, - landscape-client (>= ${Source-Version}), - landscape-client-ui-install (>= ${Source-Version}), + landscape-client (= ${binary:Version}), + landscape-client-ui-install (= ${binary:Version}), python-gi, python-dbus, policykit-1, diff -Nru landscape-client-12.04.3/debian/landscape-client.templates landscape-client-12.05/debian/landscape-client.templates --- landscape-client-12.04.3/debian/landscape-client.templates 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/debian/landscape-client.templates 2012-06-01 17:55:52.000000000 -0300 @@ -64,7 +64,7 @@ Template: landscape-client/tags Type: string Default: -_Description: Initial tags for first registration +_Description: Initial tags for first registration: Comma separated list of tags which will be assigned to this computer on its first registration. Once the machine is registered, these tags can only be changed using the Landscape server. diff -Nru landscape-client-12.04.3/debian/landscape-common.templates landscape-client-12.05/debian/landscape-common.templates --- landscape-client-12.04.3/debian/landscape-common.templates 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/debian/landscape-common.templates 2012-06-01 17:56:34.000000000 -0300 @@ -6,7 +6,7 @@ # try to keep below ~71 characters. # DO NOT USE commas (,) in Choices translations otherwise # this will break the choices shown to users -_Choices: Do not display sysinfo on login, Cache sysinfo in /etc/motd, Run sysinfo on every login +__Choices: Do not display sysinfo on login, Cache sysinfo in /etc/motd, Run sysinfo on every login Default: Cache sysinfo in /etc/motd _Description: landscape-sysinfo configuration: Landscape includes a tool and a set of modules that can display diff -Nru landscape-client-12.04.3/debian/po/templates.pot landscape-client-12.05/debian/po/templates.pot --- landscape-client-12.04.3/debian/po/templates.pot 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/debian/po/templates.pot 2012-06-01 18:35:20.000000000 -0300 @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +"Project-Id-Version: landscape-client\n" "Report-Msgid-Bugs-To: landscape-client@packages.debian.org\n" -"POT-Creation-Date: 2012-03-07 17:07+0100\n" +"POT-Creation-Date: 2012-06-01 18:35-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -152,7 +152,7 @@ #. Type: string #. Description #: ../landscape-client.templates:11001 -msgid "Initial tags for first registration" +msgid "Initial tags for first registration:" msgstr "" #. Type: string @@ -187,9 +187,31 @@ #. DO NOT USE commas (,) in Choices translations otherwise #. this will break the choices shown to users #: ../landscape-common.templates:1001 -msgid "" -"Do not display sysinfo on login, Cache sysinfo in /etc/motd, Run sysinfo on " -"every login" +msgid "Do not display sysinfo on login" +msgstr "" + +#. Type: select +#. Choices +#. Translators beware! the following three strings form a single +#. Choices menu. - Every one of these strings has to fit in a standard +#. 80 characters console, as the fancy screen setup takes up some space +#. try to keep below ~71 characters. +#. DO NOT USE commas (,) in Choices translations otherwise +#. this will break the choices shown to users +#: ../landscape-common.templates:1001 +msgid "Cache sysinfo in /etc/motd" +msgstr "" + +#. Type: select +#. Choices +#. Translators beware! the following three strings form a single +#. Choices menu. - Every one of these strings has to fit in a standard +#. 80 characters console, as the fancy screen setup takes up some space +#. try to keep below ~71 characters. +#. DO NOT USE commas (,) in Choices translations otherwise +#. this will break the choices shown to users +#: ../landscape-common.templates:1001 +msgid "Run sysinfo on every login" msgstr "" #. Type: select diff -Nru landscape-client-12.04.3/debian/rules landscape-client-12.05/debian/rules --- landscape-client-12.04.3/debian/rules 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/debian/rules 2012-06-01 17:58:20.000000000 -0300 @@ -9,7 +9,7 @@ endif dh_extra_flags = -plandscape-common -plandscape-client -ifneq (,$(filter $(dist_release),precise)) +ifeq (,$(filter $(dist_release),hardy lucid natty oneiric)) # We want landscape-client-ui only from precise onward dh_extra_flags += -plandscape-client-ui -plandscape-client-ui-install endif @@ -30,6 +30,10 @@ landscape_common_substvars = debian/landscape-common.substvars landscape_client_substvars = debian/landscape-client.substvars +build-arch: build + +build-indep: build + build: build-stamp build-stamp: dh_testdir @@ -69,10 +73,7 @@ # do nothing # binary-arch: build install - # That's not present in Ubuntu releases we still support, so - # we're just installing the lintian overrides file by hand - # for now. - #dh_lintian + dh_lintian dh_testdir dh_testroot dh_installdocs @@ -82,8 +83,10 @@ dh_installinit -- start 45 2 3 4 5 . stop 15 0 1 6 . dh_installlogrotate dh_installdebconf + dh_strip dh_compress dh_fixperms + dh_shlibdeps ifneq (,$(findstring $(dist_release),hardy)) # We depend on bug-fixed versions of python-dbus and pycurl on hardy @@ -100,7 +103,7 @@ ifeq (,$(filter $(dist_release),hardy lucid)) # Starting natty, no more hal or dbus echo "extra:Depends=libpam-modules (>= 1.0.1-9ubuntu3)" >> $(landscape_common_substvars) - echo "extra:Depends=python-pycurl, gir1.2-gudev-1.0 (>= 165-0ubuntu2)" >> $(landscape_client_substvars) + echo "extra:Depends=python-pycurl, gir1.2-gudev-1.0 (>= 165-0ubuntu2), python-gi" >> $(landscape_client_substvars) echo "extra:Suggests=python-dbus, hal" >> $(landscape_client_substvars) endif diff -Nru landscape-client-12.04.3/debian/source/format landscape-client-12.05/debian/source/format --- landscape-client-12.04.3/debian/source/format 1969-12-31 21:00:00.000000000 -0300 +++ landscape-client-12.05/debian/source/format 2012-06-01 18:00:39.000000000 -0300 @@ -0,0 +1 @@ +3.0 (quilt) diff -Nru landscape-client-12.04.3/debian/source.lintian-overrides landscape-client-12.05/debian/source.lintian-overrides --- landscape-client-12.04.3/debian/source.lintian-overrides 1969-12-31 21:00:00.000000000 -0300 +++ landscape-client-12.05/debian/source.lintian-overrides 2012-06-01 18:01:27.000000000 -0300 @@ -0,0 +1,16 @@ +# we use dh_python or dh_python2 depending on the ubuntu release +# the package is being built on, this is detected dynamically +# in the rules file +landscape-client source: dh_python-is-obsolete + +# the package has to build on lucid, where the standards version +# is 3.8.2 +landscape-client source: ancient-standards-version + +# it's a bug that should be fixed in quantal +landscape-client source: unknown-field-in-dsc original-maintainer + +# this is only used in a very specific client upgrade from +# the dbus to the amp version +landscape-client: start-stop-daemon-in-maintainer-script postinst:130 + diff -Nru landscape-client-12.04.3/debian/watch landscape-client-12.05/debian/watch --- landscape-client-12.04.3/debian/watch 1969-12-31 21:00:00.000000000 -0300 +++ landscape-client-12.05/debian/watch 2012-06-02 18:58:33.000000000 -0300 @@ -0,0 +1,2 @@ +version=3 +https://launchpad.net/landscape-client/+download https://launchpad.net/landscape-client/[^/]+/[^/]+/\+download/landscape-client-(.*)\.tar\.(?:bz2|gz) diff -Nru landscape-client-12.04.3/landscape/constants.py landscape-client-12.05/landscape/constants.py --- landscape-client-12.04.3/landscape/constants.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/constants.py 2012-06-01 17:22:32.000000000 -0300 @@ -12,6 +12,7 @@ SUCCESS_RESULT = 1 ERROR_RESULT = 100 DEPENDENCY_ERROR_RESULT = 101 +CLIENT_VERSION_ERROR_RESULT = 102 POLICY_STRICT = 0 POLICY_ALLOW_INSTALLS = 1 POLICY_ALLOW_ALL_CHANGES = 2 @@ -25,8 +26,8 @@ # package reporter. # 2. We lost some package data, for example by a deb archive becoming # inaccessible for a while. The earliest we can reasonably assume that to be -# resolved is in 60 minutes, when the smart cronjob runs again. +# resolved is in 60 minutes, when the package reporter runs again. # So we'll give the problem one chance to resolve itself, by only waiting for -# one run of smart update. +# one run of apt-update. UNKNOWN_PACKAGE_DATA_TIMEOUT = 70 * 60 diff -Nru landscape-client-12.04.3/landscape/deployment.py landscape-client-12.05/landscape/deployment.py --- landscape-client-12.04.3/landscape/deployment.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/deployment.py 2012-06-01 17:22:32.000000000 -0300 @@ -282,7 +282,7 @@ - C{quiet} (C{False}) - C{log_dir} (C{"/var/log/landscape"}) - C{log_level} (C{"info"}) - - C{url} (C{"http://landcape.canonical.com/message-system"}) + - C{url} (C{"http://landscape.canonical.com/message-system"}) - C{ping_url} (C{"http://landscape.canonical.com/ping"}) - C{ssl_public_key} - C{server_autodiscover} (C{"false"}) diff -Nru landscape-client-12.04.3/landscape/__init__.py landscape-client-12.05/landscape/__init__.py --- landscape-client-12.04.3/landscape/__init__.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/__init__.py 2012-06-01 17:23:00.000000000 -0300 @@ -1,7 +1,7 @@ import sys DEBIAN_REVISION = "" -UPSTREAM_VERSION = "12.04.2" +UPSTREAM_VERSION = "12.05" VERSION = "%s%s" % (UPSTREAM_VERSION, DEBIAN_REVISION) # The "server-api" field of outgoing messages will be set to this value, and @@ -32,7 +32,11 @@ # * Add "policy" field to "change-packages" # * Add new "change-package-locks" client accepted message type. # -CLIENT_API = "3.3" +# 3.4: +# * Add "hold" field to "change-packages" +# * Add "remove-hold" field to "change-packages" + +CLIENT_API = "3.4" from twisted.python import util diff -Nru landscape-client-12.04.3/landscape/manager/aptsources.py landscape-client-12.05/landscape/manager/aptsources.py --- landscape-client-12.04.3/landscape/manager/aptsources.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/manager/aptsources.py 2012-06-01 17:22:32.000000000 -0300 @@ -156,8 +156,8 @@ """Once the repositories are modified, trigger a reporter run.""" reporter = find_reporter_command() - # Force a smart-update run, because the sources.list has changed - args = ["--force-smart-update"] + # Force an apt-update run, because the sources.list has changed + args = ["--force-apt-update"] if self.registry.config.config is not None: args.append("--config=%s" % self.registry.config.config) diff -Nru landscape-client-12.04.3/landscape/manager/tests/test_aptsources.py landscape-client-12.05/landscape/manager/tests/test_aptsources.py --- landscape-client-12.04.3/landscape/manager/tests/test_aptsources.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/manager/tests/test_aptsources.py 2012-06-01 17:22:32.000000000 -0300 @@ -388,7 +388,7 @@ def _run_process(command, args, env={}, path=None, uid=None, gid=None): self.assertEqual(find_reporter_command(), command) - self.assertEqual(["--force-smart-update", "--config=%s" % + self.assertEqual(["--force-apt-update", "--config=%s" % self.manager.config.config], args) deferred.callback(("ok", "", 0)) return deferred diff -Nru landscape-client-12.04.3/landscape/monitor/packagemonitor.py landscape-client-12.05/landscape/monitor/packagemonitor.py --- landscape-client-12.04.3/landscape/monitor/packagemonitor.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/monitor/packagemonitor.py 2012-06-01 17:22:32.000000000 -0300 @@ -58,7 +58,7 @@ class FakeFacade(object): """ - A fake facade to workaround the issue that the SmartFacade + A fake facade to workaround the issue that the AptFacade essentially allows only once instance per process. """ diff -Nru landscape-client-12.04.3/landscape/package/changer.py landscape-client-12.05/landscape/package/changer.py --- landscape-client-12.04.3/landscape/package/changer.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/changer.py 2012-06-01 17:22:32.000000000 -0300 @@ -9,8 +9,8 @@ from twisted.internet.defer import maybeDeferred, succeed from landscape.constants import ( - SUCCESS_RESULT, ERROR_RESULT, DEPENDENCY_ERROR_RESULT, POLICY_STRICT, - POLICY_ALLOW_INSTALLS, POLICY_ALLOW_ALL_CHANGES, + SUCCESS_RESULT, ERROR_RESULT, DEPENDENCY_ERROR_RESULT, + POLICY_STRICT, POLICY_ALLOW_INSTALLS, POLICY_ALLOW_ALL_CHANGES, UNKNOWN_PACKAGE_DATA_TIMEOUT) from landscape.lib.fs import create_file @@ -18,7 +18,7 @@ from landscape.package.taskhandler import ( PackageTaskHandler, PackageTaskHandlerConfiguration, PackageTaskError, run_task_handler) -from landscape.manager.manager import FAILED, SUCCEEDED +from landscape.manager.manager import FAILED class UnknownPackageData(Exception): @@ -38,7 +38,7 @@ """Value object to hold the results of change packages operation. @ivar code: The result code of the requested changes. - @ivar text: The output from Smart. + @ivar text: The output from Apt. @ivar installs: Possible additional packages that need to be installed in order to fulfill the request. @ivar removals: Possible additional packages that need to be removed @@ -80,10 +80,6 @@ # Nothing was done return - # In order to let the reporter run smart-update cleanly, - # we have to deinitialize Smart, so that the write lock - # gets released - self._facade.deinit() if os.getuid() == 0: os.setgid(grp.getgrnam("landscape").gr_gid) os.setuid(pwd.getpwnam("landscape").pw_uid) @@ -103,8 +99,6 @@ return result.addErrback(self.unknown_package_data_error, task) if message["type"] == "change-package-locks": return self.handle_change_package_locks(message) - if message["type"] == "change-package-holds": - return self.handle_change_package_holds(message) def unknown_package_data_error(self, failure, task): """Handle L{UnknownPackageData} data errors. @@ -145,7 +139,7 @@ self._facade.clear_channels() def init_channels(self, binaries=()): - """Initialize the Smart channels as needed. + """Initialize the Apt channels as needed. @param binaries: A possibly empty list of 3-tuples of the form (hash, id, deb), holding the hash, the id and the content of @@ -168,12 +162,16 @@ self._facade.ensure_channels_reloaded() - def mark_packages(self, upgrade=False, install=(), remove=(), reset=True): + def mark_packages(self, upgrade=False, install=(), remove=(), + hold=(), remove_hold=(), reset=True): """Mark packages for upgrade, installation or removal. @param upgrade: If C{True} mark all installed packages for upgrade. @param install: A list of package ids to be marked for installation. @param remove: A list of package ids to be marked for removal. + @param hold: A list of package ids to be marked for holding. + @param remove_hold: A list of package ids to be marked to have a hold + removed. @param reset: If C{True} all existing marks will be reset. """ if reset: @@ -182,16 +180,19 @@ if upgrade: self._facade.mark_global_upgrade() - for ids, mark_func in [(install, self._facade.mark_install), - (remove, self._facade.mark_remove)]: - for id in ids: - hash = self._store.get_id_hash(id) + for mark_function, mark_ids in [ + (self._facade.mark_install, install), + (self._facade.mark_remove, remove), + (self._facade.mark_hold, hold), + (self._facade.mark_remove_hold, remove_hold)]: + for mark_id in mark_ids: + hash = self._store.get_id_hash(mark_id) if hash is None: - raise UnknownPackageData(id) + raise UnknownPackageData(mark_id) package = self._facade.get_package_by_hash(hash) if package is None: raise UnknownPackageData(hash) - mark_func(package) + mark_function(package) def change_packages(self, policy): """Perform the requested changes. @@ -202,10 +203,9 @@ @return: A L{ChangePackagesResult} holding the details about the outcome of the requested changes. """ - # Delay importing these so that we don't import Smart unless + # Delay importing these so that we don't import Apt unless # we really need to. - from landscape.package.facade import ( - DependencyError, TransactionError, SmartError) + from landscape.package.facade import DependencyError, TransactionError result = ChangePackagesResult() count = 0 @@ -213,7 +213,7 @@ count += 1 try: result.text = self._facade.perform_changes() - except (TransactionError, SmartError), exception: + except TransactionError, exception: result.code = ERROR_RESULT result.text = exception.args[0] except DependencyError, exception: @@ -264,10 +264,11 @@ """Handle a C{change-packages} message.""" self.init_channels(message.get("binaries", ())) - self.mark_packages(message.get("upgrade-all", False), - message.get("install", ()), - message.get("remove", ())) - + self.mark_packages(upgrade=message.get("upgrade-all", False), + install=message.get("install", ()), + remove=message.get("remove", ()), + hold=message.get("hold", ()), + remove_hold=message.get("remove-hold", ())) result = self.change_packages(message.get("policy", POLICY_STRICT)) self._clear_binaries() @@ -289,100 +290,17 @@ def handle_change_package_locks(self, message): """Handle a C{change-package-locks} message. - Create and delete package locks as requested by the given C{message}. + Package locks aren't supported anymore. """ - if not self._facade.supports_package_locks: - response = { - "type": "operation-result", - "operation-id": message.get("operation-id"), - "status": FAILED, - "result-text": "This client doesn't support package locks.", - "result-code": 1} - return self._broker.send_message(response, True) - - for lock in message.get("create", ()): - self._facade.set_package_lock(*lock) - for lock in message.get("delete", ()): - self._facade.remove_package_lock(*lock) - self._facade.save_config() - - response = {"type": "operation-result", - "operation-id": message.get("operation-id"), - "status": SUCCEEDED, - "result-text": "Package locks successfully changed.", - "result-code": 0} - - logging.info("Queuing message with change package locks results to " - "exchange urgently.") - return self._broker.send_message(response, True) - - def _send_change_package_holds_response(self, response): - """Log that a package holds result is sent and send the response.""" - logging.info("Queuing message with change package holds results to " - "exchange urgently.") + response = { + "type": "operation-result", + "operation-id": message.get("operation-id"), + "status": FAILED, + "result-text": "This client doesn't support package locks.", + "result-code": 1} return self._broker.send_message(response, True) - def handle_change_package_holds(self, message): - """Handle a C{change-package-holds} message. - - Create and delete package holds as requested by the given C{message}. - """ - if not self._facade.supports_package_holds: - response = { - "type": "operation-result", - "operation-id": message.get("operation-id"), - "status": FAILED, - "result-text": "This client doesn't support package holds.", - "result-code": 1} - return self._send_change_package_holds_response(response) - - not_installed = set() - holds_to_create = message.get("create", []) - versions_to_create = set() - for id in holds_to_create: - hash = self._store.get_id_hash(id) - hold_version = self._facade.get_package_by_hash(hash) - if (hold_version - and self._facade.is_package_installed(hold_version)): - versions_to_create.add((hold_version.package, hold_version)) - else: - not_installed.add(str(id)) - holds_to_remove = message.get("delete", []) - versions_to_remove = set() - for id in holds_to_remove: - hash = self._store.get_id_hash(id) - hold_version = self._facade.get_package_by_hash(hash) - if (hold_version - and self._facade.is_package_installed(hold_version)): - versions_to_remove.add((hold_version.package, hold_version)) - - if not_installed: - response = { - "type": "operation-result", - "operation-id": message.get("operation-id"), - "status": FAILED, - "result-text": "Package holds not changed, since the" + - " following packages are not installed: %s" % ( - ", ".join(sorted(not_installed))), - "result-code": 1} - return self._send_change_package_holds_response(response) - - for package, hold_version in versions_to_create: - self._facade.set_package_hold(hold_version) - for package, hold_version in versions_to_remove: - self._facade.remove_package_hold(hold_version) - - self._facade.reload_channels() - - response = {"type": "operation-result", - "operation-id": message.get("operation-id"), - "status": SUCCEEDED, - "result-text": "Package holds successfully changed.", - "result-code": 0} - - return self._send_change_package_holds_response(response) - @staticmethod def find_command(): return find_changer_command() diff -Nru landscape-client-12.04.3/landscape/package/facade.py landscape-client-12.05/landscape/package/facade.py --- landscape-client-12.04.3/landscape/package/facade.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/facade.py 2012-06-01 17:22:32.000000000 -0300 @@ -5,15 +5,6 @@ from cStringIO import StringIO from operator import attrgetter -has_smart = True -try: - import smart - from smart.transaction import ( - Transaction, PolicyInstall, PolicyUpgrade, PolicyRemove, Failed) - from smart.const import INSTALL, REMOVE, UPGRADE, ALWAYS, NEVER -except ImportError: - has_smart = False - # Importing apt throws a FutureWarning on hardy, that we don't want to # see. import warnings @@ -24,19 +15,13 @@ import apt_inst import apt_pkg -has_new_enough_apt = True from aptsources.sourceslist import SourcesList -try: - from apt.progress.text import AcquireProgress - from apt.progress.base import InstallProgress -except ImportError: - AcquireProgress = object - InstallProgress = object - has_new_enough_apt = False +from apt.progress.text import AcquireProgress +from apt.progress.base import InstallProgress from landscape.lib.fs import append_file, create_file, read_file from landscape.constants import UBUNTU_PATH -from landscape.package.skeleton import build_skeleton, build_skeleton_apt +from landscape.package.skeleton import build_skeleton_apt class TransactionError(Exception): @@ -54,10 +39,6 @@ ", ".join([str(package) for package in self.packages])) -class SmartError(Exception): - """Raised when Smart fails in an undefined way.""" - - class ChannelError(Exception): """Raised when channels fail to load.""" @@ -108,8 +89,6 @@ database. """ - supports_package_holds = True - supports_package_locks = False _dpkg_status = "/var/lib/dpkg/status" def __init__(self, root=None): @@ -127,6 +106,8 @@ self._version_installs = [] self._global_upgrade = False self._version_removals = [] + self._version_hold_creations = [] + self._version_hold_removals = [] self.refetch_package_index = False def _ensure_dir_structure(self): @@ -150,9 +131,6 @@ os.makedirs(full_path) return full_path - def deinit(self): - """This method exists solely to be compatible with C{SmartFacade}.""" - def get_packages(self): """Get all the packages available in the channels.""" return self._hash2pkg.itervalues() @@ -167,17 +145,6 @@ if (self.is_package_installed(version) and self._is_package_held(version.package))] - def get_package_locks(self): - """Return all set package locks. - - @return: A C{list} of ternary tuples, contaning the name, relation - and version details for each lock currently set on the system. - - XXX: This method isn't implemented yet. It's here to make the - transition to Apt in the package reporter easier. - """ - return [] - def get_package_holds(self): """Return the name of all the packages that are on hold.""" return [version.package.name for version in self.get_locked_packages()] @@ -204,7 +171,8 @@ @param version: The version of the package to unhold. """ - if not self._is_package_held(version.package): + if (not self.is_package_installed(version) or + not self._is_package_held(version.package)): return self._set_dpkg_selections(version.package.name + " install") @@ -556,73 +524,65 @@ all_info.append(info + or_divider.join(relation_infos)) return "\n".join(all_info) - def perform_changes(self): - """Perform the pending package operations.""" - # Try to enforce non-interactivity + def _set_frontend_noninteractive(self): + """ + Set the environment to avoid attempts by apt to interact with a user. + """ os.environ["DEBIAN_FRONTEND"] = "noninteractive" os.environ["APT_LISTCHANGES_FRONTEND"] = "none" os.environ["APT_LISTBUGS_FRONTEND"] = "none" + + def _default_path_when_missing(self): + """ + If no PATH is set in the environment, use the Ubuntu default PATH. + + When the client is launched from the landscape-client-settings-ui the + PATH variable is incorrectly set, this method rectifies that. + """ # dpkg will fail if no path is set. if "PATH" not in os.environ: os.environ["PATH"] = UBUNTU_PATH + + def _setup_dpkg_for_changes(self): + """ + Setup environment and apt options for successful package operations. + """ + self._set_frontend_noninteractive() + self._default_path_when_missing() apt_pkg.config.clear("DPkg::options") apt_pkg.config.set("DPkg::options::", "--force-confold") - held_package_names = set() - package_installs = set( - version.package for version in self._version_installs) - package_upgrades = set( - version.package for version in self._version_removals - if version.package in package_installs) - version_changes = self._version_installs[:] - version_changes.extend(self._version_removals) - if not version_changes and not self._global_upgrade: + def _perform_hold_changes(self): + """ + Perform pending hold operations on packages. + """ + hold_changes = (len(self._version_hold_creations) > 0 or + len(self._version_hold_removals) > 0) + if not hold_changes: return None - fixer = apt_pkg.ProblemResolver(self._cache._depcache) - already_broken_packages = self._get_broken_packages() - for version in self._version_installs: - # Set the candidate version, so that the version we want to - # install actually is the one getting installed. - version.package.candidate = version - version.package.mark_install(auto_fix=False) - # If we need to resolve dependencies, try avoiding having - # the package we asked to be installed from being removed. - # (This is what would have been done if auto_fix would have - # been True. - fixer.clear(version.package._pkg) - fixer.protect(version.package._pkg) - if self._global_upgrade: - self._cache.upgrade(dist_upgrade=True) - for version in self._version_removals: - if self._is_package_held(version.package): - held_package_names.add(version.package.name) - if version.package in package_upgrades: - # The server requests the old version to be removed for - # upgrades, since Smart worked that way. For Apt we have - # to take care not to mark upgraded packages for # removal. - continue - version.package.mark_delete(auto_fix=False) - # Configure the resolver in the same way - # mark_delete(auto_fix=True) would have done. - fixer.clear(version.package._pkg) - fixer.protect(version.package._pkg) - fixer.remove(version.package._pkg) - fixer.install_protect() - - if held_package_names: + not_installed = [version for version in + self._version_hold_creations + if not self.is_package_installed(version)] + if not_installed: raise TransactionError( - "Can't perform the changes, since the following packages" + - " are held: %s" % ", ".join(sorted(held_package_names))) + "Cannot perform the changes, since the following " + + "packages are not installed: %s" % ", ".join( + [version.package.name + for version in sorted(not_installed)])) - now_broken_packages = self._get_broken_packages() - if now_broken_packages != already_broken_packages: - try: - fixer.resolve(True) - except SystemError, error: - raise TransactionError( - error.args[0] + "\n" + self._get_unmet_dependency_info()) - if not self._check_changes(version_changes): - return None + for version in self._version_hold_creations: + self.set_package_hold(version) + + for version in self._version_hold_removals: + self.remove_package_hold(version) + + return "Package holds successfully changed." + + def _commit_package_changes(self): + """ + Commit cached APT operations and give feedback on the results as a + string. + """ fetch_output = StringIO() # Redirect stdout and stderr to a file. We need to work with the # file descriptors, rather than sys.stdout/stderr, since dpkg is @@ -642,8 +602,9 @@ except SystemError, error: result_text = ( fetch_output.getvalue() + read_file(install_output_path)) - raise TransactionError( - error.args[0] + "\n\nPackage operation log:\n" + result_text) + raise TransactionError(error.args[0] + + "\n\nPackage operation log:\n" + + result_text) else: result_text = ( fetch_output.getvalue() + read_file(install_output_path)) @@ -654,422 +615,126 @@ os.remove(install_output_path) return result_text - def reset_marks(self): - """Clear the pending package operations.""" - del self._version_installs[:] - del self._version_removals[:] - self._global_upgrade = False - self._cache.clear() - - def mark_install(self, version): - """Mark the package for installation.""" - self._version_installs.append(version) - - def mark_global_upgrade(self): - """Upgrade all installed packages.""" - self._global_upgrade = True - - def mark_remove(self, version): - """Mark the package for removal.""" - self._version_removals.append(version) - - -class SmartFacade(object): - """Wrapper for tasks using Smart. - - This object wraps Smart features, in a way that makes using and testing - these features slightly more comfortable. - - @param smart_init_kwargs: A dictionary that can be used to pass specific - keyword parameters to to L{smart.init}. - """ - - _deb_package_type = None - supports_package_holds = False - supports_package_locks = True - - def __init__(self, smart_init_kwargs={}, sysconf_args=None): - if not has_smart: - raise RuntimeError( - "Smart needs to be installed if SmartFacade is used.") - self._smart_init_kwargs = smart_init_kwargs.copy() - self._smart_init_kwargs.setdefault("interface", "landscape") - self._sysconfig_args = sysconf_args or {} - self._reset() - - def _reset(self): - # This attribute is initialized lazily in the _get_ctrl() method. - self._ctrl = None - self._pkg2hash = {} - self._hash2pkg = {} - self._marks = {} - self._caching = ALWAYS - self._channels_reloaded = False - - def deinit(self): - """Deinitialize the Facade and the Smart library.""" - if self._ctrl: - smart.deinit() - self._reset() - - def _get_ctrl(self): - if self._ctrl is None: - if self._smart_init_kwargs.get("interface") == "landscape": - from landscape.package.interface import ( - install_landscape_interface) - install_landscape_interface() - self._ctrl = smart.init(**self._smart_init_kwargs) - for key, value in self._sysconfig_args.items(): - smart.sysconf.set(key, value, soft=True) - smart.initDistro(self._ctrl) - smart.initPlugins() - smart.sysconf.set("pm-iface-output", True, soft=True) - smart.sysconf.set("deb-non-interactive", True, soft=True) - - # We can't import it before hand because reaching .deb.* depends - # on initialization (yeah, sucky). - from smart.backends.deb.base import DebPackage - self._deb_package_type = DebPackage - - self.smart_initialized() - return self._ctrl - - def smart_initialized(self): - """Hook called when the Smart library is initialized.""" - - def ensure_channels_reloaded(self): - """Reload the channels if they haven't been reloaded yet.""" - if self._channels_reloaded: - return - self._channels_reloaded = True - self.reload_channels() - - def reload_channels(self, force_reload_binaries=False): - """ - Reload Smart channels, getting all the cache (packages) in memory. - - @raise: L{ChannelError} if Smart fails to reload the channels. - """ - ctrl = self._get_ctrl() - - try: - reload_result = ctrl.reloadChannels(caching=self._caching) - except smart.Error: - failed = True - else: - # Raise an error only if we are trying to download remote lists - failed = not reload_result and self._caching == NEVER - if failed: - raise ChannelError("Smart failed to reload channels (%s)" - % smart.sysconf.get("channels")) - - self._hash2pkg.clear() - self._pkg2hash.clear() - - for pkg in self.get_packages(): - hash = self.get_package_skeleton(pkg, False).get_hash() - self._hash2pkg[hash] = pkg - self._pkg2hash[pkg] = hash - - self.channels_reloaded() - - def channels_reloaded(self): - """Hook called after Smart channels are reloaded.""" - - def get_package_skeleton(self, pkg, with_info=True): - """Return a skeleton for the provided package. + def _preprocess_installs(self, fixer): + for version in self._version_installs: + # Set the candidate version, so that the version we want to + # install actually is the one getting installed. + version.package.candidate = version + # Set auto_fix=False to avoid removing the package we asked to + # install when we need to resolve dependencies. + version.package.mark_install(auto_fix=False) + fixer.clear(version.package._pkg) + fixer.protect(version.package._pkg) - The skeleton represents the basic structure of the package. + def _preprocess_removes(self, fixer): + held_package_names = set() - @param pkg: Package to build skeleton from. - @param with_info: If True, the skeleton will include information - useful for sending data to the server. Such information isn't - necessary if the skeleton will be used to build a hash. + package_installs = set( + version.package for version in self._version_installs) - @return: a L{PackageSkeleton} object. - """ - return build_skeleton(pkg, with_info) + package_upgrades = set( + version.package for version in self._version_removals + if version.package in package_installs) - def get_package_hash(self, pkg): - """Return a hash from the given package. + for version in self._version_removals: + if self._is_package_held(version.package): + held_package_names.add(version.package.name) + if version.package in package_upgrades: + # The server requests the old version to be removed for + # upgrades, since Smart worked that way. For Apt we have + # to take care not to mark upgraded packages for removal. + continue + version.package.mark_delete(auto_fix=False) + # Configure the resolver in the same way + # mark_delete(auto_fix=True) would have done. + fixer.clear(version.package._pkg) + fixer.protect(version.package._pkg) + fixer.remove(version.package._pkg) + fixer.install_protect() - @param pkg: a L{smart.backends.deb.base.DebPackage} objects - """ - return self._pkg2hash.get(pkg) + if held_package_names: + raise TransactionError( + "Can't perform the changes, since the following packages" + + " are held: %s" % ", ".join(sorted(held_package_names))) - def get_package_hashes(self): - """Get the hashes of all the packages available in the channels.""" - return self._pkg2hash.values() + def _preprocess_global_upgrade(self): + if self._global_upgrade: + self._cache.upgrade(dist_upgrade=True) - def get_packages(self): + def _resolve_broken_packages(self, fixer, already_broken_packages): """ - Get all the packages available in the channels. - - @return: a C{list} of L{smart.backends.deb.base.DebPackage} objects + Attempt to automatically resolve problems with broken packages. """ - return [pkg for pkg in self._get_ctrl().getCache().getPackages() - if isinstance(pkg, self._deb_package_type)] + now_broken_packages = self._get_broken_packages() + if now_broken_packages != already_broken_packages: + try: + fixer.resolve(True) + except SystemError, error: + raise TransactionError(error.args[0] + "\n" + + self._get_unmet_dependency_info()) - def get_locked_packages(self): - """Get all packages in the channels matching the set locks.""" - return smart.pkgconf.filterByFlag("lock", self.get_packages()) + def _preprocess_package_changes(self): + version_changes = self._version_installs[:] + version_changes.extend(self._version_removals) + if (not version_changes and not self._global_upgrade): + return [] + already_broken_packages = self._get_broken_packages() + fixer = apt_pkg.ProblemResolver(self._cache._depcache) + self._preprocess_installs(fixer) + self._preprocess_global_upgrade() + self._preprocess_removes(fixer) + self._resolve_broken_packages(fixer, already_broken_packages) + return version_changes - def get_packages_by_name(self, name): + def _perform_package_changes(self): """ - Get all available packages matching the provided name. - - @return: a C{list} of L{smart.backends.deb.base.DebPackage} objects + Perform pending install/remove/upgrade operations. """ - return [pkg for pkg in self._get_ctrl().getCache().getPackages(name) - if isinstance(pkg, self._deb_package_type)] + version_changes = self._preprocess_package_changes() + if not self._check_changes(version_changes): + return None + return self._commit_package_changes() - def get_package_by_hash(self, hash): + def perform_changes(self): """ - Get all available packages matching the provided hash. - - @return: a C{list} of L{smart.backends.deb.base.DebPackage} objects + Perform the pending package operations. """ - return self._hash2pkg.get(hash) - - def mark_install(self, pkg): - self._marks[pkg] = INSTALL + self._setup_dpkg_for_changes() + hold_result_text = self._perform_hold_changes() + package_result_text = self._perform_package_changes() + results = [] + if package_result_text is not None: + results.append(package_result_text) + if hold_result_text is not None: + results.append(hold_result_text) + if len(results) > 0: + return " ".join(results) - def mark_remove(self, pkg): - self._marks[pkg] = REMOVE + def reset_marks(self): + """Clear the pending package operations.""" + del self._version_installs[:] + del self._version_removals[:] + del self._version_hold_removals[:] + del self._version_hold_creations[:] + self._global_upgrade = False + self._cache.clear() - def mark_upgrade(self, pkg): - self._marks[pkg] = UPGRADE + def mark_install(self, version): + """Mark the package for installation.""" + self._version_installs.append(version) def mark_global_upgrade(self): """Upgrade all installed packages.""" - for package in self.get_packages(): - if self.is_package_installed(package): - self.mark_upgrade(package) - - def reset_marks(self): - self._marks.clear() - - def perform_changes(self): - ctrl = self._get_ctrl() - cache = ctrl.getCache() - - transaction = Transaction(cache) - - policy = PolicyInstall - - only_remove = True - for pkg, oper in self._marks.items(): - if oper == UPGRADE: - policy = PolicyUpgrade - if oper != REMOVE: - only_remove = False - transaction.enqueue(pkg, oper) - - if only_remove: - policy = PolicyRemove - - transaction.setPolicy(policy) - - try: - transaction.run() - except Failed, e: - raise TransactionError(e.args[0]) - changeset = transaction.getChangeSet() - - if not changeset: - return None # Nothing to do. - - missing = [] - for pkg, op in changeset.items(): - if self._marks.get(pkg) != op: - missing.append(pkg) - if missing: - raise DependencyError(missing) - - try: - self._ctrl.commitChangeSet(changeset) - except smart.Error, e: - raise TransactionError(e.args[0]) - - output = smart.iface.get_output_for_landscape() - failed = smart.iface.has_failed_for_landscape() - - smart.iface.reset_for_landscape() - - if failed: - raise SmartError(output) - return output - - def reload_cache(self): - cache = self._get_ctrl().getCache() - cache.reset() - cache.load() - - def get_arch(self): - """ - Get the host dpkg architecture. - """ - self._get_ctrl() - from smart.backends.deb.loader import DEBARCH - return DEBARCH - - def set_arch(self, arch): - """ - Set the host dpkg architecture. - - To take effect it must be called before L{reload_channels}. - - @param arch: the dpkg architecture to use (e.g. C{"i386"}) - """ - self._get_ctrl() - smart.sysconf.set("deb-arch", arch) - - # XXX workaround Smart setting DEBARCH statically in the - # smart.backends.deb.base module - import smart.backends.deb.loader as loader - loader.DEBARCH = arch - - def set_caching(self, mode): - """ - Set Smart's caching mode. - - @param mode: The caching mode to pass to Smart's C{reloadChannels} - when calling L{reload_channels} (e.g C{smart.const.NEVER} or - C{smart.const.ALWAYS}). - """ - self._caching = mode - - def reset_channels(self): - """Remove all configured Smart channels.""" - self._get_ctrl() - smart.sysconf.set("channels", {}, soft=True) - - def add_channel(self, alias, channel): - """ - Add a Smart channel. - - This method can be called more than once to set multiple channels. - To take effect it must be called before L{reload_channels}. - - @param alias: A string identifying the channel to be added. - @param channel: A C{dict} holding information about the channel to - add (see the Smart API for details about valid keys and values). - """ - channels = self.get_channels() - channels.update({alias: channel}) - smart.sysconf.set("channels", channels, soft=True) - - def add_channel_apt_deb(self, url, codename, components): - """Add a Smart channel of type C{"apt-deb"}. - - @see: L{add_channel} - """ - alias = codename - channel = {"baseurl": url, "distribution": codename, - "components": components, "type": "apt-deb"} - self.add_channel(alias, channel) - - def add_channel_deb_dir(self, path): - """Add a Smart channel of type C{"deb-dir"}. - - @see: L{add_channel} - """ - alias = path - channel = {"path": path, "type": "deb-dir"} - self.add_channel(alias, channel) - - def clear_channels(self): - """Clear channels. - - This method exists to be compatible with AptFacade. Smart - doesn't need to clear its channels. - """ - - def get_channels(self): - """ - @return: A C{dict} of all configured channels. - """ - self._get_ctrl() - return smart.sysconf.get("channels") - - def get_package_locks(self): - """Return all set package locks. - - @return: A C{list} of ternary tuples, contaning the name, relation - and version details for each lock currently set on the system. - """ - self._get_ctrl() - locks = [] - locks_by_name = smart.pkgconf.getFlagTargets("lock") - for name in locks_by_name: - for condition in locks_by_name[name]: - relation = condition[0] or "" - version = condition[1] or "" - locks.append((name, relation, version)) - return locks - - def _validate_lock_condition(self, relation, version): - if relation and not version: - raise RuntimeError("Package lock version not provided") - if version and not relation: - raise RuntimeError("Package lock relation not provided") - - def set_package_lock(self, name, relation=None, version=None): - """Set a new package lock. - - Any package matching the given name and possibly the given version - condition will be locked. - - @param name: The name a package must match in order to be locked. - @param relation: Optionally, the relation of the version condition the - package must satisfy in order to be considered as locked. - @param version: Optionally, the version associated with C{relation}. - - @note: If used at all, the C{relation} and C{version} parameter must be - both provided. - """ - self._validate_lock_condition(relation, version) - self._get_ctrl() - smart.pkgconf.setFlag("lock", name, relation, version) - - def remove_package_lock(self, name, relation=None, version=None): - """Remove a package lock.""" - self._validate_lock_condition(relation, version) - self._get_ctrl() - smart.pkgconf.clearFlag("lock", name=name, relation=relation, - version=version) - - def save_config(self): - """Flush the current smart configuration to disk.""" - control = self._get_ctrl() - control.saveSysConf() - - def is_package_installed(self, package): - """Is the package installed?""" - return package.installed + self._global_upgrade = True - def is_package_available(self, package): - """Is the package available for installation?""" - for loader in package.loaders: - # Is the package also in a non-installed - # loader? IOW, "available". - if not loader.getInstalled(): - return True - return False + def mark_remove(self, version): + """Mark the package for removal.""" + self._version_removals.append(version) - def is_package_upgrade(self, package): - """Is the package an upgrade for another installed package?""" - is_upgrade = False - for upgrade in package.upgrades: - for provides in upgrade.providedby: - for provides_package in provides.packages: - if provides_package.installed: - is_upgrade = True - break - else: - continue - break - else: - continue - break - return is_upgrade + def mark_hold(self, version): + """Mark the package to be held.""" + self._version_hold_creations.append(version) + + def mark_remove_hold(self, version): + """Mark the package to have its hold removed.""" + self._version_hold_removals.append(version) diff -Nru landscape-client-12.04.3/landscape/package/interface.py landscape-client-12.05/landscape/package/interface.py --- landscape-client-12.04.3/landscape/package/interface.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/interface.py 1969-12-31 21:00:00.000000000 -0300 @@ -1,84 +0,0 @@ -import logging -import types -import sys - -try: - import smart.interfaces - from smart.interface import Interface - from smart.const import ERROR, WARNING, INFO, DEBUG -except ImportError: - # Smart is optional if AptFacade is being used. - Interface = object - - -class LandscapeInterface(Interface): - - __output = "" - __failed = False - - def reset_for_landscape(self): - """Reset output and failed flag.""" - self.__failed = False - self.__output = u"" - - def get_output_for_landscape(self): - """showOutput() is cached, and returned by this method.""" - return self.__output - - def has_failed_for_landscape(self): - """Return true if any error() messages were logged.""" - return self.__failed - - def error(self, msg): - self.__failed = True - # Calling these logging.* functions here instead of message() - # below will output the message or not depending on the debug - # level set in landscape-client, rather than the one set in - # Smart's configuration. - logging.error("[Smart] %s", msg) - super(LandscapeInterface, self).error(msg) - - def warning(self, msg): - logging.warning("[Smart] %s", msg) - super(LandscapeInterface, self).warning(msg) - - def info(self, msg): - logging.info("[Smart] %s", msg) - super(LandscapeInterface, self).info(msg) - - def debug(self, msg): - logging.debug("[Smart] %s", msg) - super(LandscapeInterface, self).debug(msg) - - def message(self, level, msg): - prefix = {ERROR: "ERROR", WARNING: "WARNING", - INFO: "INFO", DEBUG: "DEBUG"}.get(level) - self.showOutput("%s: %s\n" % (prefix, msg)) - - def showOutput(self, output): - if not isinstance(output, unicode): - try: - output = output.decode("utf-8") - except UnicodeDecodeError: - output = output.decode("ascii", "replace") - self.__output += output - - -class LandscapeInterfaceModule(types.ModuleType): - - def __init__(self): - super(LandscapeInterfaceModule, self).__init__("landscape") - - def create(self, ctrl, command=None, argv=None): - return LandscapeInterface(ctrl) - - -def install_landscape_interface(): - if "smart.interfaces.landscape" not in sys.modules: - # Plug the interface in a place Smart will recognize. - smart.interfaces.landscape = LandscapeInterfaceModule() - sys.modules["smart.interfaces.landscape"] = smart.interfaces.landscape - - -def uninstall_landscape_interface(): - sys.modules.pop("smart.interfaces.landscape", None) diff -Nru landscape-client-12.04.3/landscape/package/releaseupgrader.py landscape-client-12.05/landscape/package/releaseupgrader.py --- landscape-client-12.04.3/landscape/package/releaseupgrader.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/releaseupgrader.py 2012-06-01 17:22:32.000000000 -0300 @@ -301,8 +301,8 @@ reporter = find_reporter_command() - # Force a smart-update run, because the sources.list has changed - args = ["--force-smart-update"] + # Force an apt-update run, because the sources.list has changed + args = ["--force-apt-update"] if self._config.config is not None: args.append("--config=%s" % self._config.config) diff -Nru landscape-client-12.04.3/landscape/package/reporter.py landscape-client-12.05/landscape/package/reporter.py --- landscape-client-12.04.3/landscape/package/reporter.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/reporter.py 2012-06-01 17:22:32.000000000 -0300 @@ -12,7 +12,6 @@ from landscape.lib.fs import touch_file from landscape.lib import bpickle -from landscape.package.facade import AptFacade from landscape.package.taskhandler import ( PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler) from landscape.package.store import UnknownHashIDRequest, FakePackageStore @@ -31,9 +30,9 @@ reporter-specific options. """ parser = super(PackageReporterConfiguration, self).make_parser() - parser.add_option("--force-smart-update", default=False, + parser.add_option("--force-apt-update", default=False, action="store_true", - help="Force running smart-update.") + help="Force running apt-update.") return parser @@ -41,15 +40,14 @@ """Report information about the system packages. @cvar queue_name: Name of the task queue to pick tasks from. - @cvar smart_update_interval: Time interval in minutes to pass to - the C{--after} command line option of C{smart-update}. + @cvar apt_update_interval: Don't update the APT index more often + than the given interval in minutes. """ config_factory = PackageReporterConfiguration queue_name = "reporter" - smart_update_interval = 60 - smart_update_filename = "/usr/lib/landscape/smart-update" + apt_update_interval = 60 apt_update_filename = "/usr/lib/landscape/apt-update" sources_list_filename = "/etc/apt/sources.list" sources_list_directory = "/etc/apt/sources.list.d" @@ -57,13 +55,7 @@ def run(self): result = Deferred() - if isinstance(self._facade, AptFacade): - # Update APT cache if APT facade is enabled. - result.addCallback(lambda x: self.run_apt_update()) - else: - # Run smart-update before anything else, to make sure that - # the SmartFacade will load freshly updated channels - result.addCallback(lambda x: self.run_smart_update()) + result.addCallback(lambda x: self.run_apt_update()) # If the appropriate hash=>id db is not there, fetch it result.addCallback(lambda x: self.fetch_hash_id_db()) @@ -183,44 +175,6 @@ return False - def run_smart_update(self): - """Run smart-update and log a warning in case of non-zero exit code. - - @return: a deferred returning (out, err, code) - """ - if self._config.force_smart_update or self._apt_sources_have_changed(): - args = () - else: - args = ("--after", str(self.smart_update_interval)) - result = spawn_process(self.smart_update_filename, args=args) - - def callback((out, err, code)): - # smart-update --after N will exit with error code 1 when it - # doesn't actually run the update code because to enough time - # has passed yet, but we don't actually consider it a failure. - smart_failed = False - if code != 0 and code != 1: - smart_failed = True - if code == 1 and err.strip() != "": - smart_failed = True - if smart_failed: - logging.warning("'%s' exited with status %d (%s)" % ( - self.smart_update_filename, code, err)) - logging.debug("'%s' exited with status %d (out='%s', err='%s'" % ( - self.smart_update_filename, code, out, err)) - touch_file(self._config.update_stamp_filename) - if not smart_failed and not self._facade.get_channels(): - code = 1 - err = "There are no APT sources configured in %s or %s." % ( - self.sources_list_filename, self.sources_list_directory) - deferred = self._broker.call_if_accepted( - "package-reporter-result", self.send_result, code, err) - deferred.addCallback(lambda ignore: (out, err, code)) - return deferred - - result.addCallback(callback) - return result - def _apt_update_timeout_expired(self, interval): """Check if the apt-update timeout has passed.""" stamp = self._config.update_stamp_filename @@ -237,8 +191,8 @@ @return: a deferred returning (out, err, code) """ - if (self._config.force_smart_update or self._apt_sources_have_changed() - or self._apt_update_timeout_expired(self.smart_update_interval)): + if (self._config.force_apt_update or self._apt_sources_have_changed() + or self._apt_update_timeout_expired(self.apt_update_interval)): result = spawn_process(self.apt_update_filename) @@ -323,7 +277,6 @@ self._store.clear_available_upgrades() self._store.clear_installed() self._store.clear_locked() - self._store.clear_package_locks() # Don't clear the hash_id_requests table because the messages # associated with the existing requests might still have to be @@ -405,7 +358,7 @@ def request_unknown_hashes(self): """Detect available packages for which we have no hash=>id mappings. - This method will verify if there are packages that Smart knows + This method will verify if there are packages that APT knows about but for which we don't have an id yet (no hash => id translation), and deliver a message (unknown-package-hashes) to request them. @@ -466,16 +419,13 @@ reactor. """ - def changes_detected(results): - # Release all smart locks, in case the changer runs after us. - self._facade.deinit() - if True in results: + def changes_detected(result): + if result: # Something has changed, notify the broker. return self._broker.fire_event("package-data-changed") - result = gather_results([self.detect_packages_changes(), - self.detect_package_locks_changes()]) - return result.addCallback(changes_detected) + deferred = self.detect_packages_changes() + return deferred.addCallback(changes_detected) def detect_packages_changes(self): """Detect changes in the universe of known packages. @@ -606,53 +556,6 @@ return True result.addCallback(update_currently_known) - - return result - - def detect_package_locks_changes(self): - """Detect changes in known package locks. - - This method will verify if there are package locks that: - - - are now set, and were not; - - were previously set but are not anymore; - - In all cases, the server is notified of the new situation - with a "packages" message. - - @return: A deferred resulting in C{True} if package lock changes were - detected with respect to the previous run, or C{False} otherwise. - """ - old_package_locks = set(self._store.get_package_locks()) - current_package_locks = set(self._facade.get_package_locks()) - - set_package_locks = current_package_locks - old_package_locks - unset_package_locks = old_package_locks - current_package_locks - - message = {} - if set_package_locks: - message["created"] = sorted(set_package_locks) - if unset_package_locks: - message["deleted"] = sorted(unset_package_locks) - - if not message: - return succeed(False) - - message["type"] = "package-locks" - result = self.send_message(message) - - logging.info("Queuing message with changes in known package locks:" - " %d created, %d deleted." % - (len(set_package_locks), len(unset_package_locks))) - - def update_currently_known(result): - if set_package_locks: - self._store.add_package_locks(set_package_locks) - if unset_package_locks: - self._store.remove_package_locks(unset_package_locks) - return True - - result.addCallback(update_currently_known) return result diff -Nru landscape-client-12.04.3/landscape/package/skeleton.py landscape-client-12.05/landscape/package/skeleton.py --- landscape-client-12.04.3/landscape/package/skeleton.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/skeleton.py 2012-06-01 17:22:32.000000000 -0300 @@ -47,54 +47,6 @@ return digest.digest() -def build_skeleton(pkg, with_info=False, with_unicode=False): - if not build_skeleton.inited: - build_skeleton.inited = True - global DebPackage, DebNameProvides, DebOrDepends - - # Importing from backends depends on smart.init(). - from smart.backends.deb.base import ( - DebPackage, DebNameProvides, DebOrDepends) - - if not isinstance(pkg, DebPackage): - raise PackageTypeError() - - if with_unicode: - skeleton = PackageSkeleton(DEB_PACKAGE, unicode(pkg.name), - unicode(pkg.version)) - else: - skeleton = PackageSkeleton(DEB_PACKAGE, pkg.name, pkg.version) - relations = set() - for relation in pkg.provides: - if isinstance(relation, DebNameProvides): - relations.add((DEB_NAME_PROVIDES, str(relation))) - else: - relations.add((DEB_PROVIDES, str(relation))) - for relation in pkg.requires: - if isinstance(relation, DebOrDepends): - relations.add((DEB_OR_REQUIRES, str(relation))) - else: - relations.add((DEB_REQUIRES, str(relation))) - for relation in pkg.upgrades: - relations.add((DEB_UPGRADES, str(relation))) - for relation in pkg.conflicts: - relations.add((DEB_CONFLICTS, str(relation))) - - skeleton.relations = sorted(relations) - - if with_info: - info = pkg.loaders.keys()[0].getInfo(pkg) - skeleton.section = info.getGroup() - skeleton.summary = info.getSummary() - skeleton.description = info.getDescription() - skeleton.size = sum(info.getSize(url) for url in info.getURLs()) - skeleton.installed_size = info.getInstalledSize() - - return skeleton - -build_skeleton.inited = False - - def relation_to_string(relation_tuple): """Convert an apt relation to a string representation. diff -Nru landscape-client-12.04.3/landscape/package/store.py landscape-client-12.05/landscape/package/store.py --- landscape-client-12.04.3/landscape/package/store.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/store.py 2012-06-01 17:22:32.000000000 -0300 @@ -249,40 +249,6 @@ cursor.execute("DELETE FROM locked") @with_cursor - def get_package_locks(self, cursor): - """Get all package locks.""" - cursor.execute("SELECT name, relation, version FROM package_locks") - return [(row[0], row[1], row[2]) for row in cursor.fetchall()] - - @with_cursor - def add_package_locks(self, cursor, locks): - """Add a list of package locks to the store. - - @param locks: A C{list} of ternary tuples each one contains the - name, the relation and the version of the package lock to be added. - """ - for name, relation, version in locks: - cursor.execute("REPLACE INTO package_locks VALUES (?, ?, ?)", - (name, relation or "", version or "",)) - - @with_cursor - def remove_package_locks(self, cursor, locks): - """Remove a list of package locks from the store. - - @param locks: A C{list} of ternary tuples each one contains the name, - the relation and the version of the package lock to be removed. - """ - for name, relation, version in locks: - cursor.execute("DELETE FROM package_locks WHERE name=? AND " - "relation=? AND version=?", - (name, relation or "", version or "")) - - @with_cursor - def clear_package_locks(self, cursor): - """Remove all package locks.""" - cursor.execute("DELETE FROM package_locks") - - @with_cursor def add_hash_id_request(self, cursor, hashes): hashes = list(hashes) cursor.execute("INSERT INTO hash_id_request (hashes, timestamp)" @@ -458,9 +424,6 @@ # try block. cursor = db.cursor() try: - cursor.execute("CREATE TABLE package_locks" - " (name TEXT NOT NULL, relation TEXT, version TEXT," - " UNIQUE(name, relation, version))") cursor.execute("CREATE TABLE locked" " (id INTEGER PRIMARY KEY)") cursor.execute("CREATE TABLE available" diff -Nru landscape-client-12.04.3/landscape/package/taskhandler.py landscape-client-12.05/landscape/package/taskhandler.py --- landscape-client-12.04.3/landscape/package/taskhandler.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/taskhandler.py 2012-06-01 17:22:32.000000000 -0300 @@ -254,19 +254,15 @@ words = re.findall("[A-Z][a-z]+", cls.__name__) init_logging(config, "-".join(word.lower() for word in words)) - # Setup our umask for Smart to use, this needs to setup file permissions to + # Setup our umask for Apt to use, this needs to setup file permissions to # 0644 so... os.umask(022) package_store = cls.package_store_class(config.store_filename) # Delay importing of the facades so that we don't - # import Smart unless we need to. - from landscape.package.facade import ( - AptFacade, SmartFacade, has_new_enough_apt) - if has_new_enough_apt: - package_facade = AptFacade() - else: - package_facade = SmartFacade() + # import Apt unless we need to. + from landscape.package.facade import AptFacade + package_facade = AptFacade() def finish(): connector.disconnect() diff -Nru landscape-client-12.04.3/landscape/package/tests/helpers.py landscape-client-12.05/landscape/package/tests/helpers.py --- landscape-client-12.04.3/landscape/package/tests/helpers.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/tests/helpers.py 2012-06-01 17:22:32.000000000 -0300 @@ -3,12 +3,6 @@ import textwrap import time -try: - import smart -except ImportError: - # Smart is optional if AptFacade is being used. - pass - import apt_inst import apt_pkg @@ -127,39 +121,6 @@ test_case.facade.add_channel_deb_dir(test_case.repository_dir) -class SmartHelper(object): - - def set_up(self, test_case): - test_case.smart_dir = test_case.makeDir() - test_case.smart_config = test_case.makeFile("") - test_case.repository_dir = test_case.makeDir() - create_simple_repository(test_case.repository_dir) - - def tear_down(self, test_case): - if smart.iface.object: - smart.deinit() - - -class SmartFacadeHelper(SmartHelper): - - def set_up(self, test_case): - super(SmartFacadeHelper, self).set_up(test_case) - - from landscape.package.facade import SmartFacade - - class Facade(SmartFacade): - repository_dir = test_case.repository_dir - - def smart_initialized(self): - self.reset_channels() - self.add_channel_deb_dir(test_case.repository_dir) - - test_case.Facade = Facade - test_case.facade = Facade({"datadir": test_case.smart_dir, - "configfile": test_case.smart_config}, - {"sync-apt-sources": False}) - - PKGNAME1 = "name1_version1-release1_all.deb" PKGNAME2 = "name2_version2-release2_all.deb" PKGNAME3 = "name3_version3-release3_all.deb" diff -Nru landscape-client-12.04.3/landscape/package/tests/test_changer.py landscape-client-12.05/landscape/package/tests/test_changer.py --- landscape-client-12.04.3/landscape/package/tests/test_changer.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/tests/test_changer.py 2012-06-01 17:22:32.000000000 -0300 @@ -6,12 +6,6 @@ from twisted.internet.defer import Deferred -try: - from smart.cache import Provides -except ImportError: - # Smart is optional if AptFacade is being used. - pass - from landscape.lib.fs import create_file, read_file, touch_file from landscape.package.changer import ( PackageChanger, main, find_changer_command, UNKNOWN_PACKAGE_DATA_TIMEOUT, @@ -19,19 +13,107 @@ POLICY_ALLOW_ALL_CHANGES, ERROR_RESULT) from landscape.package.store import PackageStore from landscape.package.facade import ( - DependencyError, TransactionError, SmartError, has_new_enough_apt) + DependencyError, TransactionError) from landscape.package.changer import ( PackageChangerConfiguration, ChangePackagesResult) from landscape.tests.mocker import ANY from landscape.tests.helpers import ( LandscapeTest, BrokerServiceHelper) from landscape.package.tests.helpers import ( - SmartFacadeHelper, HASH1, HASH2, HASH3, PKGDEB1, PKGDEB2, PKGNAME2, + HASH1, HASH2, HASH3, PKGDEB1, PKGDEB2, AptFacadeHelper, SimpleRepositoryHelper) -from landscape.manager.manager import FAILED, SUCCEEDED +from landscape.manager.manager import FAILED + + +class AptPackageChangerTest(LandscapeTest): + + helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] + + def setUp(self): + + def set_up(ignored): + + self.store = PackageStore(self.makeFile()) + self.config = PackageChangerConfiguration() + self.config.data_path = self.makeDir() + os.mkdir(self.config.package_directory) + os.mkdir(self.config.binaries_path) + touch_file(self.config.update_stamp_filename) + self.changer = PackageChanger( + self.store, self.facade, self.remote, self.config) + service = self.broker_service + service.message_store.set_accepted_types(["change-packages-result", + "operation-result"]) + + result = super(AptPackageChangerTest, self).setUp() + return result.addCallback(set_up) + + def set_pkg1_installed(self): + """Return the hash of a package that is installed.""" + self._add_system_package("foo") + self.facade.reload_channels() + [foo] = self.facade.get_packages_by_name("foo") + return self.facade.get_package_hash(foo) + + def set_pkg2_satisfied(self): + """Return the hash of a package that can be installed.""" + self._add_package_to_deb_dir(self.repository_dir, "bar") + self.facade.reload_channels() + [bar] = self.facade.get_packages_by_name("bar") + return self.facade.get_package_hash(bar) + + def set_pkg1_and_pkg2_satisfied(self): + """Make a package depend on another package. + + Return the hashes of the two packages. + """ + self._add_package_to_deb_dir( + self.repository_dir, "foo", control_fields={"Depends": "bar"}) + self._add_package_to_deb_dir(self.repository_dir, "bar") + self.facade.reload_channels() + [foo] = self.facade.get_packages_by_name("foo") + [bar] = self.facade.get_packages_by_name("bar") + return ( + self.facade.get_package_hash(foo), + self.facade.get_package_hash(bar)) + + def set_pkg2_upgrades_pkg1(self): + """Make it so that one package upgrades another. + + Return the hashes of the two packages. + """ + self._add_system_package("foo", version="1.0") + self._add_package_to_deb_dir(self.repository_dir, "foo", version="2.0") + self.facade.reload_channels() + foo_1, foo_2 = sorted(self.facade.get_packages_by_name("foo")) + return ( + self.facade.get_package_hash(foo_1), + self.facade.get_package_hash(foo_2)) + + def remove_pkg2(self): + """Remove package name2 from its repository.""" + packages_file = os.path.join(self.repository_dir, "Packages") + packages_contents = read_file(packages_file) + packages_contents = "\n\n".join( + [stanza for stanza in packages_contents.split("\n\n") + if "Package: name2" not in stanza]) + create_file(packages_file, packages_contents) + + def get_transaction_error_message(self): + """Return part of the apt transaction error message.""" + return "Unable to correct problems" + def get_binaries_channels(self, binaries_path): + """Return the channels that will be used for the binaries.""" + return [{"baseurl": "file://%s" % binaries_path, + "components": "", + "distribution": "./", + "type": "deb"}] + + def get_package_name(self, version): + """Return the name of the package.""" + return version.package.name -class PackageChangerTestMixin(object): def disable_clear_channels(self): """Disable clear_channels(), so that it doesn't remove test setup. @@ -441,7 +523,7 @@ def test_tasks_are_isolated_cache(self): """ - The package (apt/smart) cache should be reset between task runs. + The package (APT) cache should be reset between task runs. In this test, we try to run two different operations, first installing package 2, then removing package 1. Both tasks will fail for lack of superuser privileges. If the package cache @@ -533,7 +615,7 @@ """ Besides asking for individual changes, the server may also request the client to perform a global upgrade. This would be the equivalent - of a "smart upgrade" command being executed in the command line. + of a "apt-get upgrade" command being executed in the command line. """ hash1, hash2 = self.set_pkg2_upgrades_pkg1() self.store.set_hash_ids({hash1: 1, hash2: 2}) @@ -638,8 +720,7 @@ """ After the package changer has run, we want the package-reporter to run to report the recent changes. If we're running as root, we want to - change to the "landscape" user and "landscape" group. We also want to - deinitialize Smart to let the reporter run smart-update cleanly. + change to the "landscape" user and "landscape" group. """ # We are running as root @@ -647,13 +728,8 @@ getuid_mock() self.mocker.result(0) - # The order matters (first smart then gid and finally uid) self.mocker.order() - # Deinitialize smart - facade_mock = self.mocker.patch(self.facade) - facade_mock.deinit() - # We want to return a known gid grnam_mock = self.mocker.replace("grp.getgrnam") grnam_mock("landscape") @@ -810,27 +886,6 @@ "type": "change-packages-result"}]) return result.addCallback(got_result) - def test_smart_error_with_unicode_data(self): - self.store.set_hash_ids({HASH1: 1}) - self.store.add_task("changer", - {"type": "change-packages", "install": [1], - "operation-id": 123}) - - def raise_error(self): - raise SmartError(u"áéíóú") - self.replace_perform_changes(raise_error) - self.disable_clear_channels() - - result = self.changer.handle_tasks() - - def got_result(result): - self.assertMessages(self.get_pending_messages(), - [{"operation-id": 123, - "result-code": 100, - "result-text": u"áéíóú", - "type": "change-packages-result"}]) - return result.addCallback(got_result) - def test_update_stamp_exists(self): """ L{PackageChanger.update_exists} returns C{True} if the @@ -892,360 +947,6 @@ self.assertFalse(os.path.exists(existing_deb_path)) -class SmartPackageChangerTest(LandscapeTest, PackageChangerTestMixin): - - helpers = [SmartFacadeHelper, BrokerServiceHelper] - - def setUp(self): - - def set_up(ignored): - - self.store = PackageStore(self.makeFile()) - self.config = PackageChangerConfiguration() - self.config.data_path = self.makeDir() - os.mkdir(self.config.package_directory) - os.mkdir(self.config.binaries_path) - touch_file(self.config.update_stamp_filename) - self.changer = PackageChanger( - self.store, self.facade, self.remote, self.config) - service = self.broker_service - service.message_store.set_accepted_types(["change-packages-result", - "operation-result"]) - - result = super(SmartPackageChangerTest, self).setUp() - return result.addCallback(set_up) - - def set_pkg1_installed(self): - previous = self.Facade.channels_reloaded - - def callback(self): - previous(self) - self.get_packages_by_name("name1")[0].installed = True - self.Facade.channels_reloaded = callback - return HASH1 - - def set_pkg2_upgrades_pkg1(self): - previous = self.Facade.channels_reloaded - - def callback(self): - from smart.backends.deb.base import DebUpgrades - previous(self) - [pkg2] = self.get_packages_by_name("name2") - pkg2.upgrades += (DebUpgrades("name1", "=", "version1-release1"),) - self.reload_cache() # Relink relations. - self.Facade.channels_reloaded = callback - self.set_pkg2_satisfied() - self.set_pkg1_installed() - return HASH1, HASH2 - - def set_pkg2_satisfied(self): - previous = self.Facade.channels_reloaded - - def callback(self): - previous(self) - [pkg2] = self.get_packages_by_name("name2") - pkg2.requires = () - self.reload_cache() # Relink relations. - self.Facade.channels_reloaded = callback - return HASH2 - - def set_pkg1_and_pkg2_satisfied(self): - previous = self.Facade.channels_reloaded - - def callback(self): - previous(self) - - provide1 = Provides("prerequirename1", "prerequireversion1") - provide2 = Provides("requirename1", "requireversion1") - [pkg2] = self.get_packages_by_name("name2") - pkg2.provides += (provide1, provide2) - - provide1 = Provides("prerequirename2", "prerequireversion2") - provide2 = Provides("requirename2", "requireversion2") - [pkg1] = self.get_packages_by_name("name1") - pkg1.provides += (provide1, provide2) - - # Ask Smart to reprocess relationships. - self.reload_cache() - self.Facade.channels_reloaded = callback - return HASH1, HASH2 - - def remove_pkg2(self): - os.remove(os.path.join(self.repository_dir, PKGNAME2)) - - def get_transaction_error_message(self): - return "requirename1 = requireversion1" - - def get_binaries_channels(self, binaries_path): - return {binaries_path: {"type": "deb-dir", - "path": binaries_path}} - - def get_package_name(self, package): - return package.name - - def test_change_package_locks(self): - """ - The L{PackageChanger.handle_tasks} method appropriately creates and - deletes package locks as requested by the C{change-package-locks} - message. - """ - self.facade.set_package_lock("bar") - self.store.add_task("changer", {"type": "change-package-locks", - "create": [("foo", ">=", "1.0")], - "delete": [("bar", None, None)], - "operation-id": 123}) - - def assert_result(result): - self.facade.deinit() - self.assertEqual(self.facade.get_package_locks(), - [("foo", ">=", "1.0")]) - self.assertIn("Queuing message with change package locks results " - "to exchange urgently.", self.logfile.getvalue()) - self.assertMessages(self.get_pending_messages(), - [{"type": "operation-result", - "operation-id": 123, - "status": SUCCEEDED, - "result-text": "Package locks successfully" - " changed.", - "result-code": 0}]) - - result = self.changer.handle_tasks() - return result.addCallback(assert_result) - - def test_change_package_locks_create_with_already_existing(self): - """ - The L{PackageChanger.handle_tasks} method gracefully handles requests - for creating package locks that already exist. - """ - self.facade.set_package_lock("foo") - self.store.add_task("changer", {"type": "change-package-locks", - "create": [("foo", None, None)], - "operation-id": 123}) - - def assert_result(result): - self.facade.deinit() - self.assertEqual(self.facade.get_package_locks(), - [("foo", "", "")]) - self.assertMessages(self.get_pending_messages(), - [{"type": "operation-result", - "operation-id": 123, - "status": SUCCEEDED, - "result-text": "Package locks successfully" - " changed.", - "result-code": 0}]) - - result = self.changer.handle_tasks() - return result.addCallback(assert_result) - - def test_change_package_locks_delete_without_already_existing(self): - """ - The L{PackageChanger.handle_tasks} method gracefully handles requests - for deleting package locks that don't exist. - """ - self.store.add_task("changer", {"type": "change-package-locks", - "delete": [("foo", ">=", "1.0")], - "operation-id": 123}) - - def assert_result(result): - self.facade.deinit() - self.assertEqual(self.facade.get_package_locks(), []) - self.assertMessages(self.get_pending_messages(), - [{"type": "operation-result", - "operation-id": 123, - "status": SUCCEEDED, - "result-text": "Package locks successfully" - " changed.", - "result-code": 0}]) - - result = self.changer.handle_tasks() - return result.addCallback(assert_result) - - def test_dpkg_error(self): - """ - Verify that errors emitted by dpkg are correctly reported to - the server as problems. - - This test is to make sure that Smart reports the problem - correctly. It doesn't make sense for AptFacade, since there we - don't call dpkg. - """ - self.log_helper.ignore_errors(".*dpkg") - - installed_hash = self.set_pkg1_installed() - self.store.set_hash_ids({installed_hash: 1}) - self.store.add_task("changer", - {"type": "change-packages", "remove": [1], - "operation-id": 123}) - - result = self.changer.handle_tasks() - - def got_result(result): - messages = self.get_pending_messages() - self.assertEqual(len(messages), 1, "Too many messages") - message = messages[0] - self.assertEqual(message["operation-id"], 123) - self.assertEqual(message["result-code"], 100) - self.assertEqual(message["type"], "change-packages-result") - text = message["result-text"] - # We can't test the actual content of the message because the dpkg - # error can be localized - self.assertIn("\n[remove] name1_version1-release1\ndpkg: ", text) - self.assertIn("ERROR", text) - self.assertIn("(2)", text) - return result.addCallback(got_result) - - def test_change_package_holds(self): - """ - If C{SmartFacade} is used, the L{PackageChanger.handle_tasks} - method fails the activity, since it can't add or remove dpkg holds. - """ - self.facade.reload_channels() - self.store.add_task("changer", {"type": "change-package-holds", - "create": [1], - "delete": [2], - "operation-id": 123}) - - def assert_result(result): - self.assertIn("Queuing message with change package holds results " - "to exchange urgently.", self.logfile.getvalue()) - self.assertMessages( - self.get_pending_messages(), - [{"type": "operation-result", - "operation-id": 123, - "status": FAILED, - "result-text": "This client doesn't support package holds.", - "result-code": 1}]) - - result = self.changer.handle_tasks() - return result.addCallback(assert_result) - - def test_global_upgrade(self): - """ - Besides asking for individual changes, the server may also request - the client to perform a global upgrade. This would be the equivalent - of a "smart upgrade" command being executed in the command line. - - This test should be run for both C{AptFacade} and - C{SmartFacade}, but due to the smart test setting up that two - packages with different names upgrade each other, the message - doesn't correctly report that the old version should be - uninstalled. The test is still useful, since it shows that the - message contains the changes that smart says are needed. - - Making the test totally correct is a lot of work, that is not - worth doing, since we're removing smart soon. - """ - hash1, hash2 = self.set_pkg2_upgrades_pkg1() - self.store.set_hash_ids({hash1: 1, hash2: 2}) - - self.store.add_task("changer", - {"type": "change-packages", "upgrade-all": True, - "operation-id": 123}) - - result = self.changer.handle_tasks() - - def got_result(result): - self.assertMessages(self.get_pending_messages(), - [{"operation-id": 123, - "must-install": [2], - "result-code": 101, - "type": "change-packages-result"}]) - - return result.addCallback(got_result) - - -class AptPackageChangerTest(LandscapeTest, PackageChangerTestMixin): - - if not has_new_enough_apt: - skip = "Can't use AptFacade on hardy" - - helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] - - def setUp(self): - - def set_up(ignored): - - self.store = PackageStore(self.makeFile()) - self.config = PackageChangerConfiguration() - self.config.data_path = self.makeDir() - os.mkdir(self.config.package_directory) - os.mkdir(self.config.binaries_path) - touch_file(self.config.update_stamp_filename) - self.changer = PackageChanger( - self.store, self.facade, self.remote, self.config) - service = self.broker_service - service.message_store.set_accepted_types(["change-packages-result", - "operation-result"]) - - result = super(AptPackageChangerTest, self).setUp() - return result.addCallback(set_up) - - def set_pkg1_installed(self): - """Return the hash of a package that is installed.""" - self._add_system_package("foo") - self.facade.reload_channels() - [foo] = self.facade.get_packages_by_name("foo") - return self.facade.get_package_hash(foo) - - def set_pkg2_satisfied(self): - """Return the hash of a package that can be installed.""" - self._add_package_to_deb_dir(self.repository_dir, "bar") - self.facade.reload_channels() - [bar] = self.facade.get_packages_by_name("bar") - return self.facade.get_package_hash(bar) - - def set_pkg1_and_pkg2_satisfied(self): - """Make a package depend on another package. - - Return the hashes of the two packages. - """ - self._add_package_to_deb_dir( - self.repository_dir, "foo", control_fields={"Depends": "bar"}) - self._add_package_to_deb_dir(self.repository_dir, "bar") - self.facade.reload_channels() - [foo] = self.facade.get_packages_by_name("foo") - [bar] = self.facade.get_packages_by_name("bar") - return ( - self.facade.get_package_hash(foo), - self.facade.get_package_hash(bar)) - - def set_pkg2_upgrades_pkg1(self): - """Make it so that one package upgrades another. - - Return the hashes of the two packages. - """ - self._add_system_package("foo", version="1.0") - self._add_package_to_deb_dir(self.repository_dir, "foo", version="2.0") - self.facade.reload_channels() - foo_1, foo_2 = sorted(self.facade.get_packages_by_name("foo")) - return ( - self.facade.get_package_hash(foo_1), - self.facade.get_package_hash(foo_2)) - - def remove_pkg2(self): - """Remove package name2 from its repository.""" - packages_file = os.path.join(self.repository_dir, "Packages") - packages_contents = read_file(packages_file) - packages_contents = "\n\n".join( - [stanza for stanza in packages_contents.split("\n\n") - if "Package: name2" not in stanza]) - create_file(packages_file, packages_contents) - - def get_transaction_error_message(self): - """Return part of the apt transaction error message.""" - return "Unable to correct problems" - - def get_binaries_channels(self, binaries_path): - """Return the channels that will be used for the binaries.""" - return [{"baseurl": "file://%s" % binaries_path, - "components": "", - "distribution": "./", - "type": "deb"}] - - def get_package_name(self, version): - """Return the name of the package.""" - return version.package.name - def test_binaries_available_in_cache(self): """ If binaries are included in the changes-packages message, those @@ -1275,7 +976,7 @@ def test_change_package_holds(self): """ The L{PackageChanger.handle_tasks} method appropriately creates and - deletes package holds as requested by the C{change-package-holds} + deletes package holds as requested by the C{change-packages} message. """ self._add_system_package("foo") @@ -1292,23 +993,22 @@ old_mtime = time.time() - 10 os.utime(self.facade._dpkg_status, (old_mtime, old_mtime)) self.facade.reload_channels() - self.store.add_task("changer", {"type": "change-package-holds", - "create": [foo.package.id], - "delete": [bar.package.id], + self.store.add_task("changer", {"type": "change-packages", + "hold": [foo.package.id], + "remove-hold": [bar.package.id], "operation-id": 123}) def assert_result(result): self.facade.reload_channels() self.assertEqual(["foo"], self.facade.get_package_holds()) - self.assertIn("Queuing message with change package holds results " + self.assertIn("Queuing response with change package results " "to exchange urgently.", self.logfile.getvalue()) self.assertMessages( self.get_pending_messages(), - [{"type": "operation-result", + [{"type": "change-packages-result", "operation-id": 123, - "status": SUCCEEDED, "result-text": "Package holds successfully changed.", - "result-code": 0}]) + "result-code": 1}]) result = self.changer.handle_tasks() return result.addCallback(assert_result) @@ -1316,7 +1016,7 @@ def test_create_package_holds_with_identical_version(self): """ The L{PackageChanger.handle_tasks} method appropriately creates - holds as requested by the C{change-package-holds} message even + holds as requested by the C{change-packages} message even when versions from two different packages are the same. """ self._add_system_package("foo", version="1.1") @@ -1327,12 +1027,13 @@ [foo] = self.facade.get_packages_by_name("foo") [bar] = self.facade.get_packages_by_name("bar") self.facade.reload_channels() - self.store.add_task("changer", {"type": "change-package-holds", - "create": [foo.package.id, - bar.package.id], + self.store.add_task("changer", {"type": "change-packages", + "hold": [foo.package.id, + bar.package.id], "operation-id": 123}) def assert_result(result): + self.facade.reload_channels() self.assertEqual(["foo", "bar"], self.facade.get_package_holds()) result = self.changer.handle_tasks() @@ -1341,7 +1042,7 @@ def test_delete_package_holds_with_identical_version(self): """ The L{PackageChanger.handle_tasks} method appropriately deletes - holds as requested by the C{change-package-holds} message even + holds as requested by the C{change-packages} message even when versions from two different packages are the same. """ self._add_system_package("foo", version="1.1") @@ -1354,12 +1055,13 @@ self.facade.set_package_hold(foo) self.facade.set_package_hold(bar) self.facade.reload_channels() - self.store.add_task("changer", {"type": "change-package-holds", - "delete": [foo.package.id, - bar.package.id], + self.store.add_task("changer", {"type": "change-packages", + "remove-hold": [foo.package.id, + bar.package.id], "operation-id": 123}) def assert_result(result): + self.facade.reload_channels() self.assertEqual([], self.facade.get_package_holds()) result = self.changer.handle_tasks() @@ -1367,7 +1069,7 @@ def test_change_package_holds_create_already_held(self): """ - If the C{change-package-holds} message requests to add holds for + If the C{change-packages} message requests to add holds for packages that are already held, the activity succeeds, since the end result is that the requested package holds are there. """ @@ -1377,29 +1079,28 @@ [foo] = self.facade.get_packages_by_name("foo") self.facade.set_package_hold(foo) self.facade.reload_channels() - self.store.add_task("changer", {"type": "change-package-holds", - "create": [foo.package.id], + self.store.add_task("changer", {"type": "change-packages", + "hold": [foo.package.id], "operation-id": 123}) def assert_result(result): self.facade.reload_channels() self.assertEqual(["foo"], self.facade.get_package_holds()) - self.assertIn("Queuing message with change package holds results " + self.assertIn("Queuing response with change package results " "to exchange urgently.", self.logfile.getvalue()) self.assertMessages( self.get_pending_messages(), - [{"type": "operation-result", + [{"type": "change-packages-result", "operation-id": 123, - "status": SUCCEEDED, "result-text": "Package holds successfully changed.", - "result-code": 0}]) + "result-code": 1}]) result = self.changer.handle_tasks() return result.addCallback(assert_result) def test_change_package_holds_create_other_version_installed(self): """ - If the C{change-package-holds} message requests to add holds for + If the C{change-packages} message requests to add holds for packages that have a different version installed than the one being requested to hold, the activity fails. @@ -1420,30 +1121,29 @@ self.facade.get_package_hash(bar1): 3, self.facade.get_package_hash(bar2): 4}) self.facade.reload_channels() - self.store.add_task("changer", {"type": "change-package-holds", - "create": [2, 3], + self.store.add_task("changer", {"type": "change-packages", + "hold": [2, 3], "operation-id": 123}) def assert_result(result): self.facade.reload_channels() self.assertEqual([], self.facade.get_package_holds()) - self.assertIn("Queuing message with change package holds results " + self.assertIn("Queuing response with change package results " "to exchange urgently.", self.logfile.getvalue()) self.assertMessages( self.get_pending_messages(), - [{"type": "operation-result", + [{"type": "change-packages-result", "operation-id": 123, - "status": FAILED, - "result-text": "Package holds not changed, since the" + - " following packages are not installed: 2", - "result-code": 1}]) + "result-text": "Cannot perform the changes, since the" + + " following packages are not installed: foo", + "result-code": 100}]) result = self.changer.handle_tasks() return result.addCallback(assert_result) def test_change_package_holds_create_not_installed(self): """ - If the C{change-package-holds} message requests to add holds for + If the C{change-packages} message requests to add holds for packages that aren't installed, the whole activity is failed. If multiple holds are specified, those won't be added. There's no difference between a package that is available in some @@ -1460,62 +1160,71 @@ [foo] = self.facade.get_packages_by_name("foo") [bar] = self.facade.get_packages_by_name("bar") [baz] = self.facade.get_packages_by_name("baz") - self.store.add_task("changer", {"type": "change-package-holds", - "create": [foo.package.id, - bar.package.id, - baz.package.id], + self.store.add_task("changer", {"type": "change-packages", + "hold": [foo.package.id, + bar.package.id, + baz.package.id], "operation-id": 123}) def assert_result(result): self.facade.reload_channels() self.assertEqual([], self.facade.get_package_holds()) - self.assertIn("Queuing message with change package holds results " + self.assertIn("Queuing response with change package results " "to exchange urgently.", self.logfile.getvalue()) self.assertMessages( self.get_pending_messages(), - [{"type": "operation-result", + [{"type": "change-packages-result", "operation-id": 123, - "status": FAILED, - "result-text": "Package holds not changed, since the " + "result-text": "Cannot perform the changes, since the " "following packages are not installed: " - "%s, %s" % tuple(sorted([bar.package.id, - baz.package.id])), - "result-code": 1}]) + "%s, %s" % tuple(sorted([bar.package.name, + baz.package.name])), + "result-code": 100}]) result = self.changer.handle_tasks() return result.addCallback(assert_result) def test_change_package_holds_create_unknown_hash(self): """ - If the C{change-package-holds} message requests to add holds for - packages that the client doesn't know about, it's being treated - as the packages not being installed. + If the C{change-packages} message requests to add holds for + packages that the client doesn't know about results in a not yet + synchronized message and a failure of the operation. """ - self.facade.reload_channels() - self.store.add_task("changer", {"type": "change-package-holds", - "create": [1], - "operation-id": 123}) - def assert_result(result): - self.facade.reload_channels() - self.assertEqual([], self.facade.get_package_holds()) - self.assertIn("Queuing message with change package holds results " - "to exchange urgently.", self.logfile.getvalue()) - self.assertMessages( - self.get_pending_messages(), - [{"type": "operation-result", - "operation-id": 123, - "status": FAILED, - "result-text": "Package holds not changed, since the" + - " following packages are not installed: 1", - "result-code": 1}]) + self.store.add_task("changer", + {"type": "change-packages", + "hold": [123], + "operation-id": 123}) - result = self.changer.handle_tasks() - return result.addCallback(assert_result) + time_mock = self.mocker.replace("time.time") + time_mock() + self.mocker.result(time.time() + UNKNOWN_PACKAGE_DATA_TIMEOUT) + self.mocker.count(1, None) + self.mocker.replay() + + try: + result = self.changer.handle_tasks() + self.mocker.verify() + finally: + # Reset it earlier so that Twisted has the true time function. + self.mocker.reset() + + self.assertIn("Package data not yet synchronized with server (123)", + self.logfile.getvalue()) + + def got_result(result): + message = {"type": "change-packages-result", + "operation-id": 123, + "result-code": 100, + "result-text": "Package data has changed. " + "Please retry the operation."} + self.assertMessages(self.get_pending_messages(), [message]) + self.assertEqual(self.store.get_next_task("changer"), None) + return result.addCallback(got_result) def test_change_package_holds_delete_not_held(self): """ - If the C{change-package-holds} message requests to remove holds + If the C{change-packages} message requests to remove holds for packages that aren't held, the activity succeeds if the right version is installed, since the end result is that the hold is removed. @@ -1524,29 +1233,28 @@ self.facade.reload_channels() self._hash_packages_by_name(self.facade, self.store, "foo") [foo] = self.facade.get_packages_by_name("foo") - self.store.add_task("changer", {"type": "change-package-holds", - "delete": [foo.package.id], + self.store.add_task("changer", {"type": "change-packages", + "remove-hold": [foo.package.id], "operation-id": 123}) def assert_result(result): self.facade.reload_channels() self.assertEqual([], self.facade.get_package_holds()) - self.assertIn("Queuing message with change package holds results " + self.assertIn("Queuing response with change package results " "to exchange urgently.", self.logfile.getvalue()) self.assertMessages( self.get_pending_messages(), - [{"type": "operation-result", + [{"type": "change-packages-result", "operation-id": 123, - "status": SUCCEEDED, "result-text": "Package holds successfully changed.", - "result-code": 0}]) + "result-code": 1}]) result = self.changer.handle_tasks() return result.addCallback(assert_result) def test_change_package_holds_delete_different_version_held(self): """ - If the C{change-package-holds} message requests to remove holds + If the C{change-packages} message requests to remove holds for packages that aren't held, the activity succeeds if the right version is installed, since the end result is that the hold is removed. @@ -1556,60 +1264,34 @@ self.repository_dir, "foo", version="2.0") self.facade.reload_channels() [foo1, foo2] = sorted(self.facade.get_packages_by_name("foo")) - self.facade.set_package_hold(foo1) self.store.set_hash_ids({self.facade.get_package_hash(foo1): 1, self.facade.get_package_hash(foo2): 2}) + self.facade.mark_install(foo1) + self.facade.mark_hold(foo1) + self.facade.perform_changes() self.facade.reload_channels() - self.store.add_task("changer", {"type": "change-package-holds", - "delete": [2], + self.store.add_task("changer", {"type": "change-packages", + "remove-hold": [2], "operation-id": 123}) def assert_result(result): self.facade.reload_channels() self.assertEqual(["foo"], self.facade.get_package_holds()) - self.assertIn("Queuing message with change package holds results " - "to exchange urgently.", self.logfile.getvalue()) - self.assertMessages( - self.get_pending_messages(), - [{"type": "operation-result", - "operation-id": 123, - "status": SUCCEEDED, - "result-text": "Package holds successfully changed.", - "result-code": 0}]) - - result = self.changer.handle_tasks() - return result.addCallback(assert_result) - - def test_change_package_holds_delete_unknown_hash(self): - """ - If the C{change-package-holds} message requests to remove holds - for packages that aren't known by the client, the activity - succeeds, since the end result is that the package isn't - held at that version. - """ - self.store.add_task("changer", {"type": "change-package-holds", - "delete": [1], - "operation-id": 123}) - - def assert_result(result): - self.facade.reload_channels() - self.assertEqual([], self.facade.get_package_holds()) - self.assertIn("Queuing message with change package holds results " + self.assertIn("Queuing response with change package results " "to exchange urgently.", self.logfile.getvalue()) self.assertMessages( self.get_pending_messages(), - [{"type": "operation-result", + [{"type": "change-packages-result", "operation-id": 123, - "status": SUCCEEDED, "result-text": "Package holds successfully changed.", - "result-code": 0}]) + "result-code": 1}]) result = self.changer.handle_tasks() return result.addCallback(assert_result) def test_change_package_holds_delete_not_installed(self): """ - If the C{change-package-holds} message requests to remove holds + If the C{change-packages} message requests to remove holds for packages that aren't installed, the activity succeeds, since the end result is still that the package isn't held at the requested version. @@ -1618,31 +1300,30 @@ self.facade.reload_channels() self._hash_packages_by_name(self.facade, self.store, "foo") [foo] = self.facade.get_packages_by_name("foo") - self.store.add_task("changer", {"type": "change-package-holds", - "delete": [foo.package.id], + self.store.add_task("changer", {"type": "change-packages", + "remove-hold": [foo.package.id], "operation-id": 123}) def assert_result(result): self.facade.reload_channels() self.assertEqual([], self.facade.get_package_holds()) - self.assertIn("Queuing message with change package holds results " + self.assertIn("Queuing response with change package results " "to exchange urgently.", self.logfile.getvalue()) self.assertMessages( self.get_pending_messages(), - [{"type": "operation-result", + [{"type": "change-packages-result", "operation-id": 123, - "status": SUCCEEDED, "result-text": "Package holds successfully changed.", - "result-code": 0}]) + "result-code": 1}]) result = self.changer.handle_tasks() return result.addCallback(assert_result) def test_change_package_locks(self): """ - If C{AptFacade} is used, the L{PackageChanger.handle_tasks} - method fails the activity, since it can't add or remove locks because - apt doesn't support this. + The L{PackageChanger.handle_tasks} method fails + change-package-locks activities, since it can't add or remove + locks because apt doesn't support this. """ self.store.add_task("changer", {"type": "change-package-locks", "create": [("foo", ">=", "1.0")], diff -Nru landscape-client-12.04.3/landscape/package/tests/test_facade.py landscape-client-12.05/landscape/package/tests/test_facade.py --- landscape-client-12.04.3/landscape/package/tests/test_facade.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/tests/test_facade.py 2012-06-01 17:22:32.000000000 -0300 @@ -1,40 +1,22 @@ -import time import os -import re -import sys import textwrap import tempfile -try: - import smart - from smart.control import Control - from smart.cache import Provides - from smart.const import NEVER, ALWAYS -except ImportError: - # Smart is optional if AptFacade is being used. - pass - import apt_pkg from apt.package import Package from aptsources.sourceslist import SourcesList -from twisted.internet import reactor -from twisted.internet.defer import Deferred -from twisted.internet.utils import getProcessOutputAndValue - from landscape.constants import UBUNTU_PATH from landscape.lib.fs import read_file, create_file -from landscape.package import facade as facade_module from landscape.package.facade import ( - TransactionError, DependencyError, ChannelError, SmartError, AptFacade, - has_new_enough_apt) + TransactionError, DependencyError, ChannelError, AptFacade) from landscape.tests.mocker import ANY from landscape.tests.helpers import LandscapeTest, EnvironSaverHelper from landscape.package.tests.helpers import ( - SmartFacadeHelper, HASH1, HASH2, HASH3, PKGNAME1, PKGNAME2, PKGNAME3, - PKGNAME4, PKGDEB4, PKGDEB1, PKGNAME_MINIMAL, PKGDEB_MINIMAL, - create_full_repository, create_deb, AptFacadeHelper, + HASH1, HASH2, HASH3, PKGNAME1, PKGNAME2, PKGNAME3, + PKGDEB1, PKGNAME_MINIMAL, PKGDEB_MINIMAL, + create_deb, AptFacadeHelper, create_simple_repository) @@ -60,9 +42,6 @@ class AptFacadeTest(LandscapeTest): - if not has_new_enough_apt: - skip = "Can't use AptFacade on hardy" - helpers = [AptFacadeHelper, EnvironSaverHelper] def version_sortkey(self, version): @@ -1564,16 +1543,24 @@ self._add_package_to_deb_dir(deb_dir, "bar", version="1.5") self._add_system_package("baz") self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./") + self._add_system_package("quux", version="1.0") + self._add_system_package("wibble", version="1.0") self.facade.reload_channels() [foo] = self.facade.get_packages_by_name("foo") self.facade.mark_install(foo) self.facade.mark_global_upgrade() [baz] = self.facade.get_packages_by_name("baz") self.facade.mark_remove(baz) + [quux] = self.facade.get_packages_by_name("quux") + self.facade.mark_hold(quux) + [wibble] = self.facade.get_packages_by_name("wibble") + self.facade.mark_remove_hold(wibble) self.facade.reset_marks() self.assertEqual(self.facade._version_installs, []) self.assertEqual(self.facade._version_removals, []) self.assertFalse(self.facade._global_upgrade) + self.assertEqual(self.facade._version_hold_creations, []) + self.assertEqual(self.facade._version_hold_removals, []) self.assertEqual(self.facade.perform_changes(), None) def test_reset_marks_resets_cache(self): @@ -2349,50 +2336,81 @@ self.assertEqual( ["baz", "foo"], sorted(self.facade.get_package_holds())) - def test_set_package_hold(self): + def test_mark_hold_and_perform_hold_changes(self): """ - C{set_package_hold} marks a package to be on hold. + Test that L{perform_hold_changes} holds packages that have previously + been marked for hold. """ self._add_system_package("foo") self.facade.reload_channels() [foo] = self.facade.get_packages_by_name("foo") - self.facade.set_package_hold(foo) + self.facade.mark_hold(foo) + self.assertEqual("Package holds successfully changed.", + self.facade._perform_hold_changes()) self.facade.reload_channels() + self.assertEqual(["foo"], self.facade.get_package_holds()) + def test_mark_hold(self): + """ + C{mark_hold} marks a package to be held. + """ + self._add_system_package("foo") + self.facade.reload_channels() + [foo] = self.facade.get_packages_by_name("foo") + self.facade.mark_hold(foo) + self.facade.perform_changes() + self.facade.reload_channels() self.assertEqual(["foo"], self.facade.get_package_holds()) - def test_set_package_hold_existing_hold(self): + def test_two_holds_with_the_same_version_id(self): + """ + Test C{mark_hold} can distinguish between two different packages with + the same version number (the version number is used to make the unique + hash for the package version). + """ + self._add_system_package("foo", version="1.0") + self._add_system_package("bar", version="1.0") + self.facade.reload_channels() + [foo] = self.facade.get_packages_by_name("foo") + [bar] = self.facade.get_packages_by_name("bar") + self.facade.mark_hold(foo) + self.facade.mark_hold(bar) + self.assertEqual(2, len(self.facade._version_hold_creations)) + + def test_mark_hold_existing_hold(self): """ - If a package is already hel, C{set_package_hold} doesn't return - an error. + If a package is already held, C{mark_hold} and + C{perform_changes} won't return an error. """ self._add_system_package( "foo", control_fields={"Status": "hold ok installed"}) self.facade.reload_channels() [foo] = self.facade.get_packages_by_name("foo") - self.facade.set_package_hold(foo) + self.facade.mark_hold(foo) + self.facade.perform_changes() self.facade.reload_channels() self.assertEqual(["foo"], self.facade.get_package_holds()) - def test_remove_package_hold(self): + def test_mark_remove_hold(self): """ - C{remove_package_hold} marks a package not to be on hold. + C{mark_remove_hold} marks a package as not held. """ self._add_system_package( "foo", control_fields={"Status": "hold ok installed"}) self.facade.reload_channels() [foo] = self.facade.get_packages_by_name("foo") - self.facade.remove_package_hold(foo) + self.facade.mark_remove_hold(foo) + self.facade.perform_changes() self.facade.reload_channels() self.assertEqual([], self.facade.get_package_holds()) - def test_remove_package_hold_no_package(self): + def test_mark_remove_hold_no_package(self): """ - If a package doesn't exist, C{remove_package_hold} doesn't - return an error. It's up to the caller to make sure that the - package exist, if it's important. + If a package doesn't exist, C{mark_remove_hold} followed by + C{perform_changes} doesn't return an error. It's up to the caller to + make sure that the package exist, if it's important. """ self._add_system_package("foo") deb_dir = self.makeDir() @@ -2400,21 +2418,23 @@ self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./") self.facade.reload_channels() [bar] = self.facade.get_packages_by_name("bar") - self.facade.remove_package_hold(bar) + self.facade.mark_remove_hold(bar) + self.facade.perform_changes() self.facade.reload_channels() self.assertEqual([], self.facade.get_package_holds()) - def test_remove_package_hold_no_hold(self): + def test_mark_remove_hold_no_hold(self): """ If a package isn't held, the existing selection is retained when - C{remove_package_hold} is called. + C{mark_remove_hold} and C{perform_changes} are called. """ self._add_system_package( "foo", control_fields={"Status": "deinstall ok installed"}) self.facade.reload_channels() [foo] = self.facade.get_packages_by_name("foo") - self.facade.remove_package_hold(foo) + self.facade.mark_remove_hold(foo) + self.facade.perform_changes() self.facade.reload_channels() self.assertEqual([], self.facade.get_package_holds()) @@ -2430,670 +2450,3 @@ test_wb_mark_install_upgrade_non_main_arch_dependency_error.skip = ( skip_message) test_wb_mark_install_upgrade_non_main_arch.skip = skip_message - - -class SmartFacadeTest(LandscapeTest): - - helpers = [SmartFacadeHelper] - - def test_needs_smart(self): - """ - If the Smart python modules can't be imported, a C{RuntimeError} - is raised when trying to create a C{SmartFacade}. - """ - - def reset_has_smart(): - facade_module.has_smart = old_has_smart - - self.addCleanup(reset_has_smart) - old_has_smart = facade_module.has_smart - facade_module.has_smart = False - - self.assertRaises(RuntimeError, self.Facade) - - def test_get_packages(self): - self.facade.reload_channels() - pkgs = self.facade.get_packages() - self.assertEqual(sorted(pkg.name for pkg in pkgs), - ["name1", "name2", "name3"]) - - def test_get_packages_wont_return_non_debian_packages(self): - self.facade.reload_channels() - ctrl_mock = self.mocker.patch(Control) - - class StubPackage(object): - pass - - cache_mock = ctrl_mock.getCache() - cache_mock.getPackages() - self.mocker.result([StubPackage(), StubPackage()]) - self.mocker.replay() - self.assertEqual(self.facade.get_packages(), []) - - def test_get_packages_by_name(self): - self.facade.reload_channels() - pkgs = self.facade.get_packages_by_name("name1") - self.assertEqual([pkg.name for pkg in pkgs], ["name1"]) - pkgs = self.facade.get_packages_by_name("name2") - self.assertEqual([pkg.name for pkg in pkgs], ["name2"]) - - def test_get_packages_by_name_wont_return_non_debian_packages(self): - self.facade.reload_channels() - ctrl_mock = self.mocker.patch(Control) - - class StubPackage(object): - pass - - cache_mock = ctrl_mock.getCache() - cache_mock.getPackages("name") - self.mocker.result([StubPackage(), StubPackage()]) - self.mocker.replay() - self.assertEqual(self.facade.get_packages_by_name("name"), []) - - def test_get_package_skeleton(self): - self.facade.reload_channels() - [pkg1] = self.facade.get_packages_by_name("name1") - [pkg2] = self.facade.get_packages_by_name("name2") - skeleton1 = self.facade.get_package_skeleton(pkg1) - skeleton2 = self.facade.get_package_skeleton(pkg2) - self.assertEqual(skeleton1.get_hash(), HASH1) - self.assertEqual(skeleton2.get_hash(), HASH2) - - def test_build_skeleton_with_info(self): - self.facade.reload_channels() - [pkg] = self.facade.get_packages_by_name("name1") - skeleton = self.facade.get_package_skeleton(pkg, True) - self.assertEqual(skeleton.section, "Group1") - self.assertEqual(skeleton.summary, "Summary1") - self.assertEqual(skeleton.description, "Description1") - self.assertEqual(skeleton.size, 1038) - self.assertEqual(skeleton.installed_size, 28672) - - def test_get_package_hash(self): - self.facade.reload_channels() - [pkg] = self.facade.get_packages_by_name("name1") - self.assertEqual(self.facade.get_package_hash(pkg), HASH1) - [pkg] = self.facade.get_packages_by_name("name2") - self.assertEqual(self.facade.get_package_hash(pkg), HASH2) - - def test_get_package_hashes(self): - self.facade.reload_channels() - hashes = self.facade.get_package_hashes() - self.assertEqual(sorted(hashes), sorted([HASH1, HASH2, HASH3])) - - def test_get_package_by_hash(self): - self.facade.reload_channels() - pkg = self.facade.get_package_by_hash(HASH1) - self.assertEqual(pkg.name, "name1") - pkg = self.facade.get_package_by_hash(HASH2) - self.assertEqual(pkg.name, "name2") - pkg = self.facade.get_package_by_hash("none") - self.assertEqual(pkg, None) - - def test_reload_channels_clears_hash_cache(self): - # Load hashes. - self.facade.reload_channels() - - # Hold a reference to packages. - [pkg1] = self.facade.get_packages_by_name("name1") - [pkg2] = self.facade.get_packages_by_name("name2") - [pkg3] = self.facade.get_packages_by_name("name3") - self.assertTrue(pkg1 and pkg2) - - # Remove the package from the repository. - os.unlink(os.path.join(self.repository_dir, PKGNAME1)) - - # Forcibly change the mtime of our repository, so that Smart - # will consider it as changed (if the change is inside the - # same second the directory's mtime will be the same) - mtime = int(time.time() + 1) - os.utime(self.repository_dir, (mtime, mtime)) - - # Reload channels. - self.facade.reload_channels() - - # Only packages with name2 and name3 should be loaded, and they're - # not the same objects anymore. - self.assertEqual( - sorted([pkg.name for pkg in self.facade.get_packages()]), - ["name2", "name3"]) - self.assertNotEquals(set(self.facade.get_packages()), - set([pkg2, pkg3])) - - # The hash cache shouldn't include either of the old packages. - self.assertEqual(self.facade.get_package_hash(pkg1), None) - self.assertEqual(self.facade.get_package_hash(pkg2), None) - self.assertEqual(self.facade.get_package_hash(pkg3), None) - - # Also, the hash for package1 shouldn't be present at all. - self.assertEqual(self.facade.get_package_by_hash(HASH1), None) - - # While HASH2 and HASH3 should point to the new packages. - new_pkgs = self.facade.get_packages() - self.assertTrue(self.facade.get_package_by_hash(HASH2) - in new_pkgs) - self.assertTrue(self.facade.get_package_by_hash(HASH3) - in new_pkgs) - - # Which are not the old packages. - self.assertFalse(pkg2 in new_pkgs) - self.assertFalse(pkg3 in new_pkgs) - - def test_ensure_reload_channels(self): - """ - The L{SmartFacade.ensure_channels_reloaded} can be called more - than once, but channels will be reloaded only the first time. - """ - self.assertEqual(len(self.facade.get_packages()), 0) - self.facade.ensure_channels_reloaded() - self.assertEqual(len(self.facade.get_packages()), 3) - - # Calling it once more won't reload channels again. - self.facade.get_packages_by_name("name1")[0].installed = True - self.facade.ensure_channels_reloaded() - self.assertTrue(self.facade.get_packages_by_name("name1")[0].installed) - - def test_perform_changes_with_nothing_to_do(self): - """perform_changes() should return None when there's nothing to do. - """ - self.facade.reload_channels() - self.assertEqual(self.facade.perform_changes(), None) - - def test_reset_marks(self): - """perform_changes() should return None when there's nothing to do. - """ - self.facade.reload_channels() - [pkg] = self.facade.get_packages_by_name("name1") - self.facade.mark_install(pkg) - self.facade.reset_marks() - self.assertEqual(self.facade.perform_changes(), None) - - def test_mark_install_transaction_error(self): - """ - Mark package 'name1' for installation, and try to perform changes. - It should fail because 'name1' depends on 'requirename1'. - """ - self.facade.reload_channels() - - [pkg] = self.facade.get_packages_by_name("name1") - self.facade.mark_install(pkg) - exception = self.assertRaises(TransactionError, - self.facade.perform_changes) - self.assertIn("requirename", exception.args[0]) - - def test_mark_install_dependency_error(self): - """ - Now we artificially inject the needed dependencies of 'name1' - in 'name2', but we don't mark 'name2' for installation, and - that should make perform_changes() fail with a dependency - error on the needed package. - """ - self.facade.reload_channels() - - provide1 = Provides("prerequirename1", "prerequireversion1") - provide2 = Provides("requirename1", "requireversion1") - [pkg2] = self.facade.get_packages_by_name("name2") - pkg2.provides += (provide1, provide2) - - # We have to satisfy *both* packages. - provide1 = Provides("prerequirename2", "prerequireversion2") - provide2 = Provides("requirename2", "requireversion2") - [pkg1] = self.facade.get_packages_by_name("name1") - pkg1.provides += (provide1, provide2) - - # Ask Smart to reprocess relationships. - self.facade.reload_cache() - - self.assertEqual(pkg1.requires[0].providedby[0].packages[0], pkg2) - self.assertEqual(pkg1.requires[1].providedby[0].packages[0], pkg2) - - self.facade.mark_install(pkg1) - try: - self.facade.perform_changes() - except DependencyError, exception: - pass - else: - exception = None - self.assertTrue(exception, "DependencyError not raised") - self.assertEqual(exception.packages, [pkg2]) - - def test_mark_remove_dependency_error(self): - """ - Besides making 'name1' satisfy 'name2' and the contrary. We'll - mark both packages installed, so that we can get an error on - removal. - """ - self.facade.reload_channels() - - provide1 = Provides("prerequirename1", "prerequireversion1") - provide2 = Provides("requirename1", "requireversion1") - [pkg2] = self.facade.get_packages_by_name("name2") - pkg2.provides += (provide1, provide2) - - # We have to satisfy *both* packages. - provide1 = Provides("prerequirename2", "prerequireversion2") - provide2 = Provides("requirename2", "requireversion2") - [pkg1] = self.facade.get_packages_by_name("name1") - pkg1.provides += (provide1, provide2) - - # Ask Smart to reprocess relationships. - self.facade.reload_cache() - - pkg1.installed = True - pkg2.installed = True - - self.assertEqual(pkg1.requires[0].providedby[0].packages[0], pkg2) - self.assertEqual(pkg1.requires[1].providedby[0].packages[0], pkg2) - - self.facade.mark_remove(pkg2) - try: - output = self.facade.perform_changes() - except DependencyError, exception: - output = "" - else: - exception = None - self.assertTrue(exception, "DependencyError not raised. Output: %s" - % repr(output)) - self.assertEqual(exception.packages, [pkg1]) - - def test_mark_upgrade_dependency_error(self): - """Artificially make pkg2 upgrade pkg1, and mark pkg1 for upgrade.""" - - # The backend only works after initialized. - from smart.backends.deb.base import DebUpgrades, DebConflicts - - self.facade.reload_channels() - - [pkg1] = self.facade.get_packages_by_name("name1") - [pkg2] = self.facade.get_packages_by_name("name2") - - # Artificially make pkg2 be self-satisfied, and make it upgrade and - # conflict with pkg1. - pkg2.requires = [] - pkg2.upgrades = [DebUpgrades("name1", "=", "version1-release1")] - pkg2.conflicts = [DebConflicts("name1", "=", "version1-release1")] - - # pkg1 will also be self-satisfied. - pkg1.requires = [] - - # Ask Smart to reprocess relationships. - self.facade.reload_cache() - - # Mark the pkg1 as installed. Must be done after reloading - # the cache as reloading will reset it to the loader installed - # status. - pkg1.installed = True - - # Check that the linkage worked. - self.assertEqual(pkg2.upgrades[0].providedby[0].packages[0], pkg1) - - # Perform the upgrade test. - self.facade.mark_upgrade(pkg1) - try: - self.facade.perform_changes() - except DependencyError, exception: - pass - else: - exception = None - self.assertTrue(exception, "DependencyError not raised") - - # Both packages should be included in the dependency error. One - # must be removed, and the other installed. - self.assertEqual(set(exception.packages), set([pkg1, pkg2])) - - def test_perform_changes_with_logged_error(self): - self.log_helper.ignore_errors(".*dpkg") - - self.facade.reload_channels() - - [pkg] = self.facade.get_packages_by_name("name1") - pkg.requires = () - - self.facade.reload_cache() - - self.facade.mark_install(pkg) - - try: - output = self.facade.perform_changes() - except SmartError, exception: - output = "" - else: - exception = None - - self.assertTrue(exception, - "SmartError not raised. Output: %s" % repr(output)) - # We can't check the whole message because the dpkg error can be - # localized. We can't use str(exception) either because it can contain - # unicode - self.assertIn("ERROR", exception.args[0]) - self.assertIn("(2)", exception.args[0]) - self.assertIn("\n[unpack] name1_version1-release1\ndpkg: ", - exception.args[0]) - - def test_perform_changes_is_non_interactive(self): - from smart.backends.deb.pm import DebPackageManager - - self.facade.reload_channels() - - [pkg] = self.facade.get_packages_by_name("name1") - pkg.requires = () - - self.facade.reload_cache() - - self.facade.mark_install(pkg) - - environ = [] - - def check_environ(self, argv, output): - environ.append(os.environ.get("DEBIAN_FRONTEND")) - environ.append(os.environ.get("APT_LISTCHANGES_FRONTEND")) - return 0 - - DebPackageManager.dpkg, olddpkg = check_environ, DebPackageManager.dpkg - - try: - self.facade.perform_changes() - finally: - DebPackageManager.dpkg = olddpkg - - self.assertEqual(environ, ["noninteractive", "none", - "noninteractive", "none"]) - - def test_perform_changes_with_policy_remove(self): - """ - When requested changes are only about removing packages, we set - the Smart transaction policy to C{PolicyRemove}. - """ - create_deb(self.repository_dir, PKGNAME4, PKGDEB4) - self.facade.reload_channels() - - # Importing these modules fail if Smart is not initialized - from smart.backends.deb.base import DebRequires - - pkg1 = self.facade.get_package_by_hash(HASH1) - pkg1.requires.append(DebRequires("name3", ">=", "version3-release3")) - - pkg3 = self.facade.get_package_by_hash(HASH3) - - # Ask Smart to reprocess relationships. - self.facade.reload_cache() - - pkg1.installed = True - pkg3.installed = True - - self.facade.mark_remove(pkg3) - error = self.assertRaises(DependencyError, self.facade.perform_changes) - [missing] = error.packages - self.assertIdentical(pkg1, missing) - - def test_perform_changes_with_commit_change_set_errors(self): - - self.facade.reload_channels() - - [pkg] = self.facade.get_packages_by_name("name1") - pkg.requires = () - - self.facade.mark_install(pkg) - - ctrl_mock = self.mocker.patch(Control) - ctrl_mock.commitChangeSet(ANY) - self.mocker.throw(smart.Error("commit error")) - self.mocker.replay() - - self.assertRaises(TransactionError, self.facade.perform_changes) - - def test_deinit_cleans_the_state(self): - self.facade.reload_channels() - self.assertTrue(self.facade.get_package_by_hash(HASH1)) - self.facade.deinit() - self.assertFalse(self.facade.get_package_by_hash(HASH1)) - - def test_deinit_deinits_smart(self): - self.facade.reload_channels() - self.assertTrue(smart.iface.object) - self.facade.deinit() - self.assertFalse(smart.iface.object) - - def test_deinit_when_smart_wasnt_initialized(self): - self.assertFalse(smart.iface.object) - # Nothing bad should happen. - self.facade.deinit() - - def test_reload_channels_wont_consider_non_debian_packages(self): - - class StubPackage(object): - pass - - pkg = StubPackage() - - ctrl_mock = self.mocker.patch(Control) - cache_mock = ctrl_mock.getCache() - cache_mock.getPackages() - self.mocker.result([pkg]) - self.mocker.replay() - - self.facade.reload_channels() - self.assertEqual(self.facade.get_package_hash(pkg), None) - - def test_reload_channels_with_channel_error(self): - """ - The L{SmartFacade.reload_channels} method raises a L{ChannelsError} if - smart fails to load the configured channels. - """ - ctrl_mock = self.mocker.patch(Control) - ctrl_mock.reloadChannels(caching=ALWAYS) - self.mocker.throw(smart.Error(u"Channel information is locked")) - self.mocker.replay() - self.assertRaises(ChannelError, self.facade.reload_channels) - - def test_reset_add_get_channels(self): - - channels = [("alias0", {"type": "test"}), - ("alias1", {"type": "test"})] - - self.facade.reset_channels() - - self.assertEqual(self.facade.get_channels(), {}) - - self.facade.add_channel(*channels[0]) - self.facade.add_channel(*channels[1]) - - self.assertEqual(self.facade.get_channels(), dict(channels)) - - def test_add_apt_deb_channel(self): - """ - The L{SmartFacade.add_channel_apt_deb} add a Smart channel of - type C{"apt-deb"}. - """ - self.facade.reset_channels() - self.facade.add_channel_apt_deb("http://url/", "name", "component") - self.assertEqual(self.facade.get_channels(), - {"name": {"baseurl": "http://url/", - "distribution": "name", - "components": "component", - "type": "apt-deb"}}) - - def test_add_deb_dir_channel(self): - """ - The L{SmartFacade.add_channel_deb_dir} add a Smart channel of - type C{"deb-dir"}. - """ - self.facade.reset_channels() - self.facade.add_channel_deb_dir("/my/repo") - self.assertEqual(self.facade.get_channels(), - {"/my/repo": {"path": "/my/repo", - "type": "deb-dir"}}) - - def test_get_arch(self): - """ - The L{SmartFacade.get_arch} should return the system dpkg - architecture. - """ - deferred = Deferred() - - def do_test(): - result = getProcessOutputAndValue("/usr/bin/dpkg", - ("--print-architecture",)) - - def callback((out, err, code)): - self.assertEqual(self.facade.get_arch(), out.strip()) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_set_arch_multiple_times(self): - - repo = create_full_repository(self.makeDir()) - - self.facade.set_arch("i386") - self.facade.reset_channels() - self.facade.add_channel_apt_deb(repo.url, repo.codename, - " ".join(repo.components)) - self.facade.reload_channels() - - pkgs = self.facade.get_packages() - self.assertEqual(len(pkgs), 2) - self.assertEqual(pkgs[0].name, "syslinux") - self.assertEqual(pkgs[1].name, "kairos") - - self.facade.deinit() - self.facade.set_arch("amd64") - self.facade.reset_channels() - self.facade.add_channel_apt_deb(repo.url, repo.codename, - " ".join(repo.components)) - self.facade.reload_channels() - - pkgs = self.facade.get_packages() - self.assertEqual(len(pkgs), 2) - self.assertEqual(pkgs[0].name, "libclthreads2") - self.assertEqual(pkgs[1].name, "kairos") - - def test_set_caching_with_reload_error(self): - - alias = "alias" - channel = {"type": "deb-dir", - "path": "/does/not/exist"} - - self.facade.reset_channels() - self.facade.add_channel(alias, channel) - self.facade.set_caching(NEVER) - - self.assertRaises(ChannelError, self.facade.reload_channels) - self.facade._channels = {} - - ignore_re = re.compile("\[Smart\].*'alias'.*/does/not/exist") - - self.log_helper.ignored_exception_regexes = [ignore_re] - - def test_init_landscape_plugins(self): - """ - The landscape plugin which helps managing proxies is loaded when smart - is initialized: this sets a smart configuration variable and load the - module. - """ - self.facade.reload_channels() - self.assertTrue(smart.sysconf.get("use-landscape-proxies")) - self.assertIn("smart.plugins.landscape", sys.modules) - - def test_get_package_locks_with_no_lock(self): - """ - If no package locks are set, L{SmartFacade.get_package_locks} returns - an empty C{list}. - """ - self.assertEqual(self.facade.get_package_locks(), []) - - def test_get_package_locks_with_one_lock(self): - """ - If one lock is set, the list of locks contains one item. - """ - self.facade.set_package_lock("name1", "<", "version1") - self.assertEqual(self.facade.get_package_locks(), - [("name1", "<", "version1")]) - - def test_get_package_locks_with_many_locks(self): - """ - It's possible to have more than one package lock and several conditions - for each of them. - """ - self.facade.set_package_lock("name1", "<", "version1") - self.facade.set_package_lock("name1", ">=", "version3") - self.facade.set_package_lock("name2") - self.assertEqual(sorted(self.facade.get_package_locks()), - sorted([("name1", "<", "version1"), - ("name1", ">=", "version3"), - ("name2", "", "")])) - - def test_set_package_lock(self): - """ - It is possible to lock a package by simply specifying its name. - """ - self.facade.set_package_lock("name1") - self.facade.reload_channels() - [package] = self.facade.get_locked_packages() - self.assertEqual(package.name, "name1") - - def test_set_package_lock_with_matching_condition(self): - """ - It is possible to set a package lock specifying both a - package name and version condition. Any matching package - will be locked. - """ - self.facade.set_package_lock("name1", "<", "version2") - self.facade.reload_channels() - [package] = self.facade.get_locked_packages() - self.assertEqual(package.name, "name1") - - def test_set_package_lock_with_non_matching_condition(self): - """ - If the package lock conditions do not match any package, - no package will be locked. - """ - self.facade.set_package_lock("name1", "<", "version1") - self.facade.reload_channels() - self.assertEqual(self.facade.get_locked_packages(), []) - - def test_set_package_lock_with_missing_version(self): - """ - When specifing a relation for a package lock condition, a version - must be provided as well. - """ - error = self.assertRaises(RuntimeError, self.facade.set_package_lock, - "name1", "<", "") - self.assertEqual(str(error), "Package lock version not provided") - - def test_set_package_lock_with_missing_relation(self): - """ - When specifing a version for a package lock condition, a relation - must be provided as well. - """ - error = self.assertRaises(RuntimeError, self.facade.set_package_lock, - "name1", "", "version1") - self.assertEqual(str(error), "Package lock relation not provided") - - def test_remove_package_lock(self): - """ - It is possibly to remove a package lock without any version condition. - """ - self.facade.set_package_lock("name1") - self.facade.remove_package_lock("name1") - self.assertEqual(self.facade.get_locked_packages(), []) - - def test_remove_package_lock_with_condition(self): - """ - It is possibly to remove a package lock with a version condition. - """ - self.facade.set_package_lock("name1", "<", "version1") - self.facade.remove_package_lock("name1", "<", "version1") - self.assertEqual(self.facade.get_locked_packages(), []) - - def test_save_config(self): - """ - It is possible to lock a package by simply specifying its name. - """ - self.facade.set_package_lock("python", "=>", "2.5") - self.facade.save_config() - self.facade.deinit() - self.assertEqual(self.facade.get_package_locks(), - [("python", "=>", "2.5")]) diff -Nru landscape-client-12.04.3/landscape/package/tests/test_interface.py landscape-client-12.05/landscape/package/tests/test_interface.py --- landscape-client-12.04.3/landscape/package/tests/test_interface.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/tests/test_interface.py 1969-12-31 21:00:00.000000000 -0300 @@ -1,34 +0,0 @@ -# -*- encoding: utf-8 -*- -from landscape.package.interface import LandscapeInterface - -from landscape.tests.helpers import LandscapeTest -from landscape.package.tests.helpers import SmartFacadeHelper - - -class LandscapeInterfaceTest(LandscapeTest): - - helpers = [SmartFacadeHelper] - - def setUp(self): - super(LandscapeInterfaceTest, self).setUp() - self.facade.reload_channels() - self.iface = LandscapeInterface(None) - - def test_message_with_unicode_and_utf8(self): - self.iface.info(u"áéíóú") - self.iface.info("áéíóú") - self.assertEqual(self.iface.get_output_for_landscape(), - u"INFO: áéíóú\nINFO: áéíóú\n") - - def test_message_with_unicode_and_unknown_encoding(self): - self.iface.info(u"áéíóú") - self.iface.info("aeíou\xc3") # UTF-8 expects a byte after \xc3 - c = u"\N{REPLACEMENT CHARACTER}" - self.assertEqual(self.iface.get_output_for_landscape(), - u"INFO: áéíóú\nINFO: ae%s%sou%s\n" % (c, c, c)) - - def test_output_with_unicode_and_utf8(self): - self.iface.showOutput(u"áéíóú") - self.iface.showOutput("áéíóú") - self.assertEqual(self.iface.get_output_for_landscape(), - u"áéíóúáéíóú") diff -Nru landscape-client-12.04.3/landscape/package/tests/test_releaseupgrader.py landscape-client-12.05/landscape/package/tests/test_releaseupgrader.py --- landscape-client-12.04.3/landscape/package/tests/test_releaseupgrader.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/tests/test_releaseupgrader.py 2012-06-01 17:22:32.000000000 -0300 @@ -655,7 +655,7 @@ def check_result((out, err, code)): self.assertFalse(os.path.exists(upgrade_tool_directory)) - self.assertEqual(out, "--force-smart-update\n%s\n" + self.assertEqual(out, "--force-apt-update\n%s\n" % os.getcwd()) self.assertEqual(err, "") self.assertEqual(code, 0) @@ -738,7 +738,7 @@ result = self.upgrader.finish() def check_result((out, err, code)): - self.assertEqual(out, "--force-smart-update " + self.assertEqual(out, "--force-apt-update " "--config=/some/config\n") self.assertEqual(err, "") self.assertEqual(code, 0) diff -Nru landscape-client-12.04.3/landscape/package/tests/test_reporter.py landscape-client-12.05/landscape/package/tests/test_reporter.py --- landscape-client-12.04.3/landscape/package/tests/test_reporter.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/tests/test_reporter.py 2012-06-01 17:22:32.000000000 -0300 @@ -1,4 +1,3 @@ -import glob import sys import os import unittest @@ -17,9 +16,9 @@ PackageReporter, HASH_ID_REQUEST_TIMEOUT, main, find_reporter_command, PackageReporterConfiguration, FakeGlobalReporter, FakeReporter) from landscape.package import reporter -from landscape.package.facade import AptFacade, has_new_enough_apt +from landscape.package.facade import AptFacade from landscape.package.tests.helpers import ( - SmartFacadeHelper, AptFacadeHelper, SimpleRepositoryHelper, + AptFacadeHelper, SimpleRepositoryHelper, HASH1, HASH2, HASH3, PKGNAME1) from landscape.tests.helpers import ( LandscapeTest, BrokerServiceHelper, EnvironSaverHelper) @@ -30,18 +29,63 @@ class PackageReporterConfigurationTest(unittest.TestCase): - def test_force_smart_update_option(self): + def test_force_apt_update_option(self): """ - The L{PackageReporterConfiguration} supports a '--force-smart-update' + The L{PackageReporterConfiguration} supports a '--force-apt-update' command line option. """ config = PackageReporterConfiguration() - self.assertFalse(config.force_smart_update) - config.load(["--force-smart-update"]) - self.assertTrue(config.force_smart_update) + self.assertFalse(config.force_apt_update) + config.load(["--force-apt-update"]) + self.assertTrue(config.force_apt_update) -class PackageReporterTestMixin(object): +class PackageReporterAptTest(LandscapeTest): + + helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] + + Facade = AptFacade + + def setUp(self): + + def set_up(ignored): + self.store = PackageStore(self.makeFile()) + self.config = PackageReporterConfiguration() + self.reporter = PackageReporter( + self.store, self.facade, self.remote, self.config) + self.config.data_path = self.makeDir() + os.mkdir(self.config.package_directory) + + result = super(PackageReporterAptTest, self).setUp() + return result.addCallback(set_up) + + def _clear_repository(self): + """Remove all packages from self.repository.""" + create_file(self.repository_dir + "/Packages", "") + + def set_pkg1_upgradable(self): + """Make it so that package "name1" is considered to be upgradable. + + Return the hash of the package that upgrades "name1". + """ + self._add_package_to_deb_dir( + self.repository_dir, "name1", version="version2") + self.facade.reload_channels() + name1_upgrade = sorted(self.facade.get_packages_by_name("name1"))[1] + return self.facade.get_package_hash(name1_upgrade) + + def set_pkg1_installed(self): + """Make it so that package "name1" is considered installed.""" + self._install_deb_file(os.path.join(self.repository_dir, PKGNAME1)) + + def _make_fake_apt_update(self, out="output", err="error", code=0): + """Create a fake apt-update executable""" + self.reporter.apt_update_filename = self.makeFile( + "#!/bin/sh\n" + "echo -n %s\n" + "echo -n %s >&2\n" + "exit %d" % (out, err, code)) + os.chmod(self.reporter.apt_update_filename, 0755) def test_set_package_ids_with_all_known(self): self.store.add_hash_id_request(["hash1", "hash2"]) @@ -497,64 +541,6 @@ return result - def test_run_smart_update(self): - """ - The L{PackageReporter.run_smart_update} method should run smart-update - with the proper arguments. - """ - self.reporter.sources_list_filename = "/I/Dont/Exist" - self.reporter.sources_list_directory = "/I/Dont/Exist" - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho -n $@") - os.chmod(self.reporter.smart_update_filename, 0755) - debug_mock = self.mocker.replace("logging.debug") - debug_mock("'%s' exited with status 0 (out='--after %d', err=''" % ( - self.reporter.smart_update_filename, - self.reporter.smart_update_interval)) - warning_mock = self.mocker.replace("logging.warning") - self.expect(warning_mock(ANY)).count(0) - self.mocker.replay() - deferred = Deferred() - - def do_test(): - - result = self.reporter.run_smart_update() - - def callback((out, err, code)): - interval = self.reporter.smart_update_interval - self.assertEqual(err, "") - self.assertEqual(out, "--after %d" % interval) - self.assertEqual(code, 0) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_run_smart_update_with_force_smart_update(self): - """ - L{PackageReporter.run_smart_update} forces a smart-update run if - the '--force-smart-update' command line option was passed. - - """ - self.config.load(["--force-smart-update"]) - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho -n $@") - os.chmod(self.reporter.smart_update_filename, 0755) - - deferred = Deferred() - - def do_test(): - result = self.reporter.run_smart_update() - - def callback((out, err, code)): - self.assertEqual(out, "") - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - def test_wb_apt_sources_have_changed(self): """ The L{PackageReporter._apt_sources_have_changed} method returns a bool @@ -581,244 +567,6 @@ content="deb http://foo ./") self.assertTrue(self.reporter._apt_sources_have_changed()) - def test_run_smart_update_with_force_smart_update_if_sources_changed(self): - """ - L{PackageReporter.run_smart_update} forces a smart-update run if - the APT sources.list file has changed. - - """ - self.assertEqual(self.reporter.sources_list_filename, - "/etc/apt/sources.list") - self.reporter.sources_list_filename = self.makeFile("deb ftp://url ./") - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho -n $@") - os.chmod(self.reporter.smart_update_filename, 0755) - - deferred = Deferred() - - def do_test(): - result = self.reporter.run_smart_update() - - def callback((out, err, code)): - # Smart update was called without the --after parameter - self.assertEqual(out, "") - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_run_smart_update_warns_about_failures(self): - """ - The L{PackageReporter.run_smart_update} method should log a warning - in case smart-update terminates with a non-zero exit code other than 1. - """ - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho -n error >&2\necho -n output\nexit 2") - os.chmod(self.reporter.smart_update_filename, 0755) - logging_mock = self.mocker.replace("logging.warning") - logging_mock("'%s' exited with status 2" - " (error)" % self.reporter.smart_update_filename) - self.mocker.replay() - deferred = Deferred() - - def do_test(): - result = self.reporter.run_smart_update() - - def callback((out, err, code)): - self.assertEqual(out, "output") - self.assertEqual(err, "error") - self.assertEqual(code, 2) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_run_smart_update_report_smart_failure(self): - """ - If L{PackageReporter.run_smart_update} fails, a message is sent to the - server reporting the error, to be able to fix the problem centrally. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["package-reporter-result"]) - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho -n error >&2\necho -n output\nexit 2") - os.chmod(self.reporter.smart_update_filename, 0755) - deferred = Deferred() - - def do_test(): - result = self.reporter.run_smart_update() - - def callback(ignore): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "package-reporter-result", - "code": 2, "err": u"error"}]) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_run_smart_update_report_no_sources(self): - """ - L{PackageReporter.run_smart_update} reports a failure if smart - succeeds but there are no APT sources defined. Smart doesn't - fail if there are no sources, but we fake a failure in order to - re-use the PackageReporterAlert on the server. - """ - self.facade.reset_channels() - message_store = self.broker_service.message_store - message_store.set_accepted_types(["package-reporter-result"]) - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho -n error >&2\necho -n output\nexit 0") - os.chmod(self.reporter.smart_update_filename, 0755) - deferred = Deferred() - - def do_test(): - result = self.reporter.run_smart_update() - - def callback(ignore): - error = "There are no APT sources configured in %s or %s." % ( - self.reporter.sources_list_filename, - self.reporter.sources_list_directory) - self.assertMessages(message_store.get_pending_messages(), - [{"type": "package-reporter-result", - "code": 1, "err": error}]) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_run_smart_update_report_smart_failure_no_sources(self): - """ - If L{PackageReporter.run_smart_update} fails and there are no - APT sources configured, the Smart error takes precedence. - """ - self.facade.reset_channels() - message_store = self.broker_service.message_store - message_store.set_accepted_types(["package-reporter-result"]) - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho -n error >&2\necho -n output\nexit 2") - os.chmod(self.reporter.smart_update_filename, 0755) - deferred = Deferred() - - def do_test(): - result = self.reporter.run_smart_update() - - def callback(ignore): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "package-reporter-result", - "code": 2, "err": u"error"}]) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_run_smart_update_report_success(self): - """ - L{PackageReporter.run_smart_update} also reports success to be able to - know the proper state of the client. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["package-reporter-result"]) - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho -n error >&2\necho -n output\nexit 0") - os.chmod(self.reporter.smart_update_filename, 0755) - deferred = Deferred() - - def do_test(): - result = self.reporter.run_smart_update() - - def callback(ignore): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "package-reporter-result", - "code": 0, "err": u"error"}]) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_run_smart_update_warns_exit_code_1_and_non_empty_stderr(self): - """ - The L{PackageReporter.run_smart_update} method should log a warning - in case smart-update terminates with exit code 1 and non empty stderr. - """ - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho -n \"error \" >&2\nexit 1") - os.chmod(self.reporter.smart_update_filename, 0755) - logging_mock = self.mocker.replace("logging.warning") - logging_mock("'%s' exited with status 1" - " (error )" % self.reporter.smart_update_filename) - self.mocker.replay() - deferred = Deferred() - - def do_test(): - result = self.reporter.run_smart_update() - - def callback((out, err, code)): - self.assertEqual(out, "") - self.assertEqual(err, "error ") - self.assertEqual(code, 1) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_run_smart_update_ignores_exit_code_1_and_empty_output(self): - """ - The L{PackageReporter.run_smart_update} method should not log anything - in case smart-update terminates with exit code 1 and output containing - only a newline character. - """ - self.reporter.smart_update_filename = self.makeFile( - "#!/bin/sh\necho\nexit 1") - os.chmod(self.reporter.smart_update_filename, 0755) - logging_mock = self.mocker.replace("logging.warning") - self.expect(logging_mock(ANY)).count(0) - self.mocker.replay() - deferred = Deferred() - - def do_test(): - - result = self.reporter.run_smart_update() - - def callback((out, err, code)): - self.assertEqual(out, "\n") - self.assertEqual(err, "") - self.assertEqual(code, 1) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - - def test_run_smart_update_touches_stamp_file(self): - """ - The L{PackageReporter.run_smart_update} method touches a stamp file - after running the smart-update wrapper. - """ - self.reporter.sources_list_filename = "/I/Dont/Exist" - self.reporter.smart_update_filename = "/bin/true" - deferred = Deferred() - - def do_test(): - - result = self.reporter.run_smart_update() - - def callback(ignored): - self.assertTrue( - os.path.exists(self.config.update_stamp_filename)) - result.addCallback(callback) - result.chainDeferred(deferred) - - reactor.callWhenRunning(do_test) - return deferred - def test_remove_expired_hash_id_request(self): request = self.store.add_hash_id_request(["hash1"]) request.message_id = 9999 @@ -1133,11 +881,7 @@ upgrade_hash = self.set_pkg1_upgradable() self.set_pkg1_installed() - # Don't reload for SmartFacade, since the hash of pkg2 will be - # changed, resulting in that name2 will be considered not - # available.. - if isinstance(self.facade, AptFacade): - self.facade.reload_channels() + self.facade.reload_channels() self.store.set_hash_ids( {HASH1: 1, upgrade_hash: 2, HASH3: 3}) @@ -1193,20 +937,13 @@ result = self.reporter.detect_packages_changes() return result.addCallback(got_result) - def test_detect_changes_considers_packages_and_locks_changes(self): + def test_detect_changes_considers_packages_changes(self): """ - The L{PackageReporter.detect_changes} method considers both package and - package locks changes. It also releases smart locks by calling the - L{SmartFacade.deinit} method. + The L{PackageReporter.detect_changes} method package changes. """ reporter_mock = self.mocker.patch(self.reporter) reporter_mock.detect_packages_changes() self.mocker.result(succeed(True)) - reporter_mock.detect_package_locks_changes() - self.mocker.result(succeed(True)) - - facade_mock = self.mocker.patch(self.facade) - facade_mock.deinit() self.mocker.replay() return self.reporter.detect_changes() @@ -1219,8 +956,6 @@ """ reporter_mock = self.mocker.patch(self.reporter) reporter_mock.detect_packages_changes() - self.mocker.result(succeed(False)) - reporter_mock.detect_package_locks_changes() self.mocker.result(succeed(True)) callback = self.mocker.mock() callback() @@ -1236,11 +971,7 @@ results = [Deferred() for i in range(7)] - # Either the Apt or Smart cache will be updated, not both. - if isinstance(self.facade, AptFacade): - reporter_mock.run_apt_update() - else: - reporter_mock.run_smart_update() + reporter_mock.run_apt_update() self.mocker.result(results[0]) reporter_mock.fetch_hash_id_db() @@ -1306,16 +1037,19 @@ This is done in the reporter so that we know it happens when no other reporter is possibly running at the same time. """ + self._add_system_package("foo") + self.facade.reload_channels() + [foo] = self.facade.get_packages_by_name("foo") + foo_hash = self.facade.get_package_hash(foo) + self.facade.set_package_hold(foo) + self.facade.reload_channels() message_store = self.broker_service.message_store message_store.set_accepted_types(["package-locks"]) - self.store.set_hash_ids({HASH1: 3, HASH2: 4}) + self.store.set_hash_ids({foo_hash: 3, HASH2: 4}) self.store.add_available([1]) self.store.add_available_upgrades([2]) self.store.add_installed([2]) self.store.add_locked([3]) - self.store.add_package_locks([("name1", None, None)]) - if self.facade.supports_package_locks: - self.facade.set_package_lock("name1") request1 = self.store.add_hash_id_request(["hash3"]) request2 = self.store.add_hash_id_request(["hash4"]) @@ -1328,12 +1062,6 @@ self.assertEqual(self.store.get_available_upgrades(), [2]) self.assertEqual(self.store.get_available(), [1]) self.assertEqual(self.store.get_installed(), [2]) - # XXX: Don't check get_locked() and get_package_locks() until - # package locks are implemented in AptFacade. - if not isinstance(self.facade, AptFacade): - self.assertEqual(self.store.get_locked(), [3]) - self.assertEqual( - self.store.get_package_locks(), [("name1", "", "")]) self.assertEqual(self.store.get_hash_id_request(request1.id).id, request1.id) @@ -1344,7 +1072,7 @@ def check_result(result): # The hashes should not go away. - hash1 = self.store.get_hash_id(HASH1) + hash1 = self.store.get_hash_id(foo_hash) hash2 = self.store.get_hash_id(HASH2) self.assertEqual([hash1, hash2], [3, 4]) @@ -1353,12 +1081,9 @@ # After running the resychronize task, detect_packages_changes is # called, and the existing known hashes are made available. - self.assertEqual(self.store.get_available(), [3, 4]) - self.assertEqual(self.store.get_installed(), []) - # XXX: Don't check get_locked() until package locks are - # implemented in AptFacade. - if not isinstance(self.facade, AptFacade): - self.assertEqual(self.store.get_locked(), [3]) + self.assertEqual(self.store.get_available(), [4]) + self.assertEqual(self.store.get_installed(), [3]) + self.assertEqual(self.store.get_locked(), [3]) # The two original hash id requests should be still there, and # a new hash id request should also be detected for HASH3. @@ -1371,314 +1096,14 @@ elif request.id == request2.id: self.assertEqual(request.hashes, ["hash4"]) elif not new_request_found: - self.assertEqual(request.hashes, [HASH3]) + self.assertEqual(request.hashes, [HASH3, HASH1]) else: self.fail("Unexpected hash-id request!") self.assertEqual(requests_count, 3) - # XXX: Don't check for package-locks messages until package - # locks are implemented in AptFacade. - if not isinstance(self.facade, AptFacade): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "package-locks", - "created": [("name1", "", "")]}]) - deferred.addCallback(check_result) return deferred - -class PackageReporterSmartTest(LandscapeTest, PackageReporterTestMixin): - - helpers = [SmartFacadeHelper, BrokerServiceHelper] - - def setUp(self): - - def set_up(ignored): - self.store = PackageStore(self.makeFile()) - self.config = PackageReporterConfiguration() - self.reporter = PackageReporter( - self.store, self.facade, self.remote, self.config) - self.config.data_path = self.makeDir() - os.mkdir(self.config.package_directory) - - result = super(PackageReporterSmartTest, self).setUp() - return result.addCallback(set_up) - - def _clear_repository(self): - """Remove all packages from self.repository.""" - for filename in glob.glob(self.repository_dir + "/*"): - os.unlink(filename) - - def set_pkg1_upgradable(self): - """Make it so that package "name1" is considered to be upgradable. - - Return the hash of the package that upgrades "name1". - """ - previous = self.Facade.channels_reloaded - - def callback(self): - from smart.backends.deb.base import DebUpgrades - previous(self) - pkg2 = self.get_packages_by_name("name2")[0] - pkg2.upgrades += (DebUpgrades("name1", "=", "version1-release1"),) - self.reload_cache() # Relink relations. - self.Facade.channels_reloaded = callback - return HASH2 - - def set_pkg1_installed(self): - """Make it so that package "name1" is considered installed.""" - previous = self.Facade.channels_reloaded - - def callback(self): - previous(self) - self.get_packages_by_name("name1")[0].installed = True - self.Facade.channels_reloaded = callback - - def test_detect_packages_changes_with_locked(self): - """ - If Smart indicates locked packages we didn't know about, report - them to the server. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["packages"]) - - self.facade.set_package_lock("name1") - self.facade.set_package_lock("name2", ">=", "version2") - - self.store.set_hash_ids({HASH1: 1, HASH2: 2}) - self.store.add_available([1, 2]) - - def got_result(result): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "packages", "locked": [1, 2]}]) - self.assertEqual(sorted(self.store.get_locked()), [1, 2]) - - result = self.reporter.detect_packages_changes() - return result.addCallback(got_result) - - def test_detect_packages_changes_with_locked_and_ranges(self): - """ - Ranges are used when reporting changes to 3 or more locked packages - having consecutive ids. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["packages"]) - - self.facade.set_package_lock("name1") - self.facade.set_package_lock("name2", ">=", "version2") - self.facade.set_package_lock("name3", "<", "version4") - - self.store.set_hash_ids({HASH1: 1, HASH2: 2, HASH3: 3}) - self.store.add_available([1, 2, 3]) - - def got_result(result): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "packages", "locked": [(1, 3)]}]) - self.assertEqual(sorted(self.store.get_locked()), [1, 2, 3]) - - result = self.reporter.detect_packages_changes() - return result.addCallback(got_result) - - def test_detect_packages_changes_with_locked_with_unknown_hash(self): - """ - Locked packages whose hashes are unknown don't get reported. - """ - self.facade.set_package_lock("name1") - - def got_result(result): - self.assertEqual(self.store.get_locked(), []) - - result = self.reporter.detect_packages_changes() - return result.addCallback(got_result) - - def test_detect_packages_changes_with_locked_and_previously_known(self): - """ - We don't report locked packages we already know about. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["packages"]) - - self.facade.set_package_lock("name1") - self.facade.set_package_lock("name2", ">=", "version2") - - self.store.set_hash_ids({HASH1: 1, HASH2: 2}) - self.store.add_available([1, 2]) - self.store.add_locked([1]) - - def got_result(result): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "packages", "locked": [2]}]) - - self.assertEqual(sorted(self.store.get_locked()), [1, 2]) - - result = self.reporter.detect_packages_changes() - return result.addCallback(got_result) - - def test_detect_packages_changes_with_not_locked(self): - """ - We report when a package was previously locked and isn't anymore. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["packages"]) - - self.store.set_hash_ids({HASH1: 1}) - self.store.add_available([1]) - self.store.add_locked([1]) - - def got_result(result): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "packages", "not-locked": [1]}]) - self.assertEqual(self.store.get_locked(), []) - - result = self.reporter.detect_packages_changes() - return result.addCallback(got_result) - - def test_detect_package_locks_changes_with_create_locks(self): - """ - If Smart indicates package locks we didn't know about, report - them to the server. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["package-locks"]) - - self.facade.set_package_lock("name") - - logging_mock = self.mocker.replace("logging.info") - logging_mock("Queuing message with changes in known package locks:" - " 1 created, 0 deleted.") - self.mocker.replay() - - def got_result(result): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "package-locks", - "created": [("name", "", "")]}]) - self.assertEqual(self.store.get_package_locks(), - [("name", "", "")]) - - result = self.reporter.detect_package_locks_changes() - return result.addCallback(got_result) - - def test_detect_package_locks_changes_with_already_known_locks(self): - """ - We don't report changes about locks we already know about. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["package-locks"]) - - self.facade.set_package_lock("name1") - self.facade.set_package_lock("name2", "<", "1.2") - - self.store.add_package_locks([("name1", "", "")]) - - logging_mock = self.mocker.replace("logging.info") - logging_mock("Queuing message with changes in known package locks:" - " 1 created, 0 deleted.") - self.mocker.replay() - - def got_result(result): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "package-locks", - "created": [("name2", "<", "1.2")]}]) - self.assertEqual(sorted(self.store.get_package_locks()), - [("name1", "", ""), - ("name2", "<", "1.2")]) - - result = self.reporter.detect_package_locks_changes() - return result.addCallback(got_result) - - def test_detect_package_locks_changes_with_deleted_locks(self): - """ - If Smart indicates newly unset package locks, report them to the - server. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["package-locks"]) - - self.store.add_package_locks([("name1", "", "")]) - - logging_mock = self.mocker.replace("logging.info") - logging_mock("Queuing message with changes in known package locks:" - " 0 created, 1 deleted.") - self.mocker.replay() - - def got_result(result): - self.assertMessages(message_store.get_pending_messages(), - [{"type": "package-locks", - "deleted": [("name1", "", "")]}]) - self.assertEqual(self.store.get_package_locks(), []) - - result = self.reporter.detect_package_locks_changes() - return result.addCallback(got_result) - - def test_detect_package_locks_changes_with_locked_already_known(self): - """ - If we didn't detect any change in the package locks, we don't send any - message, and we return a deferred resulting in C{False}. - """ - message_store = self.broker_service.message_store - message_store.set_accepted_types(["package-locks"]) - - self.facade.set_package_lock("name1") - self.store.add_package_locks([("name1", "", "")]) - - def got_result(result): - self.assertFalse(result) - self.assertMessages(message_store.get_pending_messages(), []) - - result = self.reporter.detect_packages_changes() - return result.addCallback(got_result) - - -class PackageReporterAptTest(LandscapeTest, PackageReporterTestMixin): - - if not has_new_enough_apt: - skip = "Can't use AptFacade on hardy" - - helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] - - Facade = AptFacade - - def setUp(self): - - def set_up(ignored): - self.store = PackageStore(self.makeFile()) - self.config = PackageReporterConfiguration() - self.reporter = PackageReporter( - self.store, self.facade, self.remote, self.config) - self.config.data_path = self.makeDir() - os.mkdir(self.config.package_directory) - - result = super(PackageReporterAptTest, self).setUp() - return result.addCallback(set_up) - - def _clear_repository(self): - """Remove all packages from self.repository.""" - create_file(self.repository_dir + "/Packages", "") - - def set_pkg1_upgradable(self): - """Make it so that package "name1" is considered to be upgradable. - - Return the hash of the package that upgrades "name1". - """ - self._add_package_to_deb_dir( - self.repository_dir, "name1", version="version2") - self.facade.reload_channels() - name1_upgrade = sorted(self.facade.get_packages_by_name("name1"))[1] - return self.facade.get_package_hash(name1_upgrade) - - def set_pkg1_installed(self): - """Make it so that package "name1" is considered installed.""" - self._install_deb_file(os.path.join(self.repository_dir, PKGNAME1)) - - def _make_fake_apt_update(self, out="output", err="error", code=0): - """Create a fake apt-update executable""" - self.reporter.apt_update_filename = self.makeFile( - "#!/bin/sh\n" - "echo -n %s\n" - "echo -n %s >&2\n" - "exit %d" % (out, err, code)) - os.chmod(self.reporter.apt_update_filename, 0755) - def test_run_apt_update(self): """ The L{PackageReporter.run_apt_update} method should run apt-update. @@ -1708,13 +1133,13 @@ reactor.callWhenRunning(do_test) return deferred - def test_run_apt_update_with_force_smart_update(self): + def test_run_apt_update_with_force_apt_update(self): """ L{PackageReporter.run_apt_update} forces an apt-update run if the - '--force-smart-update' command line option was passed. + '--force-apt-update' command line option was passed. """ self.makeFile("", path=self.config.update_stamp_filename) - self.config.load(["--force-smart-update"]) + self.config.load(["--force-apt-update"]) self._make_fake_apt_update() deferred = Deferred() @@ -1730,7 +1155,7 @@ reactor.callWhenRunning(do_test) return deferred - def test_run_apt_update_with_force_smart_update_if_sources_changed(self): + def test_run_apt_update_with_force_apt_update_if_sources_changed(self): """ L{PackageReporter.run_apt_update} forces an apt-update run if the APT sources.list file has changed. @@ -1929,7 +1354,22 @@ return deferred -class GlobalPackageReporterTestMixin(object): +class GlobalPackageReporterAptTest(LandscapeTest): + + helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] + + def setUp(self): + + def set_up(ignored): + self.store = FakePackageStore(self.makeFile()) + self.config = PackageReporterConfiguration() + self.reporter = FakeGlobalReporter( + self.store, self.facade, self.remote, self.config) + self.config.data_path = self.makeDir() + os.mkdir(self.config.package_directory) + + result = super(GlobalPackageReporterAptTest, self).setUp() + return result.addCallback(set_up) def test_store_messages(self): """ @@ -1937,13 +1377,13 @@ """ message_store = self.broker_service.message_store message_store.set_accepted_types(["package-reporter-result"]) - self.reporter.smart_update_filename = self.makeFile( + self.reporter.apt_update_filename = self.makeFile( "#!/bin/sh\necho -n error >&2\necho -n output\nexit 0") - os.chmod(self.reporter.smart_update_filename, 0755) + os.chmod(self.reporter.apt_update_filename, 0755) deferred = Deferred() def do_test(): - result = self.reporter.run_smart_update() + result = self.reporter.run_apt_update() def callback(ignore): message = {"type": "package-reporter-result", @@ -1962,47 +1402,6 @@ return deferred -class GlobalPackageReporterAptTest(LandscapeTest, - GlobalPackageReporterTestMixin): - - if not has_new_enough_apt: - skip = "Can't use AptFacade on hardy" - - helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] - - def setUp(self): - - def set_up(ignored): - self.store = FakePackageStore(self.makeFile()) - self.config = PackageReporterConfiguration() - self.reporter = FakeGlobalReporter( - self.store, self.facade, self.remote, self.config) - self.config.data_path = self.makeDir() - os.mkdir(self.config.package_directory) - - result = super(GlobalPackageReporterAptTest, self).setUp() - return result.addCallback(set_up) - - -class GlobalPackageReporterSmartTest(LandscapeTest, - GlobalPackageReporterTestMixin): - - helpers = [SmartFacadeHelper, BrokerServiceHelper] - - def setUp(self): - - def set_up(ignored): - self.store = FakePackageStore(self.makeFile()) - self.config = PackageReporterConfiguration() - self.reporter = FakeGlobalReporter( - self.store, self.facade, self.remote, self.config) - self.config.data_path = self.makeDir() - os.mkdir(self.config.package_directory) - - result = super(GlobalPackageReporterSmartTest, self).setUp() - return result.addCallback(set_up) - - class FakePackageReporterTest(LandscapeTest): helpers = [EnvironSaverHelper, BrokerServiceHelper] diff -Nru landscape-client-12.04.3/landscape/package/tests/test_skeleton.py landscape-client-12.05/landscape/package/tests/test_skeleton.py --- landscape-client-12.04.3/landscape/package/tests/test_skeleton.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/tests/test_skeleton.py 2012-06-01 17:22:32.000000000 -0300 @@ -1,21 +1,10 @@ -try: - import smart - from smart.cache import Package -except ImportError: - # Smart is optional if AptFacade is being used. - pass - -from landscape.package.interface import ( - install_landscape_interface, uninstall_landscape_interface) - -from landscape.package.facade import has_new_enough_apt from landscape.package.skeleton import ( - build_skeleton, PackageTypeError, build_skeleton_apt, DEB_PROVIDES, + build_skeleton_apt, DEB_PROVIDES, DEB_NAME_PROVIDES, DEB_REQUIRES, DEB_OR_REQUIRES, DEB_UPGRADES, DEB_CONFLICTS) from landscape.package.tests.helpers import ( - AptFacadeHelper, SmartHelper, HASH1, create_simple_repository, create_deb, + AptFacadeHelper, HASH1, create_simple_repository, create_deb, PKGNAME_MINIMAL, PKGDEB_MINIMAL, HASH_MINIMAL, PKGNAME_SIMPLE_RELATIONS, PKGDEB_SIMPLE_RELATIONS, HASH_SIMPLE_RELATIONS, PKGNAME_VERSION_RELATIONS, PKGDEB_VERSION_RELATIONS, HASH_VERSION_RELATIONS, @@ -47,16 +36,32 @@ PKGDEB_OR_RELATIONS) -class SkeletonTestMixin(object): - """Tests for building a skeleton from a package. +class SkeletonAptTest(LandscapeTest): + """C{PackageSkeleton} tests for apt packages.""" + + helpers = [AptFacadeHelper, SkeletonTestHelper] + + def setUp(self): + super(SkeletonAptTest, self).setUp() + self.facade.add_channel_deb_dir(self.skeleton_repository_dir) + # Don't use reload_channels(), since that causes the test setup + # depending on build_skeleton_apt working correctly, which makes + # it harder to do TDD for these tests. + self.facade._cache.open(None) + self.facade._cache.update(None) + self.facade._cache.open(None) - This class should be mixed in to test different backends, like smart - and apt. + def get_package(self, name): + """Return the package with the specified name.""" + # Don't use get_packages(), since that causes the test setup + # depending on build_skeleton_apt working correctly, which makes + # it harder to to TDD for these tests. + package = self.facade._cache[name] + return package.candidate - The main test case classes need to implement C{get_package(name)} to - get a package by name, and C{build_skeleton(package, with_info, - with_unicode}, which builds the skeleton. - """ + def build_skeleton(self, *args, **kwargs): + """Build the skeleton to be tested.""" + return build_skeleton_apt(*args, **kwargs) def test_build_skeleton(self): """ @@ -253,67 +258,3 @@ (DEB_UPGRADES, "or-relations < 1.0")] self.assertEqual(relations, skeleton.relations) self.assertEqual(HASH_OR_RELATIONS, skeleton.get_hash()) - - -class SmartSkeletonTest(LandscapeTest, SkeletonTestMixin): - """C{PackageSkeleton} tests for smart packages.""" - - helpers = [SmartHelper, SkeletonTestHelper] - - def setUp(self): - super(SmartSkeletonTest, self).setUp() - install_landscape_interface() - self.ctrl = smart.init(interface="landscape", datadir=self.smart_dir) - smart.sysconf.set( - "channels", {"alias": {"type": "deb-dir", - "path": self.skeleton_repository_dir}}) - self.ctrl.reloadChannels() - self.cache = self.ctrl.getCache() - - def tearDown(self): - uninstall_landscape_interface() - super(SmartSkeletonTest, self).tearDown() - - def get_package(self, name): - """Return the package with the specified name.""" - [package] = self.cache.getPackages(name) - return package - - def build_skeleton(self, *args, **kwargs): - """Build the skeleton to be tested.""" - return build_skeleton(*args, **kwargs) - - def test_refuse_to_build_non_debian_packages(self): - self.assertRaises(PackageTypeError, build_skeleton, - Package("name", "version")) - - -class SkeletonAptTest(LandscapeTest, SkeletonTestMixin): - """C{PackageSkeleton} tests for apt packages.""" - - if not has_new_enough_apt: - skip = "Can't use AptFacade on hardy" - - helpers = [AptFacadeHelper, SkeletonTestHelper] - - def setUp(self): - super(SkeletonAptTest, self).setUp() - self.facade.add_channel_deb_dir(self.skeleton_repository_dir) - # Don't use reload_channels(), since that causes the test setup - # depending on build_skeleton_apt working correctly, which makes - # it harder to to TDD for these tests. - self.facade._cache.open(None) - self.facade._cache.update(None) - self.facade._cache.open(None) - - def get_package(self, name): - """Return the package with the specified name.""" - # Don't use get_packages(), since that causes the test setup - # depending on build_skeleton_apt working correctly, which makes - # it harder to to TDD for these tests. - package = self.facade._cache[name] - return package.candidate - - def build_skeleton(self, *args, **kwargs): - """Build the skeleton to be tested.""" - return build_skeleton_apt(*args, **kwargs) diff -Nru landscape-client-12.04.3/landscape/package/tests/test_store.py landscape-client-12.05/landscape/package/tests/test_store.py --- landscape-client-12.04.3/landscape/package/tests/test_store.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/tests/test_store.py 2012-06-01 17:22:32.000000000 -0300 @@ -1,6 +1,5 @@ import threading import time -import sys import sqlite3 from landscape.tests.helpers import LandscapeTest @@ -356,11 +355,9 @@ database = sqlite3.connect(filename) cursor = database.cursor() - for table in ["package_locks", "locked"]: - query = "pragma table_info(%s)" % table - cursor.execute(query) - result = cursor.fetchall() - self.assertTrue(len(result) > 0) + cursor.execute("pragma table_info(locked)") + result = cursor.fetchall() + self.assertTrue(len(result) > 0) def test_add_and_get_locked(self): """ @@ -401,103 +398,6 @@ self.store1.clear_locked() self.assertEqual(self.store2.get_locked(), []) - def test_get_package_locks_with_no_lock(self): - """ - L{PackageStore.get_package_locks} returns an empty list if no package - locks are stored. - """ - self.assertEqual(self.store1.get_package_locks(), []) - - def test_add_package_locks(self): - """ - L{PackageStore.add_package_locks} adds a package lock to the store. - """ - self.store1.add_package_locks([("name", "", "")]) - self.assertEqual(self.store2.get_package_locks(), - [("name", "", "")]) - - def test_add_package_locks_idempotence(self): - """ - The operation of adding a lock is idempotent. - """ - self.store1.add_package_locks([("name", "", "")]) - self.store1.add_package_locks([("name", "", "")]) - self.assertEqual(self.store2.get_package_locks(), - [("name", "", "")]) - - def test_add_package_locks_with_none(self): - """ - If None, package locks relation and version values are automatically - converted to empty strings. - """ - self.store1.add_package_locks([("name", None, None)]) - self.assertEqual(self.store2.get_package_locks(), - [("name", "", "")]) - - def test_add_package_locks_multiple_times(self): - """ - L{PackageStore.add_package_locks} can be called multiple times and - with multiple locks each time. - """ - self.store1.add_package_locks([("name1", "", "")]) - self.store1.add_package_locks([("name2", "<", "0.2"), - ("name3", "", "")]) - self.assertEqual(sorted(self.store2.get_package_locks()), - sorted([("name1", "", ""), - ("name2", "<", "0.2"), - ("name3", "", "")])) - - def test_add_package_locks_without_name(self): - """ - It's not possible to add a package lock without a name. - """ - if sys.version_info >= (2, 5): - sqlite_error = sqlite3.IntegrityError - else: - sqlite_error = sqlite3.OperationalError - self.assertRaises(sqlite_error, - self.store1.add_package_locks, - [(None, None, None)]) - - def test_remove_package_locks(self): - """ - L{PackageStore.remove_package_locks} removes a package lock from - the store. - """ - self.store1.add_package_locks([("name1", "", "")]) - self.store1.remove_package_locks([("name1", "", "")]) - self.assertEqual(self.store2.get_package_locks(), []) - - def test_remove_package_locks_multiple_times(self): - """ - L{PackageStore.remove_package_locks} can be called multiple times and - with multiple locks each time. - """ - self.store1.add_package_locks([("name1", "", ""), - ("name2", "<", "0.2"), - ("name3", "", "")]) - self.store1.remove_package_locks([("name1", "", "")]) - self.store1.remove_package_locks([("name2", "<", "0.2"), - ("name3", "", "")]) - self.assertEqual(self.store2.get_package_locks(), []) - - def test_remove_package_locks_without_matching_lock(self): - """ - It's fine to remove a non-existent lock. - """ - self.store1.remove_package_locks([("name", "", "")]) - self.assertEqual(self.store2.get_package_locks(), []) - - def test_clear_package_locks(self): - """ - L{PackageStore.clear_package_locks} removes all package locks - from the store. - """ - self.store1.add_package_locks([("name1", "", ""), - ("name2", "<", "0.2")]) - self.store1.clear_package_locks() - self.assertEqual(self.store2.get_package_locks(), []) - def test_add_hash_id_request(self): hashes = ("ha\x00sh1", "ha\x00sh2") request1 = self.store1.add_hash_id_request(hashes) diff -Nru landscape-client-12.04.3/landscape/package/tests/test_taskhandler.py landscape-client-12.05/landscape/package/tests/test_taskhandler.py --- landscape-client-12.04.3/landscape/package/tests/test_taskhandler.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/package/tests/test_taskhandler.py 2012-06-01 17:22:32.000000000 -0300 @@ -9,8 +9,7 @@ from landscape.package.taskhandler import ( PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler, LazyRemoteBroker) -from landscape.package import facade as facade_module -from landscape.package.facade import AptFacade, SmartFacade +from landscape.package.facade import AptFacade from landscape.package.store import HashIdStore, PackageStore from landscape.package.tests.helpers import AptFacadeHelper from landscape.tests.helpers import ( @@ -30,7 +29,7 @@ def test_update_stamp_option(self): """ L{PackageReporterConfiguration.update_stamp_filename} points - to the smart-update stamp file. + to the update-stamp file. """ config = PackageTaskHandlerConfiguration() self.assertEqual( @@ -374,14 +373,11 @@ return HandlerMock, handler_args - def test_run_task_handler_new_apt(self): + def test_run_task_handler(self): """ The L{run_task_handler} function creates and runs the given task - handler with the proper arguments. If the system has a new - enough version of Apt (i.e. newer than Hardy), AptFacade will - be used. + handler with the proper arguments. """ - self._set_new_enough_apt(True) HandlerMock, handler_args = self._mock_run_task_handler() def assert_task_handler(ignored): @@ -409,46 +405,6 @@ finally: # Put reactor back in place before returning. self.mocker.reset() - - result = run_task_handler(HandlerMock, ["-c", self.config_filename]) - return result.addCallback(assert_task_handler) - - def _set_new_enough_apt(self, value): - """Override landscape.package.facade.has_new_enough_apt. - - The previous value of that attribute is replaced when the test - is finished. - """ - - def reset_new_enough_apt(): - facade_module.has_new_enough_apt = old_has_new_enough_apt - - old_has_new_enough_apt = facade_module.has_new_enough_apt - facade_module.has_new_enough_apt = value - self.addCleanup(reset_new_enough_apt) - - def test_run_task_handler_old_apt(self): - """ - If the C{python-apt} module isn't new enough, - C{run_task_handler} will create a C{SmartFacade}, since - C{AptFacade} doesn't work with old versions of C{python-apt}. - - Hardy has a too old version of C{python-apt}, but lucid and - onwards should have a new enough version. - """ - - HandlerMock, handler_args = self._mock_run_task_handler() - self._set_new_enough_apt(False) - - def assert_task_handler(ignored): - - store, facade, broker, config = handler_args - - try: - self.assertEqual(type(facade), SmartFacade) - finally: - # Put reactor back in place before returning. - self.mocker.reset() result = run_task_handler(HandlerMock, ["-c", self.config_filename]) return result.addCallback(assert_task_handler) diff -Nru landscape-client-12.04.3/landscape/ui/view/tests/test_configuration.py landscape-client-12.05/landscape/ui/view/tests/test_configuration.py --- landscape-client-12.04.3/landscape/ui/view/tests/test_configuration.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/ui/view/tests/test_configuration.py 2012-06-01 17:22:32.000000000 -0300 @@ -57,6 +57,9 @@ """ self.assertFalse(is_valid_host_name(u"\xc3a")) + if not got_gobject_introspection: + skip = gobject_skip_message + class ConfigurationViewTest(LandscapeTest): diff -Nru landscape-client-12.04.3/landscape/ui/view/ui/landscape-client-settings.glade landscape-client-12.05/landscape/ui/view/ui/landscape-client-settings.glade --- landscape-client-12.04.3/landscape/ui/view/ui/landscape-client-settings.glade 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/ui/view/ui/landscape-client-settings.glade 2012-06-01 17:22:32.000000000 -0300 @@ -103,7 +103,7 @@ True False - Landscape is a remote administration service from Canonical. If you allow it, a Landcape server can monitor this computer's performance and send administration commands. + Landscape is a remote administration service from Canonical. If you allow it, a Landscape server can monitor this computer's performance and send administration commands. True diff -Nru landscape-client-12.04.3/landscape/watchdog.py landscape-client-12.05/landscape/watchdog.py --- landscape-client-12.04.3/landscape/watchdog.py 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/landscape/watchdog.py 2012-06-01 17:22:32.000000000 -0300 @@ -631,7 +631,7 @@ In particular unset all variables beginning with DEBIAN_ or DEBCONF_, to avoid any problems when landscape-client is invoked from its postinst script. Some environment variables may be set which would affect - *other* maintainer scripts which landscape-client invokes (via smart). + *other* maintainer scripts which landscape-client invokes. """ for key in os.environ.keys(): if (key.startswith("DEBIAN_") diff -Nru landscape-client-12.04.3/po/fr.po landscape-client-12.05/po/fr.po --- landscape-client-12.04.3/po/fr.po 2012-06-19 15:01:47.000000000 -0300 +++ landscape-client-12.05/po/fr.po 2012-06-01 17:22:32.000000000 -0300 @@ -90,7 +90,7 @@ msgstr "Seuls les caractères ASCII sont autritorisés." #: ../landscape/ui/view/ui/landscape-client-settings.glade.h:1 -msgid "Landscape is a remote administration service from Canonical. If you allow it, a Landcape server can monitor this computer's performance and send administration commands." +msgid "Landscape is a remote administration service from Canonical. If you allow it, a Landscape server can monitor this computer's performance and send administration commands." msgstr "Landscape est un service de gestion à distance de Canonical. Si vous l'autorisez, un serveur Landscape peut surveiller les performance de cette machine et envoyer des commandes administratives." #: ../landscape/ui/view/ui/landscape-client-settings.glade.h:2 diff -Nru landscape-client-12.04.3/po/landscape-client.pot landscape-client-12.05/po/landscape-client.pot --- landscape-client-12.04.3/po/landscape-client.pot 2012-06-19 15:01:47.000000000 -0300 +++ landscape-client-12.05/po/landscape-client.pot 2012-06-01 17:22:32.000000000 -0300 @@ -91,7 +91,7 @@ #: ../landscape/ui/view/ui/landscape-client-settings.glade.h:1 msgid "" "Landscape is a remote administration service from Canonical. If you allow " -"it, a Landcape server can monitor this computer's performance and send " +"it, a Landscape server can monitor this computer's performance and send " "administration commands." msgstr "" diff -Nru landscape-client-12.04.3/smart-update/Makefile landscape-client-12.05/smart-update/Makefile --- landscape-client-12.04.3/smart-update/Makefile 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/smart-update/Makefile 1969-12-31 21:00:00.000000000 -0300 @@ -1,6 +0,0 @@ - -smart-update: smart-update.c - $(CC) $(CFLAGS) -Wall $< -o $@ - -clean: - rm -f smart-update diff -Nru landscape-client-12.04.3/smart-update/smart-update.c landscape-client-12.05/smart-update/smart-update.c --- landscape-client-12.04.3/smart-update/smart-update.c 2012-03-28 14:08:13.000000000 -0300 +++ landscape-client-12.05/smart-update/smart-update.c 1969-12-31 21:00:00.000000000 -0300 @@ -1,129 +0,0 @@ -/* - - Copyright (c) 2004 Conectiva, Inc. - Copyright (c) 2009 Canonical, Ltd. - - Written by Gustavo Niemeyer , - Free Ekanayaka - - This file is part of Smart Package Manager. - - Smart Package Manager is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2 of the License, or (at - your option) any later version. - - Smart Package Manager is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Smart Package Manager; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GNUPG_HOME "/root/.gnupg" - -int main(int argc, char *argv[], char *envp[]) -{ - char *smart_argv[] = {"/usr/share/smart/smart", "update", NULL, NULL}; - char *smart_envp[] = {"PATH=/bin:/usr/bin", NULL, NULL}; - - // Set the HOME environment variable - struct passwd *pwd = getpwuid(geteuid()); - if (!pwd) { - fprintf(stderr, "error: Unable to find passwd entry for uid %d (%s)\n", - geteuid(), strerror(errno)); - exit(1); - } - if (asprintf(&smart_envp[1], "HOME=%s", pwd->pw_dir) == -1) { - perror("error: Unable to create HOME environment variable"); - exit(1); - } - - // Handle the --after command line option - if (argc != 1) { - if (argc != 3 || strcmp(argv[1], "--after") != 0) { - fprintf(stderr, "error: Unsupported command line option\n"); - exit(1); - } - char *end; - long interval = strtol(argv[2], &end, 10); - if (end == argv[2]) { - fprintf(stderr, "error: Interval value '%s' not a number\n", argv[2]); - exit(1); - } - if (asprintf(&smart_argv[2], "--after=%ld", interval) == -1) { - perror("error: Unable to create argument variable"); - exit(1); - } - } - - // Drop any supplementary group - if (setgroups(0, NULL) == -1) { - perror("error: Unable to set supplementary groups IDs"); - exit(1); - } - - // Set real/effective gid and uid - if (setregid(pwd->pw_gid, pwd->pw_gid) == -1) { - fprintf(stderr, "error: Unable to set real and effective gid (%s)\n", - strerror(errno)); - exit(1); - } - if (setreuid(pwd->pw_uid, pwd->pw_uid) == -1) { - perror("error: Unable to set real and effective uid"); - exit(1); - } - - // Close all file descriptors except the standard ones - struct rlimit rlp; - if (getrlimit(RLIMIT_NOFILE, &rlp) == -1) { - perror("error: Unable to determine file descriptor limits"); - exit(1); - } - int file_max; - if (rlp.rlim_max == RLIM_INFINITY || rlp.rlim_max > 4096) - file_max = 4096; - else - file_max = rlp.rlim_max; - int file; - for (file = 3; file < file_max; file++) { - close(file); - } - - // Set umask to 022 - umask(S_IWGRP | S_IWOTH); - - if (chdir("/") == -1) { - perror("error: Unable to change working directory"); - exit(1); - } - - // XXX This is a workaround for Bug #562496, that makes the gpg command - // invoked by smart fail if the data directory doesn't exist yet - struct stat st; - if (stat(GNUPG_HOME, &st) != 0) - mkdir(GNUPG_HOME, S_IRWXU); - - // Run smart update - execve(smart_argv[0], smart_argv, smart_envp); - perror("error: Unable to execute smart"); - return 1; -} - -/* vim:ts=4:sw=4:et -*/