openshift_health_check.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. """
  2. Ansible action plugin to execute health checks in OpenShift clusters.
  3. """
  4. # pylint: disable=wrong-import-position,missing-docstring,invalid-name
  5. import sys
  6. import os
  7. from collections import defaultdict
  8. try:
  9. from __main__ import display
  10. except ImportError:
  11. from ansible.utils.display import Display
  12. display = Display()
  13. from ansible.plugins.action import ActionBase
  14. # Augment sys.path so that we can import checks from a directory relative to
  15. # this callback plugin.
  16. sys.path.insert(1, os.path.dirname(os.path.dirname(__file__)))
  17. from openshift_checks import OpenShiftCheck, OpenShiftCheckException, load_checks # noqa: E402
  18. class ActionModule(ActionBase):
  19. def run(self, tmp=None, task_vars=None):
  20. result = super(ActionModule, self).run(tmp, task_vars)
  21. if task_vars is None:
  22. task_vars = {}
  23. if "openshift" not in task_vars:
  24. result["failed"] = True
  25. result["msg"] = "'openshift' is undefined, did 'openshift_facts' run?"
  26. return result
  27. try:
  28. known_checks = self.load_known_checks()
  29. except OpenShiftCheckException as e:
  30. result["failed"] = True
  31. result["msg"] = str(e)
  32. return result
  33. args = self._task.args
  34. resolved_checks = resolve_checks(args.get("checks", []), known_checks.values())
  35. result["checks"] = check_results = {}
  36. for check_name in resolved_checks:
  37. display.banner("CHECK [{} : {}]".format(check_name, task_vars["ansible_host"]))
  38. check = known_checks[check_name]
  39. if check.is_active(task_vars):
  40. try:
  41. r = check.run(tmp, task_vars)
  42. except OpenShiftCheckException as e:
  43. r = {}
  44. r["failed"] = True
  45. r["msg"] = str(e)
  46. else:
  47. r = {"skipped": True}
  48. check_results[check_name] = r
  49. if r.get("failed", False):
  50. result["failed"] = True
  51. result["msg"] = "One or more checks failed"
  52. result["changed"] = any(r.get("changed", False) for r in check_results.values())
  53. return result
  54. def load_known_checks(self):
  55. load_checks()
  56. known_checks = {}
  57. for cls in OpenShiftCheck.subclasses():
  58. check_name = cls.name
  59. if check_name in known_checks:
  60. other_cls = known_checks[check_name].__class__
  61. raise OpenShiftCheckException(
  62. "non-unique check name '{}' in: '{}.{}' and '{}.{}'".format(
  63. check_name,
  64. cls.__module__, cls.__name__,
  65. other_cls.__module__, other_cls.__name__))
  66. known_checks[check_name] = cls(execute_module=self._execute_module)
  67. return known_checks
  68. def resolve_checks(names, all_checks):
  69. """Returns a set of resolved check names.
  70. Resolving a check name expands tag references (e.g., "@tag") to all the
  71. checks that contain the given tag. OpenShiftCheckException is raised if
  72. names contains an unknown check or tag name.
  73. names should be a sequence of strings.
  74. all_checks should be a sequence of check classes/instances.
  75. """
  76. known_check_names = set(check.name for check in all_checks)
  77. known_tag_names = set(name for check in all_checks for name in check.tags)
  78. check_names = set(name for name in names if not name.startswith('@'))
  79. tag_names = set(name[1:] for name in names if name.startswith('@'))
  80. unknown_check_names = check_names - known_check_names
  81. unknown_tag_names = tag_names - known_tag_names
  82. if unknown_check_names or unknown_tag_names:
  83. msg = []
  84. if unknown_check_names:
  85. msg.append('Unknown check names: {}.'.format(', '.join(sorted(unknown_check_names))))
  86. if unknown_tag_names:
  87. msg.append('Unknown tag names: {}.'.format(', '.join(sorted(unknown_tag_names))))
  88. msg.append('Make sure there is no typo in the playbook and no files are missing.')
  89. raise OpenShiftCheckException('\n'.join(msg))
  90. tag_to_checks = defaultdict(set)
  91. for check in all_checks:
  92. for tag in check.tags:
  93. tag_to_checks[tag].add(check.name)
  94. resolved = check_names.copy()
  95. for tag in tag_names:
  96. resolved.update(tag_to_checks[tag])
  97. return resolved