Coverage for manila/scheduler/filters/affinity.py: 90%
60 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright (c) 2021 SAP.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16from oslo_log import log
18from manila import exception
19from manila.scheduler.filters import base_host
20from manila.share import api
22LOG = log.getLogger(__name__)
24AFFINITY_FILTER = 'same_host'
25ANTI_AFFINITY_FILTER = 'different_host'
28class AffinityBaseFilter(base_host.BaseHostFilter):
29 """Base class of affinity filters"""
30 _filter_type = None
32 def __init__(self):
33 self.share_api = api.API()
35 def filter_all(self, filter_obj_list, filter_properties):
36 # _filter_type should be defined in subclass
37 if self._filter_type is None: 37 ↛ 38line 37 didn't jump to line 38 because the condition on line 37 was never true
38 raise AffinityFilterTypeNotSetError
40 try:
41 filter_properties = self._validate(filter_properties)
42 except SchedulerHintsNotSet:
43 # AffinityFilter/AntiAffinityFilter is skipped if corresponding
44 # hint is not set. If the "scheduler_hints" is not set, both
45 # filters are skipped.
46 return filter_obj_list
47 except (exception.InvalidUUID,
48 exception.ShareNotFound,
49 exception.ShareInstanceNotFound) as e:
50 # Stop scheduling share when above errors are caught
51 LOG.error('%(filter_name)s: %(error)s', {
52 'filter_name': self.__class__.__name__,
53 'error': e})
54 return None
55 else:
56 # Return list of hosts which pass the function host_passes()
57 # overriden in AffinityFilter and AntiAffinityFilter.
58 return [obj for obj in filter_obj_list
59 if self._filter_one(obj, filter_properties)]
61 def _validate(self, filter_properties):
62 context = filter_properties['context']
63 hints = filter_properties.get('scheduler_hints')
65 if hints is None:
66 raise SchedulerHintsNotSet
67 else:
68 share_uuids = hints.get(self._filter_type)
69 if share_uuids is None:
70 raise SchedulerHintsNotSet
72 share_uuids = share_uuids.split(",")
74 filter_properties['scheduler_hints'][self._filter_type] = []
76 filtered_hosts = []
77 for uuid in share_uuids:
78 try:
79 share = self.share_api.get(context, uuid)
80 except exception.NotFound:
81 raise exception.ShareNotFound(uuid)
82 instances = share.get('instances')
83 if len(instances) == 0: 83 ↛ 84line 83 didn't jump to line 84 because the condition on line 83 was never true
84 raise exception.ShareInstanceNotFound(share_instance_id=uuid)
85 filtered_hosts.append(
86 [instance.get('host') for instance in instances])
88 if self._filter_type == AFFINITY_FILTER:
89 filter_properties['scheduler_hints'][self._filter_type] = list(
90 set.intersection(*map(set, filtered_hosts)))
91 else:
92 filter_properties['scheduler_hints'][self._filter_type] = list(
93 set.union(*map(set, filtered_hosts)))
95 return filter_properties
98class AffinityFilter(AffinityBaseFilter):
99 _filter_type = AFFINITY_FILTER
101 def host_passes(self, host_state, filter_properties):
102 allowed_hosts = \
103 filter_properties['scheduler_hints'][self._filter_type]
104 return host_state.host in allowed_hosts
107class AntiAffinityFilter(AffinityBaseFilter):
108 _filter_type = ANTI_AFFINITY_FILTER
110 def host_passes(self, host_state, filter_properties):
111 forbidden_hosts = \
112 filter_properties['scheduler_hints'][self._filter_type]
113 return host_state.host not in forbidden_hosts
116class SchedulerHintsNotSet(Exception):
117 pass
120class AffinityFilterTypeNotSetError(Exception):
121 pass