Browse Source

refactor handling of scheduler defaults

Jason DeTiberus 8 years ago
parent
commit
81d8b6a835

+ 16 - 32
roles/openshift_facts/library/openshift_facts.py

@@ -883,21 +883,23 @@ def set_version_facts_if_unset(facts):
     """
     if 'common' in facts:
         deployment_type = facts['common']['deployment_type']
-        version = get_openshift_version(facts)
-        if version:
-            facts['common']['version'] = version
+        openshift_version = get_openshift_version(facts)
+        if openshift_version:
+            version = LooseVersion(openshift_version)
+            facts['common']['version'] = openshift_version
+            facts['common']['short_version'] = '.'.join([str(x) for x in version.version[0:2]])
             if deployment_type == 'origin':
-                version_gte_3_1_or_1_1 = LooseVersion(version) >= LooseVersion('1.1.0')
-                version_gte_3_1_1_or_1_1_1 = LooseVersion(version) >= LooseVersion('1.1.1')
-                version_gte_3_2_or_1_2 = LooseVersion(version) >= LooseVersion('1.2.0')
-                version_gte_3_3_or_1_3 = LooseVersion(version) >= LooseVersion('1.3.0')
-                version_gte_3_4_or_1_4 = LooseVersion(version) >= LooseVersion('1.4.0')
+                version_gte_3_1_or_1_1 = version >= LooseVersion('1.1.0')
+                version_gte_3_1_1_or_1_1_1 = version >= LooseVersion('1.1.1')
+                version_gte_3_2_or_1_2 = version >= LooseVersion('1.2.0')
+                version_gte_3_3_or_1_3 = version >= LooseVersion('1.3.0')
+                version_gte_3_4_or_1_4 = version >= LooseVersion('1.4.0')
             else:
-                version_gte_3_1_or_1_1 = LooseVersion(version) >= LooseVersion('3.0.2.905')
-                version_gte_3_1_1_or_1_1_1 = LooseVersion(version) >= LooseVersion('3.1.1')
-                version_gte_3_2_or_1_2 = LooseVersion(version) >= LooseVersion('3.1.1.901')
-                version_gte_3_3_or_1_3 = LooseVersion(version) >= LooseVersion('3.3.0')
-                version_gte_3_4_or_1_4 = LooseVersion(version) >= LooseVersion('3.4.0')
+                version_gte_3_1_or_1_1 = version >= LooseVersion('3.0.2.905')
+                version_gte_3_1_1_or_1_1_1 = version >= LooseVersion('3.1.1')
+                version_gte_3_2_or_1_2 = version >= LooseVersion('3.1.1.901')
+                version_gte_3_3_or_1_3 = version >= LooseVersion('3.3.0')
+                version_gte_3_4_or_1_4 = version >= LooseVersion('3.4.0')
         else:
             version_gte_3_1_or_1_1 = True
             version_gte_3_1_1_or_1_1_1 = True
@@ -1483,7 +1485,7 @@ def merge_facts(orig, new, additive_facts_to_overwrite, protected_facts_to_overw
             elif key in protected_facts and key not in [x.split('.')[-1] for x in protected_facts_to_overwrite]:
                 # The master count (int) can only increase unless it
                 # has been passed as a protected fact to overwrite.
-                if key == 'master_count':
+                if key == 'master_count' and new[key] is not None and new[key] is not '':
                     if int(value) <= int(new[key]):
                         facts[key] = copy.deepcopy(new[key])
                     else:
@@ -1926,22 +1928,6 @@ class OpenShiftFacts(object):
                                   debug_level=2)
 
         if 'master' in roles:
-            scheduler_predicates = [
-                {"name": "MatchNodeSelector"},
-                {"name": "PodFitsResources"},
-                {"name": "PodFitsPorts"},
-                {"name": "NoDiskConflict"},
-                {"name": "NoVolumeZoneConflict"},
-                {"name": "MaxEBSVolumeCount"},
-                {"name": "MaxGCEPDVolumeCount"},
-                {"name": "Region", "argument": {"serviceAffinity" : {"labels" : ["region"]}}}
-            ]
-            scheduler_priorities = [
-                {"name": "LeastRequestedPriority", "weight": 1},
-                {"name": "SelectorSpreadPriority", "weight": 1},
-                {"name": "Zone", "weight" : 2, "argument": {"serviceAntiAffinity" : {"label": "zone"}}}
-            ]
-
             defaults['master'] = dict(api_use_ssl=True, api_port='8443',
                                       controllers_port='8444',
                                       console_use_ssl=True,
@@ -1958,8 +1944,6 @@ class OpenShiftFacts(object):
                                       access_token_max_seconds=86400,
                                       auth_token_max_seconds=500,
                                       oauth_grant_method='auto',
-                                      scheduler_predicates=scheduler_predicates,
-                                      scheduler_priorities=scheduler_priorities,
                                       dynamic_provisioning_enabled=True,
                                       max_requests_inflight=500)
 

+ 2 - 5
roles/openshift_master/vars/main.yml

@@ -1,17 +1,14 @@
 ---
-openshift_master_config_dir: "{{ openshift.common.config_base }}/master"
-openshift_master_config_file: "{{ openshift_master_config_dir }}/master-config.yaml"
 openshift_master_loopback_config: "{{ openshift_master_config_dir }}/openshift-master.kubeconfig"
 loopback_context_string: "current-context: {{ openshift.master.loopback_context_name }}"
-openshift_master_scheduler_conf: "{{ openshift_master_config_dir }}/scheduler.json"
 openshift_master_session_secrets_file: "{{ openshift_master_config_dir }}/session-secrets.yaml"
 openshift_master_policy: "{{ openshift_master_config_dir }}/policy.json"
 
 scheduler_config:
   kind: Policy
   apiVersion: v1
-  predicates: "{{ openshift.master.scheduler_predicates }}"
-  priorities: "{{ openshift.master.scheduler_priorities }}"
+  predicates: "{{ openshift_master_scheduler_predicates }}"
+  priorities: "{{ openshift_master_scheduler_priorities }}"
 
 openshift_master_valid_grant_methods:
 - auto

+ 96 - 0
roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_predicates.py

@@ -0,0 +1,96 @@
+# pylint: disable=missing-docstring
+
+import re
+from ansible.errors import AnsibleError
+from ansible.plugins.lookup import LookupBase
+
+
+class LookupModule(LookupBase):
+    # pylint: disable=too-many-branches,too-many-statements
+
+    def run(self, terms, variables=None, regions_enabled=True, **kwargs):
+        if 'openshift' not in variables:
+            raise AnsibleError("This lookup module requires openshift_facts to be run prior to use")
+        if 'master' not in variables['openshift']:
+            raise AnsibleError("This lookup module is meant to be run against an OpenShift master host only")
+
+        if 'openshift_master_scheduler_predicates' in variables:
+            return variables['openshift_master_scheduler_predicates']
+        elif 'scheduler_predicates' in variables['openshift']['master']:
+            return variables['openshift']['master']['scheduler_predicates']
+        else:
+            predicates = []
+
+            if 'deployment_type' not in variables['openshift']['common']:
+                raise AnsibleError("This lookup module requires that the deployment_type be set")
+
+            deployment_type = variables['openshift']['common']['deployment_type']
+
+            if 'short_version' in variables['openshift']['common']:
+                short_version = variables['openshift']['common']['short_version']
+            elif 'openshift_release' in variables:
+                release = variables['openshift_release']
+                if release.startswith('v'):
+                    short_version = release[1:]
+                else:
+                    short_version = release
+            elif 'openshift_version' in variables:
+                version = variables['openshift_version']
+                short_version = '.'.join(version.split('.')[0:2])
+            else:
+                # pylint: disable=line-too-long
+                raise AnsibleError("Either OpenShift needs to be installed or openshift_release needs to be specified")
+            if deployment_type not in ['origin', 'openshift-enterprise']:
+                raise AnsibleError("Unknown deployment_type %s" % deployment_type)
+
+            if deployment_type == 'origin':
+                if short_version not in ['1.1', '1.2', '1.3', '1.4']:
+                    raise AnsibleError("Unknown short_version %s" % short_version)
+            elif deployment_type == 'openshift_enterprise':
+                if short_version not in ['3.1', '3.2', '3.3', '3.4']:
+                    raise AnsibleError("Unknown short_version %s" % short_version)
+
+            if deployment_type == 'openshift-enterprise':
+                # convert short_version to origin short_version
+                short_version = re.sub('^3.', '1.', short_version)
+
+            if short_version in ['1.1', '1.2']:
+                predicates.append({'name': 'PodFitsHostPorts'})
+                predicates.append({'name': 'PodFitsResources'})
+
+            # applies to all known versions
+            predicates.append({'name': 'NoDiskConflict'})
+
+            # only 1.1 didn't include NoVolumeZoneConflict
+            if short_version != '1.1':
+                predicates.append({'name': 'NoVolumeZoneConflict'})
+
+            if short_version in ['1.1', '1.2']:
+                predicates.append({'name': 'MatchNodeSelector'})
+                predicates.append({'name': 'Hostname'})
+
+            if short_version != '1.1':
+                predicates.append({'name': 'MaxEBSVolumeCount'})
+                predicates.append({'name': 'MaxGCEPDVolumeCount'})
+
+            if short_version not in ['1.1', '1.2']:
+                predicates.append({'name': 'GeneralPredicates'})
+                predicates.append({'name': 'PodToleratesNodeTaints'})
+                predicates.append({'name': 'CheckNodeMemoryPressure'})
+
+            if short_version not in ['1.1', '1.2', '1.3']:
+                predicates.append({'name': 'CheckNodeDiskPressure'})
+                predicates.append({'name': 'MatchInterPodAffinity'})
+
+            if regions_enabled:
+                region_predicate = {
+                    'name': 'Region',
+                    'argument': {
+                        'serviceAffinity': {
+                            'labels': ['region']
+                        }
+                    }
+                }
+                predicates.append(region_predicate)
+
+            return predicates

+ 87 - 0
roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_priorities.py

@@ -0,0 +1,87 @@
+# pylint: disable=missing-docstring
+
+import re
+from ansible.errors import AnsibleError
+from ansible.plugins.lookup import LookupBase
+
+
+class LookupModule(LookupBase):
+    # pylint: disable=too-many-branches
+
+    def run(self, terms, variables=None, zones_enabled=True, **kwargs):
+        if 'openshift' not in variables:
+            raise AnsibleError("This lookup module requires openshift_facts to be run prior to use")
+        if 'master' not in variables['openshift']:
+            raise AnsibleError("This lookup module is meant to be run against an OpenShift master host only")
+
+        if 'openshift_master_scheduler_priorities' in variables:
+            return variables['openshift_master_scheduler_priorities']
+        elif 'scheduler_priorities' in variables['openshift']['master']:
+            return variables['openshift']['master']['scheduler_priorities']
+        else:
+            priorities = [
+                {'name': 'LeastRequestedPriority', 'weight': 1},
+                {'name': 'BalancedResourceAllocation', 'weight': 1},
+                {'name': 'SelectorSpreadPriority', 'weight': 1}
+            ]
+
+            if 'deployment_type' not in variables['openshift']['common']:
+                raise AnsibleError("This lookup module requires that the deployment_type be set")
+
+            deployment_type = variables['openshift']['common']['deployment_type']
+
+            if 'short_version' in variables['openshift']['common']:
+                short_version = variables['openshift']['common']['short_version']
+            elif 'openshift_release' in variables:
+                release = variables['openshift_release']
+                if release.startswith('v'):
+                    short_version = release[1:]
+                else:
+                    short_version = release
+            elif 'openshift_version' in variables:
+                version = variables['openshift_version']
+                short_version = '.'.join(version.split('.')[0:2])
+            else:
+                # pylint: disable=line-too-long
+                raise AnsibleError("Either OpenShift needs to be installed or openshift_release needs to be specified")
+
+            if deployment_type not in ['origin', 'openshift-enterprise']:
+                raise AnsibleError("Unknown deployment_type %s" % deployment_type)
+
+            if deployment_type == 'origin':
+                if short_version not in ['1.1', '1.2', '1.3', '1.4']:
+                    raise AnsibleError("Unknown short_version %s" % short_version)
+            elif deployment_type == 'openshift_enterprise':
+                if short_version not in ['3.1', '3.2', '3.3', '3.4']:
+                    raise AnsibleError("Unknown short_version %s" % short_version)
+
+            if deployment_type == 'openshift-enterprise':
+                # convert short_version to origin short_version
+                short_version = re.sub('^3.', '1.', short_version)
+
+            if short_version == '1.4':
+                priorities.append({'name': 'NodePreferAvoidPodsPriority', 'weight': 10000})
+
+            # only 1.1 didn't include NodeAffinityPriority
+            if short_version != '1.1':
+                priorities.append({'name': 'NodeAffinityPriority', 'weight': 1})
+
+            if short_version not in ['1.1', '1.2']:
+                priorities.append({'name': 'TaintTolerationPriority', 'weight': 1})
+
+            if short_version not in ['1.1', '1.2', '1.3']:
+                priorities.append({'name': 'InterPodAffinityPriority', 'weight': 1})
+
+            if zones_enabled:
+                zone_priority = {
+                    'name': 'Zone',
+                    'argument': {
+                        'serviceAntiAffinity': {
+                            'label': 'zone'
+                        }
+                    },
+                    'weight': 2
+                }
+                priorities.append(zone_priority)
+
+            return priorities

+ 35 - 2
roles/openshift_master_facts/tasks/main.yml

@@ -64,8 +64,6 @@
       master_count: "{{ openshift_master_count | default(None) }}"
       controller_lease_ttl: "{{ osm_controller_lease_ttl | default(None) }}"
       master_image: "{{ osm_image | default(None) }}"
-      scheduler_predicates: "{{ openshift_master_scheduler_predicates | default(None) }}"
-      scheduler_priorities: "{{ openshift_master_scheduler_priorities | default(None) }}"
       admission_plugin_config: "{{openshift_master_admission_plugin_config | default(None) }}"
       kube_admission_plugin_config: "{{openshift_master_kube_admission_plugin_config | default(None) }}" # deprecated, merged with admission_plugin_config
       oauth_template: "{{ openshift_master_oauth_template | default(None) }}" # deprecated in origin 1.2 / OSE 3.2
@@ -79,3 +77,38 @@
       audit_config: "{{ openshift_master_audit_config | default(None) }}"
       metrics_public_url: "{% if openshift_hosted_metrics_deploy | default(false) %}https://{{ metrics_hostname }}/hawkular/metrics{% endif %}"
       scheduler_args: "{{ openshift_master_scheduler_args | default(None) }}"
+
+- name: Determine if scheduler config present
+  stat:
+    path: "{{ openshift_master_scheduler_conf }}"
+  register: scheduler_config_stat
+
+- block:
+  - set_fact:
+      openshift_master_scheduler_predicates: "{{ lookup('openshift_master_facts_default_predicates') }}"
+    when: "{{ openshift_master_scheduler_predicates is not defined }}"
+
+  - set_fact:
+      openshift_master_scheduler_priorities: "{{ lookup('openshift_master_facts_default_priorities') }}"
+    when: "{{ openshift_master_scheduler_priorities is not defined }}"
+  when: "{{ not scheduler_config_stat.stat.exists }}"
+
+- block:
+  - name: Retrieve current scheduler config
+    slurp:
+      src: "{{ openshift_master_scheduler_conf }}"
+    register: current_scheduler_config
+
+  - fail:
+      msg: "Could not decode scheduler config"
+    when: "{{ (current_scheduler_config.content | b64decode | from_json).apiVersion | default(none) != 'v1' }}"
+
+  - set_fact:
+      openshift_master_scheduler_predicates: "{{ (current_scheduler_config.content | b64decode | from_json).predicates }}"
+    when: "{{ openshift_master_scheduler_predicates is not defined }}"
+
+  - set_fact:
+      openshift_master_scheduler_priorities: "{{ (current_scheduler_config.content | b64decode | from_json).priorities }}"
+    when: "{{ openshift_master_scheduler_priorities is not defined }}"
+
+  when: "{{ scheduler_config_stat.stat.exists }}"

+ 174 - 0
roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py

@@ -0,0 +1,174 @@
+import copy
+import os
+import sys
+
+from ansible.errors import AnsibleError
+from nose.tools import raises, assert_equal
+
+sys.path = [os.path.abspath(os.path.dirname(__file__) + "/../lookup_plugins/")] + sys.path
+
+from openshift_master_facts_default_predicates import LookupModule  # noqa: E402
+
+DEFAULT_PREDICATES_1_1 = [
+    {'name': 'PodFitsHostPorts'},
+    {'name': 'PodFitsResources'},
+    {'name': 'NoDiskConflict'},
+    {'name': 'MatchNodeSelector'},
+    {'name': 'Hostname'}
+]
+
+DEFAULT_PREDICATES_1_2 = [
+    {'name': 'PodFitsHostPorts'},
+    {'name': 'PodFitsResources'},
+    {'name': 'NoDiskConflict'},
+    {'name': 'NoVolumeZoneConflict'},
+    {'name': 'MatchNodeSelector'},
+    {'name': 'Hostname'},
+    {'name': 'MaxEBSVolumeCount'},
+    {'name': 'MaxGCEPDVolumeCount'}
+]
+
+DEFAULT_PREDICATES_1_3 = [
+    {'name': 'NoDiskConflict'},
+    {'name': 'NoVolumeZoneConflict'},
+    {'name': 'MaxEBSVolumeCount'},
+    {'name': 'MaxGCEPDVolumeCount'},
+    {'name': 'GeneralPredicates'},
+    {'name': 'PodToleratesNodeTaints'},
+    {'name': 'CheckNodeMemoryPressure'}
+]
+
+DEFAULT_PREDICATES_1_4 = [
+    {'name': 'NoDiskConflict'},
+    {'name': 'NoVolumeZoneConflict'},
+    {'name': 'MaxEBSVolumeCount'},
+    {'name': 'MaxGCEPDVolumeCount'},
+    {'name': 'GeneralPredicates'},
+    {'name': 'PodToleratesNodeTaints'},
+    {'name': 'CheckNodeMemoryPressure'},
+    {'name': 'CheckNodeDiskPressure'},
+    {'name': 'MatchInterPodAffinity'}
+]
+
+REGION_PREDICATE = {
+    'name': 'Region',
+    'argument': {
+        'serviceAffinity': {
+            'labels': ['region']
+        }
+    }
+}
+
+
+class TestOpenShiftMasterFactsDefaultPredicates(object):
+    def setUp(self):
+        self.lookup = LookupModule()
+        self.default_facts = {
+            'openshift': {
+                'master': {},
+                'common': {}
+            }
+        }
+
+    @raises(AnsibleError)
+    def test_missing_short_version_and_missing_openshift_release(self):
+        facts = copy.deepcopy(self.default_facts)
+        facts['openshift']['common']['deployment_type'] = 'origin'
+        self.lookup.run(None, variables=facts)
+
+    def check_defaults(self, release, deployment_type, default_predicates,
+                       regions_enabled, short_version):
+        facts = copy.deepcopy(self.default_facts)
+        if short_version:
+            facts['openshift']['common']['short_version'] = release
+        else:
+            facts['openshift_release'] = release
+        facts['openshift']['common']['deployment_type'] = deployment_type
+        results = self.lookup.run(None, variables=facts,
+                                  regions_enabled=regions_enabled)
+        if regions_enabled:
+            assert_equal(results, default_predicates + [REGION_PREDICATE])
+        else:
+            assert_equal(results, default_predicates)
+
+    def test_openshift_release_defaults(self):
+        test_vars = [
+            ('1.1', 'origin', DEFAULT_PREDICATES_1_1),
+            ('3.1', 'openshift-enterprise', DEFAULT_PREDICATES_1_1),
+            ('1.2', 'origin', DEFAULT_PREDICATES_1_2),
+            ('3.2', 'openshift-enterprise', DEFAULT_PREDICATES_1_2),
+            ('1.3', 'origin', DEFAULT_PREDICATES_1_3),
+            ('3.3', 'openshift-enterprise', DEFAULT_PREDICATES_1_3),
+            ('1.4', 'origin', DEFAULT_PREDICATES_1_4),
+            ('3.4', 'openshift-enterprise', DEFAULT_PREDICATES_1_4)
+        ]
+
+        for regions_enabled in (True, False):
+            for release, deployment_type, default_predicates in test_vars:
+                for prepend_v in (True, False):
+                    if prepend_v:
+                        release = 'v' + release
+                yield self.check_defaults, release, deployment_type, default_predicates, regions_enabled, False
+
+    def test_short_version_defaults(self):
+        test_vars = [
+            ('1.1', 'origin', DEFAULT_PREDICATES_1_1),
+            ('3.1', 'openshift-enterprise', DEFAULT_PREDICATES_1_1),
+            ('1.2', 'origin', DEFAULT_PREDICATES_1_2),
+            ('3.2', 'openshift-enterprise', DEFAULT_PREDICATES_1_2),
+            ('1.3', 'origin', DEFAULT_PREDICATES_1_3),
+            ('3.3', 'openshift-enterprise', DEFAULT_PREDICATES_1_3),
+            ('1.4', 'origin', DEFAULT_PREDICATES_1_4),
+            ('3.4', 'openshift-enterprise', DEFAULT_PREDICATES_1_4)
+        ]
+        for regions_enabled in (True, False):
+            for short_version, deployment_type, default_predicates in test_vars:
+                yield self.check_defaults, short_version, deployment_type, default_predicates, regions_enabled, True
+
+    @raises(AnsibleError)
+    def test_unknown_deployment_types(self):
+        facts = copy.deepcopy(self.default_facts)
+        facts['openshift']['common']['short_version'] = '1.1'
+        facts['openshift']['common']['deployment_type'] = 'bogus'
+        self.lookup.run(None, variables=facts)
+
+    @raises(AnsibleError)
+    def test_missing_deployment_type(self):
+        facts = copy.deepcopy(self.default_facts)
+        facts['openshift']['common']['short_version'] = '10.10'
+        self.lookup.run(None, variables=facts)
+
+    @raises(AnsibleError)
+    def testMissingOpenShiftFacts(self):
+        facts = {}
+        self.lookup.run(None, variables=facts)
+
+    @raises(AnsibleError)
+    def testMissingMasterRole(self):
+        facts = {'openshift': {}}
+        self.lookup.run(None, variables=facts)
+
+    def testPreExistingPredicates(self):
+        facts = {
+            'openshift': {
+                'master': {
+                    'scheduler_predicates': [
+                        {'name': 'pred_a'},
+                        {'name': 'pred_b'}
+                    ]
+                }
+            }
+        }
+        result = self.lookup.run(None, variables=facts)
+        assert_equal(result, facts['openshift']['master']['scheduler_predicates'])
+
+    def testDefinedPredicates(self):
+        facts = {
+            'openshift': {'master': {}},
+            'openshift_master_scheduler_predicates': [
+                {'name': 'pred_a'},
+                {'name': 'pred_b'}
+            ]
+        }
+        result = self.lookup.run(None, variables=facts)
+        assert_equal(result, facts['openshift_master_scheduler_predicates'])

+ 164 - 0
roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py

@@ -0,0 +1,164 @@
+import copy
+import os
+import sys
+
+from ansible.errors import AnsibleError
+from nose.tools import raises, assert_equal
+
+sys.path = [os.path.abspath(os.path.dirname(__file__) + "/../lookup_plugins/")] + sys.path
+
+from openshift_master_facts_default_priorities import LookupModule  # noqa: E402
+
+DEFAULT_PRIORITIES_1_1 = [
+    {'name': 'LeastRequestedPriority', 'weight': 1},
+    {'name': 'BalancedResourceAllocation', 'weight': 1},
+    {'name': 'SelectorSpreadPriority', 'weight': 1}
+]
+
+DEFAULT_PRIORITIES_1_2 = [
+    {'name': 'LeastRequestedPriority', 'weight': 1},
+    {'name': 'BalancedResourceAllocation', 'weight': 1},
+    {'name': 'SelectorSpreadPriority', 'weight': 1},
+    {'name': 'NodeAffinityPriority', 'weight': 1}
+]
+
+DEFAULT_PRIORITIES_1_3 = [
+    {'name': 'LeastRequestedPriority', 'weight': 1},
+    {'name': 'BalancedResourceAllocation', 'weight': 1},
+    {'name': 'SelectorSpreadPriority', 'weight': 1},
+    {'name': 'NodeAffinityPriority', 'weight': 1},
+    {'name': 'TaintTolerationPriority', 'weight': 1}
+]
+
+DEFAULT_PRIORITIES_1_4 = [
+    {'name': 'LeastRequestedPriority', 'weight': 1},
+    {'name': 'BalancedResourceAllocation', 'weight': 1},
+    {'name': 'SelectorSpreadPriority', 'weight': 1},
+    {'name': 'NodePreferAvoidPodsPriority', 'weight': 10000},
+    {'name': 'NodeAffinityPriority', 'weight': 1},
+    {'name': 'TaintTolerationPriority', 'weight': 1},
+    {'name': 'InterPodAffinityPriority', 'weight': 1}
+]
+
+ZONE_PRIORITY = {
+    'name': 'Zone',
+    'argument': {
+        'serviceAntiAffinity': {
+            'label': 'zone'
+        }
+    },
+    'weight': 2
+}
+
+
+class TestOpenShiftMasterFactsDefaultPredicates(object):
+    def setUp(self):
+        self.lookup = LookupModule()
+        self.default_facts = {
+            'openshift': {
+                'master': {},
+                'common': {}
+            }
+        }
+
+    @raises(AnsibleError)
+    def test_missing_short_version_and_missing_openshift_release(self):
+        facts = copy.deepcopy(self.default_facts)
+        facts['openshift']['common']['deployment_type'] = 'origin'
+        self.lookup.run(None, variables=facts)
+
+    def check_defaults(self, release, deployment_type, default_priorities,
+                       zones_enabled, short_version):
+        facts = copy.deepcopy(self.default_facts)
+        if short_version:
+            facts['openshift']['common']['short_version'] = release
+        else:
+            facts['openshift_release'] = release
+        facts['openshift']['common']['deployment_type'] = deployment_type
+        results = self.lookup.run(None, variables=facts, zones_enabled=zones_enabled)
+        if zones_enabled:
+            assert_equal(results, default_priorities + [ZONE_PRIORITY])
+        else:
+            assert_equal(results, default_priorities)
+
+    def test_openshift_release_defaults(self):
+        test_vars = [
+            ('1.1', 'origin', DEFAULT_PRIORITIES_1_1),
+            ('3.1', 'openshift-enterprise', DEFAULT_PRIORITIES_1_1),
+            ('1.2', 'origin', DEFAULT_PRIORITIES_1_2),
+            ('3.2', 'openshift-enterprise', DEFAULT_PRIORITIES_1_2),
+            ('1.3', 'origin', DEFAULT_PRIORITIES_1_3),
+            ('3.3', 'openshift-enterprise', DEFAULT_PRIORITIES_1_3),
+            ('1.4', 'origin', DEFAULT_PRIORITIES_1_4),
+            ('3.4', 'openshift-enterprise', DEFAULT_PRIORITIES_1_4)
+        ]
+
+        for zones_enabled in (True, False):
+            for release, deployment_type, default_priorities in test_vars:
+                for prepend_v in (True, False):
+                    if prepend_v:
+                        release = 'v' + release
+                yield self.check_defaults, release, deployment_type, default_priorities, zones_enabled, False
+
+    def test_short_version_defaults(self):
+        test_vars = [
+            ('1.1', 'origin', DEFAULT_PRIORITIES_1_1),
+            ('3.1', 'openshift-enterprise', DEFAULT_PRIORITIES_1_1),
+            ('1.2', 'origin', DEFAULT_PRIORITIES_1_2),
+            ('3.2', 'openshift-enterprise', DEFAULT_PRIORITIES_1_2),
+            ('1.3', 'origin', DEFAULT_PRIORITIES_1_3),
+            ('3.3', 'openshift-enterprise', DEFAULT_PRIORITIES_1_3),
+            ('1.4', 'origin', DEFAULT_PRIORITIES_1_4),
+            ('3.4', 'openshift-enterprise', DEFAULT_PRIORITIES_1_4)
+        ]
+        for zones_enabled in (True, False):
+            for short_version, deployment_type, default_priorities in test_vars:
+                yield self.check_defaults, short_version, deployment_type, default_priorities, zones_enabled, True
+
+    @raises(AnsibleError)
+    def test_unknown_deployment_types(self):
+        facts = copy.deepcopy(self.default_facts)
+        facts['openshift']['common']['short_version'] = '1.1'
+        facts['openshift']['common']['deployment_type'] = 'bogus'
+        self.lookup.run(None, variables=facts)
+
+    @raises(AnsibleError)
+    def test_missing_deployment_type(self):
+        facts = copy.deepcopy(self.default_facts)
+        facts['openshift']['common']['short_version'] = '10.10'
+        self.lookup.run(None, variables=facts)
+
+    @raises(AnsibleError)
+    def test_missing_openshift_facts(self):
+        facts = {}
+        self.lookup.run(None, variables=facts)
+
+    @raises(AnsibleError)
+    def test_missing_master_role(self):
+        facts = {'openshift': {}}
+        self.lookup.run(None, variables=facts)
+
+    def test_pre_existing_priorities(self):
+        facts = {
+            'openshift': {
+                'master': {
+                    'scheduler_priorities': [
+                        {'name': 'pri_a', 'weight': 1},
+                        {'name': 'pri_b', 'weight': 1}
+                    ]
+                }
+            }
+        }
+        result = self.lookup.run(None, variables=facts)
+        assert_equal(result, facts['openshift']['master']['scheduler_priorities'])
+
+    def testDefinedPredicates(self):
+        facts = {
+            'openshift': {'master': {}},
+            'openshift_master_scheduler_priorities': [
+                {'name': 'pri_a', 'weight': 1},
+                {'name': 'pri_b', 'weight': 1}
+            ]
+        }
+        result = self.lookup.run(None, variables=facts)
+        assert_equal(result, facts['openshift_master_scheduler_priorities'])

+ 5 - 0
roles/openshift_master_facts/vars/main.yml

@@ -1,3 +1,8 @@
+---
+openshift_master_config_dir: "{{ openshift.common.config_base }}/master"
+openshift_master_config_file: "{{ openshift_master_config_dir }}/master-config.yaml"
+openshift_master_scheduler_conf: "{{ openshift_master_config_dir }}/scheduler.json"
+
 builddefaults_yaml:
   BuildDefaults:
     configuration: