Files
manila/manila/scheduler/utils.py
T
Felipe Rodrigues 2b57d15c64 Add multiple subnets per AZ support
Manila can now configure a share network with multiple subnets
in an availability zone. Also, it can add a new subnet for an
availability that has share servers, which will triger an update
share server allocations.

Changes:
- API:
  - Bump version to 2.70.
  - setup share network with multiple subents per az.
  - Block manage server with multiple subnets.
  - Allow add subnet for in-use share servers.
  - `share_network_subnet_id` is dropped from ShareServer view
  - `share_network_subnet_ids` is added in ShareServer view
  - `network_allocation_update_support` is added to ShareServer and
    ShareNetwork views.
  - Add a check operation for share network subnet create.

- DB:
  - Remove `share_network_subnet_id` from share_servers.
  - Create mapping table `share_server_share_network_subnet_mappings`.
  - Fix queries with new db design.
  - Add migration downgrade and upgrade alembic.
  - Add `share_network_subnet_id` to the NetworkAllocations.

- Scheduler:
  - Change `AvailabilityZoneFilter` to take in account if the
    host supports the allocation required by the setup request.

- Manager:
  - Bump RPC API version.
  - `_setup_server` allocating multiple subnets.
  - Modify signature of driver `_setup_server` interface, passing a
    list of `network_info` for each subnet.
  - Share server DB creation to inform a list of subnets and
    create with `network_allocation_update_support`.
  - Implement `check_update_share_server_network_allocations` and
    `update_share_server_network_allocations`.

- Drivers:
  - For legacy compatibility, all drivers implementing `_setup_server`
    consume the first element of the `network_ino`.
  - Dummy Driver:
    - Implement `_setup_server` with new signature as multiple subnet.
    - Modify the `backend_details` to save allocations for all subnets.
    - Report update allocation and share server multiple subnet support.
    - Implement `check_update_share_server_network_allocations` and
      `update_share_server_network_allocations` interfaces.

Signed-off-by: Felipe Rodrigues <felipefuty01@gmail.com>
Co-Authored-By: Andre Beltrami <debeltrami@gmail.com>
Co-Authored-By: Fábio Oliveira <fabioaurelio1269@gmail.com>
Co-Authored-By: Nahim Alves de Souza <nahimsouza@outlook.com>
Co-Authored-By: Caique Mello <caique_mellosbo@hotmail.com>

DocImpact
APIImpact
Partially-Implements: blueprint multiple-share-network-subnets

Change-Id: I7de9de4ae509182e9494bba604979cce03acceec
2022-03-03 02:31:11 +00:00

187 lines
7.4 KiB
Python

# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2016 EMC Corporation
#
# 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
from oslo_utils import strutils
from manila.scheduler.filters import extra_specs_ops
LOG = log.getLogger(__name__)
def generate_stats(host_state, properties):
"""Generates statistics from host and share data."""
host_stats = {
'host': host_state.host,
'share_backend_name': host_state.share_backend_name,
'vendor_name': host_state.vendor_name,
'driver_version': host_state.driver_version,
'storage_protocol': host_state.storage_protocol,
'qos': host_state.qos,
'total_capacity_gb': host_state.total_capacity_gb,
'allocated_capacity_gb': host_state.allocated_capacity_gb,
'free_capacity_gb': host_state.free_capacity_gb,
'reserved_percentage': host_state.reserved_percentage,
'reserved_snapshot_percentage':
host_state.reserved_snapshot_percentage,
'driver_handles_share_servers':
host_state.driver_handles_share_servers,
'thin_provisioning': host_state.thin_provisioning,
'updated': host_state.updated,
'dedupe': host_state.dedupe,
'compression': host_state.compression,
'snapshot_support': host_state.snapshot_support,
'create_share_from_snapshot_support':
host_state.create_share_from_snapshot_support,
'revert_to_snapshot_support': host_state.revert_to_snapshot_support,
'mount_snapshot_support': host_state.mount_snapshot_support,
'replication_domain': host_state.replication_domain,
'replication_type': host_state.replication_type,
'provisioned_capacity_gb': host_state.provisioned_capacity_gb,
'pools': host_state.pools,
'max_over_subscription_ratio':
host_state.max_over_subscription_ratio,
'sg_consistent_snapshot_support': (
host_state.sg_consistent_snapshot_support),
'ipv4_support': host_state.ipv4_support,
'ipv6_support': host_state.ipv6_support,
'security_service_update_support': (
host_state.security_service_update_support),
'network_allocation_update_support': (
host_state.network_allocation_update_support),
'share_server_multiple_subnet_support': (
host_state.share_server_multiple_subnet_support)
}
host_caps = host_state.capabilities
share_type = properties.get('share_type', {})
extra_specs = share_type.get('extra_specs', {})
share_group_type = properties.get('share_group_type', {})
group_specs = share_group_type.get('group_specs', {})
request_spec = properties.get('request_spec', {})
share_stats = request_spec.get('resource_properties', {})
stats = {
'host_stats': host_stats,
'host_caps': host_caps,
'share_type': share_type,
'extra_specs': extra_specs,
'share_stats': share_stats,
'share_group_type': share_group_type,
'group_specs': group_specs,
}
return stats
def use_thin_logic(share_type):
# NOTE(xyang): To preserve the existing behavior, we use thin logic
# to evaluate in two cases:
# 1) 'thin_provisioning' is not set in extra specs (This is for
# backward compatibility. If not set, the scheduler behaves
# the same as before this bug fix).
# 2) 'thin_provisioning' is set in extra specs and it is
# '<is> True' or 'True'.
# Otherwise we use the thick logic to evaluate.
use_thin_logic = True
thin_spec = None
try:
thin_spec = share_type.get('extra_specs', {}).get(
'thin_provisioning')
if thin_spec is None:
thin_spec = share_type.get('extra_specs', {}).get(
'capabilities:thin_provisioning')
# NOTE(xyang) 'use_thin_logic' and 'thin_provisioning' are NOT
# the same thing. The first purpose of "use_thin_logic" is to
# preserve the existing scheduler behavior if 'thin_provisioning'
# is NOT in extra_specs (if thin_spec is None, use_thin_logic
# should be True). The second purpose of 'use_thin_logic'
# is to honor 'thin_provisioning' if it is in extra specs (if
# thin_spec is set to True, use_thin_logic should be True; if
# thin_spec is set to False, use_thin_logic should be False).
use_thin_logic = strutils.bool_from_string(
thin_spec, strict=True) if thin_spec is not None else True
except ValueError:
# Check if the value of thin_spec is '<is> True'.
if thin_spec is not None and not extra_specs_ops.match(
True, thin_spec):
use_thin_logic = False
return use_thin_logic
def thin_provisioning(host_state_thin_provisioning):
# NOTE(xyang): host_state_thin_provisioning is reported by driver.
# It can be either bool (True or False) or
# list ([True, False], [True], [False]).
thin_capability = [host_state_thin_provisioning] if not isinstance(
host_state_thin_provisioning, list) else host_state_thin_provisioning
return True in thin_capability
def capabilities_satisfied(capabilities, extra_specs):
# These extra-specs are not capabilities for matching hosts
ignored_extra_specs = (
'availability_zones', 'capabilities:availability_zones',
)
for key, req in extra_specs.items():
# Ignore some extra_specs if told to
if key in ignored_extra_specs:
continue
# Either not scoped format, or in capabilities scope
scope = key.split(':')
# Ignore scoped (such as vendor-specific) capabilities
if len(scope) > 1 and scope[0] != "capabilities":
continue
# Strip off prefix if spec started with 'capabilities:'
elif scope[0] == "capabilities":
del scope[0]
cap = capabilities
for index in range(len(scope)):
try:
cap = cap.get(scope[index])
except AttributeError:
cap = None
if cap is None:
LOG.debug("Host doesn't provide capability '%(cap)s' "
"listed in the extra specs",
{'cap': scope[index]})
return False
# Make all capability values a list so we can handle lists
cap_list = [cap] if not isinstance(cap, list) else cap
# Loop through capability values looking for any match
for cap_value in cap_list:
if extra_specs_ops.match(cap_value, req):
break
else:
# Nothing matched, so bail out
LOG.debug('Share type extra spec requirement '
'"%(key)s=%(req)s" does not match reported '
'capability "%(cap)s"',
{'key': key, 'req': req, 'cap': cap})
return False
return True