anchor.diff:  Three topological levels defined as city (affinity) --> building (anti-affinity) --> room (anti-affinity)
   ----
   {
  -	"predicates" : [
  +	"kind" : "Policy",
  +	"version" : "v1",
  +	"predicates" : [
  +		...
   		{"name" : "CityAffinity", "argument" : {"serviceAffinity" : {"labels" : ["city"]}}}
   	],
   	"priorities" : [
  +		...
   		{"name" : "BuildingSpread", "weight" : 1, "argument" : {"serviceAntiAffinity" : {"label" : "building"}}},
   		{"name" : "RoomSpread", "weight" : 1, "argument" : {"serviceAntiAffinity" : {"label" : "room"}}}
   	]
  
positive.code: Only use nodes with the 'region' label defined and prefer nodes with the 'zone' label defined
  ----
  {
  	"predicates" : [
  		{"name" : "RequireRegion", "argument" : {"labelsPresence" : {"labels" : ["region"], "presence" : true}}},
  
  	],
  	"priorities" : [
  		{"name" : "ZonePreferred", "weight" : 1, "argument" : {"labelPreference" : {"label" : "zone", "presence" : true}}},
  	]
  }
  ----
  
  Combining fixed and configurable predicates and priority functions
  ----
  {
  	"predicates" : [
  		{"name" : "RegionAffinity", "argument" : {"serviceAffinity" : {"labels" : ["region"]}}},
  		{"name" : "RequireRegion", "argument" : {"labelsPresence" : {"labels" : ["region"], "presence" : true}}},
  		{"name" : "BuildingNodesAvoid", "argument" : {"labelsPresence" : {"labels" : ["building"], "presence" : false}}},
  		{"name" : "PodFitsPorts"},
  		{"name" : "MatchNodeSelector}
  	],
  	"priorities" : [
  		{"name" : "ZoneSpread", "weight" : 2, "argument" : {"serviceAntiAffinity" : {"label" : "zone"}}},
  		{"name" : "ZonePreferred", "weight" : 1, "argument" : {"labelPreference" : {"label" : "zone", "presence" : true}}},
  		{"name" : "ServiceSpreadingPriority", "weight" : 1}
  	]
  }
  ----
  
negative.code: The nodes are sorted based on their scores and the node with the highest score is selected to host the pod. If multiple nodes have the same high score, then one of them is selected at random. 
  
  
  == Available Predicates
  There are several predicates provided out of the box in Kubernetes.  Some of these predicates can be customized by providing certain parameters.  Multiple predicates can be combined to provide additional filtering of nodes.
  
  === Static Predicates
  These predicates do not take any configuration parameters or inputs from the user.  These are specified in the scheduler configuration using their exact name.
  
  **_PodFitsPorts_** deems a node to be fit for hosting a pod based on the absence of port conflicts.
  ----
  {"name" : "PodFitsPorts"}
  ----
  **_PodFitsResources_** determines a fit based on resource availability.  The nodes can declare their resource capacities and then pods can specify what resources they require.  Fit is based on requested, rather than used resources.
  ----
  {"name" : "PodFitsResources"}
  ----
  **_NoDiskConflict_** determines fit based on non-conflicting disk volumes.  It evaluates if a pod can fit due to the volumes it requests, and those that are already mounted.
  ----
  {"name" : "NoDiskConflict"}
  ----
  **_MatchNodeSelector_** determines fit based on node selector query that is defined in the pod.
  ----
  {"name" : "MatchNodeSelector"}
  ----
  **_HostName_** determines fit based on the presence of the Host parameter and a string match with the name of the host.
  ----
  {"name" : "HostName"}
  ----
  
  === Configurable Predicates
  These predicates can be configured by the user to tweak their functioning.  They can be given any user-defined name.  The type of the predicate is identified by the argument that they take.  Since these are configurable, multiple predicates of the same type (but different configuration parameters) can be combined as long as their user-defined names are different.
  
  **_ServiceAffinity_** filters out nodes that do not belong to the specified topological level defined by the provided labels.  This predicate takes in a list of labels and ensures affinity within the nodes (that have the same label values) for pods belonging to the same service.  If the pod specifies a value for the labels in its NodeSelector, then the nodes matching those labels are the ones where the pod is scheduled.  If the pod does not specify the labels in its NodeSelector, then the first pod can be placed on any node based on availability and all subsequent pods of the service will be scheduled on nodes that have the same label values.
  ----
  {"name" : "Zone", "argument" : {"serviceAffinity" : {"labels" : ["zone"]}}}
  ----
  **_LabelsPresence_** checks whether a particular node has a certain label defined or not, regardless of its value.
  ----
  {"name" : "ZoneRequired", "argument" : {"serviceAffinity" : {"labels" : ["zone"]}}}
  ----
  
  == Available Priority Functions
  A custom set of priority functionis can be specified to configure the scheduler.  There are several priority functions provided out-of-the-box in Kubernetes.  Some of these priority functions can be customized by providing certain parameters.  Multiple priority functions can be combined and different weights can be given to each in order to impact the prioritization.  A weight is required to be specified and cannot be 0 or negative.
  
  === Static Priority Functions
  These priority functions do not take any configuration parameters or inputs from the user.  These are specified in the scheduler configuration using their exact name as well as the weight.
  
  **_LeastRequestedPriority_** favors nodes with fewer requested resources.  It calculates the percentage of memory and CPU requested by pods scheduled on the node, and prioritizes nodes that have the highest available/remaining capacity.
  ----
  {"name" : "LeastRequestedPriority", "weight" : 1}
  ----
  **_ServiceSpreadingPriority_** spreads pods by minimizing the number of pods belonging to the same service onto the same machine
  ----
  {"name" : "ServiceSpreadingPriority", "weight" : 1}
  ----
  **_EqualPriority_** gives an equal weight of one to all nodes and is not required/recommended outside of testing.
  ----
  {"name" : "EqualPriority", "weight" : 1}
  ----
  
  === Configurable Priority Functions
  These priority functions can be configured by the user by providing certain parameters.  They can be given any user-defined name.  The type of the priority function is identified by the argument that they take.  Since these are configurable, multiple priority functions of the same type (but different configuration parameters) can be combined as long as their user-defined names are different.
  
  **_ServiceAntiAffinity_** takes a label and ensures a good spread of the pods belonging to the same service across the group of nodes based on the label values.  It gives the same score to all nodes that have the same value for the specified label.  It gives a highter score to nodes within a group with the least concentration of pods.
  ----
  {"name" : "RackSpread", "weight" : 1, "argument" : {"serviceAntiAffinity" : {"label" : "rack"}}}
  ----
  **_LabelsPreference_** prefers nodes that have a particular label defined or not, regardless of its value.
  ----
  {"name" : "RackPreferred", "weight" : 1, "argument" : {"labelPreference" : {"label" : "rack"}}}
  ----
  
  
  == Scheduler Policy
  The selection of the predicate and priority functions defines the policy for the scheduler.  Administrators can provide a JSON file that specifies the predicates and priority functions to configure the scheduler.  The path to the scheduler policy file can be specified in the master configuration file.  In the absence of the scheduler policy file, the default configuration gets applied.
  
  It is important to note that the predicates and priority functions defined in the scheduler configuration file will completely override the default scheduler policy.  If any of the default predicates and priiority functions are required, they have to be explicitly specified in the scheduler configuration file.
  
  
  === Default Scheduler Policy
  The default scheduler policy includes the following predicates.
  1. PodFitsPorts
  1. PodFitsResources
  1. NoDiskConflict
  1. MatchNodeSelector
  1. HostName
  
  The default scheduler policy includes the following priority functions.  Each of the priority function has a weight of '1' applied to it.
  1. LeastRequestedPriority
  1. BalancedResourceAllocation
  1. ServiceSpreadingPriority
  
  
  == Use Cases
  One of the important use cases for scheduling within OpenShift is to support flexible affinity and anti-affinity policies.
  
  === Infrafructure Topological Levels
  Administrators can define multiple topological levels for their infrastructure (nodes).  This is done by specifying labels on nodes (eg: region = r1, zone = z1, rack = s1). These label names have no particular meaning and administrators are free to name their infrastructure levels anything (eg, city/building/room).  Also, administrators can define any number of levels for their infrastructure topology, with three levels usually being adequate (eg. regions --> zones --> racks).  Lastly, administrators can specify affinity and anti-affinity rules at each of these levels in any combination.
  
  === Affinity
  Administrators should be able to configure the scheduler to specify affinity at any topological level, or even at multiple levels.  Affinity at a particular level indicates that all pods that belong to the same service will be scheduled onto nodes that belong to the same level.  This handles any latency requirements of applications by allowing administrators to ensure that peer pods do not end up being too geographically separated.  If no node is available within the same affinity group to host the pod, then the pod will not get scheduled.
  
  === Anti Affinity
  Administrators should be able to configure the scheduler to specify anti-affinity at any topological level, or even at multiple levels.  Anti-Affinity (or 'spread') at a particular level indicates that all pods that belong to the same service will be spread across nodes that belong to that level.  This ensures that the application is well spread for high availability purposes.  The scheduler will try to balance the service pods across all applicable nodes as evenly as possible.
  
  
  == Sample Policy Configurations
  The configuration below specifies the default scheduler configuration, if it were to be specified via the scheduler policy file.
  ----
  {
  	"kind" : "Policy",
  	"version" : "v1",
  	"predicates" : [
  		{"name" : "PodFitsPorts"},
  		{"name" : "PodFitsResources"},
  		{"name" : "NoDiskConflict"},
  		{"name" : "MatchNodeSelector"},
  		{"name" : "HostName"}
  	],
  	"priorities" : [
  		{"name" : "LeastRequestedPriority", "weight" : 1},
  		{"name" : "ServiceSpreadingPriority", "weight" : 1}
  	]
  }
  ----
  
  IMPORTANT: In all of the sample configurations below, the list of predicates and priority functions is truncated to include only the ones that pertain to the use case specified.  In practice, a complete/meaningful scheduler policy should include most, if not all, of the default predicates and priority functions listed above.
  
  Three topological levels defined as region (affinity) --> zone (affinity) --> rack (anti-affinity)
  ----
  {
  	"kind" : "Policy",
  	"version" : "v1",
  	"predicates" : [
  		...
  		{"name" : "RegionZoneAffinity", "argument" : {"serviceAffinity" : {"labels" : ["region", "zone"]}}}
  	],
  	"priorities" : [
  		...
  		{"name" : "RackSpread", "weight" : 1, "argument" : {"serviceAntiAffinity" : {"label" : "rack"}}}
  	]
  }
  