launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #07031
[Merge] lp:~rvb/maas/long-lasting-warnings into lp:maas
Raphaël Badin has proposed merging lp:~rvb/maas/long-lasting-warnings into lp:maas.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~rvb/maas/long-lasting-warnings/+merge/101126
This branch adds the basic machinery that we will use to report, discard and display component errors. The errors string are really just a first draft at this stage.
This branch is the first in 3 branches:
- this branch creates the backend utilities to report, discard and display component errors.
- a ui branch will use the backend utilities to display the errors.
- a third branch will connect the dots and apply the decorator introduced in this branch where appropriate. This branch will also polish the error messages.
--
https://code.launchpad.net/~rvb/maas/long-lasting-warnings/+merge/101126
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/maas/long-lasting-warnings into lp:maas.
=== added file 'src/maasserver/components.py'
--- src/maasserver/components.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/components.py 2012-04-06 16:06:23 +0000
@@ -0,0 +1,81 @@
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""MAAS components management."""
+
+from __future__ import (
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = [
+ "discard_persistent_error",
+ "get_persistent_errors",
+ "PERSISTENT_COMPONENTS_ERRORS",
+ "persistent_error_sensor",
+ "register_persistent_error",
+ ]
+
+from collections import Sequence
+import threading
+
+
+PERSISTENT_COMPONENTS_ERRORS = {
+ 'provisioning_server_error': """
+ The provisioning server is failing.
+ """,
+ 'cobbler_server_error': """
+ Cobbler is failing.
+ """,
+ 'maas-import-isos_error': """
+ The maas-import-isos script appears not to have been run.
+ """,
+}
+
+# Persistent errors are global to a MAAS instance.
+_PERSISTENT_ERRORS = set()
+
+
+_PERSISTENT_ERRORS_LOCK = threading.Lock()
+
+
+def register_persistent_error(error_code):
+ with _PERSISTENT_ERRORS_LOCK:
+ global _PERSISTENT_ERRORS
+ _PERSISTENT_ERRORS.add(error_code)
+
+
+def discard_persistent_error(error_code):
+ with _PERSISTENT_ERRORS_LOCK:
+ global _PERSISTENT_ERRORS
+ _PERSISTENT_ERRORS.discard(error_code)
+
+
+def get_persistent_errors():
+ for error_code in _PERSISTENT_ERRORS:
+ yield PERSISTENT_COMPONENTS_ERRORS[error_code]
+
+
+def persistent_error_sensor(exceptions, error_code):
+ """A method decorator used to report if the decorated method ran
+ successfully or raised an exception. In case of success,
+ the permanent error corresponding to error_code will be discarded if it
+ was previously registered; if one of the exceptions in 'exceptions' is
+ raised, the permanent error corresponding to error_code will be
+ registered.
+ """
+ if not isinstance(exceptions, Sequence):
+ exceptions = (exceptions, )
+
+ def wrapper(func):
+ def _wrapper(*args, **kwargs):
+ try:
+ res = func(*args, **kwargs)
+ discard_persistent_error(error_code)
+ return res
+ except exceptions:
+ register_persistent_error(error_code)
+ raise
+ return _wrapper
+ return wrapper
=== added file 'src/maasserver/tests/test_components.py'
--- src/maasserver/tests/test_components.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/tests/test_components.py 2012-04-06 16:06:23 +0000
@@ -0,0 +1,113 @@
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test maasserver components module."""
+
+from __future__ import (
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = []
+
+
+import random
+
+from maasserver import components
+from maasserver.components import (
+ discard_persistent_error,
+ get_persistent_errors,
+ PERSISTENT_COMPONENTS_ERRORS,
+ persistent_error_sensor,
+ register_persistent_error,
+ )
+from maasserver.testing.testcase import TestCase
+
+
+def get_random_error():
+ return random.choice(PERSISTENT_COMPONENTS_ERRORS.keys())
+
+
+class PersistentErrorsUtilitiesTest(TestCase):
+
+ def setUp(self):
+ super(PersistentErrorsUtilitiesTest, self).setUp()
+ self._PERSISTENT_ERRORS = set()
+ self.patch(components, '_PERSISTENT_ERRORS', self._PERSISTENT_ERRORS)
+
+ def test_register_persistent_error_registers_error(self):
+ error = get_random_error()
+ register_persistent_error(error)
+ self.assertItemsEqual([error], self._PERSISTENT_ERRORS)
+
+ def test_register_persistent_error_does_not_register_error_twice(self):
+ error = get_random_error()
+ register_persistent_error(error)
+ register_persistent_error(error)
+ self.assertItemsEqual([error], self._PERSISTENT_ERRORS)
+
+ def test_discard_persistent_error_discards_error(self):
+ error = get_random_error()
+ register_persistent_error(error)
+ discard_persistent_error(error)
+ self.assertItemsEqual([], self._PERSISTENT_ERRORS)
+
+ def test_discard_persistent_error_can_be_called_many_times(self):
+ error = get_random_error()
+ register_persistent_error(error)
+ discard_persistent_error(error)
+ discard_persistent_error(error)
+ self.assertItemsEqual([], self._PERSISTENT_ERRORS)
+
+ def get_persistent_errors_returns_text_for_error_codes(self):
+ errors = PERSISTENT_COMPONENTS_ERRORS.keys()
+ for error in errors:
+ register_persistent_error(error)
+ error_messages = get_persistent_errors()
+ self.assertEqual(len(errors), len(error_messages))
+ self.assertItemsEqual(
+ [unicode] * len(errors),
+ [type(error_message) for error_message in error_messages])
+
+ def test_error_sensor_registers_error_if_exception_raised(self):
+ error = get_random_error()
+
+ @persistent_error_sensor(NotImplementedError, error)
+ def test_method():
+ raise NotImplementedError
+
+ self.assertRaises(NotImplementedError, test_method)
+ self.assertItemsEqual([error], self._PERSISTENT_ERRORS)
+
+ def test_error_sensor_registers_does_not_register_unknown_error(self):
+ error = get_random_error()
+
+ @persistent_error_sensor(NotImplementedError, error)
+ def test_method():
+ raise ValueError
+
+ self.assertRaises(ValueError, test_method)
+ self.assertItemsEqual([], self._PERSISTENT_ERRORS)
+
+ def test_error_sensor_discards_error_if_method_runs_successfully(self):
+ error = get_random_error()
+ register_persistent_error(error)
+
+ @persistent_error_sensor(NotImplementedError, error)
+ def test_method():
+ pass
+
+ test_method()
+ self.assertItemsEqual([], self._PERSISTENT_ERRORS)
+
+ def test_error_sensor_does_not_discard_error_if_unknown_exception(self):
+ error = get_random_error()
+ register_persistent_error(error)
+
+ @persistent_error_sensor(ValueError, error)
+ def test_method():
+ raise NotImplementedError
+
+ self.assertRaises(NotImplementedError, test_method)
+ self.assertItemsEqual([error], self._PERSISTENT_ERRORS)
Follow ups