From 0be0ac10441c43d27ebd713869a3a8e911fabc6d Mon Sep 17 00:00:00 2001 From: xyhuang Date: Wed, 10 Dec 2014 23:24:40 +0800 Subject: [PATCH] Add host/rack network affinity costs/constraints. Change-Id: I87433442071fcb62654239c33f2f3a84f0abd979 --- .../costs/host_network_affinity_cost.py | 57 ++++++++++++ .../costs/rack_network_affinity_cost.py | 57 ++++++++++++ .../num_networks_per_host_constraint.py | 90 +++++++++++++++++++ .../num_networks_per_rack_constraint.py | 87 ++++++++++++++++++ 4 files changed, 291 insertions(+) create mode 100644 nova/scheduler/solvers/costs/host_network_affinity_cost.py create mode 100644 nova/scheduler/solvers/costs/rack_network_affinity_cost.py create mode 100644 nova/scheduler/solvers/linearconstraints/num_networks_per_host_constraint.py create mode 100644 nova/scheduler/solvers/linearconstraints/num_networks_per_rack_constraint.py diff --git a/nova/scheduler/solvers/costs/host_network_affinity_cost.py b/nova/scheduler/solvers/costs/host_network_affinity_cost.py new file mode 100644 index 0000000..20b18f1 --- /dev/null +++ b/nova/scheduler/solvers/costs/host_network_affinity_cost.py @@ -0,0 +1,57 @@ +# Copyright (c) 2014 Cisco Systems, Inc. +# All Rights Reserved. +# +# 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. + +"""Host network cost.""" + +from nova.openstack.common import log as logging +from nova.scheduler.solvers import costs as solvercosts + +from oslo.config import cfg + +LOG = logging.getLogger(__name__) + +CONF = cfg.CONF + + +class HostNetworkAffinityCost(solvercosts.BaseCost): + """The cost is evaluated by the existence of + requested networks in hosts. + """ + + def get_cost_matrix(self, hosts, instance_uuids, request_spec, + filter_properties): + """Calculate the cost matrix.""" + num_hosts = len(hosts) + if instance_uuids: + num_instances = len(instance_uuids) + else: + num_instances = request_spec.get('num_instances', 1) + + costs = [[0 for j in range(num_instances)] + for i in range(num_hosts)] + + requested_networks = filter_properties.get('requested_networks', None) + if requested_networks is None: + return costs + + for i in range(num_hosts): + host_cost = 0 + for network_id, requested_ip, port_id in requested_networks: + if network_id: + if network_id in hosts[i].networks: + host_cost -= 1 + costs[i] = [host_cost for j in range(num_instances)] + + return costs diff --git a/nova/scheduler/solvers/costs/rack_network_affinity_cost.py b/nova/scheduler/solvers/costs/rack_network_affinity_cost.py new file mode 100644 index 0000000..c8e95ee --- /dev/null +++ b/nova/scheduler/solvers/costs/rack_network_affinity_cost.py @@ -0,0 +1,57 @@ +# Copyright (c) 2014 Cisco Systems, Inc. +# All Rights Reserved. +# +# 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. + +"""Rack network cost.""" + +from nova.openstack.common import log as logging +from nova.scheduler.solvers import costs as solvercosts + +from oslo.config import cfg + +LOG = logging.getLogger(__name__) + +CONF = cfg.CONF + + +class RackNetworkAffinityCost(solvercosts.BaseCost): + """The cost is evaluated by the existence of + requested networks in racks. + """ + + def get_cost_matrix(self, hosts, instance_uuids, request_spec, + filter_properties): + """Calculate the cost matrix.""" + num_hosts = len(hosts) + if instance_uuids: + num_instances = len(instance_uuids) + else: + num_instances = request_spec.get('num_instances', 1) + + costs = [[0 for j in range(num_instances)] + for i in range(num_hosts)] + + requested_networks = filter_properties.get('requested_networks', None) + if requested_networks is None: + return costs + + for i in range(num_hosts): + host_cost = 0 + for network_id, requested_ip, port_id in requested_networks: + if network_id: + if network_id in sum(hosts[i].rack_networks.values(), []): + host_cost -= 1 + costs[i] = [host_cost for j in range(num_instances)] + + return costs diff --git a/nova/scheduler/solvers/linearconstraints/num_networks_per_host_constraint.py b/nova/scheduler/solvers/linearconstraints/num_networks_per_host_constraint.py new file mode 100644 index 0000000..fe5a6ae --- /dev/null +++ b/nova/scheduler/solvers/linearconstraints/num_networks_per_host_constraint.py @@ -0,0 +1,90 @@ +# Copyright (c) 2014 Cisco Systems Inc. +# All Rights Reserved. +# +# 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 oslo.config import cfg + +from nova.openstack.common import log as logging +from nova.scheduler.solvers import linearconstraints + +max_host_networks_opts = [ + cfg.IntOpt('max_networks_per_host', + default=4094, + help='The maximum number of networks allowed in a host') + ] + +CONF = cfg.CONF +CONF.register_opts(max_host_networks_opts) + +LOG = logging.getLogger(__name__) + + +class NumNetworksPerHostConstraint( + linearconstraints.BaseLinearConstraint): + """Constraint that specifies the maximum number of networks that + each host can launch. + """ + + # The linear constraint should be formed as: + # coeff_matrix * var_matrix' (operator) (constants) + # where (operator) is ==, >, >=, <, <=, !=, etc. + # For convenience, the (constants) is merged into left-hand-side, + # thus the right-hand-side is 0. + + def get_coefficient_vectors(self, variables, hosts, instance_uuids, + request_spec, filter_properties): + """Calculate the coeffivient vectors.""" + # The coefficient for each variable is 1 and constant in + # each constraint is -(max_instances_per_host) + usable_network_nums = [self._get_usable_network_num(hosts[i]) + for i in range(self.num_hosts)] + requested_networks = filter_properties.get('requested_networks', None) + num_new_networks = [0 for i in range(self.num_hosts)] + for i in range(self.num_hosts): + for network_id, requested_ip, port_id in requested_networks: + if network_id: + if network_id not in hosts[i].networks: + num_new_networks[i] += 1 + + coefficient_vectors = [ + [num_new_networks[i] - usable_network_nums[i] + for j in range(self.num_instances)] + for i in range(self.num_hosts)] + return coefficient_vectors + + def get_variable_vectors(self, variables, hosts, instance_uuids, + request_spec, filter_properties): + """Reorganize the variables.""" + # The variable_matrix[i,j] denotes the relationship between + # host[i] and instance[j]. + variable_vectors = [] + variable_vectors = [[variables[i][j] for j in range( + self.num_instances)] for i in range(self.num_hosts)] + return variable_vectors + + def get_operations(self, variables, hosts, instance_uuids, request_spec, + filter_properties): + """Set operations for each constraint function.""" + # Operations are '<='. + operations = [(lambda x: x <= 0) for i in range(self.num_hosts)] + return operations + + def _get_usable_network_num(self, host_state): + """This method returns the usable number of network + for the given host. + """ + num_networks = len(host_state.networks) + max_networks_allowed = CONF.max_networks_per_host + usable_network_num = max_networks_allowed - num_networks + return usable_network_num diff --git a/nova/scheduler/solvers/linearconstraints/num_networks_per_rack_constraint.py b/nova/scheduler/solvers/linearconstraints/num_networks_per_rack_constraint.py new file mode 100644 index 0000000..9881c59 --- /dev/null +++ b/nova/scheduler/solvers/linearconstraints/num_networks_per_rack_constraint.py @@ -0,0 +1,87 @@ +# Copyright (c) 2014 Cisco Systems Inc. +# All Rights Reserved. +# +# 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 oslo.config import cfg + +from nova.openstack.common import log as logging +from nova.scheduler.solvers import linearconstraints + +max_rack_networks_opts = [ + cfg.IntOpt('max_networks_per_rack', + default=4094, + help='The maximum number of networks allowed in a rack') + ] + +CONF = cfg.CONF +CONF.register_opts(max_rack_networks_opts) + +LOG = logging.getLogger(__name__) + + +class NumNetworksPerRackConstraint( + linearconstraints.BaseLinearConstraint): + """Constraint that specifies the maximum number of networks that + each rack can launch. + """ + + # The linear constraint should be formed as: + # coeff_matrix * var_matrix' (operator) (constants) + # where (operator) is ==, >, >=, <, <=, !=, etc. + # For convenience, the (constants) is merged into left-hand-side, + # thus the right-hand-side is 0. + + def get_coefficient_vectors(self, variables, hosts, instance_uuids, + request_spec, filter_properties): + """Calculate the coeffivient vectors.""" + # The coefficient for each variable is 1 and constant in + # each constraint is -(max_instances_per_host) + requested_networks = filter_properties.get('requested_networks', None) + max_networks_allowed = CONF.max_networks_per_rack + host_coeffs = [1 for i in range(self.num_hosts)] + for i in range(self.num_hosts): + rack_networks = hosts[i].rack_networks + for rack in rack_networks.keys(): + this_rack_networks = rack_networks[rack] + num_networks = len(this_rack_networks) + num_new_networks = 0 + for network_id, requested_ip, port_id in requested_networks: + if network_id: + if network_id not in this_rack_networks: + num_new_networks += 1 + if num_networks + num_new_networks > max_networks_allowed: + host_coeffs[i] = -1 + break + + coefficient_vectors = [ + [host_coeffs[i] for j in range(self.num_instances)] + for i in range(self.num_hosts)] + return coefficient_vectors + + def get_variable_vectors(self, variables, hosts, instance_uuids, + request_spec, filter_properties): + """Reorganize the variables.""" + # The variable_matrix[i,j] denotes the relationship between + # host[i] and instance[j]. + variable_vectors = [] + variable_vectors = [[variables[i][j] for j in range( + self.num_instances)] for i in range(self.num_hosts)] + return variable_vectors + + def get_operations(self, variables, hosts, instance_uuids, request_spec, + filter_properties): + """Set operations for each constraint function.""" + # Operations are '>='. + operations = [(lambda x: x >= 0) for i in range(self.num_hosts)] + return operations