diff --git a/zun/conf/scheduler.py b/zun/conf/scheduler.py index c807576f0..37b533aa4 100644 --- a/zun/conf/scheduler.py +++ b/zun/conf/scheduler.py @@ -66,7 +66,8 @@ Related options: "AvailabilityZoneFilter", "CPUFilter", "RamFilter", - "ComputeFilter" + "ComputeFilter", + "DiskFilter", ], help=""" Filters that the scheduler will use. diff --git a/zun/scheduler/filters/disk_filter.py b/zun/scheduler/filters/disk_filter.py new file mode 100644 index 000000000..98aa96eb1 --- /dev/null +++ b/zun/scheduler/filters/disk_filter.py @@ -0,0 +1,42 @@ +# Copyright (c) 2017 OpenStack Foundation +# 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 zun.scheduler import filters + +LOG = logging.getLogger(__name__) + + +class DiskFilter(filters.BaseHostFilter): + """Filter the containers by disk request""" + + run_filter_once_per_request = True + + def host_passes(self, host_state, container, extra_spec): + if not container.disk: + return True + + usable_disk = host_state.disk_total - host_state.disk_used + if usable_disk < container.disk: + LOG.debug("%(host_state)s does not have %(container_disk)d " + "usable disk, it only has %(usable_disk)d usable " + "disk.", + {'host_state': host_state, + 'container_disk': container.disk, + 'usable_disk': usable_disk}) + return False + host_state.limits['disk'] = host_state.disk_total + return True diff --git a/zun/scheduler/host_state.py b/zun/scheduler/host_state.py index e895743e6..69dff56cd 100644 --- a/zun/scheduler/host_state.py +++ b/zun/scheduler/host_state.py @@ -34,6 +34,8 @@ class HostState(object): self.mem_used = 0 self.cpus = 0 self.cpu_used = 0 + self.disk_total = 0 + self.disk_used = 0 self.numa_topology = None self.labels = None self.pci_stats = None @@ -62,6 +64,8 @@ class HostState(object): self.mem_used = compute_node.mem_used self.cpus = compute_node.cpus self.cpu_used = compute_node.cpu_used + self.disk_total = compute_node.disk_total + self.disk_used = compute_node.disk_used self.numa_topology = compute_node.numa_topology self.labels = compute_node.labels self.pci_stats = pci_stats.PciDeviceStats( diff --git a/zun/tests/unit/scheduler/filters/test_disk_filter.py b/zun/tests/unit/scheduler/filters/test_disk_filter.py new file mode 100644 index 000000000..8db6e3bc5 --- /dev/null +++ b/zun/tests/unit/scheduler/filters/test_disk_filter.py @@ -0,0 +1,45 @@ +# 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 zun.common import context +from zun import objects +from zun.scheduler.filters import disk_filter +from zun.tests import base +from zun.tests.unit.scheduler import fakes + + +class TestDiskFilter(base.TestCase): + + def setUp(self): + super(TestDiskFilter, self).setUp() + self.context = context.RequestContext('fake_user', 'fake_project') + + def test_disk_filter_pass(self): + self.filt_cls = disk_filter.DiskFilter() + container = objects.Container(self.context) + container.disk = 20 + host = fakes.FakeHostState('testhost') + host.disk_total = 80 + host.disk_used = 40 + extra_spec = {} + self.assertTrue(self.filt_cls.host_passes(host, container, extra_spec)) + + def test_disk_filter_fail(self): + self.filt_cls = disk_filter.DiskFilter() + container = objects.Container(self.context) + container.disk = 20 + host = fakes.FakeHostState('testhost') + host.disk_total = 80 + host.disk_used = 70 + extra_spec = {} + self.assertFalse(self.filt_cls.host_passes(host, container, + extra_spec)) diff --git a/zun/tests/unit/scheduler/test_filter_scheduler.py b/zun/tests/unit/scheduler/test_filter_scheduler.py index b2612ee7c..fc19399d8 100644 --- a/zun/tests/unit/scheduler/test_filter_scheduler.py +++ b/zun/tests/unit/scheduler/test_filter_scheduler.py @@ -58,6 +58,8 @@ class FilterSchedulerTestCase(base.TestCase): node1.mem_total = 1024 * 128 node1.mem_used = 1024 * 4 node1.mem_free = 1024 * 124 + node1.disk_total = 80 + node1.disk_used = 20 node1.hostname = 'host1' node1.numa_topology = None node1.labels = {} @@ -68,6 +70,8 @@ class FilterSchedulerTestCase(base.TestCase): node2.mem_total = 1024 * 128 node2.mem_used = 1024 * 4 node2.mem_free = 1024 * 124 + node2.disk_total = 80 + node2.disk_used = 20 node2.hostname = 'host2' node2.numa_topology = None node2.labels = {} @@ -78,6 +82,8 @@ class FilterSchedulerTestCase(base.TestCase): node3.mem_total = 1024 * 128 node3.mem_used = 1024 * 4 node3.mem_free = 1024 * 124 + node3.disk_total = 80 + node3.disk_used = 20 node3.hostname = 'host3' node3.numa_topology = None node3.labels = {} @@ -88,6 +94,8 @@ class FilterSchedulerTestCase(base.TestCase): node4.mem_total = 1024 * 128 node4.mem_used = 1024 * 4 node4.mem_free = 1024 * 124 + node4.disk_total = 80 + node4.disk_used = 20 node4.hostname = 'host4' node4.numa_topology = None node4.labels = {}