Coverage for manila/scheduler/utils.py: 97%
56 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) 2014 Hewlett-Packard Development Company, L.P.
2# Copyright (c) 2016 EMC Corporation
3#
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17from oslo_log import log
18from oslo_utils import strutils
20from manila.scheduler.filters import extra_specs_ops
22LOG = log.getLogger(__name__)
25def generate_stats(host_state, properties):
26 """Generates statistics from host and share data."""
28 host_stats = {
29 'host': host_state.host,
30 'share_backend_name': host_state.share_backend_name,
31 'vendor_name': host_state.vendor_name,
32 'driver_version': host_state.driver_version,
33 'storage_protocol': host_state.storage_protocol,
34 'qos': host_state.qos,
35 'total_capacity_gb': host_state.total_capacity_gb,
36 'allocated_capacity_gb': host_state.allocated_capacity_gb,
37 'free_capacity_gb': host_state.free_capacity_gb,
38 'reserved_percentage': host_state.reserved_percentage,
39 'reserved_snapshot_percentage':
40 host_state.reserved_snapshot_percentage,
41 'reserved_share_extend_percentage':
42 host_state.reserved_share_extend_percentage,
43 'driver_handles_share_servers':
44 host_state.driver_handles_share_servers,
45 'thin_provisioning': host_state.thin_provisioning,
46 'updated': host_state.updated,
47 'dedupe': host_state.dedupe,
48 'compression': host_state.compression,
49 'snapshot_support': host_state.snapshot_support,
50 'create_share_from_snapshot_support':
51 host_state.create_share_from_snapshot_support,
52 'revert_to_snapshot_support': host_state.revert_to_snapshot_support,
53 'mount_snapshot_support': host_state.mount_snapshot_support,
54 'replication_domain': host_state.replication_domain,
55 'replication_type': host_state.replication_type,
56 'provisioned_capacity_gb': host_state.provisioned_capacity_gb,
57 'pools': host_state.pools,
58 'max_over_subscription_ratio':
59 host_state.max_over_subscription_ratio,
60 'sg_consistent_snapshot_support': (
61 host_state.sg_consistent_snapshot_support),
62 'ipv4_support': host_state.ipv4_support,
63 'ipv6_support': host_state.ipv6_support,
64 'security_service_update_support': (
65 host_state.security_service_update_support),
66 'network_allocation_update_support': (
67 host_state.network_allocation_update_support),
68 'share_server_multiple_subnet_support': (
69 host_state.share_server_multiple_subnet_support),
70 'mount_point_name_support': (
71 host_state.mount_point_name_support),
72 'share_replicas_migration_support': (
73 host_state.share_replicas_migration_support),
74 'encryption_support': host_state.encryption_support,
75 }
77 host_caps = host_state.capabilities
79 share_type = properties.get('share_type', {})
80 extra_specs = share_type.get('extra_specs', {})
82 share_group_type = properties.get('share_group_type', {})
83 group_specs = share_group_type.get('group_specs', {})
85 request_spec = properties.get('request_spec', {})
86 share_stats = request_spec.get('resource_properties', {})
88 stats = {
89 'host_stats': host_stats,
90 'host_caps': host_caps,
91 'share_type': share_type,
92 'extra_specs': extra_specs,
93 'share_stats': share_stats,
94 'share_group_type': share_group_type,
95 'group_specs': group_specs,
96 }
98 return stats
101def use_thin_logic(share_type):
102 # NOTE(xyang): To preserve the existing behavior, we use thin logic
103 # to evaluate in two cases:
104 # 1) 'thin_provisioning' is not set in extra specs (This is for
105 # backward compatibility. If not set, the scheduler behaves
106 # the same as before this bug fix).
107 # 2) 'thin_provisioning' is set in extra specs and it is
108 # '<is> True' or 'True'.
109 # Otherwise we use the thick logic to evaluate.
110 use_thin_logic = True
111 thin_spec = None
112 try:
113 thin_spec = share_type.get('extra_specs', {}).get(
114 'thin_provisioning')
115 if thin_spec is None:
116 thin_spec = share_type.get('extra_specs', {}).get(
117 'capabilities:thin_provisioning')
118 # NOTE(xyang) 'use_thin_logic' and 'thin_provisioning' are NOT
119 # the same thing. The first purpose of "use_thin_logic" is to
120 # preserve the existing scheduler behavior if 'thin_provisioning'
121 # is NOT in extra_specs (if thin_spec is None, use_thin_logic
122 # should be True). The second purpose of 'use_thin_logic'
123 # is to honor 'thin_provisioning' if it is in extra specs (if
124 # thin_spec is set to True, use_thin_logic should be True; if
125 # thin_spec is set to False, use_thin_logic should be False).
126 use_thin_logic = strutils.bool_from_string(
127 thin_spec, strict=True) if thin_spec is not None else True
128 except ValueError:
129 # Check if the value of thin_spec is '<is> True'.
130 if thin_spec is not None and not extra_specs_ops.match(
131 True, thin_spec):
132 use_thin_logic = False
133 return use_thin_logic
136def thin_provisioning(host_state_thin_provisioning):
137 # NOTE(xyang): host_state_thin_provisioning is reported by driver.
138 # It can be either bool (True or False) or
139 # list ([True, False], [True], [False]).
140 thin_capability = [host_state_thin_provisioning] if not isinstance(
141 host_state_thin_provisioning, list) else host_state_thin_provisioning
142 return True in thin_capability
145def capabilities_satisfied(capabilities, extra_specs):
147 # These extra-specs are not capabilities for matching hosts
148 ignored_extra_specs = (
149 'availability_zones', 'capabilities:availability_zones',
150 )
152 for key, req in extra_specs.items():
153 # Ignore some extra_specs if told to
154 if key in ignored_extra_specs:
155 continue
157 # Either not scoped format, or in capabilities scope
158 scope = key.split(':')
160 # Ignore scoped (such as vendor-specific) capabilities
161 if len(scope) > 1 and scope[0] != "capabilities":
162 continue
163 # Strip off prefix if spec started with 'capabilities:'
164 elif scope[0] == "capabilities":
165 del scope[0]
167 cap = capabilities
168 for index in range(len(scope)):
169 try:
170 cap = cap.get(scope[index])
171 except AttributeError:
172 cap = None
173 if cap is None:
174 LOG.debug("Host doesn't provide capability '%(cap)s' "
175 "listed in the extra specs",
176 {'cap': scope[index]})
177 return False
179 # Make all capability values a list so we can handle lists
180 cap_list = [cap] if not isinstance(cap, list) else cap
182 # Loop through capability values looking for any match
183 for cap_value in cap_list:
184 if extra_specs_ops.match(cap_value, req):
185 break
186 else:
187 # Nothing matched, so bail out
188 LOG.debug('Share type extra spec requirement '
189 '"%(key)s=%(req)s" does not match reported '
190 'capability "%(cap)s"',
191 {'key': key, 'req': req, 'cap': cap})
192 return False
193 return True