sanity_checks.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. """
  2. Ansible action plugin to ensure inventory variables are set
  3. appropriately and no conflicting options have been provided.
  4. """
  5. import json
  6. import re
  7. from ansible.plugins.action import ActionBase
  8. from ansible import errors
  9. # pylint: disable=import-error,no-name-in-module
  10. from ansible.module_utils.six.moves.urllib.parse import urlparse
  11. # Valid values for openshift_deployment_type
  12. VALID_DEPLOYMENT_TYPES = ('origin', 'openshift-enterprise')
  13. # Tuple of variable names and default values if undefined.
  14. NET_PLUGIN_LIST = (('openshift_use_openshift_sdn', True),
  15. ('openshift_use_flannel', False),
  16. ('openshift_use_nuage', False),
  17. ('openshift_use_contiv', False),
  18. ('openshift_use_calico', False),
  19. ('openshift_use_kuryr', False),
  20. ('openshift_use_nsx', False))
  21. ENTERPRISE_TAG_REGEX_ERROR = """openshift_image_tag must be in the format
  22. v#.#[.#[.#]]. Examples: v1.2, v3.4.1, v3.5.1.3,
  23. v3.5.1.3.4, v1.2-1, v1.2.3-4, v1.2.3-4.5, v1.2.3-4.5.6
  24. You specified openshift_image_tag={}"""
  25. ORIGIN_TAG_REGEX_ERROR = """openshift_image_tag must be in the format
  26. v#.#[.#-optional.#]. Examples: v1.2.3, v3.5.1-alpha.1
  27. You specified openshift_image_tag={}"""
  28. ORIGIN_TAG_REGEX = {'re': '(^v?\\d+\\.\\d+.*)',
  29. 'error_msg': ORIGIN_TAG_REGEX_ERROR}
  30. ENTERPRISE_TAG_REGEX = {'re': '(^v\\d+\\.\\d+(\\.\\d+)*(-\\d+(\\.\\d+)*)?$)',
  31. 'error_msg': ENTERPRISE_TAG_REGEX_ERROR}
  32. IMAGE_TAG_REGEX = {'origin': ORIGIN_TAG_REGEX,
  33. 'openshift-enterprise': ENTERPRISE_TAG_REGEX}
  34. PKG_VERSION_REGEX_ERROR = """openshift_pkg_version must be in the format
  35. -[optional.release]. Examples: -3.6.0, -3.7.0-0.126.0.git.0.9351aae.el7 -3.11*
  36. You specified openshift_pkg_version={}"""
  37. PKG_VERSION_REGEX = {'re': '(^-.*)',
  38. 'error_msg': PKG_VERSION_REGEX_ERROR}
  39. RELEASE_REGEX_ERROR = """openshift_release must be in the format
  40. v#[.#[.#]]. Examples: v3.9, v3.10.0
  41. You specified openshift_release={}"""
  42. RELEASE_REGEX = {'re': '(^v?\\d+(\\.\\d+(\\.\\d+)?)?$)',
  43. 'error_msg': RELEASE_REGEX_ERROR}
  44. STORAGE_KIND_TUPLE = (
  45. 'openshift_loggingops_storage_kind',
  46. 'openshift_logging_storage_kind',
  47. 'openshift_metrics_storage_kind',
  48. 'openshift_prometheus_alertbuffer_storage_kind',
  49. 'openshift_prometheus_alertmanager_storage_kind',
  50. 'openshift_prometheus_storage_kind')
  51. IMAGE_POLICY_CONFIG_VAR = "openshift_master_image_policy_config"
  52. ALLOWED_REGISTRIES_VAR = "openshift_master_image_policy_allowed_registries_for_import"
  53. REMOVED_VARIABLES = (
  54. # TODO(michaelgugino): Remove in 3.12
  55. ('oreg_auth_credentials_replace', 'Removed: Credentials are now always updated'),
  56. ('oreg_url_master', 'oreg_url'),
  57. ('oreg_url_node', 'oreg_url'),
  58. ('openshift_cockpit_deployer_prefix', 'openshift_cockpit_deployer_image'),
  59. ('openshift_cockpit_deployer_basename', 'openshift_cockpit_deployer_image'),
  60. ('openshift_cockpit_deployer_version', 'openshift_cockpit_deployer_image'),
  61. ('openshift_hosted_logging_elasticsearch_pvc_prefix', 'openshift_logging_es_pvc_prefix'),
  62. ('logging_ops_hostname', 'openshift_logging_kibana_ops_hostname'),
  63. ('openshift_hosted_logging_ops_hostname', 'openshift_logging_kibana_ops_hostname'),
  64. ('openshift_hosted_logging_elasticsearch_cluster_size', 'logging_elasticsearch_cluster_size'),
  65. ('openshift_hosted_logging_elasticsearch_ops_cluster_size', 'logging_elasticsearch_ops_cluster_size'),
  66. ('openshift_hosted_logging_storage_kind', 'openshift_logging_storage_kind'),
  67. ('openshift_hosted_logging_storage_host', 'openshift_logging_storage_host'),
  68. ('openshift_hosted_logging_storage_labels', 'openshift_logging_storage_labels'),
  69. ('openshift_hosted_logging_storage_volume_size', 'openshift_logging_storage_volume_size'),
  70. ('openshift_hosted_loggingops_storage_kind', 'openshift_loggingops_storage_kind'),
  71. ('openshift_hosted_loggingops_storage_host', 'openshift_loggingops_storage_host'),
  72. ('openshift_hosted_loggingops_storage_labels', 'openshift_loggingops_storage_labels'),
  73. ('openshift_hosted_loggingops_storage_volume_size', 'openshift_loggingops_storage_volume_size'),
  74. ('openshift_hosted_logging_enable_ops_cluster', 'openshift_logging_use_ops'),
  75. ('openshift_hosted_logging_image_pull_secret', 'openshift_logging_image_pull_secret'),
  76. ('openshift_hosted_logging_hostname', 'openshift_logging_kibana_hostname'),
  77. ('openshift_hosted_logging_kibana_nodeselector', 'openshift_logging_kibana_nodeselector'),
  78. ('openshift_hosted_logging_kibana_ops_nodeselector', 'openshift_logging_kibana_ops_nodeselector'),
  79. ('openshift_hosted_logging_journal_source', 'openshift_logging_fluentd_journal_source'),
  80. ('openshift_hosted_logging_journal_read_from_head', 'openshift_logging_fluentd_journal_read_from_head'),
  81. ('openshift_hosted_logging_fluentd_nodeselector_label', 'openshift_logging_fluentd_nodeselector'),
  82. ('openshift_hosted_logging_elasticsearch_instance_ram', 'openshift_logging_es_memory_limit'),
  83. ('openshift_hosted_logging_elasticsearch_nodeselector', 'openshift_logging_es_nodeselector'),
  84. ('openshift_hosted_logging_elasticsearch_ops_nodeselector', 'openshift_logging_es_ops_nodeselector'),
  85. ('openshift_hosted_logging_elasticsearch_ops_instance_ram', 'openshift_logging_es_ops_memory_limit'),
  86. ('openshift_hosted_logging_storage_access_modes', 'openshift_logging_storage_access_modes'),
  87. ('openshift_hosted_logging_master_public_url', 'openshift_logging_master_public_url'),
  88. ('openshift_hosted_logging_deployer_prefix', 'openshift_logging_image_prefix'),
  89. ('openshift_hosted_logging_deployer_version', 'openshift_logging_image_version'),
  90. ('openshift_hosted_logging_deploy', 'openshift_logging_install_logging'),
  91. ('openshift_hosted_logging_curator_nodeselector', 'openshift_logging_curator_nodeselector'),
  92. ('openshift_hosted_logging_curator_ops_nodeselector', 'openshift_logging_curator_ops_nodeselector'),
  93. ('openshift_hosted_metrics_storage_access_modes', 'openshift_metrics_storage_access_modes'),
  94. ('openshift_hosted_metrics_storage_host', 'openshift_metrics_storage_host'),
  95. ('openshift_hosted_metrics_storage_nfs_directory', 'openshift_metrics_storage_nfs_directory'),
  96. ('openshift_hosted_metrics_storage_volume_name', 'openshift_metrics_storage_volume_name'),
  97. ('openshift_hosted_metrics_storage_volume_size', 'openshift_metrics_storage_volume_size'),
  98. ('openshift_hosted_metrics_storage_labels', 'openshift_metrics_storage_labels'),
  99. ('openshift_hosted_metrics_deployer_prefix', 'openshift_metrics_image_prefix'),
  100. ('openshift_hosted_metrics_deployer_version', 'openshift_metrics_image_version'),
  101. ('openshift_hosted_metrics_deploy', 'openshift_metrics_install_metrics'),
  102. ('openshift_hosted_metrics_storage_kind', 'openshift_metrics_storage_kind'),
  103. ('openshift_hosted_metrics_public_url', 'openshift_metrics_hawkular_hostname'),
  104. ('openshift_node_labels', 'openshift_node_groups[<item>].labels'),
  105. ('openshift_node_kubelet_args', 'openshift_node_groups[<item>].edits'),
  106. )
  107. # JSON_FORMAT_VARIABLES does not intende to cover all json variables, but
  108. # complicated json variables in hosts.example are covered.
  109. JSON_FORMAT_VARIABLES = (
  110. 'openshift_builddefaults_json',
  111. 'openshift_buildoverrides_json',
  112. 'openshift_master_admission_plugin_config',
  113. 'openshift_master_audit_config',
  114. 'openshift_crio_docker_gc_node_selector',
  115. 'openshift_master_image_policy_allowed_registries_for_import',
  116. 'openshift_master_image_policy_config',
  117. 'openshift_master_oauth_templates',
  118. 'container_runtime_extra_storage',
  119. 'openshift_additional_repos',
  120. 'openshift_master_identity_providers',
  121. 'openshift_master_htpasswd_users',
  122. 'openshift_additional_projects',
  123. 'openshift_hosted_routers',
  124. 'openshift_node_open_ports',
  125. 'openshift_master_open_ports',
  126. )
  127. def to_bool(var_to_check):
  128. """Determine a boolean value given the multiple
  129. ways bools can be specified in ansible."""
  130. # http://yaml.org/type/bool.html
  131. yes_list = (True, 1, "True", "1", "true", "TRUE",
  132. "Yes", "yes", "Y", "y", "YES",
  133. "on", "ON", "On")
  134. return var_to_check in yes_list
  135. def check_for_removed_vars(hostvars, host):
  136. """Fails if removed variables are found"""
  137. found_removed = []
  138. for item in REMOVED_VARIABLES:
  139. if item in hostvars[host]:
  140. found_removed.append(item)
  141. if found_removed:
  142. msg = "Found removed variables: "
  143. for item in found_removed:
  144. msg += "{} is replaced by {}; ".format(item[0], item[1])
  145. raise errors.AnsibleModuleError(msg)
  146. return None
  147. class ActionModule(ActionBase):
  148. """Action plugin to execute sanity checks."""
  149. def template_var(self, hostvars, host, varname):
  150. """Retrieve a variable from hostvars and template it.
  151. If undefined, return None type."""
  152. # We will set the current host and variable checked for easy debugging
  153. # if there are any unhandled exceptions.
  154. # pylint: disable=W0201
  155. self.last_checked_var = varname
  156. # pylint: disable=W0201
  157. self.last_checked_host = host
  158. res = hostvars[host].get(varname)
  159. if res is None:
  160. return None
  161. return self._templar.template(res)
  162. def check_openshift_deployment_type(self, hostvars, host):
  163. """Ensure a valid openshift_deployment_type is set"""
  164. openshift_deployment_type = self.template_var(hostvars, host,
  165. 'openshift_deployment_type')
  166. if openshift_deployment_type not in VALID_DEPLOYMENT_TYPES:
  167. type_strings = ", ".join(VALID_DEPLOYMENT_TYPES)
  168. msg = "openshift_deployment_type must be defined and one of {}".format(type_strings)
  169. raise errors.AnsibleModuleError(msg)
  170. return openshift_deployment_type
  171. def get_allowed_registries(self, hostvars, host):
  172. """Returns a list of configured allowedRegistriesForImport as a list of patterns"""
  173. allowed_registries_for_import = self.template_var(hostvars, host, ALLOWED_REGISTRIES_VAR)
  174. if allowed_registries_for_import is None:
  175. image_policy_config = self.template_var(hostvars, host, IMAGE_POLICY_CONFIG_VAR)
  176. if not image_policy_config:
  177. return image_policy_config
  178. if isinstance(image_policy_config, str):
  179. try:
  180. image_policy_config = json.loads(image_policy_config)
  181. except Exception:
  182. raise errors.AnsibleModuleError(
  183. "{} is not a valid json string".format(IMAGE_POLICY_CONFIG_VAR))
  184. if not isinstance(image_policy_config, dict):
  185. raise errors.AnsibleModuleError(
  186. "expected dictionary for {}, not {}".format(
  187. IMAGE_POLICY_CONFIG_VAR, type(image_policy_config)))
  188. detailed = image_policy_config.get("allowedRegistriesForImport", None)
  189. if not detailed:
  190. return detailed
  191. if not isinstance(detailed, list):
  192. raise errors.AnsibleModuleError("expected list for {}['{}'], not {}".format(
  193. IMAGE_POLICY_CONFIG_VAR, "allowedRegistriesForImport",
  194. type(allowed_registries_for_import)))
  195. try:
  196. return [i["domainName"] for i in detailed]
  197. except Exception:
  198. raise errors.AnsibleModuleError(
  199. "each item of allowedRegistriesForImport must be a dictionary with 'domainName' key")
  200. if not isinstance(allowed_registries_for_import, list):
  201. raise errors.AnsibleModuleError("expected list for {}, not {}".format(
  202. IMAGE_POLICY_CONFIG_VAR, type(allowed_registries_for_import)))
  203. return allowed_registries_for_import
  204. def check_whitelisted_registries(self, hostvars, host):
  205. """Ensure defined registries are whitelisted"""
  206. allowed = self.get_allowed_registries(hostvars, host)
  207. if allowed is None:
  208. return
  209. unmatched_registries = []
  210. for regvar in (
  211. "oreg_url"
  212. "openshift_cockpit_deployer_prefix",
  213. "openshift_metrics_image_prefix",
  214. "openshift_logging_image_prefix",
  215. "openshift_service_catalog_image_prefix",
  216. "openshift_docker_insecure_registries"):
  217. value = self.template_var(hostvars, host, regvar)
  218. if not value:
  219. continue
  220. if isinstance(value, list):
  221. registries = value
  222. else:
  223. registries = [value]
  224. for reg in registries:
  225. if not any(is_registry_match(reg, pat) for pat in allowed):
  226. unmatched_registries.append((regvar, reg))
  227. if unmatched_registries:
  228. registry_list = ", ".join(["{}:{}".format(n, v) for n, v in unmatched_registries])
  229. raise errors.AnsibleModuleError(
  230. "registry hostnames of the following image prefixes are not whitelisted by image"
  231. " policy configuration: {}".format(registry_list))
  232. def check_python_version(self, hostvars, host, distro):
  233. """Ensure python version is 3 for Fedora and python 2 for others"""
  234. ansible_python = self.template_var(hostvars, host, 'ansible_python')
  235. if distro == "Fedora":
  236. if ansible_python['version']['major'] != 3:
  237. msg = "openshift-ansible requires Python 3 for {};".format(distro)
  238. msg += " For information on enabling Python 3 with Ansible,"
  239. msg += " see https://docs.ansible.com/ansible/python_3_support.html"
  240. raise errors.AnsibleModuleError(msg)
  241. else:
  242. if ansible_python['version']['major'] != 2:
  243. msg = "openshift-ansible requires Python 2 for {};".format(distro)
  244. def check_image_tag_format(self, hostvars, host, openshift_deployment_type):
  245. """Ensure openshift_image_tag is formatted correctly"""
  246. openshift_image_tag = self.template_var(hostvars, host, 'openshift_image_tag')
  247. if not openshift_image_tag or openshift_image_tag == 'latest':
  248. return None
  249. regex_to_match = IMAGE_TAG_REGEX[openshift_deployment_type]['re']
  250. res = re.match(regex_to_match, str(openshift_image_tag))
  251. if res is None:
  252. msg = IMAGE_TAG_REGEX[openshift_deployment_type]['error_msg']
  253. msg = msg.format(str(openshift_image_tag))
  254. raise errors.AnsibleModuleError(msg)
  255. def check_pkg_version_format(self, hostvars, host):
  256. """Ensure openshift_pkg_version is formatted correctly"""
  257. openshift_pkg_version = self.template_var(hostvars, host, 'openshift_pkg_version')
  258. if not openshift_pkg_version:
  259. return None
  260. regex_to_match = PKG_VERSION_REGEX['re']
  261. res = re.match(regex_to_match, str(openshift_pkg_version))
  262. if res is None:
  263. msg = PKG_VERSION_REGEX['error_msg']
  264. msg = msg.format(str(openshift_pkg_version))
  265. raise errors.AnsibleModuleError(msg)
  266. def check_release_format(self, hostvars, host):
  267. """Ensure openshift_release is formatted correctly"""
  268. openshift_release = self.template_var(hostvars, host, 'openshift_release')
  269. if not openshift_release:
  270. return None
  271. regex_to_match = RELEASE_REGEX['re']
  272. res = re.match(regex_to_match, str(openshift_release))
  273. if res is None:
  274. msg = RELEASE_REGEX['error_msg']
  275. msg = msg.format(str(openshift_release))
  276. raise errors.AnsibleModuleError(msg)
  277. def network_plugin_check(self, hostvars, host):
  278. """Ensure only one type of network plugin is enabled"""
  279. res = []
  280. # Loop through each possible network plugin boolean, determine the
  281. # actual boolean value, and append results into a list.
  282. for plugin, default_val in NET_PLUGIN_LIST:
  283. res_temp = self.template_var(hostvars, host, plugin)
  284. if res_temp is None:
  285. res_temp = default_val
  286. res.append(to_bool(res_temp))
  287. if sum(res) not in (0, 1):
  288. plugin_str = list(zip([x[0] for x in NET_PLUGIN_LIST], res))
  289. msg = "Host Checked: {} Only one of must be true. Found: {}".format(host, plugin_str)
  290. raise errors.AnsibleModuleError(msg)
  291. def check_hostname_vars(self, hostvars, host):
  292. """Checks to ensure openshift_hostname
  293. and openshift_public_hostname
  294. conform to the proper length of 63 characters or less"""
  295. for varname in ('openshift_public_hostname', 'openshift_hostname'):
  296. var_value = self.template_var(hostvars, host, varname)
  297. if var_value and len(var_value) > 63:
  298. msg = '{} must be 63 characters or less'.format(varname)
  299. raise errors.AnsibleModuleError(msg)
  300. def check_session_auth_secrets(self, hostvars, host):
  301. """Checks session_auth_secrets is correctly formatted"""
  302. sas = self.template_var(hostvars, host,
  303. 'openshift_master_session_auth_secrets')
  304. ses = self.template_var(hostvars, host,
  305. 'openshift_master_session_encryption_secrets')
  306. # This variable isn't mandatory, only check if set.
  307. if sas is None and ses is None:
  308. return None
  309. if not (
  310. issubclass(type(sas), list) and issubclass(type(ses), list)
  311. ) or len(sas) != len(ses):
  312. raise errors.AnsibleModuleError(
  313. 'Expects openshift_master_session_auth_secrets and '
  314. 'openshift_master_session_encryption_secrets are equal length lists')
  315. for secret in sas:
  316. if len(secret) < 32:
  317. raise errors.AnsibleModuleError(
  318. 'Invalid secret in openshift_master_session_auth_secrets. '
  319. 'Secrets must be at least 32 characters in length.')
  320. for secret in ses:
  321. if len(secret) not in [16, 24, 32]:
  322. raise errors.AnsibleModuleError(
  323. 'Invalid secret in openshift_master_session_encryption_secrets. '
  324. 'Secrets must be 16, 24, or 32 characters in length.')
  325. return None
  326. def check_unsupported_nfs_configs(self, hostvars, host):
  327. """Fails if nfs storage is in use for any components. This check is
  328. ignored if openshift_enable_unsupported_configurations=True"""
  329. enable_unsupported = self.template_var(
  330. hostvars, host, 'openshift_enable_unsupported_configurations')
  331. if to_bool(enable_unsupported):
  332. return None
  333. for storage in STORAGE_KIND_TUPLE:
  334. kind = self.template_var(hostvars, host, storage)
  335. if kind == 'nfs':
  336. raise errors.AnsibleModuleError(
  337. 'nfs is an unsupported type for {}. '
  338. 'openshift_enable_unsupported_configurations=True must'
  339. 'be specified to continue with this configuration.'
  340. ''.format(storage))
  341. return None
  342. def check_htpasswd_provider(self, hostvars, host):
  343. """Fails if openshift_master_identity_providers contains an entry of
  344. kind HTPasswdPasswordIdentityProvider and
  345. openshift_master_manage_htpasswd is False"""
  346. manage_pass = self.template_var(
  347. hostvars, host, 'openshift_master_manage_htpasswd')
  348. if to_bool(manage_pass):
  349. # If we manage the file, we can just generate in the new path.
  350. return None
  351. idps = self.template_var(
  352. hostvars, host, 'openshift_master_identity_providers')
  353. if not idps:
  354. # If we don't find any identity_providers, nothing for us to do.
  355. return None
  356. old_keys = ('file', 'fileName', 'file_name', 'filename')
  357. if not isinstance(idps, list):
  358. raise errors.AnsibleModuleError("| not a list")
  359. for idp in idps:
  360. if idp['kind'] == 'HTPasswdPasswordIdentityProvider':
  361. for old_key in old_keys:
  362. if old_key in idp is not None:
  363. raise errors.AnsibleModuleError(
  364. 'openshift_master_identity_providers contains a '
  365. 'provider of kind==HTPasswdPasswordIdentityProvider '
  366. 'and {} is set. Please migrate your htpasswd '
  367. 'files to /etc/origin/master/htpasswd and update your '
  368. 'existing master configs, and remove the {} key'
  369. 'before proceeding.'.format(old_key, old_key))
  370. def validate_json_format_vars(self, hostvars, host):
  371. """Fails if invalid json format are found"""
  372. found_invalid_json = []
  373. for var in JSON_FORMAT_VARIABLES:
  374. if var in hostvars[host]:
  375. json_var = self.template_var(hostvars, host, var)
  376. try:
  377. json.loads(json_var)
  378. except ValueError as json_err:
  379. found_invalid_json.append([var, json_var, json_err])
  380. except BaseException:
  381. pass
  382. if found_invalid_json:
  383. msg = "Found invalid json format variables:\n"
  384. for item in found_invalid_json:
  385. msg += " {} specified in {} is invalid json format\n {}".format(item[1], item[0], item[2])
  386. raise errors.AnsibleModuleError(msg)
  387. return None
  388. def check_for_oreg_password(self, hostvars, host, odt):
  389. """Ensure oreg_password is defined when using registry.redhat.io"""
  390. reg_to_check = 'registry.redhat.io'
  391. err_msg = ("oreg_auth_user and oreg_auth_password must be provided when"
  392. "deploying openshift-enterprise")
  393. err_msg2 = ("oreg_auth_user and oreg_auth_password must be provided when using"
  394. "{}".format(reg_to_check))
  395. oreg_password = self.template_var(hostvars, host, 'oreg_auth_password')
  396. if oreg_password is not None:
  397. # A password is defined, so we're good to go.
  398. return None
  399. oreg_url = self.template_var(hostvars, host, 'oreg_url')
  400. if oreg_url is not None:
  401. if reg_to_check in oreg_url:
  402. raise errors.AnsibleModuleError(err_msg2)
  403. elif odt == 'openshift-enterprise':
  404. # We're not using an oreg_url, we're using default enterprise
  405. # registry. We require oreg_auth_user and oreg_auth_password
  406. raise errors.AnsibleModuleError(err_msg)
  407. def run_checks(self, hostvars, host):
  408. """Execute the hostvars validations against host"""
  409. distro = self.template_var(hostvars, host, 'ansible_distribution')
  410. odt = self.check_openshift_deployment_type(hostvars, host)
  411. self.check_whitelisted_registries(hostvars, host)
  412. self.check_python_version(hostvars, host, distro)
  413. self.check_image_tag_format(hostvars, host, odt)
  414. self.check_pkg_version_format(hostvars, host)
  415. self.check_release_format(hostvars, host)
  416. self.network_plugin_check(hostvars, host)
  417. self.check_hostname_vars(hostvars, host)
  418. self.check_session_auth_secrets(hostvars, host)
  419. self.check_unsupported_nfs_configs(hostvars, host)
  420. self.check_htpasswd_provider(hostvars, host)
  421. check_for_removed_vars(hostvars, host)
  422. self.validate_json_format_vars(hostvars, host)
  423. self.check_for_oreg_password(hostvars, host, odt)
  424. def run(self, tmp=None, task_vars=None):
  425. result = super(ActionModule, self).run(tmp, task_vars)
  426. # self.task_vars holds all in-scope variables.
  427. # Ignore settting self.task_vars outside of init.
  428. # pylint: disable=W0201
  429. self.task_vars = task_vars or {}
  430. # pylint: disable=W0201
  431. self.last_checked_host = "none"
  432. # pylint: disable=W0201
  433. self.last_checked_var = "none"
  434. # self._task.args holds task parameters.
  435. # check_hosts is a parameter to this plugin, and should provide
  436. # a list of hosts.
  437. check_hosts = self._task.args.get('check_hosts')
  438. if not check_hosts:
  439. msg = "check_hosts is required"
  440. raise errors.AnsibleModuleError(msg)
  441. # We need to access each host's variables
  442. hostvars = self.task_vars.get('hostvars')
  443. if not hostvars:
  444. msg = hostvars
  445. raise errors.AnsibleModuleError(msg)
  446. # We loop through each host in the provided list check_hosts
  447. for host in check_hosts:
  448. try:
  449. self.run_checks(hostvars, host)
  450. except Exception as uncaught_e:
  451. msg = "last_checked_host: {}, last_checked_var: {};"
  452. msg = msg.format(self.last_checked_host, self.last_checked_var)
  453. msg += str(uncaught_e)
  454. raise errors.AnsibleModuleError(msg)
  455. result["changed"] = False
  456. result["failed"] = False
  457. result["msg"] = "Sanity Checks passed"
  458. return result
  459. def is_registry_match(item, pattern):
  460. """returns True if the registry matches the given whitelist pattern
  461. Unlike in OpenShift, the comparison is done solely on hostname part
  462. (excluding the port part) since the latter is much more difficult due to
  463. vague definition of port defaulting based on insecure flag. Moreover, most
  464. of the registries will be listed without the port and insecure flag.
  465. """
  466. item = "schema://" + item.split('://', 1)[-1]
  467. return is_match(urlparse(item).hostname, pattern.rsplit(':', 1)[0])
  468. # taken from https://leetcode.com/problems/wildcard-matching/discuss/17845/python-dp-solution
  469. # (the same source as for openshift/origin/pkg/util/strings/wildcard.go)
  470. def is_match(item, pattern):
  471. """implements DP algorithm for string matching"""
  472. length = len(item)
  473. if len(pattern) - pattern.count('*') > length:
  474. return False
  475. matches = [True] + [False] * length
  476. for i in pattern:
  477. if i != '*':
  478. for index in reversed(range(length)):
  479. matches[index + 1] = matches[index] and (i == item[index] or i == '?')
  480. else:
  481. for index in range(1, length + 1):
  482. matches[index] = matches[index - 1] or matches[index]
  483. matches[0] = matches[0] and i == '*'
  484. return matches[-1]