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
This commit is contained in:
Felipe Rodrigues 2022-01-18 10:59:06 -03:00 committed by Fernando Ferraz
parent 3ce3854ae9
commit 2b57d15c64
70 changed files with 4463 additions and 999 deletions

View File

@ -25,10 +25,12 @@ from oslo_log import log
from oslo_utils import encodeutils from oslo_utils import encodeutils
from oslo_utils import strutils from oslo_utils import strutils
import webob import webob
from webob import exc
from manila.api.openstack import api_version_request as api_version from manila.api.openstack import api_version_request as api_version
from manila.api.openstack import versioned_method from manila.api.openstack import versioned_method
from manila.common import constants from manila.common import constants
from manila.db import api as db_api
from manila import exception from manila import exception
from manila.i18n import _ from manila.i18n import _
from manila import policy from manila import policy
@ -560,3 +562,53 @@ def validate_public_share_policy(context, api_params, api='create'):
raise exception.NotAuthorized(message=message) raise exception.NotAuthorized(message=message)
return api_params return api_params
def _get_existing_subnets(context, share_network_id, az):
"""Return any existing subnets in the requested AZ.
If az is None, the method will search for an existent default subnet.
"""
if az is None:
return db_api.share_network_subnet_get_default_subnets(
context, share_network_id)
return (
db_api.share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id, az,
fallback_to_default=False)
)
def validate_subnet_create(context, share_network_id, data,
multiple_subnet_support):
check_net_id_and_subnet_id(data)
try:
share_network = db_api.share_network_get(
context, share_network_id)
except exception.ShareNetworkNotFound as e:
raise exc.HTTPNotFound(explanation=e.msg)
availability_zone = data.pop('availability_zone', None)
subnet_az = {}
if availability_zone:
try:
subnet_az = db_api.availability_zone_get(context,
availability_zone)
except exception.AvailabilityZoneNotFound:
msg = _("The provided availability zone %s does not "
"exist.") % availability_zone
raise exc.HTTPBadRequest(explanation=msg)
data['availability_zone_id'] = subnet_az.get('id')
existing_subnets = _get_existing_subnets(
context, share_network_id, data['availability_zone_id'])
if existing_subnets and not multiple_subnet_support:
msg = ("Another share network subnet was found in the "
"specified availability zone. Only one share network "
"subnet is allowed per availability zone for share "
"network %s." % share_network_id)
raise exc.HTTPConflict(explanation=msg)
return share_network, existing_subnets

View File

@ -182,13 +182,17 @@ REST_API_VERSION_HISTORY = """
restore share from recycle bin. Also, a new parameter called restore share from recycle bin. Also, a new parameter called
`is_soft_deleted` was added so users can filter out `is_soft_deleted` was added so users can filter out
shares in the recycle bin while listing shares. shares in the recycle bin while listing shares.
* 2.70 - Added support for multiple share network subnets in the same
availability zone. Also, users can add subnets for an in-use share
network.
""" """
# The minimum and maximum versions of the API supported # The minimum and maximum versions of the API supported
# The default api version request is defined to be the # The default api version request is defined to be the
# minimum version of the API supported. # minimum version of the API supported.
_MIN_API_VERSION = "2.0" _MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.69" _MAX_API_VERSION = "2.70"
DEFAULT_API_VERSION = _MIN_API_VERSION DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -384,3 +384,11 @@ ____
/v2/shares/{share_id}/action {"soft_delete": null}``. List shares in /v2/shares/{share_id}/action {"soft_delete": null}``. List shares in
Recycle Bin: `` GET /v2/shares?is_soft_deleted=true``. Restore share from Recycle Bin: `` GET /v2/shares?is_soft_deleted=true``. Restore share from
Recycle Bin: `` POST /v2/shares/{share_id}/action {'restore': null}``. Recycle Bin: `` POST /v2/shares/{share_id}/action {'restore': null}``.
2.70
----
Added support to configure multiple subnets for a given share network in the
same availability zone (or the default one). Users can also add new subnets
for an in-use share network. To distinguish this update support a new
property called 'network_allocation_update_support' was added in the share
network and share server.

View File

@ -51,7 +51,6 @@ class ShareServerController(wsgi.Controller):
share_servers = db_api.share_server_get_all(context) share_servers = db_api.share_server_get_all(context)
for s in share_servers: for s in share_servers:
try: try:
s.share_network_id = s.share_network_subnet['share_network_id']
share_network = db_api.share_network_get( share_network = db_api.share_network_get(
context, s.share_network_id) context, s.share_network_id)
s.project_id = share_network['project_id'] s.project_id = share_network['project_id']
@ -75,7 +74,9 @@ class ShareServerController(wsgi.Controller):
(hasattr(s, k) and (hasattr(s, k) and
s[k] == v or k == 'share_network' and s[k] == v or k == 'share_network' and
v in [s.share_network_name, v in [s.share_network_name,
s.share_network_id])] s.share_network_id] or
k == 'share_network_subnet_id' and
v in s.share_network_subnet_ids)]
return self._view_builder.build_share_servers(req, share_servers) return self._view_builder.build_share_servers(req, share_servers)
@wsgi.Controller.authorize @wsgi.Controller.authorize
@ -85,8 +86,7 @@ class ShareServerController(wsgi.Controller):
try: try:
server = db_api.share_server_get(context, id) server = db_api.share_server_get(context, id)
share_network = db_api.share_network_get( share_network = db_api.share_network_get(
context, server.share_network_subnet['share_network_id']) context, server['share_network_id'])
server.share_network_id = share_network['id']
server.project_id = share_network['project_id'] server.project_id = share_network['project_id']
if share_network['name']: if share_network['name']:
server.share_network_name = share_network['name'] server.share_network_name = share_network['name']
@ -97,7 +97,7 @@ class ShareServerController(wsgi.Controller):
except exception.ShareNetworkNotFound: except exception.ShareNetworkNotFound:
msg = _("Share server %s could not be found. Its associated " msg = _("Share server %s could not be found. Its associated "
"share network does not " "share network does not "
"exist.") % server.share_network_subnet['share_network_id'] "exist.") % server['share_network_id']
raise exc.HTTPNotFound(explanation=msg) raise exc.HTTPNotFound(explanation=msg)
return self._view_builder.build_share_server(req, server) return self._view_builder.build_share_server(req, server)

View File

@ -355,12 +355,17 @@ class ShareMixin(object):
common.check_share_network_is_active(share_network) common.check_share_network_is_active(share_network)
if availability_zone_id: if availability_zone_id:
if not db.share_network_subnet_get_by_availability_zone_id( subnets = (
db.share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id, context, share_network_id,
availability_zone_id=availability_zone_id): availability_zone_id=availability_zone_id))
if not subnets:
msg = _("A share network subnet was not found for the " msg = _("A share network subnet was not found for the "
"requested availability zone.") "requested availability zone.")
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
kwargs['az_request_multiple_subnet_support_map'] = {
availability_zone_id: len(subnets) > 1,
}
display_name = share.get('display_name') display_name = share.get('display_name')
display_description = share.get('display_description') display_description = share.get('display_description')

View File

@ -21,11 +21,13 @@ from oslo_log import log
import webob import webob
from webob import exc from webob import exc
from manila.api.openstack import api_version_request as api_version
from manila.api.openstack import wsgi from manila.api.openstack import wsgi
from manila.api.views import share_network_subnets as subnet_views from manila.api.views import share_network_subnets as subnet_views
from manila.db import api as db_api from manila.db import api as db_api
from manila import exception from manila import exception
from manila.i18n import _ from manila.i18n import _
from manila import share
from manila.share import rpcapi as share_rpcapi from manila.share import rpcapi as share_rpcapi
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@ -40,6 +42,7 @@ class ShareNetworkSubnetController(wsgi.Controller):
def __init__(self): def __init__(self):
super(ShareNetworkSubnetController, self).__init__() super(ShareNetworkSubnetController, self).__init__()
self.share_rpcapi = share_rpcapi.ShareAPI() self.share_rpcapi = share_rpcapi.ShareAPI()
self.share_api = share.API()
@wsgi.Controller.api_version("2.51") @wsgi.Controller.api_version("2.51")
@wsgi.Controller.authorize @wsgi.Controller.authorize
@ -104,74 +107,55 @@ class ShareNetworkSubnetController(wsgi.Controller):
db_api.share_network_subnet_delete(context, share_network_subnet_id) db_api.share_network_subnet_delete(context, share_network_subnet_id)
return webob.Response(status_int=http_client.ACCEPTED) return webob.Response(status_int=http_client.ACCEPTED)
def _validate_subnet(self, context, share_network_id, az=None):
"""Validate the az for the given subnet.
If az is None, the method will search for an existent default subnet.
In case of a given AZ, validates if there's an existent subnet for it.
"""
msg = ("Another share network subnet was found in the "
"specified availability zone. Only one share network "
"subnet is allowed per availability zone for share "
"network %s." % share_network_id)
if az is None:
default_subnet = db_api.share_network_subnet_get_default_subnet(
context, share_network_id)
if default_subnet is not None:
raise exc.HTTPConflict(explanation=msg)
else:
az_subnet = (
db_api.share_network_subnet_get_by_availability_zone_id(
context, share_network_id, az['id'])
)
# If the 'availability_zone_id' is not None, we found a conflict,
# otherwise we just have found the default subnet
if az_subnet and az_subnet['availability_zone_id']:
raise exc.HTTPConflict(explanation=msg)
@wsgi.Controller.api_version("2.51") @wsgi.Controller.api_version("2.51")
@wsgi.Controller.authorize @wsgi.Controller.authorize
def create(self, req, share_network_id, body): def create(self, req, share_network_id, body):
"""Add a new share network subnet into the share network.""" """Add a new share network subnet into the share network."""
context = req.environ['manila.context'] context = req.environ['manila.context']
if not self.is_valid_body(body, 'share-network-subnet'): if not self.is_valid_body(body, 'share-network-subnet'):
msg = _("Share Network Subnet is missing from the request body.") msg = _("Share Network Subnet is missing from the request body.")
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
data = body['share-network-subnet'] data = body['share-network-subnet']
data['share_network_id'] = share_network_id data['share_network_id'] = share_network_id
multiple_subnet_support = (req.api_version_request >=
api_version.APIVersionRequest("2.70"))
share_network, existing_subnets = common.validate_subnet_create(
context, share_network_id, data, multiple_subnet_support)
common.check_net_id_and_subnet_id(data) # create subnet operation on subnets with share servers means that an
# allocation update is requested.
if existing_subnets and existing_subnets[0]['share_servers']:
try: # NOTE(felipe_rodrigues): all subnets have the same set of share
db_api.share_network_get(context, share_network_id) # servers, so we can just get the servers from one of them. Not
except exception.ShareNetworkNotFound as e: # necessarily all share servers from the specified AZ will be
raise exc.HTTPNotFound(explanation=e.msg) # updated, only the ones created with subnets in the AZ. Others
# created with default AZ will only have its allocations updated
availability_zone = data.pop('availability_zone', None) # when default subnet set is updated.
subnet_az = None data['share_servers'] = existing_subnets[0]['share_servers']
if availability_zone:
try: try:
subnet_az = db_api.availability_zone_get(context, share_network_subnet = (
availability_zone) self.share_api.update_share_server_network_allocations(
except exception.AvailabilityZoneNotFound: context, share_network, data))
msg = _("The provided availability zone %s does not " except exception.ServiceIsDown as e:
"exist.") % availability_zone msg = _('Could not add the share network subnet.')
raise exc.HTTPBadRequest(explanation=msg) LOG.error(e)
raise exc.HTTPInternalServerError(explanation=msg)
except exception.InvalidShareNetwork as e:
raise exc.HTTPBadRequest(explanation=e.msg)
except db_exception.DBError as e:
msg = _('Could not add the share network subnet.')
LOG.error(e)
raise exc.HTTPInternalServerError(explanation=msg)
else:
try:
share_network_subnet = db_api.share_network_subnet_create(
context, data)
except db_exception.DBError as e:
msg = _('Could not create the share network subnet.')
LOG.error(e)
raise exc.HTTPInternalServerError(explanation=msg)
self._validate_subnet(context, share_network_id, az=subnet_az)
try:
data['availability_zone_id'] = (
subnet_az['id'] if subnet_az is not None else None)
share_network_subnet = db_api.share_network_subnet_create(
context, data)
except db_exception.DBError as e:
msg = _('Could not create the share network subnet.')
LOG.error(e)
raise exc.HTTPInternalServerError(explanation=msg)
share_network_subnet = db_api.share_network_subnet_get( share_network_subnet = db_api.share_network_subnet_get(
context, share_network_subnet['id']) context, share_network_subnet['id'])
return self._view_builder.build_share_network_subnet( return self._view_builder.build_share_network_subnet(

View File

@ -279,15 +279,23 @@ class ShareNetworkController(wsgi.Controller, wsgi.AdminActionsMixin):
try: try:
if ('neutron_net_id' in update_values or if ('neutron_net_id' in update_values or
'neutron_subnet_id' in update_values): 'neutron_subnet_id' in update_values):
subnet = db_api.share_network_subnet_get_default_subnet( subnets = db_api.share_network_subnet_get_default_subnets(
context, id) context, id)
if not subnet: if not subnets:
msg = _("The share network %(id)s does not have a " msg = _("The share network %(id)s does not have a "
"'default' subnet that serves all availability " "'default' subnet that serves all availability "
"zones, so subnet details " "zones, so subnet details "
"('neutron_net_id', 'neutron_subnet_id') cannot " "('neutron_net_id', 'neutron_subnet_id') cannot "
"be updated.") % {'id': id} "be updated.") % {'id': id}
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
if len(subnets) > 1:
msg = _("The share network %(id)s does not have an unique "
"'default' subnet that serves all availability "
"zones, so subnet details "
"('neutron_net_id', 'neutron_subnet_id') cannot "
"be updated.") % {'id': id}
raise exc.HTTPBadRequest(explanation=msg)
subnet = subnets[0]
# NOTE(silvacarlose): If the default share network subnet have # NOTE(silvacarlose): If the default share network subnet have
# the fields neutron_net_id and neutron_subnet_id set as None, # the fields neutron_net_id and neutron_subnet_id set as None,
@ -616,6 +624,54 @@ class ShareNetworkController(wsgi.Controller, wsgi.AdminActionsMixin):
return self._view_builder.build_security_service_update_check( return self._view_builder.build_security_service_update_check(
req, data, result) req, data, result)
@wsgi.Controller.api_version('2.70')
@wsgi.action('share_network_subnet_create_check')
@wsgi.response(202)
def share_network_subnet_create_check(self, req, id, body):
"""Check the feasibility of creating a share network subnet."""
context = req.environ['manila.context']
if not self.is_valid_body(body, 'share_network_subnet_create_check'):
msg = _("Share Network Subnet Create Check is missing from "
"the request body.")
raise exc.HTTPBadRequest(explanation=msg)
data = body['share_network_subnet_create_check']
share_network, existing_subnets = common.validate_subnet_create(
context, id, data, True)
reset_check = utils.get_bool_from_api_params('reset_operation', data)
# create subnet operation alongside subnets with share servers means
# that an allocation update is requested.
if existing_subnets and existing_subnets[0]['share_servers']:
# NOTE(felipe_rodrigues): all subnets within the same az have the
# same set of share servers, so we can just get the servers from
# one of them. Not necessarily all share servers from the specified
# AZ will be updated, only the ones created with subnets in the AZ.
# Others created with default AZ will only have its allocations
# updated when default subnet set is updated.
data['share_servers'] = existing_subnets[0]['share_servers']
try:
check_result = (
self.share_api.
check_update_share_server_network_allocations(
context, share_network, data, reset_check))
except exception.ServiceIsDown as e:
msg = _("A share network subnet update check cannot be "
"performed at this time.")
LOG.error(e)
raise exc.HTTPInternalServerError(explanation=msg)
except exception.InvalidShareNetwork as e:
raise exc.HTTPBadRequest(explanation=e.msg)
else:
check_result = {
'compatible': True,
'hosts_check_result': {}
}
return self._view_builder.build_share_network_subnet_create_check(
req, check_result)
@wsgi.Controller.api_version('2.63') @wsgi.Controller.api_version('2.63')
@wsgi.action('reset_status') @wsgi.action('reset_status')
def reset_status(self, req, id, body): def reset_status(self, req, id, body):

View File

@ -73,7 +73,6 @@ class ShareServerController(share_servers.ShareServerController,
raise exc.HTTPForbidden(explanation=e.msg) raise exc.HTTPForbidden(explanation=e.msg)
result.project_id = share_network["project_id"] result.project_id = share_network["project_id"]
result.share_network_id = share_network["id"]
if share_network['name']: if share_network['name']:
result.share_network_name = share_network['name'] result.share_network_name = share_network['name']
else: else:
@ -107,14 +106,12 @@ class ShareServerController(share_servers.ShareServerController,
except exception.ShareServerNotFound as e: except exception.ShareServerNotFound as e:
raise exc.HTTPNotFound(explanation=e.msg) raise exc.HTTPNotFound(explanation=e.msg)
network_subnet_id = share_server.get('share_network_subnet_id', None) if len(share_server['share_network_subnets']) > 1:
if network_subnet_id: msg = _("Cannot unmanage the share server containing multiple "
subnet = db_api.share_network_subnet_get(context, "subnets.")
network_subnet_id) raise exc.HTTPBadRequest(explanation=msg)
share_network_id = subnet['share_network_id']
else:
share_network_id = share_server.get('share_network_id')
share_network_id = share_server['share_network_id']
share_network = db_api.share_network_get(context, share_network_id) share_network = db_api.share_network_get(context, share_network_id)
common.check_share_network_is_active(share_network) common.check_share_network_is_active(share_network)
@ -169,22 +166,30 @@ class ShareServerController(share_servers.ShareServerController,
network_subnet_id = data.get('share_network_subnet_id') network_subnet_id = data.get('share_network_subnet_id')
if network_subnet_id: if network_subnet_id:
try: try:
network_subnet = db_api.share_network_subnet_get( network_subnets = (
context, network_subnet_id) db_api.share_network_subnet_get_all_with_same_az(
context, network_subnet_id))
except exception.ShareNetworkSubnetNotFound: except exception.ShareNetworkSubnetNotFound:
msg = _("The share network subnet %s does not " msg = _("The share network subnet %s does not "
"exist.") % network_subnet_id "exist.") % network_subnet_id
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
else: else:
network_subnet = db_api.share_network_subnet_get_default_subnet( network_subnets = db_api.share_network_subnet_get_default_subnets(
context, share_network_id) context, share_network_id)
if network_subnet is None: if not network_subnets:
msg = _("The share network %s does have a default subnet. Create " msg = _("The share network %s does have a default subnet. Create "
"one or use a specific subnet to manage this share server " "one or use a specific subnet to manage this share server "
"with API version >= 2.51.") % share_network_id "with API version >= 2.51.") % share_network_id
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
if len(network_subnets) > 1:
msg = _("Cannot manage the share server, since the share network "
"subnet %s has more subnets in its availability "
"zone and share network.") % network_subnet_id
raise exc.HTTPBadRequest(explanation=msg)
network_subnet = network_subnets[0]
common.check_share_network_is_active(network_subnet['share_network']) common.check_share_network_is_active(network_subnet['share_network'])
if share_utils.extract_host(host, 'pool'): if share_utils.extract_host(host, 'pool'):
@ -260,7 +265,7 @@ class ShareServerController(share_servers.ShareServerController,
common.check_share_network_is_active(new_share_network) common.check_share_network_is_active(new_share_network)
else: else:
share_network_id = ( share_network_id = (
share_server['share_network_subnet']['share_network_id']) share_server['share_network_id'])
current_share_network = db_api.share_network_get( current_share_network = db_api.share_network_get(
context, share_network_id) context, share_network_id)
common.check_share_network_is_active(current_share_network) common.check_share_network_is_active(current_share_network)
@ -387,7 +392,7 @@ class ShareServerController(share_servers.ShareServerController,
common.check_share_network_is_active(new_share_network) common.check_share_network_is_active(new_share_network)
else: else:
share_network_id = ( share_network_id = (
share_server['share_network_subnet']['share_network_id']) share_server['share_network_id'])
current_share_network = db_api.share_network_get( current_share_network = db_api.share_network_get(
context, share_network_id) context, share_network_id)
common.check_share_network_is_active(current_share_network) common.check_share_network_is_active(current_share_network)

View File

@ -22,7 +22,8 @@ class ViewBuilder(common.ViewBuilder):
_collection_name = 'share_networks' _collection_name = 'share_networks'
_detail_version_modifiers = ["add_gateway", "add_mtu", "add_nova_net_id", _detail_version_modifiers = ["add_gateway", "add_mtu", "add_nova_net_id",
"add_subnets", "add_subnets",
"add_status_and_sec_service_update_fields"] "add_status_and_sec_service_update_fields",
"add_network_allocation_update_support_field"]
def build_share_network(self, request, share_network): def build_share_network(self, request, share_network):
"""View of a share network.""" """View of a share network."""
@ -55,6 +56,16 @@ class ViewBuilder(common.ViewBuilder):
view['hosts_check_result'] = result['hosts_check_result'] view['hosts_check_result'] = result['hosts_check_result']
return view return view
def build_share_network_subnet_create_check(self, request, result):
"""View of share network subnet create check."""
context = request.environ['manila.context']
view = {
'compatible': result['compatible'],
}
if context.is_admin:
view['hosts_check_result'] = result['hosts_check_result']
return view
def _update_share_network_info(self, request, share_network): def _update_share_network_info(self, request, share_network):
for sns in share_network.get('share_network_subnets') or []: for sns in share_network.get('share_network_subnets') or []:
if sns.get('is_default') and sns.get('is_default') is True: if sns.get('is_default') and sns.get('is_default') is True:
@ -135,3 +146,9 @@ class ViewBuilder(common.ViewBuilder):
network_dict['status'] = network.get('status') network_dict['status'] = network.get('status')
network_dict['security_service_update_support'] = network.get( network_dict['security_service_update_support'] = network.get(
'security_service_update_support') 'security_service_update_support')
@common.ViewBuilder.versioned_method("2.70")
def add_network_allocation_update_support_field(
self, context, network_dict, network):
network_dict['network_allocation_update_support'] = network.get(
'network_allocation_update_support')

View File

@ -24,7 +24,8 @@ class ViewBuilder(common.ViewBuilder):
"add_is_auto_deletable_and_identifier_fields", "add_is_auto_deletable_and_identifier_fields",
"add_share_network_subnet_id_field", "add_share_network_subnet_id_field",
"add_task_state_and_source_server_fields", "add_task_state_and_source_server_fields",
"add_sec_service_update_fields" "add_sec_service_update_fields",
"add_share_network_subnet_ids_and_network_allocation_update_support"
] ]
def build_share_server(self, request, share_server): def build_share_server(self, request, share_server):
@ -64,11 +65,13 @@ class ViewBuilder(common.ViewBuilder):
return share_server_dict return share_server_dict
@common.ViewBuilder.versioned_method("2.51") @common.ViewBuilder.versioned_method("2.51", "2.69")
def add_share_network_subnet_id_field( def add_share_network_subnet_id_field(
self, context, share_server_dict, share_server): self, context, share_server_dict, share_server):
"""In 2.70, share_network_subnet_id is dropped, it becomes a list."""
share_server_dict['share_network_subnet_id'] = ( share_server_dict['share_network_subnet_id'] = (
share_server['share_network_subnet_id']) share_server['share_network_subnet_ids'][0]
if share_server['share_network_subnet_ids'] else None)
@common.ViewBuilder.versioned_method("2.49") @common.ViewBuilder.versioned_method("2.49")
def add_is_auto_deletable_and_identifier_fields( def add_is_auto_deletable_and_identifier_fields(
@ -89,3 +92,11 @@ class ViewBuilder(common.ViewBuilder):
self, context, share_server_dict, share_server): self, context, share_server_dict, share_server):
share_server_dict['security_service_update_support'] = share_server[ share_server_dict['security_service_update_support'] = share_server[
'security_service_update_support'] 'security_service_update_support']
@common.ViewBuilder.versioned_method("2.70")
def add_share_network_subnet_ids_and_network_allocation_update_support(
self, context, share_server_dict, share_server):
share_server_dict['share_network_subnet_ids'] = sorted(
share_server['share_network_subnet_ids'])
share_server_dict['network_allocation_update_support'] = (
share_server['network_allocation_update_support'])

View File

@ -421,7 +421,8 @@ class ShareServerCommands(object):
""" """
share_servers = [server.strip() for server in share_servers.split(",")] share_servers = [server.strip() for server in share_servers.split(",")]
capabilities = [cap.strip() for cap in capabilities.split(",")] capabilities = [cap.strip() for cap in capabilities.split(",")]
supported_capabilities = ['security_service_update_support'] supported_capabilities = ['security_service_update_support',
'network_allocation_update_support']
values = dict() values = dict()
for capability in capabilities: for capability in capabilities:

View File

@ -984,29 +984,45 @@ def share_network_subnet_get(context, network_subnet_id, session=None):
session=session) session=session)
def share_network_subnet_get_all_with_same_az(context, network_subnet_id,
session=None):
"""Get requested az share network subnets DB record."""
return IMPL.share_network_subnet_get_all_with_same_az(
context, network_subnet_id, session=session)
def share_network_subnet_get_all(context): def share_network_subnet_get_all(context):
"""Get all share network subnet DB record.""" """Get all share network subnet DB record."""
return IMPL.share_network_subnet_get_all(context) return IMPL.share_network_subnet_get_all(context)
def share_network_subnet_get_by_availability_zone_id(context, share_network_id, def share_network_subnets_get_all_by_availability_zone_id(
availability_zone_id): context, share_network_id, availability_zone_id,
"""Get a share network subnet DB record. fallback_to_default=True):
"""Get the share network subnets DB record in a given AZ.
This method returns a subnet DB record for a given share network id and This method returns list of subnets DB record for a given share network id
an availability zone. If the 'availability_zone_id' is 'None', a record may and an availability zone. If the 'availability_zone_id' is 'None', a
be returned and it will represent the default share network subnet. record may be returned and it will represent the default share network
Be aware that if there is no subnet for a specific availability zone id, subnets. If there is no subnet for a specific availability zone id and
this method will return the default share network subnet, if it exists. "fallback_to_default" is True, this method will return the default share
network subnets, if it exists.
""" """
return IMPL.share_network_subnet_get_by_availability_zone_id( return IMPL.share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id, availability_zone_id) context, share_network_id, availability_zone_id,
fallback_to_default=fallback_to_default)
def share_network_subnet_get_default_subnet(context, share_network_id): def share_network_subnet_get_default_subnets(context, share_network_id):
"""Get the default share network subnet DB record.""" """Get the default share network subnets DB records."""
return IMPL.share_network_subnet_get_default_subnet(context, return IMPL.share_network_subnet_get_default_subnets(context,
share_network_id) share_network_id)
def share_network_subnet_get_all_by_share_server_id(context, share_server_id):
"""Get the subnets that are being used by the share server."""
return IMPL.share_network_subnet_get_all_by_share_server_id(
context, share_server_id)
################## ##################
@ -1035,10 +1051,12 @@ def network_allocation_get(context, id, session=None, read_deleted=None):
def network_allocations_get_for_share_server(context, share_server_id, def network_allocations_get_for_share_server(context, share_server_id,
session=None, label=None): session=None, label=None,
subnet_id=None):
"""Get network allocations for share server.""" """Get network allocations for share server."""
return IMPL.network_allocations_get_for_share_server( return IMPL.network_allocations_get_for_share_server(
context, share_server_id, label=label, session=session) context, share_server_id, label=label, session=session,
subnet_id=subnet_id)
def network_allocations_get_by_ip_address(context, ip_address): def network_allocations_get_by_ip_address(context, ip_address):
@ -1077,6 +1095,7 @@ def share_server_search_by_identifier(context, identifier, session=None):
def share_server_get_all_by_host_and_share_subnet_valid(context, host, def share_server_get_all_by_host_and_share_subnet_valid(context, host,
share_subnet_id, share_subnet_id,
server_status=None,
session=None): session=None):
"""Get share server DB records by host and share net not error.""" """Get share server DB records by host and share net not error."""
return IMPL.share_server_get_all_by_host_and_share_subnet_valid( return IMPL.share_server_get_all_by_host_and_share_subnet_valid(

View File

@ -0,0 +1,229 @@
# 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.
"""multiple share server subnets
Revision ID: a87e0fb17dee
Revises: 1946cb97bb8d
Create Date: 2022-01-14 06:12:27.596130
"""
# revision identifiers, used by Alembic.
revision = 'a87e0fb17dee'
down_revision = '1946cb97bb8d'
from alembic import op
from oslo_log import log
import sqlalchemy as sa
from manila.db.migrations import utils
SHARE_SERVERS_TABLE = 'share_servers'
SHARE_SERVER_SUBNET_MAP_TABLE = 'share_server_share_network_subnet_mappings'
NETWORK_ALLOCATIONS_TABLE = 'network_allocations'
LOG = log.getLogger(__name__)
def upgrade():
# Create mappings table.
context = op.get_context()
mysql_dl = context.bind.dialect.name == 'mysql'
datetime_type = (sa.dialects.mysql.DATETIME(fsp=6)
if mysql_dl else sa.DateTime)
try:
share_server_fk_name = "fk_ss_sns_m_share_server_id_share_servers"
share_network_subnet_fk_name = (
"fk_ss_sns_m_share_network_subnet_id_share_network_subnets")
server_subnet_mappings_table = op.create_table(
SHARE_SERVER_SUBNET_MAP_TABLE,
sa.Column('id', sa.Integer, primary_key=True, nullable=False),
sa.Column('created_at', datetime_type),
sa.Column('updated_at', datetime_type),
sa.Column('deleted_at', datetime_type),
sa.Column('deleted', sa.Integer, default=0),
sa.Column(
'share_server_id', sa.String(length=36),
sa.ForeignKey('share_servers.id', name=share_server_fk_name),
nullable=False),
sa.Column(
'share_network_subnet_id', sa.String(length=36),
sa.ForeignKey('share_network_subnets.id',
name=share_network_subnet_fk_name),
nullable=False),
mysql_engine='InnoDB',
mysql_charset='utf8')
except Exception:
LOG.error('Table %s could not be created.',
SHARE_SERVER_SUBNET_MAP_TABLE)
raise
# Populate the mappings table from the share servers table.
try:
connection = op.get_bind()
share_servers_table = utils.load_table(SHARE_SERVERS_TABLE, connection)
server_subnet_mappings = []
for server in connection.execute(share_servers_table.select()):
if server.share_network_subnet_id:
server_subnet_mappings.append({
'created_at': server.created_at,
'updated_at': server.updated_at,
'deleted_at': server.deleted_at,
'deleted': 0 if server.deleted == 'False' else 1,
'share_server_id': server.id,
'share_network_subnet_id': server.share_network_subnet_id,
})
op.bulk_insert(server_subnet_mappings_table, server_subnet_mappings)
except Exception:
LOG.error('Table %s could not be populated from the %s table.',
SHARE_SERVER_SUBNET_MAP_TABLE, SHARE_SERVERS_TABLE)
raise
# add subnet id column to the allocations table.
try:
network_allocation_fk_name = (
"fk_network_allocation_subnet_id_share_network_subnets")
op.add_column(
NETWORK_ALLOCATIONS_TABLE,
sa.Column('share_network_subnet_id', sa.String(length=36),
sa.ForeignKey('share_network_subnets.id',
name=network_allocation_fk_name))
)
except Exception:
LOG.error("Could not add ForeignKey column 'share_network_subnet_id'"
"to table %s.", NETWORK_ALLOCATIONS_TABLE)
raise
# populate the allocation with its subnet id using the share server.
network_allocation_table = utils.load_table(NETWORK_ALLOCATIONS_TABLE,
connection)
for alloc in connection.execute(network_allocation_table.select()):
# admin allocations should not contain subnet id.
if alloc['label'] == 'admin':
continue
server = connection.execute(
share_servers_table.select().where(
alloc['share_server_id'] == (
share_servers_table.c.id))).first()
# pylint: disable=no-value-for-parameter
op.execute(network_allocation_table.update().where(
alloc['id'] == network_allocation_table.c.id).values(
{'share_network_subnet_id': server['share_network_subnet_id']}))
# add a new column to share_servers.
try:
op.add_column(
SHARE_SERVERS_TABLE,
sa.Column('network_allocation_update_support', sa.Boolean,
nullable=False, server_default=sa.sql.false()))
except Exception:
LOG.error("Table %s could not add column "
"'network_allocation_update_support'.",
SHARE_SERVERS_TABLE)
raise
# drop subnet id foreign key from share servers.
try:
share_serves_fk_name = (
"fk_share_servers_share_network_subnet_id_share_network_subnets")
if connection.engine.name == 'mysql':
op.drop_constraint(share_serves_fk_name, SHARE_SERVERS_TABLE,
type_="foreignkey")
op.drop_column(SHARE_SERVERS_TABLE, 'share_network_subnet_id')
except Exception:
LOG.error("Table %s could not drop column 'share_network_subnet_id'.",
SHARE_SERVERS_TABLE)
raise
def downgrade():
"""Remove share_server_share_network_subnet_mapping table and new columns.
This method can lead to data loss because the share server can have
more than one subnet.
"""
try:
share_serves_fk_name = (
"fk_share_servers_share_network_subnet_id_share_network_subnets")
op.add_column(
SHARE_SERVERS_TABLE,
sa.Column(
'share_network_subnet_id', sa.String(36),
sa.ForeignKey('share_network_subnets.id',
name=share_serves_fk_name),
)
)
connection = op.get_bind()
server_subnet_mappings_table = utils.load_table(
SHARE_SERVER_SUBNET_MAP_TABLE, connection)
share_servers_table = utils.load_table(SHARE_SERVERS_TABLE,
connection)
session = sa.orm.Session(bind=connection.connect())
for server in connection.execute(share_servers_table.select()):
subnets = session.query(
server_subnet_mappings_table).filter(
server['id'] == (
server_subnet_mappings_table.c.share_server_id)).all()
if server['deleted'] != 'False' and len(subnets) > 1:
LOG.warning('Share server %s is not deleted and it '
'has more than one subnet (%s subnets), '
'the downgrade may cause an inconsistent '
'environment.', server['id'], len(subnets))
subnet_id = subnets[0].share_network_subnet_id if subnets else None
# pylint: disable=no-value-for-parameter
op.execute(share_servers_table.update().where(
server['id'] == share_servers_table.c.id).values(
{'share_network_subnet_id': subnet_id}))
session.close_all()
except Exception:
LOG.error("'share_network_subnet_id' field in the %s table could not "
"be created and populated from %s table.",
SHARE_SERVERS_TABLE, SHARE_SERVER_SUBNET_MAP_TABLE)
raise
try:
op.drop_table(SHARE_SERVER_SUBNET_MAP_TABLE)
except Exception:
LOG.error("Failed to drop table %s.", SHARE_SERVER_SUBNET_MAP_TABLE)
raise
try:
op.drop_column(SHARE_SERVERS_TABLE,
'network_allocation_update_support')
except Exception:
LOG.error("Table %s failed to drop the column "
"'network_allocation_update_support'.", SHARE_SERVERS_TABLE)
raise
try:
network_allocation_fk_name = (
"fk_network_allocation_subnet_id_share_network_subnets")
if connection.engine.name == 'mysql':
op.drop_constraint(network_allocation_fk_name,
NETWORK_ALLOCATIONS_TABLE,
type_="foreignkey")
op.drop_column(NETWORK_ALLOCATIONS_TABLE, 'share_network_subnet_id')
except Exception:
LOG.error("Column 'network_allocations.share_network_subnet_id' from "
"table %s failed to drop.", NETWORK_ALLOCATIONS_TABLE)
raise

View File

@ -4253,6 +4253,24 @@ def share_network_subnet_get(context, network_subnet_id, session=None):
return result return result
@require_context
def share_network_subnet_get_all_with_same_az(context, network_subnet_id,
session=None):
subnet = (_network_subnet_get_query(context, session)
.filter_by(id=network_subnet_id).subquery())
result = (_network_subnet_get_query(context, session)
.join(subnet, subnet.c.share_network_id ==
models.ShareNetworkSubnet.share_network_id)
.filter(func.coalesce(subnet.c.availability_zone_id, '0') ==
func.coalesce(
models.ShareNetworkSubnet.availability_zone_id, '0'))
.all())
if not result:
raise exception.ShareNetworkSubnetNotFound(
share_network_subnet_id=network_subnet_id)
return result
@require_context @require_context
def share_network_subnet_get_all(context): def share_network_subnet_get_all(context):
return _network_subnet_get_query(context).all() return _network_subnet_get_query(context).all()
@ -4265,25 +4283,55 @@ def share_network_subnet_get_all_by_share_network(context, network_id):
@require_context @require_context
def share_network_subnet_get_by_availability_zone_id( def share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id, availability_zone_id): context, share_network_id, availability_zone_id,
fallback_to_default=True):
"""Get the share network subnets DB records in a given AZ.
This method returns list of subnets DB record for a given share network id
and an availability zone. If the 'availability_zone_id' is 'None', a
record may be returned and it will represent the default share network
subnets. If there is no subnet for a specific availability zone id and
"fallback_to_default" is True, this method will return the default share
network subnets, if it exists.
:param context: operation context.
:param share_network_id: the share network id to be the subnets.
:param availability_zone_id: the availability zone id to be the subnets.
:param fallback_to_default: determines in case no subnets found in the
given AZ, it will return the "default" subnets.
:return: the list of share network subnets in the AZ and share network.
"""
result = (_network_subnet_get_query(context).filter_by( result = (_network_subnet_get_query(context).filter_by(
share_network_id=share_network_id, share_network_id=share_network_id,
availability_zone_id=availability_zone_id).first()) availability_zone_id=availability_zone_id).all())
# If a specific subnet wasn't found, try get the default one # If a specific subnet wasn't found, try get the default one
if availability_zone_id and not result: if availability_zone_id and not result and fallback_to_default:
return (_network_subnet_get_query(context).filter_by( return (_network_subnet_get_query(context).filter_by(
share_network_id=share_network_id, share_network_id=share_network_id,
availability_zone_id=None).first()) availability_zone_id=None).all())
return result return result
@require_context @require_context
def share_network_subnet_get_default_subnet(context, share_network_id): def share_network_subnet_get_default_subnets(context, share_network_id):
return share_network_subnet_get_by_availability_zone_id( return share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id, availability_zone_id=None) context, share_network_id, availability_zone_id=None)
@require_context
def share_network_subnet_get_all_by_share_server_id(context, share_server_id):
result = (_network_subnet_get_query(context)
.filter(models.ShareNetworkSubnet.share_servers.any(
id=share_server_id))
.all())
if not result:
raise exception.ShareNetworkSubnetNotFoundByShareServer(
share_server_id=share_server_id)
return result
################### ###################
@ -4293,7 +4341,7 @@ def _server_get_query(context, session=None):
return (model_query(context, models.ShareServer, session=session). return (model_query(context, models.ShareServer, session=session).
options(joinedload('share_instances'), options(joinedload('share_instances'),
joinedload('network_allocations'), joinedload('network_allocations'),
joinedload('share_network_subnet'))) joinedload('share_network_subnets')))
@require_context @require_context
@ -4314,6 +4362,12 @@ def share_server_delete(context, id):
session = get_session() session = get_session()
with session.begin(): with session.begin():
server_ref = share_server_get(context, id, session=session) server_ref = share_server_get(context, id, session=session)
model_query(
context, models.ShareServerShareNetworkSubnetMapping,
session=session
).filter_by(
share_server_id=id,
).soft_delete()
share_server_backend_details_delete(context, id, session=session) share_server_backend_details_delete(context, id, session=session)
server_ref.soft_delete(session=session, update_status=True) server_ref.soft_delete(session=session, update_status=True)
@ -4384,12 +4438,19 @@ def share_server_search_by_identifier(context, identifier, session=None):
@require_context @require_context
def share_server_get_all_by_host_and_share_subnet_valid(context, host, def share_server_get_all_by_host_and_share_subnet_valid(context, host,
share_subnet_id, share_subnet_id,
server_status=None,
session=None): session=None):
result = (_server_get_query(context, session).filter_by(host=host) query = (_server_get_query(context, session)
.filter_by(share_network_subnet_id=share_subnet_id) .filter_by(host=host)
.filter(models.ShareServer.status.in_( .filter(models.ShareServer.share_network_subnets.any(
(constants.STATUS_CREATING, id=share_subnet_id)))
constants.STATUS_ACTIVE))).all()) if server_status:
query.filter_by(status=server_status)
else:
query.filter(models.ShareServer.status.in_(
(constants.STATUS_CREATING, constants.STATUS_ACTIVE)))
result = query.all()
if not result: if not result:
filters_description = ('share_network_subnet_id is "%(share_net_id)s",' filters_description = ('share_network_subnet_id is "%(share_net_id)s",'
' host is "%(host)s" and status in' ' host is "%(host)s" and status in'
@ -4423,9 +4484,13 @@ def share_server_get_all_with_filters(context, filters):
source_share_server_id=filters.get('source_share_server_id')) source_share_server_id=filters.get('source_share_server_id'))
if filters.get('share_network_id'): if filters.get('share_network_id'):
query = query.join( query = query.join(
models.ShareServerShareNetworkSubnetMapping,
models.ShareServerShareNetworkSubnetMapping.share_server_id ==
models.ShareServer.id
).join(
models.ShareNetworkSubnet, models.ShareNetworkSubnet,
models.ShareNetworkSubnet.id == models.ShareNetworkSubnet.id ==
models.ShareServer.share_network_subnet_id models.ShareServerShareNetworkSubnetMapping.share_network_subnet_id
).filter( ).filter(
models.ShareNetworkSubnet.share_network_id == models.ShareNetworkSubnet.share_network_id ==
filters.get('share_network_id')) filters.get('share_network_id'))
@ -4637,7 +4702,8 @@ def network_allocations_get_by_ip_address(context, ip_address):
@require_context @require_context
def network_allocations_get_for_share_server(context, share_server_id, def network_allocations_get_for_share_server(context, share_server_id,
session=None, label=None): session=None, label=None,
subnet_id=None):
if session is None: if session is None:
session = get_session() session = get_session()
@ -4655,6 +4721,9 @@ def network_allocations_get_for_share_server(context, share_server_id,
)) ))
else: else:
query = query.filter(models.NetworkAllocation.label == label) query = query.filter(models.NetworkAllocation.label == label)
if subnet_id:
query = query.filter(
models.NetworkAllocation.share_network_subnet_id == subnet_id)
result = query.all() result = query.all()
return result return result

View File

@ -967,6 +967,17 @@ class ShareNetwork(BASE, ManilaBase):
# set to True. # set to True.
return all(share_servers_support_updating) return all(share_servers_support_updating)
@property
def network_allocation_update_support(self):
share_servers_support_updating = []
for network_subnet in self.share_network_subnets:
for server in network_subnet['share_servers']:
share_servers_support_updating.append(
server['network_allocation_update_support'])
# NOTE(felipe_rodrigues): all share servers within this share network
# must support updating in order to have this property set to True.
return all(share_servers_support_updating)
class ShareNetworkSubnet(BASE, ManilaBase): class ShareNetworkSubnet(BASE, ManilaBase):
"""Represents a share network subnet used by some resources.""" """Represents a share network subnet used by some resources."""
@ -990,11 +1001,19 @@ class ShareNetworkSubnet(BASE, ManilaBase):
String(36), ForeignKey('availability_zones.id'), nullable=True) String(36), ForeignKey('availability_zones.id'), nullable=True)
share_servers = orm.relationship( share_servers = orm.relationship(
"ShareServer", backref='share_network_subnet', "ShareServer",
secondary="share_server_share_network_subnet_mappings",
backref="share_network_subnets",
lazy='immediate', lazy='immediate',
primaryjoin='and_(ShareNetworkSubnet.id ' primaryjoin="and_(ShareNetworkSubnet.id == "
'== ShareServer.share_network_subnet_id,' "%(cls_name)s.share_network_subnet_id, "
'ShareServer.deleted == "False")') "%(cls_name)s.deleted == 0)" % {
"cls_name": "ShareServerShareNetworkSubnetMapping"},
secondaryjoin='and_('
'ShareServer.id == '
'ShareServerShareNetworkSubnetMapping.share_server_id,'
'ShareServerShareNetworkSubnetMapping.deleted == 0)'
)
_availability_zone = orm.relationship( _availability_zone = orm.relationship(
"AvailabilityZone", "AvailabilityZone",
@ -1024,9 +1043,6 @@ class ShareServer(BASE, ManilaBase):
__tablename__ = 'share_servers' __tablename__ = 'share_servers'
id = Column(String(36), primary_key=True, nullable=False) id = Column(String(36), primary_key=True, nullable=False)
deleted = Column(String(36), default='False') deleted = Column(String(36), default='False')
share_network_subnet_id = Column(
String(36), ForeignKey('share_network_subnets.id'),
nullable=True)
host = Column(String(255), nullable=False) host = Column(String(255), nullable=False)
is_auto_deletable = Column(Boolean, default=True) is_auto_deletable = Column(Boolean, default=True)
identifier = Column(String(255), nullable=True) identifier = Column(String(255), nullable=True)
@ -1035,6 +1051,8 @@ class ShareServer(BASE, ManilaBase):
nullable=True) nullable=True)
security_service_update_support = Column( security_service_update_support = Column(
Boolean, nullable=False, default=False) Boolean, nullable=False, default=False)
network_allocation_update_support = Column(
Boolean, nullable=False, default=False)
status = Column(Enum( status = Column(Enum(
constants.STATUS_INACTIVE, constants.STATUS_ACTIVE, constants.STATUS_INACTIVE, constants.STATUS_ACTIVE,
constants.STATUS_ERROR, constants.STATUS_DELETING, constants.STATUS_ERROR, constants.STATUS_DELETING,
@ -1071,12 +1089,31 @@ class ShareServer(BASE, ManilaBase):
'ShareServerBackendDetails.share_server_id, ' 'ShareServerBackendDetails.share_server_id, '
'ShareServerBackendDetails.deleted == "False")') 'ShareServerBackendDetails.deleted == "False")')
_share_network_subnet_ids = orm.relationship(
"ShareServerShareNetworkSubnetMapping",
lazy='immediate',
viewonly=True,
primaryjoin='and_('
'ShareServer.id == '
'ShareServerShareNetworkSubnetMapping.share_server_id,'
'ShareServerShareNetworkSubnetMapping.deleted == 0)')
@property @property
def backend_details(self): def backend_details(self):
return {model['key']: model['value'] return {model['key']: model['value']
for model in self._backend_details} for model in self._backend_details}
_extra_keys = ['backend_details'] @property
def share_network_subnet_ids(self):
return [model['share_network_subnet_id']
for model in self._share_network_subnet_ids]
@property
def share_network_id(self):
return (self.share_network_subnets[0]['share_network_id']
if self.share_network_subnets else None)
_extra_keys = ['backend_details', 'share_network_subnet_ids']
class ShareServerBackendDetails(BASE, ManilaBase): class ShareServerBackendDetails(BASE, ManilaBase):
@ -1090,6 +1127,16 @@ class ShareServerBackendDetails(BASE, ManilaBase):
nullable=False) nullable=False)
class ShareServerShareNetworkSubnetMapping(BASE, ManilaBase):
"""Represents the Share Server and Share Network Subnet mapping."""
__tablename__ = 'share_server_share_network_subnet_mappings'
id = Column(Integer, primary_key=True)
share_server_id = Column(
String(36), ForeignKey('share_servers.id'), nullable=False)
share_network_subnet_id = Column(
String(36), ForeignKey('share_network_subnets.id'), nullable=False)
class ShareNetworkSecurityServiceAssociation(BASE, ManilaBase): class ShareNetworkSecurityServiceAssociation(BASE, ManilaBase):
"""Association table between compute_zones and compute_nodes tables.""" """Association table between compute_zones and compute_nodes tables."""
@ -1121,6 +1168,10 @@ class NetworkAllocation(BASE, ManilaBase):
share_server_id = Column(String(36), ForeignKey('share_servers.id'), share_server_id = Column(String(36), ForeignKey('share_servers.id'),
nullable=False) nullable=False)
# NOTE(felipe_rodrigues): admin allocation does not have subnet.
share_network_subnet_id = Column(
String(36), ForeignKey('share_network_subnets.id'), nullable=True)
class DriverPrivateData(BASE, ManilaBase): class DriverPrivateData(BASE, ManilaBase):
"""Represents a private data as key-value pairs for a driver.""" """Represents a private data as key-value pairs for a driver."""

View File

@ -232,6 +232,11 @@ class ShareNetworkSubnetNotFound(NotFound):
" found.") " found.")
class ShareNetworkSubnetNotFoundByShareServer(NotFound):
message = _("Share network subnet could not be found by "
"%(share_server_id)s.")
class ShareServerNotFound(NotFound): class ShareServerNotFound(NotFound):
message = _("Share server %(share_server_id)s could not be found.") message = _("Share server %(share_server_id)s could not be found.")
@ -241,6 +246,11 @@ class ShareServerNotFoundByFilters(ShareServerNotFound):
"filters: %(filters_description)s.") "filters: %(filters_description)s.")
class AllocationsNotFoundForShareServer(NotFound):
message = _("No allocations found for the share server "
"%(share_server_id)s on the subnet.")
class InvalidShareNetwork(Invalid): class InvalidShareNetwork(Invalid):
message = _("Invalid share network: %(reason)s") message = _("Invalid share network: %(reason)s")

View File

@ -134,3 +134,8 @@ class NetworkBaseAPI(db_base.Base, metaclass=abc.ABCMeta):
"should be configured to 'True'.") "should be configured to 'True'.")
raise exception.NetworkBadConfigurationException(reason=msg) raise exception.NetworkBadConfigurationException(reason=msg)
return self._enabled_ip_versions return self._enabled_ip_versions
@abc.abstractmethod
def include_network_info(self, share_network_subnet):
"""Includes share-network-subnet with plugin specific data."""
pass

View File

@ -121,9 +121,16 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
**self._neutron_api_kwargs) **self._neutron_api_kwargs)
return self._neutron_api return self._neutron_api
def _store_neutron_net_info(self, context, share_network_subnet): def include_network_info(self, share_network_subnet):
self._save_neutron_network_data(context, share_network_subnet) """Includes share-network-subnet with plugin specific data."""
self._save_neutron_subnet_data(context, share_network_subnet) self._store_neutron_net_info(None, share_network_subnet, save_db=False)
def _store_neutron_net_info(self, context, share_network_subnet,
save_db=True):
self._save_neutron_network_data(context, share_network_subnet,
save_db=save_db)
self._save_neutron_subnet_data(context, share_network_subnet,
save_db=save_db)
def allocate_network(self, context, share_server, share_network=None, def allocate_network(self, context, share_server, share_network=None,
share_network_subnet=None, **kwargs): share_network_subnet=None, **kwargs):
@ -202,6 +209,11 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
'cidr': share_network_subnet['cidr'], 'cidr': share_network_subnet['cidr'],
'mtu': share_network_subnet['mtu'], 'mtu': share_network_subnet['mtu'],
} }
# NOTE(felipe_rodrigues): admin plugin does not have any Manila
# share net subnet, its data is from manila configuration file.
if self.label != 'admin':
port_dict['share_network_subnet_id'] = (
share_network_subnet['id'])
# There should not be existing allocations with the same port_id. # There should not be existing allocations with the same port_id.
try: try:
@ -332,6 +344,12 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
'cidr': share_network_subnet['cidr'], 'cidr': share_network_subnet['cidr'],
'mtu': share_network_subnet['mtu'], 'mtu': share_network_subnet['mtu'],
} }
# NOTE(felipe_rodrigues): admin plugin does not have any Manila
# share net subnet, its data is from manila configuration file.
if self.label != 'admin':
port_dict['share_network_subnet_id'] = (
share_network_subnet['id'])
return self.db.network_allocation_create(context, port_dict) return self.db.network_allocation_create(context, port_dict)
def _delete_port(self, context, port): def _delete_port(self, context, port):
@ -354,7 +372,8 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
share_network_subnet['neutron_net_id']) share_network_subnet['neutron_net_id'])
return 'segments' in net_info return 'segments' in net_info
def _save_neutron_network_data(self, context, share_network_subnet): def _save_neutron_network_data(self, context, share_network_subnet,
save_db=True):
net_info = self.neutron_api.get_network( net_info = self.neutron_api.get_network(
share_network_subnet['neutron_net_id']) share_network_subnet['neutron_net_id'])
segmentation_id = None segmentation_id = None
@ -389,11 +408,12 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
} }
share_network_subnet.update(provider_nw_dict) share_network_subnet.update(provider_nw_dict)
if self.label != 'admin': if self.label != 'admin' and save_db:
self.db.share_network_subnet_update( self.db.share_network_subnet_update(
context, share_network_subnet['id'], provider_nw_dict) context, share_network_subnet['id'], provider_nw_dict)
def _save_neutron_subnet_data(self, context, share_network_subnet): def _save_neutron_subnet_data(self, context, share_network_subnet,
save_db=True):
subnet_info = self.neutron_api.get_subnet( subnet_info = self.neutron_api.get_subnet(
share_network_subnet['neutron_subnet_id']) share_network_subnet['neutron_subnet_id'])
@ -404,7 +424,7 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
} }
share_network_subnet.update(subnet_values) share_network_subnet.update(subnet_values)
if self.label != 'admin': if self.label != 'admin' and save_db:
self.db.share_network_subnet_update( self.db.share_network_subnet_update(
context, share_network_subnet['id'], subnet_values) context, share_network_subnet['id'], subnet_values)
@ -576,7 +596,8 @@ class NeutronBindNetworkPlugin(NeutronNetworkPlugin):
"local_link_information": local_links} "local_link_information": local_links}
return arguments return arguments
def _save_neutron_network_data(self, context, share_network_subnet): def _save_neutron_network_data(self, context, share_network_subnet,
save_db=True):
"""Store the Neutron network info. """Store the Neutron network info.
In case of dynamic multi segments the segment is determined while In case of dynamic multi segments the segment is determined while
@ -589,12 +610,14 @@ class NeutronBindNetworkPlugin(NeutronNetworkPlugin):
if self._is_neutron_multi_segment(share_network_subnet): if self._is_neutron_multi_segment(share_network_subnet):
# In case of dynamic multi segment the segment is determined while # In case of dynamic multi segment the segment is determined while
# binding the port, only mtu is known and already needed # binding the port, only mtu is known and already needed
self._save_neutron_network_mtu(context, share_network_subnet) self._save_neutron_network_mtu(context, share_network_subnet,
save_db=save_db)
return return
super(NeutronBindNetworkPlugin, self)._save_neutron_network_data( super(NeutronBindNetworkPlugin, self)._save_neutron_network_data(
context, share_network_subnet) context, share_network_subnet, save_db=save_db)
def _save_neutron_network_mtu(self, context, share_network_subnet): def _save_neutron_network_mtu(self, context, share_network_subnet,
save_db=True):
"""Store the Neutron network mtu. """Store the Neutron network mtu.
In case of dynamic multi segments only the mtu needs storing before In case of dynamic multi segments only the mtu needs storing before
@ -608,7 +631,7 @@ class NeutronBindNetworkPlugin(NeutronNetworkPlugin):
} }
share_network_subnet.update(mtu_dict) share_network_subnet.update(mtu_dict)
if self.label != 'admin': if self.label != 'admin' and save_db:
self.db.share_network_subnet_update( self.db.share_network_subnet_update(
context, share_network_subnet['id'], mtu_dict) context, share_network_subnet['id'], mtu_dict)

View File

@ -257,7 +257,11 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
'available': len(ips)} 'available': len(ips)}
raise exception.NetworkBadConfigurationException(reason=msg) raise exception.NetworkBadConfigurationException(reason=msg)
def _save_network_info(self, context, share_network_subnet): def include_network_info(self, share_network_subnet):
"""Includes share-network-subnet with plugin specific data."""
self._save_network_info(None, share_network_subnet, save_db=False)
def _save_network_info(self, context, share_network_subnet, save_db=True):
"""Update share-network-subnet with plugin specific data.""" """Update share-network-subnet with plugin specific data."""
data = { data = {
'network_type': self.network_type, 'network_type': self.network_type,
@ -268,7 +272,7 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
'mtu': self.mtu, 'mtu': self.mtu,
} }
share_network_subnet.update(data) share_network_subnet.update(data)
if self.label != 'admin': if self.label != 'admin' and save_db:
self.db.share_network_subnet_update( self.db.share_network_subnet_update(
context, share_network_subnet['id'], data) context, share_network_subnet['id'], data)
@ -303,6 +307,9 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
'ip_version': share_network_subnet['ip_version'], 'ip_version': share_network_subnet['ip_version'],
'mtu': share_network_subnet['mtu'], 'mtu': share_network_subnet['mtu'],
} }
if self.label != 'admin':
data['share_network_subnet_id'] = (
share_network_subnet['id'])
allocations.append( allocations.append(
self.db.network_allocation_create(context, data)) self.db.network_allocation_create(context, data))
return allocations return allocations
@ -354,6 +361,9 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
'ip_version': share_network_subnet['ip_version'], 'ip_version': share_network_subnet['ip_version'],
'mtu': share_network_subnet['mtu'], 'mtu': share_network_subnet['mtu'],
} }
if self.label != 'admin':
data['share_network_subnet_id'] = (
share_network_subnet['id'])
self.db.network_allocation_create(context, data) self.db.network_allocation_create(context, data)
remaining_allocations.remove(allocation) remaining_allocations.remove(allocation)

View File

@ -99,6 +99,11 @@ deprecated_share_network_reset_status = policy.DeprecatedRule(
deprecated_reason=DEPRECATED_REASON, deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY deprecated_since=versionutils.deprecated.WALLABY
) )
deprecated_share_network_subnet_create_check = policy.DeprecatedRule(
name=BASE_POLICY_NAME % 'subnet_create_check',
check_str=base.RULE_DEFAULT
)
share_network_policies = [ share_network_policies = [
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
@ -284,6 +289,20 @@ share_network_policies = [
], ],
deprecated_rule=deprecated_share_network_get_all deprecated_rule=deprecated_share_network_get_all
), ),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'subnet_create_check',
check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,
scope_types=['system', 'project'],
description="Check the feasibility of create a new share network "
"subnet for share network.",
operations=[
{
'method': 'POST',
'path': '/share-networks/{share_network_id}/action'
}
],
deprecated_rule=deprecated_share_network_subnet_create_check
),
] ]

View File

@ -27,9 +27,13 @@ class AvailabilityZoneFilter(base_host.BaseHostFilter):
props = spec.get('resource_properties', {}) props = spec.get('resource_properties', {})
request_az_id = props.get('availability_zone_id', request_az_id = props.get('availability_zone_id',
spec.get('availability_zone_id')) spec.get('availability_zone_id'))
az_request_multiple_subnet_support_map = spec.get(
'az_request_multiple_subnet_support_map', {})
request_azs = spec.get('availability_zones') request_azs = spec.get('availability_zones')
host_az_id = host_state.service['availability_zone_id'] host_az_id = host_state.service['availability_zone_id']
host_az = host_state.service['availability_zone']['name'] host_az = host_state.service['availability_zone']['name']
host_single_subnet_only = (
not host_state.share_server_multiple_subnet_support)
host_satisfied = True host_satisfied = True
if request_az_id is not None: if request_az_id is not None:
@ -38,4 +42,15 @@ class AvailabilityZoneFilter(base_host.BaseHostFilter):
if request_azs: if request_azs:
host_satisfied = host_satisfied and host_az in request_azs host_satisfied = host_satisfied and host_az in request_azs
# Only validates the multiple subnet support in case it can deny the
# host:
# 1. host is satisfying the AZ
# 2. There is a map to be checked
# 3. The host does not support a multiple subnet
if (host_satisfied and az_request_multiple_subnet_support_map and
host_single_subnet_only):
host_satisfied = (
not az_request_multiple_subnet_support_map.get(host_az_id,
False))
return host_satisfied return host_satisfied

View File

@ -151,6 +151,8 @@ class HostState(object):
self.ipv4_support = None self.ipv4_support = None
self.ipv6_support = None self.ipv6_support = None
self.security_service_update_support = False self.security_service_update_support = False
self.network_allocation_update_support = False
self.share_server_multiple_subnet_support = False
# PoolState for all pools # PoolState for all pools
self.pools = {} self.pools = {}
@ -346,6 +348,14 @@ class HostState(object):
pool_cap['security_service_update_support'] = ( pool_cap['security_service_update_support'] = (
self.security_service_update_support) self.security_service_update_support)
if 'network_allocation_update_support' not in pool_cap:
pool_cap['network_allocation_update_support'] = (
self.network_allocation_update_support)
if 'share_server_multiple_subnet_support' not in pool_cap:
pool_cap['share_server_multiple_subnet_support'] = (
self.share_server_multiple_subnet_support)
if self.ipv4_support is not None: if self.ipv4_support is not None:
pool_cap['ipv4_support'] = self.ipv4_support pool_cap['ipv4_support'] = self.ipv4_support
@ -377,6 +387,10 @@ class HostState(object):
self.ipv6_support = capability['ipv6_support'] self.ipv6_support = capability['ipv6_support']
self.security_service_update_support = capability.get( self.security_service_update_support = capability.get(
'security_service_update_support', False) 'security_service_update_support', False)
self.network_allocation_update_support = capability.get(
'network_allocation_update_support', False)
self.share_server_multiple_subnet_support = capability.get(
'share_server_multiple_subnet_support', False)
def consume_from_share(self, share): def consume_from_share(self, share):
"""Incrementally update host state from an share.""" """Incrementally update host state from an share."""
@ -477,6 +491,10 @@ class PoolState(HostState):
'sg_consistent_snapshot_support') 'sg_consistent_snapshot_support')
self.security_service_update_support = capability.get( self.security_service_update_support = capability.get(
'security_service_update_support', False) 'security_service_update_support', False)
self.network_allocation_update_support = capability.get(
'network_allocation_update_support', False)
self.share_server_multiple_subnet_support = capability.get(
'share_server_multiple_subnet_support', False)
def update_pools(self, capability): def update_pools(self, capability):
# Do nothing, since we don't have pools within pool, yet # Do nothing, since we don't have pools within pool, yet

View File

@ -60,7 +60,11 @@ def generate_stats(host_state, properties):
'ipv4_support': host_state.ipv4_support, 'ipv4_support': host_state.ipv4_support,
'ipv6_support': host_state.ipv6_support, 'ipv6_support': host_state.ipv6_support,
'security_service_update_support': ( 'security_service_update_support': (
host_state.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 host_caps = host_state.capabilities

View File

@ -93,6 +93,30 @@ def locked_security_service_update_operation(operation):
return wrapped return wrapped
def locked_share_server_update_allocations_operation(operation):
"""Lock decorator for share server update allocations operation.
Takes a named lock prior to executing the operation. The lock is named with
the ids of the share network and the region to be updated.
"""
def wrapped(*args, **kwargs):
az_id = kwargs.get('availability_zone_id')
share_net_id = kwargs.get('share_network_id')
@coordination.synchronized(
'locked-share-server-update-allocations-operation-%(net)s-%(az)s'
% {
'net': share_net_id,
'az': az_id,
})
def locked_share_server_allocations_operation(*_args, **_kwargs):
return operation(*_args, **_kwargs)
return locked_share_server_allocations_operation(*args, **kwargs)
return wrapped
class API(base.Base): class API(base.Base):
"""API for interacting with the share manager.""" """API for interacting with the share manager."""
@ -105,13 +129,17 @@ class API(base.Base):
def _get_all_availability_zones_with_subnets(self, context, def _get_all_availability_zones_with_subnets(self, context,
share_network_id): share_network_id):
compatible_azs = [] compatible_azs_name = []
compatible_azs_multiple = {}
for az in self.db.availability_zone_get_all(context): for az in self.db.availability_zone_get_all(context):
if self.db.share_network_subnet_get_by_availability_zone_id( subnets = (
self.db.share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id=share_network_id, context, share_network_id=share_network_id,
availability_zone_id=az['id']): availability_zone_id=az['id']))
compatible_azs.append(az['name']) if subnets:
return compatible_azs compatible_azs_multiple[az['id']] = len(subnets) > 1
compatible_azs_name.append(az['name'])
return compatible_azs_name, compatible_azs_multiple
def _check_if_share_quotas_exceeded(self, context, quota_exception, def _check_if_share_quotas_exceeded(self, context, quota_exception,
share_size, operation='create'): share_size, operation='create'):
@ -189,7 +217,8 @@ class API(base.Base):
snapshot_id=None, availability_zone=None, metadata=None, snapshot_id=None, availability_zone=None, metadata=None,
share_network_id=None, share_type=None, is_public=False, share_network_id=None, share_type=None, is_public=False,
share_group_id=None, share_group_snapshot_member=None, share_group_id=None, share_group_snapshot_member=None,
availability_zones=None, scheduler_hints=None): availability_zones=None, scheduler_hints=None,
az_request_multiple_subnet_support_map=None):
"""Create new share.""" """Create new share."""
self._check_metadata_properties(metadata) self._check_metadata_properties(metadata)
@ -345,13 +374,15 @@ class API(base.Base):
# scheduler will receive a list with all availability zones that # scheduler will receive a list with all availability zones that
# contains a subnet within the selected share network. # contains a subnet within the selected share network.
if share_network_id and not availability_zone: if share_network_id and not availability_zone:
azs_with_subnet = self._get_all_availability_zones_with_subnets( compatible_azs_name, compatible_azs_multiple = (
context, share_network_id) self._get_all_availability_zones_with_subnets(
context, share_network_id))
if not availability_zones: if not availability_zones:
availability_zones = azs_with_subnet availability_zones = compatible_azs_name
else: else:
availability_zones = ( availability_zones = (
[az for az in availability_zones if az in azs_with_subnet]) [az for az in availability_zones
if az in compatible_azs_name])
if not availability_zones: if not availability_zones:
msg = _( msg = _(
"The share network is not supported within any requested " "The share network is not supported within any requested "
@ -359,6 +390,12 @@ class API(base.Base):
"'availability_zones' extra-spec and the availability " "'availability_zones' extra-spec and the availability "
"zones of the share network subnets") "zones of the share network subnets")
raise exception.InvalidInput(message=msg) raise exception.InvalidInput(message=msg)
if az_request_multiple_subnet_support_map:
az_request_multiple_subnet_support_map.update(
compatible_azs_multiple)
else:
az_request_multiple_subnet_support_map = (
compatible_azs_multiple)
try: try:
share = self.db.share_create(context, options, share = self.db.share_create(context, options,
@ -391,7 +428,9 @@ class API(base.Base):
availability_zone=availability_zone, share_group=share_group, availability_zone=availability_zone, share_group=share_group,
share_group_snapshot_member=share_group_snapshot_member, share_group_snapshot_member=share_group_snapshot_member,
share_type_id=share_type_id, availability_zones=availability_zones, share_type_id=share_type_id, availability_zones=availability_zones,
snapshot_host=snapshot_host, scheduler_hints=scheduler_hints) snapshot_host=snapshot_host, scheduler_hints=scheduler_hints,
az_request_multiple_subnet_support_map=(
az_request_multiple_subnet_support_map))
# Retrieve the share with instance details # Retrieve the share with instance details
share = self.db.share_get(context, share['id']) share = self.db.share_get(context, share['id'])
@ -468,7 +507,8 @@ class API(base.Base):
host=None, availability_zone=None, host=None, availability_zone=None,
share_group=None, share_group_snapshot_member=None, share_group=None, share_group_snapshot_member=None,
share_type_id=None, availability_zones=None, share_type_id=None, availability_zones=None,
snapshot_host=None, scheduler_hints=None): snapshot_host=None, scheduler_hints=None,
az_request_multiple_subnet_support_map=None):
request_spec, share_instance = ( request_spec, share_instance = (
self.create_share_instance_and_get_request_spec( self.create_share_instance_and_get_request_spec(
context, share, availability_zone=availability_zone, context, share, availability_zone=availability_zone,
@ -476,7 +516,9 @@ class API(base.Base):
share_network_id=share_network_id, share_network_id=share_network_id,
share_type_id=share_type_id, share_type_id=share_type_id,
availability_zones=availability_zones, availability_zones=availability_zones,
snapshot_host=snapshot_host)) snapshot_host=snapshot_host,
az_request_multiple_subnet_support_map=(
az_request_multiple_subnet_support_map)))
if share_group_snapshot_member: if share_group_snapshot_member:
# Inherit properties from the share_group_snapshot_member # Inherit properties from the share_group_snapshot_member
@ -518,7 +560,8 @@ class API(base.Base):
self, context, share, availability_zone=None, self, context, share, availability_zone=None,
share_group=None, host=None, share_network_id=None, share_group=None, host=None, share_network_id=None,
share_type_id=None, cast_rules_to_readonly=False, share_type_id=None, cast_rules_to_readonly=False,
availability_zones=None, snapshot_host=None): availability_zones=None, snapshot_host=None,
az_request_multiple_subnet_support_map=None):
availability_zone_id = None availability_zone_id = None
if availability_zone: if availability_zone:
@ -590,6 +633,8 @@ class API(base.Base):
'share_group': share_group, 'share_group': share_group,
'availability_zone_id': availability_zone_id, 'availability_zone_id': availability_zone_id,
'availability_zones': availability_zones, 'availability_zones': availability_zones,
'az_request_multiple_subnet_support_map': (
az_request_multiple_subnet_support_map),
} }
return request_spec, share_instance return request_spec, share_instance
@ -643,6 +688,7 @@ class API(base.Base):
except exception.OverQuota as e: except exception.OverQuota as e:
self._check_if_replica_quotas_exceeded(context, e, share['size']) self._check_if_replica_quotas_exceeded(context, e, share['size'])
az_request_multiple_subnet_support_map = {}
if share_network_id: if share_network_id:
if availability_zone: if availability_zone:
try: try:
@ -652,24 +698,31 @@ class API(base.Base):
msg = _("Share replica cannot be created because the " msg = _("Share replica cannot be created because the "
"specified availability zone does not exist.") "specified availability zone does not exist.")
raise exception.InvalidInput(message=msg) raise exception.InvalidInput(message=msg)
if self.db.share_network_subnet_get_by_availability_zone_id( az_id = az.get('id')
context, share_network_id, az.get('id')) is None: subnets = (
self.db.
share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id, az_id))
if not subnets:
msg = _("Share replica cannot be created because the " msg = _("Share replica cannot be created because the "
"share network is not available within the " "share network is not available within the "
"specified availability zone.") "specified availability zone.")
raise exception.InvalidShare(message=msg) raise exception.InvalidShare(message=msg)
az_request_multiple_subnet_support_map[az_id] = (
len(subnets) > 1)
else: else:
# NOTE(dviroel): If a target availability zone was not # NOTE(dviroel): If a target availability zone was not
# provided, the scheduler will receive a list with all # provided, the scheduler will receive a list with all
# availability zones that contains subnets within the # availability zones that contains subnets within the
# selected share network. # selected share network.
azs_subnet = self._get_all_availability_zones_with_subnets( compatible_azs_name, compatible_azs_multiple = (
context, share_network_id) self._get_all_availability_zones_with_subnets(
context, share_network_id))
if not type_azs: if not type_azs:
type_azs = azs_subnet type_azs = compatible_azs_name
else: else:
type_azs = ( type_azs = (
[az for az in type_azs if az in azs_subnet]) [az for az in type_azs if az in compatible_azs_name])
if not type_azs: if not type_azs:
msg = _( msg = _(
"The share network is not supported within any " "The share network is not supported within any "
@ -677,6 +730,8 @@ class API(base.Base):
"'availability_zones' extra-spec and the availability " "'availability_zones' extra-spec and the availability "
"zones of the share network subnets") "zones of the share network subnets")
raise exception.InvalidInput(message=msg) raise exception.InvalidInput(message=msg)
az_request_multiple_subnet_support_map.update(
compatible_azs_multiple)
if share['replication_type'] == constants.REPLICATION_TYPE_READABLE: if share['replication_type'] == constants.REPLICATION_TYPE_READABLE:
cast_rules_to_readonly = True cast_rules_to_readonly = True
@ -690,7 +745,9 @@ class API(base.Base):
share_network_id=share_network_id, share_network_id=share_network_id,
share_type_id=share['instance']['share_type_id'], share_type_id=share['instance']['share_type_id'],
cast_rules_to_readonly=cast_rules_to_readonly, cast_rules_to_readonly=cast_rules_to_readonly,
availability_zones=type_azs) availability_zones=type_azs,
az_request_multiple_subnet_support_map=(
az_request_multiple_subnet_support_map))
) )
QUOTAS.commit( QUOTAS.commit(
context, reservations, project_id=share['project_id'], context, reservations, project_id=share['project_id'],
@ -860,9 +917,8 @@ class API(base.Base):
if share_server['status'] != constants.STATUS_ACTIVE: if share_server['status'] != constants.STATUS_ACTIVE:
msg = _("The provided share server is not active.") msg = _("The provided share server is not active.")
raise exception.InvalidShareServer(reason=msg) raise exception.InvalidShareServer(reason=msg)
subnet = self.db.share_network_subnet_get( share_data['share_network_id'] = (
context, share_server['share_network_subnet_id']) share_server['share_network_id'])
share_data['share_network_id'] = subnet['share_network_id']
try: try:
share_network = self.db.share_network_get( share_network = self.db.share_network_get(
@ -1322,7 +1378,7 @@ class API(base.Base):
values = { values = {
'host': host, 'host': host,
'share_network_subnet_id': share_net_subnet['id'], 'share_network_subnets': [share_net_subnet],
'status': constants.STATUS_MANAGING, 'status': constants.STATUS_MANAGING,
'is_auto_deletable': False, 'is_auto_deletable': False,
'identifier': identifier, 'identifier': identifier,
@ -2659,11 +2715,11 @@ class API(base.Base):
# we should deny this operation. # we should deny this operation.
dest_az = self.db.availability_zone_get( dest_az = self.db.availability_zone_get(
context, service['availability_zone']['name']) context, service['availability_zone']['name'])
compatible_subnet = ( compatible_subnets = (
self.db.share_network_subnet_get_by_availability_zone_id( self.db.share_network_subnets_get_all_by_availability_zone_id(
context, new_share_network_id, dest_az['id'])) context, new_share_network_id, dest_az['id']))
if not compatible_subnet: if not compatible_subnets:
msg = _("The share network %(network)s does not have a subnet " msg = _("The share network %(network)s does not have a subnet "
"that spans the destination host availability zone.") "that spans the destination host availability zone.")
payload = {'network': new_share_network_id} payload = {'network': new_share_network_id}
@ -2671,11 +2727,8 @@ class API(base.Base):
net_changes_identified = False net_changes_identified = False
if new_share_network: if new_share_network:
current_subnet = self.db.share_network_subnet_get( net_changes_identified = not share_utils.is_az_subnets_compatible(
context, share_server['share_network_subnet_id']) share_server['share_network_subnets'], compatible_subnets)
for key in ['neutron_net_id', 'neutron_subnet_id']:
if current_subnet[key] != compatible_subnet[key]:
net_changes_identified = True
# NOTE(carloss): Refreshing the list of shares since something could've # NOTE(carloss): Refreshing the list of shares since something could've
# changed from the initial list. # changed from the initial list.
@ -3118,28 +3171,44 @@ class API(base.Base):
'hosts_check', new_security_service_id, 'hosts_check', new_security_service_id,
current_security_service_id=current_security_service_id) current_security_service_id=current_security_service_id)
# check if there is an entry being processed return self._do_update_validate_hosts(
context, share_network['id'], backend_hosts, update_key,
new_security_service_id=new_security_service_id,
current_security_service_id=current_security_service_id)
def _do_update_validate_hosts(
self, context, share_network_id,
backend_hosts, update_key, new_share_network_subnet=None,
new_security_service_id=None, current_security_service_id=None):
# check if there is an entry being processed.
update_value = self.db.async_operation_data_get( update_value = self.db.async_operation_data_get(
context, share_network['id'], update_key) context, share_network_id, update_key)
if not update_value: if not update_value:
# Create a new entry, send all asynchronous rpcs and return # Create a new entry, send all asynchronous rpcs and return.
hosts_to_validate = {} hosts_to_validate = {}
for host in backend_hosts: for host in backend_hosts:
hosts_to_validate[host] = None hosts_to_validate[host] = None
self.db.async_operation_data_update( self.db.async_operation_data_update(
context, share_network['id'], context, share_network_id,
{update_key: json.dumps(hosts_to_validate)}) {update_key: json.dumps(hosts_to_validate)})
for host in backend_hosts: for host in backend_hosts:
(self.share_rpcapi. if new_share_network_subnet:
check_update_share_network_security_service( (self.share_rpcapi.
context, host, share_network['id'], check_update_share_server_network_allocations(
new_security_service_id, context, host, share_network_id,
current_security_service_id=( new_share_network_subnet))
current_security_service_id))) else:
(self.share_rpcapi.
check_update_share_network_security_service(
context, host, share_network_id,
new_security_service_id,
current_security_service_id=(
current_security_service_id)))
return None, hosts_to_validate return None, hosts_to_validate
else: else:
# process current existing hosts and update them if needed # process current existing hosts and update them if needed.
current_hosts = json.loads(update_value) current_hosts = json.loads(update_value)
hosts_to_include = ( hosts_to_include = (
set(backend_hosts).difference(set(current_hosts.keys()))) set(backend_hosts).difference(set(current_hosts.keys())))
@ -3147,24 +3216,30 @@ class API(base.Base):
for host in backend_hosts: for host in backend_hosts:
hosts_to_validate[host] = current_hosts.get(host, None) hosts_to_validate[host] = current_hosts.get(host, None)
# Check if there is any unsupported host # Check if there is any unsupported host.
if any(hosts_to_validate[host] is False for host in backend_hosts): if any(hosts_to_validate[host] is False for host in backend_hosts):
return False, hosts_to_validate return False, hosts_to_validate
# Update the list of hosts to be validated # Update the list of hosts to be validated.
if hosts_to_include: if hosts_to_include:
self.db.async_operation_data_update( self.db.async_operation_data_update(
context, share_network['id'], context, share_network_id,
{update_key: json.dumps(hosts_to_validate)}) {update_key: json.dumps(hosts_to_validate)})
for host in hosts_to_include: for host in hosts_to_include:
# send asynchronous check only for new backend hosts # send asynchronous check only for new backend hosts.
(self.share_rpcapi. if new_share_network_subnet:
check_update_share_network_security_service( (self.share_rpcapi.
context, host, share_network['id'], check_update_share_server_network_allocations(
new_security_service_id, context, host, share_network_id,
current_security_service_id=( new_share_network_subnet))
current_security_service_id))) else:
(self.share_rpcapi.
check_update_share_network_security_service(
context, host, share_network_id,
new_security_service_id,
current_security_service_id=(
current_security_service_id)))
return None, hosts_to_validate return None, hosts_to_validate
@ -3190,7 +3265,6 @@ class API(base.Base):
curr_sec_serv_id = ( curr_sec_serv_id = (
current_security_service['id'] current_security_service['id']
if current_security_service else None) if current_security_service else None)
key = self.get_security_service_update_key( key = self.get_security_service_update_key(
'hosts_check', new_security_service['id'], 'hosts_check', new_security_service['id'],
current_security_service_id=curr_sec_serv_id) current_security_service_id=curr_sec_serv_id)
@ -3306,3 +3380,251 @@ class API(base.Base):
LOG.info('Security service update has been started for share network ' LOG.info('Security service update has been started for share network '
'%(share_net_id)s.', {'share_net_id': share_network['id']}) '%(share_net_id)s.', {'share_net_id': share_network['id']})
@locked_share_server_update_allocations_operation
def _share_server_update_allocations_validate_hosts(
self, context, backend_hosts, update_key, share_network_id=None,
neutron_net_id=None, neutron_subnet_id=None,
availability_zone_id=None):
new_share_network_subnet = {
'neutron_net_id': neutron_net_id,
'neutron_subnet_id': neutron_subnet_id,
'availability_zone_id': availability_zone_id,
}
return self._do_update_validate_hosts(
context, share_network_id, backend_hosts, update_key,
new_share_network_subnet=new_share_network_subnet)
def get_share_server_update_allocations_key(
self, share_network_id, availability_zone_id):
return ('share_server_update_allocations_' + share_network_id + '_' +
str(availability_zone_id) + '_' + 'hosts_check')
def _share_server_update_allocations_initial_checks(
self, context, share_network, share_servers):
api_common.check_share_network_is_active(share_network)
if not share_network['network_allocation_update_support']:
msg = _("Updating network allocations is not supported on this "
"share network (%(sn_id)s) while it has shares. "
"See the capability 'network_allocation_update_support'."
) % {"sn_id": share_network["id"]}
raise exception.InvalidShareNetwork(reason=msg)
backend_hosts = set()
for share_server in share_servers:
share_server_id = share_server['id']
if share_server['status'] != constants.STATUS_ACTIVE:
msg = _('The share server %(server)s in the specified '
'availability zone subnet is not currently '
'available.') % {'server': share_server_id}
raise exception.InvalidShareNetwork(reason=msg)
# We need an admin context to validate these hosts.
admin_ctx = manila_context.get_admin_context()
# Make sure the host is in the list of available hosts.
utils.validate_service_host(admin_ctx, share_server['host'])
# Create a set of backend hosts.
backend_hosts.add(share_server['host'])
shares = self.db.share_get_all_by_share_server(
context, share_server_id)
shares_not_available = [
share['id']
for share in shares if
share['status'] != constants.STATUS_AVAILABLE]
if shares_not_available:
msg = _("The share server (%(server_id)s) in the specified "
"availability zone subnet has some shares that are "
"not available: "
"%(share_ids)s.") % {
'server_id': share_server_id,
'share_ids': shares_not_available,
}
raise exception.InvalidShareNetwork(reason=msg)
shares_rules_not_available = [
share['id'] for share in shares if
share['instance'][
'access_rules_status'] != constants.STATUS_ACTIVE]
if shares_rules_not_available:
msg = _("The share server (%(server_id)s) in the specified "
"availability zone subnet has either these shares or "
"one of their replicas or migration copies that are "
"not available: %(share_ids)s.") % {
'server_id': share_server_id,
'share_ids': shares_rules_not_available,
}
raise exception.InvalidShareNetwork(reason=msg)
busy_shares = []
for share in shares:
try:
self._check_is_share_busy(share)
except exception.ShareBusyException:
busy_shares.append(share['id'])
if busy_shares:
msg = _("The share server (%(server_id)s) in the specified "
"availability zone subnet has some shares that are "
"busy as part of an active task: "
"%(share_ids)s.") % {
'server_id': share_server_id,
'share_ids': busy_shares,
}
raise exception.InvalidShareNetwork(reason=msg)
return backend_hosts
def check_update_share_server_network_allocations(
self, context, share_network, new_share_network_subnet,
reset_operation):
backend_hosts = self._share_server_update_allocations_initial_checks(
context, share_network, new_share_network_subnet['share_servers'])
update_key = self.get_share_server_update_allocations_key(
share_network['id'],
new_share_network_subnet['availability_zone_id'])
if reset_operation:
self.db.async_operation_data_delete(context, share_network['id'],
update_key)
try:
compatible, hosts_info = (
self._share_server_update_allocations_validate_hosts(
context, backend_hosts, update_key,
share_network_id=share_network['id'],
neutron_net_id=(
new_share_network_subnet.get('neutron_net_id')),
neutron_subnet_id=(
new_share_network_subnet.get('neutron_subnet_id')),
availability_zone_id=new_share_network_subnet.get(
"availability_zone_id")))
except Exception as e:
LOG.exception(e)
# Due to an internal error, we will delete the entry.
self.db.async_operation_data_delete(
context, share_network['id'], update_key)
msg = _(
"The server's allocations cannot be updated on availability "
"zone %(zone_id)s of the share network %(share_net_id)s, "
"since at least one of its backend hosts do not support this "
"operation.") % {
'share_net_id': share_network['id'],
'zone_id': new_share_network_subnet['availability_zone_id']}
raise exception.InvalidShareNetwork(reason=msg)
return {
'compatible': compatible,
'hosts_check_result': hosts_info
}
def update_share_server_network_allocations(
self, context, share_network, new_share_network_subnet):
backend_hosts = self._share_server_update_allocations_initial_checks(
context, share_network, new_share_network_subnet['share_servers'])
update_key = self.get_share_server_update_allocations_key(
share_network['id'],
new_share_network_subnet['availability_zone_id'])
# check if there is an entry being processed at this moment.
update_value = self.db.async_operation_data_get(
context, share_network['id'], update_key)
if not update_value:
msg = _(
'The share network %(share_net_id)s cannot start the update '
'process since no check operation was found. Before starting '
'the update operation, a "check" operation must be triggered '
'to validate if all backend hosts support the provided '
'configuration paramaters.') % {
'share_net_id': share_network['id']
}
raise exception.InvalidShareNetwork(reason=msg)
subnet_info = {
'availability_zone_id':
new_share_network_subnet.get("availability_zone_id"),
'neutron_net_id':
new_share_network_subnet.get('neutron_net_id'),
'neutron_subnet_id':
new_share_network_subnet.get('neutron_subnet_id'),
}
try:
result, __ = self._share_server_update_allocations_validate_hosts(
context, backend_hosts, update_key,
share_network_id=share_network['id'],
neutron_net_id=(
new_share_network_subnet.get('neutron_net_id')),
neutron_subnet_id=(
new_share_network_subnet.get('neutron_subnet_id')),
availability_zone_id=new_share_network_subnet.get(
"availability_zone_id"))
except Exception:
# Due to an internal error, we will delete the entry.
self.db.async_operation_data_delete(
context, share_network['id'], update_key)
msg = _(
"The server's allocations cannot be updated on availability "
"zone %(zone_id)s of the share network %(share_net_id)s, "
"since an internal error occurred."
"operation.") % {
'share_net_id': share_network['id'],
'zone_id': subnet_info['availability_zone_id']
}
raise exception.InvalidShareNetwork(reason=msg)
if result is False:
msg = _(
"The server's allocations cannot be updated on availability "
"zone %(zone_id)s of the share network %(share_net_id)s, "
"since at least one of its backend hosts do not support this "
"operation.") % {
'share_net_id': share_network['id'],
'zone_id': subnet_info['availability_zone_id']
}
raise exception.InvalidShareNetwork(reason=msg)
elif result is None:
msg = _(
'Not all of the validation has been completed yet. A '
'validation check is in progress. This operation can be '
'retried.')
raise exception.InvalidShareNetwork(reason=msg)
# change db to start the update.
self.db.share_network_update(
context, share_network['id'],
{'status': constants.STATUS_NETWORK_CHANGE})
share_servers_ids = [ss['id'] for ss in
new_share_network_subnet['share_servers']]
self.db.share_servers_update(
context, share_servers_ids,
{'status': constants.STATUS_SERVER_NETWORK_CHANGE})
# create the new subnet.
new_share_network_subnet_db = self.db.share_network_subnet_create(
context, new_share_network_subnet)
# triggering the actual update.
for backend_host in backend_hosts:
self.share_rpcapi.update_share_server_network_allocations(
context, backend_host, share_network['id'],
new_share_network_subnet_db['id'])
# Erase db entry, since we won't need it anymore.
self.db.async_operation_data_delete(
context, share_network['id'], update_key)
LOG.info('Share servers allocations update have been started for '
'share network %(share_net_id)s on its availability zone '
'%(az_id)s with new subnet %(subnet_id)s.',
{
'share_net_id': share_network['id'],
'az_id': new_share_network_subnet['availability_zone_id'],
'subnet_id': new_share_network_subnet_db['id'],
})
return new_share_network_subnet_db

View File

@ -277,6 +277,10 @@ class ShareDriver(object):
# in-use share networks. This property will be saved in every new share # in-use share networks. This property will be saved in every new share
# server. # server.
self.security_service_update_support = False self.security_service_update_support = False
# Indicates whether a driver supports adding subnet with its
# allocations to an in-use share network availability zone. This
# property will be saved in every new share server.
self.network_allocation_update_support = False
self.dhss_mandatory_security_service_association = {} self.dhss_mandatory_security_service_association = {}
self.pools = [] self.pools = []
@ -1323,6 +1327,9 @@ class ShareDriver(object):
goodness_function=self.get_goodness_function(), goodness_function=self.get_goodness_function(),
security_service_update_support=( security_service_update_support=(
self.security_service_update_support), self.security_service_update_support),
network_allocation_update_support=(
self.network_allocation_update_support),
share_server_multiple_subnet_support=False,
) )
if isinstance(data, dict): if isinstance(data, dict):
common.update(data) common.update(data)
@ -3341,3 +3348,265 @@ class ShareDriver(object):
otherwise. otherwise.
""" """
raise NotImplementedError() raise NotImplementedError()
def check_update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_share_network_subnet, security_services, share_instances,
share_instances_rules):
""""Check if the share server network allocation update is supported.
:param context: The 'context.RequestContext' object for the request.
:param share_server: Reference to the share server object that will be
updated.
:param current_network_allocations: All network allocations associated
with the share server that will be updated:
Example::
{
'admin_network_allocations':
[
{
'ip_address': '10.193.154.11',
'ip_version': 4,
'cidr': '10.193.154.0/28',
'gateway': '10.193.154.1',
'mtu': 1500,
'network_type': 'vlan',
'segmentation_id': 3000,
'mac_address': ' AA:AA:AA:AA:AA:AA',
...
},
],
'subnets':
[
{
'share_network_subnet_id': '0bdeaa8c6db3-3bc10d67',
'neutron_net_id': '2598-4122-bb62-0bdeaa8c6db3',
'neutron_subnet_id': '3bc10d67-2598-4122-bb62',
'network_allocations':
[
{
'ip_address': '10.193.154.10',
'ip_version': 4,
'cidr': '10.193.154.0/28',
'gateway': '10.193.154.1',
'mtu': 1500,
'network_type': 'vlan',
'segmentation_id': 3000,
'mac_address': ' AA:AA:AA:AA:AA:AA',
...
},
],
},
],
}
:param new_share_network_subnet: dict containing the subnet data that
has to be checked if it can be added to the share server:
Example::
{
'availability_zone_id': '0bdeaa8c6db3-3bc10d67',
'neutron_net_id': '2598-4122-bb62-0bdeaa8c6db3',
'neutron_subnet_id': '3bc10d67-2598-4122-bb62',
'ip_version': 4,
'cidr': '10.193.154.0/28',
'gateway': '10.193.154.1',
'mtu': 1500,
'network_type': 'vlan',
'segmentation_id': 3000,
}
:param security_services: list of security services configured with
this share server.
:param share_instances: A list of share instances that belong to the
share server that is affected by the update.
:param share_instances_rules: A list of access rules, grouped by share
instance, in the following format.
Example::
[
{
'share_instance_id': '3bc10d67-2598-4122-bb62-0bdeaa8c6db3',
'access_rules':
[
{
'access_id':'906d0094-3e34-4d6c-a184-d08a908033e3',
'access_type':'ip',
'access_key':None,
'access_to':'10.0.0.1',
'access_level':'rw'
...
},
],
},
]
:return Boolean indicating whether the update is possible or not. It is
the driver responsibility to log the reason why not accepting the
update.
"""
raise NotImplementedError()
def update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_network_allocations, security_services, shares, snapshots):
"""Updates a share server's network allocations.
:param context: The 'context.RequestContext' object for the request.
:param share_server: reference to the share server that have to update
network allocations.
:param current_network_allocations: all network allocations associated
with the share server that will be updated
Example::
{
'admin_network_allocations':
[
{
'ip_address': '10.193.154.11',
'ip_version': 4,
'cidr': '10.193.154.0/28',
'gateway': '10.193.154.1',
'mtu': 1500,
'network_type': 'vlan',
'segmentation_id': 3000,
'mac_address': ' AA:AA:AA:AA:AA:AA',
},
...
],
'subnets':
[
{
'share_network_subnet_id': '0bdeaa8c6db3-3bc10d67',
'neutron_net_id': '2598-4122-bb62-0bdeaa8c6db3',
'neutron_subnet_id': '3bc10d67-2598-4122-bb62',
'network_allocations':
[
{
'ip_address': '10.193.154.10',
'ip_version': 4,
'cidr': '10.193.154.0/28',
'gateway': '10.193.154.1',
'mtu': 1500,
'network_type': 'vlan',
'segmentation_id': 3000,
'mac_address': ' AA:AA:AA:AA:AA:AA',
},
...
],
},
],
}
:param new_network_allocations: allocations that must be configured in
the share server.
Example::
{
'share_network_subnet_id': '0bdeaa8c6db3-3bc10d67',
'neutron_net_id': '2598-4122-bb62-0bdeaa8c6db3',
'neutron_subnet_id': '3bc10d67-2598-4122-bb62',
'network_allocations':
[
{
'ip_address': '10.193.154.10',
'ip_version': 4,
'cidr': '10.193.154.0/28',
'gateway': '10.193.154.1',
'mtu': 1500,
'network_type': 'vlan',
'segmentation_id': 3000,
'mac_address': 'AA:AA:AA:AA:AA:AA',
...
},
],
},
:param security_services: list of security services configured with
this share server.
:param shares: All shares in the share server.
:param snapshots: All snapshots in the share server.
:raises: Exception.
By raising an exception, the share server and all its shares and
snapshots instances will be set to 'error'. The error can contain
the field 'details_data' as a dict with the key 'server_details'
containing the backend details dict that will be saved to share
server.
:return If the update changes the shares export locations or snapshots
export locations, this method should return a dictionary
containing a list of share instances and snapshot instances
indexed by their id's, where each instance should provide a
dict with the relevant information that need to be updated.
Also, the returned dict can contain the updated back end
details to be saved in the database.
Example::
{
'share_updates':
{
'4363eb92-23ca-4888-9e24-502387816e2a':
[
{
'path': '1.2.3.4:/foo',
'metadata': {},
'is_admin_only': False
},
{
'path': '5.6.7.8:/foo',
'metadata': {},
'is_admin_only': True
},
],
...
},
'snapshot_updates':
{
'bc4e3b28-0832-4168-b688-67fdc3e9d408':
{
'provider_location': '/snapshots/foo/bar_1',
'export_locations':
[
{
'path': '1.2.3.4:/snapshots/foo/bar_1',
'is_admin_only': False,
},
{
'path': '5.6.7.8:/snapshots/foo/bar_1',
'is_admin_only': True,
},
],
},
'2e62b7ea-4e30-445f-bc05-fd523ca62941':
{
'provider_location': '/snapshots/foo/bar_2',
'export_locations':
[
{
'path': '1.2.3.4:/snapshots/foo/bar_2',
'is_admin_only': False,
},
{
'path': '5.6.7.8:/snapshots/foo/bar_2',
'is_admin_only': True,
},
],
},
}
'server_details':
{
'new_share_server_info_key':
'new_share_server_info_value',
},
}
"""
raise NotImplementedError()

View File

@ -296,6 +296,8 @@ class ContainerShareDriver(driver.ShareDriver, driver.ExecuteMixin):
@utils.synchronized("veth-lock", external=True) @utils.synchronized("veth-lock", external=True)
def _setup_server(self, network_info, metadata=None): def _setup_server(self, network_info, metadata=None):
# NOTE(felipe_rodrigues): keep legacy network_info support as a dict.
network_info = network_info[0]
msg = "Creating share server '%s'." msg = "Creating share server '%s'."
server_id = self._get_container_name(network_info["server_id"]) server_id = self._get_container_name(network_info["server_id"])
LOG.debug(msg, server_id) LOG.debug(msg, server_id)

View File

@ -290,6 +290,9 @@ class EMCShareDriver(driver.ShareDriver):
def _setup_server(self, network_info, metadata=None): def _setup_server(self, network_info, metadata=None):
"""Set up and configures share server with given network parameters.""" """Set up and configures share server with given network parameters."""
# NOTE(felipe_rodrigues): keep legacy network_info support as a dict.
network_info = network_info[0]
return self.plugin.setup_server(network_info, metadata) return self.plugin.setup_server(network_info, metadata)
def _teardown_server(self, server_details, security_services=None): def _teardown_server(self, server_details, security_services=None):

View File

@ -892,6 +892,9 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
return 0 return 0
def _setup_server(self, network_info, metadata=None): def _setup_server(self, network_info, metadata=None):
# NOTE(felipe_rodrigues): keep legacy network_info support as a dict.
network_info = network_info[0]
msg = "Creating share server '%s'." msg = "Creating share server '%s'."
LOG.debug(msg, network_info['server_id']) LOG.debug(msg, network_info['server_id'])
server = self.service_instance_manager.set_up_service_instance( server = self.service_instance_manager.set_up_service_instance(

View File

@ -432,6 +432,8 @@ class HPE3ParShareDriver(driver.ShareDriver):
'vfs': vfs} 'vfs': vfs}
def _setup_server(self, network_info, metadata=None): def _setup_server(self, network_info, metadata=None):
# NOTE(felipe_rodrigues): keep legacy network_info support as a dict.
network_info = network_info[0]
LOG.debug("begin _setup_server with %s", network_info) LOG.debug("begin _setup_server with %s", network_info)

View File

@ -223,6 +223,9 @@ class HuaweiNasDriver(driver.ShareDriver):
def _setup_server(self, network_info, metadata=None): def _setup_server(self, network_info, metadata=None):
"""Set up share server with given network parameters.""" """Set up share server with given network parameters."""
# NOTE(felipe_rodrigues): keep legacy network_info support as a dict.
network_info = network_info[0]
return self.plugin.setup_server(network_info, metadata) return self.plugin.setup_server(network_info, metadata)
def _teardown_server(self, server_details, security_services=None): def _teardown_server(self, server_details, security_services=None):

View File

@ -135,6 +135,8 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
self.admin_network_api) self.admin_network_api)
def _setup_server(self, network_info, metadata=None): def _setup_server(self, network_info, metadata=None):
# NOTE(felipe_rodrigues): keep legacy network_info support as a dict.
network_info = network_info[0]
return self.library.setup_server(network_info, metadata) return self.library.setup_server(network_info, metadata)
def _teardown_server(self, server_details, **kwargs): def _teardown_server(self, server_details, **kwargs):

View File

@ -1658,6 +1658,7 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
2. Build the list of export_locations for each share 2. Build the list of export_locations for each share
3. Release all resources from the source share server 3. Release all resources from the source share server
""" """
new_network_alloc = new_network_alloc[0]
src_backend_name = share_utils.extract_host( src_backend_name = share_utils.extract_host(
source_share_server['host'], level='backend_name') source_share_server['host'], level='backend_name')
src_vserver, src_client = self._get_vserver( src_vserver, src_client = self._get_vserver(
@ -2013,7 +2014,7 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
dns_ips = set() dns_ips = set()
domains = set() domains = set()
# Read all dns-ips and domains from other security services # Read all dns-ips and domains from other security services
for sec_svc in network_info['security_services']: for sec_svc in network_info[0]['security_services']:
if sec_svc['type'] == current_type: if sec_svc['type'] == current_type:
# skip the one that we are replacing # skip the one that we are replacing
continue continue

View File

@ -243,7 +243,7 @@ def add_hooks(f):
class ShareManager(manager.SchedulerDependentManager): class ShareManager(manager.SchedulerDependentManager):
"""Manages NAS storages.""" """Manages NAS storages."""
RPC_API_VERSION = '1.22' RPC_API_VERSION = '1.23'
def __init__(self, share_driver=None, service_name=None, *args, **kwargs): def __init__(self, share_driver=None, service_name=None, *args, **kwargs):
"""Load the driver from args, or from flags.""" """Load the driver from args, or from flags."""
@ -666,20 +666,25 @@ class ShareManager(manager.SchedulerDependentManager):
raise exception.InvalidShareServer(reason=msg % error_params) raise exception.InvalidShareServer(reason=msg % error_params)
parent_share_same_dest = (snapshot['share']['instance']['host'] parent_share_same_dest = (snapshot['share']['instance']['host']
== share_instance['host']) == share_instance['host'])
share_network_subnet_id = None share_network_subnets = None
if share_network_id: if share_network_id:
share_network_subnet = ( share_network_subnets = (
self.db.share_network_subnet_get_by_availability_zone_id( self.db.share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id, context, share_network_id,
availability_zone_id=share_instance.get( availability_zone_id=share_instance.get(
'availability_zone_id'))) 'availability_zone_id')))
if not share_network_subnet: if not share_network_subnets:
raise exception.ShareNetworkSubnetNotFound( raise exception.ShareNetworkSubnetNotFound(
share_network_subnet_id=None) share_network_subnet_id=None)
share_network_subnet_id = share_network_subnet['id']
elif parent_share_server: elif parent_share_server:
share_network_subnet_id = ( share_network_subnets = (
parent_share_server['share_network_subnet_id']) parent_share_server['share_network_subnets'])
# NOTE(felipe_rodrigues): it can retrieve the available share
# servers using one single subnet_id from the availability zone
# subnets, because if the share server has one, it will have
# all subnets on that availability zone.
share_network_subnet_id = share_network_subnets[0]['id']
def get_available_share_servers(): def get_available_share_servers():
if parent_share_server and parent_share_same_dest: if parent_share_server and parent_share_same_dest:
@ -725,10 +730,12 @@ class ShareManager(manager.SchedulerDependentManager):
context, context,
{ {
'host': self.host, 'host': self.host,
'share_network_subnet_id': share_network_subnet_id, 'share_network_subnets': share_network_subnets,
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'security_service_update_support': ( 'security_service_update_support': (
self.driver.security_service_update_support), self.driver.security_service_update_support),
'network_allocation_update_support': (
self.driver.network_allocation_update_support),
} }
) )
@ -796,28 +803,29 @@ class ShareManager(manager.SchedulerDependentManager):
migration. migration.
""" """
share_network_subnet = ( share_network_subnets = (
self.db.share_network_subnet_get_by_availability_zone_id( self.db.share_network_subnets_get_all_by_availability_zone_id(
context, new_share_network_id, context, new_share_network_id,
availability_zone_id=availability_zone_id)) availability_zone_id=availability_zone_id))
if not share_network_subnet: if not share_network_subnets:
raise exception.ShareNetworkSubnetNotFound( raise exception.ShareNetworkSubnetNotFound(
share_network_subnet_id=None) share_network_subnet_id=None)
share_network_subnet_id = share_network_subnet['id']
server_metadata = {} if not server_metadata else server_metadata server_metadata = {} if not server_metadata else server_metadata
@utils.synchronized("share_manager_%s" % share_network_subnet_id, @utils.synchronized(
external=True) "share_manager_%s" % share_network_subnets[0]['id'], external=True)
def _wrapped_provide_share_server_for_migration(): def _wrapped_provide_share_server_for_migration():
destination_share_server = self.db.share_server_create( destination_share_server = self.db.share_server_create(
context, context,
{ {
'host': self.host, 'host': self.host,
'share_network_subnet_id': share_network_subnet_id, 'share_network_subnets': share_network_subnets,
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'security_service_update_support': ( 'security_service_update_support': (
self.driver.security_service_update_support), self.driver.security_service_update_support),
'network_allocation_update_support': (
self.driver.network_allocation_update_support),
} }
) )
@ -917,7 +925,7 @@ class ShareManager(manager.SchedulerDependentManager):
def _provide_share_server_for_share_group(self, context, def _provide_share_server_for_share_group(self, context,
share_network_id, share_network_id,
share_network_subnet_id, share_network_subnets,
share_group_ref, share_group_ref,
share_group_snapshot=None): share_group_snapshot=None):
"""Gets or creates share_server and updates share group with its id. """Gets or creates share_server and updates share group with its id.
@ -935,10 +943,8 @@ class ShareManager(manager.SchedulerDependentManager):
:param context: Current context :param context: Current context
:param share_network_id: Share network where existing share server :param share_network_id: Share network where existing share server
should be found or created. should be found or created.
:param share_network_subnet_id: Share network subnet where :param share_network_subnets: Share network subnets where existing
existing share server should be found share server should be found or created.
or created. If not specified, the
default subnet will be used.
:param share_group_ref: Share Group model :param share_group_ref: Share Group model
:param share_group_snapshot: Optional -- ShareGroupSnapshot model. If :param share_group_snapshot: Optional -- ShareGroupSnapshot model. If
supplied, driver will use it to choose supplied, driver will use it to choose
@ -962,6 +968,11 @@ class ShareManager(manager.SchedulerDependentManager):
@utils.synchronized("share_manager_%s" % share_network_id, @utils.synchronized("share_manager_%s" % share_network_id,
external=True) external=True)
def _wrapped_provide_share_server_for_share_group(): def _wrapped_provide_share_server_for_share_group():
# NOTE(felipe_rodrigues): it can retrieve the available share
# servers using one single subnet_id from the availability zone
# subnets, because if the share server has one, it will have
# all subnets on that availability zone.
share_network_subnet_id = share_network_subnets[0]['id']
try: try:
available_share_servers = ( available_share_servers = (
self.db self.db
@ -996,10 +1007,12 @@ class ShareManager(manager.SchedulerDependentManager):
context, context,
{ {
'host': self.host, 'host': self.host,
'share_network_subnet_id': share_network_subnet_id, 'share_network_subnets': share_network_subnets,
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'security_service_update_support': ( 'security_service_update_support': (
self.driver.security_service_update_support), self.driver.security_service_update_support),
'network_allocation_update_support': (
self.driver.network_allocation_update_support),
} }
) )
@ -3037,13 +3050,13 @@ class ShareManager(manager.SchedulerDependentManager):
msg = ("Since share %(share)s has been un-managed from share " msg = ("Since share %(share)s has been un-managed from share "
"server %(server)s. This share server must be removed " "server %(server)s. This share server must be removed "
"manually, either by un-managing or by deleting it. The " "manually, either by un-managing or by deleting it. The "
"share network subnet %(subnet)s and share network " "share network subnets %(subnets)s and share network "
"%(network)s cannot be deleted unless this share server " "%(network)s cannot be deleted unless this share server "
"has been removed.") "has been removed.")
msg_args = { msg_args = {
'share': share_id, 'share': share_id,
'server': share_server['id'], 'server': share_server['id'],
'subnet': share_server['share_network_subnet_id'], 'subnets': share_server['share_network_subnet_ids'],
'network': share_instance['share_network_id'] 'network': share_instance['share_network_id']
} }
LOG.warning(msg, msg_args) LOG.warning(msg, msg_args)
@ -3130,8 +3143,11 @@ class ShareManager(manager.SchedulerDependentManager):
server = self.db.share_server_get(context, share_server_id) server = self.db.share_server_get(context, share_server_id)
try: try:
# NOTE(felipe_rodrigues): Manila does not support manage share
# server with multiple allocations, so it can get the first
# subnet_id element.
share_network_subnet = self.db.share_network_subnet_get( share_network_subnet = self.db.share_network_subnet_get(
context, server['share_network_subnet_id']) context, server['share_network_subnet_ids'][0])
share_network = self.db.share_network_get( share_network = self.db.share_network_get(
context, share_network_subnet['share_network_id']) context, share_network_subnet['share_network_id'])
@ -3217,7 +3233,9 @@ class ShareManager(manager.SchedulerDependentManager):
self.db.share_server_update( self.db.share_server_update(
context, share_server_id, context, share_server_id,
{'status': constants.STATUS_ACTIVE, {'status': constants.STATUS_ACTIVE,
'identifier': new_identifier}) 'identifier': new_identifier,
'network_allocation_update_support': (
self.driver.network_allocation_update_support)})
except Exception: except Exception:
msg = "Error managing share server %s" msg = "Error managing share server %s"
@ -4039,56 +4057,105 @@ class ShareManager(manager.SchedulerDependentManager):
self._publish_service_capabilities(context) self._publish_service_capabilities(context)
def _form_server_setup_info(self, context, share_server, share_network, def _form_server_setup_info(self, context, share_server, share_network,
share_network_subnet): share_network_subnets):
share_server_id = share_server['id'] share_server_id = share_server['id']
# Network info is used by driver for setting up share server # Network info is used by driver for setting up share server
# and getting server info on share creation. # and getting server info on share creation.
network_allocations = self.db.network_allocations_get_for_share_server(
context, share_server_id, label='user')
admin_network_allocations = ( admin_network_allocations = (
self.db.network_allocations_get_for_share_server( self.db.network_allocations_get_for_share_server(
context, share_server_id, label='admin')) context, share_server_id, label='admin'))
# NOTE(vponomaryov): following network_info fields are deprecated:
# 'segmentation_id', 'cidr' and 'network_type'. # NOTE(felipe_rodrigues): items in the network_info list contain
# And they should be used from network allocations directly. # same values for the keys: server_id, admin_network_allocations,
# They should be removed right after no one uses them. # security_services and backend_details.
network_info = { network_info = []
'server_id': share_server['id'], for share_network_subnet in share_network_subnets:
'segmentation_id': share_network_subnet['segmentation_id'], network_allocations = (
'cidr': share_network_subnet['cidr'], self.db.network_allocations_get_for_share_server(
'neutron_net_id': share_network_subnet['neutron_net_id'], context, share_server_id, label='user',
'neutron_subnet_id': share_network_subnet['neutron_subnet_id'], subnet_id=share_network_subnet['id']))
'security_services': share_network['security_services'], # NOTE(vponomaryov): following network_info fields are deprecated:
'network_allocations': network_allocations, # 'segmentation_id', 'cidr' and 'network_type'.
'admin_network_allocations': admin_network_allocations, # And they should be used from network allocations directly.
'backend_details': share_server.get('backend_details'), # They should be removed right after no one uses them.
'network_type': share_network_subnet['network_type'], network_info.append({
} 'server_id': share_server['id'],
'segmentation_id': share_network_subnet['segmentation_id'],
'cidr': share_network_subnet['cidr'],
'neutron_net_id': share_network_subnet['neutron_net_id'],
'neutron_subnet_id': share_network_subnet['neutron_subnet_id'],
'security_services': share_network['security_services'],
'network_allocations': network_allocations,
'admin_network_allocations': admin_network_allocations,
'backend_details': share_server.get('backend_details'),
'network_type': share_network_subnet['network_type'],
})
return network_info return network_info
def _handle_setup_server_error(self, context, share_server_id, e):
details = getattr(e, "detail_data", {})
if isinstance(details, dict):
server_details = details.get("server_details", {})
if not isinstance(server_details, dict):
LOG.debug(
("Cannot save non-dict data (%(data)s) provided as "
"'server details' of failed share server '%(server)s'."),
{"server": share_server_id, "data": server_details})
else:
invalid_details = []
for key, value in server_details.items():
try:
self.db.share_server_backend_details_set(
context, share_server_id, {key: value})
except Exception:
invalid_details.append("%(key)s: %(value)s" % {
'key': str(key),
'value': str(value)
})
if invalid_details:
LOG.debug(
("Following server details cannot be written to db : "
"%s"), str("\n".join(invalid_details)))
else:
LOG.debug(
("Cannot save non-dict data (%(data)s) provided as 'detail "
"data' of failed share server '%(server)s'."),
{"server": share_server_id, "data": details})
self.db.share_server_update(
context, share_server_id, {'status': constants.STATUS_ERROR})
def _setup_server(self, context, share_server, metadata): def _setup_server(self, context, share_server, metadata):
subnets = share_server['share_network_subnets']
if not subnets:
raise exception.NetworkBadConfigurationException(
reason="share server does not have subnet")
# all subnets reside on same share network, get it from the first one.
share_network_id = subnets[0]['share_network_id']
try: try:
share_network_subnet = share_server['share_network_subnet'] share_network = self.db.share_network_get(context,
share_network_subnet_id = share_network_subnet['id'] share_network_id)
share_network_id = share_network_subnet['share_network_id'] for share_network_subnet in subnets:
share_network = self.db.share_network_get( self.driver.allocate_network(
context, share_network_id) context, share_server, share_network, share_network_subnet)
self.driver.allocate_network(context, share_server, share_network,
share_network_subnet)
self.driver.allocate_admin_network(context, share_server) self.driver.allocate_admin_network(context, share_server)
# Get share_network_subnet in case it was updated. # Get share_network_subnets in case they were updated.
share_network_subnet = self.db.share_network_subnet_get( share_network_subnets = (
context, share_network_subnet_id) self.db.share_network_subnet_get_all_by_share_server_id(
network_info = self._form_server_setup_info( context, share_server['id']))
context, share_server, share_network, share_network_subnet)
self._validate_segmentation_id(network_info) network_info_list = self._form_server_setup_info(
context, share_server, share_network, share_network_subnets)
for network_info in network_info_list:
self._validate_segmentation_id(network_info)
# NOTE(vponomaryov): Save security services data to share server # NOTE(vponomaryov): Save security services data to share server
# details table to remove dependency from share network after # details table to remove dependency from share network after
# creation operation. It will allow us to delete share server and # creation operation. It will allow us to delete share server and
# share network separately without dependency on each other. # share network separately without dependency on each other.
for security_service in network_info['security_services']: for security_service in network_info_list[0]['security_services']:
ss_type = security_service['type'] ss_type = security_service['type']
data = { data = {
'name': security_service['name'], 'name': security_service['name'],
@ -4105,7 +4172,7 @@ class ShareManager(manager.SchedulerDependentManager):
{'security_service_' + ss_type: jsonutils.dumps(data)}) {'security_service_' + ss_type: jsonutils.dumps(data)})
server_info = self.driver.setup_server( server_info = self.driver.setup_server(
network_info, metadata=metadata) network_info_list, metadata=metadata)
self.driver.update_network_allocation(context, share_server) self.driver.update_network_allocation(context, share_server)
self.driver.update_admin_network_allocation(context, share_server) self.driver.update_admin_network_allocation(context, share_server)
@ -4120,42 +4187,7 @@ class ShareManager(manager.SchedulerDependentManager):
'identifier', share_server['id'])}) 'identifier', share_server['id'])})
except Exception as e: except Exception as e:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
details = getattr(e, "detail_data", {}) self._handle_setup_server_error(context, share_server['id'], e)
if isinstance(details, dict):
server_details = details.get("server_details", {})
if not isinstance(server_details, dict):
LOG.debug(
("Cannot save non-dict data (%(data)s) "
"provided as 'server details' of "
"failed share server '%(server)s'."),
{"server": share_server["id"],
"data": server_details})
else:
invalid_details = []
for key, value in server_details.items():
try:
self.db.share_server_backend_details_set(
context, share_server['id'], {key: value})
except Exception:
invalid_details.append("%(key)s: %(value)s" % {
'key': key,
'value': value
})
if invalid_details:
LOG.debug(
("Following server details "
"cannot be written to db : %s"),
"\n".join(invalid_details))
else:
LOG.debug(
("Cannot save non-dict data (%(data)s) provided as "
"'detail data' of failed share server '%(server)s'."),
{"server": share_server["id"], "data": details})
self.db.share_server_update(
context, share_server['id'],
{'status': constants.STATUS_ERROR})
self.driver.deallocate_network(context, share_server['id']) self.driver.deallocate_network(context, share_server['id'])
def _validate_segmentation_id(self, network_info): def _validate_segmentation_id(self, network_info):
@ -4200,8 +4232,11 @@ class ShareManager(manager.SchedulerDependentManager):
@utils.require_driver_initialized @utils.require_driver_initialized
def delete_share_server(self, context, share_server): def delete_share_server(self, context, share_server):
subnet_id = (share_server['share_network_subnet_ids'][0]
if share_server['share_network_subnet_ids'] else None)
@utils.synchronized( @utils.synchronized(
"share_manager_%s" % share_server['share_network_subnet_id']) "share_manager_%s" % subnet_id)
def _wrapped_delete_share_server(): def _wrapped_delete_share_server():
# NOTE(vponomaryov): Verify that there are no dependent shares. # NOTE(vponomaryov): Verify that there are no dependent shares.
# Without this verification we can get here exception in next case: # Without this verification we can get here exception in next case:
@ -4443,8 +4478,8 @@ class ShareManager(manager.SchedulerDependentManager):
if parent_share_server_id and self.driver.driver_handles_share_servers: if parent_share_server_id and self.driver.driver_handles_share_servers:
share_server = self.db.share_server_get(context, share_server = self.db.share_server_get(context,
parent_share_server_id) parent_share_server_id)
share_network_subnet = share_server['share_network_subnet'] share_network_id = (
share_network_id = share_network_subnet['share_network_id'] share_server['share_network_id'])
if share_network_id and not self.driver.driver_handles_share_servers: if share_network_id and not self.driver.driver_handles_share_servers:
self.db.share_group_update( self.db.share_group_update(
@ -4457,14 +4492,17 @@ class ShareManager(manager.SchedulerDependentManager):
availability_zone_id = self._get_az_for_share_group( availability_zone_id = self._get_az_for_share_group(
context, share_group_ref) context, share_group_ref)
subnet = self.db.share_network_subnet_get_by_availability_zone_id( subnets = (
context, share_network_id, availability_zone_id) self.db.share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id, availability_zone_id))
if not subnets:
raise exception.ShareNetworkSubnetNotFound(
share_network_subnet_id=None)
try: try:
share_server, share_group_ref = ( share_server, share_group_ref = (
self._provide_share_server_for_share_group( self._provide_share_server_for_share_group(
context, share_network_id, subnet.get('id'), context, share_network_id, subnets, share_group_ref,
share_group_ref,
share_group_snapshot=snap_ref, share_group_snapshot=snap_ref,
) )
) )
@ -4764,8 +4802,8 @@ class ShareManager(manager.SchedulerDependentManager):
'share_network_id': share_server.get('share_network_id'), 'share_network_id': share_server.get('share_network_id'),
'created_at': share_server.get('created_at'), 'created_at': share_server.get('created_at'),
'backend_details': share_server.get('backend_details'), 'backend_details': share_server.get('backend_details'),
'share_network_subnet_id': 'share_network_subnet_ids':
share_server.get('share_network_subnet_id', None), share_server.get('share_network_subnet_ids', []),
'is_auto_deletable': share_server.get('is_auto_deletable', None), 'is_auto_deletable': share_server.get('is_auto_deletable', None),
'identifier': share_server.get('identifier', None), 'identifier': share_server.get('identifier', None),
'network_allocations': share_server.get('network_allocations', 'network_allocations': share_server.get('network_allocations',
@ -5101,11 +5139,10 @@ class ShareManager(manager.SchedulerDependentManager):
if not create_server_on_backend: if not create_server_on_backend:
dest_share_server = self.db.share_server_get( dest_share_server = self.db.share_server_get(
context, dest_share_server['id']) context, dest_share_server['id'])
current_subnet = dest_share_server['share_network_subnet'] net_changes_identified = (
old_subnet = source_share_server['share_network_subnet'] not share_utils.is_az_subnets_compatible(
for key in ['neutron_net_id', 'neutron_subnet_id']: dest_share_server['share_network_subnets'],
if current_subnet.get(key) != old_subnet.get(key): source_share_server['share_network_subnets']))
net_changes_identified = True
# NOTE(carloss): Even though the share back end won't need to # NOTE(carloss): Even though the share back end won't need to
# create a share server, if a network change was identified, # create a share server, if a network change was identified,
@ -5115,11 +5152,14 @@ class ShareManager(manager.SchedulerDependentManager):
# In such case, the migration will be disruptive, since the old # In such case, the migration will be disruptive, since the old
# allocations will be replaced by the new ones. # allocations will be replaced by the new ones.
if net_changes_identified: if net_changes_identified:
share_network_subnet = self.db.share_network_subnet_get( share_network_subnets = (
context, dest_share_server['share_network_subnet_id']) self.db.
self.driver.allocate_network( share_network_subnet_get_all_by_share_server_id(
context, dest_share_server, new_share_network, context, dest_share_server['id']))
share_network_subnet) for share_network_subnet in share_network_subnets:
self.driver.allocate_network(
context, dest_share_server, new_share_network,
share_network_subnet)
self.driver.allocate_admin_network( self.driver.allocate_admin_network(
context, dest_share_server) context, dest_share_server)
# Refresh the share server so it will have the network # Refresh the share server so it will have the network
@ -5503,11 +5543,11 @@ class ShareManager(manager.SchedulerDependentManager):
{'task_state': constants.TASK_STATE_MIGRATION_COMPLETING}) {'task_state': constants.TASK_STATE_MIGRATION_COMPLETING})
# Retrieve network allocations reserved for the new share server # Retrieve network allocations reserved for the new share server
dest_sns = dest_share_server['share_network_subnet'] dest_snss = dest_share_server['share_network_subnets']
dest_sns_id = dest_sns['id'] dest_sn_id = dest_snss[0]['share_network_id']
dest_sn_id = dest_sns['share_network_id']
dest_sn = self.db.share_network_get(context, dest_sn_id) dest_sn = self.db.share_network_get(context, dest_sn_id)
dest_sns = self.db.share_network_subnet_get(context, dest_sns_id) dest_snss = self.db.share_network_subnet_get_all_by_share_server_id(
context, dest_share_server['id'])
migration_reused_network_allocations = (len( migration_reused_network_allocations = (len(
self.db.network_allocations_get_for_share_server( self.db.network_allocations_get_for_share_server(
@ -5519,16 +5559,20 @@ class ShareManager(manager.SchedulerDependentManager):
else source_share_server) else source_share_server)
new_network_allocations = self._form_server_setup_info( new_network_allocations = self._form_server_setup_info(
context, server_to_get_allocations, dest_sn, dest_sns) context, server_to_get_allocations, dest_sn, dest_snss)
model_update = self.driver.share_server_migration_complete( model_update = self.driver.share_server_migration_complete(
context, source_share_server, dest_share_server, share_instances, context, source_share_server, dest_share_server, share_instances,
snapshot_instances, new_network_allocations) snapshot_instances, new_network_allocations)
if not migration_reused_network_allocations: if not migration_reused_network_allocations:
network_allocations = []
for net_allocation in new_network_allocations:
network_allocations += net_allocation['network_allocations']
all_allocations = [ all_allocations = [
new_network_allocations['network_allocations'], network_allocations,
new_network_allocations['admin_network_allocations'] new_network_allocations[0]['admin_network_allocations']
] ]
for allocations in all_allocations: for allocations in all_allocations:
for allocation in allocations: for allocation in allocations:
@ -5726,7 +5770,8 @@ class ShareManager(manager.SchedulerDependentManager):
snapshot_instances) snapshot_instances)
@locked_share_network_operation @locked_share_network_operation
def _check_share_network_update_finished(self, context, share_network_id): def _check_share_network_update_finished(
self, context, share_network_id=None):
# Check if this share network is already active # Check if this share network is already active
share_network = self.db.share_network_get(context, share_network_id) share_network = self.db.share_network_get(context, share_network_id)
if share_network['status'] == constants.STATUS_NETWORK_ACTIVE: if share_network['status'] == constants.STATUS_NETWORK_ACTIVE:
@ -5775,14 +5820,14 @@ class ShareManager(manager.SchedulerDependentManager):
filters={'share_network_id': share_network_id}) filters={'share_network_id': share_network_id})
for share_server in share_servers: for share_server in share_servers:
share_network_subnet = share_server['share_network_subnet']
share_network_subnet_id = share_network_subnet['id']
# Get share_network_subnet in case it was updated. # Get share_network_subnet in case it was updated.
share_network_subnet = self.db.share_network_subnet_get( share_network_subnets = (
context, share_network_subnet_id) self.db.share_network_subnet_get_all_by_share_server_id(
context, share_server['id']))
network_info = self._form_server_setup_info( network_info = self._form_server_setup_info(
context, share_server, share_network, share_network_subnet) context, share_server, share_network, share_network_subnets)
share_instances = ( share_instances = (
self.db.share_instances_get_all_by_share_server( self.db.share_instances_get_all_by_share_server(
@ -5883,7 +5928,8 @@ class ShareManager(manager.SchedulerDependentManager):
# Check if all share servers have already finished their updates in # Check if all share servers have already finished their updates in
# order to properly update share network status # order to properly update share network status
self._check_share_network_update_finished(context, share_network['id']) self._check_share_network_update_finished(
context, share_network_id=share_network['id'])
def update_share_network_security_service( def update_share_network_security_service(
self, context, share_network_id, new_security_service_id, self, context, share_network_id, new_security_service_id,
@ -5925,3 +5971,234 @@ class ShareManager(manager.SchedulerDependentManager):
LOG.debug('A share network security service check was requested ' LOG.debug('A share network security service check was requested '
'but no entries were found in database. Ignoring call ' 'but no entries were found in database. Ignoring call '
'and returning.') 'and returning.')
@api.locked_share_server_update_allocations_operation
def _update_share_server_allocations_check_operation(
self, context, is_supported, share_network_id=None,
availability_zone_id=None):
update_key = self.share_api.get_share_server_update_allocations_key(
share_network_id, availability_zone_id)
current_hosts_info = self.db.async_operation_data_get(
context, share_network_id, update_key)
if current_hosts_info:
current_hosts = json.loads(current_hosts_info)
current_hosts[self.host] = is_supported
self.db.async_operation_data_update(
context, share_network_id,
{update_key: json.dumps(current_hosts)})
else:
LOG.debug('A share network subnet create check was requested '
'but no entries were found in database. Ignoring call '
'and returning.')
def _get_subnet_allocations(self, context, share_server_id,
share_network_subnet):
network_allocations = (
self.db.network_allocations_get_for_share_server(
context, share_server_id, label='user',
subnet_id=share_network_subnet['id']))
return {
'share_network_subnet_id': share_network_subnet['id'],
'neutron_net_id': share_network_subnet['neutron_net_id'],
'neutron_subnet_id': share_network_subnet['neutron_subnet_id'],
'network_allocations': network_allocations,
}
def _form_network_allocations(self, context, share_server_id,
share_network_subnets):
subnet_allocations = []
for share_network_subnet in share_network_subnets:
subnet_allocations.append(self._get_subnet_allocations(
context, share_server_id, share_network_subnet))
admin_network_allocations = (
self.db.network_allocations_get_for_share_server(
context, share_server_id, label='admin'))
return {
'admin_network_allocations': admin_network_allocations,
'subnets': subnet_allocations,
}
def check_update_share_server_network_allocations(
self, context, share_network_id, new_share_network_subnet):
share_network = self.db.share_network_get(
context, share_network_id)
az_subnets = (
self.db.share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id,
new_share_network_subnet['availability_zone_id'],
fallback_to_default=False)
)
self.driver.network_api.include_network_info(new_share_network_subnet)
# all subnets have the same set of share servers, so do the check from
# servers in the first subnet.
share_servers = az_subnets[0]['share_servers'] if az_subnets else []
is_supported = True
for share_server in share_servers:
current_network_allocations = self._form_network_allocations(
context, share_server['id'], az_subnets)
share_instances = (
self.db.share_instances_get_all_by_share_server(
context, share_server['id'], with_share_data=True))
share_instance_ids = [sn.id for sn in share_instances]
share_instances_rules = []
for share_instance_id in share_instance_ids:
instance_rules = {
'share_instance_id': share_instance_id,
'access_rules': (
self.db.share_access_get_all_for_instance(
context, share_instance_id))
}
share_instances_rules.append(instance_rules)
if self.driver.check_update_share_server_network_allocations(
context, share_server, current_network_allocations,
new_share_network_subnet,
share_network['security_services'],
share_instances, share_instances_rules):
# Check the next share server.
continue
else:
# At least one share server doesn't support this update.
is_supported = False
break
self._update_share_server_allocations_check_operation(
context, is_supported, share_network_id=share_network_id,
availability_zone_id=(
new_share_network_subnet['availability_zone_id']))
def _do_update_share_server_network_allocations(
self, context, share_server, share_network, new_subnet,
current_network_allocations, share_instances,
snapshot_instance_ids):
self.driver.allocate_network(
context, share_server, share_network, new_subnet)
new_network_allocations = self._get_subnet_allocations(
context, share_server['id'], new_subnet)
if not new_network_allocations['network_allocations']:
raise exception.AllocationsNotFoundForShareServer(
share_server_id=share_server['id'])
# NOTE(felipe_rodrigues): all allocations have the same network
# segmentation info, so validation from the first one.
self._validate_segmentation_id(
new_network_allocations['network_allocations'][0])
model_update = self.driver.update_share_server_network_allocations(
context, share_server, current_network_allocations,
new_network_allocations, share_network['security_services'],
share_instances, snapshot_instance_ids)
self.driver.update_network_allocation(context, share_server)
driver_backend_details = model_update.get('server_details')
if driver_backend_details:
self.db.share_server_backend_details_set(
context, share_server['id'], driver_backend_details)
share_updates = model_update.get('share_updates', {})
for share_instance_id, export_locations in share_updates.items():
self.db.share_export_locations_update(
context, share_instance_id, export_locations)
snapshot_updates = model_update.get('snapshot_updates', {})
for snap_instance_id, model_update in snapshot_updates.items():
snapshot_export_locations = model_update.pop(
'export_locations', [])
if model_update:
self.db.share_snapshot_instance_update(
context, snap_instance_id, model_update)
if snapshot_export_locations:
export_locations_update = []
for exp_location in snapshot_export_locations:
updated_el = {
'path': exp_location['path'],
'is_admin_only': exp_location['is_admin_only'],
}
export_locations_update.append(updated_el)
self.db.share_snapshot_instance_export_locations_update(
context, snap_instance_id, export_locations_update)
def update_share_server_network_allocations(
self, context, share_network_id, new_share_network_subnet_id):
share_network = self.db.share_network_get(
context, share_network_id)
new_subnet = self.db.share_network_subnet_get(
context, new_share_network_subnet_id)
current_subnets = (
self.db.share_network_subnets_get_all_by_availability_zone_id(
context, share_network_id,
new_subnet['availability_zone_id'],
fallback_to_default=False)
)
current_subnets = [subnet for subnet in current_subnets
if subnet['id'] != new_share_network_subnet_id]
share_servers = (
self.db.share_server_get_all_by_host_and_share_subnet_valid(
context, self.host, new_share_network_subnet_id,
server_status=constants.STATUS_SERVER_NETWORK_CHANGE))
for share_server in share_servers:
share_server_id = share_server['id']
current_network_allocations = self._form_network_allocations(
context, share_server_id, current_subnets)
share_instances = (
self.db.share_instances_get_all_by_share_server(
context, share_server_id, with_share_data=True))
share_instance_ids = [x['id'] for x in share_instances]
snapshot_instances = (
self.db.share_snapshot_instance_get_all_with_filters(
context,
{'share_instance_ids': share_instance_ids}))
snapshot_instance_ids = [x['id'] for x in snapshot_instances]
try:
self._do_update_share_server_network_allocations(
context, share_server, share_network, new_subnet,
current_network_allocations, share_instances,
snapshot_instances)
except Exception as e:
msg = ('Failed to update allocations of share server '
'%(server_id)s on subnet %(subnet_id)s: %(e)s.')
data = {
'server_id': share_server_id,
'subnet_id': new_share_network_subnet_id,
'e': str(e),
}
LOG.exception(msg, data)
# Set resources to error. Allocations configuration must be
# fixed before restoring it to active again.
self._handle_setup_server_error(context, share_server_id, e)
self._update_resource_status(
context, constants.STATUS_ERROR,
share_instance_ids=share_instance_ids,
snapshot_instance_ids=snapshot_instance_ids)
continue
msg = _(
"Network allocations was successfully updated on share "
"server %s.") % share_server['id']
LOG.info(msg)
self.db.share_server_update(
context, share_server['id'],
{'status': constants.STATUS_ACTIVE})
# Check if all share servers have already finished their updates in
# order to properly update share network status.
self._check_share_network_update_finished(
context, share_network_id=share_network['id'])

View File

@ -81,6 +81,8 @@ class ShareAPI(object):
and share_server_get_progress() and share_server_get_progress()
1.22 - Add update_share_network_security_service() and 1.22 - Add update_share_network_security_service() and
check_update_share_network_security_service() check_update_share_network_security_service()
1.23 - Add update_share_server_network_allocations() and
check_update_share_server_network_allocations()
""" """
BASE_RPC_API_VERSION = '1.0' BASE_RPC_API_VERSION = '1.0'
@ -89,7 +91,7 @@ class ShareAPI(object):
super(ShareAPI, self).__init__() super(ShareAPI, self).__init__()
target = messaging.Target(topic=CONF.share_topic, target = messaging.Target(topic=CONF.share_topic,
version=self.BASE_RPC_API_VERSION) version=self.BASE_RPC_API_VERSION)
self.client = rpc.get_client(target, version_cap='1.22') self.client = rpc.get_client(target, version_cap='1.23')
def create_share_instance(self, context, share_instance, host, def create_share_instance(self, context, share_instance, host,
request_spec, filter_properties, request_spec, filter_properties,
@ -462,3 +464,25 @@ class ShareAPI(object):
share_network_id=share_network_id, share_network_id=share_network_id,
new_security_service_id=new_security_service_id, new_security_service_id=new_security_service_id,
current_security_service_id=current_security_service_id) current_security_service_id=current_security_service_id)
def check_update_share_server_network_allocations(
self, context, dest_host, share_network_id,
new_share_network_subnet):
host = utils.extract_host(dest_host)
call_context = self.client.prepare(server=host, version='1.23')
call_context.cast(
context,
'check_update_share_server_network_allocations',
share_network_id=share_network_id,
new_share_network_subnet=new_share_network_subnet)
def update_share_server_network_allocations(
self, context, dest_host, share_network_id,
new_share_network_subnet_id):
host = utils.extract_host(dest_host)
call_context = self.client.prepare(server=host, version='1.23')
call_context.cast(
context,
'update_share_server_network_allocations',
share_network_id=share_network_id,
new_share_network_subnet_id=new_share_network_subnet_id)

View File

@ -152,3 +152,22 @@ def _usage_from_share(share_ref, share_instance_ref, **extra_usage_info):
def get_recent_db_migration_id(): def get_recent_db_migration_id():
return migration.version() 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

View File

@ -24,6 +24,7 @@ import webob
import webob.exc import webob.exc
from manila.api import common from manila.api import common
from manila.db import api as db_api
from manila import exception from manila import exception
from manila import policy from manila import policy
from manila import test from manila import test
@ -354,6 +355,108 @@ class MiscFunctionsTest(test.TestCase):
common.parse_is_public, common.parse_is_public,
'fakefakefake') 'fakefakefake')
@ddt.data(None, 'fake_az')
def test__get_existing_subnets(self, az):
default_subnets = 'fake_default_subnets'
mock_get_default_subnets = self.mock_object(
db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=default_subnets))
subnets = 'fake_subnets'
mock_get_subnets = self.mock_object(
db_api, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value=subnets))
net_id = 'fake_net'
context = 'fake_context'
res_subnets = common._get_existing_subnets(context, net_id, az)
if az:
self.assertEqual(subnets, res_subnets)
mock_get_subnets.assert_called_once_with(context, net_id, az,
fallback_to_default=False)
mock_get_default_subnets.assert_not_called()
else:
self.assertEqual(default_subnets, res_subnets)
mock_get_subnets.assert_not_called()
mock_get_default_subnets.assert_called_once_with(context, net_id)
def test_validate_subnet_create(self):
mock_check_net = self.mock_object(common, 'check_net_id_and_subnet_id')
net = 'fake_net'
mock_get_net = self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=net))
az_id = 'fake_az_id'
az = {'id': az_id}
mock_get_az = self.mock_object(db_api, 'availability_zone_get',
mock.Mock(return_value=az))
subnets = 'fake_subnets'
mock_get_subnets = self.mock_object(common, '_get_existing_subnets',
mock.Mock(return_value=subnets))
net_id = 'fake_net_id'
context = 'fake_context'
az_name = 'fake_az'
data = {'availability_zone': az_name}
res_net, res_subnets = common.validate_subnet_create(
context, net_id, data, True)
self.assertEqual(net, res_net)
self.assertEqual(subnets, res_subnets)
self.assertEqual(data['availability_zone_id'], az_id)
mock_check_net.assert_called_once_with(data)
mock_get_net.assert_called_once_with(context, net_id)
mock_get_az.assert_called_once_with(context, az_name)
mock_get_subnets.assert_called_once_with(context, net_id, az_id)
def test_validate_subnet_create_net_not_found(self):
self.mock_object(common, 'check_net_id_and_subnet_id')
self.mock_object(db_api, 'share_network_get',
mock.Mock(side_effect=exception.ShareNetworkNotFound(
share_network_id="fake_id")))
net_id = 'fake_net_id'
context = 'fake_context'
az_name = 'fake_az'
data = {'availability_zone': az_name}
self.assertRaises(webob.exc.HTTPNotFound,
common.validate_subnet_create,
context, net_id, data, True)
def test_validate_subnet_create_az_not_found(self):
self.mock_object(common, 'check_net_id_and_subnet_id')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value='fake_net'))
self.mock_object(
db_api, 'availability_zone_get',
mock.Mock(side_effect=exception.AvailabilityZoneNotFound(
id='fake_id')))
net_id = 'fake_net_id'
context = 'fake_context'
az_name = 'fake_az'
data = {'availability_zone': az_name}
self.assertRaises(webob.exc.HTTPBadRequest,
common.validate_subnet_create,
context, net_id, data, True)
def test_validate_subnet_create_multiple_subnet_not_support(self):
self.mock_object(common, 'check_net_id_and_subnet_id')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value='fake_net'))
self.mock_object(db_api, 'availability_zone_get',
mock.Mock(return_value={'id': 'fake_az_id'}))
self.mock_object(common, '_get_existing_subnets',
mock.Mock(return_value='fake_subnets'))
net_id = 'fake_net_id'
context = 'fake_context'
az_name = 'fake_az'
data = {'availability_zone': az_name}
self.assertRaises(webob.exc.HTTPConflict,
common.validate_subnet_create,
context, net_id, data, False)
@ddt.ddt @ddt.ddt
class ViewBuilderTest(test.TestCase): class ViewBuilderTest(test.TestCase):

View File

@ -15,6 +15,7 @@
from unittest import mock from unittest import mock
import copy
import ddt import ddt
from webob import exc from webob import exc
@ -36,14 +37,15 @@ fake_share_server_list = {
'host': 'fake_host', 'host': 'fake_host',
'share_network_name': 'fake_sn_name', 'share_network_name': 'fake_sn_name',
'share_network_id': 'fake_sn_id', 'share_network_id': 'fake_sn_id',
'share_network_subnet_id': 'fake_sns_id', 'share_network_subnet_ids': ['fake_sns_id'],
'project_id': 'fake_project_id', 'project_id': 'fake_project_id',
'id': 'fake_server_id', 'id': 'fake_server_id',
'is_auto_deletable': False, 'is_auto_deletable': False,
'task_state': None, 'task_state': None,
'source_share_server_id': None, 'source_share_server_id': None,
'identifier': 'fake_id', 'identifier': 'fake_id',
'security_service_update_support': False 'security_service_update_support': False,
'network_allocation_update_support': False
}, },
{ {
'status': constants.STATUS_ERROR, 'status': constants.STATUS_ERROR,
@ -51,14 +53,15 @@ fake_share_server_list = {
'host': 'fake_host_2', 'host': 'fake_host_2',
'share_network_name': 'fake_sn_id_2', 'share_network_name': 'fake_sn_id_2',
'share_network_id': 'fake_sn_id_2', 'share_network_id': 'fake_sn_id_2',
'share_network_subnet_id': 'fake_sns_id_2', 'share_network_subnet_ids': ['fake_sns_id_2'],
'project_id': 'fake_project_id_2', 'project_id': 'fake_project_id_2',
'id': 'fake_server_id_2', 'id': 'fake_server_id_2',
'is_auto_deletable': True, 'is_auto_deletable': True,
'task_state': None, 'task_state': None,
'source_share_server_id': None, 'source_share_server_id': None,
'identifier': 'fake_id_2', 'identifier': 'fake_id_2',
'security_service_update_support': False 'security_service_update_support': False,
'network_allocation_update_support': False
}, },
] ]
@ -87,7 +90,7 @@ fake_share_server_get_result = {
'host': 'fake_host', 'host': 'fake_host',
'share_network_name': 'fake_sn_name', 'share_network_name': 'fake_sn_name',
'share_network_id': 'fake_sn_id', 'share_network_id': 'fake_sn_id',
'share_network_subnet_id': 'fake_sns_id', 'share_network_subnet_ids': ['fake_sns_id'],
'project_id': 'fake_project_id', 'project_id': 'fake_project_id',
'id': 'fake_server_id', 'id': 'fake_server_id',
'backend_details': { 'backend_details': {
@ -98,7 +101,8 @@ fake_share_server_get_result = {
'task_state': None, 'task_state': None,
'source_share_server_id': None, 'source_share_server_id': None,
'identifier': 'fake_id', 'identifier': 'fake_id',
'security_service_update_support': False 'security_service_update_support': False,
'network_allocation_update_support': False
} }
} }
@ -124,10 +128,11 @@ class FakeShareServer(object):
self.created_at = kwargs.get('created_at', None) self.created_at = kwargs.get('created_at', None)
self.updated_at = kwargs.get('updated_at', None) self.updated_at = kwargs.get('updated_at', None)
self.host = kwargs.get('host', 'fake_host') self.host = kwargs.get('host', 'fake_host')
self.share_network_subnet = kwargs.get('share_network_subnet', { self.share_network_subnets = kwargs.get('share_network_subnets', [{
'id': 'fake_sns_id', 'share_network_id': 'fake_sn_id'}) 'id': 'fake_sns_id', 'share_network_id': 'fake_sn_id'}])
self.share_network_subnet_id = kwargs.get( self.share_network_subnet_ids = kwargs.get(
'share_network_subnet_id', self.share_network_subnet['id']) 'share_network_subnet_ids',
[sn['id'] for sn in self.share_network_subnets])
self.status = kwargs.get('status', constants.STATUS_ACTIVE) self.status = kwargs.get('status', constants.STATUS_ACTIVE)
self.project_id = 'fake_project_id' self.project_id = 'fake_project_id'
self.identifier = kwargs.get('identifier', 'fake_id') self.identifier = kwargs.get('identifier', 'fake_id')
@ -137,6 +142,9 @@ class FakeShareServer(object):
self.backend_details = share_server_backend_details self.backend_details = share_server_backend_details
self.security_service_update_support = kwargs.get( self.security_service_update_support = kwargs.get(
'security_service_update_support', False) 'security_service_update_support', False)
self.network_allocation_update_support = kwargs.get(
'network_allocation_update_support', False)
self.share_network_id = kwargs.get('share_network_id', 'fake_sn_id')
def __getitem__(self, item): def __getitem__(self, item):
return getattr(self, item) return getattr(self, item)
@ -147,15 +155,17 @@ def fake_share_server_get_all():
FakeShareServer(), FakeShareServer(),
FakeShareServer(id='fake_server_id_2', FakeShareServer(id='fake_server_id_2',
host='fake_host_2', host='fake_host_2',
share_network_subnet={ share_network_subnets=[{
'id': 'fake_sns_id_2', 'id': 'fake_sns_id_2',
'share_network_id': 'fake_sn_id_2', 'share_network_id': 'fake_sn_id_2',
}, }],
share_network_id='fake_sn_id_2',
identifier='fake_id_2', identifier='fake_id_2',
task_state=None, task_state=None,
is_auto_deletable=True, is_auto_deletable=True,
status=constants.STATUS_ERROR, status=constants.STATUS_ERROR,
security_service_update_support=False) security_service_update_support=False,
network_allocation_update_support=False),
] ]
return fake_share_servers return fake_share_servers
@ -184,7 +194,7 @@ class FakeRequestWithProjectId(FakeRequestAdmin):
class FakeRequestWithShareNetworkSubnetId(FakeRequestAdmin): class FakeRequestWithShareNetworkSubnetId(FakeRequestAdmin):
GET = { GET = {
'share_network_subnet_id': 'share_network_subnet_id':
fake_share_server_get_all()[0].share_network_subnet_id, fake_share_server_get_all()[0].share_network_subnet_ids,
} }
@ -347,20 +357,43 @@ class ShareServerAPITest(test.TestCase):
ctxt, self.resource_name, 'index') ctxt, self.resource_name, 'index')
self.assertEqual(0, len(result['share_servers'])) self.assertEqual(0, len(result['share_servers']))
def test_show(self): @ddt.data({'version': '2.70', 'share_network_name': ''},
{'version': '2.70', 'share_network_name': 'fake_sn_name'},
{'version': '2.68', 'share_network_name': 'fake_sn_name'})
@ddt.unpack
def test_show(self, version, share_network_name):
self.mock_object(db_api, 'share_server_get', self.mock_object(db_api, 'share_server_get',
mock.Mock(return_value=fake_share_server_get())) mock.Mock(return_value=fake_share_server_get()))
request, ctxt = self._prepare_request('/show', use_admin_context=True) request, ctxt = self._prepare_request('/show', use_admin_context=True,
version=version)
share_network = copy.deepcopy(
fake_share_network_get_list['share_networks'][0])
share_server = copy.deepcopy(
fake_share_server_get_result['share_server'])
if version == '2.68':
share_server['share_network_subnet_id'] = \
share_server['share_network_subnet_ids'][0]
share_server.pop('share_network_subnet_ids')
share_server.pop('network_allocation_update_support')
share_network['name'] = share_network_name
if share_network['name']:
share_server['share_network_name'] = share_network['name']
else:
share_server['share_network_name'] = share_network['id']
self.mock_object(db_api, 'share_network_get', mock.Mock( self.mock_object(db_api, 'share_network_get', mock.Mock(
return_value=fake_share_network_get_list['share_networks'][0])) return_value=share_network))
result = self.controller.show( result = self.controller.show(
request, request,
fake_share_server_get_result['share_server']['id']) share_server['id'])
policy.check_policy.assert_called_once_with( policy.check_policy.assert_called_once_with(
ctxt, self.resource_name, 'show') ctxt, self.resource_name, 'show')
db_api.share_server_get.assert_called_once_with( db_api.share_server_get.assert_called_once_with(
ctxt, fake_share_server_get_result['share_server']['id']) ctxt, share_server['id'])
self.assertEqual(fake_share_server_get_result['share_server'], self.assertEqual(share_server,
result['share_server']) result['share_server'])
@ddt.data( @ddt.data(
@ -389,7 +422,7 @@ class ShareServerAPITest(test.TestCase):
ctxt, fake_share_server_get_result['share_server']['id']) ctxt, fake_share_server_get_result['share_server']['id'])
if isinstance(share_net_side_effect, exception.ShareNetworkNotFound): if isinstance(share_net_side_effect, exception.ShareNetworkNotFound):
exp_share_net_id = (fake_share_server_get() exp_share_net_id = (fake_share_server_get()
.share_network_subnet['share_network_id']) .share_network_subnets[0]['share_network_id'])
db_api.share_network_get.assert_called_once_with( db_api.share_network_get.assert_called_once_with(
ctxt, exp_share_net_id) ctxt, exp_share_net_id)

View File

@ -242,7 +242,7 @@ class ShareAPITest(test.TestCase):
self.mock_object(common, 'check_share_network_is_active', self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True)) mock.Mock(return_value=True))
self.mock_object( self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id', db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value={'id': 'fakesubnetid'})) mock.Mock(return_value={'id': 'fakesubnetid'}))
body = {"share": copy.deepcopy(shr)} body = {"share": copy.deepcopy(shr)}
@ -339,6 +339,8 @@ class ShareAPITest(test.TestCase):
} }
parent_share_net = 444 parent_share_net = 444
fake_share_net = {'id': parent_share_net} fake_share_net = {'id': parent_share_net}
share_net_subnets = [db_utils.create_share_network_subnet(
id='fake_subnet_id', share_network_id=fake_share_net['id'])]
create_mock = mock.Mock(return_value=stubs.stub_share('1', create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'], display_name=shr['name'],
display_description=shr['description'], display_description=shr['description'],
@ -361,7 +363,8 @@ class ShareAPITest(test.TestCase):
self.mock_object(common, 'check_share_network_is_active', self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True)) mock.Mock(return_value=True))
self.mock_object( self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id') db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value=share_net_subnets))
body = {"share": copy.deepcopy(shr)} body = {"share": copy.deepcopy(shr)}
req = fakes.HTTPRequest.blank('/v1/fake/shares') req = fakes.HTTPRequest.blank('/v1/fake/shares')
@ -391,6 +394,8 @@ class ShareAPITest(test.TestCase):
"share_network_id": parent_share_net "share_network_id": parent_share_net
} }
fake_share_net = {'id': parent_share_net} fake_share_net = {'id': parent_share_net}
share_net_subnets = [db_utils.create_share_network_subnet(
id='fake_subnet_id', share_network_id=fake_share_net['id'])]
create_mock = mock.Mock(return_value=stubs.stub_share('1', create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'], display_name=shr['name'],
display_description=shr['description'], display_description=shr['description'],
@ -413,7 +418,8 @@ class ShareAPITest(test.TestCase):
self.mock_object(share_api.API, 'get_share_network', mock.Mock( self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value=fake_share_net)) return_value=fake_share_net))
self.mock_object( self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id') db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value=share_net_subnets))
body = {"share": copy.deepcopy(shr)} body = {"share": copy.deepcopy(shr)}
req = fakes.HTTPRequest.blank('/v1/fake/shares') req = fakes.HTTPRequest.blank('/v1/fake/shares')
@ -468,6 +474,8 @@ class ShareAPITest(test.TestCase):
"share_network_id": parent_share_net "share_network_id": parent_share_net
} }
fake_share_net = {'id': parent_share_net} fake_share_net = {'id': parent_share_net}
share_net_subnets = [db_utils.create_share_network_subnet(
id='fake_subnet_id', share_network_id=fake_share_net['id'])]
create_mock = mock.Mock(return_value=stubs.stub_share('1', create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'], display_name=shr['name'],
display_description=shr['description'], display_description=shr['description'],
@ -490,7 +498,8 @@ class ShareAPITest(test.TestCase):
self.mock_object(share_api.API, 'get_share_network', mock.Mock( self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value=fake_share_net)) return_value=fake_share_net))
self.mock_object( self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id') db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value=share_net_subnets))
body = {"share": copy.deepcopy(shr)} body = {"share": copy.deepcopy(shr)}
req = fakes.HTTPRequest.blank('/v1/fake/shares', version=microversion) req = fakes.HTTPRequest.blank('/v1/fake/shares', version=microversion)
@ -558,7 +567,7 @@ class ShareAPITest(test.TestCase):
self.mock_object(db, 'share_network_get', self.mock_object(db, 'share_network_get',
mock.Mock(side_effect=share_network_side_effect)) mock.Mock(side_effect=share_network_side_effect))
self.mock_object( self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id', db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value=None)) mock.Mock(return_value=None))
self.mock_object(common, 'check_share_network_is_active') self.mock_object(common, 'check_share_network_is_active')

View File

@ -20,6 +20,7 @@ import ddt
from oslo_db import exception as db_exception from oslo_db import exception as db_exception
from manila.api import common from manila.api import common
from manila.api.openstack import api_version_request as api_version
from manila.api.v2 import share_network_subnets from manila.api.v2 import share_network_subnets
from manila.db import api as db_api from manila.db import api as db_api
from manila import exception from manila import exception
@ -61,16 +62,17 @@ class ShareNetworkSubnetControllerTest(test.TestCase):
mock.Mock(return_value=fake_az)) mock.Mock(return_value=fake_az))
self.share_network = db_utils.create_share_network( self.share_network = db_utils.create_share_network(
name='fake_network', id='fake_sn_id') name='fake_network', id='fake_sn_id')
self.share_server = db_utils.create_share_server(
share_network_subnet_id='fake_sns_id')
self.subnet = db_utils.create_share_network_subnet( self.subnet = db_utils.create_share_network_subnet(
share_network_id=self.share_network['id']) share_network_id=self.share_network['id'])
self.share_server = db_utils.create_share_server(
share_network_subnets=[self.subnet])
self.share = db_utils.create_share() self.share = db_utils.create_share()
def test_share_network_subnet_delete(self): def test_share_network_subnet_delete(self):
req = fakes.HTTPRequest.blank('/subnets/%s' % self.subnet['id'], req = fakes.HTTPRequest.blank('/subnets/%s' % self.subnet['id'],
version="2.51") version="2.51")
context = req.environ['manila.context'] context = req.environ['manila.context']
self.subnet['share_servers'] = [self.share_server]
mock_sns_get = self.mock_object( mock_sns_get = self.mock_object(
db_api, 'share_network_subnet_get', db_api, 'share_network_subnet_get',
@ -150,6 +152,7 @@ class ShareNetworkSubnetControllerTest(test.TestCase):
req = fakes.HTTPRequest.blank('/subnets/%s' % self.subnet['id'], req = fakes.HTTPRequest.blank('/subnets/%s' % self.subnet['id'],
version="2.51") version="2.51")
context = req.environ['manila.context'] context = req.environ['manila.context']
self.subnet['share_servers'] = [self.share_server]
mock_sns_get = self.mock_object( mock_sns_get = self.mock_object(
db_api, 'share_network_subnet_get', db_api, 'share_network_subnet_get',
@ -182,7 +185,10 @@ class ShareNetworkSubnetControllerTest(test.TestCase):
req = fakes.HTTPRequest.blank('/subnets/%s' % self.subnet['id'], req = fakes.HTTPRequest.blank('/subnets/%s' % self.subnet['id'],
version="2.51") version="2.51")
context = req.environ['manila.context'] context = req.environ['manila.context']
self.subnet['share_servers'] = [self.share_server]
mock_network_get = self.mock_object(
db_api, 'share_network_get')
mock_sns_get = self.mock_object( mock_sns_get = self.mock_object(
db_api, 'share_network_subnet_get', db_api, 'share_network_subnet_get',
mock.Mock(return_value=self.subnet)) mock.Mock(return_value=self.subnet))
@ -196,6 +202,8 @@ class ShareNetworkSubnetControllerTest(test.TestCase):
self.share_network['id'], self.share_network['id'],
self.subnet['id']) self.subnet['id'])
mock_network_get.assert_called_once_with(
context, self.share_network['id'])
mock_sns_get.assert_called_once_with( mock_sns_get.assert_called_once_with(
context, self.subnet['id']) context, self.subnet['id'])
mock_all_get_all_shares_by_ss.assert_called_once_with( mock_all_get_all_shares_by_ss.assert_called_once_with(
@ -204,34 +212,6 @@ class ShareNetworkSubnetControllerTest(test.TestCase):
self.mock_policy_check.assert_called_once_with( self.mock_policy_check.assert_called_once_with(
context, self.resource_name, 'delete') context, self.resource_name, 'delete')
@ddt.data((None, fake_default_subnet, None),
(fake_az, None, fake_subnet_with_az))
@ddt.unpack
def test__validate_subnet(self, az, default_subnet, subnet_az):
req = fakes.HTTPRequest.blank('/subnets', version='2.51')
context = req.environ['manila.context']
mock_get_default_sns = self.mock_object(
db_api, 'share_network_subnet_get_default_subnet',
mock.Mock(return_value=default_subnet))
mock_get_subnet_by_az = self.mock_object(
db_api, 'share_network_subnet_get_by_availability_zone_id',
mock.Mock(return_value=subnet_az))
self.assertRaises(exc.HTTPConflict,
self.controller._validate_subnet,
context,
self.share_network['id'],
az)
if az:
mock_get_subnet_by_az.assert_called_once_with(
context, self.share_network['id'], az['id'])
mock_get_default_sns.assert_not_called()
else:
mock_get_default_sns.assert_called_once_with(
context, self.share_network['id'])
mock_get_subnet_by_az.assert_not_called()
def _setup_create_test_request_body(self): def _setup_create_test_request_body(self):
body = { body = {
'share_network_id': self.share_network['id'], 'share_network_id': self.share_network['id'],
@ -241,131 +221,145 @@ class ShareNetworkSubnetControllerTest(test.TestCase):
} }
return body return body
def test_subnet_create(self): @ddt.data({'version': "2.51", 'has_share_servers': False},
req = fakes.HTTPRequest.blank('/subnets', version="2.51") {'version': "2.70", 'has_share_servers': False},
{'version': "2.70", 'has_share_servers': True})
@ddt.unpack
def test_subnet_create(self, version, has_share_servers):
req = fakes.HTTPRequest.blank('/subnets', version=version)
multiple_subnet_support = (req.api_version_request >=
api_version.APIVersionRequest("2.70"))
context = req.environ['manila.context']
body = {
'share-network-subnet': self._setup_create_test_request_body()
}
sn_id = body['share-network-subnet']['share_network_id']
expected_subnet = copy.deepcopy(self.subnet)
if has_share_servers:
expected_subnet['share_servers'] = [self.share_server]
mock_validate_subnet_create = self.mock_object(
common, 'validate_subnet_create',
mock.Mock(return_value=(self.share_network, [expected_subnet])))
mock_subnet_create = self.mock_object(
db_api, 'share_network_subnet_create',
mock.Mock(return_value=expected_subnet))
mock_update_net_allocations = self.mock_object(
self.controller.share_api,
'update_share_server_network_allocations',
mock.Mock(return_value=expected_subnet))
mock_share_network_subnet_get = self.mock_object(
db_api, 'share_network_subnet_get',
mock.Mock(return_value=expected_subnet))
fake_data = body['share-network-subnet']
fake_data['share_network_id'] = self.share_network['id']
res = self.controller.create(
req, body['share-network-subnet']['share_network_id'], body)
view_subnet = {
'id': expected_subnet.get('id'),
'availability_zone': expected_subnet.get('availability_zone'),
'share_network_id': expected_subnet.get('share_network_id'),
'share_network_name': expected_subnet['share_network_name'],
'created_at': expected_subnet.get('created_at'),
'segmentation_id': expected_subnet.get('segmentation_id'),
'neutron_subnet_id': expected_subnet.get('neutron_subnet_id'),
'updated_at': expected_subnet.get('updated_at'),
'neutron_net_id': expected_subnet.get('neutron_net_id'),
'ip_version': expected_subnet.get('ip_version'),
'cidr': expected_subnet.get('cidr'),
'network_type': expected_subnet.get('network_type'),
'mtu': expected_subnet.get('mtu'),
'gateway': expected_subnet.get('gateway')
}
self.assertEqual(view_subnet, res['share_network_subnet'])
mock_share_network_subnet_get.assert_called_once_with(
context, expected_subnet['id'])
mock_validate_subnet_create.assert_called_once_with(
context, sn_id, fake_data, multiple_subnet_support)
if has_share_servers:
fake_data['share_servers'] = [self.share_server]
mock_update_net_allocations.assert_called_once_with(
context, self.share_network, fake_data)
else:
mock_subnet_create.assert_called_once_with(
context, fake_data)
@ddt.data({'exception1': exception.ServiceIsDown(),
'exc_raise': exc.HTTPInternalServerError},
{'exception1': exception.InvalidShareNetwork(),
'exc_raise': exc.HTTPBadRequest},
{'exception1': db_exception.DBError(),
'exc_raise': exc.HTTPInternalServerError})
@ddt.unpack
def test_subnet_create_fail_update_network_allocation(self, exception1,
exc_raise):
req = fakes.HTTPRequest.blank('/subnets', version="2.70")
multiple_subnet_support = (req.api_version_request >=
api_version.APIVersionRequest("2.70"))
context = req.environ['manila.context'] context = req.environ['manila.context']
body = { body = {
'share-network-subnet': self._setup_create_test_request_body() 'share-network-subnet': self._setup_create_test_request_body()
} }
sn_id = body['share-network-subnet']['share_network_id'] sn_id = body['share-network-subnet']['share_network_id']
expected_result = copy.deepcopy(body) expected_subnet = copy.deepcopy(self.subnet)
expected_result['share-network-subnet']['id'] = self.subnet['id'] expected_subnet['share_servers'] = [self.share_server]
mock_check_net_and_subnet_id = self.mock_object(
common, 'check_net_id_and_subnet_id')
mock_validate_subnet = self.mock_object(
self.controller, '_validate_subnet')
mock_subnet_create = self.mock_object(
db_api, 'share_network_subnet_create',
mock.Mock(return_value=self.subnet))
self.controller.create( mock_validate_subnet_create = self.mock_object(
req, body['share-network-subnet']['share_network_id'], body) common, 'validate_subnet_create',
mock.Mock(return_value=(self.share_network, [expected_subnet])))
mock_update_net_allocations = self.mock_object(
self.controller.share_api,
'update_share_server_network_allocations',
mock.Mock(side_effect=exception1))
mock_check_net_and_subnet_id.assert_called_once_with( fake_data = body['share-network-subnet']
body['share-network-subnet']) fake_data['share_network_id'] = self.share_network['id']
mock_validate_subnet.assert_called_once_with( fake_data['share_servers'] = [self.share_server]
context, sn_id, az=fake_az)
mock_subnet_create.assert_called_once_with(
context, body['share-network-subnet'])
def test_subnet_create_share_network_not_found(self): self.assertRaises(exc_raise,
fake_sn_id = 'fake_id'
req = fakes.HTTPRequest.blank('/subnets', version="2.51")
context = req.environ['manila.context']
body = {
'share-network-subnet': self._setup_create_test_request_body()
}
mock_sn_get = self.mock_object(
db_api, 'share_network_get',
mock.Mock(side_effect=exception.ShareNetworkNotFound(
share_network_id=fake_sn_id)))
self.assertRaises(exc.HTTPNotFound,
self.controller.create, self.controller.create,
req, req,
fake_sn_id, body['share-network-subnet']['share_network_id'],
body) body)
mock_sn_get.assert_called_once_with(context, fake_sn_id)
def test_subnet_create_az_not_found(self): mock_validate_subnet_create.assert_called_once_with(
context, sn_id, fake_data, multiple_subnet_support)
mock_update_net_allocations.assert_called_once_with(
context, self.share_network, fake_data)
def test_subnet_create_invalid_body(self):
fake_sn_id = 'fake_id' fake_sn_id = 'fake_id'
req = fakes.HTTPRequest.blank('/subnets', version="2.51") req = fakes.HTTPRequest.blank('/subnets', version="2.51")
context = req.environ['manila.context'] body = {}
body = {
'share-network-subnet': self._setup_create_test_request_body()
}
mock_sn_get = self.mock_object(db_api, 'share_network_get')
mock_az_get = self.mock_object(
db_api, 'availability_zone_get',
mock.Mock(side_effect=exception.AvailabilityZoneNotFound(id='')))
expected_az = body['share-network-subnet']['availability_zone']
self.assertRaises(exc.HTTPBadRequest, self.assertRaises(exc.HTTPBadRequest,
self.controller.create, self.controller.create,
req, req,
fake_sn_id, fake_sn_id,
body) body)
mock_sn_get.assert_called_once_with(context, fake_sn_id)
mock_az_get.assert_called_once_with(
context, expected_az)
def test_subnet_create_subnet_default_or_same_az_exists(self): @ddt.data("2.51", "2.70")
fake_sn_id = 'fake_id' def test_subnet_create_subnet_db_error(self, version):
req = fakes.HTTPRequest.blank('/subnets', version="2.51") req = fakes.HTTPRequest.blank('/subnets', version=version)
context = req.environ['manila.context']
body = { body = {
'share-network-subnet': self._setup_create_test_request_body() 'share-network-subnet': self._setup_create_test_request_body()
} }
mock_sn_get = self.mock_object(db_api, 'share_network_get') expected_subnet = copy.deepcopy(self.subnet)
mock__validate_subnet = self.mock_object( self.mock_object(
self.controller, '_validate_subnet', common, 'validate_subnet_create',
mock.Mock(side_effect=exc.HTTPConflict('')) mock.Mock(return_value=(self.share_network, [expected_subnet])))
) self.mock_object(
expected_az = body['share-network-subnet']['availability_zone']
self.assertRaises(exc.HTTPConflict,
self.controller.create,
req,
fake_sn_id,
body)
mock_sn_get.assert_called_once_with(context, fake_sn_id)
self.mock_az_get.assert_called_once_with(context, expected_az)
mock__validate_subnet.assert_called_once_with(
context, fake_sn_id, az=fake_az)
def test_subnet_create_subnet_db_error(self):
fake_sn_id = 'fake_sn_id'
req = fakes.HTTPRequest.blank('/subnets', version="2.51")
context = req.environ['manila.context']
body = {
'share-network-subnet': self._setup_create_test_request_body()
}
mock_sn_get = self.mock_object(db_api, 'share_network_get')
mock__validate_subnet = self.mock_object(
self.controller, '_validate_subnet')
mock_db_subnet_create = self.mock_object(
db_api, 'share_network_subnet_create', db_api, 'share_network_subnet_create',
mock.Mock(side_effect=db_exception.DBError())) mock.Mock(side_effect=db_exception.DBError()))
expected_data = copy.deepcopy(body['share-network-subnet'])
expected_data['availability_zone_id'] = fake_az['id']
expected_data.pop('availability_zone')
self.assertRaises(exc.HTTPInternalServerError, self.assertRaises(exc.HTTPInternalServerError,
self.controller.create, self.controller.create,
req, req,
fake_sn_id, 'fake_sn_id',
body) body)
mock_sn_get.assert_called_once_with(context, fake_sn_id)
self.mock_az_get.assert_called_once_with(context, fake_az['name'])
mock__validate_subnet.assert_called_once_with(
context, fake_sn_id, az=fake_az)
mock_db_subnet_create.assert_called_once_with(
context, expected_data
)
def test_show_subnet(self): def test_show_subnet(self):
subnet = db_utils.create_share_network_subnet( subnet = db_utils.create_share_network_subnet(
id='fake_sns_2', share_network_id=self.share_network['id']) id='fake_sns_2', share_network_id=self.share_network['id'])

View File

@ -776,6 +776,13 @@ class ShareNetworkAPITest(test.TestCase):
share_nw, share_nw,
self.body) self.body)
def test_update_invalid_body(self):
self.assertRaises(webob_exc.HTTPUnprocessableEntity,
self.controller.update,
self.req,
'fake_sn_id',
None)
@mock.patch.object(db_api, 'share_network_get', mock.Mock()) @mock.patch.object(db_api, 'share_network_get', mock.Mock())
def test_update_invalid_key_in_use(self): def test_update_invalid_key_in_use(self):
share_nw = fake_share_network.copy() share_nw = fake_share_network.copy()
@ -829,8 +836,8 @@ class ShareNetworkAPITest(test.TestCase):
self.mock_object( self.mock_object(
self.controller, '_share_network_subnets_contain_share_servers', self.controller, '_share_network_subnets_contain_share_servers',
mock.Mock(return_value=False)) mock.Mock(return_value=False))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet', self.mock_object(db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=fake_share_network_subnet)) mock.Mock(return_value=[fake_share_network_subnet]))
self.mock_object(db_api, 'share_network_subnet_update') self.mock_object(db_api, 'share_network_subnet_update')
body = {share_networks.RESOURCE_NAME: {'neutron_subnet_id': body = {share_networks.RESOURCE_NAME: {'neutron_subnet_id':
@ -844,18 +851,20 @@ class ShareNetworkAPITest(test.TestCase):
self.req, self.req,
share_nw, share_nw,
body) body)
db_api.share_network_subnet_get_default_subnet.assert_called_once_with( (db_api.share_network_subnet_get_default_subnets.
self.context, share_nw) assert_called_once_with(self.context, share_nw))
db_api.share_network_subnet_update.assert_called_once_with( db_api.share_network_subnet_update.assert_called_once_with(
self.context, fake_share_network_subnet['id'], self.context, fake_share_network_subnet['id'],
body['share_network']) body['share_network'])
@ddt.data((webob_exc.HTTPBadRequest, fake_share_network_subnet, None, @ddt.data((webob_exc.HTTPBadRequest, 1, None,
'new subnet'),
(webob_exc.HTTPBadRequest, 2, None,
'new subnet'), 'new subnet'),
(webob_exc.HTTPBadRequest, None, 'neutron net', None)) (webob_exc.HTTPBadRequest, None, 'neutron net', None))
@ddt.unpack @ddt.unpack
def test_update_default_subnet_errors(self, exception_to_raise, def test_update_default_subnet_errors(self, exception_to_raise,
get_default_subnet_return, get_default_subnet_return_length,
neutron_net_id, neutron_subnet_id): neutron_net_id, neutron_subnet_id):
share_nw = 'fake network id' share_nw = 'fake network id'
self.mock_object(db_api, 'share_network_get', self.mock_object(db_api, 'share_network_get',
@ -863,15 +872,21 @@ class ShareNetworkAPITest(test.TestCase):
self.mock_object( self.mock_object(
self.controller, '_share_network_subnets_contain_share_servers', self.controller, '_share_network_subnets_contain_share_servers',
mock.Mock(return_value=False)) mock.Mock(return_value=False))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet', self.mock_object(db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=get_default_subnet_return)) mock.Mock(return_value=None))
if get_default_subnet_return: if get_default_subnet_return_length:
fake_subnet = copy.deepcopy(fake_share_network_subnet) fake_subnet = copy.deepcopy(fake_share_network_subnet)
fake_subnet['neutron_net_id'] = None fake_subnet['neutron_net_id'] = None
fake_subnet['neutron_subnet_id'] = None fake_subnet['neutron_subnet_id'] = None
db_api.share_network_subnet_get_default_subnet.return_value = (
fake_subnet) if get_default_subnet_return_length == 1:
(db_api.share_network_subnet_get_default_subnets.
return_value) = [fake_subnet]
elif get_default_subnet_return_length == 2:
(db_api.share_network_subnet_get_default_subnets.
return_value) = [fake_subnet, fake_subnet]
body = { body = {
share_networks.RESOURCE_NAME: { share_networks.RESOURCE_NAME: {
'neutron_net_id': neutron_net_id, 'neutron_net_id': neutron_net_id,
@ -885,8 +900,8 @@ class ShareNetworkAPITest(test.TestCase):
share_nw, share_nw,
body) body)
db_api.share_network_subnet_get_default_subnet.assert_called_once_with( (db_api.share_network_subnet_get_default_subnets.
self.context, share_nw) assert_called_once_with(self.context, share_nw))
@ddt.data(*set(("1.0", "2.25", "2.26", api_version._MAX_API_VERSION))) @ddt.data(*set(("1.0", "2.25", "2.26", api_version._MAX_API_VERSION)))
def test_add_security_service(self, microversion): def test_add_security_service(self, microversion):
@ -1620,3 +1635,82 @@ class ShareNetworkAPITest(test.TestCase):
db_api.share_network_update.assert_called_once_with( db_api.share_network_update.assert_called_once_with(
request.environ['manila.context'], request.environ['manila.context'],
share_network['id'], {'status': 'active'}) share_network['id'], {'status': 'active'})
@ddt.data([], ['fake_server'])
def test_share_network_subnet_create_check(self, servers):
body = {
'share_network_subnet_create_check': {
'reset_operation': False,
'availability_zone': 'fake_az',
}
}
request = fakes.HTTPRequest.blank(
'/share-networks', use_admin_context=True, version='2.70')
context = request.environ['manila.context']
share_net = 'fake_net'
subnet = {'share_servers': servers}
existing_subnets = [subnet]
mock_validate_subnet = self.mock_object(
common, 'validate_subnet_create',
mock.Mock(return_value=(share_net, existing_subnets)))
share_api_return = {
'compatible': not bool(servers),
'hosts_check_result': {}
}
mock_check_update = self.mock_object(
self.controller.share_api,
'check_update_share_server_network_allocations',
mock.Mock(return_value=share_api_return))
subnet_view = 'fake_subnet'
mock_view = self.mock_object(
self.controller._view_builder,
'build_share_network_subnet_create_check',
mock.Mock(return_value=subnet_view))
net_id = 'fake_net_id'
response = self.controller.share_network_subnet_create_check(
request, net_id, body)
self.assertEqual(subnet_view, response)
data = body['share_network_subnet_create_check']
mock_validate_subnet.assert_called_once_with(
context, net_id, data, True)
if servers:
data['share_servers'] = servers
mock_check_update.assert_called_once_with(
context, share_net, data, False)
else:
mock_check_update.assert_not_called()
mock_view.assert_called_once_with(request, share_api_return)
@ddt.data(
(exception.ServiceIsDown(message='fake'),
webob_exc.HTTPInternalServerError),
(exception.InvalidShareNetwork(message='fake'),
webob_exc.HTTPBadRequest))
@ddt.unpack
def test_share_network_subnet_create_check_api_failed(
self, captured_exception, exception_to_be_raised):
body = {
'share_network_subnet_create_check': {
'reset_operation': False,
'availability_zone': 'fake_az',
}
}
request = fakes.HTTPRequest.blank(
'/share-networks', use_admin_context=True, version='2.70')
share_net = 'fake_net'
subnet = {'share_servers': 'fake_server'}
existing_subnets = [subnet]
self.mock_object(
common, 'validate_subnet_create',
mock.Mock(return_value=(share_net, existing_subnets)))
self.mock_object(
self.controller.share_api,
'check_update_share_server_network_allocations',
mock.Mock(side_effect=captured_exception))
self.assertRaises(exception_to_be_raised,
self.controller.share_network_subnet_create_check,
request, 'fake_net_id', body)

View File

@ -120,17 +120,18 @@ class ShareServerControllerTest(test.TestCase):
version="2.49") version="2.49")
context = req.environ['manila.context'] context = req.environ['manila.context']
share_network = db_utils.create_share_network(name=share_net_name) share_network = db_utils.create_share_network(name=share_net_name)
share_net_subnet = db_utils.create_share_network_subnet( share_net_subnet = [db_utils.create_share_network_subnet(
share_network_id=share_network['id']) share_network_id=share_network['id'])]
share_server = db_utils.create_share_server( share_server = db_utils.create_share_server(
share_network_subnet_id=share_net_subnet['id'], share_network_subnet_id=share_net_subnet[0]['id'],
host='fake_host', host='fake_host',
identifier='fake_identifier', identifier='fake_identifier',
is_auto_deletable=False) is_auto_deletable=False,
share_network_subnets=share_net_subnet)
self.mock_object(db_api, 'share_network_get', mock.Mock( self.mock_object(db_api, 'share_network_get', mock.Mock(
return_value=share_network)) return_value=share_network))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet', self.mock_object(db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=share_net_subnet)) mock.Mock(return_value=share_net_subnet))
self.mock_object(utils, 'validate_service_host') self.mock_object(utils, 'validate_service_host')
@ -151,7 +152,8 @@ class ShareServerControllerTest(test.TestCase):
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'host': 'fake_host', 'host': 'fake_host',
'share_network_id': 'share_network_id':
share_server['share_network_subnet']['share_network_id'], (share_server['share_network_subnets'][0]
['share_network_id']),
'created_at': share_server['created_at'], 'created_at': share_server['created_at'],
'backend_details': {}, 'backend_details': {},
'identifier': share_server['identifier'], 'identifier': share_server['identifier'],
@ -163,12 +165,12 @@ class ShareServerControllerTest(test.TestCase):
'fake_net_name') 'fake_net_name')
else: else:
expected_result['share_server']['share_network_name'] = ( expected_result['share_server']['share_network_name'] = (
share_net_subnet['share_network_id']) share_net_subnet[0]['share_network_id'])
req_params = body['share_server'] req_params = body['share_server']
manage_share_server_mock.assert_called_once_with( manage_share_server_mock.assert_called_once_with(
context, req_params['identifier'], req_params['host'], context, req_params['identifier'], req_params['host'],
share_net_subnet, req_params['driver_options']) share_net_subnet[0], req_params['driver_options'])
self.assertEqual(expected_result, result) self.assertEqual(expected_result, result)
@ -178,32 +180,23 @@ class ShareServerControllerTest(test.TestCase):
def test_manage_invalid(self): def test_manage_invalid(self):
req = fakes.HTTPRequest.blank('/manage_share_server', req = fakes.HTTPRequest.blank('/manage_share_server',
use_admin_context=True, version="2.49") use_admin_context=True, version="2.49")
context = req.environ['manila.context']
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
share_net_subnet = db_utils.create_share_network_subnet( share_net_subnet = [db_utils.create_share_network_subnet(
share_network_id=share_network['id']) share_network_id=share_network['id'])]
body = { body = {
'share_server': self._setup_manage_test_request_body() 'share_server': self._setup_manage_test_request_body()
} }
body['share_server']['driver_options'] = []
self.mock_object(utils, 'validate_service_host') self.mock_object(utils, 'validate_service_host')
self.mock_object(db_api, 'share_network_get', self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network)) mock.Mock(return_value=share_network))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet', self.mock_object(db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=share_net_subnet)) mock.Mock(return_value=share_net_subnet))
manage_share_server_mock = self.mock_object(
share_api.API, 'manage_share_server',
mock.Mock(side_effect=exception.InvalidInput('foobar')))
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.manage, req, body) self.controller.manage, req, body)
req_params = body['share_server']
manage_share_server_mock.assert_called_once_with(
context, req_params['identifier'], req_params['host'],
share_net_subnet, req_params['driver_options'])
def test_manage_forbidden(self): def test_manage_forbidden(self):
"""Tests share server manage without admin privileges""" """Tests share server manage without admin privileges"""
req = fakes.HTTPRequest.blank('/manage_share_server', version="2.49") req = fakes.HTTPRequest.blank('/manage_share_server', version="2.49")
@ -211,12 +204,12 @@ class ShareServerControllerTest(test.TestCase):
self.mock_object(share_api.API, 'manage_share_server', error) self.mock_object(share_api.API, 'manage_share_server', error)
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
share_net_subnet = db_utils.create_share_network_subnet( share_net_subnet = [db_utils.create_share_network_subnet(
share_network_id=share_network['id']) share_network_id=share_network['id'])]
self.mock_object(db_api, 'share_network_get', mock.Mock( self.mock_object(db_api, 'share_network_get', mock.Mock(
return_value=share_network)) return_value=share_network))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet', self.mock_object(db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=share_net_subnet)) mock.Mock(return_value=share_net_subnet))
self.mock_object(utils, 'validate_service_host') self.mock_object(utils, 'validate_service_host')
@ -281,12 +274,12 @@ class ShareServerControllerTest(test.TestCase):
self.mock_object(utils, 'validate_service_host', error) self.mock_object(utils, 'validate_service_host', error)
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
share_net_subnet = db_utils.create_share_network_subnet( share_net_subnet = [db_utils.create_share_network_subnet(
share_network_id=share_network['id']) share_network_id=share_network['id'])]
self.mock_object(db_api, 'share_network_get', mock.Mock( self.mock_object(db_api, 'share_network_get', mock.Mock(
return_value=share_network)) return_value=share_network))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet', self.mock_object(db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=share_net_subnet)) mock.Mock(return_value=share_net_subnet))
self.mock_object(common, 'check_share_network_is_active', self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True)) mock.Mock(return_value=True))
@ -296,7 +289,7 @@ class ShareServerControllerTest(test.TestCase):
{'share_server': self._setup_manage_test_request_body()}) {'share_server': self._setup_manage_test_request_body()})
common.check_share_network_is_active.assert_called_once_with( common.check_share_network_is_active.assert_called_once_with(
share_net_subnet['share_network']) share_net_subnet[0]['share_network'])
policy.check_policy.assert_called_once_with( policy.check_policy.assert_called_once_with(
context, self.resource_name, 'manage_share_server') context, self.resource_name, 'manage_share_server')
@ -305,12 +298,12 @@ class ShareServerControllerTest(test.TestCase):
context = req.environ['manila.context'] context = req.environ['manila.context']
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
share_net_subnet = db_utils.create_share_network_subnet( share_net_subnet = [db_utils.create_share_network_subnet(
share_network_id=share_network['id']) share_network_id=share_network['id'])]
self.mock_object(db_api, 'share_network_get', mock.Mock( self.mock_object(db_api, 'share_network_get', mock.Mock(
return_value=share_network)) return_value=share_network))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet', self.mock_object(db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=share_net_subnet)) mock.Mock(return_value=share_net_subnet))
self.mock_object(utils, 'validate_service_host') self.mock_object(utils, 'validate_service_host')
self.mock_object(common, 'check_share_network_is_active', self.mock_object(common, 'check_share_network_is_active',
@ -321,7 +314,7 @@ class ShareServerControllerTest(test.TestCase):
{'share_server': self._setup_manage_test_request_body()}) {'share_server': self._setup_manage_test_request_body()})
common.check_share_network_is_active.assert_called_once_with( common.check_share_network_is_active.assert_called_once_with(
share_net_subnet['share_network']) share_net_subnet[0]['share_network'])
policy.check_policy.assert_called_once_with( policy.check_policy.assert_called_once_with(
context, self.resource_name, 'manage_share_server') context, self.resource_name, 'manage_share_server')
@ -377,16 +370,16 @@ class ShareServerControllerTest(test.TestCase):
context = req.environ['manila.context'] context = req.environ['manila.context']
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
body = {'share_server': self._setup_manage_test_request_body()} body = {'share_server': self._setup_manage_test_request_body()}
share_net_subnet = db_utils.create_share_network_subnet( share_net_subnet = [db_utils.create_share_network_subnet(
share_network_id=share_network['id']) share_network_id=share_network['id'])]
body['share_server']['share_network_subnet_id'] = ( body['share_server']['share_network_subnet_id'] = (
share_net_subnet['id'] if body_contains_subnet else None) share_net_subnet[0]['id'] if body_contains_subnet else None)
self.mock_object( self.mock_object(
db_api, 'share_network_subnet_get', db_api, 'share_network_subnet_get_all_with_same_az',
mock.Mock(side_effect=exception.ShareNetworkSubnetNotFound( mock.Mock(side_effect=exception.ShareNetworkSubnetNotFound(
share_network_subnet_id='fake'))) share_network_subnet_id='fake')))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet', self.mock_object(db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=None)) mock.Mock(return_value=None))
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
@ -397,10 +390,47 @@ class ShareServerControllerTest(test.TestCase):
policy.check_policy.assert_called_once_with( policy.check_policy.assert_called_once_with(
context, self.resource_name, 'manage_share_server') context, self.resource_name, 'manage_share_server')
if body_contains_subnet: if body_contains_subnet:
db_api.share_network_subnet_get.assert_called_once_with( (db_api.share_network_subnet_get_all_with_same_az.
context, share_net_subnet['id']) assert_called_once_with(context, share_net_subnet[0]['id']))
else: else:
(db_api.share_network_subnet_get_default_subnet (db_api.share_network_subnet_get_default_subnets
.assert_called_once_with(
context, body['share_server']['share_network_id']))
@ddt.data(True, False)
def test__validate_manage_share_server_error_multiple_subnet(
self, body_contains_subnet):
req = fakes.HTTPRequest.blank('/manage', version="2.70")
context = req.environ['manila.context']
share_network = db_utils.create_share_network()
body = {'share_server': self._setup_manage_test_request_body()}
share_net_subnets = [
db_utils.create_share_network_subnet(
share_network_id=share_network['id']),
db_utils.create_share_network_subnet(
share_network_id=share_network['id'], id='fake_sns_id_2'),
]
body['share_server']['share_network_subnet_id'] = (
share_net_subnets[0]['id'] if body_contains_subnet else None)
self.mock_object(
db_api, 'share_network_subnet_get_all_with_same_az',
mock.Mock(return_value=share_net_subnets))
self.mock_object(db_api, 'share_network_subnet_get_default_subnets',
mock.Mock(return_value=share_net_subnets))
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.manage,
req,
body)
policy.check_policy.assert_called_once_with(
context, self.resource_name, 'manage_share_server')
if body_contains_subnet:
(db_api.share_network_subnet_get_all_with_same_az.
assert_called_once_with(context, share_net_subnets[0]['id']))
else:
(db_api.share_network_subnet_get_default_subnets
.assert_called_once_with( .assert_called_once_with(
context, body['share_server']['share_network_id'])) context, body['share_server']['share_network_id']))
@ -442,6 +472,23 @@ class ShareServerControllerTest(test.TestCase):
get_mock.assert_called_once_with(context, 'fake_server_id') get_mock.assert_called_once_with(context, 'fake_server_id')
def test_unmanage_share_server_multiple_subnets_fail(self):
"""Tests unmanaging share servers"""
server = self._setup_unmanage_tests(multiple_subnets=True)
get_mock = self.mock_object(db_api, 'share_server_get',
mock.Mock(return_value=server))
req = fakes.HTTPRequest.blank('/unmanage_share_server', version="2.70")
context = req.environ['manila.context']
body = {'unmanage': {'force': True}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.unmanage,
req,
server['id'],
body)
get_mock.assert_called_once_with(context, server['id'])
@ddt.data(constants.STATUS_MANAGING, constants.STATUS_DELETING, @ddt.data(constants.STATUS_MANAGING, constants.STATUS_DELETING,
constants.STATUS_CREATING, constants.STATUS_UNMANAGING) constants.STATUS_CREATING, constants.STATUS_UNMANAGING)
def test_unmanage_share_server_invalid_statuses(self, status): def test_unmanage_share_server_invalid_statuses(self, status):
@ -461,18 +508,24 @@ class ShareServerControllerTest(test.TestCase):
get_mock.assert_called_once_with(context, server['id']) get_mock.assert_called_once_with(context, server['id'])
def _setup_unmanage_tests(self, status=constants.STATUS_ACTIVE): def _setup_unmanage_tests(self, status=constants.STATUS_ACTIVE,
server = db_utils.create_share_server( multiple_subnets=False):
id='fake_server_id', status=status)
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
network_subnet = db_utils.create_share_network_subnet( network_subnets = [db_utils.create_share_network_subnet(
share_network_id=share_network['id']) id='fake_sns_id', share_network_id=share_network['id'])]
if multiple_subnets:
share_network1 = db_utils.create_share_network()
network_subnets.append(db_utils.create_share_network_subnet(
share_network_id=share_network1['id'], id='fake_sns_id_2'))
server = db_utils.create_share_server(
id='fake_server_id', status=status,
share_network_subnets=network_subnets)
self.mock_object(db_api, 'share_server_get', self.mock_object(db_api, 'share_server_get',
mock.Mock(return_value=server)) mock.Mock(return_value=server))
self.mock_object(db_api, 'share_network_get', self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network)) mock.Mock(return_value=share_network))
self.mock_object(db_api, 'share_network_subnet_get', self.mock_object(db_api, 'share_network_subnet_get',
mock.Mock(return_value=network_subnet)) mock.Mock(return_value=network_subnets))
return server return server
@ddt.data(exception.ShareServerInUse, exception.PolicyNotAuthorized) @ddt.data(exception.ShareServerInUse, exception.PolicyNotAuthorized)
@ -505,13 +558,11 @@ class ShareServerControllerTest(test.TestCase):
'/v2/share-servers/fake_server_id/', version="2.63") '/v2/share-servers/fake_server_id/', version="2.63")
context = req.environ['manila.context'] context = req.environ['manila.context']
share_server = db_utils.create_share_server() share_server = db_utils.create_share_server()
network_subnet = db_utils.create_share_network_subnet() network_subnets = [db_utils.create_share_network_subnet()]
share_server['share_network_subnets'] = network_subnets
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
get_mock = self.mock_object( get_mock = self.mock_object(
db_api, 'share_server_get', mock.Mock(return_value=share_server)) db_api, 'share_server_get', mock.Mock(return_value=share_server))
get_subnet_mock = self.mock_object(
db_api, 'share_network_subnet_get',
mock.Mock(return_value=network_subnet))
get_network_mock = self.mock_object( get_network_mock = self.mock_object(
db_api, 'share_network_get', db_api, 'share_network_get',
mock.Mock(return_value=share_network)) mock.Mock(return_value=share_network))
@ -526,10 +577,9 @@ class ShareServerControllerTest(test.TestCase):
'fake_server_id', 'fake_server_id',
body) body)
get_mock.assert_called_once_with(context, 'fake_server_id') get_mock.assert_called_once_with(context, 'fake_server_id')
get_subnet_mock.assert_called_once_with(
context, share_server.get('share_network_subnet_id'))
get_network_mock.assert_called_once_with( get_network_mock.assert_called_once_with(
context, network_subnet['share_network_id']) context,
share_server['share_network_subnets'][0]['share_network_id'])
is_active_mock.assert_called_once_with(share_network) is_active_mock.assert_called_once_with(share_network)
def _get_server_migration_request(self, server_id, version='2.57'): def _get_server_migration_request(self, server_id, version='2.57'):
@ -589,11 +639,12 @@ class ShareServerControllerTest(test.TestCase):
def test__share_server_migration_start_conflict(self, api_exception, def test__share_server_migration_start_conflict(self, api_exception,
expected_exception): expected_exception):
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
share_network_subnet = db_utils.create_share_network_subnet( share_network_subnet = [db_utils.create_share_network_subnet(
share_network_id=share_network['id']) share_network_id=share_network['id'])]
server = db_utils.create_share_server( server = db_utils.create_share_server(
id='fake_server_id', status=constants.STATUS_ACTIVE, id='fake_server_id', status=constants.STATUS_ACTIVE,
share_network_subnet_id=share_network_subnet['id']) share_network_subnet_id=share_network_subnet[0]['id'])
server['share_network_subnets'] = share_network_subnet
req = self._get_server_migration_request(server['id']) req = self._get_server_migration_request(server['id'])
context = req.environ['manila.context'] context = req.environ['manila.context']
body = { body = {

View File

@ -715,6 +715,8 @@ class ShareAPITest(test.TestCase):
"share_network_id": "fakenetid" "share_network_id": "fakenetid"
} }
fake_network = {'id': 'fakenetid'} fake_network = {'id': 'fakenetid'}
share_net_subnets = [db_utils.create_share_network_subnet(
id='fake_subnet_id', share_network_id=fake_network['id'])]
create_mock = mock.Mock(return_value=stubs.stub_share('1', create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'], display_name=shr['name'],
display_description=shr['description'], display_description=shr['description'],
@ -728,7 +730,8 @@ class ShareAPITest(test.TestCase):
self.mock_object(common, 'check_share_network_is_active', self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True)) mock.Mock(return_value=True))
self.mock_object( self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id') db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value=share_net_subnets))
body = {"share": copy.deepcopy(shr)} body = {"share": copy.deepcopy(shr)}
req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7') req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')
@ -1342,6 +1345,8 @@ class ShareAPITest(test.TestCase):
} }
parent_share_net = 444 parent_share_net = 444
fake_network = {'id': parent_share_net} fake_network = {'id': parent_share_net}
share_net_subnets = [db_utils.create_share_network_subnet(
id='fake_subnet_id', share_network_id=fake_network['id'])]
create_mock = mock.Mock(return_value=stubs.stub_share('1', create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'], display_name=shr['name'],
display_description=shr['description'], display_description=shr['description'],
@ -1364,7 +1369,8 @@ class ShareAPITest(test.TestCase):
self.mock_object(share_api.API, 'get_share_network', mock.Mock( self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value=fake_network)) return_value=fake_network))
self.mock_object( self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id') db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value=share_net_subnets))
body = {"share": copy.deepcopy(shr)} body = {"share": copy.deepcopy(shr)}
req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7') req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')
@ -1391,6 +1397,8 @@ class ShareAPITest(test.TestCase):
"snapshot_id": 333, "snapshot_id": 333,
"share_network_id": parent_share_net, "share_network_id": parent_share_net,
} }
share_net_subnets = [db_utils.create_share_network_subnet(
id='fake_subnet_id', share_network_id=parent_share_net)]
create_mock = mock.Mock(return_value=stubs.stub_share('1', create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'], display_name=shr['name'],
display_description=shr['description'], display_description=shr['description'],
@ -1413,7 +1421,8 @@ class ShareAPITest(test.TestCase):
self.mock_object(common, 'check_share_network_is_active', self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True)) mock.Mock(return_value=True))
self.mock_object( self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id') db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value=share_net_subnets))
body = {"share": copy.deepcopy(shr)} body = {"share": copy.deepcopy(shr)}
req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7') req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')

View File

@ -65,6 +65,9 @@ class ViewBuilderTestCase(test.TestCase):
status_and_sec_serv_update = ( status_and_sec_serv_update = (
api_version.APIVersionRequest(microversion) >= api_version.APIVersionRequest(microversion) >=
api_version.APIVersionRequest('2.63')) api_version.APIVersionRequest('2.63'))
network_allocation_update_support = (
api_version.APIVersionRequest(microversion) >=
api_version.APIVersionRequest('2.69'))
req = fakes.HTTPRequest.blank('/share-networks', version=microversion) req = fakes.HTTPRequest.blank('/share-networks', version=microversion)
expected_keys = { expected_keys = {
'id', 'name', 'project_id', 'created_at', 'updated_at', 'id', 'name', 'project_id', 'created_at', 'updated_at',
@ -85,6 +88,8 @@ class ViewBuilderTestCase(test.TestCase):
expected_keys.add('nova_net_id') expected_keys.add('nova_net_id')
if status_and_sec_serv_update: if status_and_sec_serv_update:
expected_keys.update({'status', 'security_service_update_support'}) expected_keys.update({'status', 'security_service_update_support'})
if network_allocation_update_support:
expected_keys.add('network_allocation_update_support')
result = self.builder.build_share_network(req, share_network_data) result = self.builder.build_share_network(req, share_network_data)
self.assertEqual(1, len(result)) self.assertEqual(1, len(result))
@ -137,6 +142,10 @@ class ViewBuilderTestCase(test.TestCase):
status_and_sec_serv_update = ( status_and_sec_serv_update = (
api_version.APIVersionRequest(microversion) >= api_version.APIVersionRequest(microversion) >=
api_version.APIVersionRequest('2.63')) api_version.APIVersionRequest('2.63'))
network_allocation_update_support = (
api_version.APIVersionRequest(microversion) >=
api_version.APIVersionRequest('2.69'))
req = fakes.HTTPRequest.blank('/share-networks', version=microversion) req = fakes.HTTPRequest.blank('/share-networks', version=microversion)
expected_networks_list = [] expected_networks_list = []
for share_network in share_networks: for share_network in share_networks:
@ -181,7 +190,13 @@ class ViewBuilderTestCase(test.TestCase):
expected_data.update( expected_data.update(
{'status': 'active', {'status': 'active',
'security_service_update_support': False}) 'security_service_update_support': False})
if network_allocation_update_support:
share_network.update(
{'network_allocation_update_support': None})
expected_data.update(
{'network_allocation_update_support': None})
expected_networks_list.append(expected_data) expected_networks_list.append(expected_data)
expected = {'share_networks': expected_networks_list} expected = {'share_networks': expected_networks_list}
result = self.builder.build_share_networks(req, share_networks, result = self.builder.build_share_networks(req, share_networks,
@ -248,3 +263,20 @@ class ViewBuilderTestCase(test.TestCase):
hosts_result) hosts_result)
self.assertEqual(expected, result) self.assertEqual(expected, result)
@ddt.data(True, False)
def test_build_share_network_subnet_create_check(self, is_admin):
req = fakes.HTTPRequest.blank('/share-networks',
use_admin_context=is_admin)
hosts_result = {
'compatible': True,
'hosts_check_result': {'hostA': True}
}
expected = {'compatible': True}
if is_admin:
expected['hosts_check_result'] = hosts_result['hosts_check_result']
result = self.builder.build_share_network_subnet_create_check(
req, hosts_result)
self.assertEqual(expected, result)

View File

@ -446,26 +446,28 @@ class ManilaCmdManageTestCase(test.TestCase):
share_servers = 'server_id_a,server_id_b' share_servers = 'server_id_a,server_id_b'
share_server_list = [server.strip() share_server_list = [server.strip()
for server in share_servers.split(",")] for server in share_servers.split(",")]
capability = 'security_service_update_support' capabilities = "security_service_update_support" \
values_to_update = { ",network_allocation_update_support"
capability: True capabilities_list = capabilities.split(",")
} values_to_update = [
{capabilities_list[0]: True,
capabilities_list[1]: True}]
with mock.patch('sys.stdout', new=io.StringIO()) as output: with mock.patch('sys.stdout', new=io.StringIO()) as output:
self.server_cmds.update_share_server_capabilities( self.server_cmds.update_share_server_capabilities(
share_servers, capability, True) share_servers, capabilities, True)
expected_op = ("The capability(ies) %(cap)s of the following share " expected_op = ("The capability(ies) %(cap)s of the following share "
"server(s) %(servers)s was(were) updated to " "server(s) %(servers)s was(were) updated to "
"%(value)s.") % { "%(value)s.") % {
'cap': [capability], 'cap': capabilities_list,
'servers': share_server_list, 'servers': share_server_list,
'value': True, 'value': True,
} }
self.assertEqual(expected_op, output.getvalue().strip()) self.assertEqual(expected_op, output.getvalue().strip())
db.share_servers_update.assert_called_once_with( db.share_servers_update.assert_called_once_with(
'admin_ctxt', share_server_list, values_to_update) 'admin_ctxt', share_server_list, values_to_update[0])
def test_share_server_update_capability_not_supported(self): def test_share_server_update_capability_not_supported(self):
share_servers = 'server_id_a' share_servers = 'server_id_a'

View File

@ -3084,3 +3084,94 @@ class ShareIsSoftDeleted(BaseMigrationChecks):
self.test_case.assertFalse(hasattr(s, 'is_soft_deleted')) self.test_case.assertFalse(hasattr(s, 'is_soft_deleted'))
self.test_case.assertFalse(hasattr(s, self.test_case.assertFalse(hasattr(s,
'scheduled_to_be_deleted_at')) 'scheduled_to_be_deleted_at'))
@map_to_migration('a87e0fb17dee')
class ShareServerMultipleSubnets(BaseMigrationChecks):
def setup_upgrade_data(self, engine):
user_id = 'user_id_multiple_subnets'
project_id = 'project_id_multiple_subnets'
# Create share network
share_network_data = {
'id': uuidutils.generate_uuid(),
'user_id': user_id,
'project_id': project_id,
}
sn_table = utils.load_table('share_networks', engine)
engine.execute(sn_table.insert(share_network_data))
# Create share network subnets
share_network_subnet_data = {
'id': uuidutils.generate_uuid(),
'share_network_id': share_network_data['id']
}
sns_table = utils.load_table('share_network_subnets', engine)
engine.execute(sns_table.insert(share_network_subnet_data))
# Create share server
share_server_data = {
'id': uuidutils.generate_uuid(),
'host': 'fake_host',
'status': 'active',
'share_network_subnet_id': share_network_subnet_data['id'],
}
ss_table = utils.load_table('share_servers', engine)
engine.execute(ss_table.insert(share_server_data))
def check_upgrade(self, engine, data):
ss_sns_map_table = utils.load_table(
'share_server_share_network_subnet_mappings', engine)
ss_table = utils.load_table('share_servers', engine)
sns_table = utils.load_table('share_network_subnets', engine)
na_table = utils.load_table('network_allocations', engine)
na_record = engine.execute(na_table.select()).first()
self.test_case.assertFalse(na_record is None)
self.test_case.assertTrue(
hasattr(na_record, 'share_network_subnet_id'))
for map_record in engine.execute(ss_sns_map_table.select()):
self.test_case.assertTrue(
hasattr(map_record, 'share_network_subnet_id'))
self.test_case.assertTrue(
hasattr(map_record, 'share_server_id'))
ss_record = engine.execute(
ss_table
.select()
.where(ss_table.c.id == map_record['share_server_id'])
).first()
self.test_case.assertFalse(ss_record is None)
self.test_case.assertFalse(
hasattr(ss_record, 'share_network_subnet_id'))
self.test_case.assertTrue(
hasattr(ss_record, 'network_allocation_update_support'))
sns_record = engine.execute(
sns_table
.select()
.where(sns_table.c.id == map_record['share_network_subnet_id'])
).first()
self.test_case.assertFalse(sns_record is None)
def check_downgrade(self, engine):
ss_table = utils.load_table('share_servers', engine)
na_table = utils.load_table('network_allocations', engine)
self.test_case.assertRaises(
sa_exc.NoSuchTableError, utils.load_table,
'share_server_share_network_subnet_mappings', engine)
for ss_record in engine.execute(ss_table.select()):
self.test_case.assertTrue(
hasattr(ss_record, 'share_network_subnet_id'))
self.test_case.assertFalse(
hasattr(ss_record, 'network_allocation_update_support'))
na_record = engine.execute(
na_table
.select()
).first()
self.test_case.assertFalse(
hasattr(na_record, 'share_network_subnet_id'))

View File

@ -366,8 +366,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
def test_share_filter_all_by_share_server(self): def test_share_filter_all_by_share_server(self):
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
share_server = db_utils.create_share_server( share_server = db_utils.create_share_server()
share_network_id=share_network['id'])
share = db_utils.create_share(share_server_id=share_server['id'], share = db_utils.create_share(share_server_id=share_server['id'],
share_network_id=share_network['id']) share_network_id=share_network['id'])
@ -379,8 +378,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
def test_share_in_recycle_bin_filter_all_by_share_server(self): def test_share_in_recycle_bin_filter_all_by_share_server(self):
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
share_server = db_utils.create_share_server( share_server = db_utils.create_share_server()
share_network_id=share_network['id'])
share = db_utils.create_share(share_server_id=share_server['id'], share = db_utils.create_share(share_server_id=share_server['id'],
share_network_id=share_network['id'], share_network_id=share_network['id'],
is_soft_deleted=True) is_soft_deleted=True)
@ -393,8 +391,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
def test_share_in_recycle_bin_filter_all_by_share_network(self): def test_share_in_recycle_bin_filter_all_by_share_network(self):
share_network = db_utils.create_share_network() share_network = db_utils.create_share_network()
share_server = db_utils.create_share_server( share_server = db_utils.create_share_server()
share_network_id=share_network['id'])
share = db_utils.create_share(share_server_id=share_server['id'], share = db_utils.create_share(share_server_id=share_server['id'],
share_network_id=share_network['id'], share_network_id=share_network['id'],
is_soft_deleted=True) is_soft_deleted=True)
@ -798,7 +795,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
db_utils.create_share_replica(share_id=share_2['id']) db_utils.create_share_replica(share_id=share_2['id'])
expected_ss_keys = { expected_ss_keys = {
'backend_details', 'host', 'id', 'backend_details', 'host', 'id',
'share_network_subnet_id', 'status', 'share_network_subnet_ids', 'status',
} }
expected_share_keys = { expected_share_keys = {
'project_id', 'share_type_id', 'display_name', 'project_id', 'share_type_id', 'display_name',
@ -846,7 +843,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
share_server_id=share_server['id']) share_server_id=share_server['id'])
expected_ss_keys = { expected_ss_keys = {
'backend_details', 'host', 'id', 'backend_details', 'host', 'id',
'share_network_subnet_id', 'status', 'share_network_subnet_ids', 'status',
} }
expected_share_keys = { expected_share_keys = {
'project_id', 'share_type_id', 'display_name', 'project_id', 'share_type_id', 'display_name',
@ -911,7 +908,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
session = db_api.get_session() session = db_api.get_session()
expected_ss_keys = { expected_ss_keys = {
'backend_details', 'host', 'id', 'backend_details', 'host', 'id',
'share_network_subnet_id', 'status', 'share_network_subnet_ids', 'status',
} }
expected_share_keys = { expected_share_keys = {
'project_id', 'share_type_id', 'display_name', 'project_id', 'share_type_id', 'display_name',
@ -999,7 +996,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
) )
expected_extra_keys = { expected_extra_keys = {
'backend_details', 'host', 'id', 'backend_details', 'host', 'id',
'share_network_subnet_id', 'status', 'share_network_subnet_ids', 'status',
} }
with session.begin(): with session.begin():
share_replica = db_api.share_replica_get( share_replica = db_api.share_replica_get(
@ -2866,11 +2863,12 @@ class ShareNetworkSubnetDatabaseAPITestCase(BaseDatabaseAPITestCase):
{'id': 'fake_id_3', 'identifier': 'fake_identifier', {'id': 'fake_id_3', 'identifier': 'fake_identifier',
'host': 'fake_host'}]) 'host': 'fake_host'}])
def test_get_with_share_servers(self, share_servers): def test_get_with_share_servers(self, share_servers):
db_api.share_network_subnet_create(self.fake_context, share_net_subnets = [
self.subnet_dict) db_api.share_network_subnet_create(
self.fake_context, self.subnet_dict)]
for share_server in share_servers: for share_server in share_servers:
share_server['share_network_subnet_id'] = self.subnet_dict['id'] share_server['share_network_subnets'] = share_net_subnets
db_api.share_server_create(self.fake_context, share_server) db_api.share_server_create(self.fake_context, share_server)
result = db_api.share_network_subnet_get(self.fake_context, result = db_api.share_network_subnet_get(self.fake_context,
@ -2880,8 +2878,11 @@ class ShareNetworkSubnetDatabaseAPITestCase(BaseDatabaseAPITestCase):
len(result['share_servers'])) len(result['share_servers']))
for index, share_server in enumerate(share_servers): for index, share_server in enumerate(share_servers):
self._check_fields(expected=share_server, result = db_api.share_network_subnet_get_all_by_share_server_id(
actual=result['share_servers'][index]) self.fake_context, share_server['id'])
for key, value in share_server['share_network_subnets'][0].items():
if key != 'share_servers':
self.assertEqual(value, result[0][key])
def test_get_not_found(self): def test_get_not_found(self):
db_api.share_network_subnet_create(self.fake_context, self.subnet_dict) db_api.share_network_subnet_create(self.fake_context, self.subnet_dict)
@ -2967,18 +2968,43 @@ class ShareNetworkSubnetDatabaseAPITestCase(BaseDatabaseAPITestCase):
self.subnet_dict['availability_zone_id'] = az['id'] self.subnet_dict['availability_zone_id'] = az['id']
db_api.share_network_subnet_create(self.fake_context, self.subnet_dict) db_api.share_network_subnet_create(self.fake_context, self.subnet_dict)
result = db_api.share_network_subnet_get_by_availability_zone_id( result = db_api.share_network_subnets_get_all_by_availability_zone_id(
self.fake_context, self.subnet_dict['share_network_id'], az['id']) self.fake_context, self.subnet_dict['share_network_id'], az['id'])
self._check_fields(expected=self.subnet_dict, actual=result) self._check_fields(expected=self.subnet_dict, actual=result[0])
def test_get_az_subnets(self):
az = db_api.availability_zone_create_if_not_exist(self.fake_context,
'fake_zone_id')
self.subnet_dict['availability_zone_id'] = az['id']
db_api.share_network_subnet_create(self.fake_context, self.subnet_dict)
result = db_api.share_network_subnet_get_all_with_same_az(
self.fake_context, self.subnet_dict['id'])
self.subnet_dict['share_network'] = None
self._check_fields(expected=self.subnet_dict, actual=result[0])
def test_get_az_subnets_not_found(self):
self.assertRaises(
exception.ShareNetworkSubnetNotFound,
db_api.share_network_subnet_get_all_with_same_az,
self.fake_context, 'share_network_subnet_id')
def test_get_default_subnet(self): def test_get_default_subnet(self):
db_api.share_network_subnet_create(self.fake_context, self.subnet_dict) db_api.share_network_subnet_create(self.fake_context, self.subnet_dict)
result = db_api.share_network_subnet_get_default_subnet( result = db_api.share_network_subnet_get_default_subnets(
self.fake_context, self.subnet_dict['share_network_id']) self.fake_context, self.subnet_dict['share_network_id'])
self._check_fields(expected=self.subnet_dict, actual=result) self._check_fields(expected=self.subnet_dict, actual=result[0])
def test_get_by_share_server_id_not_found(self):
self.assertRaises(
exception.ShareNetworkSubnetNotFoundByShareServer,
db_api.share_network_subnet_get_all_by_share_server_id,
self.fake_context, 'share_server_id')
@ddt.ddt @ddt.ddt
@ -3144,13 +3170,21 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
self.ctxt = context.RequestContext(user_id='user_id', self.ctxt = context.RequestContext(user_id='user_id',
project_id='project_id', project_id='project_id',
is_admin=True) is_admin=True)
self.share_net_subnets = [
db_utils.create_share_network_subnet(
id=uuidutils.generate_uuid(),
share_network_id=uuidutils.generate_uuid())]
def test_share_server_get(self): def test_share_server_get(self):
expected = db_utils.create_share_server() expected = db_utils.create_share_server(
share_network_subnets=self.share_net_subnets)
server = db_api.share_server_get(self.ctxt, expected['id']) server = db_api.share_server_get(self.ctxt, expected['id'])
self.assertEqual(expected['id'], server['id']) self.assertEqual(expected['id'], server['id'])
self.assertEqual(expected.share_network_subnet_id, self.assertEqual(expected.share_network_subnets[0]['id'],
server.share_network_subnet_id) server.share_network_subnets[0]['id'])
self.assertEqual(
expected.share_network_subnets[0]['share_network_id'],
server.share_network_subnets[0]['share_network_id'])
self.assertEqual(expected.host, server.host) self.assertEqual(expected.host, server.host)
self.assertEqual(expected.status, server.status) self.assertEqual(expected.status, server.status)
@ -3160,10 +3194,14 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
db_api.share_server_get, self.ctxt, fake_id) db_api.share_server_get, self.ctxt, fake_id)
def test_create(self): def test_create(self):
server = db_utils.create_share_server() server = db_utils.create_share_server(
share_network_subnets=self.share_net_subnets)
self.assertTrue(server['id']) self.assertTrue(server['id'])
self.assertEqual(server.share_network_subnet_id, self.assertEqual(server.share_network_subnets[0]['id'],
server['share_network_subnet_id']) server['share_network_subnets'][0]['id'])
self.assertEqual(
server.share_network_subnets[0]['share_network_id'],
server['share_network_subnets'][0]['share_network_id'])
self.assertEqual(server.host, server['host']) self.assertEqual(server.host, server['host'])
self.assertEqual(server.status, server['status']) self.assertEqual(server.status, server['status'])
@ -3181,17 +3219,23 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
self.ctxt, fake_id) self.ctxt, fake_id)
def test_update(self): def test_update(self):
share_net_subnets_update = [
db_utils.create_share_network_subnet(
id=uuidutils.generate_uuid(),
share_network_id=uuidutils.generate_uuid())]
update = { update = {
'share_network_id': 'update_net', 'share_network_subnets': share_net_subnets_update,
'host': 'update_host', 'host': 'update_host',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
} }
server = db_utils.create_share_server() server = db_utils.create_share_server(
share_network_subnets=self.share_net_subnets)
updated_server = db_api.share_server_update(self.ctxt, server['id'], updated_server = db_api.share_server_update(self.ctxt, server['id'],
update) update)
self.assertEqual(server['id'], updated_server['id']) self.assertEqual(server['id'], updated_server['id'])
self.assertEqual(update['share_network_id'], self.assertEqual(
updated_server.share_network_id) update['share_network_subnets'][0]['share_network_id'],
updated_server.share_network_subnets[0]['share_network_id'])
self.assertEqual(update['host'], updated_server.host) self.assertEqual(update['host'], updated_server.host)
self.assertEqual(update['status'], updated_server.status) self.assertEqual(update['status'], updated_server.status)
@ -3201,7 +3245,8 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
db_api.share_server_update, db_api.share_server_update,
self.ctxt, fake_id, {}) self.ctxt, fake_id, {})
def test_get_all_by_host_and_share_net_valid(self): @ddt.data(None, constants.STATUS_SERVER_NETWORK_CHANGE)
def test_get_all_by_host_and_share_net_valid(self, server_status):
subnet_1 = { subnet_1 = {
'id': '1', 'id': '1',
'share_network_id': '1', 'share_network_id': '1',
@ -3210,31 +3255,41 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
'id': '2', 'id': '2',
'share_network_id': '2', 'share_network_id': '2',
} }
valid = { share_net_subnets1 = db_utils.create_share_network_subnet(**subnet_1)
'share_network_subnet_id': '1', share_net_subnets2 = db_utils.create_share_network_subnet(**subnet_2)
valid_no_status = {
'share_network_subnets': [share_net_subnets1],
'host': 'host1', 'host': 'host1',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
} }
valid_with_status = {
'share_network_subnets': [share_net_subnets1],
'host': 'host1',
'status': constants.STATUS_SERVER_NETWORK_CHANGE,
}
invalid = { invalid = {
'share_network_subnet_id': '2', 'share_network_subnets': [share_net_subnets2],
'host': 'host1', 'host': 'host1',
'status': constants.STATUS_ERROR, 'status': constants.STATUS_ERROR,
} }
other = { other = {
'share_network_subnet_id': '1', 'share_network_subnets': [share_net_subnets1],
'host': 'host2', 'host': 'host2',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
} }
db_utils.create_share_network_subnet(**subnet_1) if server_status:
db_utils.create_share_network_subnet(**subnet_2) valid = db_utils.create_share_server(**valid_with_status)
valid = db_utils.create_share_server(**valid) else:
valid = db_utils.create_share_server(**valid_no_status)
db_utils.create_share_server(**invalid) db_utils.create_share_server(**invalid)
db_utils.create_share_server(**other) db_utils.create_share_server(**other)
servers = db_api.share_server_get_all_by_host_and_share_subnet_valid( servers = db_api.share_server_get_all_by_host_and_share_subnet_valid(
self.ctxt, self.ctxt,
host='host1', host='host1',
share_subnet_id='1') share_subnet_id='1',
server_status=server_status)
self.assertEqual(valid['id'], servers[0]['id']) self.assertEqual(valid['id'], servers[0]['id'])
def test_get_all_by_host_and_share_net_not_found(self): def test_get_all_by_host_and_share_net_not_found(self):
@ -3246,17 +3301,14 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
def test_get_all(self): def test_get_all(self):
srv1 = { srv1 = {
'share_network_id': '1',
'host': 'host1', 'host': 'host1',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
} }
srv2 = { srv2 = {
'share_network_id': '1',
'host': 'host1', 'host': 'host1',
'status': constants.STATUS_ERROR, 'status': constants.STATUS_ERROR,
} }
srv3 = { srv3 = {
'share_network_id': '2',
'host': 'host2', 'host': 'host2',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
} }
@ -3296,7 +3348,10 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
def test_get_with_details(self): def test_get_with_details(self):
values = { values = {
'share_network_subnet_id': 'fake-share-net-id', 'share_network_subnets': [
db_utils.create_share_network_subnet(
id='fake_subnet_id',
share_network_id='fake_share_net_id')],
'host': 'hostname', 'host': 'hostname',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
} }
@ -3308,8 +3363,11 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
db_api.share_server_backend_details_set(self.ctxt, srv_id, details) db_api.share_server_backend_details_set(self.ctxt, srv_id, details)
server = db_api.share_server_get(self.ctxt, srv_id) server = db_api.share_server_get(self.ctxt, srv_id)
self.assertEqual(srv_id, server['id']) self.assertEqual(srv_id, server['id'])
self.assertEqual(values['share_network_subnet_id'], self.assertEqual(values['share_network_subnets'][0]['id'],
server.share_network_subnet_id) server.share_network_subnets[0]['id'])
self.assertEqual(
values['share_network_subnets'][0]['share_network_id'],
server.share_network_subnets[0]['share_network_id'])
self.assertEqual(values['host'], server.host) self.assertEqual(values['host'], server.host)
self.assertEqual(values['status'], server.status) self.assertEqual(values['status'], server.status)
self.assertDictEqual(server['backend_details'], details) self.assertDictEqual(server['backend_details'], details)
@ -3331,7 +3389,6 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
def test_share_server_search_by_identifier(self, identifier): def test_share_server_search_by_identifier(self, identifier):
server = { server = {
'share_network_id': 'fake-share-net-id',
'host': 'hostname', 'host': 'hostname',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'is_auto_deletable': True, 'is_auto_deletable': True,
@ -3360,21 +3417,18 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
server_3_is_auto_deletable, server_3_is_auto_deletable,
expected_len): expected_len):
server1 = { server1 = {
'share_network_id': 'fake-share-net-id',
'host': 'hostname', 'host': 'hostname',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'is_auto_deletable': server_1_is_auto_deletable, 'is_auto_deletable': server_1_is_auto_deletable,
'updated_at': datetime.datetime(2018, 5, 1) 'updated_at': datetime.datetime(2018, 5, 1)
} }
server2 = { server2 = {
'share_network_id': 'fake-share-net-id',
'host': 'hostname', 'host': 'hostname',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'is_auto_deletable': server_2_is_auto_deletable, 'is_auto_deletable': server_2_is_auto_deletable,
'updated_at': datetime.datetime(2018, 5, 1) 'updated_at': datetime.datetime(2018, 5, 1)
} }
server3 = { server3 = {
'share_network_id': 'fake-share-net-id',
'host': 'hostname', 'host': 'hostname',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'is_auto_deletable': server_3_is_auto_deletable, 'is_auto_deletable': server_3_is_auto_deletable,
@ -3403,7 +3457,7 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
share_network_subnet = db_utils.create_share_network_subnet( share_network_subnet = db_utils.create_share_network_subnet(
id=uuidutils.generate_uuid(), id=uuidutils.generate_uuid(),
share_network_id=share_network_id) share_network_id=share_network_id)
server_data['share_network_subnet_id'] = share_network_subnet['id'] server_data['share_network_subnets'] = [share_network_subnet]
db_utils.create_share_server(**server_data) db_utils.create_share_server(**server_data)
db_utils.create_share_server() db_utils.create_share_server()
filter_keys = filters.keys() filter_keys = filters.keys()
@ -3417,7 +3471,7 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
self.assertEqual(share_network_subnet['share_network_id'], self.assertEqual(share_network_subnet['share_network_id'],
filters[key]) filters[key])
self.assertEqual(share_network_subnet['id'], self.assertEqual(share_network_subnet['id'],
result['share_network_subnet_id']) result['share_network_subnets'][0]['id'])
else: else:
self.assertEqual(result[key], filters[key]) self.assertEqual(result[key], filters[key])
@ -3534,27 +3588,32 @@ class NetworkAllocationsDatabaseAPITestCase(test.TestCase):
self.user_id = 'user_id' self.user_id = 'user_id'
self.project_id = 'project_id' self.project_id = 'project_id'
self.share_server_id = 'foo_share_server_id' self.share_server_id = 'foo_share_server_id'
self.share_network_subnet_id = 'foo_share_network_subnet_id'
self.ctxt = context.RequestContext( self.ctxt = context.RequestContext(
user_id=self.user_id, project_id=self.project_id, is_admin=True) user_id=self.user_id, project_id=self.project_id, is_admin=True)
self.user_network_allocations = [ self.user_network_allocations = [
{'share_server_id': self.share_server_id, {'share_server_id': self.share_server_id,
'ip_address': '1.1.1.1', 'ip_address': '1.1.1.1',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'label': None}, 'label': None,
'share_network_subnet_id': self.share_network_subnet_id},
{'share_server_id': self.share_server_id, {'share_server_id': self.share_server_id,
'ip_address': '2.2.2.2', 'ip_address': '2.2.2.2',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'label': 'user'}, 'label': 'user',
'share_network_subnet_id': self.share_network_subnet_id},
] ]
self.admin_network_allocations = [ self.admin_network_allocations = [
{'share_server_id': self.share_server_id, {'share_server_id': self.share_server_id,
'ip_address': '3.3.3.3', 'ip_address': '3.3.3.3',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'label': 'admin'}, 'label': 'admin',
'share_network_subnet_id': None},
{'share_server_id': self.share_server_id, {'share_server_id': self.share_server_id,
'ip_address': '4.4.4.4', 'ip_address': '4.4.4.4',
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'label': 'admin'}, 'label': 'admin',
'share_network_subnet_id': None},
] ]
def _setup_network_allocations_get_for_share_server(self): def _setup_network_allocations_get_for_share_server(self):
@ -3566,10 +3625,17 @@ class NetworkAllocationsDatabaseAPITestCase(test.TestCase):
} }
db_api.share_network_create(self.ctxt, share_network_data) db_api.share_network_create(self.ctxt, share_network_data)
# Create share network subnet
share_network_subnet_data = {
'id': self.share_network_subnet_id,
'share_network_id': self.user_id,
}
db_api.share_network_subnet_create(self.ctxt,
share_network_subnet_data)
# Create share server # Create share server
share_server_data = { share_server_data = {
'id': self.share_server_id, 'id': self.share_server_id,
'share_network_id': share_network_data['id'],
'host': 'fake_host', 'host': 'fake_host',
'status': 'active', 'status': 'active',
} }
@ -3644,6 +3710,20 @@ class NetworkAllocationsDatabaseAPITestCase(test.TestCase):
self.ctxt, self.ctxt,
id='fake') id='fake')
def test_network_allocation_get_by_subnet_id(self):
self._setup_network_allocations_get_for_share_server()
result = db_api.network_allocations_get_for_share_server(
self.ctxt, self.share_server_id,
subnet_id=self.share_network_subnet_id)
self.assertEqual(2, len(result))
for network_allocation in result:
self.assertIsInstance(network_allocation, models.NetworkAllocation)
self.assertEqual(self.share_network_subnet_id,
network_allocation.share_network_subnet_id)
@ddt.data(True, False) @ddt.data(True, False)
def test_network_allocation_get_read_deleted(self, read_deleted): def test_network_allocation_get_read_deleted(self, read_deleted):
self._setup_network_allocations_get_for_share_server() self._setup_network_allocations_get_for_share_server()
@ -3798,8 +3878,7 @@ class PurgeDeletedTest(test.TestCase):
# create share server # create share server
db_utils.create_share_server( db_utils.create_share_server(
id=uuidutils.generate_uuid(), id=uuidutils.generate_uuid(),
deleted_at=self._days_ago(start, end), deleted_at=self._days_ago(start, end))
share_network_id=network.id)
# create snapshot # create snapshot
db_api.share_snapshot_create( db_api.share_snapshot_create(
self.context, {'share_id': share['id'], self.context, {'share_id': share['id'],
@ -4360,6 +4439,9 @@ class ShareResourcesAPITestCase(test.TestCase):
share_id = uuidutils.generate_uuid() share_id = uuidutils.generate_uuid()
share_network_id = uuidutils.generate_uuid() share_network_id = uuidutils.generate_uuid()
share_network_subnet_id = uuidutils.generate_uuid() share_network_subnet_id = uuidutils.generate_uuid()
share_net_subnets = [db_utils.create_share_network_subnet(
id=share_network_subnet_id,
share_network_id=share_network_id)]
if '@' in current_host: if '@' in current_host:
if '#' in current_host: if '#' in current_host:
new_host = 'new-controller-X@backendX#poolX' new_host = 'new-controller-X@backendX#poolX'
@ -4400,15 +4482,15 @@ class ShareResourcesAPITestCase(test.TestCase):
status=constants.STATUS_DELETING), status=constants.STATUS_DELETING),
# share servers # share servers
db_utils.create_share_server( db_utils.create_share_server(
share_network_subnet_id=share_network_subnet_id, share_network_subnets=share_net_subnets,
host='controller-0@fancystore01', host='controller-0@fancystore01',
status=constants.STATUS_ACTIVE), status=constants.STATUS_ACTIVE),
db_utils.create_share_server( db_utils.create_share_server(
share_network_subnet_id=share_network_subnet_id, share_network_subnets=share_net_subnets,
host='controller-0@otherstore02#pool100', host='controller-0@otherstore02#pool100',
status=constants.STATUS_ERROR), status=constants.STATUS_ERROR),
db_utils.create_share_server( db_utils.create_share_server(
share_network_subnet_id=share_network_subnet_id, share_network_subnets=share_net_subnets,
host='controller-2@beststore07', host='controller-2@beststore07',
status=constants.STATUS_DELETING), status=constants.STATUS_DELETING),
@ -4425,8 +4507,9 @@ class ShareResourcesAPITestCase(test.TestCase):
self.context, filters={'share_id': share_id}) self.context, filters={'share_id': share_id})
share_groups = db_api.share_group_get_all( share_groups = db_api.share_group_get_all(
self.context, filters={'share_network_id': share_network_id}) self.context, filters={'share_network_id': share_network_id})
share_servers = db_api._server_get_query(self.context).filter_by( share_servers = db_api._server_get_query(self.context).filter(
share_network_subnet_id=share_network_subnet_id).all() models.ShareServer.share_network_subnets.any(
id=share_net_subnets[0]['id'])).all()
self.assertEqual(3, len(share_instances)) self.assertEqual(3, len(share_instances))
self.assertEqual(3, len(share_groups)) self.assertEqual(3, len(share_groups))
self.assertEqual(3, len(share_servers)) self.assertEqual(3, len(share_servers))
@ -4450,6 +4533,9 @@ class ShareResourcesAPITestCase(test.TestCase):
share_id = uuidutils.generate_uuid() share_id = uuidutils.generate_uuid()
share_network_id = uuidutils.generate_uuid() share_network_id = uuidutils.generate_uuid()
share_network_subnet_id = uuidutils.generate_uuid() share_network_subnet_id = uuidutils.generate_uuid()
share_net_subnets = [db_utils.create_share_network_subnet(
id=share_network_subnet_id,
share_network_id=share_network_id)]
if '@' in current_host: if '@' in current_host:
if '#' in current_host: if '#' in current_host:
new_host = 'new-controller-X@backendX#poolX' new_host = 'new-controller-X@backendX#poolX'
@ -4493,15 +4579,15 @@ class ShareResourcesAPITestCase(test.TestCase):
status=constants.STATUS_DELETING), status=constants.STATUS_DELETING),
# share servers # share servers
db_utils.create_share_server( db_utils.create_share_server(
share_network_subnet_id=share_network_subnet_id, share_network_subnets=share_net_subnets,
host='controller-0@fancystore01#pool100', host='controller-0@fancystore01#pool100',
status=constants.STATUS_ACTIVE), status=constants.STATUS_ACTIVE),
db_utils.create_share_server( db_utils.create_share_server(
share_network_subnet_id=share_network_subnet_id, share_network_subnets=share_net_subnets,
host='controller-2@fancystore01', host='controller-2@fancystore01',
status=constants.STATUS_ERROR), status=constants.STATUS_ERROR),
db_utils.create_share_server( db_utils.create_share_server(
share_network_subnet_id=share_network_subnet_id, share_network_subnets=share_net_subnets,
host='controller-2@beststore07#pool200', host='controller-2@beststore07#pool200',
status=constants.STATUS_DELETING), status=constants.STATUS_DELETING),
] ]
@ -4513,8 +4599,9 @@ class ShareResourcesAPITestCase(test.TestCase):
self.context, filters={'share_id': share_id}) self.context, filters={'share_id': share_id})
share_groups = db_api.share_group_get_all( share_groups = db_api.share_group_get_all(
self.context, filters={'share_network_id': share_network_id}) self.context, filters={'share_network_id': share_network_id})
share_servers = db_api._server_get_query(self.context).filter_by( share_servers = db_api._server_get_query(self.context).filter(
share_network_subnet_id=share_network_subnet_id).all() models.ShareServer.share_network_subnets.any(
id=share_net_subnets[0]['id'])).all()
updated_resources = [ updated_resources = [
res for res in share_instances + share_groups + share_servers res for res in share_instances + share_groups + share_servers

View File

@ -214,7 +214,6 @@ def create_share_server(**kwargs):
backend_details = kwargs.pop('backend_details', {}) backend_details = kwargs.pop('backend_details', {})
srv = { srv = {
'host': 'host1', 'host': 'host1',
'share_network_subnet_id': 'fake_srv_id',
'status': constants.STATUS_ACTIVE 'status': constants.STATUS_ACTIVE
} }
share_srv = _create_db_row(db.share_server_create, srv, kwargs) share_srv = _create_db_row(db.share_server_create, srv, kwargs)

View File

@ -305,7 +305,6 @@ def fake_share_server_get():
'status': constants.STATUS_ACTIVE, 'status': constants.STATUS_ACTIVE,
'updated_at': None, 'updated_at': None,
'host': 'fake_host', 'host': 'fake_host',
'share_network_subnet_id': 'fake_sn_id',
'share_network_name': 'fake_sn_name', 'share_network_name': 'fake_sn_name',
'project_id': 'fake_project_id', 'project_id': 'fake_project_id',
'id': 'fake_share_server_id', 'id': 'fake_share_server_id',

View File

@ -122,6 +122,7 @@ fake_network_allocation = {
'cidr': fake_share_network_subnet['cidr'], 'cidr': fake_share_network_subnet['cidr'],
'gateway': fake_share_network_subnet['gateway'], 'gateway': fake_share_network_subnet['gateway'],
'mtu': 1509, 'mtu': 1509,
'share_network_subnet_id': fake_share_network_subnet['id'],
} }
fake_nw_info = { fake_nw_info = {
@ -187,6 +188,7 @@ fake_network_allocation_multi = {
'cidr': fake_neutron_subnet['cidr'], 'cidr': fake_neutron_subnet['cidr'],
'gateway': fake_neutron_subnet['gateway_ip'], 'gateway': fake_neutron_subnet['gateway_ip'],
'mtu': fake_neutron_network_multi['mtu'], 'mtu': fake_neutron_network_multi['mtu'],
'share_network_subnet_id': fake_share_network['id'],
} }
fake_binding_profile = { fake_binding_profile = {
@ -240,9 +242,11 @@ class NeutronNetworkPluginTest(test.TestCase):
has_provider_nw_ext.assert_any_call() has_provider_nw_ext.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context, save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
save_subnet_data.assert_called_once_with(self.fake_context, save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
self.plugin.neutron_api.create_port.assert_called_once_with( self.plugin.neutron_api.create_port.assert_called_once_with(
fake_share_network['project_id'], fake_share_network['project_id'],
network_id=fake_share_network_subnet['neutron_net_id'], network_id=fake_share_network_subnet['neutron_net_id'],
@ -401,6 +405,7 @@ class NeutronNetworkPluginTest(test.TestCase):
'ip_version': fake_share_network_subnet['ip_version'], 'ip_version': fake_share_network_subnet['ip_version'],
'cidr': fake_share_network_subnet['cidr'], 'cidr': fake_share_network_subnet['cidr'],
'mtu': fake_share_network_subnet['mtu'], 'mtu': fake_share_network_subnet['mtu'],
'share_network_subnet_id': fake_share_network_subnet['id'],
} for x in ['192.168.0.11', '192.168.0.12']] } for x in ['192.168.0.11', '192.168.0.12']]
if side_effect: if side_effect:
@ -961,9 +966,11 @@ class NeutronBindNetworkPluginTest(test.TestCase):
self.bind_plugin._has_provider_network_extension.assert_any_call() self.bind_plugin._has_provider_network_extension.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context, save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
save_subnet_data.assert_called_once_with(self.fake_context, save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
expected_kwargs = { expected_kwargs = {
'binding:vnic_type': 'baremetal', 'binding:vnic_type': 'baremetal',
'host_id': 'foohost1', 'host_id': 'foohost1',
@ -1486,9 +1493,11 @@ class NeutronBindSingleNetworkPluginTest(test.TestCase):
self.bind_plugin._has_provider_network_extension.assert_any_call() self.bind_plugin._has_provider_network_extension.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context, save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
save_subnet_data.assert_called_once_with(self.fake_context, save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
expected_kwargs = { expected_kwargs = {
'binding:vnic_type': 'baremetal', 'binding:vnic_type': 'baremetal',
'host_id': 'foohost1', 'host_id': 'foohost1',
@ -1713,9 +1722,11 @@ class NeutronBindNetworkPluginWithNormalTypeTest(test.TestCase):
self.bind_plugin._has_provider_network_extension.assert_any_call() self.bind_plugin._has_provider_network_extension.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context, save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
save_subnet_data.assert_called_once_with(self.fake_context, save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
expected_kwargs = { expected_kwargs = {
'binding:vnic_type': 'normal', 'binding:vnic_type': 'normal',
'host_id': 'foohost1', 'host_id': 'foohost1',
@ -1800,9 +1811,11 @@ class NeutronBindSingleNetworkPluginWithNormalTypeTest(test.TestCase):
self.bind_plugin._has_provider_network_extension.assert_any_call() self.bind_plugin._has_provider_network_extension.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context, save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
save_subnet_data.assert_called_once_with(self.fake_context, save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network_subnet) fake_share_network_subnet,
save_db=True)
expected_kwargs = { expected_kwargs = {
'binding:vnic_type': 'normal', 'binding:vnic_type': 'normal',
'host_id': 'foohost1', 'host_id': 'foohost1',
@ -1866,3 +1879,23 @@ class NeutronBindSingleNetworkPluginWithNormalTypeTest(test.TestCase):
self.assertRaises(exception.NetworkBadConfigurationException, self.assertRaises(exception.NetworkBadConfigurationException,
self.bind_plugin._get_matched_ip_address, self.bind_plugin._get_matched_ip_address,
fix_ips, version) fix_ips, version)
def _setup_include_network_info(self):
data = {
'DEFAULT': {
'neutron_net_id': 'fake net id',
'neutron_subnet_id': 'fake subnet id',
'neutron_physical_net_name': 'net1',
}
}
with test_utils.create_temp_config_with_opts(data):
instance = plugin.NeutronNetworkPlugin()
return instance
def test_include_network_info(self):
instance = self._setup_include_network_info()
self.mock_object(instance, '_store_neutron_net_info')
instance.include_network_info(fake_share_network)
instance._store_neutron_net_info.assert_called_once_with(
None, fake_share_network, save_db=False)

View File

@ -346,7 +346,8 @@ class StandaloneNetworkPluginTest(test.TestCase):
ip_version=6, ip_version=6,
mtu=1500)) mtu=1500))
def test_allocate_network_one_ip_address_ipv4_no_usages_exist(self): @ddt.data('admin', 'user')
def test_allocate_network_one_ip_address_ipv4_no_usages_exist(self, label):
data = { data = {
'DEFAULT': { 'DEFAULT': {
'standalone_network_plugin_network_type': 'vlan', 'standalone_network_plugin_network_type': 'vlan',
@ -356,8 +357,9 @@ class StandaloneNetworkPluginTest(test.TestCase):
}, },
} }
with test_utils.create_temp_config_with_opts(data): with test_utils.create_temp_config_with_opts(data):
instance = plugin.StandaloneNetworkPlugin() instance = plugin.StandaloneNetworkPlugin(label=label)
self.mock_object(instance.db, 'share_network_subnet_update') if label != 'admin':
self.mock_object(instance.db, 'share_network_subnet_update')
self.mock_object(instance.db, 'network_allocation_create') self.mock_object(instance.db, 'network_allocation_create')
self.mock_object( self.mock_object(
instance.db, 'network_allocations_get_by_ip_address', instance.db, 'network_allocations_get_by_ip_address',
@ -376,15 +378,18 @@ class StandaloneNetworkPluginTest(test.TestCase):
'ip_version': 4, 'ip_version': 4,
'mtu': 1500, 'mtu': 1500,
} }
instance.db.share_network_subnet_update.assert_called_once_with( if label != 'admin':
fake_context, fake_share_network_subnet['id'], na_data) instance.db.share_network_subnet_update.assert_called_once_with(
fake_context, fake_share_network_subnet['id'], na_data)
na_data['share_network_subnet_id'] = \
fake_share_network_subnet['id']
instance.db.network_allocations_get_by_ip_address.assert_has_calls( instance.db.network_allocations_get_by_ip_address.assert_has_calls(
[mock.call(fake_context, '10.0.0.2')]) [mock.call(fake_context, '10.0.0.2')])
instance.db.network_allocation_create.assert_called_once_with( instance.db.network_allocation_create.assert_called_once_with(
fake_context, fake_context,
dict(share_server_id=fake_share_server['id'], dict(share_server_id=fake_share_server['id'],
ip_address='10.0.0.2', status=constants.STATUS_ACTIVE, ip_address='10.0.0.2', status=constants.STATUS_ACTIVE,
label='user', **na_data)) label=label, **na_data))
def test_allocate_network_two_ip_addresses_ipv4_two_usages_exist(self): def test_allocate_network_two_ip_addresses_ipv4_two_usages_exist(self):
ctxt = type('FakeCtxt', (object,), {'fake': ['10.0.0.2', '10.0.0.4']}) ctxt = type('FakeCtxt', (object,), {'fake': ['10.0.0.2', '10.0.0.4']})
@ -428,6 +433,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
instance.db.network_allocations_get_by_ip_address.assert_has_calls( instance.db.network_allocations_get_by_ip_address.assert_has_calls(
[mock.call(ctxt, '10.0.0.2'), mock.call(ctxt, '10.0.0.3'), [mock.call(ctxt, '10.0.0.2'), mock.call(ctxt, '10.0.0.3'),
mock.call(ctxt, '10.0.0.4'), mock.call(ctxt, '10.0.0.5')]) mock.call(ctxt, '10.0.0.4'), mock.call(ctxt, '10.0.0.5')])
na_data['share_network_subnet_id'] = fake_share_network_subnet['id']
instance.db.network_allocation_create.assert_has_calls([ instance.db.network_allocation_create.assert_has_calls([
mock.call( mock.call(
ctxt, ctxt,
@ -522,6 +528,10 @@ class StandaloneNetworkPluginTest(test.TestCase):
if not label: if not label:
instance.db.share_network_subnet_update.assert_called_once_with( instance.db.share_network_subnet_update.assert_called_once_with(
fake_context, fake_share_network_subnet['id'], network_data) fake_context, fake_share_network_subnet['id'], network_data)
data_list[0]['share_network_subnet_id'] = (
fake_share_network_subnet['id'])
data_list[1]['share_network_subnet_id'] = (
fake_share_network_subnet['id'])
instance._verify_share_network_subnet.assert_called_once_with( instance._verify_share_network_subnet.assert_called_once_with(
fake_share_server['id'], fake_share_network_subnet) fake_share_server['id'], fake_share_network_subnet)
@ -536,3 +546,22 @@ class StandaloneNetworkPluginTest(test.TestCase):
instance.unmanage_network_allocations('context', 'server_id') instance.unmanage_network_allocations('context', 'server_id')
instance.deallocate_network.assert_called_once_with( instance.deallocate_network.assert_called_once_with(
'context', 'server_id') 'context', 'server_id')
def _setup_include_network_info(self):
data = {
'DEFAULT': {
'standalone_network_plugin_gateway': '192.168.0.1',
'standalone_network_plugin_mask': '24',
},
}
with test_utils.create_temp_config_with_opts(data):
instance = plugin.StandaloneNetworkPlugin()
return instance
def test_include_network_info(self):
instance = self._setup_include_network_info()
self.mock_object(instance, '_save_network_info')
instance.include_network_info(fake_share_network)
instance._save_network_info.assert_called_once_with(
None, fake_share_network, save_db=False)

View File

@ -101,6 +101,8 @@ class HostFiltersTestCase(test.TestCase):
} }
request = self._make_zone_request(None) request = self._make_zone_request(None)
request['request_spec']['availability_zones'] = supported_azs request['request_spec']['availability_zones'] = supported_azs
request['request_spec']['az_request_multiple_subnet_support_map'] = \
{'zone2': 2}
host = fakes.FakeHostState('host1', {'service': service}) host = fakes.FakeHostState('host1', {'service': service})
self.assertEqual(host_passes, self.filter.host_passes(host, request)) self.assertEqual(host_passes, self.filter.host_passes(host, request))

View File

@ -214,6 +214,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, { }, {
'name': 'host2@back1#BBB', 'name': 'host2@back1#BBB',
@ -244,6 +246,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, { }, {
'name': 'host2@back2#CCC', 'name': 'host2@back2#CCC',
@ -274,6 +278,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, },
] ]
@ -326,6 +332,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, { }, {
'name': 'host2@BBB#pool2', 'name': 'host2@BBB#pool2',
@ -357,6 +365,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, { }, {
'name': 'host3@CCC#pool3', 'name': 'host3@CCC#pool3',
@ -388,6 +398,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, { }, {
'name': 'host4@DDD#pool4a', 'name': 'host4@DDD#pool4a',
@ -419,6 +431,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, { }, {
'name': 'host4@DDD#pool4b', 'name': 'host4@DDD#pool4b',
@ -450,6 +464,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, },
] ]
@ -514,6 +530,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, { }, {
'name': 'host2@back1#BBB', 'name': 'host2@back1#BBB',
@ -544,6 +562,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, },
] ]
@ -602,6 +622,8 @@ class HostManagerTestCase(test.TestCase):
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None, 'sg_consistent_snapshot_support': None,
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
}, },
}, },
] ]

View File

@ -372,7 +372,7 @@ class ContainerShareDriverTestCase(test.TestCase):
before, after) before, after)
def test__setup_server_container_fails(self): def test__setup_server_container_fails(self):
network_info = cont_fakes.fake_network() network_info = [cont_fakes.fake_network()]
self.mock_object(self._driver.container, 'start_container') self.mock_object(self._driver.container, 'start_container')
self._driver.container.start_container.side_effect = KeyError() self._driver.container.start_container.side_effect = KeyError()
@ -380,21 +380,21 @@ class ContainerShareDriverTestCase(test.TestCase):
self._driver._setup_server, network_info) self._driver._setup_server, network_info)
def test__setup_server_ok(self): def test__setup_server_ok(self):
network_info = cont_fakes.fake_network() network_info = [cont_fakes.fake_network()]
server_id = self._driver._get_container_name(network_info["server_id"]) server_id = self._driver._get_container_name(
network_info[0]["server_id"])
self.mock_object(self._driver.container, 'start_container') self.mock_object(self._driver.container, 'start_container')
self.mock_object(self._driver, '_get_veth_state') self.mock_object(self._driver, '_get_veth_state')
self.mock_object(self._driver, '_get_corresponding_veth', self.mock_object(self._driver, '_get_corresponding_veth',
mock.Mock(return_value='veth0')) mock.Mock(return_value='veth0'))
self.mock_object(self._driver, '_connect_to_network') self.mock_object(self._driver, '_connect_to_network')
self.assertEqual(network_info['server_id'], self.assertEqual(network_info[0]['server_id'],
self._driver._setup_server(network_info)['id']) self._driver._setup_server(network_info)['id'])
self._driver.container.start_container.assert_called_once_with( self._driver.container.start_container.assert_called_once_with(
server_id) server_id)
self._driver._connect_to_network.assert_called_once_with(server_id, self._driver._connect_to_network.assert_called_once_with(
network_info, server_id, network_info[0], 'veth0')
'veth0')
def test_manage_existing(self): def test_manage_existing(self):

View File

@ -145,6 +145,8 @@ class EMCShareFrameworkTestCase(test.TestCase):
data['max_shares_per_share_server'] = -1 data['max_shares_per_share_server'] = -1
data['max_share_server_size'] = -1 data['max_share_server_size'] = -1
data['security_service_update_support'] = False data['security_service_update_support'] = False
data['share_server_multiple_subnet_support'] = False
data['network_allocation_update_support'] = False
self.assertEqual(data, self.driver._stats) self.assertEqual(data, self.driver._stats)
def _fake_safe_get(self, value): def _fake_safe_get(self, value):
@ -214,3 +216,9 @@ class EMCShareFrameworkTestCase(test.TestCase):
expected = None expected = None
actual = self.driver.get_default_filter_function() actual = self.driver.get_default_filter_function()
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
def test_setup_server(self):
network_info = [{}]
expected = None
result = self.driver._setup_server(network_info)
self.assertEqual(expected, result)

View File

@ -34,6 +34,7 @@ import time
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log from oslo_log import log
from oslo_serialization import jsonutils
from oslo_utils import timeutils from oslo_utils import timeutils
from manila.common import constants from manila.common import constants
@ -141,6 +142,7 @@ class DummyDriver(driver.ShareDriver):
"share_backend_name") or "DummyDriver" "share_backend_name") or "DummyDriver"
self.migration_progress = {} self.migration_progress = {}
self.security_service_update_support = True self.security_service_update_support = True
self.network_allocation_update_support = True
def _verify_configuration(self): def _verify_configuration(self):
allowed_driver_methods = [m for m in dir(self) if m[0] != '_'] allowed_driver_methods = [m for m in dir(self) if m[0] != '_']
@ -184,24 +186,40 @@ class DummyDriver(driver.ShareDriver):
"s_id": snapshot["snapshot_id"].replace("-", "_"), "s_id": snapshot["snapshot_id"].replace("-", "_"),
"si_id": snapshot["id"].replace("-", "_")} "si_id": snapshot["id"].replace("-", "_")}
def _generate_export_locations(self, mountpoint, share_server=None): def _get_export(self, mountpoint, ip, is_admin_only, preferred):
details = share_server["backend_details"] if share_server else { return {
"primary_public_ip": "10.0.0.10", "path": "%(ip)s:%(mp)s" % {"ip": ip, "mp": mountpoint},
"secondary_public_ip": "10.0.0.20", "metadata": {
"service_ip": "11.0.0.11", "preferred": preferred,
},
"is_admin_only": is_admin_only,
} }
return [
{ def _generate_export_locations(self, mountpoint, share_server=None):
"path": "%(ip)s:%(mp)s" % {"ip": ip, "mp": mountpoint}, if share_server:
"metadata": { subnet_allocations = jsonutils.loads(
"preferred": preferred, share_server["backend_details"]["subnet_allocations"])
}, service_ip = share_server["backend_details"]["service_ip"]
"is_admin_only": is_admin_only, else:
} for ip, is_admin_only, preferred in ( subnet_allocations = [{
(details["primary_public_ip"], False, True), "primary_public_ip": "10.0.0.10",
(details["secondary_public_ip"], False, False), "secondary_public_ip": "10.0.0.20",
(details["service_ip"], True, False)) }]
] service_ip = "11.0.0.11"
export_locations = [
self._get_export(mountpoint, service_ip, True, False)]
for subnet_allocation in subnet_allocations:
export_locations.append(
self._get_export(
mountpoint, subnet_allocation["primary_public_ip"],
False, True))
export_locations.append(
self._get_export(
mountpoint, subnet_allocation["secondary_public_ip"],
False, False))
return export_locations
def _create_share(self, share, share_server=None): def _create_share(self, share, share_server=None):
share_proto = share["share_proto"] share_proto = share["share_proto"]
@ -410,16 +428,25 @@ class DummyDriver(driver.ShareDriver):
Redefine it within share driver when it is going to handle share Redefine it within share driver when it is going to handle share
servers. servers.
""" """
common_net_info = network_info[0]
server_details = { server_details = {
"primary_public_ip": network_info[ "service_ip": common_net_info[
"network_allocations"][0]["ip_address"],
"secondary_public_ip": network_info[
"network_allocations"][1]["ip_address"],
"service_ip": network_info[
"admin_network_allocations"][0]["ip_address"], "admin_network_allocations"][0]["ip_address"],
"username": "fake_username", "username": "fake_username",
"server_id": network_info['server_id'] "server_id": common_net_info['server_id'],
} }
subnet_allocations = []
for subnet_info in network_info:
subnet_allocations.append({
"primary_public_ip": subnet_info[
"network_allocations"][0]["ip_address"],
"secondary_public_ip": subnet_info[
"network_allocations"][1]["ip_address"]
})
server_details['subnet_allocations'] = jsonutils.dumps(
subnet_allocations)
return server_details return server_details
@slow_me_down @slow_me_down
@ -460,6 +487,7 @@ class DummyDriver(driver.ShareDriver):
"share_group_stats": { "share_group_stats": {
"consistent_snapshot_support": "pool", "consistent_snapshot_support": "pool",
}, },
'share_server_multiple_subnet_support': True,
} }
if self.configuration.replication_domain: if self.configuration.replication_domain:
data["replication_type"] = "readable" data["replication_type"] = "readable"
@ -828,9 +856,12 @@ class DummyDriver(driver.ShareDriver):
"private storage." % identifier) "private storage." % identifier)
raise exception.ShareBackendException(msg=msg) raise exception.ShareBackendException(msg=msg)
return [server_details['primary_public_ip'], ips = [server_details['service_ip']]
server_details['secondary_public_ip'], subnet_allocations = jsonutils.loads(
server_details['service_ip']] server_details['subnet_allocations'])
for subnet_allocation in subnet_allocations:
ips += list(subnet_allocation.values())
return ips
@slow_me_down @slow_me_down
def manage_server(self, context, share_server, identifier, driver_options): def manage_server(self, context, share_server, identifier, driver_options):
@ -889,3 +920,65 @@ class DummyDriver(driver.ShareDriver):
share_instance_rules, new_security_service, share_instance_rules, new_security_service,
current_security_service=None): current_security_service=None):
return True return True
def check_update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_share_network_subnet, security_services, share_instances,
share_instances_rules):
LOG.debug("Share server %(server)s can be updated with allocations "
"from new subnet.", {'server': share_server['id']})
return True
def update_share_server_network_allocations(
self, context, share_server, current_network_allocations,
new_network_allocations, security_services, shares, snapshots):
subnet_allocations = jsonutils.loads(
share_server['backend_details']['subnet_allocations'])
subnet_allocations.append({
'primary_public_ip': new_network_allocations[
'network_allocations'][0]['ip_address'],
'secondary_public_ip': new_network_allocations[
'network_allocations'][1]['ip_address'],
})
new_server = {
"backend_details": {
"subnet_allocations": jsonutils.dumps(subnet_allocations),
"service_ip": share_server["backend_details"]["service_ip"],
}
}
shares_updates = {}
for instance in shares:
share_name = self._get_share_name(instance)
mountpoint = "/path/to/fake/share/%s" % share_name
export_locations = self._generate_export_locations(
mountpoint, share_server=new_server)
shares_updates.update(
{instance['id']: export_locations}
)
snapshot_updates = {}
for instance in snapshots:
snapshot_name = self._get_snapshot_name(instance)
mountpoint = "/path/to/fake/snapshot/%s" % snapshot_name
snap_export_locations = self._generate_export_locations(
mountpoint, share_server=new_server)
snapshot_updates.update(
{instance['id']: {
'provider_location': mountpoint,
'export_locations': snap_export_locations}}
)
LOG.debug(
"Network update allocations of dummy share server with ID '%s' "
"has been completed.", share_server["id"])
return {
"share_updates": shares_updates,
"snapshot_updates": snapshot_updates,
"server_details": {
"subnet_allocations": (
new_server["backend_details"]["subnet_allocations"])
},
}

View File

@ -270,6 +270,8 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase):
'ipv4_support': True, 'ipv4_support': True,
'ipv6_support': False, 'ipv6_support': False,
'security_service_update_support': False, 'security_service_update_support': False,
'share_server_multiple_subnet_support': False,
'network_allocation_update_support': False,
} }
self.assertEqual(test_data, self._driver._stats) self.assertEqual(test_data, self._driver._stats)

View File

@ -750,6 +750,8 @@ class HPE3ParDriverTestCase(test.TestCase):
'max_share_server_size': -1, 'max_share_server_size': -1,
'max_shares_per_share_server': -1, 'max_shares_per_share_server': -1,
'security_service_update_support': False, 'security_service_update_support': False,
'share_server_multiple_subnet_support': False,
'network_allocation_update_support': False,
} }
result = self.driver.get_share_stats(refresh=True) result = self.driver.get_share_stats(refresh=True)
@ -824,6 +826,8 @@ class HPE3ParDriverTestCase(test.TestCase):
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'security_service_update_support': False, 'security_service_update_support': False,
'share_server_multiple_subnet_support': False,
'network_allocation_update_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'share_group_stats': { 'share_group_stats': {
'consistent_snapshot_support': None, 'consistent_snapshot_support': None,
@ -872,6 +876,8 @@ class HPE3ParDriverTestCase(test.TestCase):
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'security_service_update_support': False, 'security_service_update_support': False,
'share_server_multiple_subnet_support': False,
'network_allocation_update_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'share_group_stats': { 'share_group_stats': {
'consistent_snapshot_support': None, 'consistent_snapshot_support': None,
@ -926,7 +932,7 @@ class HPE3ParDriverTestCase(test.TestCase):
self.init_driver() self.init_driver()
network_info = { network_info = [{
'network_allocations': [ 'network_allocations': [
{'ip_address': constants.EXPECTED_IP_1234}], {'ip_address': constants.EXPECTED_IP_1234}],
'cidr': '/'.join((constants.EXPECTED_IP_1234, 'cidr': '/'.join((constants.EXPECTED_IP_1234,
@ -934,7 +940,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'network_type': constants.EXPECTED_VLAN_TYPE, 'network_type': constants.EXPECTED_VLAN_TYPE,
'segmentation_id': constants.EXPECTED_VLAN_TAG, 'segmentation_id': constants.EXPECTED_VLAN_TAG,
'server_id': constants.EXPECTED_SERVER_ID, 'server_id': constants.EXPECTED_SERVER_ID,
} }]
expected_result = { expected_result = {
'share_server_name': constants.EXPECTED_SERVER_ID, 'share_server_name': constants.EXPECTED_SERVER_ID,
@ -964,7 +970,7 @@ class HPE3ParDriverTestCase(test.TestCase):
self.init_driver() self.init_driver()
network_info = { network_info = [{
'network_allocations': [ 'network_allocations': [
{'ip_address': constants.EXPECTED_IP_1234}], {'ip_address': constants.EXPECTED_IP_1234}],
'cidr': '/'.join((constants.EXPECTED_IP_1234, 'cidr': '/'.join((constants.EXPECTED_IP_1234,
@ -972,7 +978,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'network_type': constants.EXPECTED_VXLAN_TYPE, 'network_type': constants.EXPECTED_VXLAN_TYPE,
'segmentation_id': constants.EXPECTED_VLAN_TAG, 'segmentation_id': constants.EXPECTED_VLAN_TAG,
'server_id': constants.EXPECTED_SERVER_ID, 'server_id': constants.EXPECTED_SERVER_ID,
} }]
metadata = {'request_host': constants.EXPECTED_HOST} metadata = {'request_host': constants.EXPECTED_HOST}
self.assertRaises(exception.NetworkBadConfigurationException, self.assertRaises(exception.NetworkBadConfigurationException,
@ -984,7 +990,7 @@ class HPE3ParDriverTestCase(test.TestCase):
self.init_driver() self.init_driver()
network_info = { network_info = [{
'network_allocations': [ 'network_allocations': [
{'ip_address': constants.EXPECTED_IP_1234}], {'ip_address': constants.EXPECTED_IP_1234}],
'cidr': '/'.join((constants.EXPECTED_IP_1234, 'cidr': '/'.join((constants.EXPECTED_IP_1234,
@ -992,7 +998,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'network_type': constants.EXPECTED_VLAN_TYPE, 'network_type': constants.EXPECTED_VLAN_TYPE,
'segmentation_id': constants.EXPECTED_VLAN_TAG, 'segmentation_id': constants.EXPECTED_VLAN_TAG,
'server_id': constants.EXPECTED_SERVER_ID, 'server_id': constants.EXPECTED_SERVER_ID,
} }]
metadata = {'request_host': constants.EXPECTED_HOST} metadata = {'request_host': constants.EXPECTED_HOST}
expected_vfs = self.driver.fpgs[ expected_vfs = self.driver.fpgs[

View File

@ -1181,7 +1181,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
'id': 'fake_network_allocation_id', 'id': 'fake_network_allocation_id',
'ip_address': '111.111.111.109', 'ip_address': '111.111.111.109',
}] }]
self.fake_network_info = { self.fake_network_info = [{
'server_id': '0', 'server_id': '0',
'segmentation_id': '2', 'segmentation_id': '2',
'cidr': '111.111.111.0/24', 'cidr': '111.111.111.0/24',
@ -1190,7 +1190,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
'security_services': '', 'security_services': '',
'network_allocations': self.fake_network_allocations, 'network_allocations': self.fake_network_allocations,
'network_type': 'vlan', 'network_type': 'vlan',
} }]
self.fake_active_directory = { self.fake_active_directory = {
'type': 'active_directory', 'type': 'active_directory',
'dns_ip': '100.97.5.5', 'dns_ip': '100.97.5.5',
@ -2435,6 +2435,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
"ipv4_support": True, "ipv4_support": True,
"ipv6_support": False, "ipv6_support": False,
"security_service_update_support": False, "security_service_update_support": False,
"share_server_multiple_subnet_support": False,
"network_allocation_update_support": False,
} }
if replication_support: if replication_support:
@ -3323,7 +3325,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
def test_setup_server_invalid_ipv4(self): def test_setup_server_invalid_ipv4(self):
netwot_info_invali_ipv4 = self.fake_network_info netwot_info_invali_ipv4 = self.fake_network_info
netwot_info_invali_ipv4['network_allocations'][0]['ip_address'] = ( netwot_info_invali_ipv4[0]['network_allocations'][0]['ip_address'] = (
"::1/128") "::1/128")
self.assertRaises(exception.InvalidInput, self.assertRaises(exception.InvalidInput,
self.driver._setup_server, self.driver._setup_server,
@ -3332,7 +3334,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
@dec_driver_handles_share_servers @dec_driver_handles_share_servers
def test_setup_server_network_type_error(self): def test_setup_server_network_type_error(self):
vxlan_netwotk_info = self.fake_network_info vxlan_netwotk_info = self.fake_network_info
vxlan_netwotk_info['network_type'] = 'vxlan' vxlan_netwotk_info[0]['network_type'] = 'vxlan'
self.assertRaises(exception.NetworkBadConfigurationException, self.assertRaises(exception.NetworkBadConfigurationException,
self.driver.setup_server, self.driver.setup_server,
vxlan_netwotk_info) vxlan_netwotk_info)
@ -3412,13 +3414,13 @@ class HuaweiShareDriverTestCase(test.TestCase):
logical_port='CTE0.A.H0;CTE0.A.H2;CTE0.B.H0;BOND0') logical_port='CTE0.A.H0;CTE0.A.H2;CTE0.B.H0;BOND0')
self.driver.plugin.configuration.manila_huawei_conf_file = ( self.driver.plugin.configuration.manila_huawei_conf_file = (
self.fake_conf_file) self.fake_conf_file)
fake_network_info = { fake_network_info = [{
'server_id': '0', 'server_id': '0',
'segmentation_id': None, 'segmentation_id': None,
'cidr': '111.111.111.0/24', 'cidr': '111.111.111.0/24',
'network_allocations': self.fake_network_allocations, 'network_allocations': self.fake_network_allocations,
'network_type': None, 'network_type': None,
} }]
self.mock_object(self.driver.plugin, '_get_online_port', self.mock_object(self.driver.plugin, '_get_online_port',
mock.Mock(return_value=(['CTE0.A.H0', 'CTE0.A.H2', mock.Mock(return_value=(['CTE0.A.H0', 'CTE0.A.H2',
'CTE0.B.H0'], ['BOND0']))) 'CTE0.B.H0'], ['BOND0'])))
@ -3476,7 +3478,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
return self.driver.plugin.helper.do_call(*args, **kwargs) return self.driver.plugin.helper.do_call(*args, **kwargs)
fake_network_info = self.fake_network_info fake_network_info = self.fake_network_info
fake_network_info['security_services'] = [ fake_network_info[0]['security_services'] = [
self.fake_active_directory, self.fake_ldap] self.fake_active_directory, self.fake_ldap]
self.mock_object(self.driver.plugin.helper, "delete_vlan") self.mock_object(self.driver.plugin.helper, "delete_vlan")
self.mock_object(self.driver.plugin.helper, "delete_AD_config") self.mock_object(self.driver.plugin.helper, "delete_AD_config")
@ -3509,7 +3511,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
@dec_driver_handles_share_servers @dec_driver_handles_share_servers
def test_setup_server_with_ad_domain_success(self): def test_setup_server_with_ad_domain_success(self):
fake_network_info = self.fake_network_info fake_network_info = self.fake_network_info
fake_network_info['security_services'] = [self.fake_active_directory] fake_network_info[0]['security_services'] = (
[self.fake_active_directory])
self.mock_object(self.driver.plugin.helper, self.mock_object(self.driver.plugin.helper,
"get_AD_config", "get_AD_config",
mock.Mock( mock.Mock(
@ -3531,8 +3534,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
@dec_driver_handles_share_servers @dec_driver_handles_share_servers
def test_setup_server_with_ldap_domain_success(self, server_ips): def test_setup_server_with_ldap_domain_success(self, server_ips):
fake_network_info = self.fake_network_info fake_network_info = self.fake_network_info
fake_network_info['security_services'] = [self.fake_ldap] fake_network_info[0]['security_services'] = [self.fake_ldap]
fake_network_info['security_services'][0]['server'] = server_ips fake_network_info[0]['security_services'][0]['server'] = server_ips
self.mock_object( self.mock_object(
self.driver.plugin.helper, self.driver.plugin.helper,
"get_LDAP_config", "get_LDAP_config",
@ -3547,8 +3550,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
def test_setup_server_with_ldap_domain_fail(self): def test_setup_server_with_ldap_domain_fail(self):
server_ips = "100.97.5.87,100.97.5.88,100.97.5.89,100.97.5.86" server_ips = "100.97.5.87,100.97.5.88,100.97.5.89,100.97.5.86"
fake_network_info = self.fake_network_info fake_network_info = self.fake_network_info
fake_network_info['security_services'] = [self.fake_ldap] fake_network_info[0]['security_services'] = [self.fake_ldap]
fake_network_info['security_services'][0]['server'] = server_ips fake_network_info[0]['security_services'][0]['server'] = server_ips
self.mock_object( self.mock_object(
self.driver.plugin.helper, self.driver.plugin.helper,
"get_LDAP_config", "get_LDAP_config",
@ -3573,7 +3576,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
@dec_driver_handles_share_servers @dec_driver_handles_share_servers
def test_setup_server_with_security_service_invalid(self, data): def test_setup_server_with_security_service_invalid(self, data):
fake_network_info = self.fake_network_info fake_network_info = self.fake_network_info
fake_network_info['security_services'] = [data] fake_network_info[0]['security_services'] = [data]
self.assertRaises(exception.InvalidInput, self.assertRaises(exception.InvalidInput,
self.driver.setup_server, self.driver.setup_server,
fake_network_info) fake_network_info)
@ -3592,7 +3595,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
'server': '', 'server': '',
'domain': ''}, 'domain': ''},
] ]
fake_network_info['security_services'] = ss fake_network_info[0]['security_services'] = ss
self.assertRaises(exception.InvalidInput, self.assertRaises(exception.InvalidInput,
self.driver.setup_server, self.driver.setup_server,
fake_network_info) fake_network_info)
@ -3600,7 +3603,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
@dec_driver_handles_share_servers @dec_driver_handles_share_servers
def test_setup_server_dns_exist_error(self): def test_setup_server_dns_exist_error(self):
fake_network_info = self.fake_network_info fake_network_info = self.fake_network_info
fake_network_info['security_services'] = [self.fake_active_directory] fake_network_info[0]['security_services'] = (
[self.fake_active_directory])
self.mock_object(self.driver.plugin.helper, self.mock_object(self.driver.plugin.helper,
"get_DNS_ip_address", "get_DNS_ip_address",
mock.Mock(return_value=['100.97.5.85'])) mock.Mock(return_value=['100.97.5.85']))
@ -3612,7 +3616,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
@dec_driver_handles_share_servers @dec_driver_handles_share_servers
def test_setup_server_ad_exist_error(self): def test_setup_server_ad_exist_error(self):
fake_network_info = self.fake_network_info fake_network_info = self.fake_network_info
fake_network_info['security_services'] = [self.fake_active_directory] fake_network_info[0]['security_services'] = (
[self.fake_active_directory])
self.mock_object(self.driver.plugin.helper, self.mock_object(self.driver.plugin.helper,
"get_AD_config", "get_AD_config",
mock.Mock( mock.Mock(
@ -3626,7 +3631,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
@dec_driver_handles_share_servers @dec_driver_handles_share_servers
def test_setup_server_ldap_exist_error(self): def test_setup_server_ldap_exist_error(self):
fake_network_info = self.fake_network_info fake_network_info = self.fake_network_info
fake_network_info['security_services'] = [self.fake_ldap] fake_network_info[0]['security_services'] = [self.fake_ldap]
self.mock_object(self.driver.plugin.helper, self.mock_object(self.driver.plugin.helper,
"get_LDAP_config", "get_LDAP_config",
mock.Mock( mock.Mock(
@ -3642,7 +3647,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
fake_active_directory = self.fake_active_directory fake_active_directory = self.fake_active_directory
ip_list = "100.97.5.5,100.97.5.6,100.97.5.7,100.97.5.8" ip_list = "100.97.5.5,100.97.5.6,100.97.5.7,100.97.5.8"
fake_active_directory['dns_ip'] = ip_list fake_active_directory['dns_ip'] = ip_list
fake_network_info['security_services'] = [fake_active_directory] fake_network_info[0]['security_services'] = [fake_active_directory]
self.mock_object( self.mock_object(
self.driver.plugin.helper, self.driver.plugin.helper,
"get_AD_config", "get_AD_config",
@ -3655,7 +3660,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
@dec_driver_handles_share_servers @dec_driver_handles_share_servers
def test_setup_server_with_ad_domain_fail(self): def test_setup_server_with_ad_domain_fail(self):
fake_network_info = self.fake_network_info fake_network_info = self.fake_network_info
fake_network_info['security_services'] = [self.fake_active_directory] fake_network_info[0]['security_services'] = (
[self.fake_active_directory])
self.mock_object(self.driver.plugin, self.mock_object(self.driver.plugin,
'_get_wait_interval', '_get_wait_interval',
mock.Mock(return_value=1)) mock.Mock(return_value=1))

View File

@ -2852,7 +2852,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.fake_src_share_server, self.fake_src_share_server,
self.fake_dest_share_server, self.fake_dest_share_server,
share_instances, [], share_instances, [],
fake.NETWORK_INFO [fake.NETWORK_INFO]
) )
expected_share_updates = { expected_share_updates = {
@ -2932,7 +2932,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.fake_dest_share_server, self.fake_dest_share_server,
self.fake_src_vserver, self.fake_src_vserver,
self.mock_src_client, [fake.SHARE_INSTANCE], self.mock_src_client, [fake.SHARE_INSTANCE],
fake.NETWORK_INFO) [fake.NETWORK_INFO])
dm_session_mock.update_snapmirror_svm.assert_called_once_with( dm_session_mock.update_snapmirror_svm.assert_called_once_with(
self.fake_src_share_server, self.fake_dest_share_server self.fake_src_share_server, self.fake_dest_share_server
@ -2977,7 +2977,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.fake_src_share_server, self.fake_src_share_server,
self.fake_dest_share_server, self.fake_dest_share_server,
[fake.SHARE_INSTANCE], [], [fake.SHARE_INSTANCE], [],
fake.NETWORK_INFO) [fake.NETWORK_INFO])
self.library._get_vserver.assert_has_calls([ self.library._get_vserver.assert_has_calls([
mock.call(share_server=self.fake_src_share_server, mock.call(share_server=self.fake_src_share_server,
@ -3513,7 +3513,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake_vserver_client, 'modify_active_directory_security_service') fake_vserver_client, 'modify_active_directory_security_service')
self.library.update_share_server_security_service( self.library.update_share_server_security_service(
fake_context, fake.SHARE_SERVER, fake_net_info, fake_context, fake.SHARE_SERVER, [fake_net_info],
new_sec_service, current_security_service=curr_sec_service) new_sec_service, current_security_service=curr_sec_service)
dns_ips = set() dns_ips = set()
@ -3530,7 +3530,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock_get_vserver.assert_called_once_with( mock_get_vserver.assert_called_once_with(
share_server=fake.SHARE_SERVER) share_server=fake.SHARE_SERVER)
mock_check_update.assert_called_once_with( mock_check_update.assert_called_once_with(
fake_context, fake.SHARE_SERVER, fake_net_info, new_sec_service, fake_context, fake.SHARE_SERVER, [fake_net_info], new_sec_service,
current_security_service=curr_sec_service) current_security_service=curr_sec_service)
if curr_sec_service is None: if curr_sec_service is None:
@ -3565,13 +3565,13 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertRaises( self.assertRaises(
exception.NetAppException, exception.NetAppException,
self.library.update_share_server_security_service, self.library.update_share_server_security_service,
fake_context, fake.SHARE_SERVER, fake_net_info, fake_context, fake.SHARE_SERVER, [fake_net_info],
new_sec_service, current_security_service=curr_sec_service) new_sec_service, current_security_service=curr_sec_service)
mock_get_vserver.assert_called_once_with( mock_get_vserver.assert_called_once_with(
share_server=fake.SHARE_SERVER) share_server=fake.SHARE_SERVER)
mock_check_update.assert_called_once_with( mock_check_update.assert_called_once_with(
fake_context, fake.SHARE_SERVER, fake_net_info, fake_context, fake.SHARE_SERVER, [fake_net_info],
new_sec_service, current_security_service=curr_sec_service) new_sec_service, current_security_service=curr_sec_service)
@ddt.data( @ddt.data(

View File

@ -1182,23 +1182,23 @@ class GenericShareDriverTestCase(test.TestCase):
def test__setup_server(self): def test__setup_server(self):
sim = self._driver.instance_manager sim = self._driver.instance_manager
net_info = { net_info = [{
'server_id': 'fake', 'server_id': 'fake',
'neutron_net_id': 'fake-net-id', 'neutron_net_id': 'fake-net-id',
'neutron_subnet_id': 'fake-subnet-id', 'neutron_subnet_id': 'fake-subnet-id',
} }]
self._driver.setup_server(net_info) self._driver.setup_server(net_info)
sim.set_up_service_instance.assert_called_once_with( sim.set_up_service_instance.assert_called_once_with(
self._context, net_info) self._context, net_info[0])
def test__setup_server_revert(self): def test__setup_server_revert(self):
def raise_exception(*args, **kwargs): def raise_exception(*args, **kwargs):
raise exception.ServiceInstanceException raise exception.ServiceInstanceException
net_info = {'server_id': 'fake', net_info = [{'server_id': 'fake',
'neutron_net_id': 'fake-net-id', 'neutron_net_id': 'fake-net-id',
'neutron_subnet_id': 'fake-subnet-id'} 'neutron_subnet_id': 'fake-subnet-id'}]
self.mock_object(self._driver.service_instance_manager, self.mock_object(self._driver.service_instance_manager,
'set_up_service_instance', 'set_up_service_instance',
mock.Mock(side_effect=raise_exception)) mock.Mock(side_effect=raise_exception))

View File

@ -447,6 +447,8 @@ class ACCESSShareDriverTestCase(test.TestCase):
'share_group_stats': {'consistent_snapshot_support': None}, 'share_group_stats': {'consistent_snapshot_support': None},
'snapshot_support': True, 'snapshot_support': True,
'security_service_update_support': False, 'security_service_update_support': False,
'share_server_multiple_subnet_support': False,
'network_allocation_update_support': False,
} }
self.assertEqual(data, self._driver._stats) self.assertEqual(data, self._driver._stats)

View File

@ -374,6 +374,8 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
'ipv4_support': True, 'ipv4_support': True,
'ipv6_support': False, 'ipv6_support': False,
'security_service_update_support': False, 'security_service_update_support': False,
'share_server_multiple_subnet_support': False,
'network_allocation_update_support': False,
} }
if replication_domain: if replication_domain:
expected['replication_type'] = 'readable' expected['replication_type'] = 'readable'

File diff suppressed because it is too large Load Diff

View File

@ -971,6 +971,37 @@ class ShareDriverTestCase(test.TestCase):
{'id', 'fake_sec_service_id'}, {'id', 'fake_sec_service_id'},
current_security_service=None) current_security_service=None)
def test_check_update_share_server_network_allocations(self):
share_driver = self._instantiate_share_driver(None, True)
self.assertRaises(
NotImplementedError,
share_driver.check_update_share_server_network_allocations,
'fake_context',
{'id', 'share_server_id'},
{'admin_network_allocations': [], 'subnets': []},
{"id": "fake_subnet_id"},
[{"id": "fake_security_service_id"}],
[{'id', 'fake_share_instance_id'}],
[{"id": "fake_rule_id"}])
def test_update_share_server_network_allocations(self):
share_driver = self._instantiate_share_driver(None, True)
self.assertRaises(
NotImplementedError,
share_driver.update_share_server_network_allocations,
'fake_context',
{'id', 'share_server_id'},
{'admin_network_allocations': [], 'subnets': []},
{
'share_network_subnet_id': 'fake_share_network_subnet_id',
'neutron_net_id': 'fake_neutron_net_id',
'neutron_subnet_id': 'fake_neutron_subnet_id',
'network_allocations': []
},
[{"id": "fake_security_service_id"}],
[{"id": "fake_share_id"}],
[{"id": "fake_snapshot_id"}])
def test_create_share_group_from_sg_snapshot_with_no_members(self): def test_create_share_group_from_sg_snapshot_with_no_members(self):
share_driver = self._instantiate_share_driver(None, False) share_driver = self._instantiate_share_driver(None, False)
fake_share_group_dict = {} fake_share_group_dict = {}

File diff suppressed because it is too large Load Diff

View File

@ -48,6 +48,17 @@ class ShareRpcAPITestCase(test.TestCase):
share_group_snapshot = {'id': 'fake_share_group_id'} share_group_snapshot = {'id': 'fake_share_group_id'}
host = 'fake_host' host = 'fake_host'
share_server = db_utils.create_share_server(host=host) share_server = db_utils.create_share_server(host=host)
share_network_subnet = {
'availability_zone_id': 'fake_az_id',
'neutron_net_id': 'fake_neutron_net_id',
'neutron_subnet_id': 'fake_neutron_subnet_id',
'ip_version': 4,
'cidr': '127.0.0.0/28',
'gateway': '127.0.0.1',
'mtu': 1500,
'network_type': 'vlan',
'segmentation_id': 3000,
}
self.fake_share = jsonutils.to_primitive(share) self.fake_share = jsonutils.to_primitive(share)
# mock out the getattr on the share db model object since jsonutils # mock out the getattr on the share db model object since jsonutils
# doesn't know about those extra attributes to pull in # doesn't know about those extra attributes to pull in
@ -61,6 +72,8 @@ class ShareRpcAPITestCase(test.TestCase):
self.fake_share_group_snapshot = jsonutils.to_primitive( self.fake_share_group_snapshot = jsonutils.to_primitive(
share_group_snapshot) share_group_snapshot)
self.fake_host = jsonutils.to_primitive(host) self.fake_host = jsonutils.to_primitive(host)
self.fake_share_network_subnet = jsonutils.to_primitive(
share_network_subnet)
self.ctxt = context.RequestContext('fake_user', 'fake_project') self.ctxt = context.RequestContext('fake_user', 'fake_project')
self.rpcapi = share_rpcapi.ShareAPI() self.rpcapi = share_rpcapi.ShareAPI()
@ -476,3 +489,21 @@ class ShareRpcAPITestCase(test.TestCase):
share_network_id='fake_net_id', share_network_id='fake_net_id',
new_security_service_id='fake_sec_service_id', new_security_service_id='fake_sec_service_id',
current_security_service_id='fake_sec_service_id') current_security_service_id='fake_sec_service_id')
def test_check_update_share_server_network_allocations(self):
self._test_share_api(
'check_update_share_server_network_allocations',
rpc_method='cast',
version='1.23',
dest_host=self.fake_host,
share_network_id='fake_net_id',
new_share_network_subnet=self.fake_share_network_subnet)
def test_update_share_server_network_allocations(self):
self._test_share_api(
'update_share_server_network_allocations',
rpc_method='cast',
version='1.23',
dest_host=self.fake_host,
share_network_id='fake_net_id',
new_share_network_subnet_id='new_share_network_subnet_id')

View File

@ -165,6 +165,33 @@ class ShareUtilsTestCase(test.TestCase):
replica = share_utils.get_active_replica(replica_list) replica = share_utils.get_active_replica(replica_list)
self.assertIsNone(replica) self.assertIsNone(replica)
@ddt.data(
{'fake_subnet': [{'neutron_net_id': 'fake_nn_id',
'neutron_subnet_id': 'fake_nsb_id'}],
'fake_new_subnet': [{'neutron_net_id': 'fake_nn_id',
'neutron_subnet_id': 'fake_nsb_id'}],
'is_compatible': True},
{'fake_subnet': [{'neutron_net_id': 'fake_nn_id',
'neutron_subnet_id': 'fake_nsb_id'}],
'fake_new_subnet': [{'neutron_net_id': 'fake_nn_id',
'neutron_subnet_id': 'fake_nsb_id2'}],
'is_compatible': False},
{'fake_subnet': [{'neutron_net_id': 'fake_nn_id',
'neutron_subnet_id': 'fake_nsb_id'},
{'neutron_net_id': 'fake_nn_id2',
'neutron_subnet_id': 'fake_nsb_id2'}],
'fake_new_subnet': [{'neutron_net_id': 'fake_nn_id',
'neutron_subnet_id': 'fake_nsb_id'}],
'is_compatible': False}
)
@ddt.unpack
def test_is_az_subnets_compatible(self, fake_subnet, fake_new_subnet,
is_compatible):
expected_result = is_compatible
result = share_utils.is_az_subnets_compatible(fake_subnet,
fake_new_subnet)
self.assertEqual(expected_result, result)
class NotifyUsageTestCase(test.TestCase): class NotifyUsageTestCase(test.TestCase):
@mock.patch('manila.share.utils._usage_from_share') @mock.patch('manila.share.utils._usage_from_share')

View File

@ -88,6 +88,9 @@ class NetworkBaseAPITestCase(test.TestCase):
def unmanage_network_allocations(self, context, share_server_id): def unmanage_network_allocations(self, context, share_server_id):
pass pass
def include_network_info(self, share_network_subnet):
pass
self.assertRaises(TypeError, FakeNetworkAPI) self.assertRaises(TypeError, FakeNetworkAPI)
def test_inherit_network_base_api_allocate_not_redefined(self): def test_inherit_network_base_api_allocate_not_redefined(self):
@ -103,6 +106,9 @@ class NetworkBaseAPITestCase(test.TestCase):
def unmanage_network_allocations(self, context, share_server_id): def unmanage_network_allocations(self, context, share_server_id):
pass pass
def include_network_info(self, share_network_subnet):
pass
self.assertRaises(TypeError, FakeNetworkAPI) self.assertRaises(TypeError, FakeNetworkAPI)
def test_inherit_network_base_api(self): def test_inherit_network_base_api(self):
@ -121,6 +127,9 @@ class NetworkBaseAPITestCase(test.TestCase):
def unmanage_network_allocations(self, context, share_server_id): def unmanage_network_allocations(self, context, share_server_id):
pass pass
def include_network_info(self, share_network_subnet):
pass
result = FakeNetworkAPI() result = FakeNetworkAPI()
self.assertTrue(hasattr(result, '_verify_share_network')) self.assertTrue(hasattr(result, '_verify_share_network'))
@ -143,6 +152,9 @@ class NetworkBaseAPITestCase(test.TestCase):
def unmanage_network_allocations(self, context, share_server_id): def unmanage_network_allocations(self, context, share_server_id):
pass pass
def include_network_info(self, share_network_subnet):
pass
result = FakeNetworkAPI() result = FakeNetworkAPI()
result._verify_share_network('foo_id', {'id': 'bar_id'}) result._verify_share_network('foo_id', {'id': 'bar_id'})
@ -163,6 +175,9 @@ class NetworkBaseAPITestCase(test.TestCase):
def unmanage_network_allocations(self, context, share_server_id): def unmanage_network_allocations(self, context, share_server_id):
pass pass
def include_network_info(self, share_network_subnet):
pass
result = FakeNetworkAPI() result = FakeNetworkAPI()
self.assertRaises( self.assertRaises(
@ -190,10 +205,13 @@ class NetworkBaseAPITestCase(test.TestCase):
def unmanage_network_allocations(self, context, share_server_id): def unmanage_network_allocations(self, context, share_server_id):
pass pass
network.CONF.set_default('network_plugin_ipv6_enabled', def include_network_info(self, share_network_subnet):
network_plugin_ipv6_enabled) pass
network.CONF.set_default('network_plugin_ipv4_enabled',
network_plugin_ipv4_enabled) network.CONF.set_default(
'network_plugin_ipv6_enabled', network_plugin_ipv6_enabled)
network.CONF.set_default(
'network_plugin_ipv4_enabled', network_plugin_ipv4_enabled)
result = FakeNetworkAPI() result = FakeNetworkAPI()

View File

@ -89,6 +89,7 @@ class ServiceFlagsTestCase(test.TestCase):
app = service.Service.create(host=host, binary=binary) app = service.Service.create(host=host, binary=binary)
app.start() app.start()
app.stop() app.stop()
app.wait()
ref = db.service_get(context.get_admin_context(), app.service_id) ref = db.service_get(context.get_admin_context(), app.service_id)
db.service_destroy(context.get_admin_context(), app.service_id) db.service_destroy(context.get_admin_context(), app.service_id)
self.assertFalse(ref['disabled']) self.assertFalse(ref['disabled'])
@ -123,6 +124,13 @@ service_create = {
'report_count': 0, 'report_count': 0,
'availability_zone': 'nova', 'availability_zone': 'nova',
} }
service_create_other_az = {
'host': host,
'binary': binary,
'topic': topic,
'report_count': 0,
'availability_zone': 'other-zone',
}
service_ref = { service_ref = {
'host': host, 'host': host,
'binary': binary, 'binary': binary,
@ -200,6 +208,58 @@ class ServiceTestCase(test.TestCase):
service.db.service_update.assert_called_once_with( service.db.service_update.assert_called_once_with(
mock.ANY, service_ref['id'], mock.ANY) mock.ANY, service_ref['id'], mock.ANY)
@mock.patch.object(service.db, 'service_get_by_args',
mock.Mock(side_effect=fake_service_get_by_args))
@mock.patch.object(service.db, 'service_create',
mock.Mock(return_value=service_ref))
@mock.patch.object(service.db, 'service_get',
mock.Mock(return_value=service_ref))
@mock.patch.object(service.db, 'service_update',
mock.Mock(return_value=service_ref.
update({'report_count': 1})))
def test_report_state_newly_connected_different_az(self):
serv = service.Service(host, binary, topic, CONF.fake_manager)
serv.availability_zone = 'other-zone'
serv.start()
serv.model_disconnected = True
serv.report_state()
self.assertFalse(serv.model_disconnected)
service.db.service_get_by_args.assert_called_once_with(
mock.ANY, host, binary)
service.db.service_create.assert_called_once_with(
mock.ANY, service_create_other_az)
service.db.service_get.assert_called_once_with(
mock.ANY, service_ref['id'])
service.db.service_update.assert_called_once_with(
mock.ANY, service_ref['id'], mock.ANY)
@mock.patch.object(service.db, 'service_get_by_args',
mock.Mock(side_effect=fake_service_get_by_args))
@mock.patch.object(service.db, 'service_create',
mock.Mock(return_value=service_ref))
@mock.patch.object(service.db, 'service_get',
mock.Mock(side_effect=[exception.NotFound,
service_ref]))
@mock.patch.object(service.db, 'service_update',
mock.Mock(return_value=service_ref.
update({'report_count': 1})))
def test_report_state_newly_connected_not_found(self):
serv = service.Service(host, binary, topic, CONF.fake_manager)
serv.start()
serv.model_disconnected = True
serv.report_state()
self.assertFalse(serv.model_disconnected)
service.db.service_get_by_args.assert_called_once_with(
mock.ANY, host, binary)
service.db.service_create.assert_has_calls([
mock.call(mock.ANY, service_create),
mock.call(mock.ANY, service_create)])
service.db.service_get.assert_has_calls([
mock.call(mock.ANY, service_ref['id']),
mock.call(mock.ANY, service_ref['id'])])
service.db.service_update.assert_called_once_with(
mock.ANY, service_ref['id'], mock.ANY)
def test_report_state_service_not_ready(self): def test_report_state_service_not_ready(self):
with mock.patch.object(service, 'db') as mock_db: with mock.patch.object(service, 'db') as mock_db:
mock_db.service_get.return_value = service_ref mock_db.service_get.return_value = service_ref

View File

@ -0,0 +1,25 @@
---
features:
- |
Add support for multiple subnet per availability zone. The multiple
configuration can be done either on share server deployment or updating
a pre-existent share server.
The new field ``network_allocation_update_support`` was added to share
server's model This field defaults to ``False``, and all of the
already deployed share servers are going to get the default value even if
their backend support it. Administrators will be able to update the field
value using ``manila-manage`` commands.
The driver will report its support for adding a subnet on a pre-existent
share server through ``network_allocation_update_support``. Also, it will
report the support for creating the server with multiple subnets with the
``share_server_multiple_subnet_support``. The scheduler will filter out
backend that does not handle this request during some operations. Example,
creating a share with a share network containing multiple subnets, only
hosts that support this deployment will be selected.
deprecations:
- |
Remove 'share_network_subnet_id' attribute from share server view and
add 'share_network_subnet_ids' starting with microversion '2.70'. The share
server has a list of subnets.