Log.warn deprecated in python3, and pep8 errors resolved.
Change-Id: I44c9cbf71b033a2338746c25c50a041aeae4c825
This commit is contained in:
parent
3aae5eda0a
commit
63e2de012e
|
@ -24,13 +24,13 @@ from nova_solverscheduler.scheduler.solvers import costs
|
|||
from nova_solverscheduler import solver_scheduler_exception as exception
|
||||
|
||||
scheduler_solver_opts = [
|
||||
cfg.ListOpt('scheduler_solver_costs',
|
||||
default=['RamCost'],
|
||||
help='Which cost matrices to use in the '
|
||||
'scheduler solver.'),
|
||||
cfg.ListOpt('scheduler_solver_constraints',
|
||||
default=['ActiveHostsConstraint'],
|
||||
help='Which constraints to use in scheduler solver'),
|
||||
cfg.ListOpt('scheduler_solver_costs',
|
||||
default=['RamCost'],
|
||||
help='Which cost matrices to use in the '
|
||||
'scheduler solver.'),
|
||||
cfg.ListOpt('scheduler_solver_constraints',
|
||||
default=['ActiveHostsConstraint'],
|
||||
help='Which constraints to use in scheduler solver'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -38,6 +38,7 @@ CONF.register_opts(scheduler_solver_opts, group='solver_scheduler')
|
|||
|
||||
|
||||
class BaseHostSolver(object):
|
||||
|
||||
"""Base class for host constraint solvers."""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -54,7 +55,7 @@ class BaseHostSolver(object):
|
|||
for cost in expected_costs:
|
||||
if cost in all_cost_names:
|
||||
cost_classes.append(all_cost_classes[
|
||||
all_cost_names.index(cost)])
|
||||
all_cost_names.index(cost)])
|
||||
else:
|
||||
bad_cost_names.append(cost)
|
||||
if bad_cost_names:
|
||||
|
@ -70,17 +71,17 @@ class BaseHostSolver(object):
|
|||
all_constraint_classes = constraint_handler.get_all_classes()
|
||||
all_constraint_names = [c.__name__ for c in all_constraint_classes]
|
||||
expected_constraints = (
|
||||
CONF.solver_scheduler.scheduler_solver_constraints)
|
||||
CONF.solver_scheduler.scheduler_solver_constraints)
|
||||
for constraint in expected_constraints:
|
||||
if constraint in all_constraint_names:
|
||||
constraint_classes.append(all_constraint_classes[
|
||||
all_constraint_names.index(constraint)])
|
||||
all_constraint_names.index(constraint)])
|
||||
else:
|
||||
bad_constraint_names.append(constraint)
|
||||
if bad_constraint_names:
|
||||
msg = ", ".join(bad_constraint_names)
|
||||
raise exception.SchedulerSolverConstraintNotFound(
|
||||
constraint_name=msg)
|
||||
constraint_name=msg)
|
||||
return constraint_classes
|
||||
|
||||
def solve(self, hosts, filter_properties):
|
||||
|
|
|
@ -22,6 +22,7 @@ from nova.scheduler import filters
|
|||
|
||||
|
||||
class BaseConstraint(object):
|
||||
|
||||
"""Base class for constraints."""
|
||||
|
||||
precedence = 0
|
||||
|
@ -32,6 +33,7 @@ class BaseConstraint(object):
|
|||
|
||||
|
||||
class BaseLinearConstraint(BaseConstraint):
|
||||
|
||||
"""Base class of LP constraint."""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -59,6 +61,7 @@ class BaseLinearConstraint(BaseConstraint):
|
|||
|
||||
|
||||
class BaseFilterConstraint(BaseLinearConstraint):
|
||||
|
||||
"""Base class for constraints that correspond to 1-time host filters."""
|
||||
|
||||
# override this in sub classes
|
||||
|
@ -73,11 +76,11 @@ class BaseFilterConstraint(BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
host_passes = self.host_filter.host_passes(
|
||||
hosts[i], filter_properties)
|
||||
hosts[i], filter_properties)
|
||||
if not host_passes:
|
||||
constraint_matrix[i] = [False for j in xrange(num_instances)]
|
||||
|
||||
|
@ -85,6 +88,7 @@ class BaseFilterConstraint(BaseLinearConstraint):
|
|||
|
||||
|
||||
class ConstraintHandler(loadables.BaseLoader):
|
||||
|
||||
def __init__(self):
|
||||
super(ConstraintHandler, self).__init__(BaseConstraint)
|
||||
|
||||
|
|
|
@ -18,5 +18,6 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class ActiveHostsConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Constraint that only allows active hosts to be selected."""
|
||||
host_filter_cls = compute_filter.ComputeFilter
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class SameHostConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Schedule the instance on the same host as another instance in a set
|
||||
of instances.
|
||||
"""
|
||||
|
@ -25,10 +26,12 @@ class SameHostConstraint(constraints.BaseFilterConstraint):
|
|||
|
||||
|
||||
class DifferentHostConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Schedule the instance on a different host from a set of instances."""
|
||||
host_filter_cls = affinity_filter.DifferentHostFilter
|
||||
|
||||
|
||||
class SimpleCidrAffinityConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Schedule the instance on a host with a particular cidr."""
|
||||
host_filter_cls = affinity_filter.SimpleCIDRAffinityFilter
|
||||
|
|
|
@ -27,6 +27,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class AggregateDiskConstraint(disk_constraint.DiskConstraint):
|
||||
|
||||
"""AggregateDiskConstraint with per-aggregate disk subscription flag.
|
||||
|
||||
Fall back to global disk_allocation_ratio if no per-aggregate setting
|
||||
|
@ -35,11 +36,11 @@ class AggregateDiskConstraint(disk_constraint.DiskConstraint):
|
|||
|
||||
def _get_disk_allocation_ratio(self, host_state, filter_properties):
|
||||
aggregate_vals = utils.aggregate_values_from_key(
|
||||
host_state, 'disk_allocation_ratio')
|
||||
host_state, 'disk_allocation_ratio')
|
||||
|
||||
try:
|
||||
ratio = utils.validate_num_values(
|
||||
aggregate_vals, CONF.disk_allocation_ratio, cast_to=float)
|
||||
aggregate_vals, CONF.disk_allocation_ratio, cast_to=float)
|
||||
except ValueError as e:
|
||||
LOG.warning(_LW("Could not decode disk_allocation_ratio: '%s'"), e)
|
||||
ratio = CONF.disk_allocation_ratio
|
||||
|
|
|
@ -18,7 +18,8 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class AggregateImagePropertiesIsolationConstraint(
|
||||
constraints.BaseFilterConstraint):
|
||||
constraints.BaseFilterConstraint):
|
||||
|
||||
"""AggregateImagePropertiesIsolation works with image properties."""
|
||||
host_filter_cls = aggregate_image_properties_isolation.\
|
||||
AggregateImagePropertiesIsolation
|
||||
AggregateImagePropertiesIsolation
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class AggregateInstanceExtraSpecsConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""AggregateInstanceExtraSpecsFilter works with InstanceType records."""
|
||||
host_filter_cls = aggregate_instance_extra_specs.\
|
||||
AggregateInstanceExtraSpecsFilter
|
||||
AggregateInstanceExtraSpecsFilter
|
||||
|
|
|
@ -18,7 +18,8 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class AggregateMultiTenancyIsolationConstraint(
|
||||
constraints.BaseFilterConstraint):
|
||||
constraints.BaseFilterConstraint):
|
||||
|
||||
"""Isolate tenants in specific aggregates."""
|
||||
host_filter_cls = aggregate_multitenancy_isolation.\
|
||||
AggregateMultiTenancyIsolation
|
||||
AggregateMultiTenancyIsolation
|
||||
|
|
|
@ -18,7 +18,7 @@ from oslo_log import log as logging
|
|||
|
||||
from nova.i18n import _LW
|
||||
from nova_solverscheduler.scheduler.solvers.constraints \
|
||||
import num_instances_constraint
|
||||
import num_instances_constraint
|
||||
from nova_solverscheduler.scheduler.solvers import utils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -30,6 +30,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
class AggregateNumInstancesConstraint(
|
||||
num_instances_constraint.NumInstancesConstraint):
|
||||
|
||||
"""AggregateNumInstancesConstraint with per-aggregate max num instances
|
||||
per host.
|
||||
|
||||
|
@ -39,11 +40,11 @@ class AggregateNumInstancesConstraint(
|
|||
|
||||
def _get_max_instances_per_host(self, host_state, filter_properties):
|
||||
aggregate_vals = utils.aggregate_values_from_key(
|
||||
host_state, 'max_instances_per_host')
|
||||
host_state, 'max_instances_per_host')
|
||||
|
||||
try:
|
||||
value = utils.validate_num_values(
|
||||
aggregate_vals, CONF.max_instances_per_host, cast_to=int)
|
||||
aggregate_vals, CONF.max_instances_per_host, cast_to=int)
|
||||
except ValueError as e:
|
||||
LOG.warning(_LW("Could not decode max_instances_per_host: '%s'"),
|
||||
e)
|
||||
|
|
|
@ -27,6 +27,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class AggregateRamConstraint(ram_constraint.RamConstraint):
|
||||
|
||||
"""AggregateRamConstraint with per-aggregate ram subscription flag.
|
||||
|
||||
Fall back to global ram_allocation_ratio if no per-aggregate setting found.
|
||||
|
@ -34,11 +35,11 @@ class AggregateRamConstraint(ram_constraint.RamConstraint):
|
|||
|
||||
def _get_ram_allocation_ratio(self, host_state, filter_properties):
|
||||
aggregate_vals = utils.aggregate_values_from_key(
|
||||
host_state, 'ram_allocation_ratio')
|
||||
host_state, 'ram_allocation_ratio')
|
||||
|
||||
try:
|
||||
ratio = utils.validate_num_values(
|
||||
aggregate_vals, CONF.ram_allocation_ratio, cast_to=float)
|
||||
aggregate_vals, CONF.ram_allocation_ratio, cast_to=float)
|
||||
except ValueError as e:
|
||||
LOG.warning(_LW("Could not decode ram_allocation_ratio: '%s'"), e)
|
||||
ratio = CONF.ram_allocation_ratio
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class AggregateTypeAffinityConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""AggregateTypeAffinityFilter limits instance_type by aggregate
|
||||
|
||||
return True if no instance_type key is set or if the aggregate metadata
|
||||
|
|
|
@ -27,6 +27,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class AggregateVcpuConstraint(vcpu_constraint.VcpuConstraint):
|
||||
|
||||
"""AggregateVcpuConstraint with per-aggregate CPU subscription flag.
|
||||
|
||||
Fall back to global cpu_allocation_ratio if no per-aggregate setting found.
|
||||
|
@ -34,11 +35,11 @@ class AggregateVcpuConstraint(vcpu_constraint.VcpuConstraint):
|
|||
|
||||
def _get_cpu_allocation_ratio(self, host_state, filter_properties):
|
||||
aggregate_vals = utils.aggregate_values_from_key(
|
||||
host_state, 'cpu_allocation_ratio')
|
||||
host_state, 'cpu_allocation_ratio')
|
||||
|
||||
try:
|
||||
ratio = utils.validate_num_values(
|
||||
aggregate_vals, CONF.cpu_allocation_ratio, cast_to=float)
|
||||
aggregate_vals, CONF.cpu_allocation_ratio, cast_to=float)
|
||||
except ValueError as e:
|
||||
LOG.warning(_LW("Could not decode cpu_allocation_ratio: '%s'"), e)
|
||||
ratio = CONF.cpu_allocation_ratio
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class AvailabilityZoneConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Selects Hosts by availability zone.
|
||||
|
||||
Works with aggregate metadata availability zones, using the key
|
||||
|
|
|
@ -18,5 +18,6 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class ComputeCapabilitiesConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Hard-coded to work with InstanceType records."""
|
||||
host_filter_cls = compute_capabilities_filter.ComputeCapabilitiesFilter
|
||||
|
|
|
@ -27,6 +27,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class DiskConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Constraint of the maximum total disk demand acceptable on each host."""
|
||||
|
||||
def _get_disk_allocation_ratio(self, host_state, filter_properties):
|
||||
|
@ -37,26 +38,26 @@ class DiskConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
# get requested disk
|
||||
instance_type = filter_properties.get('instance_type') or {}
|
||||
requested_disk = (1024 * (instance_type.get('root_gb', 0) +
|
||||
instance_type.get('ephemeral_gb', 0)) +
|
||||
instance_type.get('swap', 0))
|
||||
instance_type.get('swap', 0))
|
||||
for inst_type_key in ['root_gb', 'ephemeral_gb', 'swap']:
|
||||
if inst_type_key not in instance_type:
|
||||
LOG.warn(_LW("Disk information of requested instances\' %s "
|
||||
"is incomplete, use 0 as the requested size."),
|
||||
inst_type_key)
|
||||
LOG.warning(_LW("Disk information of requested instances\' %s "
|
||||
"is incomplete, use 0 as the requested size."),
|
||||
inst_type_key)
|
||||
if requested_disk <= 0:
|
||||
LOG.warn(_LW("DiskConstraint is skipped because requested "
|
||||
LOG.warning(_LW("DiskConstraint is skipped because requested "
|
||||
"instance disk size is 0 or invalid."))
|
||||
return constraint_matrix
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
disk_allocation_ratio = self._get_disk_allocation_ratio(
|
||||
hosts[i], filter_properties)
|
||||
hosts[i], filter_properties)
|
||||
# get usable disk
|
||||
free_disk_mb = hosts[i].free_disk_mb
|
||||
total_usable_disk_mb = hosts[i].total_usable_disk_gb * 1024
|
||||
|
@ -68,13 +69,13 @@ class DiskConstraint(constraints.BaseLinearConstraint):
|
|||
if acceptable_num_instances < num_instances:
|
||||
inacceptable_num = (num_instances - acceptable_num_instances)
|
||||
constraint_matrix[i] = (
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
|
||||
LOG.debug("%(host)s can accept %(num)s requested instances "
|
||||
"according to DiskConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
"according to DiskConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
|
||||
disk_gb_limit = disk_mb_limit / 1024
|
||||
hosts[i].limits['disk_gb'] = disk_gb_limit
|
||||
|
|
|
@ -23,6 +23,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class ExactDiskConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Constraint that selects hosts with exact amount of disk space."""
|
||||
|
||||
def get_constraint_matrix(self, hosts, filter_properties):
|
||||
|
@ -30,27 +31,27 @@ class ExactDiskConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
# get requested disk
|
||||
instance_type = filter_properties.get('instance_type') or {}
|
||||
requested_disk = (1024 * (instance_type.get('root_gb', 0) +
|
||||
instance_type.get('ephemeral_gb', 0)) +
|
||||
instance_type.get('swap', 0))
|
||||
instance_type.get('swap', 0))
|
||||
for inst_type_key in ['root_gb', 'ephemeral_gb', 'swap']:
|
||||
if inst_type_key not in instance_type:
|
||||
LOG.warn(_LW("Disk information of requested instances\' %s "
|
||||
"is incomplete, use 0 as the requested size."),
|
||||
inst_type_key)
|
||||
LOG.warning(_LW("Disk information of requested instances\' %s "
|
||||
"is incomplete, use 0 as the requested size."),
|
||||
inst_type_key)
|
||||
if requested_disk <= 0:
|
||||
LOG.warn(_LW("ExactDiskConstraint is skipped because requested "
|
||||
LOG.warning(_LW("ExactDiskConstraint is skipped because requested "
|
||||
"instance disk size is 0 or invalid."))
|
||||
return constraint_matrix
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
if requested_disk == hosts[i].free_disk_mb:
|
||||
constraint_matrix[i] = (
|
||||
[True] + [False for j in xrange(num_instances - 1)])
|
||||
[True] + [False for j in xrange(num_instances - 1)])
|
||||
else:
|
||||
constraint_matrix[i] = [False for j in xrange(num_instances)]
|
||||
LOG.debug("%(host)s does not have exactly %(requested_disk)s "
|
||||
|
|
|
@ -23,6 +23,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class ExactRamConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Constraint that selects hosts with exact amount of RAM available."""
|
||||
|
||||
def get_constraint_matrix(self, hosts, filter_properties):
|
||||
|
@ -30,23 +31,23 @@ class ExactRamConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
# get requested ram
|
||||
instance_type = filter_properties.get('instance_type') or {}
|
||||
requested_ram = instance_type.get('memory_mb', 0)
|
||||
if 'memory_mb' not in instance_type:
|
||||
LOG.warn(_LW("No information about requested instances\' RAM size "
|
||||
"was found, default value (0) is used."))
|
||||
LOG.warning(_LW("No information about requested instances\' RAM size "
|
||||
"was found, default value (0) is used."))
|
||||
if requested_ram <= 0:
|
||||
LOG.warn(_LW("ExactRamConstraint is skipped because requested "
|
||||
LOG.warning(_LW("ExactRamConstraint is skipped because requested "
|
||||
"instance RAM size is 0 or invalid."))
|
||||
return constraint_matrix
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
if requested_ram == hosts[i].free_ram_mb:
|
||||
constraint_matrix[i] = (
|
||||
[True] + [False for j in xrange(num_instances - 1)])
|
||||
[True] + [False for j in xrange(num_instances - 1)])
|
||||
else:
|
||||
constraint_matrix[i] = [False for j in xrange(num_instances)]
|
||||
LOG.debug("%(host)s does not have exactly %(requested_ram)s MB"
|
||||
|
|
|
@ -22,6 +22,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class ExactVcpuConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Constraint that selects hosts with exact number of vCPUs."""
|
||||
|
||||
def get_constraint_matrix(self, hosts, filter_properties):
|
||||
|
@ -29,7 +30,7 @@ class ExactVcpuConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
# get requested vcpus
|
||||
instance_type = filter_properties.get('instance_type') or {}
|
||||
|
@ -38,15 +39,15 @@ class ExactVcpuConstraint(constraints.BaseLinearConstraint):
|
|||
else:
|
||||
instance_vcpus = instance_type['vcpus']
|
||||
if instance_vcpus <= 0:
|
||||
LOG.warn(_LW("ExactVcpuConstraint is skipped because requested "
|
||||
"instance vCPU number is 0 or invalid."))
|
||||
LOG.warning(_LW("ExactVcpuConstraint is skipped because requested "
|
||||
"instance vCPU number is 0 or invalid."))
|
||||
return constraint_matrix
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
# get available vcpus
|
||||
if not hosts[i].vcpus_total:
|
||||
LOG.warn(_LW("vCPUs of %(host)s not set; assuming CPU "
|
||||
"collection broken."), {'host': hosts[i]})
|
||||
LOG.warning(_LW("vCPUs of %(host)s not set; assuming CPU "
|
||||
"collection broken."), {'host': hosts[i]})
|
||||
constraint_matrix[i] = [False for j in xrange(num_instances)]
|
||||
continue
|
||||
else:
|
||||
|
@ -54,7 +55,7 @@ class ExactVcpuConstraint(constraints.BaseLinearConstraint):
|
|||
|
||||
if instance_vcpus == usable_vcpus:
|
||||
constraint_matrix[i] = (
|
||||
[True] + [False for j in xrange(num_instances - 1)])
|
||||
[True] + [False for j in xrange(num_instances - 1)])
|
||||
else:
|
||||
constraint_matrix[i] = [False for j in xrange(num_instances)]
|
||||
LOG.debug("%(host)s does not have exactly %(requested_num)s "
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class ImagePropertiesConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Select compute nodes that satisfy instance image properties.
|
||||
|
||||
The ImagePropertiesConstraint selects compute nodes that satisfy
|
||||
|
|
|
@ -25,6 +25,7 @@ CONF.import_opt('max_io_ops_per_host', 'nova.scheduler.filters.io_ops_filter')
|
|||
|
||||
|
||||
class IoOpsConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""A constraint to ensure only those hosts are selected whose number of
|
||||
concurrent I/O operations are within a set threshold.
|
||||
"""
|
||||
|
@ -36,7 +37,7 @@ class IoOpsConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
num_io_ops = hosts[i].num_io_ops
|
||||
|
@ -47,12 +48,12 @@ class IoOpsConstraint(constraints.BaseLinearConstraint):
|
|||
if acceptable_num_instances < num_instances:
|
||||
inacceptable_num = (num_instances - acceptable_num_instances)
|
||||
constraint_matrix[i] = (
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
|
||||
LOG.debug("%(host)s can accept %(num)s requested instances "
|
||||
"according to IoOpsConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
"according to IoOpsConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
|
||||
return constraint_matrix
|
||||
|
|
|
@ -18,5 +18,6 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class IsolatedHostsConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Keep specified images to selected hosts."""
|
||||
host_filter_cls = isolated_hosts_filter.IsolatedHostsFilter
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class JsonConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Constraint to allow simple JSON-based grammar for
|
||||
selecting hosts.
|
||||
"""
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class MetricsConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""This constraint is used to filter out those hosts which don't have the
|
||||
corresponding metrics.
|
||||
"""
|
||||
|
|
|
@ -17,6 +17,7 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class NoConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""No-op constraint that returns empty linear constraint components."""
|
||||
|
||||
def get_constraint_matrix(self, hosts, filter_properties):
|
||||
|
@ -24,6 +25,6 @@ class NoConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
return constraint_matrix
|
||||
|
|
|
@ -20,12 +20,13 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt("max_instances_per_host",
|
||||
"nova.scheduler.filters.num_instances_filter")
|
||||
"nova.scheduler.filters.num_instances_filter")
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NumInstancesConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Constraint that specifies the maximum number of instances that
|
||||
each host can launch.
|
||||
"""
|
||||
|
@ -38,7 +39,7 @@ class NumInstancesConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
max_instances = self._get_max_instances_per_host(hosts[i],
|
||||
|
@ -50,12 +51,12 @@ class NumInstancesConstraint(constraints.BaseLinearConstraint):
|
|||
if acceptable_num_instances < num_instances:
|
||||
inacceptable_num = num_instances - acceptable_num_instances
|
||||
constraint_matrix[i] = (
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
|
||||
LOG.debug("%(host)s can accept %(num)s requested instances "
|
||||
"according to NumInstancesConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
"according to NumInstancesConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
|
||||
return constraint_matrix
|
||||
|
|
|
@ -24,6 +24,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class NUMATopologyConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Constraint on requested NUMA topology."""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -47,23 +48,23 @@ class NUMATopologyConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
host_state = copy.deepcopy(hosts[i])
|
||||
acceptable_instance_num = self._get_acceptable_instance_num(
|
||||
host_state, filter_properties, num_instances)
|
||||
host_state, filter_properties, num_instances)
|
||||
|
||||
if acceptable_instance_num < num_instances:
|
||||
inacceptable_num = num_instances - acceptable_instance_num
|
||||
constraint_matrix[i] = (
|
||||
[True for j in xrange(acceptable_instance_num)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
[True for j in xrange(acceptable_instance_num)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
|
||||
LOG.debug("%(host)s can accept %(num)s requested instances "
|
||||
"according to NUMATopologyConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_instance_num})
|
||||
"according to NUMATopologyConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_instance_num})
|
||||
|
||||
numa_topology_limit = host_state.limits.get('numa_topology')
|
||||
if numa_topology_limit:
|
||||
|
|
|
@ -24,6 +24,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class PciPassthroughConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Constraint that schedules instances on a host if the host has devices
|
||||
to meet the device requests in the 'extra_specs' for the flavor.
|
||||
|
||||
|
@ -38,7 +39,7 @@ class PciPassthroughConstraint(constraints.BaseLinearConstraint):
|
|||
"""
|
||||
|
||||
def _get_acceptable_pci_requests_times(self, max_times_to_try,
|
||||
pci_requests, host_pci_stats):
|
||||
pci_requests, host_pci_stats):
|
||||
acceptable_times = 0
|
||||
while acceptable_times < max_times_to_try:
|
||||
if host_pci_stats.support_requests(pci_requests):
|
||||
|
@ -53,29 +54,29 @@ class PciPassthroughConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
pci_requests = filter_properties.get('pci_requests')
|
||||
if not pci_requests:
|
||||
LOG.warn(_LW("PciPassthroughConstraint check is skipped because "
|
||||
LOG.warning(_LW("PciPassthroughConstraint check is skipped because "
|
||||
"requested instance PCI requests is unavailable."))
|
||||
return constraint_matrix
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
host_pci_stats = copy.deepcopy(hosts[i].pci_stats)
|
||||
acceptable_num_instances = (
|
||||
self._get_acceptable_pci_requests_times(num_instances,
|
||||
pci_requests, host_pci_stats))
|
||||
self._get_acceptable_pci_requests_times(num_instances,
|
||||
pci_requests, host_pci_stats))
|
||||
|
||||
if acceptable_num_instances < num_instances:
|
||||
inacceptable_num = num_instances - acceptable_num_instances
|
||||
constraint_matrix[i] = (
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
|
||||
LOG.debug("%(host)s can accept %(num)s requested instances "
|
||||
"according to PciPassthroughConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
"according to PciPassthroughConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
|
||||
return constraint_matrix
|
||||
|
|
|
@ -25,6 +25,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class SameRackConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Place instances in the same racks as those of specified instances.
|
||||
If the specified instances are in hosts without rack config, then place
|
||||
instances in the same hosts as those of specified instances.
|
||||
|
@ -35,7 +36,7 @@ class SameRackConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
scheduler_hints = filter_properties.get('scheduler_hints') or {}
|
||||
affinity_uuids = scheduler_hints.get('same_rack', [])
|
||||
|
@ -63,19 +64,20 @@ class SameRackConstraint(constraints.BaseLinearConstraint):
|
|||
host_racks = host_racks_map.get(host_name, set([]))
|
||||
if host_name in affinity_hosts:
|
||||
LOG.debug(_("%(host)s passed same-rack check."),
|
||||
{'host': host_name})
|
||||
{'host': host_name})
|
||||
continue
|
||||
elif (len(host_racks) == 0) or any([rack not in affinity_racks
|
||||
for rack in host_racks]):
|
||||
constraint_matrix[i] = [False for j in xrange(num_instances)]
|
||||
else:
|
||||
LOG.debug(_("%(host)s passed same-rack check."),
|
||||
{'host': host_name})
|
||||
{'host': host_name})
|
||||
|
||||
return constraint_matrix
|
||||
|
||||
|
||||
class DifferentRackConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Place instances in different racks as those of specified instances.
|
||||
If the specified instances are in hosts without rack config, then place
|
||||
instances in different hosts as those of specified instances.
|
||||
|
@ -86,7 +88,7 @@ class DifferentRackConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
scheduler_hints = filter_properties.get('scheduler_hints') or {}
|
||||
affinity_uuids = scheduler_hints.get('different_rack', [])
|
||||
|
@ -116,6 +118,6 @@ class DifferentRackConstraint(constraints.BaseLinearConstraint):
|
|||
host_name in affinity_hosts):
|
||||
constraint_matrix[i] = [False for j in xrange(num_instances)]
|
||||
LOG.debug(_("%(host)s didnot pass different-rack check."),
|
||||
{'host': host_name})
|
||||
{'host': host_name})
|
||||
|
||||
return constraint_matrix
|
||||
|
|
|
@ -27,6 +27,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class RamConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Constraint of the total ram demand acceptable on each host."""
|
||||
|
||||
def _get_ram_allocation_ratio(self, host_state, filter_properties):
|
||||
|
@ -37,22 +38,22 @@ class RamConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
# get requested ram
|
||||
instance_type = filter_properties.get('instance_type') or {}
|
||||
requested_ram = instance_type.get('memory_mb', 0)
|
||||
if 'memory_mb' not in instance_type:
|
||||
LOG.warn(_LW("No information about requested instances\' RAM size "
|
||||
"was found, default value (0) is used."))
|
||||
LOG.warning(_LW("No information about requested instances\' RAM size "
|
||||
"was found, default value (0) is used."))
|
||||
if requested_ram <= 0:
|
||||
LOG.warn(_LW("RamConstraint is skipped because requested "
|
||||
LOG.warning(_LW("RamConstraint is skipped because requested "
|
||||
"instance RAM size is 0 or invalid."))
|
||||
return constraint_matrix
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
ram_allocation_ratio = self._get_ram_allocation_ratio(
|
||||
hosts[i], filter_properties)
|
||||
hosts[i], filter_properties)
|
||||
# get available ram
|
||||
free_ram_mb = hosts[i].free_ram_mb
|
||||
total_usable_ram_mb = hosts[i].total_usable_ram_mb
|
||||
|
@ -64,13 +65,13 @@ class RamConstraint(constraints.BaseLinearConstraint):
|
|||
if acceptable_num_instances < num_instances:
|
||||
inacceptable_num = num_instances - acceptable_num_instances
|
||||
constraint_matrix[i] = (
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
|
||||
LOG.debug("%(host)s can accept %(num)s requested instances "
|
||||
"according to RamConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
"according to RamConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
|
||||
hosts[i].limits['memory_mb'] = memory_mb_limit
|
||||
|
||||
|
|
|
@ -18,5 +18,6 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class RetryConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Selects nodes that have not been attempted for scheduling purposes."""
|
||||
host_filter_cls = retry_filter.RetryFilter
|
||||
|
|
|
@ -21,6 +21,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class ServerGroupAffinityConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Force to select hosts which host given server group."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -32,7 +33,7 @@ class ServerGroupAffinityConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
policies = filter_properties.get('group_policies', [])
|
||||
if self.policy_name not in policies:
|
||||
|
@ -45,8 +46,8 @@ class ServerGroupAffinityConstraint(constraints.BaseLinearConstraint):
|
|||
|
||||
if not group_hosts:
|
||||
constraint_matrix = [
|
||||
([False for j in xrange(num_instances - 1)] + [True])
|
||||
for i in xrange(num_hosts)]
|
||||
([False for j in xrange(num_instances - 1)] + [True])
|
||||
for i in xrange(num_hosts)]
|
||||
else:
|
||||
for i in xrange(num_hosts):
|
||||
if hosts[i].host not in group_hosts:
|
||||
|
@ -60,11 +61,12 @@ class ServerGroupAffinityConstraint(constraints.BaseLinearConstraint):
|
|||
|
||||
|
||||
class ServerGroupAntiAffinityConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Force to select hosts which host given server group."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ServerGroupAntiAffinityConstraint, self).__init__(
|
||||
*args, **kwargs)
|
||||
*args, **kwargs)
|
||||
self.policy_name = 'anti-affinity'
|
||||
|
||||
def get_constraint_matrix(self, hosts, filter_properties):
|
||||
|
@ -72,7 +74,7 @@ class ServerGroupAntiAffinityConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
policies = filter_properties.get('group_policies', [])
|
||||
if self.policy_name not in policies:
|
||||
|
|
|
@ -21,9 +21,9 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
from nova_solverscheduler.scheduler.solvers import utils as solver_utils
|
||||
|
||||
tenant_rack_opts = [
|
||||
cfg.IntOpt('max_racks_per_tenant',
|
||||
default=1,
|
||||
help='Maximum number of racks each tenant can have.'),
|
||||
cfg.IntOpt('max_racks_per_tenant',
|
||||
default=1,
|
||||
help='Maximum number of racks each tenant can have.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -49,7 +49,7 @@ def _get_sorted_racks(racks, hosts, host_racks_map, filter_properties):
|
|||
return list(racks)
|
||||
if not cost_matrix:
|
||||
cost_matrix = [[0 for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
rack_avail_insts = {}
|
||||
rack_avg_costs = {}
|
||||
|
@ -81,8 +81,8 @@ def _get_sorted_racks(racks, hosts, host_racks_map, filter_properties):
|
|||
cost_matrix[i][0]) / n
|
||||
|
||||
rack_score_tuples = [
|
||||
(rack, rack_avail_insts[rack], rack_avg_costs[rack]) for
|
||||
rack in rack_set]
|
||||
(rack, rack_avail_insts[rack], rack_avg_costs[rack]) for
|
||||
rack in rack_set]
|
||||
|
||||
sorted_rack_tuples = sorted(rack_score_tuples, key=mixed_order)
|
||||
sorted_racks = [rack for (rack, inst, cost) in sorted_rack_tuples]
|
||||
|
@ -91,6 +91,7 @@ def _get_sorted_racks(racks, hosts, host_racks_map, filter_properties):
|
|||
|
||||
|
||||
class TenantRackConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Limit the maximum number of racks that instances of each tenant can
|
||||
spread across.
|
||||
If a host doesnot have rack config, it won't be filtered out by this
|
||||
|
@ -104,7 +105,7 @@ class TenantRackConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
max_racks = CONF.max_racks_per_tenant
|
||||
project_id = filter_properties['project_id']
|
||||
|
@ -132,7 +133,7 @@ class TenantRackConstraint(constraints.BaseLinearConstraint):
|
|||
additional_racks = list(other_racks)
|
||||
else:
|
||||
sorted_other_racks = _get_sorted_racks(
|
||||
other_racks, hosts, host_racks_map, filter_properties)
|
||||
other_racks, hosts, host_racks_map, filter_properties)
|
||||
additional_racks = sorted_other_racks[0:additional_rack_num]
|
||||
|
||||
acceptable_racks = project_racks.union(additional_racks)
|
||||
|
@ -144,11 +145,11 @@ class TenantRackConstraint(constraints.BaseLinearConstraint):
|
|||
constraint_matrix[i] = [False for j in xrange(num_instances)]
|
||||
|
||||
LOG.debug(_("%(host)s cannot accept requested instances "
|
||||
"according to TenantRackConstraint."),
|
||||
{'host': host_name})
|
||||
"according to TenantRackConstraint."),
|
||||
{'host': host_name})
|
||||
else:
|
||||
LOG.debug(_("%(host)s can accept requested instances "
|
||||
"according to TenantRackConstraint."),
|
||||
{'host': host_name})
|
||||
"according to TenantRackConstraint."),
|
||||
{'host': host_name})
|
||||
|
||||
return constraint_matrix
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class TrustedHostsConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""Constraint to add support for Trusted Computing Pools.
|
||||
|
||||
Allows a host to be selected by scheduler only when the integrity (trust)
|
||||
|
|
|
@ -18,5 +18,6 @@ from nova_solverscheduler.scheduler.solvers import constraints
|
|||
|
||||
|
||||
class TypeAffinityConstraint(constraints.BaseFilterConstraint):
|
||||
|
||||
"""TypeAffinityConstraint doesn't allow more then one VM type per host."""
|
||||
host_filter_cls = type_filter.TypeAffinityFilter
|
||||
|
|
|
@ -26,6 +26,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class VcpuConstraint(constraints.BaseLinearConstraint):
|
||||
|
||||
"""Constraint of the total vcpu demand acceptable on each host."""
|
||||
|
||||
def _get_cpu_allocation_ratio(self, host_state, filter_properties):
|
||||
|
@ -36,7 +37,7 @@ class VcpuConstraint(constraints.BaseLinearConstraint):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
# get requested vcpus
|
||||
instance_type = filter_properties.get('instance_type') or {}
|
||||
|
@ -45,16 +46,16 @@ class VcpuConstraint(constraints.BaseLinearConstraint):
|
|||
else:
|
||||
instance_vcpus = instance_type['vcpus']
|
||||
if instance_vcpus <= 0:
|
||||
LOG.warn(_LW("VcpuConstraint is skipped because requested "
|
||||
LOG.warning(_LW("VcpuConstraint is skipped because requested "
|
||||
"instance vCPU number is 0 or invalid."))
|
||||
return constraint_matrix
|
||||
|
||||
for i in xrange(num_hosts):
|
||||
cpu_allocation_ratio = self._get_cpu_allocation_ratio(
|
||||
hosts[i], filter_properties)
|
||||
hosts[i], filter_properties)
|
||||
# get available vcpus
|
||||
if not hosts[i].vcpus_total:
|
||||
LOG.warn(_LW("vCPUs of %(host)s not set; assuming CPU "
|
||||
LOG.warning(_LW("vCPUs of %(host)s not set; assuming CPU "
|
||||
"collection broken."), {'host': hosts[i]})
|
||||
continue
|
||||
else:
|
||||
|
@ -65,13 +66,13 @@ class VcpuConstraint(constraints.BaseLinearConstraint):
|
|||
if acceptable_num_instances < num_instances:
|
||||
inacceptable_num = num_instances - acceptable_num_instances
|
||||
constraint_matrix[i] = (
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
[True for j in xrange(acceptable_num_instances)] +
|
||||
[False for j in xrange(inacceptable_num)])
|
||||
|
||||
LOG.debug("%(host)s can accept %(num)s requested instances "
|
||||
"according to VcpuConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
"according to VcpuConstraint.",
|
||||
{'host': hosts[i],
|
||||
'num': acceptable_num_instances})
|
||||
|
||||
if vcpus_total > 0:
|
||||
hosts[i].limits['vcpu'] = vcpus_total
|
||||
|
|
|
@ -21,6 +21,7 @@ from nova import loadables
|
|||
|
||||
|
||||
class BaseCost(object):
|
||||
|
||||
"""Base class for cost."""
|
||||
|
||||
precedence = 0
|
||||
|
@ -40,6 +41,7 @@ class BaseCost(object):
|
|||
|
||||
|
||||
class BaseLinearCost(BaseCost):
|
||||
|
||||
"""Base class of LP cost."""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -70,6 +72,7 @@ class BaseLinearCost(BaseCost):
|
|||
|
||||
|
||||
class CostHandler(loadables.BaseLoader):
|
||||
|
||||
def __init__(self):
|
||||
super(CostHandler, self).__init__(BaseCost)
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@ from nova_solverscheduler.scheduler.solvers import costs as solver_costs
|
|||
from nova_solverscheduler.scheduler.solvers import utils as solver_utils
|
||||
|
||||
affinity_cost_opts = [
|
||||
cfg.FloatOpt('affinity_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for affinity cost. Must be a '
|
||||
'positive number.'),
|
||||
cfg.FloatOpt('anti_affinity_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for anti-affinity cost. Must be '
|
||||
'a positive number.'),
|
||||
cfg.FloatOpt('affinity_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for affinity cost. Must be a '
|
||||
'positive number.'),
|
||||
cfg.FloatOpt('anti_affinity_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for anti-affinity cost. Must be '
|
||||
'a positive number.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -78,7 +78,7 @@ class AffinityCost(solver_costs.BaseLinearCost):
|
|||
if solver_utils.instance_uuids_overlap(hosts[i],
|
||||
affinity_uuids):
|
||||
extended_cost_matrix[i] = [float(-j) / multiplier for j in
|
||||
xrange(num_instances + 1)]
|
||||
xrange(num_instances + 1)]
|
||||
else:
|
||||
extended_cost_matrix = [[0 for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
@ -125,7 +125,7 @@ class AntiAffinityCost(solver_costs.BaseLinearCost):
|
|||
if solver_utils.instance_uuids_overlap(hosts[i],
|
||||
affinity_uuids):
|
||||
extended_cost_matrix[i] = [1 + (float(j) / multiplier)
|
||||
for j in xrange(num_instances + 1)]
|
||||
for j in xrange(num_instances + 1)]
|
||||
else:
|
||||
extended_cost_matrix = [[0 for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
|
|
@ -28,10 +28,10 @@ from nova_solverscheduler.scheduler.solvers import costs as solver_costs
|
|||
from nova_solverscheduler.scheduler.solvers.costs import utils
|
||||
|
||||
io_ops_cost_opts = [
|
||||
cfg.FloatOpt('io_ops_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for io ops cost. Negative '
|
||||
'numbers mean to stack vs spread.'),
|
||||
cfg.FloatOpt('io_ops_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for io ops cost. Negative '
|
||||
'numbers mean to stack vs spread.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -48,8 +48,8 @@ class IoOpsCost(solver_costs.BaseLinearCost):
|
|||
num_instances = filter_properties.get('num_instances')
|
||||
|
||||
extended_cost_matrix = [
|
||||
[hosts[i].num_io_ops + j for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
[hosts[i].num_io_ops + j for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
extended_cost_matrix = utils.normalize_cost_matrix(
|
||||
extended_cost_matrix)
|
||||
extended_cost_matrix)
|
||||
return extended_cost_matrix
|
||||
|
|
|
@ -33,19 +33,19 @@ from nova_solverscheduler.scheduler.solvers import costs as solver_costs
|
|||
from nova_solverscheduler.scheduler.solvers.costs import utils as cost_utils
|
||||
|
||||
metrics_cost_opts = [
|
||||
cfg.FloatOpt('metrics_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for metrics costs.'),
|
||||
cfg.FloatOpt('metrics_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for metrics costs.'),
|
||||
]
|
||||
|
||||
metrics_weight_opts = [
|
||||
cfg.FloatOpt('weight_multiplier_of_unavailable',
|
||||
default=(-1.0),
|
||||
help='If any one of the metrics set by weight_setting '
|
||||
'is unavailable, the metric weight of the host '
|
||||
'will be set to (minw + (maxw - minw) * m), '
|
||||
'where maxw and minw are the max and min weights '
|
||||
'among all hosts, and m is the multiplier.'),
|
||||
cfg.FloatOpt('weight_multiplier_of_unavailable',
|
||||
default=(-1.0),
|
||||
help='If any one of the metrics set by weight_setting '
|
||||
'is unavailable, the metric weight of the host '
|
||||
'will be set to (minw + (maxw - minw) * m), '
|
||||
'where maxw and minw are the max and min weights '
|
||||
'among all hosts, and m is the multiplier.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -56,6 +56,7 @@ CONF.import_opt('weight_setting', 'nova.scheduler.weights.metrics',
|
|||
|
||||
|
||||
class MetricsCost(solver_costs.BaseLinearCost):
|
||||
|
||||
def __init__(self):
|
||||
self._parse_setting()
|
||||
|
||||
|
@ -90,7 +91,7 @@ class MetricsCost(solver_costs.BaseLinearCost):
|
|||
minval = min(numeric_values)
|
||||
maxval = max(numeric_values)
|
||||
weight_of_unavailable = (minval + (maxval - minval) *
|
||||
CONF.metrics.weight_multiplier_of_unavailable)
|
||||
CONF.metrics.weight_multiplier_of_unavailable)
|
||||
for i in range(num_hosts):
|
||||
if host_weights[i] is None:
|
||||
host_weights[i] = weight_of_unavailable
|
||||
|
@ -101,5 +102,5 @@ class MetricsCost(solver_costs.BaseLinearCost):
|
|||
for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
extended_cost_matrix = cost_utils.normalize_cost_matrix(
|
||||
extended_cost_matrix)
|
||||
extended_cost_matrix)
|
||||
return extended_cost_matrix
|
||||
|
|
|
@ -29,10 +29,10 @@ from nova_solverscheduler.scheduler.solvers import costs as solver_costs
|
|||
from nova_solverscheduler.scheduler.solvers.costs import utils
|
||||
|
||||
ram_cost_opts = [
|
||||
cfg.FloatOpt('ram_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for ram costs. Negative '
|
||||
'numbers mean to stack vs spread.'),
|
||||
cfg.FloatOpt('ram_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for ram costs. Negative '
|
||||
'numbers mean to stack vs spread.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -53,25 +53,25 @@ class RamCost(solver_costs.BaseLinearCost):
|
|||
instance_type = filter_properties.get('instance_type') or {}
|
||||
requested_ram = instance_type.get('memory_mb', 0)
|
||||
if 'memory_mb' not in instance_type:
|
||||
LOG.warn(_LW("No information about requested instances\' RAM size "
|
||||
"was found, default value (0) is used."))
|
||||
LOG.warning(_LW("No information about requested instances\' RAM size "
|
||||
"was found, default value (0) is used."))
|
||||
|
||||
extended_cost_matrix = [[0 for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
|
||||
if requested_ram == 0:
|
||||
extended_cost_matrix = [
|
||||
[(-hosts[i].free_ram_mb)
|
||||
[(-hosts[i].free_ram_mb)
|
||||
for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
else:
|
||||
# we use int approximation here to avoid scaling problems after
|
||||
# normalization, in the case that the free ram in all hosts are
|
||||
# of very small values
|
||||
extended_cost_matrix = [
|
||||
[-int(hosts[i].free_ram_mb / requested_ram) + j
|
||||
[-int(hosts[i].free_ram_mb / requested_ram) + j
|
||||
for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
extended_cost_matrix = utils.normalize_cost_matrix(
|
||||
extended_cost_matrix)
|
||||
extended_cost_matrix)
|
||||
return extended_cost_matrix
|
||||
|
|
|
@ -21,10 +21,10 @@ from nova_solverscheduler.scheduler.solvers import costs as solver_costs
|
|||
from nova_solverscheduler.scheduler.solvers import utils as solver_utils
|
||||
|
||||
affinity_cost_opts = [
|
||||
cfg.FloatOpt('tenant_rack_affinity_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for tenant rack affinity cost. '
|
||||
'Must be a positive number.'),
|
||||
cfg.FloatOpt('tenant_rack_affinity_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for tenant rack affinity cost. '
|
||||
'Must be a positive number.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -34,6 +34,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class TenantRackAffinityCost(solver_costs.BaseLinearCost):
|
||||
|
||||
"""Tenant Rack Affinity Cost tends to let scheduler place instances in
|
||||
the racks that contain existing instances of the tenant.
|
||||
If a rack has existing instances of the same tenant as that making request,
|
||||
|
@ -75,9 +76,9 @@ class TenantRackAffinityCost(solver_costs.BaseLinearCost):
|
|||
if (not any([rack in affinity_racks for rack in host_racks])) and (
|
||||
host_name not in affinity_hosts):
|
||||
extended_cost_matrix[i] = [1 for j
|
||||
in xrange(num_instances + 1)]
|
||||
in xrange(num_instances + 1)]
|
||||
else:
|
||||
LOG.debug(_("%(host)s is in tenant affinity rack."),
|
||||
{'host': host_name})
|
||||
{'host': host_name})
|
||||
|
||||
return extended_cost_matrix
|
||||
|
|
|
@ -29,10 +29,10 @@ from nova_solverscheduler.scheduler.solvers import costs as solver_costs
|
|||
from nova_solverscheduler.scheduler.solvers.costs import utils
|
||||
|
||||
vcpu_cost_opts = [
|
||||
cfg.FloatOpt('vcpu_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for vcpu costs. Negative '
|
||||
'numbers mean to stack vs spread.'),
|
||||
cfg.FloatOpt('vcpu_cost_multiplier',
|
||||
default=1.0,
|
||||
help='Multiplier used for vcpu costs. Negative '
|
||||
'numbers mean to stack vs spread.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -53,15 +53,15 @@ class VcpuCost(solver_costs.BaseLinearCost):
|
|||
instance_type = filter_properties.get('instance_type') or {}
|
||||
requested_vcpus = instance_type.get('vcpus', 0)
|
||||
if requested_vcpus <= 0:
|
||||
LOG.warn(_LW("Requested instances\' vCPU number is 0 or invalid, "
|
||||
"default value (0) is used."))
|
||||
LOG.warning(_LW("Requested instances\' vCPU number is 0 or invalid, "
|
||||
"default value (0) is used."))
|
||||
|
||||
remaining_vcpus_list = []
|
||||
for i in xrange(num_hosts):
|
||||
vcpus_total = hosts[i].vcpus_total
|
||||
vcpus_used = hosts[i].vcpus_used
|
||||
if not vcpus_total:
|
||||
LOG.warn(_LW("vCPUs of %(host)s not set; assuming CPU "
|
||||
LOG.warning(_LW("vCPUs of %(host)s not set; assuming CPU "
|
||||
"collection broken."), {'host': hosts[i]})
|
||||
vcpus_total = 0
|
||||
remaining_vcpus = vcpus_total - vcpus_used
|
||||
|
@ -72,17 +72,17 @@ class VcpuCost(solver_costs.BaseLinearCost):
|
|||
|
||||
if requested_vcpus == 0:
|
||||
extended_cost_matrix = [
|
||||
[(-remaining_vcpus_list[i])
|
||||
[(-remaining_vcpus_list[i])
|
||||
for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
else:
|
||||
# we use int approximation here to avoid scaling problems after
|
||||
# normalization, in the case that the free vcpus in all hosts are
|
||||
# of very small values
|
||||
extended_cost_matrix = [
|
||||
[-int(remaining_vcpus_list[i] / requested_vcpus) + j
|
||||
[-int(remaining_vcpus_list[i] / requested_vcpus) + j
|
||||
for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
extended_cost_matrix = utils.normalize_cost_matrix(
|
||||
extended_cost_matrix)
|
||||
extended_cost_matrix)
|
||||
return extended_cost_matrix
|
||||
|
|
|
@ -31,7 +31,7 @@ class FastSolver(scheduler_solver.BaseHostSolver):
|
|||
solver_cache = filter_properties['solver_cache']
|
||||
# initialize cost matrix
|
||||
cost_matrix = [[0 for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
solver_cache['cost_matrix'] = cost_matrix
|
||||
cost_objects = [cost() for cost in self.cost_classes]
|
||||
cost_objects.sort(key=lambda cost: cost.precedence)
|
||||
|
@ -47,9 +47,9 @@ class FastSolver(scheduler_solver.BaseHostSolver):
|
|||
if not this_cost_mat:
|
||||
continue
|
||||
cost_matrix = [[cost_matrix[i][j] +
|
||||
this_cost_mat[i][j] * cost_multiplier
|
||||
for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
this_cost_mat[i][j] * cost_multiplier
|
||||
for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
# update cost matrix in the solver cache
|
||||
solver_cache['cost_matrix'] = cost_matrix
|
||||
|
||||
|
@ -61,7 +61,7 @@ class FastSolver(scheduler_solver.BaseHostSolver):
|
|||
solver_cache = filter_properties['solver_cache']
|
||||
# initialize constraint_matrix
|
||||
constraint_matrix = [[True for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
solver_cache['constraint_matrix'] = constraint_matrix
|
||||
constraint_objects = [cons() for cons in self.constraint_classes]
|
||||
constraint_objects.sort(key=lambda cons: cons.precedence)
|
||||
|
@ -72,12 +72,12 @@ class FastSolver(scheduler_solver.BaseHostSolver):
|
|||
solver_cache['constraint_matrix'] = constraint_matrix
|
||||
precedence_level = constraint_object.precedence
|
||||
this_cons_mat = constraint_object.get_constraint_matrix(hosts,
|
||||
filter_properties)
|
||||
filter_properties)
|
||||
if not this_cons_mat:
|
||||
continue
|
||||
constraint_matrix = [[constraint_matrix[i][j] &
|
||||
this_cons_mat[i][j] for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
this_cons_mat[i][j] for j in xrange(num_instances)]
|
||||
for i in xrange(num_hosts)]
|
||||
# update constraint matrix in the solver cache
|
||||
solver_cache['constraint_matrix'] = constraint_matrix
|
||||
|
||||
|
@ -94,7 +94,7 @@ class FastSolver(scheduler_solver.BaseHostSolver):
|
|||
|
||||
filter_properties.setdefault('solver_cache', {})
|
||||
filter_properties['solver_cache'].update(
|
||||
{'cost_matrix': [],
|
||||
{'cost_matrix': [],
|
||||
'constraint_matrix': []})
|
||||
|
||||
cost_matrix = self._get_cost_matrix(hosts, filter_properties)
|
||||
|
@ -109,7 +109,7 @@ class FastSolver(scheduler_solver.BaseHostSolver):
|
|||
inst_num = j + 1
|
||||
cost_val = cost_matrix[i][j]
|
||||
placement_cost_tuples.append(
|
||||
(host_idx, inst_num, cost_val))
|
||||
(host_idx, inst_num, cost_val))
|
||||
|
||||
sorted_placement_costs = sorted(placement_cost_tuples,
|
||||
key=operator.itemgetter(2))
|
||||
|
@ -133,6 +133,6 @@ class FastSolver(scheduler_solver.BaseHostSolver):
|
|||
num = host_inst_alloc[i]
|
||||
for n in xrange(num):
|
||||
host_instance_combinations.append(
|
||||
(hosts[i], instances_iter.next()))
|
||||
(hosts[i], instances_iter.next()))
|
||||
|
||||
return host_instance_combinations
|
||||
|
|
|
@ -24,11 +24,11 @@ from nova.i18n import _LW
|
|||
from nova_solverscheduler.scheduler import solvers as scheduler_solver
|
||||
|
||||
pulp_solver_opts = [
|
||||
cfg.IntOpt('pulp_solver_timeout_seconds',
|
||||
default=20,
|
||||
help='How much time in seconds is allowed for solvers to '
|
||||
'solve the scheduling problem. If this time limit '
|
||||
'is exceeded the solver will be stopped.'),
|
||||
cfg.IntOpt('pulp_solver_timeout_seconds',
|
||||
default=20,
|
||||
help='How much time in seconds is allowed for solvers to '
|
||||
'solve the scheduling problem. If this time limit '
|
||||
'is exceeded the solver will be stopped.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -38,6 +38,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class PulpSolver(scheduler_solver.BaseHostSolver):
|
||||
|
||||
"""A LP based pluggable LP solver implemented using PULP modeler."""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -51,7 +52,7 @@ class PulpSolver(scheduler_solver.BaseHostSolver):
|
|||
solver_cache = filter_properties['solver_cache']
|
||||
# initialize cost matrix
|
||||
cost_matrix = [[0 for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
solver_cache['cost_matrix'] = cost_matrix
|
||||
cost_objects = [cost() for cost in self.cost_classes]
|
||||
cost_objects.sort(key=lambda cost: cost.precedence)
|
||||
|
@ -63,13 +64,13 @@ class PulpSolver(scheduler_solver.BaseHostSolver):
|
|||
precedence_level = cost_object.precedence
|
||||
cost_multiplier = cost_object.cost_multiplier()
|
||||
this_cost_mat = cost_object.get_extended_cost_matrix(hosts,
|
||||
filter_properties)
|
||||
filter_properties)
|
||||
if not this_cost_mat:
|
||||
continue
|
||||
cost_matrix = [[cost_matrix[i][j] +
|
||||
this_cost_mat[i][j] * cost_multiplier
|
||||
for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
this_cost_mat[i][j] * cost_multiplier
|
||||
for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
# update cost matrix in the solver cache
|
||||
solver_cache['cost_matrix'] = cost_matrix
|
||||
|
||||
|
@ -81,7 +82,7 @@ class PulpSolver(scheduler_solver.BaseHostSolver):
|
|||
solver_cache = filter_properties['solver_cache']
|
||||
# initialize constraint_matrix
|
||||
constraint_matrix = [[True for j in xrange(num_instances + 1)]
|
||||
for i in xrange(num_hosts)]
|
||||
for i in xrange(num_hosts)]
|
||||
solver_cache['constraint_matrix'] = constraint_matrix
|
||||
constraint_objects = [cons() for cons in self.constraint_classes]
|
||||
constraint_objects.sort(key=lambda cons: cons.precedence)
|
||||
|
@ -92,12 +93,12 @@ class PulpSolver(scheduler_solver.BaseHostSolver):
|
|||
solver_cache['constraint_matrix'] = constraint_matrix
|
||||
precedence_level = constraint_object.precedence
|
||||
this_cons_mat = constraint_object.get_constraint_matrix(hosts,
|
||||
filter_properties)
|
||||
filter_properties)
|
||||
if not this_cons_mat:
|
||||
continue
|
||||
for i in xrange(num_hosts):
|
||||
constraint_matrix[i][1:] = [constraint_matrix[i][j + 1] &
|
||||
this_cons_mat[i][j] for j in xrange(num_instances)]
|
||||
this_cons_mat[i][j] for j in xrange(num_instances)]
|
||||
# update constraint matrix in the solver cache
|
||||
solver_cache['constraint_matrix'] = constraint_matrix
|
||||
|
||||
|
@ -119,7 +120,7 @@ class PulpSolver(scheduler_solver.BaseHostSolver):
|
|||
for i in xrange(len(cost_matrix)):
|
||||
for j in xrange(len(cost_matrix[i])):
|
||||
new_cost_matrix[i][j] = sign * (
|
||||
(cost_matrix[i][j] - offset) ** 2)
|
||||
(cost_matrix[i][j] - offset) ** 2)
|
||||
return new_cost_matrix
|
||||
|
||||
def solve(self, hosts, filter_properties):
|
||||
|
@ -134,11 +135,11 @@ class PulpSolver(scheduler_solver.BaseHostSolver):
|
|||
num_hosts = len(hosts)
|
||||
|
||||
instance_uuids = filter_properties.get('instance_uuids') or [
|
||||
'(unknown_uuid)' + str(i) for i in xrange(num_instances)]
|
||||
'(unknown_uuid)' + str(i) for i in xrange(num_instances)]
|
||||
|
||||
filter_properties.setdefault('solver_cache', {})
|
||||
filter_properties['solver_cache'].update(
|
||||
{'cost_matrix': [],
|
||||
{'cost_matrix': [],
|
||||
'constraint_matrix': []})
|
||||
|
||||
cost_matrix = self._get_cost_matrix(hosts, filter_properties)
|
||||
|
@ -152,49 +153,49 @@ class PulpSolver(scheduler_solver.BaseHostSolver):
|
|||
host_keys = ['Host' + str(i) for i in xrange(num_hosts)]
|
||||
host_key_map = dict(zip(host_keys, hosts))
|
||||
instance_num_keys = ['InstanceNum' + str(i) for
|
||||
i in xrange(num_instances + 1)]
|
||||
i in xrange(num_instances + 1)]
|
||||
instance_num_key_map = dict(zip(instance_num_keys,
|
||||
xrange(num_instances + 1)))
|
||||
|
||||
# create the pulp variables
|
||||
variable_matrix = [
|
||||
[pulp.LpVariable('HI_' + host_key + '_' + instance_num_key,
|
||||
0, 1, constants.LpInteger)
|
||||
[pulp.LpVariable('HI_' + host_key + '_' + instance_num_key,
|
||||
0, 1, constants.LpInteger)
|
||||
for instance_num_key in instance_num_keys]
|
||||
for host_key in host_keys]
|
||||
for host_key in host_keys]
|
||||
|
||||
# create the 'prob' variable to contain the problem data.
|
||||
prob = pulp.LpProblem("Host Instance Scheduler Problem",
|
||||
constants.LpMinimize)
|
||||
constants.LpMinimize)
|
||||
|
||||
# add cost function to pulp solver
|
||||
cost_variables = [variable_matrix[i][j] for i in xrange(num_hosts)
|
||||
for j in xrange(num_instances + 1)]
|
||||
for j in xrange(num_instances + 1)]
|
||||
cost_coefficients = [cost_matrix[i][j] for i in xrange(num_hosts)
|
||||
for j in xrange(num_instances + 1)]
|
||||
for j in xrange(num_instances + 1)]
|
||||
prob += (pulp.lpSum([cost_coefficients[i] * cost_variables[i]
|
||||
for i in xrange(len(cost_variables))]), "Sum_Costs")
|
||||
for i in xrange(len(cost_variables))]), "Sum_Costs")
|
||||
|
||||
# add constraints to pulp solver
|
||||
for i in xrange(num_hosts):
|
||||
for j in xrange(num_instances + 1):
|
||||
if constraint_matrix[i][j] is False:
|
||||
prob += (variable_matrix[i][j] == 0,
|
||||
"Cons_Host_%s" % i + "_NumInst_%s" % j)
|
||||
"Cons_Host_%s" % i + "_NumInst_%s" % j)
|
||||
|
||||
# add additional constraints to ensure the problem is valid
|
||||
# (1) non-trivial solution: number of all instances == that requested
|
||||
prob += (pulp.lpSum([variable_matrix[i][j] * j for i in
|
||||
xrange(num_hosts) for j in xrange(num_instances + 1)]) ==
|
||||
num_instances, "NonTrivialCons")
|
||||
xrange(num_hosts) for j in xrange(num_instances + 1)]) ==
|
||||
num_instances, "NonTrivialCons")
|
||||
# (2) valid solution: each host is assigned 1 num-instances value
|
||||
for i in xrange(num_hosts):
|
||||
prob += (pulp.lpSum([variable_matrix[i][j] for j in
|
||||
xrange(num_instances + 1)]) == 1, "ValidCons_Host_%s" % i)
|
||||
xrange(num_instances + 1)]) == 1, "ValidCons_Host_%s" % i)
|
||||
|
||||
# The problem is solved using PULP's choice of Solver.
|
||||
prob.solve(pulp_solver_classes.PULP_CBC_CMD(
|
||||
maxSeconds=CONF.solver_scheduler.pulp_solver_timeout_seconds))
|
||||
maxSeconds=CONF.solver_scheduler.pulp_solver_timeout_seconds))
|
||||
|
||||
# Create host-instance tuples from the solutions.
|
||||
if pulp.LpStatus[prob.status] == 'Optimal':
|
||||
|
@ -202,19 +203,19 @@ class PulpSolver(scheduler_solver.BaseHostSolver):
|
|||
for v in prob.variables():
|
||||
if v.name.startswith('HI'):
|
||||
(host_key, instance_num_key) = v.name.lstrip('HI').lstrip(
|
||||
'_').split('_')
|
||||
'_').split('_')
|
||||
if v.varValue == 1:
|
||||
num_insts_on_host[host_key] = (
|
||||
instance_num_key_map[instance_num_key])
|
||||
instance_num_key_map[instance_num_key])
|
||||
instances_iter = iter(instance_uuids)
|
||||
for host_key in host_keys:
|
||||
num_insts_on_this_host = num_insts_on_host.get(host_key, 0)
|
||||
for i in xrange(num_insts_on_this_host):
|
||||
host_instance_combinations.append(
|
||||
(host_key_map[host_key], instances_iter.next()))
|
||||
(host_key_map[host_key], instances_iter.next()))
|
||||
else:
|
||||
LOG.warn(_LW("Pulp solver didnot find optimal solution! "
|
||||
"reason: %s"), pulp.LpStatus[prob.status])
|
||||
LOG.warning(_LW("Pulp solver didnot find optimal solution! "
|
||||
"reason: %s"), pulp.LpStatus[prob.status])
|
||||
host_instance_combinations = []
|
||||
|
||||
return host_instance_combinations
|
||||
|
|
|
@ -25,19 +25,19 @@ from nova.scheduler.filters.utils import * # noqa
|
|||
|
||||
rack_config_file_opts = [
|
||||
cfg.StrOpt('rack_config',
|
||||
default='',
|
||||
help='The config file specifying physical rack configuration. By '
|
||||
'default Cisco\'s Neutron ML2 plugin config is supported, '
|
||||
'otherwise the format of the config file needs to be '
|
||||
'compatible with Cisco\'s Neutron ML2 plugin config file.'),
|
||||
default='',
|
||||
help='The config file specifying physical rack configuration. By '
|
||||
'default Cisco\'s Neutron ML2 plugin config is supported, '
|
||||
'otherwise the format of the config file needs to be '
|
||||
'compatible with Cisco\'s Neutron ML2 plugin config file.'),
|
||||
cfg.StrOpt('rack_config_prefix',
|
||||
default='',
|
||||
help='The section name in rack config file should start with the '
|
||||
'prefix, so the config parser can recognize the section as '
|
||||
'information of a specific rack or ToR switch. For example, '
|
||||
'in Cisco\'s Neutron ML2 plugin config a section name like '
|
||||
'[ml2_mech_cisco_nexus:1.1.1.1] identifies a specific ToR '
|
||||
'switch, then the prefix is \'ml2_mech_cisco_nexus\'.')
|
||||
default='',
|
||||
help='The section name in rack config file should start with the '
|
||||
'prefix, so the config parser can recognize the section as '
|
||||
'information of a specific rack or ToR switch. For example, '
|
||||
'in Cisco\'s Neutron ML2 plugin config a section name like '
|
||||
'[ml2_mech_cisco_nexus:1.1.1.1] identifies a specific ToR '
|
||||
'switch, then the prefix is \'ml2_mech_cisco_nexus\'.')
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -102,7 +102,7 @@ def get_host_racks_map(hosts):
|
|||
if host_racks:
|
||||
host_racks_map.setdefault(host_name, set())
|
||||
host_racks_map[host_name] = host_racks_map[host_name].union(
|
||||
host_racks)
|
||||
host_racks)
|
||||
|
||||
if not host_racks_map:
|
||||
host_racks_map = get_host_racks_config()
|
||||
|
|
Loading…
Reference in New Issue