From 9041a816210132b1dad86217ecfef5f9fec4a8d2 Mon Sep 17 00:00:00 2001
From: Rodolfo Carvalho <rhcarvalho@gmail.com>
Date: Fri, 6 Jan 2017 20:53:05 +0100
Subject: Refactor preflight check into roles

---
 roles/openshift_preflight/README.md                | 53 ++++++++++++
 roles/openshift_preflight/common/library           |  1 +
 roles/openshift_preflight/common/tasks/main.yml    | 22 +++++
 roles/openshift_preflight/facts/tasks/main.yml     |  7 ++
 roles/openshift_preflight/masters/library          |  1 +
 roles/openshift_preflight/masters/tasks/main.yml   | 47 +++++++++++
 roles/openshift_preflight/nodes/library            |  1 +
 roles/openshift_preflight/nodes/tasks/main.yml     | 57 +++++++++++++
 .../callback_plugins/zz_failure_summary.py         | 96 ++++++++++++++++++++++
 .../verify_status/tasks/main.yml                   |  8 ++
 10 files changed, 293 insertions(+)
 create mode 100644 roles/openshift_preflight/README.md
 create mode 120000 roles/openshift_preflight/common/library
 create mode 100644 roles/openshift_preflight/common/tasks/main.yml
 create mode 100644 roles/openshift_preflight/facts/tasks/main.yml
 create mode 120000 roles/openshift_preflight/masters/library
 create mode 100644 roles/openshift_preflight/masters/tasks/main.yml
 create mode 120000 roles/openshift_preflight/nodes/library
 create mode 100644 roles/openshift_preflight/nodes/tasks/main.yml
 create mode 100644 roles/openshift_preflight/verify_status/callback_plugins/zz_failure_summary.py
 create mode 100644 roles/openshift_preflight/verify_status/tasks/main.yml

(limited to 'roles/openshift_preflight')

diff --git a/roles/openshift_preflight/README.md b/roles/openshift_preflight/README.md
new file mode 100644
index 000000000..a3d734e32
--- /dev/null
+++ b/roles/openshift_preflight/README.md
@@ -0,0 +1,53 @@
+OpenShift Preflight Checks
+==========================
+
+This role detects common problems prior to installing OpenShift.
+
+Requirements
+------------
+
+* Ansible 2.2+
+
+Role Variables
+--------------
+
+None
+
+Dependencies
+------------
+
+None
+
+Example Playbook
+----------------
+
+```yaml
+---
+- hosts: OSEv3
+  gather_facts: no
+  roles:
+    - openshift_preflight/facts
+
+- hosts: OSEv3
+  name: checks that apply to all hosts
+  gather_facts: no
+  ignore_errors: yes
+  roles:
+    - openshift_preflight/common
+
+- hosts: OSEv3
+  name: verify check results
+  gather_facts: no
+  roles:
+    - openshift_preflight/verify_status
+```
+
+License
+-------
+
+Apache License Version 2.0
+
+Author Information
+------------------
+
+Customer Success team (dev@lists.openshift.redhat.com)
diff --git a/roles/openshift_preflight/common/library b/roles/openshift_preflight/common/library
new file mode 120000
index 000000000..ba40d2f56
--- /dev/null
+++ b/roles/openshift_preflight/common/library
@@ -0,0 +1 @@
+../../../library
\ No newline at end of file
diff --git a/roles/openshift_preflight/common/tasks/main.yml b/roles/openshift_preflight/common/tasks/main.yml
new file mode 100644
index 000000000..423b13586
--- /dev/null
+++ b/roles/openshift_preflight/common/tasks/main.yml
@@ -0,0 +1,22 @@
+---
+# check content available on all hosts
+- when:
+    - not containerized
+  block:
+
+    - name: determine if yum update will work
+      action: check_yum_update
+      register: r
+
+    - set_fact:
+        oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'check content available on all hosts'})] }}"
+
+    - name: determine if expected version matches what is available
+      aos_version:
+        version: "{{ openshift_release }}"
+      when:
+        - deployment_type == "openshift-enterprise"
+      register: r
+
+    - set_fact:
+        oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'determine if expected version matches what is available'})] }}"
diff --git a/roles/openshift_preflight/facts/tasks/main.yml b/roles/openshift_preflight/facts/tasks/main.yml
new file mode 100644
index 000000000..15a108510
--- /dev/null
+++ b/roles/openshift_preflight/facts/tasks/main.yml
@@ -0,0 +1,7 @@
+---
+- name: set common variables
+  set_fact:
+    deployment_type: "{{ deployment_type | default('openshift-enterprise') }}"
+    containerized: "{{ containerized | default('no') | bool }}"
+    openshift_release: "{{ openshift_release | default('3.3') }}"
+    oo_preflight_check_results: "{{ oo_preflight_check_results | default([]) }}"
diff --git a/roles/openshift_preflight/masters/library b/roles/openshift_preflight/masters/library
new file mode 120000
index 000000000..ba40d2f56
--- /dev/null
+++ b/roles/openshift_preflight/masters/library
@@ -0,0 +1 @@
+../../../library
\ No newline at end of file
diff --git a/roles/openshift_preflight/masters/tasks/main.yml b/roles/openshift_preflight/masters/tasks/main.yml
new file mode 100644
index 000000000..ed42f69fa
--- /dev/null
+++ b/roles/openshift_preflight/masters/tasks/main.yml
@@ -0,0 +1,47 @@
+---
+# determine if yum install of master pkgs will work
+- when:
+    - not containerized
+  block:
+
+    - name: main packages for enterprise
+      when:
+        - deployment_type == "openshift-enterprise"
+      check_yum_update:
+        packages:
+          - atomic-openshift
+          - atomic-openshift-clients
+          - atomic-openshift-master
+      register: r
+
+    - set_fact:
+        oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'main packages for enterprise'})] }}"
+
+    - name: main packages for origin
+      when:
+        - deployment_type == "origin"
+      check_yum_update:
+        packages:
+          - origin
+          - origin-clients
+          - origin-master
+      register: r
+
+    - set_fact:
+        oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'main packages for origin'})] }}"
+
+    - name: other master packages
+      check_yum_update:
+        packages:
+          - etcd
+          - bash-completion
+          - cockpit-bridge
+          - cockpit-docker
+          - cockpit-kubernetes
+          - cockpit-shell
+          - cockpit-ws
+          - httpd-tools
+      register: r
+
+    - set_fact:
+        oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'other master packages'})] }}"
diff --git a/roles/openshift_preflight/nodes/library b/roles/openshift_preflight/nodes/library
new file mode 120000
index 000000000..ba40d2f56
--- /dev/null
+++ b/roles/openshift_preflight/nodes/library
@@ -0,0 +1 @@
+../../../library
\ No newline at end of file
diff --git a/roles/openshift_preflight/nodes/tasks/main.yml b/roles/openshift_preflight/nodes/tasks/main.yml
new file mode 100644
index 000000000..16c7212d4
--- /dev/null
+++ b/roles/openshift_preflight/nodes/tasks/main.yml
@@ -0,0 +1,57 @@
+---
+# determine if yum install of node pkgs will work
+- when:
+    - not containerized
+  block:
+
+    - name: main packages for enterprise
+      when:
+        - deployment_type == "openshift-enterprise"
+      check_yum_update:
+        packages:
+          - atomic-openshift
+          - atomic-openshift-node
+          - atomic-openshift-sdn-ovs
+      register: r
+
+    - set_fact:
+        oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'main packages for enterprise'})] }}"
+
+    - name: main packages for origin
+      when:
+        - deployment_type == "origin"
+      check_yum_update:
+        packages:
+          - origin
+          - origin-node
+          - origin-sdn-ovs
+      register: r
+
+    - set_fact:
+        oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'main packages for origin'})] }}"
+
+    - name: other node packages
+      check_yum_update:
+        packages:
+          - docker
+          - PyYAML
+          - firewalld
+          - iptables
+          - iptables-services
+          - nfs-utils
+          - ntp
+          - yum-utils
+          - dnsmasq
+          - libselinux-python
+          - ceph-common
+          - glusterfs-fuse
+          - iscsi-initiator-utils
+          - pyparted
+          - python-httplib2
+          - openssl
+          - flannel
+          - bind
+      register: r
+
+    - set_fact:
+        oo_preflight_check_results: "{{ oo_preflight_check_results + [r|combine({'_task': 'other node packages'})] }}"
diff --git a/roles/openshift_preflight/verify_status/callback_plugins/zz_failure_summary.py b/roles/openshift_preflight/verify_status/callback_plugins/zz_failure_summary.py
new file mode 100644
index 000000000..180ed8d8f
--- /dev/null
+++ b/roles/openshift_preflight/verify_status/callback_plugins/zz_failure_summary.py
@@ -0,0 +1,96 @@
+# vim: expandtab:tabstop=4:shiftwidth=4
+'''
+Ansible callback plugin.
+'''
+
+from ansible.plugins.callback import CallbackBase
+from ansible import constants as C
+from ansible.utils.color import stringc
+
+
+class CallbackModule(CallbackBase):
+    '''
+    This callback plugin stores task results and summarizes failures.
+    The file name is prefixed with `zz_` to make this plugin be loaded last by
+    Ansible, thus making its output the last thing that users see.
+    '''
+
+    CALLBACK_VERSION = 2.0
+    CALLBACK_TYPE = 'aggregate'
+    CALLBACK_NAME = 'failure_summary'
+    CALLBACK_NEEDS_WHITELIST = False
+
+    def __init__(self):
+        super(CallbackModule, self).__init__()
+        self.__failures = []
+
+    def v2_runner_on_failed(self, result, ignore_errors=False):
+        super(CallbackModule, self).v2_runner_on_failed(result, ignore_errors)
+        self.__failures.append(dict(result=result, ignore_errors=ignore_errors))
+
+    def v2_playbook_on_stats(self, stats):
+        super(CallbackModule, self).v2_playbook_on_stats(stats)
+        # TODO: update condition to consider a host var or env var to
+        # enable/disable the summary, so that we can control the output from a
+        # play.
+        if self.__failures:
+            self._print_failure_summary()
+
+    def _print_failure_summary(self):
+        '''Print a summary of failed tasks (including ignored failures).'''
+        self._display.display(u'\nFailure summary:\n')
+
+        # TODO: group failures by host or by task. If grouped by host, it is
+        # easy to see all problems of a given host. If grouped by task, it is
+        # easy to see what hosts needs the same fix.
+
+        width = len(str(len(self.__failures)))
+        initial_indent_format = u'  {{:>{width}}}. '.format(width=width)
+        initial_indent_len = len(initial_indent_format.format(0))
+        subsequent_indent = u' ' * initial_indent_len
+        subsequent_extra_indent = u' ' * (initial_indent_len + 10)
+
+        for i, failure in enumerate(self.__failures, 1):
+            lines = _format_failure(failure)
+            self._display.display(u'\n{}{}'.format(initial_indent_format.format(i), lines[0]))
+            for line in lines[1:]:
+                line = line.replace(u'\n', u'\n' + subsequent_extra_indent)
+                indented = u'{}{}'.format(subsequent_indent, line)
+                self._display.display(indented)
+
+
+# Reason: disable pylint protected-access because we need to access _*
+#         attributes of a task result to implement this method.
+# Status: permanently disabled unless Ansible's API changes.
+# pylint: disable=protected-access
+def _format_failure(failure):
+    '''Return a list of pretty-formatted lines describing a failure, including
+    relevant information about it. Line separators are not included.'''
+    result = failure['result']
+    host = result._host.get_name()
+    play = _get_play(result._task)
+    if play:
+        play = play.get_name()
+    task = result._task.get_name()
+    msg = result._result.get('msg', u'???')
+    rows = (
+        (u'Host', host),
+        (u'Play', play),
+        (u'Task', task),
+        (u'Message', stringc(msg, C.COLOR_ERROR)),
+    )
+    row_format = '{:10}{}'
+    return [row_format.format(header + u':', body) for header, body in rows]
+
+
+# Reason: disable pylint protected-access because we need to access _*
+#         attributes of obj to implement this function.
+#         This is inspired by ansible.playbook.base.Base.dump_me.
+# Status: permanently disabled unless Ansible's API changes.
+# pylint: disable=protected-access
+def _get_play(obj):
+    '''Given a task or block, recursively tries to find its parent play.'''
+    if hasattr(obj, '_play'):
+        return obj._play
+    if getattr(obj, '_parent'):
+        return _get_play(obj._parent)
diff --git a/roles/openshift_preflight/verify_status/tasks/main.yml b/roles/openshift_preflight/verify_status/tasks/main.yml
new file mode 100644
index 000000000..36ccf648a
--- /dev/null
+++ b/roles/openshift_preflight/verify_status/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- name: find check failures
+  set_fact:
+    oo_preflight_check_failures: "{{ oo_preflight_check_results | select('failed', 'equalto', True) | list }}"
+
+- name: ensure all checks succeed
+  action: fail
+  when: oo_preflight_check_failures
-- 
cgit v1.2.3