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:
parent
0a2ae6ff51
commit
2bc27c5678
@ -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."""
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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'.
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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())
|
||||
|
@ -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:
|
||||
|
@ -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(
|
||||
|
@ -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']:
|
||||
|
@ -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:
|
||||
|
@ -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')
|
||||
|
@ -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']
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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
|
@ -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()})
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.")
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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']})
|
||||
|
@ -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()
|
||||
|
@ -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.')
|
||||
|
@ -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)
|
||||
|
@ -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 "
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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))
|
||||
|
@ -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', )
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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',
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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):
|
||||
|
@ -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 = {}
|
||||
|
@ -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):
|
||||
|
@ -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')
|
||||
|
@ -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',
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user