diff --git a/nova_solverscheduler/scheduler/solvers/constraints/exact_disk_constraint.py b/nova_solverscheduler/scheduler/solvers/constraints/exact_disk_constraint.py new file mode 100644 index 0000000..f7007d2 --- /dev/null +++ b/nova_solverscheduler/scheduler/solvers/constraints/exact_disk_constraint.py @@ -0,0 +1,62 @@ +# Copyright (c) 2015 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_log import log as logging + +from nova.i18n import _LW +from nova_solverscheduler.scheduler.solvers import constraints + +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): + num_hosts = len(hosts) + num_instances = filter_properties.get('num_instances') + + constraint_matrix = [[True for j in xrange(num_instances)] + 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)) + 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) + if requested_disk <= 0: + LOG.warn(_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)]) + else: + constraint_matrix[i] = [False for j in xrange(num_instances)] + LOG.debug("%(host)s does not have exactly %(requested_disk)s " + "MB disk, it has %(usable_disk)s MB disk.", + {'host': hosts[i], + 'requested_disk': requested_disk, + 'usable_disk': hosts[i].free_disk_mb}) + + return constraint_matrix diff --git a/nova_solverscheduler/scheduler/solvers/constraints/exact_ram_constraint.py b/nova_solverscheduler/scheduler/solvers/constraints/exact_ram_constraint.py new file mode 100644 index 0000000..85d2bec --- /dev/null +++ b/nova_solverscheduler/scheduler/solvers/constraints/exact_ram_constraint.py @@ -0,0 +1,58 @@ +# Copyright (c) 2015 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_log import log as logging + +from nova.i18n import _LW +from nova_solverscheduler.scheduler.solvers import constraints + +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): + num_hosts = len(hosts) + num_instances = filter_properties.get('num_instances') + + constraint_matrix = [[True for j in xrange(num_instances)] + 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.")) + if requested_ram <= 0: + LOG.warn(_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)]) + else: + constraint_matrix[i] = [False for j in xrange(num_instances)] + LOG.debug("%(host)s does not have exactly %(requested_ram)s MB" + "RAM, it has %(usable_ram)s MB RAM.", + {'host': hosts[i], + 'requested_ram': requested_ram, + 'usable_ram': hosts[i].free_ram_mb}) + + return constraint_matrix diff --git a/nova_solverscheduler/scheduler/solvers/constraints/exact_vcpu_constraint.py b/nova_solverscheduler/scheduler/solvers/constraints/exact_vcpu_constraint.py new file mode 100644 index 0000000..7e9e3f4 --- /dev/null +++ b/nova_solverscheduler/scheduler/solvers/constraints/exact_vcpu_constraint.py @@ -0,0 +1,66 @@ +# Copyright (c) 2015 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_log import log as logging + +from nova.i18n import _LW +from nova_solverscheduler.scheduler.solvers import constraints + +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): + num_hosts = len(hosts) + num_instances = filter_properties.get('num_instances') + + constraint_matrix = [[True for j in xrange(num_instances)] + for i in xrange(num_hosts)] + + # get requested vcpus + instance_type = filter_properties.get('instance_type') or {} + if not instance_type: + return constraint_matrix + 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.")) + 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]}) + constraint_matrix[i] = [False for j in xrange(num_instances)] + continue + else: + usable_vcpus = hosts[i].vcpus_total - hosts[i].vcpus_used + + if instance_vcpus == usable_vcpus: + constraint_matrix[i] = ( + [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 " + "vcpus, it has %(usable_num)s vcpus.", + {'host': hosts[i], + 'requested_num': instance_vcpus, + 'usable_num': usable_vcpus}) + + return constraint_matrix diff --git a/nova_solverscheduler/tests/scheduler/solvers/constraints/test_exact_disk_constraint.py b/nova_solverscheduler/tests/scheduler/solvers/constraints/test_exact_disk_constraint.py new file mode 100644 index 0000000..175e201 --- /dev/null +++ b/nova_solverscheduler/tests/scheduler/solvers/constraints/test_exact_disk_constraint.py @@ -0,0 +1,71 @@ +# 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 nova import test +from nova_solverscheduler.scheduler.solvers.constraints \ + import exact_disk_constraint +from nova_solverscheduler.tests.scheduler import solver_scheduler_fakes \ + as fakes + + +class TestExactDiskConstraint(test.NoDBTestCase): + + def setUp(self): + super(TestExactDiskConstraint, self).setUp() + self.constraint_cls = exact_disk_constraint.ExactDiskConstraint + + def _gen_fake_hosts(self): + host1 = fakes.FakeSolverSchedulerHostState('host1', 'node1', + {'free_disk_mb': 2560, 'total_usable_disk_gb': 4}) + host2 = fakes.FakeSolverSchedulerHostState('host2', 'node1', + {'free_disk_mb': 10 * 1024, 'total_usable_disk_gb': 12}) + host3 = fakes.FakeSolverSchedulerHostState('host3', 'node1', + {'free_disk_mb': 1 * 1024, 'total_usable_disk_gb': 6}) + hosts = [host1, host2, host3] + return hosts + + def test_get_constraint_matrix(self): + fake_hosts = self._gen_fake_hosts() + fake_filter_properties = { + 'instance_type': {'root_gb': 1, 'ephemeral_gb': 1, 'swap': 512}, + 'num_instances': 2} + expected_cons_mat = [ + [True, False], + [False, False], + [False, False]] + cons_mat = self.constraint_cls().get_constraint_matrix( + fake_hosts, fake_filter_properties) + self.assertEqual(expected_cons_mat, cons_mat) + + def test_get_constraint_matrix_bad_request_info(self): + fake_hosts = self._gen_fake_hosts() + expected_cons_mat = [ + [True, True], + [True, True], + [True, True]] + + fake_filter_properties = { + 'instance_type': {'root_gb': 0, 'ephemeral_gb': 0, 'swap': 0}, + 'num_instances': 2} + cons_mat = self.constraint_cls().get_constraint_matrix( + fake_hosts, fake_filter_properties) + self.assertEqual(expected_cons_mat, cons_mat) + + fake_filter_properties = { + 'instance_type': None, + 'num_instances': 2} + cons_mat = self.constraint_cls().get_constraint_matrix( + fake_hosts, fake_filter_properties) + self.assertEqual(expected_cons_mat, cons_mat) diff --git a/nova_solverscheduler/tests/scheduler/solvers/constraints/test_exact_ram_constraint.py b/nova_solverscheduler/tests/scheduler/solvers/constraints/test_exact_ram_constraint.py new file mode 100644 index 0000000..164a385 --- /dev/null +++ b/nova_solverscheduler/tests/scheduler/solvers/constraints/test_exact_ram_constraint.py @@ -0,0 +1,71 @@ +# Copyright (c) 2015 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 nova import test +from nova_solverscheduler.scheduler.solvers.constraints \ + import exact_ram_constraint +from nova_solverscheduler.tests.scheduler import solver_scheduler_fakes \ + as fakes + + +class TestExactRamConstraint(test.NoDBTestCase): + + def setUp(self): + super(TestExactRamConstraint, self).setUp() + self.constraint_cls = exact_ram_constraint.ExactRamConstraint + + def _gen_fake_hosts(self): + host1 = fakes.FakeSolverSchedulerHostState('host1', 'node1', + {'free_ram_mb': 512, 'total_usable_ram_mb': 1024}) + host2 = fakes.FakeSolverSchedulerHostState('host2', 'node1', + {'free_ram_mb': 1024, 'total_usable_ram_mb': 2048}) + host3 = fakes.FakeSolverSchedulerHostState('host3', 'node1', + {'free_ram_mb': -256, 'total_usable_ram_mb': 512}) + hosts = [host1, host2, host3] + return hosts + + def test_get_constraint_matrix(self): + fake_hosts = self._gen_fake_hosts() + fake_filter_properties = { + 'instance_type': {'memory_mb': 1024}, + 'num_instances': 2} + expected_cons_mat = [ + [False, False], + [True, False], + [False, False]] + cons_mat = self.constraint_cls().get_constraint_matrix( + fake_hosts, fake_filter_properties) + self.assertEqual(expected_cons_mat, cons_mat) + + def test_get_constraint_matrix_bad_request_info(self): + fake_hosts = self._gen_fake_hosts() + expected_cons_mat = [ + [True, True], + [True, True], + [True, True]] + + fake_filter_properties = { + 'instance_type': {'memory_mb': 0}, + 'num_instances': 2} + cons_mat = self.constraint_cls().get_constraint_matrix( + fake_hosts, fake_filter_properties) + self.assertEqual(expected_cons_mat, cons_mat) + + fake_filter_properties = { + 'instance_type': None, + 'num_instances': 2} + cons_mat = self.constraint_cls().get_constraint_matrix( + fake_hosts, fake_filter_properties) + self.assertEqual(expected_cons_mat, cons_mat) diff --git a/nova_solverscheduler/tests/scheduler/solvers/constraints/test_exact_vcpu_constraint.py b/nova_solverscheduler/tests/scheduler/solvers/constraints/test_exact_vcpu_constraint.py new file mode 100644 index 0000000..1f8f51b --- /dev/null +++ b/nova_solverscheduler/tests/scheduler/solvers/constraints/test_exact_vcpu_constraint.py @@ -0,0 +1,71 @@ +# Copyright (c) 2015 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 nova import test +from nova_solverscheduler.scheduler.solvers.constraints \ + import exact_vcpu_constraint +from nova_solverscheduler.tests.scheduler import solver_scheduler_fakes \ + as fakes + + +class TestExactVcpuConstraint(test.NoDBTestCase): + + def setUp(self): + super(TestExactVcpuConstraint, self).setUp() + self.constraint_cls = exact_vcpu_constraint.ExactVcpuConstraint + + def _gen_fake_hosts(self): + host1 = fakes.FakeSolverSchedulerHostState('host1', 'node1', + {'vcpus_total': 4, 'vcpus_used': 2}) + host2 = fakes.FakeSolverSchedulerHostState('host2', 'node1', + {'vcpus_total': 8, 'vcpus_used': 2}) + host3 = fakes.FakeSolverSchedulerHostState('host3', 'node1', {}) + hosts = [host1, host2, host3] + return hosts + + def test_get_constraint_matrix(self): + fake_hosts = self._gen_fake_hosts() + fake_filter_properties = { + 'instance_type': {'vcpus': 2}, + 'num_instances': 2} + expected_cons_mat = [ + [True, False], + [False, False], + [False, False]] + cons_mat = self.constraint_cls().get_constraint_matrix( + fake_hosts, fake_filter_properties) + self.assertEqual(expected_cons_mat, cons_mat) + + def test_get_constraint_matrix_bad_request_info(self): + fake_hosts = self._gen_fake_hosts() + expected_cons_mat = [ + [True, True], + [True, True], + [True, True]] + + fake_filter_properties = { + 'instance_type': {'vcpus': 0}, + 'num_instances': 2} + cons_mat = self.constraint_cls().get_constraint_matrix( + fake_hosts, fake_filter_properties) + self.assertEqual(expected_cons_mat, cons_mat) + + fake_filter_properties = { + 'instance_type': None, + 'instance_uuids': ['fake_uuid_%s' % x for x in range(2)], + 'num_instances': 2} + cons_mat = self.constraint_cls().get_constraint_matrix( + fake_hosts, fake_filter_properties) + self.assertEqual(expected_cons_mat, cons_mat)