__init__.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. """
  2. Health checks for OpenShift clusters.
  3. """
  4. import operator
  5. import os
  6. from abc import ABCMeta, abstractmethod, abstractproperty
  7. from importlib import import_module
  8. from ansible.module_utils import six
  9. from ansible.module_utils.six.moves import reduce # pylint: disable=import-error,redefined-builtin
  10. class OpenShiftCheckException(Exception):
  11. """Raised when a check cannot proceed."""
  12. pass
  13. @six.add_metaclass(ABCMeta)
  14. class OpenShiftCheck(object):
  15. """A base class for defining checks for an OpenShift cluster environment."""
  16. def __init__(self, execute_module=None, module_executor=None):
  17. if execute_module is module_executor is None:
  18. raise TypeError(
  19. "__init__() takes either execute_module (recommended) "
  20. "or module_executor (deprecated), none given")
  21. self.execute_module = execute_module or module_executor
  22. self.module_executor = self.execute_module
  23. @abstractproperty
  24. def name(self):
  25. """The name of this check, usually derived from the class name."""
  26. return "openshift_check"
  27. @property
  28. def tags(self):
  29. """A list of tags that this check satisfy.
  30. Tags are used to reference multiple checks with a single '@tagname'
  31. special check name.
  32. """
  33. return []
  34. @classmethod
  35. def is_active(cls, task_vars): # pylint: disable=unused-argument
  36. """Returns true if this check applies to the ansible-playbook run."""
  37. return True
  38. @abstractmethod
  39. def run(self, tmp, task_vars):
  40. """Executes a check, normally implemented as a module."""
  41. return {}
  42. @classmethod
  43. def subclasses(cls):
  44. """Returns a generator of subclasses of this class and its subclasses."""
  45. # AUDIT: no-member makes sense due to this having a metaclass
  46. for subclass in cls.__subclasses__(): # pylint: disable=no-member
  47. yield subclass
  48. for subclass in subclass.subclasses():
  49. yield subclass
  50. LOADER_EXCLUDES = (
  51. "__init__.py",
  52. "mixins.py",
  53. "logging.py",
  54. )
  55. def load_checks(path=None, subpkg=""):
  56. """Dynamically import all check modules for the side effect of registering checks."""
  57. if path is None:
  58. path = os.path.dirname(__file__)
  59. modules = []
  60. for name in os.listdir(path):
  61. if os.path.isdir(os.path.join(path, name)):
  62. modules = modules + load_checks(os.path.join(path, name), subpkg + "." + name)
  63. continue
  64. if name.endswith(".py") and name not in LOADER_EXCLUDES:
  65. modules.append(import_module(__package__ + subpkg + "." + name[:-3]))
  66. return modules
  67. def get_var(task_vars, *keys, **kwargs):
  68. """Helper function to get deeply nested values from task_vars.
  69. Ansible task_vars structures are Python dicts, often mapping strings to
  70. other dicts. This helper makes it easier to get a nested value, raising
  71. OpenShiftCheckException when a key is not found or returning a default value
  72. provided as a keyword argument.
  73. """
  74. try:
  75. value = reduce(operator.getitem, keys, task_vars)
  76. except (KeyError, TypeError):
  77. if "default" in kwargs:
  78. return kwargs["default"]
  79. raise OpenShiftCheckException("'{}' is undefined".format(".".join(map(str, keys))))
  80. return value