Files
manila/manila/share/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

174 lines
5.7 KiB
Python

# Copyright (c) 2012 OpenStack Foundation
# Copyright (c) 2015 Rushil Chugh
# 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.
"""Share-related Utilities and helpers."""
from oslo_config import cfg
from manila.common import constants
from manila.db import migration
from manila import rpc
from manila import utils
DEFAULT_POOL_NAME = '_pool0'
CONF = cfg.CONF
def extract_host(host, level='backend', use_default_pool_name=False):
"""Extract Host, Backend or Pool information from host string.
:param host: String for host, which could include host@backend#pool info
:param level: Indicate which level of information should be extracted
from host string. Level can be 'host', 'backend', 'pool',
or 'backend_name', default value is 'backend'
:param use_default_pool_name: This flag specifies what to do
if level == 'pool' and there is no 'pool' info
encoded in host string. default_pool_name=True
will return DEFAULT_POOL_NAME, otherwise it will
return None. Default value of this parameter
is False.
:return: expected level of information
For example:
host = 'HostA@BackendB#PoolC'
ret = extract_host(host, 'host')
# ret is 'HostA'
ret = extract_host(host, 'backend')
# ret is 'HostA@BackendB'
ret = extract_host(host, 'pool')
# ret is 'PoolC'
ret = extract_host(host, 'backend_name')
# ret is 'BackendB'
host = 'HostX@BackendY'
ret = extract_host(host, 'pool')
# ret is None
ret = extract_host(host, 'pool', True)
# ret is '_pool0'
"""
if level == 'host':
# Make sure pool is not included
hst = host.split('#')[0]
return hst.split('@')[0]
if level == 'backend_name':
hst = host.split('#')[0]
return hst.split('@')[1]
elif level == 'backend':
return host.split('#')[0]
elif level == 'pool':
lst = host.split('#')
if len(lst) == 2:
return lst[1]
elif use_default_pool_name is True:
return DEFAULT_POOL_NAME
else:
return None
def append_host(host, pool):
"""Encode pool into host info."""
if not host or not pool:
return host
new_host = "#".join([host, pool])
return new_host
def get_active_replica(replica_list):
"""Returns the first 'active' replica in the list of replicas provided."""
for replica in replica_list:
if replica['replica_state'] == constants.REPLICA_STATE_ACTIVE:
return replica
def change_rules_to_readonly(access_rules, add_rules, delete_rules):
dict_access_rules = cast_access_object_to_dict_in_readonly(access_rules)
dict_add_rules = cast_access_object_to_dict_in_readonly(add_rules)
dict_delete_rules = cast_access_object_to_dict_in_readonly(delete_rules)
return dict_access_rules, dict_add_rules, dict_delete_rules
def cast_access_object_to_dict_in_readonly(rules):
dict_rules = []
for rule in rules:
dict_rules.append({
'access_level': constants.ACCESS_LEVEL_RO,
'access_type': rule['access_type'],
'access_to': rule['access_to']
})
return dict_rules
@utils.if_notifications_enabled
def notify_about_share_usage(context, share, share_instance,
event_suffix, extra_usage_info=None, host=None):
if not host:
host = CONF.host
if not extra_usage_info:
extra_usage_info = {}
usage_info = _usage_from_share(share, share_instance, **extra_usage_info)
rpc.get_notifier("share", host).info(context, 'share.%s' % event_suffix,
usage_info)
def _usage_from_share(share_ref, share_instance_ref, **extra_usage_info):
usage_info = {
'share_id': share_ref['id'],
'user_id': share_ref['user_id'],
'project_id': share_ref['project_id'],
'snapshot_id': share_ref['snapshot_id'],
'share_group_id': share_ref['share_group_id'],
'size': share_ref['size'],
'name': share_ref['display_name'],
'description': share_ref['display_description'],
'proto': share_ref['share_proto'],
'is_public': share_ref['is_public'],
'availability_zone': share_instance_ref['availability_zone'],
'host': share_instance_ref['host'],
'status': share_instance_ref['status'],
}
usage_info.update(extra_usage_info)
return usage_info
def get_recent_db_migration_id():
return migration.version()
def is_az_subnets_compatible(subnet_list, new_subnet_list):
if len(subnet_list) != len(new_subnet_list):
return False
for subnet in subnet_list:
found_compatible = False
for new_subnet in new_subnet_list:
if (subnet.get('neutron_net_id') ==
new_subnet.get('neutron_net_id') and
subnet.get('neutron_subnet_id') ==
new_subnet.get('neutron_subnet_id')):
found_compatible = True
break
if not found_compatible:
return False
return True