
The current repo has a number of python and conf files with executable flag set (+x). This patch set removes that flag from the files. Change-Id: I00a5f92afae22f14b046e32563cba731cdaa47b2 Signed-off-by: Tin Lam <tin@irrational.io>
428 lines
18 KiB
Python
428 lines
18 KiB
Python
#
|
|
# Copyright 2014-2017 AT&T Intellectual Property
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from valet.engine.optimizer.app_manager.app_topology_base import LEVELS
|
|
from valet.engine.optimizer.app_manager.app_topology_base import VGroup
|
|
from valet.engine.optimizer.app_manager.app_topology_base import VM
|
|
from valet.engine.optimizer.ostro.openstack_filters \
|
|
import AggregateInstanceExtraSpecsFilter
|
|
from valet.engine.optimizer.ostro.openstack_filters \
|
|
import AvailabilityZoneFilter
|
|
from valet.engine.optimizer.ostro.openstack_filters import CoreFilter
|
|
from valet.engine.optimizer.ostro.openstack_filters import DiskFilter
|
|
from valet.engine.optimizer.ostro.openstack_filters import RamFilter
|
|
|
|
|
|
class ConstraintSolver(object):
|
|
"""ConstraintSolver."""
|
|
|
|
def __init__(self, _logger):
|
|
"""Initialization."""
|
|
"""Instantiate filters to help enforce constraints."""
|
|
self.logger = _logger
|
|
|
|
self.openstack_AZ = AvailabilityZoneFilter(self.logger)
|
|
self.openstack_AIES = AggregateInstanceExtraSpecsFilter(self.logger)
|
|
self.openstack_R = RamFilter(self.logger)
|
|
self.openstack_C = CoreFilter(self.logger)
|
|
self.openstack_D = DiskFilter(self.logger)
|
|
|
|
self.status = "success"
|
|
|
|
def compute_candidate_list(self, _level, _n, _node_placements,
|
|
_avail_resources, _avail_logical_groups):
|
|
"""Compute candidate list for the given VGroup or VM."""
|
|
candidate_list = []
|
|
|
|
"""When replanning."""
|
|
if _n.node.host is not None and len(_n.node.host) > 0:
|
|
for hk in _n.node.host:
|
|
for ark, ar in _avail_resources.iteritems():
|
|
if hk == ark:
|
|
candidate_list.append(ar)
|
|
else:
|
|
for _, r in _avail_resources.iteritems():
|
|
candidate_list.append(r)
|
|
if len(candidate_list) == 0:
|
|
self.status = "no candidate for node = " + _n.node.name
|
|
self.logger.warn(self.status)
|
|
return candidate_list
|
|
else:
|
|
self.logger.debug("ConstraintSolver: num of candidates = " +
|
|
str(len(candidate_list)))
|
|
|
|
"""Availability zone constraint."""
|
|
if isinstance(_n.node, VGroup) or isinstance(_n.node, VM):
|
|
if (isinstance(_n.node, VM) and _n.node.availability_zone
|
|
is not None) or (isinstance(_n.node, VGroup) and
|
|
len(_n.node.availability_zone_list) > 0):
|
|
self._constrain_availability_zone(_level, _n, candidate_list)
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate availability zone constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
|
|
"""Host aggregate constraint."""
|
|
if isinstance(_n.node, VGroup) or isinstance(_n.node, VM):
|
|
if len(_n.node.extra_specs_list) > 0:
|
|
self._constrain_host_aggregates(_level, _n, candidate_list)
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate host aggregate constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
|
|
"""CPU capacity constraint."""
|
|
if isinstance(_n.node, VGroup) or isinstance(_n.node, VM):
|
|
self._constrain_cpu_capacity(_level, _n, candidate_list)
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate cpu capacity constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
|
|
"""Memory capacity constraint."""
|
|
if isinstance(_n.node, VGroup) or isinstance(_n.node, VM):
|
|
self._constrain_mem_capacity(_level, _n, candidate_list)
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate memory capacity constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
|
|
"""Local disk capacity constraint."""
|
|
if isinstance(_n.node, VGroup) or isinstance(_n.node, VM):
|
|
self._constrain_local_disk_capacity(_level, _n, candidate_list)
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate local disk capacity constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
|
|
""" diversity constraint """
|
|
if len(_n.node.diversity_groups) > 0:
|
|
for _, diversity_id in _n.node.diversity_groups.iteritems():
|
|
if diversity_id.split(":")[0] == _level:
|
|
if diversity_id in _avail_logical_groups.keys():
|
|
self._constrain_diversity_with_others(_level,
|
|
diversity_id,
|
|
candidate_list)
|
|
if len(candidate_list) == 0:
|
|
break
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate diversity constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
else:
|
|
self._constrain_diversity(_level, _n, _node_placements,
|
|
candidate_list)
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate diversity constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
|
|
"""Exclusivity constraint."""
|
|
exclusivities = self.get_exclusivities(_n.node.exclusivity_groups,
|
|
_level)
|
|
if len(exclusivities) > 1:
|
|
self.status = "violate exclusivity constraint (more than one " \
|
|
"exclusivity) for node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return []
|
|
else:
|
|
if len(exclusivities) == 1:
|
|
exclusivity_id = exclusivities[exclusivities.keys()[0]]
|
|
if exclusivity_id.split(":")[0] == _level:
|
|
self._constrain_exclusivity(_level, exclusivity_id,
|
|
candidate_list)
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate exclusivity constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
else:
|
|
self._constrain_non_exclusivity(_level, candidate_list)
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate non-exclusivity constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
|
|
"""Affinity constraint."""
|
|
affinity_id = _n.get_affinity_id() # level:name, except name == "any"
|
|
if affinity_id is not None:
|
|
if affinity_id.split(":")[0] == _level:
|
|
if affinity_id in _avail_logical_groups.keys():
|
|
self._constrain_affinity(_level, affinity_id,
|
|
candidate_list)
|
|
if len(candidate_list) == 0:
|
|
self.status = "violate affinity constraint for " \
|
|
"node = " + _n.node.name
|
|
self.logger.error("ConstraintSolver: " + self.status)
|
|
return candidate_list
|
|
|
|
return candidate_list
|
|
|
|
"""
|
|
Constraint modules.
|
|
"""
|
|
|
|
def _constrain_affinity(self, _level, _affinity_id, _candidate_list):
|
|
conflict_list = []
|
|
|
|
for r in _candidate_list:
|
|
if self.exist_group(_level, _affinity_id, "AFF", r) is False:
|
|
if r not in conflict_list:
|
|
conflict_list.append(r)
|
|
|
|
_candidate_list[:] = [
|
|
c for c in _candidate_list if c not in conflict_list]
|
|
|
|
def _constrain_diversity_with_others(self, _level, _diversity_id,
|
|
_candidate_list):
|
|
conflict_list = []
|
|
|
|
for r in _candidate_list:
|
|
if self.exist_group(_level, _diversity_id, "DIV", r) is True:
|
|
if r not in conflict_list:
|
|
conflict_list.append(r)
|
|
|
|
_candidate_list[:] = [
|
|
c for c in _candidate_list if c not in conflict_list]
|
|
|
|
def exist_group(self, _level, _id, _group_type, _candidate):
|
|
"""Check if group esists."""
|
|
"""Return True if there exists a group within the candidate's
|
|
membership list that matches the provided id and group type.
|
|
"""
|
|
match = False
|
|
|
|
memberships = _candidate.get_memberships(_level)
|
|
for lgk, lgr in memberships.iteritems():
|
|
if lgr.group_type == _group_type and lgk == _id:
|
|
match = True
|
|
break
|
|
|
|
return match
|
|
|
|
def _constrain_diversity(self, _level, _n, _node_placements,
|
|
_candidate_list):
|
|
conflict_list = []
|
|
|
|
for r in _candidate_list:
|
|
if self.conflict_diversity(_level, _n, _node_placements, r):
|
|
if r not in conflict_list:
|
|
conflict_list.append(r)
|
|
|
|
_candidate_list[:] = [
|
|
c for c in _candidate_list if c not in conflict_list]
|
|
|
|
def conflict_diversity(self, _level, _n, _node_placements, _candidate):
|
|
"""Return True if the candidate has a placement conflict."""
|
|
conflict = False
|
|
|
|
for v in _node_placements.keys():
|
|
diversity_level = _n.get_common_diversity(v.diversity_groups)
|
|
if diversity_level != "ANY" and \
|
|
LEVELS.index(diversity_level) >= \
|
|
LEVELS.index(_level):
|
|
if diversity_level == "host":
|
|
if _candidate.cluster_name == \
|
|
_node_placements[v].cluster_name and \
|
|
_candidate.rack_name == \
|
|
_node_placements[v].rack_name and \
|
|
_candidate.host_name == \
|
|
_node_placements[v].host_name:
|
|
conflict = True
|
|
break
|
|
elif diversity_level == "rack":
|
|
if _candidate.cluster_name == \
|
|
_node_placements[v].cluster_name and \
|
|
_candidate.rack_name == _node_placements[v].rack_name:
|
|
conflict = True
|
|
break
|
|
elif diversity_level == "cluster":
|
|
if _candidate.cluster_name == \
|
|
_node_placements[v].cluster_name:
|
|
conflict = True
|
|
break
|
|
|
|
return conflict
|
|
|
|
def _constrain_non_exclusivity(self, _level, _candidate_list):
|
|
conflict_list = []
|
|
|
|
for r in _candidate_list:
|
|
if self.conflict_exclusivity(_level, r) is True:
|
|
if r not in conflict_list:
|
|
conflict_list.append(r)
|
|
|
|
_candidate_list[:] = [
|
|
c for c in _candidate_list if c not in conflict_list]
|
|
|
|
def conflict_exclusivity(self, _level, _candidate):
|
|
"""Check for an exculsivity conflict."""
|
|
"""Check if the candidate contains an exclusivity group within its
|
|
list of memberships."""
|
|
conflict = False
|
|
|
|
memberships = _candidate.get_memberships(_level)
|
|
for mk in memberships.keys():
|
|
if memberships[mk].group_type == "EX" and \
|
|
mk.split(":")[0] == _level:
|
|
conflict = True
|
|
|
|
return conflict
|
|
|
|
def get_exclusivities(self, _exclusivity_groups, _level):
|
|
"""Return a list of filtered exclusivities."""
|
|
"""Extract and return only those exclusivities that exist at the
|
|
specified level.
|
|
"""
|
|
exclusivities = {}
|
|
|
|
for exk, level in _exclusivity_groups.iteritems():
|
|
if level.split(":")[0] == _level:
|
|
exclusivities[exk] = level
|
|
|
|
return exclusivities
|
|
|
|
def _constrain_exclusivity(self, _level, _exclusivity_id, _candidate_list):
|
|
candidate_list = self._get_exclusive_candidates(
|
|
_level, _exclusivity_id, _candidate_list)
|
|
|
|
if len(candidate_list) == 0:
|
|
candidate_list = self._get_hibernated_candidates(_level,
|
|
_candidate_list)
|
|
_candidate_list[:] = [x for x in _candidate_list
|
|
if x in candidate_list]
|
|
else:
|
|
_candidate_list[:] = [x for x in _candidate_list
|
|
if x in candidate_list]
|
|
|
|
def _get_exclusive_candidates(self, _level, _exclusivity_id,
|
|
_candidate_list):
|
|
candidate_list = []
|
|
|
|
for r in _candidate_list:
|
|
if self.exist_group(_level, _exclusivity_id, "EX", r):
|
|
if r not in candidate_list:
|
|
candidate_list.append(r)
|
|
|
|
return candidate_list
|
|
|
|
def _get_hibernated_candidates(self, _level, _candidate_list):
|
|
candidate_list = []
|
|
|
|
for r in _candidate_list:
|
|
if self.check_hibernated(_level, r) is True:
|
|
if r not in candidate_list:
|
|
candidate_list.append(r)
|
|
|
|
return candidate_list
|
|
|
|
def check_hibernated(self, _level, _candidate):
|
|
"""Check if the candidate is hibernated.
|
|
|
|
Return True if the candidate has no placed VMs at the specified
|
|
level.
|
|
"""
|
|
match = False
|
|
|
|
num_of_placed_vms = _candidate.get_num_of_placed_vms(_level)
|
|
if num_of_placed_vms == 0:
|
|
match = True
|
|
|
|
return match
|
|
|
|
def _constrain_host_aggregates(self, _level, _n, _candidate_list):
|
|
conflict_list = []
|
|
|
|
for r in _candidate_list:
|
|
if self.check_host_aggregates(_level, r, _n.node) is False:
|
|
if r not in conflict_list:
|
|
conflict_list.append(r)
|
|
|
|
_candidate_list[:] = [
|
|
c for c in _candidate_list if c not in conflict_list]
|
|
|
|
def check_host_aggregates(self, _level, _candidate, _v):
|
|
"""Check if candidate passes aggregate instance extra specs.
|
|
|
|
Return true if the candidate passes the aggregate instance extra specs
|
|
zone filter.
|
|
"""
|
|
return self.openstack_AIES.host_passes(_level, _candidate, _v)
|
|
|
|
def _constrain_availability_zone(self, _level, _n, _candidate_list):
|
|
conflict_list = []
|
|
|
|
for r in _candidate_list:
|
|
if self.check_availability_zone(_level, r, _n.node) is False:
|
|
if r not in conflict_list:
|
|
conflict_list.append(r)
|
|
|
|
_candidate_list[:] = [
|
|
c for c in _candidate_list if c not in conflict_list]
|
|
|
|
def check_availability_zone(self, _level, _candidate, _v):
|
|
"""Check if the candidate passes the availability zone filter."""
|
|
return self.openstack_AZ.host_passes(_level, _candidate, _v)
|
|
|
|
def _constrain_cpu_capacity(self, _level, _n, _candidate_list):
|
|
conflict_list = []
|
|
|
|
for ch in _candidate_list:
|
|
if self.check_cpu_capacity(_level, _n.node, ch) is False:
|
|
conflict_list.append(ch)
|
|
|
|
_candidate_list[:] = [
|
|
c for c in _candidate_list if c not in conflict_list]
|
|
|
|
def check_cpu_capacity(self, _level, _v, _candidate):
|
|
"""Check if the candidate passes the core filter."""
|
|
return self.openstack_C.host_passes(_level, _candidate, _v)
|
|
|
|
def _constrain_mem_capacity(self, _level, _n, _candidate_list):
|
|
conflict_list = []
|
|
|
|
for ch in _candidate_list:
|
|
if self.check_mem_capacity(_level, _n.node, ch) is False:
|
|
conflict_list.append(ch)
|
|
|
|
_candidate_list[:] = [
|
|
c for c in _candidate_list if c not in conflict_list]
|
|
|
|
def check_mem_capacity(self, _level, _v, _candidate):
|
|
"""Check if the candidate passes the RAM filter."""
|
|
return self.openstack_R.host_passes(_level, _candidate, _v)
|
|
|
|
def _constrain_local_disk_capacity(self, _level, _n, _candidate_list):
|
|
conflict_list = []
|
|
|
|
for ch in _candidate_list:
|
|
if self.check_local_disk_capacity(_level, _n.node, ch) is False:
|
|
conflict_list.append(ch)
|
|
|
|
_candidate_list[:] = [
|
|
c for c in _candidate_list if c not in conflict_list]
|
|
|
|
def check_local_disk_capacity(self, _level, _v, _candidate):
|
|
"""Check if the candidate passes the disk filter."""
|
|
return self.openstack_D.host_passes(_level, _candidate, _v)
|