Add security service update for in-use share networks

This patch implements the update of security service's association
with in-use share networks. The following changes were added:

 - New share network APIs: `share_network_security_service_update`
 and `share_network_reset_state`.

 - A new `status` attribute was added to share network model to
 identify when it's in a modification state, called 'network_change'.
 Other supported status that were added: 'active' and 'error'.

 - New 'security_service_update_support' property was added to both
 share server and share network models, to identify when this resources
 are able to process security service update for in-use share networks.

 - New driver interface was added to support update of security service's
 configuration of a given share server.

DocImpact
APIImpact
Partially Implements: bp add-security-service-in-use-share-networks

Co-Authored-By: Carlos Eduardo <ces.eduardo98@gmail.com>
Co-Authored-By: Douglas Viroel <viroel@gmail.com>
Co-Authored-By: Andre Beltrami <debeltrami@gmail.com>

Change-Id: I129a794dfd2d179fa2b9a2fed050459d6f00b0de
This commit is contained in:
debeltrami 2020-12-07 20:15:26 +00:00 committed by Douglas Viroel
parent 0a2ae6ff51
commit 2bc27c5678
51 changed files with 3355 additions and 141 deletions

View File

@ -254,6 +254,18 @@ def check_net_id_and_subnet_id(body):
raise webob.exc.HTTPBadRequest(explanation=msg)
def check_share_network_is_active(share_network):
network_status = share_network.get('status')
if network_status != constants.STATUS_NETWORK_ACTIVE:
msg = _("The share network %(id)s used isn't in an 'active' state. "
"Current status is %(status)s. The action may be retried "
"after the share network has changed its state.") % {
'id': share_network['id'],
'status': share_network.get('status'),
}
raise webob.exc.HTTPBadRequest(explanation=msg)
class ViewBuilder(object):
"""Model API responses as dictionaries."""

View File

@ -165,13 +165,19 @@ REST_API_VERSION_HISTORY = """
which can add minimum and maximum share size restrictions
on a per share-type granularity.
* 2.62 - Added quota control to per share size.
* 2.63 - Changed the existing behavior of 'add_security_service' action on
the share network's endpoint to allow the addition of security
services, even when the share network is in use. Also, added new
actions on the share network's endpoint:
'update_security_service', 'update_security_service_check' and
'add_security_service_check'.
"""
# The minimum and maximum versions of the API supported
# The default api version request is defined to be the
# minimum version of the API supported.
_MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.62"
_MAX_API_VERSION = "2.63"
DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -341,3 +341,15 @@ user documentation.
2.62
----
Added quota control to per share size.
2.63
----
Added the possibility to attach security services to share networks in use.
Also, an attached security service can be replaced for another one of
the same 'type'. In order to support those operations a 'status' field was
added in the share networks as well as, a new property called
'security_service_update_support' was included in the share networks and
share servers. Also new action APIs have been added to the share-networks
endpoint: 'update_security_service', 'update_security_service_check' and
'add_security_service_check'.

View File

@ -330,12 +330,14 @@ class ShareMixin(object):
if share_network_id:
try:
self.share_api.get_share_network(
share_network = self.share_api.get_share_network(
context,
share_network_id)
except exception.ShareNetworkNotFound as e:
raise exc.HTTPNotFound(explanation=e.msg)
kwargs['share_network_id'] = share_network_id
common.check_share_network_is_active(share_network)
if availability_zone_id:
if not db.share_network_subnet_get_by_availability_zone_id(
context, share_network_id,
@ -402,6 +404,8 @@ class ShareMixin(object):
if share_type:
kwargs['share_type'] = share_type
if share_network_id:
kwargs['share_network_id'] = share_network_id
new_share = self.share_api.create(context,
share_proto,
size,
@ -430,6 +434,11 @@ class ShareMixin(object):
access_data.pop('metadata', None)
share = self.share_api.get(context, id)
share_network_id = share.get('share_network_id')
if share_network_id:
share_network = db.share_network_get(context, share_network_id)
common.check_share_network_is_active(share_network)
if (not allow_on_error_status and
self._any_instance_has_errored_rules(share)):
msg = _("Access rules cannot be added while the share or any of "
@ -471,6 +480,13 @@ class ShareMixin(object):
access_id = body.get(
'deny_access', body.get('os-deny_access'))['access_id']
share = self.share_api.get(context, id)
share_network_id = share.get('share_network_id', None)
if share_network_id:
share_network = db.share_network_get(context, share_network_id)
common.check_share_network_is_active(share_network)
try:
access = self.share_api.access_get(context, access_id)
if access.share_id != id:

View File

@ -19,7 +19,6 @@ import copy
from oslo_db import exception as db_exception
from oslo_log import log
from oslo_utils import timeutils
import six
from six.moves import http_client
import webob
from webob import exc
@ -28,11 +27,13 @@ from manila.api import common
from manila.api.openstack import api_version_request as api_version
from manila.api.openstack import wsgi
from manila.api.views import share_networks as share_networks_views
from manila.common import constants
from manila.db import api as db_api
from manila import exception
from manila.i18n import _
from manila import policy
from manila import quota
from manila import share
from manila.share import rpcapi as share_rpcapi
from manila import utils
@ -42,14 +43,20 @@ LOG = log.getLogger(__name__)
QUOTAS = quota.QUOTAS
class ShareNetworkController(wsgi.Controller):
class ShareNetworkController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Share Network API controller for the OpenStack API."""
resource_name = 'share_network'
_view_builder_class = share_networks_views.ViewBuilder
def __init__(self):
super(ShareNetworkController, self).__init__()
self.share_rpcapi = share_rpcapi.ShareAPI()
self.share_api = share.API()
valid_statuses = {
'status': set(constants.SHARE_NETWORK_STATUSES)
}
def show(self, req, id):
"""Return data about the requested network info."""
@ -59,7 +66,7 @@ class ShareNetworkController(wsgi.Controller):
try:
share_network = db_api.share_network_get(context, id)
except exception.ShareNetworkNotFound as e:
raise exc.HTTPNotFound(explanation=six.text_type(e))
raise exc.HTTPNotFound(explanation=e.msg)
return self._view_builder.build_share_network(req, share_network)
@ -70,6 +77,9 @@ class ShareNetworkController(wsgi.Controller):
def _share_network_contains_subnets(self, share_network):
return len(share_network['share_network_subnets']) > 1
def _update(self, *args, **kwargs):
db_api.share_network_update(*args, **kwargs)
def delete(self, req, id):
"""Delete specified share network."""
context = req.environ['manila.context']
@ -78,7 +88,7 @@ class ShareNetworkController(wsgi.Controller):
try:
share_network = db_api.share_network_get(context, id)
except exception.ShareNetworkNotFound as e:
raise exc.HTTPNotFound(explanation=six.text_type(e))
raise exc.HTTPNotFound(explanation=e.msg)
share_instances = (
db_api.share_instances_get_all_by_share_network(context, id)
@ -251,7 +261,7 @@ class ShareNetworkController(wsgi.Controller):
try:
share_network = db_api.share_network_get(context, id)
except exception.ShareNetworkNotFound as e:
raise exc.HTTPNotFound(explanation=six.text_type(e))
raise exc.HTTPNotFound(explanation=e.msg)
update_values = body[RESOURCE_NAME]
@ -397,56 +407,62 @@ class ShareNetworkController(wsgi.Controller):
share_network['id'])
return self._view_builder.build_share_network(req, share_network)
def action(self, req, id, body):
_actions = {
'add_security_service': self._add_security_service,
'remove_security_service': self._remove_security_service
}
for action, data in body.items():
try:
return _actions[action](req, id, data)
except KeyError:
msg = _("Share networks does not have %s action") % action
raise exc.HTTPBadRequest(explanation=msg)
def _add_security_service(self, req, id, data):
@wsgi.action("add_security_service")
def add_security_service(self, req, id, body):
"""Associate share network with a given security service."""
context = req.environ['manila.context']
policy.check_policy(context, RESOURCE_NAME, 'add_security_service')
share_network = db_api.share_network_get(context, id)
if self._share_network_subnets_contain_share_servers(share_network):
msg = _("Cannot add security services. Share network is used.")
raise exc.HTTPForbidden(explanation=msg)
security_service = db_api.security_service_get(
context, data['security_service_id'])
for attached_service in share_network['security_services']:
if attached_service['type'] == security_service['type']:
msg = _("Cannot add security service to share network. "
"Security service with '%(ss_type)s' type already "
"added to '%(sn_id)s' share network") % {
'ss_type': security_service['type'],
'sn_id': share_network['id']}
raise exc.HTTPConflict(explanation=msg)
policy.check_policy(context, RESOURCE_NAME, 'add_security_service',
target_obj=share_network)
try:
data = body['add_security_service']
security_service = db_api.security_service_get(
context, data['security_service_id'])
except KeyError:
msg = "Malformed request body"
raise exc.HTTPBadRequest(explanation=msg)
contain_share_servers = (
self._share_network_subnets_contain_share_servers(share_network))
support_adding_to_in_use_networks = (
req.api_version_request >= api_version.APIVersionRequest("2.63"))
if contain_share_servers:
if not support_adding_to_in_use_networks:
msg = _("Cannot add security services. Share network is used.")
raise exc.HTTPForbidden(explanation=msg)
try:
self.share_api.update_share_network_security_service(
context, share_network, security_service)
except exception.ServiceIsDown as e:
raise exc.HTTPConflict(explanation=e.msg)
except exception.InvalidShareNetwork as e:
raise exc.HTTPBadRequest(explanation=e.msg)
except exception.InvalidSecurityService as e:
raise exc.HTTPConflict(explanation=e.msg)
try:
share_network = db_api.share_network_add_security_service(
context,
id,
data['security_service_id'])
except KeyError:
msg = "Malformed request body"
raise exc.HTTPBadRequest(explanation=msg)
except exception.NotFound as e:
raise exc.HTTPNotFound(explanation=six.text_type(e))
raise exc.HTTPNotFound(explanation=e.msg)
except exception.ShareNetworkSecurityServiceAssociationError as e:
raise exc.HTTPBadRequest(explanation=six.text_type(e))
raise exc.HTTPBadRequest(explanation=e.msg)
return self._view_builder.build_share_network(req, share_network)
def _remove_security_service(self, req, id, data):
@wsgi.action('remove_security_service')
def remove_security_service(self, req, id, body):
"""Dissociate share network from a given security service."""
context = req.environ['manila.context']
policy.check_policy(context, RESOURCE_NAME, 'remove_security_service')
share_network = db_api.share_network_get(context, id)
policy.check_policy(context, RESOURCE_NAME, 'remove_security_service',
target_obj=share_network)
data = body['remove_security_service']
if self._share_network_subnets_contain_share_servers(share_network):
msg = _("Cannot remove security services. Share network is used.")
@ -460,12 +476,152 @@ class ShareNetworkController(wsgi.Controller):
msg = "Malformed request body"
raise exc.HTTPBadRequest(explanation=msg)
except exception.NotFound as e:
raise exc.HTTPNotFound(explanation=six.text_type(e))
raise exc.HTTPNotFound(explanation=e.msg)
except exception.ShareNetworkSecurityServiceDissociationError as e:
raise exc.HTTPBadRequest(explanation=six.text_type(e))
raise exc.HTTPBadRequest(explanation=e.msg)
return self._view_builder.build_share_network(req, share_network)
@wsgi.Controller.api_version('2.63')
@wsgi.action('update_security_service')
@wsgi.response(202)
def update_security_service(self, req, id, body):
"""Update security service parameters from a given share network."""
context = req.environ['manila.context']
share_network = db_api.share_network_get(context, id)
policy.check_policy(context, RESOURCE_NAME, 'update_security_service',
target_obj=share_network)
try:
data = body['update_security_service']
current_security_service = db_api.security_service_get(
context, data['current_service_id']
)
new_security_service = db_api.security_service_get(
context, data['new_service_id']
)
except KeyError:
msg = "Malformed request body."
raise exc.HTTPBadRequest(explanation=msg)
except exception.NotFound:
msg = ("The current security service or the new security service "
"doesn't exist.")
raise exc.HTTPBadRequest(explanation=msg)
try:
self.share_api.update_share_network_security_service(
context, share_network, new_security_service,
current_security_service=current_security_service)
except exception.ServiceIsDown as e:
raise exc.HTTPConflict(explanation=e.msg)
except exception.InvalidShareNetwork as e:
raise exc.HTTPBadRequest(explanation=e.msg)
except exception.InvalidSecurityService as e:
raise exc.HTTPConflict(explanation=e.msg)
try:
share_network = db_api.share_network_update_security_service(
context,
id,
data['current_service_id'],
data['new_service_id'])
except exception.NotFound as e:
raise exc.HTTPNotFound(explanation=e.msg)
except (exception.ShareNetworkSecurityServiceDissociationError,
exception.ShareNetworkSecurityServiceAssociationError) as e:
raise exc.HTTPBadRequest(explanation=e.msg)
return self._view_builder.build_share_network(req, share_network)
@wsgi.Controller.api_version('2.63')
@wsgi.action('update_security_service_check')
@wsgi.response(202)
def check_update_security_service(self, req, id, body):
"""Check the feasibility of updating a security service."""
context = req.environ['manila.context']
share_network = db_api.share_network_get(context, id)
policy.check_policy(context, RESOURCE_NAME,
'update_security_service_check',
target_obj=share_network)
try:
data = body['update_security_service_check']
current_security_service = db_api.security_service_get(
context, data['current_service_id']
)
new_security_service = db_api.security_service_get(
context, data['new_service_id']
)
except KeyError:
msg = "Malformed request body."
raise exc.HTTPBadRequest(explanation=msg)
except exception.NotFound:
msg = ("The current security service or the new security service "
"doesn't exist.")
raise exc.HTTPBadRequest(explanation=msg)
reset_check = utils.get_bool_from_api_params('reset_operation', data)
try:
result = (
self.share_api.check_share_network_security_service_update(
context, share_network, new_security_service,
current_security_service=current_security_service,
reset_operation=reset_check))
except exception.ServiceIsDown as e:
raise exc.HTTPConflict(explanation=e.msg)
except exception.InvalidShareNetwork as e:
raise exc.HTTPBadRequest(explanation=e.msg)
except exception.InvalidSecurityService as e:
raise exc.HTTPConflict(explanation=e.msg)
return self._view_builder.build_security_service_update_check(
req, data, result)
@wsgi.Controller.api_version('2.63')
@wsgi.action("add_security_service_check")
@wsgi.response(202)
def check_add_security_service(self, req, id, body):
"""Check the feasibility of associate a new security service."""
context = req.environ['manila.context']
share_network = db_api.share_network_get(context, id)
policy.check_policy(context, RESOURCE_NAME,
'add_security_service_check',
target_obj=share_network)
data = body['add_security_service_check']
try:
security_service = db_api.security_service_get(
context, data['security_service_id'])
except KeyError:
msg = "Malformed request body."
raise exc.HTTPBadRequest(explanation=msg)
except exception.NotFound:
msg = ("Security service %s doesn't exist."
) % data['security_service_id']
raise exc.HTTPBadRequest(explanation=msg)
reset_check = utils.get_bool_from_api_params('reset_operation', data)
try:
result = (
self.share_api.check_share_network_security_service_update(
context, share_network, security_service,
reset_operation=reset_check))
except exception.ServiceIsDown as e:
raise exc.HTTPConflict(explanation=e.msg)
except exception.InvalidShareNetwork as e:
raise exc.HTTPBadRequest(explanation=e.msg)
except exception.InvalidSecurityService as e:
raise exc.HTTPConflict(explanation=e.msg)
return self._view_builder.build_security_service_update_check(
req, data, result)
@wsgi.Controller.api_version('2.63')
@wsgi.action('reset_status')
def reset_status(self, req, id, body):
return self._reset_status(req, id, body)
def create_resource():
return wsgi.Resource(ShareNetworkController())

View File

@ -162,6 +162,10 @@ class ShareReplicationController(wsgi.Controller, wsgi.AdminActionsMixin):
share_network_id = share_ref.get('share_network_id', None)
if share_network_id:
share_network = db.share_network_get(context, share_network_id)
common.check_share_network_is_active(share_network)
try:
new_replica = self.share_api.create_share_replica(
context, share_ref, availability_zone=availability_zone,
@ -226,6 +230,11 @@ class ShareReplicationController(wsgi.Controller, wsgi.AdminActionsMixin):
msg = _("No replica exists with ID %s.")
raise exc.HTTPNotFound(explanation=msg % id)
share_network_id = replica.get('share_network_id')
if share_network_id:
share_network = db.share_network_get(context, share_network_id)
common.check_share_network_is_active(share_network)
replica_state = replica.get('replica_state')
if replica_state == constants.REPLICA_STATE_ACTIVE:

View File

@ -18,6 +18,7 @@ from six.moves import http_client
import webob
from webob import exc
from manila.api import common
from manila.api.openstack import wsgi
from manila.api.v1 import share_servers
from manila.api.views import share_server_migration as server_migration_views
@ -105,6 +106,17 @@ class ShareServerController(share_servers.ShareServerController,
except exception.ShareServerNotFound as e:
raise exc.HTTPNotFound(explanation=e.msg)
network_subnet_id = share_server.get('share_network_subnet_id', None)
if network_subnet_id:
subnet = db_api.share_network_subnet_get(context,
network_subnet_id)
share_network_id = subnet['share_network_id']
else:
share_network_id = share_server.get('share_network_id')
share_network = db_api.share_network_get(context, share_network_id)
common.check_share_network_is_active(share_network)
allowed_statuses = [constants.STATUS_ERROR, constants.STATUS_ACTIVE,
constants.STATUS_MANAGE_ERROR,
constants.STATUS_UNMANAGE_ERROR]
@ -172,6 +184,8 @@ class ShareServerController(share_servers.ShareServerController,
"with API version >= 2.51.") % share_network_id
raise exc.HTTPBadRequest(explanation=msg)
common.check_share_network_is_active(network_subnet['share_network'])
if share_utils.extract_host(host, 'pool'):
msg = _("Host parameter should not contain pool.")
raise exc.HTTPBadRequest(explanation=msg)
@ -242,6 +256,13 @@ class ShareServerController(share_servers.ShareServerController,
msg = _("Share network %s not "
"found.") % new_share_network_id
raise exc.HTTPBadRequest(explanation=msg)
common.check_share_network_is_active(new_share_network)
else:
share_network_id = (
share_server['share_network_subnet']['share_network_id'])
current_share_network = db_api.share_network_get(
context, share_network_id)
common.check_share_network_is_active(current_share_network)
try:
self.share_api.share_server_migration_start(
@ -359,6 +380,13 @@ class ShareServerController(share_servers.ShareServerController,
msg = _("Share network %s not "
"found.") % new_share_network_id
raise exc.HTTPBadRequest(explanation=msg)
common.check_share_network_is_active(new_share_network)
else:
share_network_id = (
share_server['share_network_subnet']['share_network_id'])
current_share_network = db_api.share_network_get(
context, share_network_id)
common.check_share_network_is_active(current_share_network)
try:
result = self.share_api.share_server_migration_check(

View File

@ -27,6 +27,7 @@ from manila.api.openstack import wsgi
from manila.api.v1 import share_snapshots
from manila.api.views import share_snapshots as snapshot_views
from manila.common import constants
from manila.db import api as db_api
from manila import exception
from manila.i18n import _
from manila import share
@ -162,6 +163,13 @@ class ShareSnapshotsController(share_snapshots.ShareSnapshotMixin,
msg = _("Required parameter %s is empty.") % parameter
raise exc_response(explanation=msg)
def _check_if_share_share_network_is_active(self, context, snapshot):
share_network_id = snapshot['share'].get('share_network_id')
if share_network_id:
share_network = db_api.share_network_get(
context, share_network_id)
common.check_share_network_is_active(share_network)
def _allow(self, req, id, body, enable_ipv6=False):
context = req.environ['manila.context']
@ -184,6 +192,8 @@ class ShareSnapshotsController(share_snapshots.ShareSnapshotMixin,
snapshot = self.share_api.get_snapshot(context, id)
self._check_if_share_share_network_is_active(context, snapshot)
self._check_mount_snapshot_support(context, snapshot)
try:
@ -212,6 +222,8 @@ class ShareSnapshotsController(share_snapshots.ShareSnapshotMixin,
self._check_mount_snapshot_support(context, snapshot)
self._check_if_share_share_network_is_active(context, snapshot)
access = self.share_api.snapshot_access_get(context, access_id)
if access['share_snapshot_id'] != snapshot['id']:

View File

@ -18,6 +18,7 @@ from six.moves import http_client
import webob
from webob import exc
from manila.api import common
from manila.api.openstack import api_version_request as api_version
from manila.api.openstack import wsgi
from manila.api.v1 import share_manage
@ -256,6 +257,13 @@ class ShareController(shares.ShareMixin,
msg = _("Share network %s not "
"found.") % new_share_network_id
raise exc.HTTPBadRequest(explanation=msg)
common.check_share_network_is_active(new_share_network)
else:
share_network_id = share.get('share_network_id', None)
if share_network_id:
current_share_network = db.share_network_get(
context, share_network_id)
common.check_share_network_is_active(current_share_network)
new_share_type_id = params.get('new_share_type_id', None)
if new_share_type_id:

View File

@ -21,7 +21,8 @@ class ViewBuilder(common.ViewBuilder):
_collection_name = 'share_networks'
_detail_version_modifiers = ["add_gateway", "add_mtu", "add_nova_net_id",
"add_subnets"]
"add_subnets",
"add_status_and_sec_service_update_fields"]
def build_share_network(self, request, share_network):
"""View of a share network."""
@ -35,6 +36,25 @@ class ViewBuilder(common.ViewBuilder):
request, share_network, is_detail)
for share_network in share_networks]}
def build_security_service_update_check(self, request, params, result):
"""View of security service add or update check."""
context = request.environ['manila.context']
requested_operation = {
'operation': ('update_security_service'
if params.get('current_service_id')
else 'add_security_service'),
'current_security_service': params.get('current_service_id'),
'new_security_service': (params.get('new_service_id') or
params.get('security_service_id'))
}
view = {
'compatible': result['compatible'],
'requested_operation': requested_operation,
}
if context.is_admin:
view['hosts_check_result'] = result['hosts_check_result']
return view
def _update_share_network_info(self, request, share_network):
for sns in share_network.get('share_network_subnets') or []:
if sns.get('is_default') and sns.get('is_default') is True:
@ -108,3 +128,10 @@ class ViewBuilder(common.ViewBuilder):
@common.ViewBuilder.versioned_method("1.0", "2.25")
def add_nova_net_id(self, context, network_dict, network):
network_dict['nova_net_id'] = None
@common.ViewBuilder.versioned_method("2.63")
def add_status_and_sec_service_update_fields(
self, context, network_dict, network):
network_dict['status'] = network.get('status')
network_dict['security_service_update_support'] = network.get(
'security_service_update_support')

View File

@ -23,7 +23,8 @@ class ViewBuilder(common.ViewBuilder):
_detail_version_modifiers = [
"add_is_auto_deletable_and_identifier_fields",
"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"
]
def build_share_server(self, request, share_server):
@ -82,3 +83,9 @@ class ViewBuilder(common.ViewBuilder):
share_server_dict['task_state'] = share_server['task_state']
share_server_dict['source_share_server_id'] = (
share_server['source_share_server_id'])
@common.ViewBuilder.versioned_method("2.63")
def add_sec_service_update_fields(
self, context, share_server_dict, share_server):
share_server_dict['security_service_update_support'] = share_server[
'security_service_update_support']

View File

@ -78,6 +78,10 @@ HOST_UPDATE_HELP_MSG = ("A fully qualified host string is of the format "
HOST_UPDATE_CURRENT_HOST_HELP = ("Current share host name. %s" %
HOST_UPDATE_HELP_MSG)
HOST_UPDATE_NEW_HOST_HELP = "New share host name. %s" % HOST_UPDATE_HELP_MSG
SHARE_SERVERS_UPDATE_HELP = ("List of share servers to be updated, separated "
"by commas.")
SHARE_SERVERS_UPDATE_CAPABILITIES_HELP = (
"List of share server capabilities to be updated, separated by commas.")
# Decorators for actions
@ -399,6 +403,42 @@ class ShareCommands(object):
print(msg % msg_args)
class ShareServerCommands(object):
@args('--share_servers', required=True,
help=SHARE_SERVERS_UPDATE_HELP)
@args('--capabilities', required=True,
help=SHARE_SERVERS_UPDATE_CAPABILITIES_HELP)
@args('--value', required=False, type=bool, default=False,
help="If those capabilities will be enabled (True) or disabled "
"(False)")
def update_share_server_capabilities(self, share_servers, capabilities,
value=False):
"""Update the share server capabilities.
This method receives a list of share servers and capabilities
in order to have it updated with the value specified. If the value
was not specified the default is False.
"""
share_servers = [server.strip() for server in share_servers.split(",")]
capabilities = [cap.strip() for cap in capabilities.split(",")]
supported_capabilities = ['security_service_update_support']
values = dict()
for capability in capabilities:
if capability not in supported_capabilities:
print("One or more capabilities are invalid for this "
"operation. The supported capability(ies) is(are) %s."
% supported_capabilities)
sys.exit(1)
values[capability] = value
ctxt = context.get_admin_context()
db.share_servers_update(ctxt, share_servers, values)
print("The capability(ies) %s of the following share server(s)"
" %s was(were) updated to %s." %
(capabilities, share_servers, value))
CATEGORIES = {
'config': ConfigCommands,
'db': DbCommands,
@ -406,6 +446,7 @@ CATEGORIES = {
'logs': GetLogCommands,
'service': ServiceCommands,
'share': ShareCommands,
'share_server': ShareServerCommands,
'shell': ShellCommands,
'version': VersionCommands
}

View File

@ -66,6 +66,14 @@ STATUS_ACTIVE = 'active'
STATUS_SERVER_MIGRATING = 'server_migrating'
STATUS_SERVER_MIGRATING_TO = 'server_migrating_to'
# Share server update statuses
STATUS_SERVER_NETWORK_CHANGE = 'network_change'
# Share network statuses
STATUS_NETWORK_ACTIVE = 'active'
STATUS_NETWORK_ERROR = 'error'
STATUS_NETWORK_CHANGE = 'network_change'
ACCESS_RULES_STATES = (
ACCESS_STATE_QUEUED_TO_APPLY,
ACCESS_STATE_QUEUED_TO_DENY,
@ -214,6 +222,13 @@ SHARE_SERVER_STATUSES = (
STATUS_INACTIVE,
STATUS_SERVER_MIGRATING,
STATUS_SERVER_MIGRATING_TO,
STATUS_SERVER_NETWORK_CHANGE,
)
SHARE_NETWORK_STATUSES = (
STATUS_NETWORK_ACTIVE,
STATUS_NETWORK_ERROR,
STATUS_NETWORK_CHANGE,
)
REPLICA_STATE_ACTIVE = 'active'

View File

@ -854,17 +854,34 @@ def share_network_get_all_by_security_service(context, security_service_id):
def share_network_add_security_service(context, id, security_service_id):
"""Associate a security service with a share network."""
return IMPL.share_network_add_security_service(context,
id,
security_service_id)
def share_network_remove_security_service(context, id, security_service_id):
"""Dissociate a security service from a share network."""
return IMPL.share_network_remove_security_service(context,
id,
security_service_id)
def share_network_security_service_association_get(
context, share_network_id, security_service_id):
"""Get given share network and security service association."""
return IMPL.share_network_security_service_association_get(
context, share_network_id, security_service_id)
def share_network_update_security_service(context, id,
current_security_service_id,
new_security_service_id):
"""Update a security service association with a share network."""
return IMPL.share_network_update_security_service(
context, id, current_security_service_id, new_security_service_id)
def count_share_networks(context, project_id, user_id=None,
share_type_id=None, session=None):
return IMPL.count_share_networks(
@ -1022,6 +1039,12 @@ def share_server_backend_details_set(context, share_server_id, server_details):
server_details)
def share_servers_update(context, share_server_ids, values):
"""Updates values of a bunch of share servers at once."""
return IMPL.share_servers_update(
context, share_server_ids, values)
##################
@ -1483,3 +1506,22 @@ def backend_info_update(context, host, value=None,
"""Update hash info for host."""
return IMPL.backend_info_update(context, host=host, value=value,
delete_existing=delete_existing)
####################
def async_operation_data_get(context, entity_id, key=None, default=None):
"""Get one, list or all key-value pairs for given entity_id."""
return IMPL.async_operation_data_get(context, entity_id, key, default)
def async_operation_data_update(context, entity_id, details,
delete_existing=False):
"""Update key-value pairs for given entity_id."""
return IMPL.async_operation_data_update(context, entity_id, details,
delete_existing)
def async_operation_data_delete(context, entity_id, key=None):
"""Remove one, list or all key-value pairs for given entity_id."""
return IMPL.async_operation_data_delete(context, entity_id, key)

View File

@ -0,0 +1,90 @@
# 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.
"""add_security_service_update_control_fields
Revision ID: 478c445d8d3e
Revises: 0c23aec99b74
Create Date: 2020-12-07 12:33:41.444202
"""
# revision identifiers, used by Alembic.
revision = '478c445d8d3e'
down_revision = '0c23aec99b74'
from alembic import op
from manila.common import constants
from oslo_log import log
import sqlalchemy as sa
SHARE_SERVERS_TABLE = 'share_servers'
SHARE_NETWORKS_TABLE = 'share_networks'
ASYNC_OPERATION_DATA_TABLE = 'async_operation_data'
LOG = log.getLogger(__name__)
def upgrade():
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:
op.create_table(
ASYNC_OPERATION_DATA_TABLE,
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('entity_uuid', sa.String(36),
nullable=False, primary_key=True),
sa.Column('key', sa.String(255),
nullable=False, primary_key=True),
sa.Column('value', sa.String(1023), nullable=False),
mysql_engine='InnoDB',
)
op.add_column(
SHARE_SERVERS_TABLE,
sa.Column('security_service_update_support', sa.Boolean,
nullable=False, server_default=sa.sql.false())
)
op.add_column(
SHARE_NETWORKS_TABLE,
sa.Column('status', sa.String(36), nullable=False,
server_default=constants.STATUS_NETWORK_ACTIVE))
except Exception:
msg_args = {
'async_op_table': ASYNC_OPERATION_DATA_TABLE,
'sec_serv_column': 'share_servers.security_service_update_support',
'shr_net_column': 'share_networks.status',
}
LOG.error('Table %(async_op_table)s and table columns '
'%(sec_serv_column)s and %(shr_net_column)s were not'
' created!', msg_args)
raise
def downgrade():
try:
op.drop_table(ASYNC_OPERATION_DATA_TABLE)
op.drop_column(SHARE_SERVERS_TABLE, 'security_service_update_support')
op.drop_column(SHARE_NETWORKS_TABLE, 'status')
except Exception:
msg_args = {
'async_op_table': ASYNC_OPERATION_DATA_TABLE,
'sec_serv_column': 'share_servers.security_service_update_support',
'shr_net_column': 'share_networks.status',
}
LOG.error('Table %(async_op_table)s and table columns '
'%(sec_serv_column)s and %(shr_net_column)s were not '
'dropped!', msg_args)
raise

View File

@ -3847,6 +3847,21 @@ def share_network_add_security_service(context, id, security_service_id):
return share_nw_ref
@require_context
def share_network_security_service_association_get(
context, share_network_id, security_service_id):
session = get_session()
with session.begin():
association = (model_query(
context,
models.ShareNetworkSecurityServiceAssociation,
session=session).filter_by(
share_network_id=share_network_id).filter_by(
security_service_id=security_service_id).first())
return association
@require_context
def share_network_remove_security_service(context, id, security_service_id):
session = get_session()
@ -3874,6 +3889,43 @@ def share_network_remove_security_service(context, id, security_service_id):
return share_nw_ref
@require_context
def share_network_update_security_service(context, id,
current_security_service_id,
new_security_service_id):
session = get_session()
with session.begin():
share_nw_ref = share_network_get(context, id, session=session)
# Check if the old security service exists
security_service_get(context, current_security_service_id,
session=session)
new_security_service_ref = security_service_get(
context, new_security_service_id, session=session)
assoc_ref = (model_query(
context,
models.ShareNetworkSecurityServiceAssociation,
session=session).filter_by(
share_network_id=id).filter_by(
security_service_id=current_security_service_id).first())
if assoc_ref:
assoc_ref.soft_delete(session)
else:
msg = "No association defined"
raise exception.ShareNetworkSecurityServiceDissociationError(
share_network_id=id,
security_service_id=current_security_service_id,
reason=msg)
# Add new association
share_nw_ref.security_services += [new_security_service_ref]
share_nw_ref.save(session=session)
return share_nw_ref
@require_context
def count_share_networks(context, project_id, user_id=None,
share_type_id=None, session=None):
@ -4117,7 +4169,10 @@ def share_server_get_all_with_filters(context, filters):
if filters.get('source_share_server_id'):
query = query.filter_by(
source_share_server_id=filters.get('source_share_server_id'))
if filters.get('share_network_id'):
query = query.filter(
models.ShareNetworkSubnet.share_network_id ==
filters.get('share_network_id'))
return query.all()
@ -4177,6 +4232,20 @@ def share_server_backend_details_delete(context, share_server_id,
item.soft_delete(session)
@require_context
def share_servers_update(
context, share_server_ids, values, session=None):
session = session or get_session()
result = (
model_query(
context, models.ShareServer, read_deleted="no",
session=session).filter(
models.ShareServer.id.in_(share_server_ids)).update(
values, synchronize_session=False))
return result
###################
def _driver_private_data_query(session, context, entity_id, key=None,
@ -5775,3 +5844,92 @@ def _backend_info_query(session, context, host, read_deleted=False):
).first()
return result
###################
def _async_operation_data_query(session, context, entity_id, key=None,
read_deleted=False):
query = model_query(
context, models.AsynchronousOperationData, session=session,
read_deleted=read_deleted,
).filter_by(
entity_uuid=entity_id,
)
if isinstance(key, list):
return query.filter(models.AsynchronousOperationData.key.in_(key))
elif key is not None:
return query.filter_by(key=key)
return query
@require_context
def async_operation_data_get(context, entity_id, key=None,
default=None, session=None):
if not session:
session = get_session()
query = _async_operation_data_query(session, context, entity_id, key)
if key is None or isinstance(key, list):
return {item.key: item.value for item in query.all()}
else:
result = query.first()
return result["value"] if result is not None else default
@require_context
def async_operation_data_update(context, entity_id, details,
delete_existing=False, session=None):
new_details = copy.deepcopy(details)
if not session:
session = get_session()
with session.begin():
# Process existing data
original_data = session.query(
models.AsynchronousOperationData).filter_by(
entity_uuid=entity_id).all()
for data_ref in original_data:
in_new_details = data_ref['key'] in new_details
if in_new_details:
new_value = str(new_details.pop(data_ref['key']))
data_ref.update({
"value": new_value,
"deleted": 0,
"deleted_at": None
})
data_ref.save(session=session)
elif delete_existing and data_ref['deleted'] != 1:
data_ref.update({
"deleted": 1, "deleted_at": timeutils.utcnow()
})
data_ref.save(session=session)
# Add new data
for key, value in new_details.items():
data_ref = models.AsynchronousOperationData()
data_ref.update({
"entity_uuid": entity_id,
"key": key,
"value": str(value)
})
data_ref.save(session=session)
return details
@require_context
def async_operation_data_delete(context, entity_id, key=None, session=None):
if not session:
session = get_session()
with session.begin():
query = _async_operation_data_query(session, context,
entity_id, key)
query.update({"deleted": 1, "deleted_at": timeutils.utcnow()})

View File

@ -188,7 +188,8 @@ class Share(BASE, ManilaBase):
__tablename__ = 'shares'
_extra_keys = ['name', 'export_location', 'export_locations', 'status',
'host', 'share_server_id', 'share_network_id',
'availability_zone', 'access_rules_status', 'share_type_id']
'availability_zone', 'access_rules_status', 'share_type_id',
'share_network_status']
@property
def name(self):
@ -227,7 +228,8 @@ class Share(BASE, ManilaBase):
def __getattr__(self, item):
proxified_properties = ('status', 'host', 'share_server_id',
'share_network_id', 'availability_zone',
'share_type_id', 'share_type')
'share_type_id', 'share_type',
'share_network_status')
if item in proxified_properties:
return getattr(self.instance, item, None)
@ -920,6 +922,10 @@ class ShareNetwork(BASE, ManilaBase):
user_id = Column(String(255), nullable=False)
name = Column(String(255), nullable=True)
description = Column(String(255), nullable=True)
status = Column(Enum(
constants.STATUS_NETWORK_ACTIVE, constants.STATUS_NETWORK_ERROR,
constants.STATUS_NETWORK_CHANGE),
default=constants.STATUS_NETWORK_ACTIVE)
security_services = orm.relationship(
"SecurityService",
secondary="share_network_security_service_association",
@ -935,7 +941,7 @@ class ShareNetwork(BASE, ManilaBase):
'SecurityService.deleted == "False")')
share_instances = orm.relationship(
"ShareInstance",
backref='share_network',
backref=orm.backref('share_network'),
primaryjoin='and_('
'ShareNetwork.id == ShareInstance.share_network_id,'
'ShareInstance.deleted == "False")')
@ -947,6 +953,18 @@ class ShareNetwork(BASE, ManilaBase):
'(ShareNetwork.id == ShareNetworkSubnet.share_network_id,'
'ShareNetworkSubnet.deleted == "False")')
@property
def security_service_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['security_service_update_support'])
# NOTE(carloss): all share servers within this share network must
# support updating security services in order to have this property
# set to True.
return all(share_servers_support_updating)
class ShareNetworkSubnet(BASE, ManilaBase):
"""Represents a share network subnet used by some resources."""
@ -998,6 +1016,10 @@ class ShareNetworkSubnet(BASE, ManilaBase):
def share_network_name(self):
return self.share_network['name']
@property
def share_network_status(self):
return self.share_network['status']
class ShareServer(BASE, ManilaBase):
"""Represents share server used by share."""
@ -1013,6 +1035,8 @@ class ShareServer(BASE, ManilaBase):
task_state = Column(String(255), nullable=True)
source_share_server_id = Column(String(36), ForeignKey('share_servers.id'),
nullable=True)
security_service_update_support = Column(
Boolean, nullable=False, default=False)
status = Column(Enum(
constants.STATUS_INACTIVE, constants.STATUS_ACTIVE,
constants.STATUS_ERROR, constants.STATUS_DELETING,
@ -1020,7 +1044,8 @@ class ShareServer(BASE, ManilaBase):
constants.STATUS_MANAGING, constants.STATUS_UNMANAGING,
constants.STATUS_UNMANAGE_ERROR, constants.STATUS_MANAGE_ERROR,
constants.STATUS_SERVER_MIGRATING,
constants.STATUS_SERVER_MIGRATING_TO),
constants.STATUS_SERVER_MIGRATING_TO,
constants.STATUS_SERVER_NETWORK_CHANGE),
default=constants.STATUS_INACTIVE)
network_allocations = orm.relationship(
"NetworkAllocation",
@ -1053,6 +1078,10 @@ class ShareServer(BASE, ManilaBase):
return {model['key']: model['value']
for model in self._backend_details}
@property
def share_network_status(self):
return self.share_network_subnet['share_network']['status']
_extra_keys = ['backend_details']
@ -1309,6 +1338,14 @@ class BackendInfo(BASE, ManilaBase):
info_hash = Column(String(255))
class AsynchronousOperationData(BASE, ManilaBase):
"""Represents data as key-value pairs for asynchronous operations."""
__tablename__ = 'async_operation_data'
entity_uuid = Column(String(36), nullable=False, primary_key=True)
key = Column(String(255), nullable=False, primary_key=True)
value = Column(String(1023), nullable=False)
def register_models():
"""Register Models and create metadata.

View File

@ -242,6 +242,10 @@ class ShareServerNotFoundByFilters(ShareServerNotFound):
"filters: %(filters_description)s.")
class InvalidShareNetwork(Invalid):
message = _("Invalid share network: %(reason)s")
class ShareServerInUse(InUse):
message = _("Share server %(share_server_id)s is in use.")
@ -597,6 +601,10 @@ class SecurityServiceNotFound(NotFound):
message = _("Security service %(security_service_id)s could not be found.")
class InvalidSecurityService(Invalid):
message = _("Invalid security service: %(reason)s")
class ShareNetworkSecurityServiceAssociationError(ManilaException):
message = _("Failed to associate share network %(share_network_id)s"
" and security service %(security_service_id)s: %(reason)s.")

View File

@ -57,7 +57,22 @@ deprecated_share_network_get_all = policy.DeprecatedRule(
name=BASE_POLICY_NAME % 'get_all_share_networks',
check_str=base.RULE_ADMIN_API
)
deprecated_share_network_add_security_service_check = policy.DeprecatedRule(
name=BASE_POLICY_NAME % 'add_security_service_check',
check_str=base.RULE_DEFAULT
)
deprecated_share_network_update_security_service = policy.DeprecatedRule(
name=BASE_POLICY_NAME % 'update_security_service',
check_str=base.RULE_DEFAULT
)
deprecated_share_network_update_security_service_check = policy.DeprecatedRule(
name=BASE_POLICY_NAME % 'update_security_service_check',
check_str=base.RULE_DEFAULT
)
deprecated_share_network_reset_status = policy.DeprecatedRule(
name=BASE_POLICY_NAME % 'reset_status',
check_str=base.RULE_ADMIN_API
)
share_network_policies = [
policy.DocumentedRuleDefault(
@ -173,6 +188,22 @@ share_network_policies = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'add_security_service_check',
check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,
scope_types=['system', 'project'],
description="Check the feasibility of add security service to a share "
"network.",
operations=[
{
'method': 'POST',
'path': '/share-networks/{share_network_id}/action'
}
],
deprecated_rule=deprecated_share_network_add_security_service_check,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'remove_security_service',
check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,
@ -188,6 +219,52 @@ share_network_policies = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'update_security_service',
check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,
scope_types=['system', 'project'],
description="Update security service from share network.",
operations=[
{
'method': 'POST',
'path': '/share-networks/{share_network_id}/action'
}
],
deprecated_rule=deprecated_share_network_update_security_service,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'update_security_service_check',
check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,
scope_types=['system', 'project'],
description="Check the feasibility of update a security service from "
"share network.",
operations=[
{
'method': 'POST',
'path': '/share-networks/{share_network_id}/action'
}
],
deprecated_rule=deprecated_share_network_update_security_service_check,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'reset_status',
check_str=base.SYSTEM_ADMIN_OR_PROJECT_ADMIN,
scope_types=['system', 'project'],
description="Reset share network`s status.",
operations=[
{
'method': 'POST',
'path': '/share-networks/{share_network_id}/action'
}
],
deprecated_rule=deprecated_share_network_reset_status,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
),
policy.DocumentedRuleDefault(
name=BASE_POLICY_NAME % 'get_all_share_networks',
check_str=base.SYSTEM_READER,

View File

@ -146,6 +146,7 @@ class HostState(object):
self.replication_domain = None
self.ipv4_support = None
self.ipv6_support = None
self.security_service_update_support = False
# PoolState for all pools
self.pools = {}
@ -335,6 +336,10 @@ class HostState(object):
pool_cap['sg_consistent_snapshot_support'] = (
self.sg_consistent_snapshot_support)
if 'security_service_update_support' not in pool_cap:
pool_cap['security_service_update_support'] = (
self.security_service_update_support)
if self.ipv4_support is not None:
pool_cap['ipv4_support'] = self.ipv4_support
@ -364,6 +369,8 @@ class HostState(object):
self.ipv4_support = capability['ipv4_support']
if capability.get('ipv6_support') is not None:
self.ipv6_support = capability['ipv6_support']
self.security_service_update_support = capability.get(
'security_service_update_support', False)
def consume_from_share(self, share):
"""Incrementally update host state from an share."""
@ -460,6 +467,8 @@ class PoolState(HostState):
'replication_domain')
self.sg_consistent_snapshot_support = capability.get(
'sg_consistent_snapshot_support')
self.security_service_update_support = capability.get(
'security_service_update_support', False)
def update_pools(self, capability):
# Do nothing, since we don't have pools within pool, yet

View File

@ -57,6 +57,8 @@ def generate_stats(host_state, properties):
host_state.sg_consistent_snapshot_support),
'ipv4_support': host_state.ipv4_support,
'ipv6_support': host_state.ipv6_support,
'security_service_update_support': (
host_state.security_service_update_support)
}
host_caps = host_state.capabilities

View File

@ -94,6 +94,25 @@ class ShareInstanceAccessDatabaseMixin(object):
context, share_instance_id, updates, with_share_data=True)
return share_instance
def update_share_instances_access_rules_status(
self, context, status, share_instance_ids):
"""Update the access_rules_status of all share instances.
.. note::
Before making this call, make sure that all share instances have
their status set to a value that will block new operations to
happen during this update.
:param status: Force a state change on all share instances regardless
of the prior state.
:param share_instance_ids: List of share instance ids to have their
access rules status updated.
"""
updates = {'access_rules_status': status}
self.db.share_instances_status_update(
context, share_instance_ids, updates)
@locked_access_rules_operation
def get_and_update_share_instance_access_rules(self, context,
filters=None, updates=None,
@ -321,7 +340,7 @@ class ShareInstanceAccess(ShareInstanceAccessDatabaseMixin):
add_rules, delete_rules, rules_to_be_removed_from_db,
share_server)
self._process_driver_rule_updates(
self.process_driver_rule_updates(
context, driver_rule_updates, share_instance_id)
# Update access rules that are still in 'applying' state
@ -434,8 +453,8 @@ class ShareInstanceAccess(ShareInstanceAccessDatabaseMixin):
context, conditionally_change=conditionally_change,
share_instance_id=share_instance_id)
def _process_driver_rule_updates(self, context, driver_rule_updates,
share_instance_id):
def process_driver_rule_updates(self, context, driver_rule_updates,
share_instance_id):
for rule_id, rule_updates in driver_rule_updates.items():
if 'state' in rule_updates:
# We allow updates *only* if the state is unchanged from

View File

@ -19,6 +19,7 @@
"""
Handles all requests relating to shares.
"""
import json
from oslo_config import cfg
from oslo_log import log
@ -27,7 +28,10 @@ from oslo_utils import strutils
from oslo_utils import timeutils
import six
from manila.api import common as api_common
from manila.common import constants
from manila import context as manila_context
from manila import coordination
from manila.data import rpcapi as data_rpcapi
from manila.db import base
from manila import exception
@ -61,6 +65,29 @@ GB = 1048576 * 1024
QUOTAS = quota.QUOTAS
def locked_security_service_update_operation(operation):
"""Lock decorator for security service operation.
Takes a named lock prior to executing the operation. The lock is named with
the ids of the security services.
"""
def wrapped(*args, **kwargs):
new_id = kwargs.get('new_security_service_id', '')
current_id = kwargs.get('current_security_service_id', '')
@coordination.synchronized(
'locked-security-service-update-operation-%(new)s-%(curr)s' % {
'new': new_id,
'curr': current_id,
})
def locked_security_service_operation(*_args, **_kwargs):
return operation(*_args, **_kwargs)
return locked_security_service_operation(*args, **kwargs)
return wrapped
class API(base.Base):
"""API for interacting with the share manager."""
@ -69,6 +96,7 @@ class API(base.Base):
self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
self.share_rpcapi = share_rpcapi.ShareAPI()
self.access_helper = access.ShareInstanceAccess(self.db, None)
coordination.LOCK_COORDINATOR.start()
def _get_all_availability_zones_with_subnets(self, context,
share_network_id):
@ -826,6 +854,16 @@ class API(base.Base):
context, share_server['share_network_subnet_id'])
share_data['share_network_id'] = subnet['share_network_id']
try:
share_network = self.db.share_network_get(
context, share_data['share_network_id'])
except exception.ShareNetworkNotFound:
msg = _("Share network %s was not found."
) % share_data['share_network_id']
raise exception.InvalidInput(reason=msg)
# Check if share network is active, otherwise raise a BadRequest
api_common.check_share_network_is_active(share_network)
share_data.update({
'user_id': context.user_id,
'project_id': context.project_id,
@ -2694,3 +2732,338 @@ class API(base.Base):
'task_state': dest_share_server['task_state']
})
return result
def _share_network_update_initial_checks(self, context, share_network,
new_security_service,
current_security_service=None):
api_common.check_share_network_is_active(share_network)
if not current_security_service:
# Since we are adding a new security service, we can't have one
# of the same type already associated with this share network
for attached_service in share_network['security_services']:
if attached_service['type'] == new_security_service['type']:
msg = _("Cannot add security service to share network. "
"Security service with '%(ss_type)s' type already "
"added to '%(sn_id)s' share network") % {
'ss_type': new_security_service['type'],
'sn_id': share_network['id']
}
raise exception.InvalidSecurityService(reason=msg)
else:
# Validations needed only for update operation
current_service_is_associated = (
self.db.share_network_security_service_association_get(
context, share_network['id'],
current_security_service['id']))
if not current_service_is_associated:
msg = _("The specified current security service %(service)s "
"is not associated to the share network %(network)s."
) % {
'service': current_security_service['id'],
'network': share_network['id']
}
raise exception.InvalidSecurityService(reason=msg)
if (current_security_service['type'] !=
new_security_service['type']):
msg = _("A security service can only be replaced by one of "
"the same type. The current security service type is "
"'%(ss_type)s' and the new security service type is "
"'%(new_ss_type)s'") % {
'ss_type': current_security_service['type'],
'new_ss_type': new_security_service['type'],
'sn_id': share_network['id']
}
raise exception.InvalidSecurityService(reason=msg)
share_servers = set()
for subnet in share_network['share_network_subnets']:
if subnet['share_servers']:
share_servers.update(subnet['share_servers'])
backend_hosts = set()
if share_servers:
if not share_network['security_service_update_support']:
msg = _("Updating security services is not supported on this "
"share network (%(sn_id)s) while it has shares. "
"See the capability "
"'security_service_update_support'.") % {
"sn_id": share_network["id"]
}
raise exception.InvalidShareNetwork(reason=msg)
# We can only handle "active" share servers for now
for share_server in share_servers:
if share_server['status'] != constants.STATUS_ACTIVE:
msg = _('Some resources exported on share network '
'%(shar_net_id)s are not currently available.') % {
'shar_net_id': share_network['id']
}
raise exception.InvalidShareNetwork(reason=msg)
# Create a set of backend hosts
backend_hosts.add(share_server['host'])
for backend_host in backend_hosts:
# 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, backend_host)
shares = self.get_all(
context, search_opts={'share_network_id': share_network['id']})
shares_not_available = [
share['id'] for share in shares if
share['status'] != constants.STATUS_AVAILABLE]
if shares_not_available:
msg = _("Some shares exported on share network %(sn_id)s are "
"not available: %(share_ids)s.") % {
'sn_id': share_network['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 = _(
"Either these shares or one of their replicas or "
"migration copies exported on share network %(sn_id)s "
"are not available: %(share_ids)s.") % {
'sn_id': share_network['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 = _("Some shares exported on share network %(sn_id)s "
"are busy: %(share_ids)s.") % {
'sn_id': share_network['id'],
'share_ids': busy_shares,
}
raise exception.InvalidShareNetwork(reason=msg)
return list(share_servers), list(backend_hosts)
def get_security_service_update_key(
self, operation, new_security_service_id,
current_security_service_id=None):
if current_security_service_id:
return ('share_network_sec_service_update_' +
current_security_service_id + '_' +
new_security_service_id + '_' + operation)
else:
return ('share_network_sec_service_add_' +
new_security_service_id + '_' + operation)
@locked_security_service_update_operation
def _security_service_update_validate_hosts(
self, context, share_network,
backend_hosts, share_servers,
new_security_service_id=None,
current_security_service_id=None):
# create a key based on users request
update_key = self.get_security_service_update_key(
'hosts_check', new_security_service_id,
current_security_service_id=current_security_service_id)
# check if there is an entry being processed
update_value = self.db.async_operation_data_get(
context, share_network['id'], update_key)
if not update_value:
# Create a new entry, send all asynchronous rpcs and return
hosts_to_validate = {}
for host in backend_hosts:
hosts_to_validate[host] = None
self.db.async_operation_data_update(
context, share_network['id'],
{update_key: json.dumps(hosts_to_validate)})
for host in backend_hosts:
(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
else:
# process current existing hosts and update them if needed
current_hosts = json.loads(update_value)
hosts_to_include = (
set(backend_hosts).difference(set(current_hosts.keys())))
hosts_to_validate = {}
for host in backend_hosts:
hosts_to_validate[host] = current_hosts.get(host, None)
# Check if there is any unsupported host
if any(hosts_to_validate[host] is False for host in backend_hosts):
return False, hosts_to_validate
# Update the list of hosts to be validated
if hosts_to_include:
self.db.async_operation_data_update(
context, share_network['id'],
{update_key: json.dumps(hosts_to_validate)})
for host in hosts_to_include:
# send asynchronous check only for new backend hosts
(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
if all(hosts_to_validate[host] for host in backend_hosts):
return True, hosts_to_validate
return None, current_hosts
def check_share_network_security_service_update(
self, context, share_network, new_security_service,
current_security_service=None, reset_operation=False):
share_servers, backend_hosts = (
self._share_network_update_initial_checks(
context, share_network, new_security_service,
current_security_service=current_security_service))
if not backend_hosts:
# There is no backend host to validate. Operation is supported.
return {
'compatible': True,
'hosts_check_result': {},
}
curr_sec_serv_id = (
current_security_service['id']
if current_security_service else None)
key = self.get_security_service_update_key(
'hosts_check', new_security_service['id'],
current_security_service_id=curr_sec_serv_id)
if reset_operation:
self.db.async_operation_data_delete(context, share_network['id'],
key)
try:
compatible, hosts_info = (
self._security_service_update_validate_hosts(
context, share_network, backend_hosts, share_servers,
new_security_service_id=new_security_service['id'],
current_security_service_id=curr_sec_serv_id))
except Exception as e:
LOG.error(e)
# Due to an internal error, we will delete the entry
self.db.async_operation_data_delete(
context, share_network['id'], key)
msg = _(
'The share network %(share_net_id)s cannot be updated '
'since at least one of its backend hosts do not support '
'this operation.') % {
'share_net_id': share_network['id']}
raise exception.InvalidShareNetwork(reason=msg)
return {
'compatible': compatible,
'hosts_check_result': hosts_info
}
def update_share_network_security_service(self, context, share_network,
new_security_service,
current_security_service=None):
share_servers, backend_hosts = (
self._share_network_update_initial_checks(
context, share_network, new_security_service,
current_security_service=current_security_service))
if not backend_hosts:
# There is no backend host to validate or update.
return
curr_sec_serv_id = (
current_security_service['id']
if current_security_service else None)
update_key = self.get_security_service_update_key(
'hosts_check', new_security_service['id'],
current_security_service_id=curr_sec_serv_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)
try:
result, __ = self._security_service_update_validate_hosts(
context, share_network, backend_hosts, share_servers,
new_security_service_id=new_security_service['id'],
current_security_service_id=curr_sec_serv_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 share network %(share_net_id)s cannot be updated '
'since at least one of its backend hosts do not support '
'this operation.') % {
'share_net_id': share_network['id']}
raise exception.InvalidShareNetwork(reason=msg)
if result is False:
msg = _(
'The share network %(share_net_id)s cannot be updated '
'since at least one of its backend hosts do not support '
'this operation.') % {
'share_net_id': share_network['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)
self.db.share_network_update(
context, share_network['id'],
{'status': constants.STATUS_NETWORK_CHANGE})
# NOTE(dviroel): We want to change the status for all share servers to
# identify when all modifications are made, and update share network
# status to 'active' again.
share_servers_ids = [ss.id for ss in share_servers]
self.db.share_servers_update(
context, share_servers_ids,
{'status': constants.STATUS_SERVER_NETWORK_CHANGE})
for backend_host in backend_hosts:
self.share_rpcapi.update_share_network_security_service(
context, backend_host, share_network['id'],
new_security_service['id'],
current_security_service_id=curr_sec_serv_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('Security service update has been started for share network '
'%(share_net_id)s.', {'share_net_id': share_network['id']})

View File

@ -273,6 +273,10 @@ class ShareDriver(object):
self._stats = {}
self.ip_versions = None
self.ipv6_implemented = False
# Indicates whether a driver supports update of security services for
# in-use share networks. This property will be saved in every new share
# server.
self.security_service_update_support = False
self.pools = []
if self.configuration:
@ -1315,6 +1319,8 @@ class ShareDriver(object):
replication_domain=self.replication_domain,
filter_function=self.get_filter_function(),
goodness_function=self.get_goodness_function(),
security_service_update_support=(
self.security_service_update_support),
)
if isinstance(data, dict):
common.update(data)
@ -3184,3 +3190,134 @@ class ShareDriver(object):
"""
raise NotImplementedError()
def update_share_server_security_service(
self, context, share_server, network_info, share_instances,
share_instance_rules, new_security_service,
current_security_service=None):
"""Updates share server security service configuration.
If the driver supports different security services, the user can
request the addition of a new security service, with a different type.
If the user wants to update the current security service configuration,
the driver will receive both current and new security services, which
will always be of the same type.
:param context: The 'context.RequestContext' object for the request.
:param share_server: Reference to the share server object that will be
updated.
:param network_info: All network allocation associated with the share
server that will be updated.
:param share_instances: A list of share instances that belong to the
share server that is being updated.
:param share_instance_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'
...
},
],
},
]
:param new_security_service: New security service object to be
configured in the share server.
:param current_security_service: When provided, represents the current
security service that will be replaced by the
'new_security_service'.
:raises: ShareBackendException.
A ShareBackendException should only be raised if the share server
failed to update the security service, compromising all its access
rules. By raising an exception, the share server and all its share
instances will be set to 'error'.
:return: None, or a dictionary of updates in the following format.
Example::
{
'3bc10d67-2598-4122-bb62-0bdeaa8c6db3':
{
'09960614-8574-4e03-89cf-7cf267b0bd08':
{
'access_key': 'alice31493e5441b8171d2310d80e37e',
'state': 'error',
},
'28f6eabb-4342-486a-a7f4-45688f0c0295':
{
'access_key': 'bob0078aa042d5a7325480fd13228b',
'state': 'active',
},
},
}
The top level keys are share_instance_id's which should provide
another dictionary of access rules to be updated, indexed by their
'access_id'. The inner access rules dictionary should only contain the
access rules that need to be updated.
"""
raise NotImplementedError()
def check_update_share_server_security_service(
self, context, share_server, network_info, share_instances,
share_instance_rules, new_security_service,
current_security_service=None):
"""Check if the current share server security service is supported.
If the driver supports different security services, the user can
request the addition of a new security service, with a different type.
If the user wants to update the current security service configuration,
the driver will receive both current and new security services, which
will always be of the same type.
:param context: The 'context.RequestContext' object for the request.
:param share_server: Reference to the share server object that will be
updated.
:param network_info: All network allocation associated with the share
server that will be updated.
:param share_instances: A list of share instances that belong to the
share server that is affected by the update.
:param share_instance_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'
...
},
],
},
]
:param new_security_service: New security service object to be
configured in the share server.
:param current_security_service: When provided, represents the current
security service that will be replaced by the
'new_security_service'.
:return: 'True' if the driver support the requested update, 'False'
otherwise.
"""
raise NotImplementedError()

View File

@ -23,6 +23,7 @@ import copy
import datetime
import functools
import hashlib
import json
from operator import xor
from oslo_config import cfg
@ -183,6 +184,25 @@ def locked_share_replica_operation(operation):
return wrapped
def locked_share_network_operation(operation):
"""Lock decorator for share network operations.
Takes a named lock prior to executing the operation. The lock is named with
the id of the share network.
"""
def wrapped(*args, **kwargs):
share_network_id = kwargs.get('share_network_id')
@coordination.synchronized(
'locked-share-network-operation-%s' % share_network_id)
def locked_network_operation(*_args, **_kwargs):
return operation(*_args, **_kwargs)
return locked_network_operation(*args, **kwargs)
return wrapped
def add_hooks(f):
"""Hook decorator to perform action before and after a share method call
@ -218,7 +238,7 @@ def add_hooks(f):
class ShareManager(manager.SchedulerDependentManager):
"""Manages NAS storages."""
RPC_API_VERSION = '1.21'
RPC_API_VERSION = '1.22'
def __init__(self, share_driver=None, service_name=None, *args, **kwargs):
"""Load the driver from args, or from flags."""
@ -698,6 +718,8 @@ class ShareManager(manager.SchedulerDependentManager):
'host': self.host,
'share_network_subnet_id': share_network_subnet_id,
'status': constants.STATUS_CREATING,
'security_service_update_support': (
self.driver.security_service_update_support),
}
)
@ -785,6 +807,8 @@ class ShareManager(manager.SchedulerDependentManager):
'host': self.host,
'share_network_subnet_id': share_network_subnet_id,
'status': constants.STATUS_CREATING,
'security_service_update_support': (
self.driver.security_service_update_support),
}
)
@ -962,7 +986,9 @@ class ShareManager(manager.SchedulerDependentManager):
{
'host': self.host,
'share_network_subnet_id': share_network_subnet_id,
'status': constants.STATUS_CREATING
'status': constants.STATUS_CREATING,
'security_service_update_support': (
self.driver.security_service_update_support),
}
)
@ -2046,11 +2072,11 @@ class ShareManager(manager.SchedulerDependentManager):
self._notify_about_share_usage(context, share,
share_instance, "create.end")
def _update_share_replica_access_rules_state(self, context,
share_replica_id, state):
"""Update the access_rules_status for the share replica."""
def _update_share_instance_access_rules_state(self, context,
share_instance_id, state):
"""Update the access_rules_status for the share instance."""
self.access_helper.get_and_update_share_instance_access_rules_status(
context, status=state, share_instance_id=share_replica_id)
context, status=state, share_instance_id=share_instance_id)
def _get_replica_snapshots_for_snapshot(self, context, snapshot_id,
active_replica_id,
@ -2208,7 +2234,7 @@ class ShareManager(manager.SchedulerDependentManager):
context, share_replica['id'],
{'status': constants.STATUS_ERROR,
'replica_state': constants.STATUS_ERROR})
self._update_share_replica_access_rules_state(
self._update_share_instance_access_rules_state(
context, share_replica['id'], constants.STATUS_ERROR)
self.message_api.create(
context,
@ -2236,11 +2262,11 @@ class ShareManager(manager.SchedulerDependentManager):
'progress': '100%'})
if replica_ref.get('access_rules_status'):
self._update_share_replica_access_rules_state(
self._update_share_instance_access_rules_state(
context, share_replica['id'],
replica_ref.get('access_rules_status'))
else:
self._update_share_replica_access_rules_state(
self._update_share_instance_access_rules_state(
context, share_replica['id'],
constants.STATUS_ACTIVE)
@ -2497,7 +2523,7 @@ class ShareManager(manager.SchedulerDependentManager):
context, updated_replica['id'], updates)
if updated_replica.get('access_rules_status'):
self._update_share_replica_access_rules_state(
self._update_share_instance_access_rules_state(
context, share_replica['id'],
updated_replica.get('access_rules_status'))
@ -3861,6 +3887,7 @@ class ShareManager(manager.SchedulerDependentManager):
def _report_driver_status(self, context):
LOG.info('Updating share status')
share_stats = self.driver.get_share_stats(refresh=True)
if not share_stats:
return
@ -4629,7 +4656,9 @@ class ShareManager(manager.SchedulerDependentManager):
'is_auto_deletable': share_server.get('is_auto_deletable', None),
'identifier': share_server.get('identifier', None),
'network_allocations': share_server.get('network_allocations',
None)
None),
'share_network_status': share_server.get(
'share_network_status', None)
}
return share_server_ref
@ -4680,6 +4709,7 @@ class ShareManager(manager.SchedulerDependentManager):
'source_share_group_snapshot_member_id': share_instance.get(
'source_share_group_snapshot_member_id'),
'availability_zone': share_instance.get('availability_zone'),
'share_network_status': share_instance.get('share_network_status')
}
if share_instance_ref['share_server']:
share_instance_ref['share_server'] = self._get_share_server_dict(
@ -5486,3 +5516,204 @@ class ShareManager(manager.SchedulerDependentManager):
return self.driver.share_server_migration_get_progress(
context, src_share_server, dest_share_server, share_instances,
snapshot_instances)
@locked_share_network_operation
def _check_share_network_update_finished(self, context, share_network_id):
# Check if this share network is already active
share_network = self.db.share_network_get(context, share_network_id)
if share_network['status'] == constants.STATUS_NETWORK_ACTIVE:
return
share_servers = self.db.share_server_get_all_with_filters(
context, {'share_network_id': share_network_id}
)
if all([ss['status'] != constants.STATUS_SERVER_NETWORK_CHANGE
for ss in share_servers]):
# All share servers have updated their configuration
self.db.share_network_update(
context, share_network_id,
{'status': constants.STATUS_NETWORK_ACTIVE})
def _update_share_network_security_service(
self, context, share_network_id, new_security_service_id,
current_security_service_id=None, check_only=False):
new_security_service = self.db.security_service_get(
context, new_security_service_id)
current_security_service = None
if current_security_service_id:
current_security_service = self.db.security_service_get(
context, current_security_service_id)
new_ss_type = new_security_service['type']
backend_details_data = {
'name': new_security_service['name'],
'ou': new_security_service['ou'],
'domain': new_security_service['domain'],
'server': new_security_service['server'],
'dns_ip': new_security_service['dns_ip'],
'user': new_security_service['user'],
'type': new_ss_type,
'password': new_security_service['password'],
}
share_network = self.db.share_network_get(
context, share_network_id)
share_servers = self.db.share_server_get_all_by_host(
context, self.host,
filters={'share_network_id': share_network_id})
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.
share_network_subnet = self.db.share_network_subnet_get(
context, share_network_subnet_id)
network_info = self._form_server_setup_info(
context, share_server, share_network, share_network_subnet)
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)
# Only check if the driver supports this kind of update.
if check_only:
if self.driver.check_update_share_server_security_service(
context, share_server, network_info,
share_instances, share_instances_rules,
new_security_service,
current_security_service=current_security_service):
# Check the next share server.
continue
else:
# At least one share server doesn't support this update
return False
# NOTE(dviroel): We always do backend details update since it
# should be the expected configuration for this share server. Any
# issue with this operation should be fixed by the admin which will
# guarantee that storage and backend_details configurations match.
self.db.share_server_backend_details_set(
context, share_server['id'],
{'security_service_' + new_ss_type: jsonutils.dumps(
backend_details_data)})
try:
updates = self.driver.update_share_server_security_service(
context, share_server, network_info,
share_instances, share_instances_rules,
new_security_service,
current_security_service=current_security_service) or {}
except Exception:
operation = 'add'
sec_serv_info = ('new security service %s'
% new_security_service_id)
if current_security_service_id:
operation = 'update'
sec_serv_info = ('current security service %s and '
% current_security_service_id +
sec_serv_info)
msg = _("Share server %(server_id)s has failed on security "
"service %(operation)s operation for "
"%(sec_serv_ids)s.") % {
'server_id': share_server['id'],
'operation': operation,
'sec_serv_ids': sec_serv_info,
}
LOG.exception(msg)
# Set share server to error. Security service configuration
# must be fixed before restoring it to active again.
self.db.share_server_update(
context, share_server['id'],
{'status': constants.STATUS_ERROR})
if current_security_service:
# NOTE(dviroel): An already configured security service has
# failed on update operation. We will set all share
# instances to 'error'.
if share_instance_ids:
self.db.share_instances_status_update(
context, share_instance_ids,
{'status': constants.STATUS_ERROR})
# Update share instance access rules status
(self.access_helper
.update_share_instances_access_rules_status(
context, constants.SHARE_INSTANCE_RULES_ERROR,
share_instance_ids))
# Go to the next share server
continue
# Update access rules based on drivers updates
for instance_id, rules_updates in updates.items():
self.access_helper.process_driver_rule_updates(
context, rules_updates, instance_id)
msg = _("Security service 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})
if check_only:
# All share servers support the requested update
return True
# 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'])
def update_share_network_security_service(
self, context, share_network_id, new_security_service_id,
current_security_service_id=None):
self._update_share_network_security_service(
context, share_network_id, new_security_service_id,
current_security_service_id=current_security_service_id,
check_only=False)
def check_update_share_network_security_service(
self, context, share_network_id, new_security_service_id,
current_security_service_id=None):
is_supported = self._update_share_network_security_service(
context, share_network_id, new_security_service_id,
current_security_service_id=current_security_service_id,
check_only=True)
self._update_share_network_security_service_operations(
context, share_network_id, is_supported,
new_security_service_id=new_security_service_id,
current_security_service_id=current_security_service_id)
@api.locked_security_service_update_operation
def _update_share_network_security_service_operations(
self, context, share_network_id, is_supported,
new_security_service_id=None,
current_security_service_id=None):
update_check_key = self.share_api.get_security_service_update_key(
'hosts_check', new_security_service_id,
current_security_service_id)
current_hosts_info = self.db.async_operation_data_get(
context, share_network_id, update_check_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_check_key: json.dumps(current_hosts)})
else:
LOG.debug('A share network security service check was requested '
'but no entries were found in database. Ignoring call '
'and returning.')

View File

@ -79,6 +79,8 @@ class ShareAPI(object):
1.20 - Add share_instance_id parameter for create_share_server() method
1.21 - Add share_server_migration_start, share_server_migration_check()
and share_server_get_progress()
1.22 - Add update_share_network_security_service() and
check_update_share_network_security_service()
"""
BASE_RPC_API_VERSION = '1.0'
@ -87,7 +89,7 @@ class ShareAPI(object):
super(ShareAPI, self).__init__()
target = messaging.Target(topic=CONF.share_topic,
version=self.BASE_RPC_API_VERSION)
self.client = rpc.get_client(target, version_cap='1.21')
self.client = rpc.get_client(target, version_cap='1.22')
def create_share_instance(self, context, share_instance, host,
request_spec, filter_properties,
@ -436,3 +438,27 @@ class ShareAPI(object):
call_context.cast(context,
'snapshot_update_access',
snapshot_instance_id=snapshot_instance['id'])
def update_share_network_security_service(
self, context, dest_host, share_network_id,
new_security_service_id, current_security_service_id=None):
host = utils.extract_host(dest_host)
call_context = self.client.prepare(server=host, version='1.22')
call_context.cast(
context,
'update_share_network_security_service',
share_network_id=share_network_id,
new_security_service_id=new_security_service_id,
current_security_service_id=current_security_service_id)
def check_update_share_network_security_service(
self, context, dest_host, share_network_id,
new_security_service_id, current_security_service_id=None):
host = utils.extract_host(dest_host)
call_context = self.client.prepare(server=host, version='1.22')
call_context.cast(
context,
'check_update_share_network_security_service',
share_network_id=share_network_id,
new_security_service_id=new_security_service_id,
current_security_service_id=current_security_service_id)

View File

@ -23,6 +23,7 @@ from oslo_utils import excutils
from oslo_utils import strutils
import six
from manila.api import common as api_common
from manila.common import constants
from manila.db import base
from manila import exception
@ -109,13 +110,19 @@ class API(base.Base):
"False, a share_network_id must not be provided.")
raise exception.InvalidInput(reason=msg)
share_network = {}
try:
if share_network_id:
self.db.share_network_get(context, share_network_id)
share_network = self.db.share_network_get(
context, share_network_id)
except exception.ShareNetworkNotFound:
msg = _("The specified share network does not exist.")
raise exception.InvalidInput(reason=msg)
if share_network:
# Check if share network is active, otherwise raise a BadRequest
api_common.check_share_network_is_active(share_network)
if (driver_handles_share_servers and
not (source_share_group_snapshot_id or share_network_id)):
msg = _("When using a share type with the "

View File

@ -42,7 +42,8 @@ fake_share_server_list = {
'is_auto_deletable': False,
'task_state': None,
'source_share_server_id': None,
'identifier': 'fake_id'
'identifier': 'fake_id',
'security_service_update_support': False
},
{
'status': constants.STATUS_ERROR,
@ -56,7 +57,9 @@ fake_share_server_list = {
'is_auto_deletable': True,
'task_state': None,
'source_share_server_id': None,
'identifier': 'fake_id_2'
'identifier': 'fake_id_2',
'security_service_update_support': False
},
]
}
@ -94,7 +97,8 @@ fake_share_server_get_result = {
'is_auto_deletable': False,
'task_state': None,
'source_share_server_id': None,
'identifier': 'fake_id'
'identifier': 'fake_id',
'security_service_update_support': False
}
}
@ -131,6 +135,8 @@ class FakeShareServer(object):
self.task_state = kwargs.get('task_state')
self.source_share_server_id = kwargs.get('source_share_server_id')
self.backend_details = share_server_backend_details
self.security_service_update_support = kwargs.get(
'security_service_update_support', False)
def __getitem__(self, item):
return getattr(self, item)
@ -148,7 +154,8 @@ def fake_share_server_get_all():
identifier='fake_id_2',
task_state=None,
is_auto_deletable=True,
status=constants.STATUS_ERROR)
status=constants.STATUS_ERROR,
security_service_update_support=False)
]
return fake_share_servers

View File

@ -228,6 +228,7 @@ class ShareAPITest(test.TestCase):
"availability_zone": "zone1:host1",
"share_network_id": "fakenetid"
}
fake_network = {'id': 'fakenetid'}
create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'],
display_description=shr['description'],
@ -237,7 +238,9 @@ class ShareAPITest(test.TestCase):
share_network_id=shr['share_network_id']))
self.mock_object(share_api.API, 'create', create_mock)
self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value={'id': 'fakenetid'}))
return_value=fake_network))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id',
mock.Mock(return_value={'id': 'fakesubnetid'}))
@ -250,11 +253,50 @@ class ShareAPITest(test.TestCase):
req.environ['manila.context'], self.resource_name, 'create')
expected = self._get_expected_share_detailed_response(shr)
expected['share'].pop('snapshot_support')
common.check_share_network_is_active.assert_called_once_with(
fake_network)
self.assertEqual(expected, res_dict)
# pylint: disable=unsubscriptable-object
self.assertEqual("fakenetid",
create_mock.call_args[1]['share_network_id'])
def test_share_create_with_share_net_not_active(self):
shr = {
"size": 100,
"name": "Share Test Name",
"description": "Share Test Desc",
"share_proto": "fakeproto",
"availability_zone": "zone1:host1",
"share_network_id": "fakenetid"
}
share_network = db_utils.create_share_network(
status=constants.STATUS_NETWORK_CHANGE)
create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'],
display_description=shr['description'],
size=shr['size'],
share_proto=shr['share_proto'].upper(),
availability_zone=shr['availability_zone'],
share_network_id=shr['share_network_id']))
self.mock_object(share_api.API, 'create', create_mock)
self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value=share_network))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(side_effect=webob.exc.HTTPBadRequest()))
body = {"share": copy.deepcopy(shr)}
req = fakes.HTTPRequest.blank('/shares')
self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller.create,
req,
body)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'create')
common.check_share_network_is_active.assert_called_once_with(
share_network)
def test_share_create_from_snapshot_without_share_net_no_parent(self):
shr = {
"size": 100,
@ -296,6 +338,7 @@ class ShareAPITest(test.TestCase):
"share_network_id": None,
}
parent_share_net = 444
fake_share_net = {'id': parent_share_net}
create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'],
display_description=shr['description'],
@ -314,7 +357,9 @@ class ShareAPITest(test.TestCase):
self.mock_object(share_api.API, 'get', mock.Mock(
return_value=parent_share))
self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value={'id': parent_share_net}))
return_value=fake_share_net))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id')
@ -327,6 +372,8 @@ class ShareAPITest(test.TestCase):
req.environ['manila.context'], self.resource_name, 'create')
expected = self._get_expected_share_detailed_response(shr)
expected['share'].pop('snapshot_support')
common.check_share_network_is_active.assert_called_once_with(
fake_share_net)
self.assertEqual(expected, res_dict)
# pylint: disable=unsubscriptable-object
self.assertEqual(parent_share_net,
@ -343,6 +390,7 @@ class ShareAPITest(test.TestCase):
"snapshot_id": 333,
"share_network_id": parent_share_net
}
fake_share_net = {'id': parent_share_net}
create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'],
display_description=shr['description'],
@ -355,13 +403,15 @@ class ShareAPITest(test.TestCase):
self.mock_object(share_api.API, 'create', create_mock)
self.mock_object(share_api.API, 'get_snapshot',
stubs.stub_snapshot_get)
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
parent_share = stubs.stub_share(
'1', instance={'share_network_id': parent_share_net},
create_share_from_snapshot_support=True)
self.mock_object(share_api.API, 'get', mock.Mock(
return_value=parent_share))
self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value={'id': parent_share_net}))
return_value=fake_share_net))
self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id')
@ -374,6 +424,8 @@ class ShareAPITest(test.TestCase):
req.environ['manila.context'], self.resource_name, 'create')
expected = self._get_expected_share_detailed_response(shr)
expected['share'].pop('snapshot_support')
common.check_share_network_is_active.assert_called_once_with(
fake_share_net)
self.assertEqual(expected, res_dict)
# pylint: disable=unsubscriptable-object
self.assertEqual(parent_share_net,
@ -415,6 +467,7 @@ class ShareAPITest(test.TestCase):
"snapshot_id": 333,
"share_network_id": parent_share_net
}
fake_share_net = {'id': parent_share_net}
create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'],
display_description=shr['description'],
@ -427,13 +480,15 @@ class ShareAPITest(test.TestCase):
self.mock_object(share_api.API, 'create', create_mock)
self.mock_object(share_api.API, 'get_snapshot',
stubs.stub_snapshot_get)
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
parent_share = stubs.stub_share(
'1', instance={'share_network_id': parent_share_net},
create_share_from_snapshot_support=False)
self.mock_object(share_api.API, 'get', mock.Mock(
return_value=parent_share))
self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value={'id': parent_share_net}))
return_value=fake_share_net))
self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id')
@ -446,6 +501,8 @@ class ShareAPITest(test.TestCase):
req.environ['manila.context'], self.resource_name, 'create')
expected = self._get_expected_share_detailed_response(shr)
expected['share'].pop('snapshot_support')
common.check_share_network_is_active.assert_called_once_with(
fake_share_net)
self.assertDictEqual(expected, res_dict)
# pylint: disable=unsubscriptable-object
self.assertEqual(parent_share_net,
@ -503,6 +560,7 @@ class ShareAPITest(test.TestCase):
self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id',
mock.Mock(return_value=None))
self.mock_object(common, 'check_share_network_is_active')
body = {"share": fake_share_with_sn}
@ -903,6 +961,29 @@ class ShareActionsTest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], 'share', 'allow_access')
def test_allow_access_with_network_id(self):
share_network = db_utils.create_share_network()
share = db_utils.create_share(share_network_id=share_network['id'])
access = {'access_type': 'user', 'access_to': '1' * 4}
self.mock_object(share_api.API,
'allow_access',
mock.Mock(return_value={'fake': 'fake'}))
self.mock_object(self.controller._access_view_builder, 'view',
mock.Mock(return_value={'access': {'fake': 'fake'}}))
self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
id = 'fake_share_id'
body = {'os-allow_access': access}
expected = {'access': {'fake': 'fake'}}
req = fakes.HTTPRequest.blank('/v1/tenant1/shares/%s/action' % id)
res = self.controller._allow_access(req, id, body)
self.assertEqual(expected, res)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], 'share', 'allow_access')
@ddt.data(
{'access_type': 'error_type', 'access_to': '127.0.0.1'},
{'access_type': 'ip', 'access_to': 'localhost'},
@ -947,6 +1028,23 @@ class ShareActionsTest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], 'share', 'deny_access')
def test_deny_access_with_share_network_id(self):
self.mock_object(share_api.API, "deny_access", mock.Mock())
self.mock_object(share_api.API, "access_get", _fake_access_get)
share_network = db_utils.create_share_network()
share = db_utils.create_share(share_network_id=share_network['id'])
self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
id = 'fake_share_id'
body = {"os-deny_access": {"access_id": 'fake_acces_id'}}
req = fakes.HTTPRequest.blank('/v1/tenant1/shares/%s/action' % id)
res = self.controller._deny_access(req, id, body)
self.assertEqual(202, res.status_int)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], 'share', 'deny_access')
def test_deny_access_not_found(self):
def _stub_deny_access(*args, **kwargs):
pass

View File

@ -28,8 +28,10 @@ from manila.api.v2 import share_networks
from manila.db import api as db_api
from manila import exception
from manila import quota
from manila.share import api as share_api
from manila import test
from manila.tests.api import fakes
from manila.tests import db_utils
fake_share_network_subnet = {
@ -54,7 +56,9 @@ fake_share_network = {
'name': 'fake name',
'description': 'fake description',
'security_services': [],
'share_network_subnets': []
'share_network_subnets': [],
'security_service_update_support': True,
'status': 'active'
}
@ -79,6 +83,8 @@ fake_sn_with_ss_shortened = {
'name': 'test-sn',
}
ADD_UPDATE_SEC_SERVICE_VERSION = '2.63'
QUOTAS = quota.QUOTAS
@ -91,6 +97,7 @@ class ShareNetworkAPITest(test.TestCase):
self.req = fakes.HTTPRequest.blank('/share-networks')
self.body = {share_networks.RESOURCE_NAME: {'name': 'fake name'}}
self.context = self.req.environ['manila.context']
self.share_api = share_api.API()
def _check_share_network_view_shortened(self, view, share_nw):
self.assertEqual(share_nw['id'], view['id'])
@ -874,7 +881,7 @@ class ShareNetworkAPITest(test.TestCase):
self.context, share_nw)
@ddt.data(*set(("1.0", "2.25", "2.26", api_version._MAX_API_VERSION)))
def test_action_add_security_service(self, microversion):
def test_add_security_service(self, microversion):
share_network_id = 'fake network id'
security_service_id = 'fake ss id'
self.mock_object(
@ -884,15 +891,123 @@ class ShareNetworkAPITest(test.TestCase):
security_service_id}}
req = fakes.HTTPRequest.blank('/share-networks', version=microversion)
with mock.patch.object(self.controller, '_add_security_service',
with mock.patch.object(self.controller, 'add_security_service',
mock.Mock()):
self.controller.action(req, share_network_id, body)
self.controller._add_security_service.assert_called_once_with(
req, share_network_id, body['add_security_service'])
self.controller.add_security_service(req, share_network_id, body)
self.controller.add_security_service.assert_called_once_with(
req, share_network_id, body)
@mock.patch.object(db_api, 'share_network_get', mock.Mock())
@mock.patch.object(db_api, 'security_service_get', mock.Mock())
def test_action_add_security_service_conflict(self):
def _setup_add_sec_services_with_servers_tests(
self, share_network, security_service, network_is_active=True,
version=ADD_UPDATE_SEC_SERVICE_VERSION,
share_api_update_services_action=mock.Mock()):
self.mock_object(
db_api, 'share_network_get', mock.Mock(return_value=share_network))
self.mock_object(
db_api, 'security_service_get',
mock.Mock(return_value=security_service))
self.mock_object(
self.controller, '_share_network_subnets_contain_share_servers',
mock.Mock(return_value=True))
self.mock_object(
self.controller.share_api, 'update_share_network_security_service',
share_api_update_services_action)
self.mock_object(
common, 'check_share_network_is_active',
mock.Mock(return_value=network_is_active))
self.mock_object(db_api, 'share_network_add_security_service')
self.mock_object(self.controller._view_builder, 'build_share_network')
body = {
'add_security_service': {
'security_service_id': security_service['id']
}
}
req = fakes.HTTPRequest.blank(
'/add_security_service', version=version, use_admin_context=True)
context = req.environ['manila.context']
return req, context, body
def test_add_security_service_with_servers(self):
security_service = db_utils.create_security_service()
security_service_id = security_service['id']
share_network = db_utils.create_share_network()
share_network_id = share_network['id']
req, context, body = self._setup_add_sec_services_with_servers_tests(
share_network, security_service)
self.controller.add_security_service(req, share_network_id, body)
db_api.security_service_get.assert_called_once_with(
context, security_service_id)
(self.controller._share_network_subnets_contain_share_servers.
assert_called_once_with(share_network))
db_api.share_network_get.assert_called_once_with(
context, share_network_id)
(self.controller.share_api.update_share_network_security_service.
assert_called_once_with(context, share_network, security_service))
def test_add_security_service_with_server_invalid_version(self):
security_service = db_utils.create_security_service()
security_service_id = security_service['id']
share_network = db_utils.create_share_network()
share_network_id = share_network['id']
req, context, body = self._setup_add_sec_services_with_servers_tests(
share_network, security_service, version='2.59')
self.assertRaises(
webob_exc.HTTPForbidden,
self.controller.add_security_service,
req, share_network_id, body
)
db_api.security_service_get.assert_called_once_with(
context, security_service_id)
(self.controller._share_network_subnets_contain_share_servers.
assert_called_once_with(share_network))
db_api.share_network_get.assert_called_once_with(
context, share_network_id)
@ddt.data(
(exception.ServiceIsDown(message='fake'), webob_exc.HTTPConflict),
(exception.InvalidShareNetwork(message='fake'),
webob_exc.HTTPBadRequest)
)
@ddt.unpack
def test_add_security_service_with_server_update_failed(
self, side_effect, exception_to_raise):
security_service = db_utils.create_security_service()
security_service_id = security_service['id']
share_network_id = fake_share_network['id']
fake_share_network['security_service_update_support'] = True
action = mock.Mock(side_effect=side_effect)
req, context, body = self._setup_add_sec_services_with_servers_tests(
fake_share_network, security_service,
share_api_update_services_action=action)
self.assertRaises(
exception_to_raise,
self.controller.add_security_service,
req, share_network_id, body
)
db_api.security_service_get.assert_called_once_with(
context, security_service_id)
db_api.share_network_get.assert_called_once_with(
context, share_network_id)
(self.controller.share_api.update_share_network_security_service.
assert_called_once_with(context, fake_share_network,
security_service))
@ddt.data(
(exception.NotFound(message='fake'), webob_exc.HTTPNotFound),
(exception.ShareNetworkSecurityServiceAssociationError(message='fake'),
webob_exc.HTTPBadRequest))
@ddt.unpack
def test_action_add_security_service_conflict(self, captured_exception,
expected_raised_exception):
share_network = fake_share_network.copy()
share_network['security_services'] = [{'id': 'security_service_1',
'type': 'ldap'}]
@ -900,28 +1015,148 @@ class ShareNetworkAPITest(test.TestCase):
'type': 'ldap'}
body = {'add_security_service': {'security_service_id':
security_service['id']}}
request = fakes.HTTPRequest.blank(
'/share-networks', use_admin_context=True)
self.mock_object(
self.controller, '_share_network_subnets_contain_share_servers',
mock.Mock(return_value=False))
update_sec_serv_mock = self.mock_object(
self.controller.share_api, 'update_share_network_security_service')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(db_api, 'security_service_get',
mock.Mock(return_value=security_service))
self.mock_object(share_networks.policy, 'check_policy')
self.mock_object(
db_api, 'share_network_add_security_service',
mock.Mock(side_effect=captured_exception))
db_api.security_service_get.return_value = security_service
db_api.share_network_get.return_value = share_network
with mock.patch.object(share_networks.policy, 'check_policy',
mock.Mock()):
self.assertRaises(webob_exc.HTTPConflict,
self.controller.action,
self.req,
share_network['id'],
body)
db_api.share_network_get.assert_called_once_with(
self.req.environ['manila.context'], share_network['id'])
db_api.security_service_get.assert_called_once_with(
self.req.environ['manila.context'], security_service['id'])
share_networks.policy.check_policy.assert_called_once_with(
self.req.environ['manila.context'],
share_networks.RESOURCE_NAME,
'add_security_service',
)
self.assertRaises(expected_raised_exception,
self.controller.add_security_service,
request,
share_network['id'],
body)
db_api.share_network_get.assert_called_once_with(
request.environ['manila.context'], share_network['id'])
db_api.security_service_get.assert_called_once_with(
request.environ['manila.context'], security_service['id'])
share_networks.policy.check_policy.assert_called_once_with(
request.environ['manila.context'],
share_networks.RESOURCE_NAME,
'add_security_service', target_obj=share_network)
update_sec_serv_mock.assert_called_once_with(
request.environ['manila.context'], share_network,
security_service)
def _setup_update_sec_services_with_servers_tests(
self, share_network, security_services,
version=ADD_UPDATE_SEC_SERVICE_VERSION,
share_api_update_services_action=mock.Mock()):
self.mock_object(
db_api, 'share_network_get', mock.Mock(return_value=share_network))
self.mock_object(
db_api, 'security_service_get',
mock.Mock(side_effect=security_services))
self.mock_object(
self.controller.share_api, 'update_share_network_security_service',
share_api_update_services_action)
self.mock_object(self.controller._view_builder, 'build_share_network')
self.mock_object(db_api, 'share_network_update_security_service')
body = {
'update_security_service': {
'current_service_id': security_services[0]['id'],
'new_service_id': security_services[1]['id']
}
}
req = fakes.HTTPRequest.blank(
'/add_security_service', version=version, use_admin_context=True)
context = req.environ['manila.context']
return req, context, body
def test_update_security_service_service_not_found(self):
security_services = [
db_utils.create_security_service() for i in range(2)]
share_network = copy.deepcopy(fake_share_network)
share_network['security_service_update_support'] = True
req, context, body = (
self._setup_update_sec_services_with_servers_tests(
share_network, security_services))
db_api.security_service_get.side_effect = exception.NotFound('fake')
self.assertRaises(
webob_exc.HTTPBadRequest,
self.controller.update_security_service,
req, share_network['id'], body)
db_api.share_network_get.assert_called_once_with(
context, share_network['id'])
db_api.security_service_get.assert_has_calls(
[mock.call(context, security_services[0]['id'])]
)
@ddt.data(
(exception.ServiceIsDown(message='fake'), webob_exc.HTTPConflict),
(exception.InvalidShareNetwork(message='fake'),
webob_exc.HTTPBadRequest))
@ddt.unpack
def test_update_security_service_share_api_failure(self, side_effect, exc):
security_services = [
db_utils.create_security_service() for i in range(2)]
share_network = copy.deepcopy(fake_share_network)
share_network['security_service_update_support'] = True
req, context, body = (
self._setup_update_sec_services_with_servers_tests(
share_network, security_services,
share_api_update_services_action=mock.Mock(
side_effect=side_effect)))
self.assertRaises(
exc,
self.controller.update_security_service,
req, share_network['id'], body)
db_api.share_network_get.assert_called_once_with(
context, share_network['id'])
db_api.security_service_get.assert_has_calls(
[mock.call(context, security_services[0]['id']),
mock.call(context, security_services[1]['id'])]
)
def test_update_security_service(self):
security_services = [
db_utils.create_security_service() for i in range(2)]
share_network = copy.copy(fake_share_network)
share_network['security_service_update_support'] = True
req, context, body = (
self._setup_update_sec_services_with_servers_tests(
share_network, security_services))
self.controller.update_security_service(
req, share_network['id'], body)
db_api.share_network_get.assert_called_once_with(
context, share_network['id'])
db_api.security_service_get.assert_has_calls(
[mock.call(context, security_services[0]['id']),
mock.call(context, security_services[1]['id'])]
)
(self.controller.share_api.update_share_network_security_service.
assert_called_once_with(
context, share_network, security_services[1],
current_security_service=security_services[0]))
db_api.share_network_update_security_service.assert_called_once_with(
context, share_network['id'], security_services[0]['id'],
security_services[1]['id'])
@ddt.data(*set(("1.0", "2.25", "2.26", api_version._MAX_API_VERSION)))
def test_action_remove_security_service(self, microversion):
@ -933,11 +1168,12 @@ class ShareNetworkAPITest(test.TestCase):
security_service_id}}
req = fakes.HTTPRequest.blank('/share-networks', version=microversion)
with mock.patch.object(self.controller, '_remove_security_service',
with mock.patch.object(self.controller, 'remove_security_service',
mock.Mock()):
self.controller.action(req, share_network_id, body)
self.controller._remove_security_service.assert_called_once_with(
req, share_network_id, body['remove_security_service'])
self.controller.remove_security_service(
req, share_network_id, body)
self.controller.remove_security_service.assert_called_once_with(
req, share_network_id, body)
@mock.patch.object(db_api, 'share_network_get', mock.Mock())
@mock.patch.object(share_networks.policy, 'check_policy', mock.Mock())
@ -956,7 +1192,7 @@ class ShareNetworkAPITest(test.TestCase):
},
}
self.assertRaises(webob_exc.HTTPForbidden,
self.controller.action,
self.controller.remove_security_service,
self.req,
share_network['id'],
body)
@ -965,23 +1201,17 @@ class ShareNetworkAPITest(test.TestCase):
share_networks.policy.check_policy.assert_called_once_with(
self.req.environ['manila.context'],
share_networks.RESOURCE_NAME,
'remove_security_service')
def test_action_bad_request(self):
share_network_id = 'fake network id'
body = {'bad_action': {}}
self.assertRaises(webob_exc.HTTPBadRequest,
self.controller.action,
self.req,
share_network_id,
body)
'remove_security_service', target_obj=share_network)
@ddt.data('add_security_service', 'remove_security_service')
def test_action_security_service_contains_share_servers(self, action):
share_network = fake_share_network.copy()
security_service = {'id': ' security_service_2',
'type': 'ldap'}
method_to_call = (
self.controller.add_security_service
if action == 'add_security_service'
else self.controller.remove_security_service)
body = {
action: {
'security_service_id': security_service['id']
@ -990,12 +1220,14 @@ class ShareNetworkAPITest(test.TestCase):
self.mock_object(share_networks.policy, 'check_policy')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(db_api, 'security_service_get',
mock.Mock(return_value=security_service))
self.mock_object(
self.controller, '_share_network_subnets_contain_share_servers',
mock.Mock(return_value=True))
self.assertRaises(webob_exc.HTTPForbidden,
self.controller.action,
method_to_call,
self.req,
share_network['id'],
body)
@ -1003,4 +1235,228 @@ class ShareNetworkAPITest(test.TestCase):
self.req.environ['manila.context'], share_network['id'])
share_networks.policy.check_policy.assert_called_once_with(
self.req.environ['manila.context'],
share_networks.RESOURCE_NAME, action)
share_networks.RESOURCE_NAME, action, target_obj=share_network)
def _setup_data_for_check_update_tests(self):
security_services = [
db_utils.create_security_service() for i in range(2)]
share_network = db_utils.create_share_network()
body = {
'update_security_service_check': {
'reset_operation': False,
'current_service_id': security_services[0]['id'],
'new_service_id': security_services[1]['id'],
}
}
request = fakes.HTTPRequest.blank(
'/share-networks', use_admin_context=True, version='2.63')
return security_services, share_network, body, request
def test_check_update_security_service_not_found(self):
security_services, share_network, body, request = (
self._setup_data_for_check_update_tests())
context = request.environ['manila.context']
self.mock_object(share_networks.policy, 'check_policy')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(db_api, 'security_service_get',
mock.Mock(side_effect=exception.NotFound()))
self.assertRaises(
webob_exc.HTTPBadRequest,
self.controller.check_update_security_service,
request,
share_network['id'],
body)
db_api.share_network_get.assert_called_once_with(
context, share_network['id']
)
db_api.security_service_get.assert_called_once_with(
context, security_services[0]['id'])
def test_check_update_security_service(self):
security_services, share_network, body, request = (
self._setup_data_for_check_update_tests())
context = request.environ['manila.context']
share_api_return = {'fake_key': 'fake_value'}
self.mock_object(share_networks.policy, 'check_policy')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(
db_api, 'security_service_get',
mock.Mock(
side_effect=[security_services[0], security_services[1]]))
self.mock_object(
self.controller.share_api,
'check_share_network_security_service_update',
mock.Mock(return_vale=share_api_return))
self.mock_object(
self.controller._view_builder,
'build_security_service_update_check')
self.controller.check_update_security_service(
request, share_network['id'], body)
db_api.share_network_get.assert_called_once_with(
context, share_network['id'])
db_api.security_service_get.assert_has_calls(
[mock.call(context, security_services[0]['id']),
mock.call(context, security_services[1]['id'])])
(self.controller.share_api.check_share_network_security_service_update.
assert_called_once_with(
context, share_network, security_services[1],
current_security_service=security_services[0],
reset_operation=False))
@ddt.data(
(exception.ServiceIsDown(message='fake'), webob_exc.HTTPConflict),
(exception.InvalidShareNetwork(message='fake'),
webob_exc.HTTPBadRequest))
@ddt.unpack
def test_check_update_security_service_share_api_failed(
self, captured_exception, exception_to_be_raised):
security_services, share_network, body, request = (
self._setup_data_for_check_update_tests())
context = request.environ['manila.context']
self.mock_object(share_networks.policy, 'check_policy')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(
db_api, 'security_service_get',
mock.Mock(
side_effect=[security_services[0], security_services[1]]))
self.mock_object(
self.controller.share_api,
'check_share_network_security_service_update',
mock.Mock(side_effect=captured_exception))
self.assertRaises(
exception_to_be_raised,
self.controller.check_update_security_service,
request,
share_network['id'],
body)
db_api.share_network_get.assert_called_once_with(
context, share_network['id'])
db_api.security_service_get.assert_has_calls(
[mock.call(context, security_services[0]['id']),
mock.call(context, security_services[1]['id'])])
(self.controller.share_api.check_share_network_security_service_update.
assert_called_once_with(
context, share_network, security_services[1],
current_security_service=security_services[0],
reset_operation=False))
def _setup_data_for_check_add_tests(self):
security_service = db_utils.create_security_service()
share_network = db_utils.create_share_network()
body = {
'add_security_service_check': {
'reset_operation': False,
'security_service_id': security_service['id'],
}
}
request = fakes.HTTPRequest.blank(
'/share-networks', use_admin_context=True, version='2.63')
return security_service, share_network, body, request
def test_check_add_security_service_not_found(self):
security_service, share_network, body, request = (
self._setup_data_for_check_add_tests())
context = request.environ['manila.context']
self.mock_object(share_networks.policy, 'check_policy')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(db_api, 'security_service_get',
mock.Mock(side_effect=exception.NotFound()))
self.assertRaises(
webob_exc.HTTPBadRequest,
self.controller.check_add_security_service,
request,
share_network['id'],
body)
db_api.share_network_get.assert_called_once_with(
context, share_network['id']
)
db_api.security_service_get.assert_called_once_with(
context, security_service['id'])
def test_check_add_security_service(self):
security_service, share_network, body, request = (
self._setup_data_for_check_add_tests())
context = request.environ['manila.context']
share_api_return = {'fake_key': 'fake_value'}
self.mock_object(share_networks.policy, 'check_policy')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(
db_api, 'security_service_get',
mock.Mock(return_value=security_service))
self.mock_object(
self.controller.share_api,
'check_share_network_security_service_update',
mock.Mock(return_vale=share_api_return))
self.mock_object(
self.controller._view_builder,
'build_security_service_update_check')
self.controller.check_add_security_service(
request, share_network['id'], body)
db_api.share_network_get.assert_called_once_with(
context, share_network['id'])
db_api.security_service_get.assert_called_once_with(
context, security_service['id'])
(self.controller.share_api.check_share_network_security_service_update.
assert_called_once_with(
context, share_network, security_service,
reset_operation=False))
@ddt.data(
(exception.ServiceIsDown(message='fake'), webob_exc.HTTPConflict),
(exception.InvalidShareNetwork(message='fake'),
webob_exc.HTTPBadRequest))
@ddt.unpack
def test_check_add_security_service_share_api_failed(
self, captured_exception, exception_to_be_raised):
security_service, share_network, body, request = (
self._setup_data_for_check_add_tests())
context = request.environ['manila.context']
self.mock_object(share_networks.policy, 'check_policy')
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(
db_api, 'security_service_get',
mock.Mock(return_value=security_service))
self.mock_object(
self.controller.share_api,
'check_share_network_security_service_update',
mock.Mock(side_effect=captured_exception))
self.assertRaises(
exception_to_be_raised,
self.controller.check_add_security_service,
request,
share_network['id'],
body)
db_api.share_network_get.assert_called_once_with(
context, share_network['id'])
db_api.security_service_get.assert_called_once_with(
context, security_service['id'])
(self.controller.share_api.check_share_network_security_service_update.
assert_called_once_with(
context, share_network, security_service,
reset_operation=False))

View File

@ -15,11 +15,13 @@
from unittest import mock
import copy
import ddt
from oslo_config import cfg
from oslo_serialization import jsonutils
from webob import exc
from manila.api import common
from manila.api.openstack import api_version_request as api_version
from manila.api.v2 import share_replicas
from manila.common import constants
@ -56,6 +58,17 @@ class ShareReplicasApiTest(test.TestCase):
experimental=True, use_admin_context=True)
self.admin_context = self.replicas_req_admin.environ['manila.context']
self.mock_policy_check = self.mock_object(policy, 'check_policy')
self.fake_share_network = {
'id': 'fake network id',
'project_id': 'fake project',
'updated_at': None,
'name': 'fake name',
'description': 'fake description',
'security_services': [],
'share_network_subnets': [],
'security_service_update_support': True,
'status': 'active'
}
def _get_context(self, role):
return getattr(self, '%s_context' % role)
@ -370,6 +383,7 @@ class ShareReplicasApiTest(test.TestCase):
mock__view_builder_call = self.mock_object(
share_replicas.replication_view.ReplicationViewBuilder,
'detail_list')
share_network = db_utils.create_share_network()
body = {
'share_replica': {
'share_id': 'FAKE_SHAREID',
@ -381,6 +395,10 @@ class ShareReplicasApiTest(test.TestCase):
mock.Mock(return_value=fake_replica))
self.mock_object(share.API, 'create_share_replica',
mock.Mock(side_effect=exception_type(**exc_args)))
self.mock_object(share_replicas.db, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
self.assertRaises(exc.HTTPBadRequest,
self.controller.create,
@ -388,6 +406,10 @@ class ShareReplicasApiTest(test.TestCase):
self.assertFalse(mock__view_builder_call.called)
self.mock_policy_check.assert_called_once_with(
self.member_context, self.resource_name, 'create')
share_replicas.db.share_network_get.assert_called_once_with(
self.member_context, fake_replica['share_network_id'])
common.check_share_network_is_active.assert_called_once_with(
share_network)
@ddt.data((True, PRE_GRADUATION_VERSION), (False, GRADUATION_VERSION))
@ddt.unpack
@ -395,6 +417,7 @@ class ShareReplicasApiTest(test.TestCase):
fake_replica, expected_replica = self._get_fake_replica(
replication_type='writable', admin=is_admin,
microversion=microversion)
share_network = db_utils.create_share_network()
body = {
'share_replica': {
'share_id': 'FAKE_SHAREID',
@ -408,6 +431,10 @@ class ShareReplicasApiTest(test.TestCase):
self.mock_object(share_replicas.db,
'share_replicas_get_available_active_replica',
mock.Mock(return_value=[{'id': 'active1'}]))
self.mock_object(share_replicas.db, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
req = self._get_request(microversion, is_admin)
req_context = req.environ['manila.context']
@ -417,6 +444,10 @@ class ShareReplicasApiTest(test.TestCase):
self.assertEqual(expected_replica, res_dict['share_replica'])
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'create')
share_replicas.db.share_network_get.assert_called_once_with(
req_context, fake_replica['share_network_id'])
common.check_share_network_is_active.assert_called_once_with(
share_network)
def test_delete_invalid_replica(self):
fake_exception = exception.ShareReplicaNotFound(
@ -492,6 +523,8 @@ class ShareReplicasApiTest(test.TestCase):
replica_state=constants.REPLICA_STATE_ACTIVE)
self.mock_object(share_replicas.db, 'share_replica_get',
mock.Mock(return_value=replica))
self.mock_object(share_replicas.db, 'share_network_get',
mock.Mock(return_value=self.fake_share_network))
mock_api_promote_replica_call = self.mock_object(
share.API, 'promote_share_replica')
@ -509,6 +542,8 @@ class ShareReplicasApiTest(test.TestCase):
exception_type = exception.ReplicationException(reason='xyz')
self.mock_object(share_replicas.db, 'share_replica_get',
mock.Mock(return_value=replica))
self.mock_object(share_replicas.db, 'share_network_get',
mock.Mock(return_value=self.fake_share_network))
mock_api_promote_replica_call = self.mock_object(
share.API, 'promote_share_replica',
mock.Mock(side_effect=exception_type))
@ -522,12 +557,33 @@ class ShareReplicasApiTest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
self.member_context, self.resource_name, 'promote')
def test_promote_share_network_not_active(self):
body = {'promote': None}
replica, expected_replica = self._get_fake_replica(
replica_state=constants.REPLICA_STATE_IN_SYNC)
fake_share_network = copy.deepcopy(self.fake_share_network)
fake_share_network['status'] = constants.STATUS_NETWORK_CHANGE
self.mock_object(share_replicas.db, 'share_replica_get',
mock.Mock(return_value=replica))
self.mock_object(share_replicas.db, 'share_network_get',
mock.Mock(return_value=fake_share_network))
self.assertRaises(exc.HTTPBadRequest,
self.controller.promote,
self.replicas_req,
replica['id'],
body)
self.mock_policy_check.assert_called_once_with(
self.member_context, self.resource_name, 'promote')
def test_promote_admin_required_exception(self):
body = {'promote': None}
replica, expected_replica = self._get_fake_replica(
replica_state=constants.REPLICA_STATE_IN_SYNC)
self.mock_object(share_replicas.db, 'share_replica_get',
mock.Mock(return_value=replica))
self.mock_object(share_replicas.db, 'share_network_get',
mock.Mock(return_value=self.fake_share_network))
mock_api_promote_replica_call = self.mock_object(
share.API, 'promote_share_replica',
mock.Mock(side_effect=exception.AdminRequired))
@ -549,6 +605,8 @@ class ShareReplicasApiTest(test.TestCase):
microversion=microversion)
self.mock_object(share_replicas.db, 'share_replica_get',
mock.Mock(return_value=replica))
self.mock_object(share_replicas.db, 'share_network_get',
mock.Mock(return_value=self.fake_share_network))
mock_api_promote_replica_call = self.mock_object(
share.API, 'promote_share_replica',
mock.Mock(return_value=replica))

View File

@ -18,6 +18,7 @@ from unittest import mock
import ddt
import webob
from manila.api import common
from manila.api.v2 import share_servers
from manila.common import constants
from manila import context as ctx_api
@ -287,11 +288,40 @@ class ShareServerControllerTest(test.TestCase):
return_value=share_network))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet',
mock.Mock(return_value=share_net_subnet))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
self.assertRaises(
exception_to_raise, self.controller.manage, req,
{'share_server': self._setup_manage_test_request_body()})
common.check_share_network_is_active.assert_called_once_with(
share_net_subnet['share_network'])
policy.check_policy.assert_called_once_with(
context, self.resource_name, 'manage_share_server')
def test__validate_manage_share_network_not_active(self):
req = fakes.HTTPRequest.blank('/manage', version="2.49")
context = req.environ['manila.context']
share_network = db_utils.create_share_network()
share_net_subnet = db_utils.create_share_network_subnet(
share_network_id=share_network['id'])
self.mock_object(db_api, 'share_network_get', mock.Mock(
return_value=share_network))
self.mock_object(db_api, 'share_network_subnet_get_default_subnet',
mock.Mock(return_value=share_net_subnet))
self.mock_object(utils, 'validate_service_host')
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(side_effect=webob.exc.HTTPBadRequest()))
self.assertRaises(
webob.exc.HTTPBadRequest, self.controller.manage, req,
{'share_server': self._setup_manage_test_request_body()})
common.check_share_network_is_active.assert_called_once_with(
share_net_subnet['share_network'])
policy.check_policy.assert_called_once_with(
context, self.resource_name, 'manage_share_server')
@ -434,8 +464,15 @@ class ShareServerControllerTest(test.TestCase):
def _setup_unmanage_tests(self, status=constants.STATUS_ACTIVE):
server = db_utils.create_share_server(
id='fake_server_id', status=status)
share_network = db_utils.create_share_network()
network_subnet = db_utils.create_share_network_subnet(
share_network_id=share_network['id'])
self.mock_object(db_api, 'share_server_get',
mock.Mock(return_value=server))
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(db_api, 'share_network_subnet_get',
mock.Mock(return_value=network_subnet))
return server
@ddt.data(exception.ShareServerInUse, exception.PolicyNotAuthorized)
@ -446,6 +483,8 @@ class ShareServerControllerTest(test.TestCase):
error = mock.Mock(side_effect=exc('foobar'))
mock_unmanage = self.mock_object(
share_api.API, 'unmanage_share_server', error)
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
body = {'unmanage': {'force': True}}
self.assertRaises(webob.exc.HTTPBadRequest,
@ -455,9 +494,44 @@ class ShareServerControllerTest(test.TestCase):
body)
mock_unmanage.assert_called_once_with(context, server, force=True)
db_api.share_network_get.assert_called()
common.check_share_network_is_active.assert_called()
policy.check_policy.assert_called_once_with(
context, self.resource_name, 'unmanage_share_server')
def test_unmanage_share_server_network_not_active(self):
"""Tests unmanaging share servers"""
req = fakes.HTTPRequest.blank(
'/v2/share-servers/fake_server_id/', version="2.63")
context = req.environ['manila.context']
share_server = db_utils.create_share_server()
network_subnet = db_utils.create_share_network_subnet()
share_network = db_utils.create_share_network()
get_mock = self.mock_object(
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(
db_api, 'share_network_get',
mock.Mock(return_value=share_network))
is_active_mock = self.mock_object(
common, 'check_share_network_is_active',
mock.Mock(side_effect=webob.exc.HTTPBadRequest()))
body = {'unmanage': {'force': True}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.unmanage,
req,
'fake_server_id',
body)
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(
context, network_subnet['share_network_id'])
is_active_mock.assert_called_once_with(share_network)
def _get_server_migration_request(self, server_id):
req = fakes.HTTPRequest.blank(
'/share-servers/%s/action' % server_id,
@ -478,6 +552,8 @@ class ShareServerControllerTest(test.TestCase):
return_value=share_network))
self.mock_object(db_api, 'share_server_get',
mock.Mock(return_value=server))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
self.mock_object(share_api.API, 'share_server_migration_start')
body = {
@ -499,6 +575,8 @@ class ShareServerControllerTest(test.TestCase):
new_share_network=share_network)
db_api.share_network_get.assert_called_once_with(
context, 'fake_net_id')
common.check_share_network_is_active.assert_called_once_with(
share_network)
@ddt.data({'api_exception': exception.ServiceIsDown(service='fake_srv'),
'expected_exception': webob.exc.HTTPBadRequest},
@ -507,8 +585,12 @@ class ShareServerControllerTest(test.TestCase):
@ddt.unpack
def test_share_server_migration_start_conflict(self, api_exception,
expected_exception):
share_network = db_utils.create_share_network()
share_network_subnet = db_utils.create_share_network_subnet(
share_network_id=share_network['id'])
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'])
req = self._get_server_migration_request(server['id'])
context = req.environ['manila.context']
body = {
@ -523,6 +605,10 @@ class ShareServerControllerTest(test.TestCase):
mock.Mock(side_effect=api_exception))
self.mock_object(db_api, 'share_server_get',
mock.Mock(return_value=server))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.assertRaises(expected_exception,
self.controller.share_server_migration_start,
@ -531,6 +617,10 @@ class ShareServerControllerTest(test.TestCase):
db_api.share_server_get.assert_called_once_with(context,
server['id'])
migration_start_params = body['migration_start']
common.check_share_network_is_active.assert_called_once_with(
share_network)
db_api.share_network_get.assert_called_once_with(
context, share_network['id'])
share_api.API.share_server_migration_start.assert_called_once_with(
context, server, migration_start_params['host'],
migration_start_params['writable'],
@ -960,6 +1050,8 @@ class ShareServerControllerTest(test.TestCase):
mock_network_get = self.mock_object(
db_api, 'share_network_get',
mock.Mock(return_value=fake_share_network))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
mock_migration_check = self.mock_object(
share_api.API, 'share_server_migration_check',
mock.Mock(return_value=driver_result))
@ -974,6 +1066,8 @@ class ShareServerControllerTest(test.TestCase):
context, fake_share_server['id'])
mock_network_get.assert_called_once_with(
context, fake_share_network['id'])
common.check_share_network_is_active.assert_called_once_with(
fake_share_network)
mock_migration_check.assert_called_once_with(
context, fake_share_server, fake_host, requested_writable,
requested_nondisruptive, requested_preserve_snapshots,
@ -1030,7 +1124,7 @@ class ShareServerControllerTest(test.TestCase):
{'api_exception': exception.ServiceIsDown(service='fake_srv'),
'expected_exception': webob.exc.HTTPBadRequest},
{'api_exception': exception.InvalidShareServer(reason=""),
'expected_exception': webob.exc.HTTPConflict})
'expected_exception': webob.exc.HTTPBadRequest})
@ddt.unpack
def test_share_server_migration_complete_exceptions_from_api(
self, api_exception, expected_exception):
@ -1048,21 +1142,16 @@ class ShareServerControllerTest(test.TestCase):
self.mock_object(db_api, 'share_server_get',
mock.Mock(return_value='fake_share_server'))
self.mock_object(share_api.API, 'share_server_migration_check',
self.mock_object(share_api.API, 'share_server_migration_complete',
mock.Mock(side_effect=api_exception))
self.assertRaises(
expected_exception,
self.controller.share_server_migration_check,
self.controller.share_server_migration_complete,
req, 'fake_id', body
)
db_api.share_server_get.assert_called_once_with(context,
'fake_id')
migration_check_params = body['migration_check']
share_api.API.share_server_migration_check.assert_called_once_with(
context, 'fake_share_server', migration_check_params['host'],
migration_check_params['writable'],
migration_check_params['nondisruptive'],
migration_check_params['preserve_snapshots'],
new_share_network=None)
share_api.API.share_server_migration_complete.assert_called_once_with(
context, 'fake_share_server', )

View File

@ -664,6 +664,7 @@ class ShareAPITest(test.TestCase):
"availability_zone": "zone1:host1",
"share_network_id": "fakenetid"
}
fake_network = {'id': 'fakenetid'}
create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'],
display_description=shr['description'],
@ -673,7 +674,9 @@ class ShareAPITest(test.TestCase):
share_network_id=shr['share_network_id']))
self.mock_object(share_api.API, 'create', create_mock)
self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value={'id': 'fakenetid'}))
return_value=fake_network))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id')
@ -687,6 +690,8 @@ class ShareAPITest(test.TestCase):
# pylint: disable=unsubscriptable-object
self.assertEqual("fakenetid",
create_mock.call_args[1]['share_network_id'])
common.check_share_network_is_active.assert_called_once_with(
fake_network)
@ddt.data("2.15", "2.16")
def test_share_create_original_with_user_id(self, microversion):
@ -1268,6 +1273,7 @@ class ShareAPITest(test.TestCase):
"share_network_id": None,
}
parent_share_net = 444
fake_network = {'id': parent_share_net}
create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'],
display_description=shr['description'],
@ -1280,13 +1286,15 @@ class ShareAPITest(test.TestCase):
self.mock_object(share_api.API, 'create', create_mock)
self.mock_object(share_api.API, 'get_snapshot',
stubs.stub_snapshot_get)
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
parent_share = stubs.stub_share(
'1', instance={'share_network_id': parent_share_net},
create_share_from_snapshot_support=True)
self.mock_object(share_api.API, 'get', mock.Mock(
return_value=parent_share))
self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value={'id': parent_share_net}))
return_value=fake_network))
self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id')
@ -1301,6 +1309,8 @@ class ShareAPITest(test.TestCase):
# pylint: disable=unsubscriptable-object
self.assertEqual(parent_share_net,
create_mock.call_args[1]['share_network_id'])
common.check_share_network_is_active.assert_called_once_with(
fake_network)
def test_share_create_from_snapshot_with_share_net_equals_parent(self):
parent_share_net = 444
@ -1332,6 +1342,8 @@ class ShareAPITest(test.TestCase):
return_value=parent_share))
self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value={'id': parent_share_net}))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
self.mock_object(
db, 'share_network_subnet_get_by_availability_zone_id')

View File

@ -62,6 +62,9 @@ class ViewBuilderTestCase(test.TestCase):
<= api_version.APIVersionRequest('2.49'))
subnets_support = (api_version.APIVersionRequest(microversion) >
api_version.APIVersionRequest('2.49'))
status_and_sec_serv_update = (
api_version.APIVersionRequest(microversion) >=
api_version.APIVersionRequest('2.63'))
req = fakes.HTTPRequest.blank('/share-networks', version=microversion)
expected_keys = {
'id', 'name', 'project_id', 'created_at', 'updated_at',
@ -80,6 +83,8 @@ class ViewBuilderTestCase(test.TestCase):
expected_keys.add('mtu')
if nova_net_support:
expected_keys.add('nova_net_id')
if status_and_sec_serv_update:
expected_keys.update({'status', 'security_service_update_support'})
result = self.builder.build_share_network(req, share_network_data)
self.assertEqual(1, len(result))
@ -129,6 +134,9 @@ class ViewBuilderTestCase(test.TestCase):
<= api_version.APIVersionRequest('2.49'))
subnets_support = (api_version.APIVersionRequest(microversion) >
api_version.APIVersionRequest('2.49'))
status_and_sec_serv_update = (
api_version.APIVersionRequest(microversion) >=
api_version.APIVersionRequest('2.63'))
req = fakes.HTTPRequest.blank('/share-networks', version=microversion)
expected_networks_list = []
for share_network in share_networks:
@ -166,6 +174,13 @@ class ViewBuilderTestCase(test.TestCase):
if nova_net_support:
share_network.update({'nova_net_id': 'fake_nova_net_id'})
expected_data.update({'nova_net_id': None})
if status_and_sec_serv_update:
share_network.update(
{'status': 'active',
'security_service_update_support': False})
expected_data.update(
{'status': 'active',
'security_service_update_support': False})
expected_networks_list.append(expected_data)
expected = {'share_networks': expected_networks_list}

View File

@ -2971,3 +2971,76 @@ class ShareServerTaskState(BaseMigrationChecks):
for ss in engine.execute(ss_table.select()):
self.test_case.assertFalse(hasattr(ss, 'task_state'))
self.test_case.assertFalse(hasattr(ss, 'source_share_server_id'))
@map_to_migration('478c445d8d3e')
class AddUpdateSecurityServiceControlFields(BaseMigrationChecks):
def setup_upgrade_data(self, engine):
user_id = 'user_id'
project_id = 'project_id'
# 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))
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(),
'share_network_subnet_id': share_network_subnet_data['id'],
'host': 'fake_host',
'status': 'active',
}
ss_table = utils.load_table('share_servers', engine)
engine.execute(ss_table.insert(share_server_data))
def check_upgrade(self, engine, data):
ss_table = utils.load_table('share_servers', engine)
for ss in engine.execute(ss_table.select()):
self.test_case.assertTrue(
hasattr(ss, 'security_service_update_support'))
self.test_case.assertEqual(
False, ss.security_service_update_support)
sn_table = utils.load_table('share_networks', engine)
for sn in engine.execute(sn_table.select()):
self.test_case.assertTrue(hasattr(sn, 'status'))
self.test_case.assertEqual(constants.STATUS_NETWORK_ACTIVE,
sn.status)
async_op_data = {
'created_at': datetime.datetime(2021, 3, 12, 17, 40, 34),
'updated_at': None,
'deleted_at': None,
'deleted': 0,
'entity_uuid': uuidutils.generate_uuid(),
'key': 't' * 255,
'value': 'v' * 1023,
}
async_op_data_table = utils.load_table('async_operation_data', engine)
engine.execute(async_op_data_table.insert(async_op_data))
def check_downgrade(self, engine):
ss_table = utils.load_table('share_servers', engine)
for ss in engine.execute(ss_table.select()):
self.test_case.assertFalse(
hasattr(ss, 'security_service_update_support'))
sn_table = utils.load_table('share_networks', engine)
for sn in engine.execute(sn_table.select()):
self.test_case.assertFalse(hasattr(sn, 'status'))
self.test_case.assertRaises(
sa_exc.NoSuchTableError,
utils.load_table, 'async_operation_data', engine)

View File

@ -2544,6 +2544,45 @@ class ShareNetworkDatabaseAPITestCase(BaseDatabaseAPITestCase):
self.assertEqual(0, len(result['share_instances']))
def test_association_get(self):
network = db_api.share_network_create(
self.fake_context, self.share_nw_dict)
security_service = db_api.security_service_create(
self.fake_context, security_service_dict)
network_id = network['id']
security_service_id = security_service['id']
db_api.share_network_add_security_service(
self.fake_context, network_id, security_service_id)
result = db_api.share_network_security_service_association_get(
self.fake_context, network_id, security_service_id)
self.assertEqual(result['share_network_id'], network_id)
self.assertEqual(result['security_service_id'], security_service_id)
def test_share_network_update_security_service(self):
new_sec_service = copy.copy(security_service_dict)
new_sec_service['id'] = 'fakeid'
share_network_id = self.share_nw_dict['id']
db_api.share_network_create(
self.fake_context, self.share_nw_dict)
db_api.security_service_create(
self.fake_context, security_service_dict)
db_api.security_service_create(self.fake_context, new_sec_service)
db_api.share_network_add_security_service(
self.fake_context, share_network_id,
security_service_dict['id'])
db_api.share_network_update_security_service(
self.fake_context, share_network_id, security_service_dict['id'],
new_sec_service['id'])
association = db_api.share_network_security_service_association_get(
self.fake_context, share_network_id, new_sec_service['id'])
self.assertEqual(association['share_network_id'], share_network_id)
self.assertEqual(
association['security_service_id'], new_sec_service['id'])
@ddt.ddt
class ShareNetworkSubnetDatabaseAPITestCase(BaseDatabaseAPITestCase):

View File

@ -251,7 +251,7 @@ def create_share_network(**kwargs):
net = {
'user_id': 'fake',
'project_id': 'fake',
'status': 'new',
'status': 'active',
'name': 'whatever',
'description': 'fake description',
}

View File

@ -219,6 +219,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
}, {
'name': 'host2@back1#BBB',
@ -247,6 +248,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
}, {
'name': 'host2@back2#CCC',
@ -275,6 +277,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
},
]
@ -325,6 +328,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
}, {
'name': 'host2@BBB#pool2',
@ -354,6 +358,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
}, {
'name': 'host3@CCC#pool3',
@ -383,6 +388,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
}, {
'name': 'host4@DDD#pool4a',
@ -412,6 +418,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
}, {
'name': 'host4@DDD#pool4b',
@ -441,6 +448,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
},
]
@ -503,6 +511,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
}, {
'name': 'host2@back1#BBB',
@ -531,6 +540,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
},
]
@ -587,6 +597,7 @@ class HostManagerTestCase(test.TestCase):
'replication_type': None,
'replication_domain': None,
'sg_consistent_snapshot_support': None,
'security_service_update_support': False,
},
},
]

View File

@ -142,6 +142,7 @@ class EMCShareFrameworkTestCase(test.TestCase):
data['ipv6_support'] = False
data['max_shares_per_share_server'] = -1
data['max_share_server_size'] = -1
data['security_service_update_support'] = False
self.assertEqual(data, self.driver._stats)
def _fake_safe_get(self, value):

View File

@ -140,6 +140,7 @@ class DummyDriver(driver.ShareDriver):
self.backend_name = self.configuration.safe_get(
"share_backend_name") or "DummyDriver"
self.migration_progress = {}
self.security_service_update_support = True
def _verify_configuration(self):
allowed_driver_methods = [m for m in dir(self) if m[0] != '_']
@ -852,3 +853,34 @@ class DummyDriver(driver.ShareDriver):
'export_locations': self.private_storage.get(share['id'],
key='export_location')
}
@slow_me_down
def update_share_server_security_service(self, context, share_server,
network_info, share_instances,
share_instance_rules,
new_security_service,
current_security_service=None):
if current_security_service:
msg = _("Replacing security service %(cur_sec_serv_id)s by "
"security service %(new_sec_serv_id)s on share server "
"%(server_id)s."
) % {
'cur_sec_serv_id': current_security_service['id'],
'new_sec_serv_id': new_security_service['id'],
'server_id': share_server['id']
}
else:
msg = _("Adding security service %(sec_serv_id)s on share server "
"%(server_id)s."
) % {
'sec_serv_id': new_security_service['id'],
'server_id': share_server['id']
}
LOG.debug(msg)
def check_update_share_server_security_service(
self, context, share_server, network_info, share_instances,
share_instance_rules, new_security_service,
current_security_service=None):
return True

View File

@ -268,6 +268,7 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase):
'goodness_function': None,
'ipv4_support': True,
'ipv6_support': False,
'security_service_update_support': False,
}
self.assertEqual(test_data, self._driver._stats)

View File

@ -748,6 +748,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'ipv6_support': False,
'max_share_server_size': -1,
'max_shares_per_share_server': -1,
'security_service_update_support': False,
}
result = self.driver.get_share_stats(refresh=True)
@ -801,6 +802,8 @@ class HPE3ParDriverTestCase(test.TestCase):
'provisioned_capacity_gb': 0,
'reserved_percentage': 0,
'max_over_subscription_ratio': None,
'max_share_server_size': -1,
'max_shares_per_share_server': -1,
'qos': False,
'thin_provisioning': True,
'pools': [{
@ -816,6 +819,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'snapshot_support': True,
'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False,
'security_service_update_support': False,
'mount_snapshot_support': False,
'share_group_stats': {
'consistent_snapshot_support': None,
@ -825,8 +829,6 @@ class HPE3ParDriverTestCase(test.TestCase):
'goodness_function': None,
'ipv4_support': True,
'ipv6_support': False,
'max_share_server_size': -1,
'max_shares_per_share_server': -1,
}
result = self.driver.get_share_stats(refresh=True)
@ -851,6 +853,8 @@ class HPE3ParDriverTestCase(test.TestCase):
'driver_version': expected_version,
'free_capacity_gb': 0,
'max_over_subscription_ratio': None,
'max_share_server_size': -1,
'max_shares_per_share_server': -1,
'pools': None,
'provisioned_capacity_gb': 0,
'reserved_percentage': 0,
@ -862,6 +866,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'snapshot_support': True,
'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False,
'security_service_update_support': False,
'mount_snapshot_support': False,
'share_group_stats': {
'consistent_snapshot_support': None,
@ -871,8 +876,6 @@ class HPE3ParDriverTestCase(test.TestCase):
'goodness_function': None,
'ipv4_support': True,
'ipv6_support': False,
'max_share_server_size': -1,
'max_shares_per_share_server': -1,
}
result = self.driver.get_share_stats(refresh=True)

View File

@ -2434,6 +2434,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
"share_group_stats": {"consistent_snapshot_support": None},
"ipv4_support": True,
"ipv6_support": False,
"security_service_update_support": False,
}
if replication_support:

View File

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

View File

@ -368,6 +368,7 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
'goodness_function': None,
'ipv4_support': True,
'ipv6_support': False,
'security_service_update_support': False,
}
if replication_domain:
expected['replication_type'] = 'readable'

View File

@ -23,6 +23,7 @@ import ddt
from oslo_config import cfg
from oslo_utils import timeutils
from oslo_utils import uuidutils
from webob import exc as webob_exc
from manila.common import constants
from manila import context
@ -1098,6 +1099,7 @@ class ShareAPITestCase(test.TestCase):
share_server = db_utils.create_share_server(
status=constants.STATUS_ACTIVE, id=share_server_id,
share_network_subnet_id=fake_subnet['id'])
share_network = db_utils.create_share_network(id='fake')
fake_share_data = {
'id': 'fakeid',
'status': constants.STATUS_CREATING,
@ -1130,6 +1132,8 @@ class ShareAPITestCase(test.TestCase):
mock.Mock(return_value=fake_subnet))
self.mock_object(db_api, 'share_instances_get_all',
mock.Mock(return_value=[]))
self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_network))
self.api.manage(self.context, copy.deepcopy(share_data),
driver_options)
@ -5664,6 +5668,262 @@ class ShareAPITestCase(test.TestCase):
self.api.share_rpcapi.migration_get_progress.assert_called_once_with(
self.context, instance1, instance2['id'])
def test__share_network_update_initial_checks_network_not_active(self):
share_network = db_utils.create_share_network(
status=constants.STATUS_NETWORK_CHANGE)
new_sec_service = db_utils.create_security_service(
share_network_id=share_network['id'], type='ldap')
self.assertRaises(
webob_exc.HTTPBadRequest,
self.api._share_network_update_initial_checks,
self.context, share_network, new_sec_service
)
def test__share_network_update_initial_checks_server_not_active(self):
db_utils.create_share_server(
share_network_subnet_id='fakeid', status=constants.STATUS_ERROR,
security_service_update_support=True)
db_utils.create_share_network_subnet(
id='fakeid', share_network_id='fakenetid')
share_network = db_utils.create_share_network(id='fakenetid')
new_sec_service = db_utils.create_security_service(
share_network_id='fakenetid', type='ldap')
self.assertRaises(
exception.InvalidShareNetwork,
self.api._share_network_update_initial_checks,
self.context, share_network, new_sec_service,
)
def test__share_network_update_initial_checks_shares_not_available(self):
db_utils.create_share_server(share_network_subnet_id='fakeid',
security_service_update_support=True)
db_utils.create_share_network_subnet(
id='fakeid', share_network_id='fake_network_id')
share_network = db_utils.create_share_network(
id='fake_network_id')
new_sec_service = db_utils.create_security_service(
share_network_id='fake_network_id', type='ldap')
shares = [db_utils.create_share(status=constants.STATUS_ERROR)]
self.mock_object(utils, 'validate_service_host')
self.mock_object(
self.api, 'get_all', mock.Mock(return_value=shares))
self.assertRaises(
exception.InvalidShareNetwork,
self.api._share_network_update_initial_checks,
self.context, share_network, new_sec_service
)
utils.validate_service_host.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), 'host1')
self.api.get_all.assert_called_once_with(
self.context,
search_opts={'share_network_id': share_network['id']})
def test__share_network_update_initial_checks_rules_in_error(self):
db_utils.create_share_server(share_network_subnet_id='fakeid',
security_service_update_support=True)
db_utils.create_share_network_subnet(
id='fakeid', share_network_id='fake_network_id')
share_network = db_utils.create_share_network(
id='fake_network_id')
new_sec_service = db_utils.create_security_service(
share_network_id='fake_network_id', type='ldap')
shares = [db_utils.create_share(status=constants.STATUS_AVAILABLE)]
shares[0]['instance']['access_rules_status'] = (
constants.ACCESS_STATE_ERROR)
self.mock_object(utils, 'validate_service_host')
self.mock_object(
self.api, 'get_all', mock.Mock(return_value=shares))
self.assertRaises(
exception.InvalidShareNetwork,
self.api._share_network_update_initial_checks,
self.context, share_network, new_sec_service
)
utils.validate_service_host.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), 'host1')
self.api.get_all.assert_called_once_with(
self.context,
search_opts={'share_network_id': share_network['id']})
def test__share_network_update_initial_checks_share_is_busy(self):
db_utils.create_share_server(share_network_subnet_id='fakeid',
security_service_update_support=True)
db_utils.create_share_network_subnet(
id='fakeid', share_network_id='fake_net_id')
share_network = db_utils.create_share_network(id='fake_net_id')
new_sec_service = db_utils.create_security_service(
share_network_id='fake_net_id', type='ldap')
shares = [db_utils.create_share(status=constants.STATUS_AVAILABLE)]
self.mock_object(utils, 'validate_service_host')
self.mock_object(
self.api, 'get_all', mock.Mock(return_value=shares))
self.mock_object(
self.api, '_check_is_share_busy',
mock.Mock(side_effect=exception.ShareBusyException(message='fake'))
)
self.assertRaises(
exception.InvalidShareNetwork,
self.api._share_network_update_initial_checks,
self.context, share_network, new_sec_service
)
utils.validate_service_host.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), 'host1')
self.api.get_all.assert_called_once_with(
self.context,
search_opts={'share_network_id': share_network['id']})
self.api._check_is_share_busy.assert_called_once_with(shares[0])
def test__share_network_update_initial_checks_unsupported_server(self):
db_utils.create_share_server(share_network_subnet_id='fakeid',
security_service_update_support=False)
db_utils.create_share_network_subnet(
id='fakeid', share_network_id='fake_net_id')
share_network = db_utils.create_share_network(id='fake_net_id')
self.assertRaises(
exception.InvalidShareNetwork,
self.api._share_network_update_initial_checks,
self.context, share_network, None
)
def test__share_network_update_initial_checks_update_different_types(self):
db_utils.create_share_server(share_network_subnet_id='fakeid',
security_service_update_support=True)
db_utils.create_share_network_subnet(
id='fakeid', share_network_id='fake_net_id')
share_network = db_utils.create_share_network(id='fake_net_id')
new_sec_service = db_utils.create_security_service(
share_network_id='fake_net_id', type='ldap')
curr_sec_service = db_utils.create_security_service(
share_network_id='fake_net_id', type='kerberos')
self.assertRaises(
exception.InvalidSecurityService,
self.api._share_network_update_initial_checks,
self.context, share_network, new_sec_service,
current_security_service=curr_sec_service
)
def test__share_network_update_initial_checks_add_type_conflict(self):
db_utils.create_share_server(share_network_subnet_id='fakeid',
security_service_update_support=True)
db_utils.create_share_network_subnet(
id='fakeid', share_network_id='fake_net_id')
share_network = db_utils.create_share_network(id='fake_net_id')
db_utils.create_security_service(
share_network_id='fake_net_id', type='ldap')
share_network = db_api.share_network_get(self.context,
share_network['id'])
new_sec_service = db_utils.create_security_service(
share_network_id='fake_net_id', type='ldap')
self.assertRaises(
exception.InvalidSecurityService,
self.api._share_network_update_initial_checks,
self.context, share_network, new_sec_service,
)
def test_update_share_network_security_service_backend_host_failure(self):
share_network = db_utils.create_share_network()
security_service = db_utils.create_security_service()
backend_host = 'fakehost'
mock_initial_checks = self.mock_object(
self.api, '_share_network_update_initial_checks',
mock.Mock(return_value=(['fake_server'], [backend_host])))
mock_get_update_key = self.mock_object(
self.api, 'get_security_service_update_key',
mock.Mock(return_value='fake_key'))
mock_db_async_op = self.mock_object(
db_api, 'async_operation_data_get',
mock.Mock(return_value='fake_update_value'))
mock_validate_host = self.mock_object(
self.api, '_security_service_update_validate_hosts',
mock.Mock(return_value=(False, None)))
self.assertRaises(
exception.InvalidShareNetwork,
self.api.update_share_network_security_service,
self.context, share_network, security_service)
mock_initial_checks.assert_called_once_with(
self.context, share_network, security_service,
current_security_service=None)
mock_db_async_op.assert_called_once_with(
self.context, share_network['id'], 'fake_key')
mock_get_update_key.assert_called_once_with(
'hosts_check', security_service['id'],
current_security_service_id=None)
mock_validate_host.assert_called_once_with(
self.context, share_network, [backend_host], ['fake_server'],
new_security_service_id=security_service['id'],
current_security_service_id=None)
def test_update_share_network_security_service(self):
share_network = db_utils.create_share_network()
security_service = db_utils.create_security_service()
backend_hosts = ['fakehost']
fake_update_key = 'fake_key'
servers = [
db_utils.create_share_server() for i in range(2)]
server_ids = [server['id'] for server in servers]
mock_initial_checks = self.mock_object(
self.api, '_share_network_update_initial_checks',
mock.Mock(return_value=(servers, backend_hosts)))
mock_get_update_key = self.mock_object(
self.api, 'get_security_service_update_key',
mock.Mock(return_value=fake_update_key))
mock_db_async_op = self.mock_object(
db_api, 'async_operation_data_get',
mock.Mock(return_value='fake_update_value'))
mock_validate_host = self.mock_object(
self.api, '_security_service_update_validate_hosts',
mock.Mock(return_value=(True, None)))
mock_network_update = self.mock_object(
db_api, 'share_network_update')
mock_servers_update = self.mock_object(
db_api, 'share_servers_update')
mock_update_security_services = self.mock_object(
self.share_rpcapi, 'update_share_network_security_service')
mock_db_async_op_del = self.mock_object(
db_api, 'async_operation_data_delete',)
self.api.update_share_network_security_service(
self.context, share_network, security_service)
mock_initial_checks.assert_called_once_with(
self.context, share_network, security_service,
current_security_service=None)
mock_db_async_op.assert_called_once_with(
self.context, share_network['id'], fake_update_key)
mock_get_update_key.assert_called_once_with(
'hosts_check', security_service['id'],
current_security_service_id=None)
mock_validate_host.assert_called_once_with(
self.context, share_network, backend_hosts, servers,
new_security_service_id=security_service['id'],
current_security_service_id=None)
mock_network_update.assert_called_once_with(
self.context, share_network['id'],
{'status': constants.STATUS_NETWORK_CHANGE})
mock_servers_update.assert_called_once_with(
self.context, server_ids,
{'status': constants.STATUS_SERVER_NETWORK_CHANGE}
)
mock_update_security_services.assert_called_once_with(
self.context, backend_hosts[0], share_network['id'],
security_service['id'], current_security_service_id=None)
mock_db_async_op_del.assert_called_once_with(
self.context, share_network['id'], fake_update_key)
class OtherTenantsShareActionsTestCase(test.TestCase):
def setUp(self):

View File

@ -945,6 +945,31 @@ class ShareDriverTestCase(test.TestCase):
self.assertIsNone(share_group_update)
self.assertEqual(expected_share_updates, share_update)
def test_update_share_server_security_service(self):
share_driver = self._instantiate_share_driver(None, True)
self.assertRaises(NotImplementedError,
share_driver.update_share_server_security_service,
'fake_context',
{'id', 'share_server_id'},
{'fake', 'fake_net_info'},
[{"id": "fake_instance_id"}],
[{"id": "fake_rule_id"}],
{'id', 'fake_sec_service_id'},
current_security_service=None)
def test_check_update_share_server_security_service(self):
share_driver = self._instantiate_share_driver(None, True)
self.assertRaises(
NotImplementedError,
share_driver.check_update_share_server_security_service,
'fake_context',
{'id', 'share_server_id'},
{'fake', 'fake_net_info'},
[{"id": "fake_instance_id"}],
[{"id": "fake_rule_id"}],
{'id', 'fake_sec_service_id'},
current_security_service=None)
def test_create_share_group_from_sg_snapshot_with_no_members(self):
share_driver = self._instantiate_share_driver(None, False)
fake_share_group_dict = {}

View File

@ -579,6 +579,7 @@ class ShareManagerTestCase(test.TestCase):
'source_share_group_snapshot_member_id'),
'availability_zone': share_instance.get('availability_zone'),
'export_locations': share_instance.get('export_locations') or [],
'share_network_status': share_instance.get('share_network_status')
}
return share_instance_ref
@ -1115,7 +1116,7 @@ class ShareManagerTestCase(test.TestCase):
self.mock_object(db, 'share_instance_access_get',
mock.Mock(return_value=fake_access_rules[0]))
mock_share_replica_access_update = self.mock_object(
self.share_manager, '_update_share_replica_access_rules_state')
self.share_manager, '_update_share_instance_access_rules_state')
driver_call = self.mock_object(
self.share_manager.driver, 'create_replica',
mock.Mock(return_value=replica))
@ -2828,6 +2829,7 @@ class ShareManagerTestCase(test.TestCase):
'host': self.share_manager.host,
'share_network_subnet_id': fake_data['fake_network_subnet']['id'],
'status': constants.STATUS_CREATING,
'security_service_update_support': False,
}
fake_metadata = {
'migration_destination': True,
@ -9034,6 +9036,377 @@ class ShareManagerTestCase(test.TestCase):
self.context, fake_source_share_server, fake_dest_share_server,
fake_share_instances, fake_snapshot_instances)
@ddt.data([constants.STATUS_ERROR, constants.STATUS_ACTIVE],
[constants.STATUS_ACTIVE, constants.STATUS_ACTIVE])
def test__check_share_network_update_finished(self, server_statuses):
share_servers = [
db_utils.create_share_server(status=status)
for status in server_statuses]
share_network = db_utils.create_share_network(
status=constants.STATUS_SERVER_NETWORK_CHANGE)
all_servers_are_active = (
all(server_statuses) == constants.STATUS_ACTIVE)
self.mock_object(db, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(
db, 'share_server_get_all_with_filters',
mock.Mock(return_value=share_servers))
self.mock_object(db, 'share_network_update')
self.share_manager._check_share_network_update_finished(
self.context, share_network['id'])
db.share_server_get_all_with_filters.assert_called_once_with(
self.context, {'share_network_id': share_network['id']})
db.share_network_get.assert_called_once_with(
self.context, share_network['id'])
if all_servers_are_active:
db.share_network_update.assert_called_once_with(
self.context, share_network['id'],
{'status': constants.STATUS_NETWORK_ACTIVE})
def test__check_share_network_update_finished_already_active(self):
share_network = db_utils.create_share_network()
self.mock_object(db, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(db, 'share_server_get_all_with_filters')
self.share_manager._check_share_network_update_finished(
self.context, share_network['id'])
db.share_network_get.assert_called_once_with(
self.context, share_network['id'])
db.share_server_get_all_with_filters.assert_not_called()
def _setup_mocks_for_sec_service_update(
self, service_get_effect, share_network, share_servers, subnet,
network_info, share_instances, fake_rules,
driver_support_update=True, driver_update_action=mock.Mock()):
self.mock_object(
db, 'security_service_get',
mock.Mock(side_effect=service_get_effect))
self.mock_object(
db, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(
db, 'share_server_get_all_by_host',
mock.Mock(return_value=share_servers))
self.mock_object(
db, 'share_network_subnet_get', mock.Mock(return_value=subnet))
self.mock_object(
self.share_manager, '_form_server_setup_info',
mock.Mock(return_value=network_info))
self.mock_object(
db, 'share_instances_get_all_by_share_server',
mock.Mock(return_value=share_instances))
self.mock_object(
db, 'share_access_get_all_for_instance',
mock.Mock(return_value=fake_rules))
self.mock_object(
self.share_manager.driver,
'check_update_share_server_security_service',
mock.Mock(return_value=driver_support_update))
self.mock_object(db, 'share_server_backend_details_set')
self.mock_object(
self.share_manager.driver,
'update_share_server_security_service', driver_update_action)
self.mock_object(db, 'share_server_update')
self.mock_object(
self.share_manager, '_check_share_network_update_finished')
self.mock_object(
self.share_manager.access_helper,
'get_and_update_share_instance_access_rules')
self.mock_object(
self.share_manager.access_helper,
'update_share_instances_access_rules_status')
self.mock_object(
self.share_manager.access_helper, 'process_driver_rule_updates')
@ddt.data(False, True)
def test__update_share_network_security_service(self, is_check_only):
security_services = [
db_utils.create_security_service() for i in range(2)]
share_network = db_utils.create_share_network()
share_network_subnet = db_utils.create_share_network_subnet()
share_servers = [
db_utils.create_share_server(
share_network_subnet_id=share_network_subnet['id'])]
security_services_effect = mock.Mock(side_effect=security_services)
share_network_id = share_network['id']
current_security_service_id = security_services[0]['id']
new_security_service_id = security_services[1]['id']
share_network_subnet_id = share_servers[0]['share_network_subnet_id']
share_instances = [db_utils.create_share()['instance']]
fake_rules = ['fake_rules']
network_info = {'fake': 'fake'}
backend_details_keys = [
'name', 'ou', 'domain', 'server', 'dns_ip', 'user', 'type',
'password']
backend_details_data = {}
[backend_details_data.update(
{key: security_services[0][key]}) for key in backend_details_keys]
backend_details_exp_update = {
'security_service_' + security_services[0]['type']:
jsonutils.dumps(backend_details_data)
}
expected_instance_rules = [{
'share_instance_id': share_instances[0]['id'],
'access_rules': fake_rules
}]
rule_updates = {
share_instances[0]['id']: {
'access_rule_id': {
'access_key': 'fake_access_key',
'state': 'active',
},
},
}
expected_rule_updates_value = rule_updates[share_instances[0]['id']]
driver_return = mock.Mock(return_value=rule_updates)
self._setup_mocks_for_sec_service_update(
security_services_effect, share_network, share_servers,
share_network_subnet, network_info, share_instances, fake_rules,
driver_update_action=driver_return)
result = self.share_manager._update_share_network_security_service(
self.context, share_network_id, new_security_service_id,
current_security_service_id=current_security_service_id,
check_only=is_check_only)
db.security_service_get.assert_has_calls(
[mock.call(self.context, security_services[1]['id']),
mock.call(self.context, security_services[0]['id'])]
)
db.share_network_get.assert_called_once_with(
self.context, share_network_id)
db.share_server_get_all_by_host.assert_called_once_with(
self.context, self.share_manager.host,
filters={'share_network_id': share_network_id})
db.share_network_subnet_get.assert_called_once_with(
self.context, share_network_subnet_id)
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, share_servers[0], share_network, share_network_subnet
)
db.share_instances_get_all_by_share_server.assert_called_once_with(
self.context, share_servers[0]['id'], with_share_data=True)
db.share_access_get_all_for_instance.assert_called_once_with(
self.context, share_instances[0]['id'])
if not is_check_only:
(self.share_manager.driver.update_share_server_security_service.
assert_called_once_with(
self.context, share_servers[0], network_info,
share_instances,
expected_instance_rules,
security_services[0],
current_security_service=security_services[1]))
db.share_server_backend_details_set.assert_called_once_with(
self.context, share_servers[0]['id'],
backend_details_exp_update)
db.share_server_update.assert_called_once_with(
self.context, share_servers[0]['id'],
{'status': constants.STATUS_ACTIVE})
(self.share_manager.access_helper.process_driver_rule_updates.
assert_called_once_with(
self.context, expected_rule_updates_value,
share_instances[0]['id']))
else:
(self.share_manager.driver.
check_update_share_server_security_service.
assert_called_once_with(
self.context, share_servers[0], network_info,
share_instances,
expected_instance_rules,
security_services[0],
current_security_service=security_services[1]))
self.assertEqual(result, True)
def test__update_share_network_security_service_no_support(self):
security_services = [
db_utils.create_security_service() for i in range(2)]
share_network = db_utils.create_share_network()
share_network_subnet = db_utils.create_share_network_subnet()
share_servers = [
db_utils.create_share_server(
share_network_subnet_id=share_network_subnet['id'])]
security_services_effect = mock.Mock(side_effect=security_services)
share_network_id = share_network['id']
current_security_service_id = security_services[0]['id']
new_security_service_id = security_services[1]['id']
share_network_subnet_id = share_servers[0]['share_network_subnet_id']
network_info = {'fake': 'fake'}
share_instances = [db_utils.create_share()['instance']]
fake_rules = ['fake_rules']
expected_instance_rules = [{
'share_instance_id': share_instances[0]['id'],
'access_rules': fake_rules
}]
self._setup_mocks_for_sec_service_update(
security_services_effect, share_network, share_servers,
share_network_subnet, network_info, share_instances, fake_rules,
driver_support_update=False)
result = self.share_manager._update_share_network_security_service(
self.context, share_network_id, new_security_service_id,
current_security_service_id=current_security_service_id,
check_only=True)
db.security_service_get.assert_has_calls(
[mock.call(self.context, security_services[1]['id']),
mock.call(self.context, security_services[0]['id'])]
)
db.share_network_get.assert_called_once_with(
self.context, share_network_id)
db.share_server_get_all_by_host.assert_called_once_with(
self.context, self.share_manager.host,
filters={'share_network_id': share_network_id})
db.share_network_subnet_get.assert_called_once_with(
self.context, share_network_subnet_id)
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, share_servers[0], share_network, share_network_subnet
)
db.share_instances_get_all_by_share_server.assert_called_once_with(
self.context, share_servers[0]['id'], with_share_data=True)
db.share_access_get_all_for_instance.assert_called_once_with(
self.context, share_instances[0]['id'])
(self.share_manager.driver.check_update_share_server_security_service.
assert_called_once_with(
self.context, share_servers[0], network_info,
share_instances,
expected_instance_rules,
security_services[0],
current_security_service=security_services[1]))
self.assertEqual(result, False)
def test__update_share_network_security_service_exception(self):
security_services = [
db_utils.create_security_service() for i in range(2)]
share_network = db_utils.create_share_network()
share_network_subnet = db_utils.create_share_network_subnet()
share_servers = [
db_utils.create_share_server(
share_network_subnet_id=share_network_subnet['id'])]
share_instances = [db_utils.create_share_instance(share_id='fake')]
share_instance_ids = [instance['id'] for instance in share_instances]
security_services_effect = mock.Mock(side_effect=security_services)
share_network_id = share_network['id']
current_security_service_id = security_services[0]['id']
new_security_service_id = security_services[1]['id']
share_network_subnet_id = share_servers[0]['share_network_subnet_id']
network_info = {'fake': 'fake'}
backend_details_keys = [
'name', 'ou', 'domain', 'server', 'dns_ip', 'user', 'type',
'password']
backend_details_data = {}
[backend_details_data.update(
{key: security_services[0][key]}) for key in backend_details_keys]
backend_details_exp_update = {
'security_service_' + security_services[0]['type']:
jsonutils.dumps(backend_details_data)
}
driver_exception = mock.Mock(side_effect=Exception())
share_instances = [db_utils.create_share()['instance']]
fake_rules = ['fake_rules']
expected_instance_rules = [{
'share_instance_id': share_instances[0]['id'],
'access_rules': fake_rules
}]
self._setup_mocks_for_sec_service_update(
security_services_effect, share_network, share_servers,
share_network_subnet, network_info, share_instances, fake_rules,
driver_update_action=driver_exception)
self.mock_object(
self.share_manager.access_helper,
'update_share_instances_access_rules_status')
self.mock_object(
db, 'share_instances_get_all_by_share_server',
mock.Mock(return_value=share_instances))
self.share_manager._update_share_network_security_service(
self.context, share_network_id, new_security_service_id,
current_security_service_id=current_security_service_id)
db.security_service_get.assert_has_calls(
[mock.call(self.context, security_services[1]['id']),
mock.call(self.context, security_services[0]['id'])]
)
db.share_network_get.assert_called_once_with(
self.context, share_network_id)
db.share_server_get_all_by_host.assert_called_once_with(
self.context, self.share_manager.host,
filters={'share_network_id': share_network_id})
db.share_network_subnet_get.assert_called_once_with(
self.context, share_network_subnet_id)
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, share_servers[0], share_network, share_network_subnet
)
(self.share_manager.driver.update_share_server_security_service.
assert_called_once_with(
self.context, share_servers[0], network_info,
share_instances,
expected_instance_rules,
security_services[0],
current_security_service=security_services[1]))
db.share_server_backend_details_set.assert_called_once_with(
self.context, share_servers[0]['id'],
backend_details_exp_update)
db.share_server_update.assert_called_once_with(
self.context, share_servers[0]['id'],
{'status': constants.STATUS_ERROR})
db.share_instances_get_all_by_share_server.assert_called_once_with(
self.context, share_servers[0]['id'], with_share_data=True)
db.share_access_get_all_for_instance.assert_called_once_with(
self.context, share_instances[0]['id'])
(self.share_manager.access_helper.
update_share_instances_access_rules_status(
self.context, constants.SHARE_INSTANCE_RULES_ERROR,
share_instance_ids))
(self.share_manager.access_helper.
get_and_update_share_instance_access_rules(
self.context, updates={'state': constants.STATUS_ERROR},
share_instance_id=share_instances[0]['id']))
def test_update_share_network_security_service(self):
share_network_id = 'fake_sn_id'
new_security_service_id = 'new_sec_service_id'
current_security_service_id = 'current_sec_service_id'
self.mock_object(
self.share_manager, '_update_share_network_security_service')
self.share_manager.update_share_network_security_service(
self.context, share_network_id, new_security_service_id,
current_security_service_id=current_security_service_id)
(self.share_manager._update_share_network_security_service.
assert_called_once_with(
self.context, share_network_id, new_security_service_id,
current_security_service_id=current_security_service_id,
check_only=False))
def test_check_update_share_network_security_service(self):
share_network_id = 'fake_sn_id'
new_security_service_id = 'new_sec_service_id'
current_security_service_id = 'current_sec_service_id'
self.mock_object(
self.share_manager, '_update_share_network_security_service')
self.share_manager.check_update_share_network_security_service(
self.context, share_network_id, new_security_service_id,
current_security_service_id=current_security_service_id)
(self.share_manager._update_share_network_security_service.
assert_called_once_with(
self.context, share_network_id, new_security_service_id,
current_security_service_id=current_security_service_id,
check_only=True))
@ddt.ddt
class HookWrapperTestCase(test.TestCase):

View File

@ -457,3 +457,22 @@ class ShareRpcAPITestCase(test.TestCase):
dest_host=self.fake_host,
share_instance_ids=[self.fake_share['instance']['id']],
share_server_id=self.fake_share_server['id'])
def test_update_share_network_security_service(self):
self._test_share_api(
'update_share_network_security_service',
rpc_method='cast',
version='1.22',
dest_host=self.fake_host,
share_network_id='fake_net_id',
new_security_service_id='fake_sec_service_id',
current_security_service_id='fake_sec_service_id')
def test_check_update_share_network_security_service(self):
self._test_share_api('check_update_share_network_security_service',
rpc_method='cast',
version='1.22',
dest_host=self.fake_host,
share_network_id='fake_net_id',
new_security_service_id='fake_sec_service_id',
current_security_service_id='fake_sec_service_id')

View File

@ -21,6 +21,7 @@ from unittest import mock
import ddt
from oslo_config import cfg
from oslo_utils import timeutils
from webob import exc as webob_exc
from manila.common import constants
from manila import context
@ -460,6 +461,10 @@ class ShareGroupsAPITestCase(test.TestCase):
host='fake_original_host',
share_network_id='fake_network_id',
share_server_id='fake_server_id')
share_network = {
'id': 'fakeid',
'status': constants.STATUS_NETWORK_ACTIVE
}
expected_values = share_group.copy()
for name in ('id', 'created_at', 'share_network_id',
'share_server_id'):
@ -484,7 +489,8 @@ class ShareGroupsAPITestCase(test.TestCase):
self.mock_object(
share_types, 'get_share_type',
mock.Mock(return_value={"id": self.fake_share_type['id']}))
self.mock_object(db_driver, 'share_network_get')
self.mock_object(db_driver, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(
db_driver, 'share_group_snapshot_members_get_all',
mock.Mock(return_value=[]))
@ -502,6 +508,44 @@ class ShareGroupsAPITestCase(test.TestCase):
self.context, share_group_api.QUOTAS.reserve.return_value)
share_group_api.QUOTAS.rollback.assert_not_called()
def test_create_share_group_network_not_active(self):
fake_share_type_mapping = {'share_type_id': self.fake_share_type['id']}
share_group = fake_share_group(
'fakeid', user_id=self.context.user_id,
project_id=self.context.project_id,
share_types=[fake_share_type_mapping],
status=constants.STATUS_CREATING,
host='fake_original_host',
share_network_id='fake_network_id',
share_server_id='fake_server_id')
network_id = 'fake_sn'
share_network = {
'id': network_id,
'status': constants.STATUS_SERVER_NETWORK_CHANGE
}
expected_values = share_group.copy()
for name in ('id', 'created_at', 'share_network_id',
'share_server_id'):
expected_values.pop(name, None)
expected_values['share_types'] = [self.fake_share_type['id']]
expected_values['share_network_id'] = 'fake_network_id'
expected_values['share_server_id'] = 'fake_server_id'
self.mock_object(
share_types, 'get_share_type',
mock.Mock(return_value={"id": self.fake_share_type['id']}))
self.mock_object(db_driver, 'share_network_get',
mock.Mock(return_value=share_network))
self.assertRaises(
webob_exc.HTTPBadRequest,
self.api.create,
self.context, share_type_ids=[fake_share_type_mapping],
share_network_id="fake_sn")
db_driver.share_network_get.assert_called_once_with(
self.context, network_id)
def test_create_with_source_share_group_snapshot_id_with_member(self):
snap = fake_share_group_snapshot(
"fake_source_share_group_snapshot_id",
@ -524,6 +568,10 @@ class ShareGroupsAPITestCase(test.TestCase):
share_network_id='fake_network_id',
share_server_id='fake_server_id')
expected_values = share_group.copy()
share_network = {
'id': 'fakeid',
'status': constants.STATUS_NETWORK_ACTIVE
}
for name in ('id', 'created_at', 'fake_network_id',
'fake_share_server_id'):
expected_values.pop(name, None)
@ -547,7 +595,8 @@ class ShareGroupsAPITestCase(test.TestCase):
self.mock_object(
share_types, 'get_share_type',
mock.Mock(return_value={"id": self.fake_share_type['id']}))
self.mock_object(db_driver, 'share_network_get')
self.mock_object(db_driver, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(
db_driver, 'share_instance_get', mock.Mock(return_value=share))
self.mock_object(
@ -593,6 +642,10 @@ class ShareGroupsAPITestCase(test.TestCase):
status=constants.STATUS_CREATING,
share_network_id='fake_network_id',
share_server_id='fake_server_id')
share_network = {
'id': 'fakeid',
'status': constants.STATUS_NETWORK_ACTIVE
}
expected_values = share_group.copy()
for name in ('id', 'created_at', 'share_network_id',
'share_server_id'):
@ -606,7 +659,8 @@ class ShareGroupsAPITestCase(test.TestCase):
mock.Mock(return_value=snap))
self.mock_object(db_driver, 'share_group_get',
mock.Mock(return_value=orig_share_group))
self.mock_object(db_driver, 'share_network_get')
self.mock_object(db_driver, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(db_driver, 'share_instance_get',
mock.Mock(return_value=share))
self.mock_object(db_driver, 'share_group_create',

View File

@ -0,0 +1,20 @@
---
features:
- |
Added the possibility to add and update an entire security service when
a share network is already being used.
A new field called ``status`` was added to the share network model and its
default value is ``active``. Some operations might be blocked depending on
the share network status.
A boolean field called ``security_service_update_support`` was added to the
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 scheduler will filter out backend that does not handle this request
during some operations.
upgrade:
- |
``manila-manage`` now supports share server commands, which allow
administrators to modify the field value of some share server's
capabilities.