Add CPUFilter for filter scheduler
Add CPUFilter for filter scheduler Change-Id: I52091780cb5dff8b40666e90fed90f0ae5b18ebf Partially-Implements: blueprint import-nova-filter-scheduler
This commit is contained in:
@@ -64,6 +64,7 @@ Related options:
|
||||
cfg.ListOpt("enabled_filters",
|
||||
default=[
|
||||
"NoopFilter",
|
||||
"CPUFilter"
|
||||
],
|
||||
help="""
|
||||
Filters that the scheduler will use.
|
||||
|
||||
@@ -20,6 +20,7 @@ import random
|
||||
from zun.common import exception
|
||||
from zun.common.i18n import _
|
||||
import zun.conf
|
||||
from zun import objects
|
||||
from zun.scheduler import driver
|
||||
from zun.scheduler import filters
|
||||
|
||||
@@ -42,21 +43,23 @@ class FilterScheduler(driver.Scheduler):
|
||||
def _schedule(self, context, container):
|
||||
"""Picks a host according to filters."""
|
||||
hosts = self.hosts_up(context)
|
||||
hosts = self.filter_handler.get_filtered_objects(self.enabled_filters,
|
||||
hosts,
|
||||
nodes = objects.ComputeNode.list(context)
|
||||
nodes = [node for node in nodes if node.hostname in hosts]
|
||||
nodes = self.filter_handler.get_filtered_objects(self.enabled_filters,
|
||||
nodes,
|
||||
container)
|
||||
if not hosts:
|
||||
if not nodes:
|
||||
msg = _("Is the appropriate service running?")
|
||||
raise exception.NoValidHost(reason=msg)
|
||||
|
||||
return random.choice(hosts)
|
||||
return random.choice(nodes)
|
||||
|
||||
def select_destinations(self, context, containers):
|
||||
"""Selects destinations by filters."""
|
||||
dests = []
|
||||
for container in containers:
|
||||
host = self._schedule(context, container)
|
||||
host_state = dict(host=host, nodename=None, limits=None)
|
||||
node = self._schedule(context, container)
|
||||
host_state = dict(host=node.hostname, nodename=None, limits=None)
|
||||
dests.append(host_state)
|
||||
|
||||
if len(dests) < 1:
|
||||
|
||||
38
zun/scheduler/filters/cpu_filter.py
Normal file
38
zun/scheduler/filters/cpu_filter.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# 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 CPUFilter(filters.BaseHostFilter):
|
||||
"""Filter the containers by cpu request"""
|
||||
|
||||
run_filter_once_per_request = True
|
||||
|
||||
def host_passes(self, host_state, container):
|
||||
cpu_free = host_state.cpus - host_state.cpu_used
|
||||
if cpu_free < container.cpu:
|
||||
LOG.debug("%(host_state)s does not have %(container_vcpus).2f "
|
||||
"usable vcpus, it only has %(free_vcpus).2f usable "
|
||||
"vcpus",
|
||||
{'host_state': host_state,
|
||||
'container_vcpus': container.cpu,
|
||||
'free_vcpus': cpu_free})
|
||||
return False
|
||||
return True
|
||||
0
zun/tests/unit/scheduler/filters/__init__.py
Normal file
0
zun/tests/unit/scheduler/filters/__init__.py
Normal file
41
zun/tests/unit/scheduler/filters/test_cpu_filter.py
Normal file
41
zun/tests/unit/scheduler/filters/test_cpu_filter.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# 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 cpu_filter
|
||||
from zun.tests import base
|
||||
|
||||
|
||||
class TestCPUFilter(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCPUFilter, self).setUp()
|
||||
self.context = context.RequestContext('fake_user', 'fake_project')
|
||||
|
||||
def test_cpu_filter_pass(self):
|
||||
self.filt_cls = cpu_filter.CPUFilter()
|
||||
container = objects.Container(self.context)
|
||||
container.cpu = 5.0
|
||||
host = objects.ComputeNode(self.context)
|
||||
host.cpus = 8
|
||||
host.cpu_used = 0.0
|
||||
self.assertTrue(self.filt_cls.host_passes(host, container))
|
||||
|
||||
def test_cpu_filter_fail(self):
|
||||
self.filt_cls = cpu_filter.CPUFilter()
|
||||
container = objects.Container(self.context)
|
||||
container.cpu = 8.0
|
||||
host = objects.ComputeNode(self.context)
|
||||
host.cpus = 5
|
||||
host.cpu_used = 2.0
|
||||
self.assertFalse(self.filt_cls.host_passes(host, container))
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
import mock
|
||||
|
||||
from zun.common import context
|
||||
from zun.common import exception
|
||||
from zun import objects
|
||||
from zun.scheduler import filter_scheduler
|
||||
@@ -33,27 +34,46 @@ class FilterSchedulerTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FilterSchedulerTestCase, self).setUp()
|
||||
self.context = context.RequestContext('fake_user', 'fake_project')
|
||||
self.driver = self.driver_cls()
|
||||
|
||||
@mock.patch.object(objects.ComputeNode, 'list')
|
||||
@mock.patch.object(objects.ZunService, 'list_by_binary')
|
||||
@mock.patch('random.choice')
|
||||
def test_select_destinations(self, mock_random_choice,
|
||||
mock_list_by_binary):
|
||||
mock_list_by_binary, mock_compute_list):
|
||||
all_services = [FakeService('service1', 'host1'),
|
||||
FakeService('service2', 'host2'),
|
||||
FakeService('service3', 'host3'),
|
||||
FakeService('service4', 'host4')]
|
||||
all_hosts = ['host1', 'host2', 'host3', 'host4']
|
||||
|
||||
def _return_services(*args, **kwargs):
|
||||
return all_services
|
||||
|
||||
mock_random_choice.side_effect = ['host3']
|
||||
self.driver.servicegroup_api.service_is_up = mock.Mock(
|
||||
return_value=True)
|
||||
mock_list_by_binary.side_effect = _return_services
|
||||
test_container = utils.get_test_container()
|
||||
containers = [objects.Container(self.context, **test_container)]
|
||||
node1 = objects.ComputeNode(self.context)
|
||||
node1.cpus = 48
|
||||
node1.cpu_used = 0.0
|
||||
node1.hostname = 'host1'
|
||||
node2 = objects.ComputeNode(self.context)
|
||||
node2.cpus = 48
|
||||
node2.cpu_used = 0.0
|
||||
node2.hostname = 'host2'
|
||||
node3 = objects.ComputeNode(self.context)
|
||||
node3.cpus = 48
|
||||
node3.cpu_used = 0.0
|
||||
node3.hostname = 'host3'
|
||||
node4 = objects.ComputeNode(self.context)
|
||||
node4.cpus = 48
|
||||
node4.cpu_used = 0.0
|
||||
node4.hostname = 'host4'
|
||||
nodes = [node1, node2, node3, node4]
|
||||
mock_compute_list.return_value = nodes
|
||||
mock_random_choice.side_effect = [node3]
|
||||
dests = self.driver.select_destinations(self.context, containers)
|
||||
|
||||
self.assertEqual(1, len(dests))
|
||||
@@ -61,13 +81,15 @@ class FilterSchedulerTestCase(base.TestCase):
|
||||
self.assertEqual('host3', host)
|
||||
self.assertIsNone(node)
|
||||
|
||||
calls = [mock.call(all_hosts)]
|
||||
calls = [mock.call(nodes)]
|
||||
self.assertEqual(calls, mock_random_choice.call_args_list)
|
||||
|
||||
@mock.patch.object(objects.ComputeNode, 'list')
|
||||
@mock.patch.object(objects.ZunService, 'list_by_binary')
|
||||
@mock.patch('random.choice')
|
||||
def test_select_destinations_no_valid_host(self, mock_random_choice,
|
||||
mock_list_by_binary):
|
||||
mock_list_by_binary,
|
||||
mock_compute_list):
|
||||
|
||||
def _return_services(*args, **kwargs):
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user