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:
ShunliZhou
2017-05-02 17:09:58 +08:00
parent 1f76d65b77
commit b55985c658
6 changed files with 116 additions and 11 deletions

View File

@@ -64,6 +64,7 @@ Related options:
cfg.ListOpt("enabled_filters",
default=[
"NoopFilter",
"CPUFilter"
],
help="""
Filters that the scheduler will use.

View File

@@ -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:

View 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

View 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))

View File

@@ -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 []