API for ensure shares
Introduces a new API to allow OpenStack administrators to start the share manager's ensure shares operation. Also, introduced a new configuration option named `update_shares_status_on_ensure`, so administrators can define whether the shares status should be updated during the ensure shares operation or not. A new column was added to the services table, named `ensuring`. Through this field, we can identify whether there is an undergoing ensure shares operation or not. Closes-Bug: #1996793 Partially-Implements: bp ensure-shares-api Change-Id: If7bf059eb8581f20a3ceb7c1af93558774f4ef5e
This commit is contained in:
parent
4bf505404a
commit
101becf9b4
@ -203,13 +203,14 @@ REST_API_VERSION_HISTORY = """
|
|||||||
* 2.83 - Added 'disabled_reason' field to services.
|
* 2.83 - Added 'disabled_reason' field to services.
|
||||||
* 2.84 - Added mount_point_name to shares.
|
* 2.84 - Added mount_point_name to shares.
|
||||||
* 2.85 - Added backup_type field to share backups.
|
* 2.85 - Added backup_type field to share backups.
|
||||||
|
* 2.86 - Add ensure share API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The minimum and maximum versions of the API supported
|
# The minimum and maximum versions of the API supported
|
||||||
# The default api version request is defined to be the
|
# The default api version request is defined to be the
|
||||||
# minimum version of the API supported.
|
# minimum version of the API supported.
|
||||||
_MIN_API_VERSION = "2.0"
|
_MIN_API_VERSION = "2.0"
|
||||||
_MAX_API_VERSION = "2.85"
|
_MAX_API_VERSION = "2.86"
|
||||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
@ -458,3 +458,7 @@ user documentation.
|
|||||||
2.85
|
2.85
|
||||||
----
|
----
|
||||||
Added ``backup_type`` field to share backup object.
|
Added ``backup_type`` field to share backup object.
|
||||||
|
|
||||||
|
2.86
|
||||||
|
----
|
||||||
|
Added ensure shares API.
|
||||||
|
@ -102,6 +102,13 @@ class APIRouter(manila.api.openstack.APIRouter):
|
|||||||
mapper.resource("service",
|
mapper.resource("service",
|
||||||
"services",
|
"services",
|
||||||
controller=self.resources["services"])
|
controller=self.resources["services"])
|
||||||
|
for path_prefix in ['/{project_id}', '']:
|
||||||
|
# project_id is optional
|
||||||
|
mapper.connect("services",
|
||||||
|
"%s/services/ensure-shares" % path_prefix,
|
||||||
|
controller=self.resources["services"],
|
||||||
|
action="ensure_shares",
|
||||||
|
conditions={"method": ["POST"]})
|
||||||
|
|
||||||
self.resources["quota_sets_legacy"] = (
|
self.resources["quota_sets_legacy"] = (
|
||||||
quota_sets.create_resource_legacy())
|
quota_sets.create_resource_legacy())
|
||||||
|
@ -14,13 +14,17 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import http.client as http_client
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
from manila.api.openstack import wsgi
|
from manila.api.openstack import wsgi
|
||||||
from manila.api.views import services as services_views
|
from manila.api.views import services as services_views
|
||||||
from manila import db
|
from manila import db
|
||||||
|
from manila import exception
|
||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
|
from manila import policy
|
||||||
|
from manila.services import api as service_api
|
||||||
|
|
||||||
|
|
||||||
class ServiceMixin(object):
|
class ServiceMixin(object):
|
||||||
@ -34,7 +38,7 @@ class ServiceMixin(object):
|
|||||||
_view_builder_class = services_views.ViewBuilder
|
_view_builder_class = services_views.ViewBuilder
|
||||||
|
|
||||||
@wsgi.Controller.authorize("index")
|
@wsgi.Controller.authorize("index")
|
||||||
def _index(self, req):
|
def _index(self, req, support_ensure_shares=False):
|
||||||
"""Return a list of all running services."""
|
"""Return a list of all running services."""
|
||||||
|
|
||||||
context = req.environ['manila.context']
|
context = req.environ['manila.context']
|
||||||
@ -42,7 +46,7 @@ class ServiceMixin(object):
|
|||||||
|
|
||||||
services = []
|
services = []
|
||||||
for service in all_services:
|
for service in all_services:
|
||||||
service = {
|
service_data = {
|
||||||
'id': service['id'],
|
'id': service['id'],
|
||||||
'binary': service['binary'],
|
'binary': service['binary'],
|
||||||
'host': service['host'],
|
'host': service['host'],
|
||||||
@ -52,7 +56,9 @@ class ServiceMixin(object):
|
|||||||
'state': service['state'],
|
'state': service['state'],
|
||||||
'updated_at': service['updated_at'],
|
'updated_at': service['updated_at'],
|
||||||
}
|
}
|
||||||
services.append(service)
|
if support_ensure_shares:
|
||||||
|
service_data['ensuring'] = service['ensuring']
|
||||||
|
services.append(service_data)
|
||||||
|
|
||||||
search_opts = [
|
search_opts = [
|
||||||
'host',
|
'host',
|
||||||
@ -141,10 +147,18 @@ class ServiceController(ServiceMixin, wsgi.Controller):
|
|||||||
Registered under API URL 'services'.
|
Registered under API URL 'services'.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wsgi.Controller.api_version('2.7')
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.service_api = service_api.API()
|
||||||
|
|
||||||
|
@wsgi.Controller.api_version('2.7', '2.85')
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
return self._index(req)
|
return self._index(req)
|
||||||
|
|
||||||
|
@wsgi.Controller.api_version('2.86') # noqa
|
||||||
|
def index(self, req): # pylint: disable=function-redefined # noqa F811
|
||||||
|
return self._index(req, support_ensure_shares=True)
|
||||||
|
|
||||||
@wsgi.Controller.api_version('2.7', '2.82')
|
@wsgi.Controller.api_version('2.7', '2.82')
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
return self._update(req, id, body, support_disabled_reason=False)
|
return self._update(req, id, body, support_disabled_reason=False)
|
||||||
@ -153,6 +167,32 @@ class ServiceController(ServiceMixin, wsgi.Controller):
|
|||||||
def update(self, req, id, body): # pylint: disable=function-redefined # noqa F811
|
def update(self, req, id, body): # pylint: disable=function-redefined # noqa F811
|
||||||
return self._update(req, id, body)
|
return self._update(req, id, body)
|
||||||
|
|
||||||
|
@wsgi.Controller.api_version('2.86')
|
||||||
|
@wsgi.Controller.authorize
|
||||||
|
def ensure_shares(self, req, body):
|
||||||
|
"""Starts ensure shares for a given manila-share binary."""
|
||||||
|
context = req.environ['manila.context']
|
||||||
|
|
||||||
|
policy.check_policy(context, 'service', 'ensure_shares')
|
||||||
|
host = body.get('host', None)
|
||||||
|
if not host:
|
||||||
|
raise webob.exc.HTTPBadRequest('Missing host parameter.')
|
||||||
|
|
||||||
|
try:
|
||||||
|
# The only binary supported is Manila share.
|
||||||
|
service = db.service_get_by_args(context, host, 'manila-share')
|
||||||
|
except exception.NotFound:
|
||||||
|
raise webob.exc.HTTPNotFound(
|
||||||
|
"manila-share binary for '%s' host not found" % id
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.service_api.ensure_shares(context, service, host)
|
||||||
|
except webob.exc.HTTPConflict:
|
||||||
|
raise
|
||||||
|
|
||||||
|
return webob.Response(status_int=http_client.ACCEPTED)
|
||||||
|
|
||||||
|
|
||||||
def create_resource_legacy():
|
def create_resource_legacy():
|
||||||
return wsgi.Resource(ServiceControllerLegacy())
|
return wsgi.Resource(ServiceControllerLegacy())
|
||||||
|
@ -21,6 +21,7 @@ class ViewBuilder(common.ViewBuilder):
|
|||||||
_collection_name = "services"
|
_collection_name = "services"
|
||||||
_detail_version_modifiers = [
|
_detail_version_modifiers = [
|
||||||
"add_disabled_reason_field",
|
"add_disabled_reason_field",
|
||||||
|
"add_ensuring_field",
|
||||||
]
|
]
|
||||||
|
|
||||||
def summary(self, request, service):
|
def summary(self, request, service):
|
||||||
@ -49,3 +50,7 @@ class ViewBuilder(common.ViewBuilder):
|
|||||||
service_dict.pop('disabled', None)
|
service_dict.pop('disabled', None)
|
||||||
service_dict['status'] = service.get('status')
|
service_dict['status'] = service.get('status')
|
||||||
service_dict['disabled_reason'] = service.get('disabled_reason')
|
service_dict['disabled_reason'] = service.get('disabled_reason')
|
||||||
|
|
||||||
|
@common.ViewBuilder.versioned_method("2.86")
|
||||||
|
def add_ensuring_field(self, context, service_dict, service):
|
||||||
|
service_dict['ensuring'] = service.get('ensuring')
|
||||||
|
@ -147,6 +147,11 @@ global_opts = [
|
|||||||
'(element of the list is <driver_updatable_key>, '
|
'(element of the list is <driver_updatable_key>, '
|
||||||
'i.e max_files) can be passed to share drivers as part '
|
'i.e max_files) can be passed to share drivers as part '
|
||||||
'of metadata create/update operations.'),
|
'of metadata create/update operations.'),
|
||||||
|
cfg.BoolOpt('update_shares_status_on_ensure',
|
||||||
|
default=True,
|
||||||
|
help='Whether Manila should update the status of all shares '
|
||||||
|
'within a backend during ongoing ensure_shares '
|
||||||
|
'run.'),
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF.register_opts(global_opts)
|
CONF.register_opts(global_opts)
|
||||||
|
@ -53,6 +53,7 @@ STATUS_AWAITING_TRANSFER = 'awaiting_transfer'
|
|||||||
STATUS_BACKUP_CREATING = 'backup_creating'
|
STATUS_BACKUP_CREATING = 'backup_creating'
|
||||||
STATUS_BACKUP_RESTORING = 'backup_restoring'
|
STATUS_BACKUP_RESTORING = 'backup_restoring'
|
||||||
STATUS_BACKUP_RESTORING_ERROR = 'backup_restoring_error'
|
STATUS_BACKUP_RESTORING_ERROR = 'backup_restoring_error'
|
||||||
|
STATUS_ENSURING = 'ensuring'
|
||||||
|
|
||||||
# Transfer resource type
|
# Transfer resource type
|
||||||
SHARE_RESOURCE_TYPE = 'share'
|
SHARE_RESOURCE_TYPE = 'share'
|
||||||
@ -144,6 +145,7 @@ TRANSITIONAL_STATUSES = (
|
|||||||
STATUS_RESTORING, STATUS_REVERTING,
|
STATUS_RESTORING, STATUS_REVERTING,
|
||||||
STATUS_SERVER_MIGRATING, STATUS_SERVER_MIGRATING_TO,
|
STATUS_SERVER_MIGRATING, STATUS_SERVER_MIGRATING_TO,
|
||||||
STATUS_BACKUP_RESTORING, STATUS_BACKUP_CREATING,
|
STATUS_BACKUP_RESTORING, STATUS_BACKUP_CREATING,
|
||||||
|
STATUS_ENSURING,
|
||||||
)
|
)
|
||||||
|
|
||||||
INVALID_SHARE_INSTANCE_STATUSES_FOR_ACCESS_RULE_UPDATES = (
|
INVALID_SHARE_INSTANCE_STATUSES_FOR_ACCESS_RULE_UPDATES = (
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
# 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-ensuring-field-to-services
|
||||||
|
|
||||||
|
Revision ID: cdefa6287df8
|
||||||
|
Revises: 2f27d904214c
|
||||||
|
Create Date: 2024-07-15 14:29:16.733696
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'cdefa6287df8'
|
||||||
|
down_revision = '2f27d904214c'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
from oslo_log import log
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
try:
|
||||||
|
op.add_column('services', sa.Column(
|
||||||
|
'ensuring', sa.Boolean,
|
||||||
|
nullable=False, server_default=sa.sql.false()))
|
||||||
|
except Exception:
|
||||||
|
LOG.error("Column services.ensuring not created!")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
try:
|
||||||
|
op.drop_column('services', 'ensuring')
|
||||||
|
except Exception:
|
||||||
|
LOG.error("Column shares.ensuring not dropped!")
|
||||||
|
raise
|
@ -72,6 +72,7 @@ class Service(BASE, ManilaBase):
|
|||||||
availability_zone_id = Column(String(36),
|
availability_zone_id = Column(String(36),
|
||||||
ForeignKey('availability_zones.id'),
|
ForeignKey('availability_zones.id'),
|
||||||
nullable=True)
|
nullable=True)
|
||||||
|
ensuring = Column(Boolean, default=False)
|
||||||
|
|
||||||
availability_zone = orm.relationship(
|
availability_zone = orm.relationship(
|
||||||
"AvailabilityZone",
|
"AvailabilityZone",
|
||||||
|
@ -34,6 +34,12 @@ deprecated_service_update = policy.DeprecatedRule(
|
|||||||
deprecated_reason=DEPRECATED_REASON,
|
deprecated_reason=DEPRECATED_REASON,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY
|
deprecated_since=versionutils.deprecated.WALLABY
|
||||||
)
|
)
|
||||||
|
deprecated_service_ensure = policy.DeprecatedRule(
|
||||||
|
name=BASE_POLICY_NAME % 'ensure_shares',
|
||||||
|
check_str=base.RULE_ADMIN_API,
|
||||||
|
deprecated_reason=DEPRECATED_REASON,
|
||||||
|
deprecated_since='2024.2/Dalmatian'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
service_policies = [
|
service_policies = [
|
||||||
@ -79,6 +85,19 @@ service_policies = [
|
|||||||
],
|
],
|
||||||
deprecated_rule=deprecated_service_update
|
deprecated_rule=deprecated_service_update
|
||||||
),
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name=BASE_POLICY_NAME % 'ensure_shares',
|
||||||
|
check_str=base.ADMIN,
|
||||||
|
scope_types=['project'],
|
||||||
|
description="Run ensure shares for a manila-share binary.",
|
||||||
|
operations=[
|
||||||
|
{
|
||||||
|
'method': 'POST',
|
||||||
|
'path': '/services/ensure',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
deprecated_rule=deprecated_service_ensure
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
0
manila/services/__init__.py
Normal file
0
manila/services/__init__.py
Normal file
38
manila/services/api.py
Normal file
38
manila/services/api.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
|
from manila.db import base
|
||||||
|
from manila.share import rpcapi as share_rpcapi
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class API(base.Base):
|
||||||
|
"""API for handling service actions."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(API, self).__init__()
|
||||||
|
self.share_rpcapi = share_rpcapi.ShareAPI()
|
||||||
|
|
||||||
|
def ensure_shares(self, context, service, host):
|
||||||
|
"""Start the ensure shares in a given host."""
|
||||||
|
|
||||||
|
if service['state'] != "up":
|
||||||
|
raise exc.HTTPConflict(
|
||||||
|
"The service must have its state set to 'up' prior to running "
|
||||||
|
"ensure shares.")
|
||||||
|
|
||||||
|
self.share_rpcapi.ensure_driver_resources(context, host)
|
@ -264,7 +264,7 @@ def add_hooks(f):
|
|||||||
class ShareManager(manager.SchedulerDependentManager):
|
class ShareManager(manager.SchedulerDependentManager):
|
||||||
"""Manages NAS storages."""
|
"""Manages NAS storages."""
|
||||||
|
|
||||||
RPC_API_VERSION = '1.28'
|
RPC_API_VERSION = '1.29'
|
||||||
|
|
||||||
def __init__(self, share_driver=None, service_name=None, *args, **kwargs):
|
def __init__(self, share_driver=None, service_name=None, *args, **kwargs):
|
||||||
"""Load the driver from args, or from flags."""
|
"""Load the driver from args, or from flags."""
|
||||||
@ -401,7 +401,8 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
"""
|
"""
|
||||||
return self.driver.initialized
|
return self.driver.initialized
|
||||||
|
|
||||||
def ensure_driver_resources(self, ctxt):
|
def ensure_driver_resources(self, ctxt, skip_backend_info_check=False):
|
||||||
|
update_instances_status = CONF.update_shares_status_on_ensure
|
||||||
old_backend_info = self.db.backend_info_get(ctxt, self.host)
|
old_backend_info = self.db.backend_info_get(ctxt, self.host)
|
||||||
old_backend_info_hash = (old_backend_info.get('info_hash')
|
old_backend_info_hash = (old_backend_info.get('info_hash')
|
||||||
if old_backend_info is not None else None)
|
if old_backend_info is not None else None)
|
||||||
@ -409,31 +410,33 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
new_backend_info_hash = None
|
new_backend_info_hash = None
|
||||||
backend_info_implemented = True
|
backend_info_implemented = True
|
||||||
update_share_instances = []
|
update_share_instances = []
|
||||||
try:
|
if not skip_backend_info_check:
|
||||||
new_backend_info = self.driver.get_backend_info(ctxt)
|
try:
|
||||||
except Exception as e:
|
new_backend_info = self.driver.get_backend_info(ctxt)
|
||||||
if not isinstance(e, NotImplementedError):
|
except Exception as e:
|
||||||
LOG.exception(
|
if not isinstance(e, NotImplementedError):
|
||||||
"The backend %(host)s could not get backend info.",
|
LOG.exception(
|
||||||
{'host': self.host})
|
"The backend %(host)s could not get backend info.",
|
||||||
raise
|
{'host': self.host})
|
||||||
else:
|
raise
|
||||||
backend_info_implemented = False
|
else:
|
||||||
LOG.debug(
|
backend_info_implemented = False
|
||||||
("The backend %(host)s does not support get backend"
|
LOG.debug(
|
||||||
" info method."),
|
("The backend %(host)s does not support get backend"
|
||||||
{'host': self.host})
|
" info method."),
|
||||||
|
{'host': self.host})
|
||||||
|
|
||||||
if new_backend_info:
|
if new_backend_info:
|
||||||
new_backend_info_hash = hashlib.sha1(str(
|
new_backend_info_hash = hashlib.sha1(
|
||||||
sorted(new_backend_info.items())).encode('utf-8')).hexdigest()
|
str(sorted(new_backend_info.items())).encode(
|
||||||
if (old_backend_info_hash == new_backend_info_hash and
|
'utf-8')).hexdigest()
|
||||||
backend_info_implemented):
|
if ((old_backend_info_hash == new_backend_info_hash and
|
||||||
LOG.debug(
|
backend_info_implemented) and not skip_backend_info_check):
|
||||||
("Ensure shares is being skipped because the %(host)s's old "
|
LOG.debug(
|
||||||
"backend info is the same as its new backend info."),
|
("Ensure shares is being skipped because the %(host)s's "
|
||||||
{'host': self.host})
|
"old backend info is the same as its new backend info."),
|
||||||
return
|
{'host': self.host})
|
||||||
|
return
|
||||||
|
|
||||||
share_instances = self.db.share_instance_get_all_by_host(
|
share_instances = self.db.share_instance_get_all_by_host(
|
||||||
ctxt, self.host)
|
ctxt, self.host)
|
||||||
@ -467,7 +470,19 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
ctxt, share_instance)
|
ctxt, share_instance)
|
||||||
update_share_instances.append(share_instance_dict)
|
update_share_instances.append(share_instance_dict)
|
||||||
|
|
||||||
|
do_service_status_update = False
|
||||||
if update_share_instances:
|
if update_share_instances:
|
||||||
|
# No reason to update the shares status if nothing will be done.
|
||||||
|
do_service_status_update = True
|
||||||
|
service = self.db.service_get_by_args(
|
||||||
|
ctxt, self.host, 'manila-share')
|
||||||
|
self.db.service_update(ctxt, service['id'], {'ensuring': True})
|
||||||
|
if update_instances_status:
|
||||||
|
for instance in update_share_instances:
|
||||||
|
self.db.share_instance_update(
|
||||||
|
ctxt, instance['id'],
|
||||||
|
{'status': constants.STATUS_ENSURING}
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
update_share_instances = self.driver.ensure_shares(
|
update_share_instances = self.driver.ensure_shares(
|
||||||
ctxt, update_share_instances) or {}
|
ctxt, update_share_instances) or {}
|
||||||
@ -494,10 +509,11 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
share_instance_update_dict = (
|
share_instance_update_dict = (
|
||||||
update_share_instances[share_instance['id']]
|
update_share_instances[share_instance['id']]
|
||||||
)
|
)
|
||||||
if share_instance_update_dict.get('status'):
|
backend_provided_status = share_instance_update_dict.get('status')
|
||||||
|
if backend_provided_status:
|
||||||
self.db.share_instance_update(
|
self.db.share_instance_update(
|
||||||
ctxt, share_instance['id'],
|
ctxt, share_instance['id'],
|
||||||
{'status': share_instance_update_dict.get('status'),
|
{'status': backend_provided_status,
|
||||||
'host': share_instance['host']}
|
'host': share_instance['host']}
|
||||||
)
|
)
|
||||||
metadata_updates = share_instance_update_dict.get('metadata')
|
metadata_updates = share_instance_update_dict.get('metadata')
|
||||||
@ -568,6 +584,13 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
"Unexpected error occurred while updating "
|
"Unexpected error occurred while updating "
|
||||||
"access rules for snapshot instance %s.",
|
"access rules for snapshot instance %s.",
|
||||||
snap_instance['id'])
|
snap_instance['id'])
|
||||||
|
if not backend_provided_status and update_instances_status:
|
||||||
|
self.db.share_instance_update(
|
||||||
|
ctxt, share_instance['id'],
|
||||||
|
{'status': constants.STATUS_AVAILABLE}
|
||||||
|
)
|
||||||
|
if do_service_status_update:
|
||||||
|
self.db.service_update(ctxt, service['id'], {'ensuring': False})
|
||||||
|
|
||||||
def _ensure_share(self, ctxt, share_instance):
|
def _ensure_share(self, ctxt, share_instance):
|
||||||
export_locations = None
|
export_locations = None
|
||||||
|
@ -89,6 +89,7 @@ class ShareAPI(object):
|
|||||||
restore_backup() methods
|
restore_backup() methods
|
||||||
1.27 - Update delete_share_instance() and delete_snapshot() methods
|
1.27 - Update delete_share_instance() and delete_snapshot() methods
|
||||||
1.28 - Add update_share_from_metadata() method
|
1.28 - Add update_share_from_metadata() method
|
||||||
|
1.29 - Add ensure_shares()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
BASE_RPC_API_VERSION = '1.0'
|
BASE_RPC_API_VERSION = '1.0'
|
||||||
@ -97,7 +98,7 @@ class ShareAPI(object):
|
|||||||
super(ShareAPI, self).__init__()
|
super(ShareAPI, self).__init__()
|
||||||
target = messaging.Target(topic=CONF.share_topic,
|
target = messaging.Target(topic=CONF.share_topic,
|
||||||
version=self.BASE_RPC_API_VERSION)
|
version=self.BASE_RPC_API_VERSION)
|
||||||
self.client = rpc.get_client(target, version_cap='1.28')
|
self.client = rpc.get_client(target, version_cap='1.29')
|
||||||
|
|
||||||
def create_share_instance(self, context, share_instance, host,
|
def create_share_instance(self, context, share_instance, host,
|
||||||
request_spec, filter_properties,
|
request_spec, filter_properties,
|
||||||
@ -540,3 +541,12 @@ class ShareAPI(object):
|
|||||||
'update_share_from_metadata',
|
'update_share_from_metadata',
|
||||||
share_id=share['id'],
|
share_id=share['id'],
|
||||||
metadata=metadata)
|
metadata=metadata)
|
||||||
|
|
||||||
|
def ensure_driver_resources(self, context, host):
|
||||||
|
host = utils.extract_host(host)
|
||||||
|
call_context = self.client.prepare(server=host, version='1.29')
|
||||||
|
return call_context.cast(
|
||||||
|
context,
|
||||||
|
'ensure_driver_resources',
|
||||||
|
skip_backend_info_check=True
|
||||||
|
)
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
import webob
|
||||||
|
|
||||||
import ddt
|
import ddt
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
@ -158,6 +159,7 @@ fake_response_service_list_with_disabled_reason = {'services': [
|
|||||||
'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38),
|
'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
ENSURE_SHARES_VERSION = "2.86"
|
||||||
|
|
||||||
|
|
||||||
def fake_service_get_all(context):
|
def fake_service_get_all(context):
|
||||||
@ -386,3 +388,99 @@ class ServicesTest(test.TestCase):
|
|||||||
|
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exception.VersionNotFoundForAPIMethod, controller().index, req)
|
exception.VersionNotFoundForAPIMethod, controller().index, req)
|
||||||
|
|
||||||
|
def test_ensure_shares_no_host_param(self):
|
||||||
|
req = fakes.HTTPRequest.blank(
|
||||||
|
'/fooproject/services/ensure', version=ENSURE_SHARES_VERSION)
|
||||||
|
body = {}
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.ensure_shares,
|
||||||
|
req,
|
||||||
|
body
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ensure_shares_host_not_found(self):
|
||||||
|
req = fakes.HTTPRequest.blank(
|
||||||
|
'/fooproject/services/ensure', version=ENSURE_SHARES_VERSION)
|
||||||
|
req_context = req.environ['manila.context']
|
||||||
|
body = {'host': 'host1'}
|
||||||
|
|
||||||
|
mock_service_get = self.mock_object(
|
||||||
|
db, 'service_get_by_args',
|
||||||
|
mock.Mock(side_effect=exception.NotFound())
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
webob.exc.HTTPNotFound,
|
||||||
|
self.controller.ensure_shares,
|
||||||
|
req,
|
||||||
|
body
|
||||||
|
)
|
||||||
|
mock_service_get.assert_called_once_with(
|
||||||
|
req_context,
|
||||||
|
body['host'],
|
||||||
|
'manila-share'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ensure_shares_conflict(self):
|
||||||
|
req = fakes.HTTPRequest.blank(
|
||||||
|
'/fooproject/services/ensure', version=ENSURE_SHARES_VERSION)
|
||||||
|
req_context = req.environ['manila.context']
|
||||||
|
body = {'host': 'host1'}
|
||||||
|
fake_service = {'id': 'fake_service_id'}
|
||||||
|
|
||||||
|
mock_service_get = self.mock_object(
|
||||||
|
db,
|
||||||
|
'service_get_by_args',
|
||||||
|
mock.Mock(return_value=fake_service)
|
||||||
|
)
|
||||||
|
mock_ensure = self.mock_object(
|
||||||
|
self.controller.service_api,
|
||||||
|
'ensure_shares',
|
||||||
|
mock.Mock(side_effect=webob.exc.HTTPConflict)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
webob.exc.HTTPConflict,
|
||||||
|
self.controller.ensure_shares,
|
||||||
|
req,
|
||||||
|
body
|
||||||
|
)
|
||||||
|
mock_service_get.assert_called_once_with(
|
||||||
|
req_context,
|
||||||
|
body['host'],
|
||||||
|
'manila-share'
|
||||||
|
)
|
||||||
|
mock_ensure.assert_called_once_with(
|
||||||
|
req_context, fake_service, body['host']
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ensure_shares(self):
|
||||||
|
req = fakes.HTTPRequest.blank(
|
||||||
|
'/fooproject/services/ensure', version=ENSURE_SHARES_VERSION)
|
||||||
|
req_context = req.environ['manila.context']
|
||||||
|
body = {'host': 'host1'}
|
||||||
|
fake_service = {'id': 'fake_service_id'}
|
||||||
|
|
||||||
|
mock_service_get = self.mock_object(
|
||||||
|
db,
|
||||||
|
'service_get_by_args',
|
||||||
|
mock.Mock(return_value=fake_service)
|
||||||
|
)
|
||||||
|
mock_ensure = self.mock_object(
|
||||||
|
self.controller.service_api, 'ensure_shares',
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.controller.ensure_shares(req, body)
|
||||||
|
|
||||||
|
self.assertEqual(202, response.status_int)
|
||||||
|
mock_service_get.assert_called_once_with(
|
||||||
|
req_context,
|
||||||
|
body['host'],
|
||||||
|
'manila-share'
|
||||||
|
)
|
||||||
|
mock_ensure.assert_called_once_with(
|
||||||
|
req_context, fake_service, body['host']
|
||||||
|
)
|
||||||
|
0
manila/tests/services/__init__.py
Normal file
0
manila/tests/services/__init__.py
Normal file
61
manila/tests/services/test_api.py
Normal file
61
manila/tests/services/test_api.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
|
from manila import context
|
||||||
|
from manila.services import api as service_api
|
||||||
|
from manila import test
|
||||||
|
|
||||||
|
|
||||||
|
class ServicesApiTest(test.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ServicesApiTest, self).setUp()
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
self.share_rpcapi = mock.Mock()
|
||||||
|
self.share_rpcapi.ensure_shares = mock.Mock()
|
||||||
|
self.services_api = service_api.API()
|
||||||
|
self.mock_object(
|
||||||
|
self.services_api, 'share_rpcapi', self.share_rpcapi
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ensure_shares(self):
|
||||||
|
host = 'fake_host@fakebackend'
|
||||||
|
fake_service = {
|
||||||
|
'id': 'fake_service_id',
|
||||||
|
'state': 'up'
|
||||||
|
}
|
||||||
|
|
||||||
|
self.services_api.ensure_shares(self.context, fake_service, host)
|
||||||
|
|
||||||
|
self.share_rpcapi.ensure_driver_resources.assert_called_once_with(
|
||||||
|
self.context, host
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ensure_shares_host_down(self):
|
||||||
|
host = 'fake_host@fakebackend'
|
||||||
|
fake_service = {
|
||||||
|
'id': 'fake_service_id',
|
||||||
|
'state': 'down'
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exc.HTTPConflict,
|
||||||
|
self.services_api.ensure_shares,
|
||||||
|
self.context,
|
||||||
|
fake_service,
|
||||||
|
host
|
||||||
|
)
|
||||||
|
|
||||||
|
self.share_rpcapi.ensure_shares.assert_not_called()
|
@ -208,6 +208,11 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
'reapply_access_rules': driver_needs_to_reapply_rules,
|
'reapply_access_rules': driver_needs_to_reapply_rules,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
fake_service = {'id': 'fake_service_id', 'binary': 'manila-share'}
|
||||||
|
self.mock_object(self.share_manager.db,
|
||||||
|
'service_get_by_args',
|
||||||
|
mock.Mock(return_value=fake_service))
|
||||||
|
self.mock_object(self.share_manager.db, 'service_update')
|
||||||
mock_backend_info_update = self.mock_object(
|
mock_backend_info_update = self.mock_object(
|
||||||
self.share_manager.db, 'backend_info_update')
|
self.share_manager.db, 'backend_info_update')
|
||||||
mock_share_get_all_by_host = self.mock_object(
|
mock_share_get_all_by_host = self.mock_object(
|
||||||
@ -263,6 +268,23 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
mock.call(utils.IsAMatcher(context.RequestContext),
|
mock.call(utils.IsAMatcher(context.RequestContext),
|
||||||
instances[2]),
|
instances[2]),
|
||||||
])
|
])
|
||||||
|
self.share_manager.db.service_get_by_args.assert_called_once_with(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
self.share_manager.host,
|
||||||
|
'manila-share'
|
||||||
|
)
|
||||||
|
self.share_manager.db.service_update.assert_has_calls([
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': True}
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': False}
|
||||||
|
)
|
||||||
|
])
|
||||||
if driver_needs_to_reapply_rules:
|
if driver_needs_to_reapply_rules:
|
||||||
# don't care if share_instance['access_rules_status'] is "syncing"
|
# don't care if share_instance['access_rules_status'] is "syncing"
|
||||||
mock_reset_rules_method.assert_has_calls([
|
mock_reset_rules_method.assert_has_calls([
|
||||||
@ -301,6 +323,11 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
'metadata': metadata_updates,
|
'metadata': metadata_updates,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
fake_service = {'id': 'fake_service_id', 'binary': 'manila-share'}
|
||||||
|
self.mock_object(self.share_manager.db,
|
||||||
|
'service_get_by_args',
|
||||||
|
mock.Mock(return_value=fake_service))
|
||||||
|
self.mock_object(self.share_manager.db, 'service_update')
|
||||||
mock_backend_info_update = self.mock_object(
|
mock_backend_info_update = self.mock_object(
|
||||||
self.share_manager.db, 'backend_info_update')
|
self.share_manager.db, 'backend_info_update')
|
||||||
mock_share_get_all_by_host = self.mock_object(
|
mock_share_get_all_by_host = self.mock_object(
|
||||||
@ -359,6 +386,23 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
])
|
])
|
||||||
# none of the share instances in the fake data have syncing rules
|
# none of the share instances in the fake data have syncing rules
|
||||||
mock_reset_rules_method.assert_not_called()
|
mock_reset_rules_method.assert_not_called()
|
||||||
|
self.share_manager.db.service_get_by_args.assert_called_once_with(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
self.share_manager.host,
|
||||||
|
'manila-share'
|
||||||
|
)
|
||||||
|
self.share_manager.db.service_update.assert_has_calls([
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': True}
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': False}
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
def test_init_host_with_no_shares(self):
|
def test_init_host_with_no_shares(self):
|
||||||
self.mock_object(self.share_manager.db,
|
self.mock_object(self.share_manager.db,
|
||||||
@ -520,6 +564,7 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
}
|
}
|
||||||
instances[0]['access_rules_status'] = ''
|
instances[0]['access_rules_status'] = ''
|
||||||
instances[2]['access_rules_status'] = ''
|
instances[2]['access_rules_status'] = ''
|
||||||
|
fake_service = {'id': 'fake_service_id', 'binary': 'manila-share'}
|
||||||
self.mock_object(self.share_manager.db,
|
self.mock_object(self.share_manager.db,
|
||||||
'backend_info_get',
|
'backend_info_get',
|
||||||
mock.Mock(return_value=old_backend_info))
|
mock.Mock(return_value=old_backend_info))
|
||||||
@ -530,6 +575,10 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
mock_share_get_all_by_host = self.mock_object(
|
mock_share_get_all_by_host = self.mock_object(
|
||||||
self.share_manager.db, 'share_instance_get_all_by_host',
|
self.share_manager.db, 'share_instance_get_all_by_host',
|
||||||
mock.Mock(return_value=instances))
|
mock.Mock(return_value=instances))
|
||||||
|
self.mock_object(self.share_manager.db,
|
||||||
|
'service_get_by_args',
|
||||||
|
mock.Mock(return_value=fake_service))
|
||||||
|
self.mock_object(self.share_manager.db, 'service_update')
|
||||||
self.mock_object(self.share_manager.db, 'share_instance_get',
|
self.mock_object(self.share_manager.db, 'share_instance_get',
|
||||||
mock.Mock(side_effect=[instances[0], instances[2],
|
mock.Mock(side_effect=[instances[0], instances[2],
|
||||||
instances[4]]))
|
instances[4]]))
|
||||||
@ -607,6 +656,23 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
mock.call(mock.ANY, instances[2]['id'],
|
mock.call(mock.ANY, instances[2]['id'],
|
||||||
share_server=share_server),
|
share_server=share_server),
|
||||||
]))
|
]))
|
||||||
|
self.share_manager.db.service_get_by_args.assert_called_once_with(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
self.share_manager.host,
|
||||||
|
'manila-share'
|
||||||
|
)
|
||||||
|
self.share_manager.db.service_update.assert_has_calls([
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': True}
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': False}
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
@ddt.data(("some_hash", {"db_version": "test_version"}),
|
@ddt.data(("some_hash", {"db_version": "test_version"}),
|
||||||
("ddd86ec90923b686597501e2f2431f3af59238c0",
|
("ddd86ec90923b686597501e2f2431f3af59238c0",
|
||||||
@ -623,6 +689,7 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
new_backend_info else None)
|
new_backend_info else None)
|
||||||
mock_backend_info_update = self.mock_object(
|
mock_backend_info_update = self.mock_object(
|
||||||
self.share_manager.db, 'backend_info_update')
|
self.share_manager.db, 'backend_info_update')
|
||||||
|
fake_service = {'id': 'fake_service_id', 'binary': 'manila-share'}
|
||||||
self.mock_object(
|
self.mock_object(
|
||||||
self.share_manager.db, 'backend_info_get',
|
self.share_manager.db, 'backend_info_get',
|
||||||
mock.Mock(return_value=old_backend_info))
|
mock.Mock(return_value=old_backend_info))
|
||||||
@ -630,6 +697,10 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
mock.Mock(return_value=new_backend_info))
|
mock.Mock(return_value=new_backend_info))
|
||||||
self.mock_object(self.share_manager, 'publish_service_capabilities',
|
self.mock_object(self.share_manager, 'publish_service_capabilities',
|
||||||
mock.Mock())
|
mock.Mock())
|
||||||
|
self.mock_object(self.share_manager.db,
|
||||||
|
'service_get_by_args',
|
||||||
|
mock.Mock(return_value=fake_service))
|
||||||
|
self.mock_object(self.share_manager.db, 'service_update')
|
||||||
mock_ensure_shares = self.mock_object(
|
mock_ensure_shares = self.mock_object(
|
||||||
self.share_manager.driver, 'ensure_shares')
|
self.share_manager.driver, 'ensure_shares')
|
||||||
mock_share_instance_get_all_by_host = self.mock_object(
|
mock_share_instance_get_all_by_host = self.mock_object(
|
||||||
@ -665,6 +736,7 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
|
|
||||||
instances = self._setup_init_mocks(setup_access_rules=False)
|
instances = self._setup_init_mocks(setup_access_rules=False)
|
||||||
share_server = fakes.fake_share_server_get()
|
share_server = fakes.fake_share_server_get()
|
||||||
|
fake_service = {'id': 'fake_service_id', 'binary': 'manila-share'}
|
||||||
self.mock_object(self.share_manager.db,
|
self.mock_object(self.share_manager.db,
|
||||||
'share_instance_get_all_by_host',
|
'share_instance_get_all_by_host',
|
||||||
mock.Mock(return_value=instances))
|
mock.Mock(return_value=instances))
|
||||||
@ -683,6 +755,10 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
self.mock_object(self.share_manager, '_get_share_server_dict',
|
self.mock_object(self.share_manager, '_get_share_server_dict',
|
||||||
mock.Mock(return_value=share_server))
|
mock.Mock(return_value=share_server))
|
||||||
self.mock_object(self.share_manager, 'publish_service_capabilities')
|
self.mock_object(self.share_manager, 'publish_service_capabilities')
|
||||||
|
self.mock_object(self.share_manager.db,
|
||||||
|
'service_get_by_args',
|
||||||
|
mock.Mock(return_value=fake_service))
|
||||||
|
self.mock_object(self.share_manager.db, 'service_update')
|
||||||
self.mock_object(manager.LOG, 'error')
|
self.mock_object(manager.LOG, 'error')
|
||||||
self.mock_object(manager.LOG, 'info')
|
self.mock_object(manager.LOG, 'info')
|
||||||
|
|
||||||
@ -730,6 +806,23 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
mock.ANY,
|
mock.ANY,
|
||||||
{'id': instances[1]['id'], 'status': instances[1]['status']},
|
{'id': instances[1]['id'], 'status': instances[1]['status']},
|
||||||
)
|
)
|
||||||
|
self.share_manager.db.service_get_by_args.assert_called_once_with(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
self.share_manager.host,
|
||||||
|
'manila-share'
|
||||||
|
)
|
||||||
|
self.share_manager.db.service_update.assert_has_calls([
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': True}
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': False}
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
def _get_share_instance_dict(self, share_instance, **kwargs):
|
def _get_share_instance_dict(self, share_instance, **kwargs):
|
||||||
# TODO(gouthamr): remove method when the db layer returns primitives
|
# TODO(gouthamr): remove method when the db layer returns primitives
|
||||||
@ -775,11 +868,16 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
raise exception.ManilaException(message="Fake raise")
|
raise exception.ManilaException(message="Fake raise")
|
||||||
|
|
||||||
instances = self._setup_init_mocks(setup_access_rules=False)
|
instances = self._setup_init_mocks(setup_access_rules=False)
|
||||||
|
fake_service = {'id': 'fake_service_id', 'binary': 'manila-share'}
|
||||||
mock_ensure_share = self.mock_object(
|
mock_ensure_share = self.mock_object(
|
||||||
self.share_manager.driver, 'ensure_share')
|
self.share_manager.driver, 'ensure_share')
|
||||||
self.mock_object(self.share_manager.db,
|
self.mock_object(self.share_manager.db,
|
||||||
'share_instance_get_all_by_host',
|
'share_instance_get_all_by_host',
|
||||||
mock.Mock(return_value=instances))
|
mock.Mock(return_value=instances))
|
||||||
|
self.mock_object(self.share_manager.db,
|
||||||
|
'service_get_by_args',
|
||||||
|
mock.Mock(return_value=fake_service))
|
||||||
|
self.mock_object(self.share_manager.db, 'service_update')
|
||||||
self.mock_object(self.share_manager.db, 'share_instance_get',
|
self.mock_object(self.share_manager.db, 'share_instance_get',
|
||||||
mock.Mock(side_effect=[instances[0], instances[2],
|
mock.Mock(side_effect=[instances[0], instances[2],
|
||||||
instances[3]]))
|
instances[3]]))
|
||||||
@ -808,6 +906,20 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
mock.call(utils.IsAMatcher(context.RequestContext), instances[0]),
|
mock.call(utils.IsAMatcher(context.RequestContext), instances[0]),
|
||||||
mock.call(utils.IsAMatcher(context.RequestContext), instances[2]),
|
mock.call(utils.IsAMatcher(context.RequestContext), instances[2]),
|
||||||
])
|
])
|
||||||
|
self.share_manager.db.service_get_by_args.assert_called_once_with(
|
||||||
|
utils.IsAMatcher(context.RequestContext), self.share_manager.host,
|
||||||
|
'manila-share'
|
||||||
|
)
|
||||||
|
self.share_manager.db.service_update.assert_has_calls([
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext), fake_service['id'],
|
||||||
|
{'ensuring': True}
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext), fake_service['id'],
|
||||||
|
{'ensuring': False}
|
||||||
|
)
|
||||||
|
])
|
||||||
self.share_manager.driver.ensure_shares.assert_called_once_with(
|
self.share_manager.driver.ensure_shares.assert_called_once_with(
|
||||||
utils.IsAMatcher(context.RequestContext),
|
utils.IsAMatcher(context.RequestContext),
|
||||||
[dict_instances[0], dict_instances[2], dict_instances[3]])
|
[dict_instances[0], dict_instances[2], dict_instances[3]])
|
||||||
@ -854,11 +966,16 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
instances[4]['id']: {'status': 'available'}
|
instances[4]['id']: {'status': 'available'}
|
||||||
}
|
}
|
||||||
smanager = self.share_manager
|
smanager = self.share_manager
|
||||||
|
fake_service = {'id': 'fake_service_id', 'binary': 'manila-share'}
|
||||||
self.mock_object(smanager.db, 'share_instance_get_all_by_host',
|
self.mock_object(smanager.db, 'share_instance_get_all_by_host',
|
||||||
mock.Mock(return_value=instances))
|
mock.Mock(return_value=instances))
|
||||||
self.mock_object(self.share_manager.db, 'share_instance_get',
|
self.mock_object(self.share_manager.db, 'share_instance_get',
|
||||||
mock.Mock(side_effect=[instances[0], instances[2],
|
mock.Mock(side_effect=[instances[0], instances[2],
|
||||||
instances[4]]))
|
instances[4]]))
|
||||||
|
self.mock_object(self.share_manager.db,
|
||||||
|
'service_get_by_args',
|
||||||
|
mock.Mock(return_value=fake_service))
|
||||||
|
self.mock_object(self.share_manager.db, 'service_update')
|
||||||
self.mock_object(self.share_manager.driver, 'ensure_share',
|
self.mock_object(self.share_manager.driver, 'ensure_share',
|
||||||
mock.Mock(return_value=None))
|
mock.Mock(return_value=None))
|
||||||
self.mock_object(self.share_manager.driver, 'ensure_shares',
|
self.mock_object(self.share_manager.driver, 'ensure_shares',
|
||||||
@ -915,6 +1032,23 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
manager.LOG.exception.assert_has_calls([
|
manager.LOG.exception.assert_has_calls([
|
||||||
mock.call(mock.ANY, mock.ANY),
|
mock.call(mock.ANY, mock.ANY),
|
||||||
])
|
])
|
||||||
|
self.share_manager.db.service_get_by_args.assert_called_once_with(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
self.share_manager.host,
|
||||||
|
'manila-share'
|
||||||
|
)
|
||||||
|
self.share_manager.db.service_update.assert_has_calls([
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': True}
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
utils.IsAMatcher(context.RequestContext),
|
||||||
|
fake_service['id'],
|
||||||
|
{'ensuring': False}
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
def test_create_share_instance_from_snapshot_with_server(self):
|
def test_create_share_instance_from_snapshot_with_server(self):
|
||||||
"""Test share can be created from snapshot if server exists."""
|
"""Test share can be created from snapshot if server exists."""
|
||||||
|
@ -147,6 +147,8 @@ class ShareRpcAPITestCase(test.TestCase):
|
|||||||
and method in src_dest_share_server_methods):
|
and method in src_dest_share_server_methods):
|
||||||
share_server = expected_msg.pop('dest_share_server', None)
|
share_server = expected_msg.pop('dest_share_server', None)
|
||||||
expected_msg['dest_share_server_id'] = share_server['id']
|
expected_msg['dest_share_server_id'] = share_server['id']
|
||||||
|
if method == 'ensure_driver_resources':
|
||||||
|
expected_msg['skip_backend_info_check'] = True
|
||||||
|
|
||||||
if 'host' in kwargs:
|
if 'host' in kwargs:
|
||||||
host = kwargs['host']
|
host = kwargs['host']
|
||||||
@ -527,3 +529,11 @@ class ShareRpcAPITestCase(test.TestCase):
|
|||||||
dest_host=self.fake_host,
|
dest_host=self.fake_host,
|
||||||
share_network_id='fake_net_id',
|
share_network_id='fake_net_id',
|
||||||
new_share_network_subnet_id='new_share_network_subnet_id')
|
new_share_network_subnet_id='new_share_network_subnet_id')
|
||||||
|
|
||||||
|
def test_ensure_driver_resources(self):
|
||||||
|
self._test_share_api(
|
||||||
|
'ensure_driver_resources',
|
||||||
|
rpc_method='cast',
|
||||||
|
version='1.29',
|
||||||
|
host=self.fake_host,
|
||||||
|
)
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A new API to start the ensure shares procedure for Manila has been added.
|
||||||
|
Through this API, OpenStack administrators will be able to recalculate the
|
||||||
|
shares' export location without restarting the shares manager service.
|
||||||
|
Additionally, a new configuration option named
|
||||||
|
`update_shares_status_on_ensure` is now available to help OpenStack
|
||||||
|
administrators determine whether the shares' status should be modified
|
||||||
|
during the ensure shares procedure or not.
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
When restarting the service on an upgrade, when ensure shares is being run
|
||||||
|
it will automatically transition the shares status to `ensuring`. In case
|
||||||
|
you would like to prevent it, please change the value of the
|
||||||
|
`update_shares_status_on_ensure` configuration option.
|
Loading…
x
Reference in New Issue
Block a user