Merge "Human readable export location core implementation"

This commit is contained in:
Zuul 2024-03-08 18:10:03 +00:00 committed by Gerrit Code Review
commit a230ea511e
39 changed files with 689 additions and 120 deletions

View File

@ -196,6 +196,20 @@ Share type common capability extra-specs that are visible to end users:
this extra-spec, the share type is assumed to be serviceable in all
availability zones known to the Shared File Systems service.
* **mount_point_name_support** whether a custom export location could
be specified during share creation. To enable users to specify a custom
mount point for their shares, administrators must set this
extra-specification in the share type to True. They must also provide
an extra-spec named ``provisioning:mount_point_prefix``. The service will
use this prefix in conjunction with the mount point name provided by end
users during share creation. When ``provisioning:mount_point_prefix`` is not
set on a share type, but ``mount_point_name_support`` is enabled, the
share's export location will be prefixed with the ``project_id``.
However, shares created with a ``project_id`` prefix are not eligible
for transfer. For these shares to be transferred to a different project,
the admin will need to manually unmount them from the current project
and mount them to the target project.
Share type common capability extra-specs that are not visible to end users:
---------------------------------------------------------------------------
@ -274,3 +288,7 @@ Share type common capability extra-specs that are not visible to end users:
the share type can not be greater than the specified value. This capability
is ignored for regular users and the "provisioning:max_share_size" is the
only effective limit.
* **provisioning:mount_point_prefix** can set prefix for human readable
mount_point_name, the value must be a string containing ASCII alphabets
and optionally, the underscore character.

View File

@ -261,77 +261,77 @@ Mapping of share drivers and common capabilities
More information: :ref:`capabilities_and_extra_specs`
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Driver name | DHSS=True | DHSS=False | dedupe | compression | thin_provisioning | thick_provisioning | qos | create share from snapshot | revert to snapshot | mountable snapshot | ipv4_support | ipv6_support | multiple subnets per AZ |
+========================================+===========+============+========+=============+===================+====================+=====+============================+====================+====================+==============+==============+=========================+
| ZFSonLinux | \- | M | M | M | M | \- | \- | M | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Container | N | \- | \- | \- | \- | N | \- | \- | \- | \- | P | \- | Y |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Generic (Cinder as back-end) | J | K | \- | \- | \- | L | \- | J | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| NetApp Clustered Data ONTAP | J | K | M | M | M | L | P | J | O | \- | P | Q | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Dell EMC PowerMax | O | \- | \- | \- | \- | \- | \- | O | \- | \- | P | R | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| EMC VNX | J | \- | \- | \- | \- | L | \- | J | \- | \- | P | Q | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| EMC Unity | N | T | \- | \- | N | \- | \- | N | S | \- | P | Q | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| EMC Isilon | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Dell EMC PowerStore | \- | B | \- | \- | B | \- | \- | B | B | \- | B | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Dell EMC PowerFlex | \- | B | \- | \- | B | \- | \- | \- | \- | \- | B | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| GlusterFS | \- | J | \- | \- | \- | L | \- | volume layout (L) | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| GlusterFS-Native | \- | J | \- | \- | \- | L | \- | L | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| HDFS | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Hitachi HNAS | \- | L | N | \- | L | \- | \- | L | O | O | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Hitachi HSP | \- | N | \- | \- | N | \- | \- | \- | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| HPE 3PAR | L | K | L | \- | L | L | \- | K | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Huawei | M | K | L | L | L | L | M | M | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| INFINIDAT | \- | Q | \- | \- | Q | Q | \- | Q | Q | Q | Q | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Infortrend | \- | T | \- | \- | \- | \- | \- | \- | \- | \- | T | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| LVM | \- | M | \- | \- | \- | M | \- | K | O | O | P | P | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Macrosan | \- | Z | \- | \- | \- | Z | \- | \- | \- | \- | Z | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Quobyte | \- | K | \- | \- | \- | L | \- | M | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Windows SMB | L | L | \- | \- | \- | L | \- | \- | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| IBM GPFS | \- | K | \- | \- | \- | L | \- | L | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Oracle ZFSSA | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| CephFS | \- | M | \- | \- | \- | M | \- | \- | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Tegile | \- | M | M | M | M | \- | \- | M | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| NexentaStor4 | \- | N | N | N | N | N | \- | N | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| NexentaStor5 | \- | N | \- | N | N | N | \- | N | T | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| MapRFS | \- | N | \- | \- | \- | N | \- | O | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| QNAP | \- | O | Q | Q | O | Q | \- | O | \- | \- | P | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| INSPUR AS13000 | \- | R | \- | \- | R | \- | \- | R | \- | \- | R | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| INSPUR InStorage | \- | T | \- | \- | \- | T | \- | \- | \- | \- | T | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
| Pure Storage FlashBlade | \- | X | \- | \- | X | \- | \- | \- | X | \- | X | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Driver name | DHSS=True | DHSS=False | dedupe | compression | thin_provisioning | thick_provisioning | qos | create share from snapshot | revert to snapshot | mountable snapshot | ipv4_support | ipv6_support | multiple subnets per AZ | mount point name support |
+========================================+===========+============+========+=============+===================+====================+=====+============================+====================+====================+==============+==============+=========================+==========================+
| ZFSonLinux | \- | M | M | M | M | \- | \- | M | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Container | N | \- | \- | \- | \- | N | \- | \- | \- | \- | P | \- | Y | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Generic (Cinder as back-end) | J | K | \- | \- | \- | L | \- | J | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| NetApp Clustered Data ONTAP | J | K | M | M | M | L | P | J | O | \- | P | Q | \- | Y |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Dell EMC PowerMax | O | \- | \- | \- | \- | \- | \- | O | \- | \- | P | R | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| EMC VNX | J | \- | \- | \- | \- | L | \- | J | \- | \- | P | Q | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| EMC Unity | N | T | \- | \- | N | \- | \- | N | S | \- | P | Q | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| EMC Isilon | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Dell EMC PowerStore | \- | B | \- | \- | B | \- | \- | B | B | \- | B | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Dell EMC PowerFlex | \- | B | \- | \- | B | \- | \- | \- | \- | \- | B | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| GlusterFS | \- | J | \- | \- | \- | L | \- | volume layout (L) | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| GlusterFS-Native | \- | J | \- | \- | \- | L | \- | L | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| HDFS | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Hitachi HNAS | \- | L | N | \- | L | \- | \- | L | O | O | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Hitachi HSP | \- | N | \- | \- | N | \- | \- | \- | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| HPE 3PAR | L | K | L | \- | L | L | \- | K | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Huawei | M | K | L | L | L | L | M | M | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| INFINIDAT | \- | Q | \- | \- | Q | Q | \- | Q | Q | Q | Q | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Infortrend | \- | T | \- | \- | \- | \- | \- | \- | \- | \- | T | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| LVM | \- | M | \- | \- | \- | M | \- | K | O | O | P | P | \- | Y |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Macrosan | \- | Z | \- | \- | \- | Z | \- | \- | \- | \- | Z | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Quobyte | \- | K | \- | \- | \- | L | \- | M | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Windows SMB | L | L | \- | \- | \- | L | \- | \- | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| IBM GPFS | \- | K | \- | \- | \- | L | \- | L | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Oracle ZFSSA | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| CephFS | \- | M | \- | \- | \- | M | \- | \- | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Tegile | \- | M | M | M | M | \- | \- | M | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| NexentaStor4 | \- | N | N | N | N | N | \- | N | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| NexentaStor5 | \- | N | \- | N | N | N | \- | N | T | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| MapRFS | \- | N | \- | \- | \- | N | \- | O | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| QNAP | \- | O | Q | Q | O | Q | \- | O | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| INSPUR AS13000 | \- | R | \- | \- | R | \- | \- | R | \- | \- | R | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| INSPUR InStorage | \- | T | \- | \- | \- | T | \- | \- | \- | \- | T | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Pure Storage FlashBlade | \- | X | \- | \- | X | \- | \- | \- | X | \- | X | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
.. note::

View File

@ -201,13 +201,14 @@ REST_API_VERSION_HISTORY = """
* 2.81 - Added API methods, endpoint /resource-locks.
* 2.82 - Added lock and restriction to share access rules.
* 2.83 - Added 'disabled_reason' field to services.
* 2.84 - Added mount_point_name to shares.
"""
# 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.83"
_MAX_API_VERSION = "2.84"
DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -450,3 +450,7 @@ user documentation.
The ``disabled_reason`` field was added to the service to mark the reason why
the user disabled the service. ``disabled`` field will be replaced by
``status`` field.
2.84
----
Added optional ``mount_point_name`` field to share.

View File

@ -25,6 +25,7 @@ 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.views import share_accesses as share_access_views
from manila.api.views import shares as share_views
@ -445,6 +446,9 @@ class ShareMixin(object):
kwargs['scheduler_hints'] = scheduler_hints
if req.api_version_request >= api_version.APIVersionRequest("2.84"):
kwargs['mount_point_name'] = share.pop('mount_point_name', None)
new_share = self.share_api.create(context,
share_proto,
size,

View File

@ -300,10 +300,12 @@ class ExtraSpecs(object):
CREATE_SHARE_FROM_SNAPSHOT_SUPPORT = "create_share_from_snapshot_support"
REVERT_TO_SNAPSHOT_SUPPORT = "revert_to_snapshot_support"
MOUNT_SNAPSHOT_SUPPORT = "mount_snapshot_support"
MOUNT_POINT_NAME_SUPPORT = "mount_point_name_support"
AVAILABILITY_ZONES = "availability_zones"
PROVISIONING_MAX_SHARE_SIZE = "provisioning:max_share_size"
PROVISIONING_MIN_SHARE_SIZE = "provisioning:min_share_size"
PROVISIONING_MAX_SHARE_EXTEND_SIZE = "provisioning:max_share_extend_size"
PROVISIONING_MOUNT_POINT_PREFIX = "provisioning:mount_point_prefix"
# Extra specs containers
REQUIRED = (
@ -316,10 +318,12 @@ class ExtraSpecs(object):
REVERT_TO_SNAPSHOT_SUPPORT,
REPLICATION_TYPE_SPEC,
MOUNT_SNAPSHOT_SUPPORT,
MOUNT_POINT_NAME_SUPPORT,
AVAILABILITY_ZONES,
PROVISIONING_MAX_SHARE_SIZE,
PROVISIONING_MIN_SHARE_SIZE,
PROVISIONING_MAX_SHARE_EXTEND_SIZE
PROVISIONING_MAX_SHARE_EXTEND_SIZE,
PROVISIONING_MOUNT_POINT_PREFIX,
)
# NOTE(cknight): Some extra specs are necessary parts of the Manila API and

View File

@ -0,0 +1,47 @@
# 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 mount_point_name to share_instances
Revision ID: 6e32091979e0
Revises: 99d328f0a3d2
Create Date: 2024-01-26 22:08:22.412974
"""
# revision identifiers, used by Alembic.
revision = '6e32091979e0'
down_revision = '99d328f0a3d2'
from alembic import op
from oslo_log import log
import sqlalchemy as sa
LOG = log.getLogger(__name__)
share_instances_table_name = 'share_instances'
column_name = "mount_point_name"
def upgrade():
try:
op.add_column(share_instances_table_name, sa.Column(column_name,
sa.String(255),
nullable=True))
except Exception:
LOG.error("Column mount_point_name not created!")
raise
def downgrade():
try:
op.drop_column(share_instances_table_name, column_name)
except Exception:
LOG.error("Column mount_point_name not dropped!")
raise

View File

@ -1619,6 +1619,7 @@ def _extract_share_instance_values(values):
'status', 'host', 'scheduled_at', 'launched_at', 'terminated_at',
'share_server_id', 'share_network_id', 'availability_zone_id',
'replica_state', 'share_type_id', 'share_type', 'access_rules_status',
'mount_point_name',
]
share_instance_values, share_values = (
_extract_subdict_by_fields(values, share_instance_model_fields)

View File

@ -371,7 +371,7 @@ class ShareInstance(BASE, ManilaBase):
host = Column(String(255))
status = Column(String(255))
progress = Column(String(32))
mount_point_name = Column(String(255))
ACCESS_STATUS_PRIORITIES = {
constants.STATUS_ACTIVE: 0,
constants.SHARE_INSTANCE_RULES_SYNCING: 1,

View File

@ -39,6 +39,7 @@ class CapabilitiesFilter(base_host.BaseHostFilter):
def host_passes(self, host_state, filter_properties):
"""Return a list of hosts that can create resource_type."""
resource_type = filter_properties.get('resource_type')
if not self._satisfies_extra_specs(host_state.capabilities,
resource_type):
LOG.debug("%(host_state)s fails resource_type extra_specs "

View File

@ -162,6 +162,7 @@ class HostState(object):
self.security_service_update_support = False
self.network_allocation_update_support = False
self.share_server_multiple_subnet_support = False
self.mount_point_name_support = False
# PoolState for all pools
self.pools = {}

View File

@ -66,7 +66,9 @@ def generate_stats(host_state, properties):
'network_allocation_update_support': (
host_state.network_allocation_update_support),
'share_server_multiple_subnet_support': (
host_state.share_server_multiple_subnet_support)
host_state.share_server_multiple_subnet_support),
'mount_point_name_support': (
host_state.mount_point_name_support)
}
host_caps = host_state.capabilities

View File

@ -21,6 +21,7 @@ Handles all requests relating to shares.
"""
import functools
import json
import re
from oslo_config import cfg
from oslo_log import log
@ -270,7 +271,8 @@ class API(base.Base):
share_network_id=None, share_type=None, is_public=False,
share_group_id=None, share_group_snapshot_member=None,
availability_zones=None, scheduler_hints=None,
az_request_multiple_subnet_support_map=None):
az_request_multiple_subnet_support_map=None,
mount_point_name=None):
"""Create new share."""
api_common.check_metadata_properties(metadata)
@ -348,6 +350,18 @@ class API(base.Base):
deltas = {'shares': 1, 'gigabytes': size}
share_type_attributes = self.get_share_attributes_from_share_type(
share_type)
mount_point_name_support = share_type_attributes.get(
constants.ExtraSpecs.MOUNT_POINT_NAME_SUPPORT, None)
if mount_point_name is not None:
if not mount_point_name_support:
msg = _("Setting a mount point name is not supported"
" by the share type used: %s." % share_type_id)
raise exception.InvalidInput(reason=msg)
mount_point_name = self._prefix_mount_point_name(
share_type, context, mount_point_name
)
share_type_supports_replication = share_type_attributes.get(
'replication_type', None)
if share_type_supports_replication:
@ -482,7 +496,8 @@ class API(base.Base):
share_type_id=share_type_id, availability_zones=availability_zones,
snapshot_host=snapshot_host, scheduler_hints=scheduler_hints,
az_request_multiple_subnet_support_map=(
az_request_multiple_subnet_support_map))
az_request_multiple_subnet_support_map),
mount_point_name=mount_point_name)
# Retrieve the share with instance details
share = self.db.share_get(context, share['id'])
@ -505,6 +520,9 @@ class API(base.Base):
constants.ExtraSpecs.REVERT_TO_SNAPSHOT_SUPPORT)
mount_snapshot_support_key = (
constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT)
mount_point_name_support_key = (
constants.ExtraSpecs.MOUNT_POINT_NAME_SUPPORT
)
snapshot_support_default = inferred_map.get(snapshot_support_key)
create_share_from_snapshot_support_default = inferred_map.get(
@ -513,6 +531,7 @@ class API(base.Base):
revert_to_snapshot_key)
mount_snapshot_support_default = inferred_map.get(
constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT)
mount_point_name_support_default = False
if share_type:
snapshot_support = share_types.parse_boolean_extra_spec(
@ -536,6 +555,11 @@ class API(base.Base):
'extra_specs', {}).get(
mount_snapshot_support_key,
mount_snapshot_support_default))
mount_point_name_support = share_types.parse_boolean_extra_spec(
mount_point_name_support_key, share_type.get(
'extra_specs', {}).get(
mount_point_name_support_key,
mount_point_name_support_default))
replication_type = share_type.get('extra_specs', {}).get(
'replication_type')
else:
@ -544,6 +568,7 @@ class API(base.Base):
create_share_from_snapshot_support_default)
revert_to_snapshot_support = revert_to_snapshot_support_default
mount_snapshot_support = mount_snapshot_support_default
mount_point_name_support = mount_point_name_support_default
replication_type = None
return {
@ -553,6 +578,7 @@ class API(base.Base):
'revert_to_snapshot_support': revert_to_snapshot_support,
'replication_type': replication_type,
'mount_snapshot_support': mount_snapshot_support,
'mount_point_name_support': mount_point_name_support,
}
def create_instance(self, context, share, share_network_id=None,
@ -560,7 +586,8 @@ class API(base.Base):
share_group=None, share_group_snapshot_member=None,
share_type_id=None, availability_zones=None,
snapshot_host=None, scheduler_hints=None,
az_request_multiple_subnet_support_map=None):
az_request_multiple_subnet_support_map=None,
mount_point_name=None):
request_spec, share_instance = (
self.create_share_instance_and_get_request_spec(
context, share, availability_zone=availability_zone,
@ -570,7 +597,8 @@ class API(base.Base):
availability_zones=availability_zones,
snapshot_host=snapshot_host,
az_request_multiple_subnet_support_map=(
az_request_multiple_subnet_support_map)))
az_request_multiple_subnet_support_map),
mount_point_name=mount_point_name))
if share_group_snapshot_member:
# Inherit properties from the share_group_snapshot_member
@ -613,7 +641,8 @@ class API(base.Base):
share_group=None, host=None, share_network_id=None,
share_type_id=None, cast_rules_to_readonly=False,
availability_zones=None, snapshot_host=None,
az_request_multiple_subnet_support_map=None):
az_request_multiple_subnet_support_map=None,
mount_point_name=None):
availability_zone_id = None
if availability_zone:
@ -633,6 +662,7 @@ class API(base.Base):
'availability_zone_id': availability_zone_id,
'share_type_id': share_type_id,
'cast_rules_to_readonly': cast_rules_to_readonly,
'mount_point_name': mount_point_name,
}
)
@ -666,7 +696,7 @@ class API(base.Base):
'host': share_instance['host'],
'status': share_instance['status'],
'replica_state': share_instance['replica_state'],
'share_type_id': share_instance['share_type_id'],
'share_type_id': share_instance['share_type_id']
}
share_type = None
@ -686,7 +716,7 @@ class API(base.Base):
'availability_zone_id': availability_zone_id,
'availability_zones': availability_zones,
'az_request_multiple_subnet_support_map': (
az_request_multiple_subnet_support_map),
az_request_multiple_subnet_support_map)
}
return request_spec, share_instance
@ -1063,6 +1093,11 @@ class API(base.Base):
share_type.get('extra_specs', {}).get(
'mount_snapshot_support')
),
'mount_point_name_support': kwargs.get(
'mount_point_name_support',
share_type.get('extra_specs', {}).get(
'mount_point_name_support')
),
'share_proto': kwargs.get('share_proto', share.get('share_proto')),
'share_type_id': share_type['id'],
'is_public': kwargs.get('is_public', share.get('is_public')),
@ -1094,6 +1129,25 @@ class API(base.Base):
}
return request_spec
def _prefix_mount_point_name(self, share_type, context,
mount_point_name=None):
prefix = share_type.get('extra_specs').get(
constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX)
prefix = prefix or context.project_id
prefix = prefix.format(context.to_dict())
mount_point_name = f"{prefix}_{mount_point_name}"
if mount_point_name and (
not re.match(
r'^[a-zA-Z0-9_]*$', mount_point_name)
or len(mount_point_name) > 255
):
msg = _("Invalid mount_point_name: %s")
LOG.error(msg, mount_point_name)
raise exception.InvalidInput(msg % mount_point_name)
return mount_point_name
@prevent_locked_action_on_share('delete')
def unmanage(self, context, share):
policy.check_policy(context, 'share', 'unmanage')

View File

@ -1356,6 +1356,7 @@ class ShareDriver(object):
network_allocation_update_support=(
self.network_allocation_update_support),
share_server_multiple_subnet_support=False,
mount_point_name_support=False,
)
if isinstance(data, dict):
common.update(data)

View File

@ -137,6 +137,7 @@ class ContainerShareDriver(driver.ShareDriver, driver.ExecuteMixin):
'pools': self.storage.get_share_server_pools(),
'security_service_update_support': True,
'share_server_multiple_subnet_support': True,
'mount_point_name_support': False,
}
super(ContainerShareDriver, self)._update_share_stats(data)

View File

@ -116,6 +116,9 @@ class LVMMixin(driver.ExecuteMixin):
except processutils.ProcessExecutionError:
raise
def _get_mount_point_name(self, share):
return share.get('mount_point_name') or share.get('name')
def _extend_container(self, share, device_name, size):
privsep_common.execute_with_retries(
privsep_lvm.lvextend, [device_name, size],
@ -267,14 +270,16 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
'reserved_percentage': 0,
'reserved_snapshot_percentage': 0,
'reserved_share_extend_percentage': 0,
'mount_point_name_support': True,
}, ]
def create_share(self, context, share, share_server=None):
self._allocate_container(share)
# create file system
device_name = self._get_local_path(share)
share_export_location = self._get_mount_point_name(share)
location = self._get_helper(share).create_exports(
self.share_server, share['name'])
self.share_server, share_export_location)
self._mount_device(share, device_name)
return location
@ -287,8 +292,9 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
self._set_random_uuid_to_device(share)
self._copy_volume(
snapshot_device_name, share_device_name, share['size'])
share_export_location = self._get_mount_point_name(share)
location = self._get_helper(share).create_exports(
self.share_server, share['name'])
self.share_server, share_export_location)
self._mount_device(share, share_device_name)
return location
@ -342,14 +348,19 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
"""Ensure that storage are mounted and exported."""
device_name = self._get_local_path(share)
self._mount_device(share, device_name)
share_export_location = self._get_mount_point_name(share)
return self._get_helper(share).create_exports(
self.share_server, share['name'], recreate=True)
self.share_server,
share_export_location,
recreate=True
)
def _delete_share(self, ctx, share):
share_export_location = self._get_mount_point_name(share)
"""Delete a share."""
try:
self._get_helper(share).remove_exports(
self.share_server, share['name'])
self.share_server, share_export_location)
except exception.ProcessExecutionError:
LOG.warning("Can't remove share %r", share['id'])
except exception.InvalidShare as exc:
@ -379,8 +390,10 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
removed. access_rules doesn't contain these rules.
:param share_server: None or Share server model
"""
share_export_location = self._get_mount_point_name(share)
self._get_helper(share).update_access(self.share_server,
share['name'], access_rules,
share_export_location,
access_rules,
add_rules=add_rules,
delete_rules=delete_rules)
@ -434,11 +447,15 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
def revert_to_snapshot(self, context, snapshot, share_access_rules,
snapshot_access_rules, share_server=None):
share = snapshot['share']
snapshot_export_location = self._get_mount_point_name(snapshot)
share_export_location = self._get_mount_point_name(share)
# Temporarily remove all access rules
self._get_helper(share).update_access(self.share_server,
snapshot['name'], [], [], [])
snapshot_export_location,
[], [], [])
self._get_helper(share).update_access(self.share_server,
share['name'], [], [], [])
share_export_location,
[], [], [])
# Unmount the snapshot filesystem
self._unmount_device(snapshot)
# Unmount the share filesystem
@ -459,15 +476,17 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
# Also remount the snapshot
device_name = self._get_local_path(snapshot)
self._mount_device(snapshot, device_name)
share_export_location = self._get_mount_point_name(share)
snapshot_export_location = self._get_mount_point_name(share)
# Lastly we add all the access rules back
self._get_helper(share).update_access(self.share_server,
share['name'],
share_export_location,
share_access_rules,
[], [])
snapshot_access_rules, __, __ = share_utils.change_rules_to_readonly(
snapshot_access_rules, [], [])
self._get_helper(share).update_access(self.share_server,
snapshot['name'],
snapshot_export_location,
snapshot_access_rules,
[], [])

View File

@ -330,6 +330,10 @@ def is_valid_csv(extra_spec_value):
return all([v.strip() for v in values])
def is_valid_string(v):
return isinstance(v, str) and len(v) in range(1, 256)
def sanitize_csv(csv_string):
return ','.join(value.strip() for value in csv_string.split(',')
if (csv_string and value))
@ -356,8 +360,12 @@ def is_valid_optional_extra_spec(key, value):
return value in constants.ExtraSpecs.REPLICATION_TYPES
elif key == constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT:
return parse_boolean_extra_spec(key, value) is not None
elif key == constants.ExtraSpecs.MOUNT_POINT_NAME_SUPPORT:
return parse_boolean_extra_spec(key, value) is not None
elif key == constants.ExtraSpecs.AVAILABILITY_ZONES:
return is_valid_csv(value)
elif key == constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX:
return is_valid_string(value)
elif key in [constants.ExtraSpecs.PROVISIONING_MAX_SHARE_SIZE,
constants.ExtraSpecs.PROVISIONING_MIN_SHARE_SIZE,
constants.ExtraSpecs.PROVISIONING_MAX_SHARE_EXTEND_SIZE]:

View File

@ -261,6 +261,37 @@ class ShareAPITest(test.TestCase):
self.assertEqual("fakenetid",
create_mock.call_args[1]['share_network_id'])
def test_share_create_mount_point_name(self):
shr = {
"size": 100,
"name": "Share Test Name",
"description": "Share Test Desc",
"share_proto": "fakeproto",
"mount_point_name": "fake_mp"
}
fake_network = {'id': 'fakenetid'}
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(),
mount_point_name=shr['mount_point_name']))
self.mock_object(share_api.API, 'create', create_mock)
self.mock_object(share_api.API, 'get_share_network', mock.Mock(
return_value=fake_network))
self.mock_object(common, 'check_share_network_is_active',
mock.Mock(return_value=True))
self.mock_object(
db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value={'id': 'fakesubnetid'}))
body = {"share": copy.deepcopy(shr)}
req = fakes.HTTPRequest.blank('/v1/fake/shares')
self.controller.create(req, body)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'create')
def test_share_create_with_share_net_not_active(self):
shr = {
"size": 100,
@ -459,6 +490,53 @@ class ShareAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'create')
def test_share_create_from_mount_point_name(self):
parent_share_net = 444
shr = {
"size": 100,
"name": "Share Test Name",
"description": "Share Test Desc",
"share_proto": "fakeproto",
"availability_zone": "zone1:host1",
"snapshot_id": 333,
"share_network_id": parent_share_net,
"mount_point_name": "fake_mp"
}
fake_share_net = {'id': parent_share_net}
share_net_subnets = [db_utils.create_share_network_subnet(
id='fake_subnet_id', share_network_id=fake_share_net['id'])]
create_mock = mock.Mock(return_value=stubs.stub_share('1',
display_name=shr['name'],
display_description=shr['description'],
size=shr['size'],
share_proto=shr['share_proto'].upper(),
snapshot_id=shr['snapshot_id'],
mount_point_name=shr['mount_point_name'],
instance=dict(
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_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=fake_share_net))
self.mock_object(
db, 'share_network_subnets_get_all_by_availability_zone_id',
mock.Mock(return_value=share_net_subnets))
body = {"share": copy.deepcopy(shr)}
req = fakes.HTTPRequest.blank('/v1/fake/shares', version='2.84')
res_dict = self.controller.create(req, body)
self.assertEqual(res_dict['share']['project_id'], 'fakeproject')
@ddt.data(
{'name': 'name1', 'description': 'x' * 256},
{'name': 'x' * 256, 'description': 'description1'},

View File

@ -61,17 +61,40 @@ class ShareTransferAPITestCase(test.TestCase):
size=1,
project_id='fake_project_id',
user_id='fake_user_id',
share_network_id=None):
share_network_id=None,
mount_point_name=None):
"""Create a share object."""
share_type = db_utils.create_share_type()
share = db_utils.create_share(display_name=display_name,
display_description=display_description,
status=status, size=size,
project_id=project_id,
user_id=user_id,
share_type_id=share_type['id'],
share_network_id=share_network_id
)
if mount_point_name:
instance_list = [
db_utils.create_share_instance(
status=status,
share_id='fake_id',
mount_point_name=mount_point_name
)
]
share = db_utils.create_share(
display_name=display_name,
display_description=display_description,
status=status, size=size,
project_id=project_id,
user_id=user_id,
share_type_id=share_type['id'],
share_network_id=share_network_id,
instances=instance_list
)
else:
share = db_utils.create_share(
display_name=display_name,
display_description=display_description,
status=status,
size=size,
project_id=project_id,
user_id=user_id,
share_type_id=share_type['id'],
share_network_id=share_network_id,
mount_point_name=mount_point_name
)
share_id = share['id']
return share_id
@ -232,6 +255,35 @@ class ShareTransferAPITestCase(test.TestCase):
self.assertRaises(webob.exc.HTTPBadRequest,
self.v2_controller.create, req, body)
def test_create_transfer_with_invalid_mount_point_name(self):
share_id = self._create_share(
project_id='fake_pid',
mount_point_name='fake_pid_mount_point_name')
body = {"transfer": {"name": "transfer1",
"share_id": share_id}}
db.share_update(context.get_admin_context(),
share_id, {'status': 'error'})
path = '/v2/fake_project_id/share-transfers'
req = fakes.HTTPRequest.blank(path, version=self.microversion)
req.environ['manila.context'] = self.ctxt
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = jsonutils.dumps(body).encode("utf-8")
self.assertRaises(webob.exc.HTTPBadRequest,
self.v2_controller.create, req, body)
def test_create_transfer_with_project_id_prefix_mount_point_name(self):
share_id = self._create_share(project_id='fake',
mount_point_name='fake_mp')
'''self.share_transfer_api.create(context.get_admin_context(),
share_id,
'test_missing_share_type')'''
self.assertRaises(exception.Invalid,
self.share_transfer_api.create,
context.get_admin_context(), share_id,
'test_missing_share_type')
def test_create_transfer_share_with_network_id(self):
share_id = self._create_share(share_network_id='fake_id')
body = {"transfer": {"name": "transfer1",

View File

@ -393,6 +393,7 @@ class ShareTypesAPITest(test.TestCase):
constants.ExtraSpecs.CREATE_SHARE_FROM_SNAPSHOT_SUPPORT: False,
constants.ExtraSpecs.REVERT_TO_SNAPSHOT_SUPPORT: True,
constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT: True,
constants.ExtraSpecs.MOUNT_POINT_NAME_SUPPORT: True,
}
now = timeutils.utcnow().isoformat()

View File

@ -94,7 +94,8 @@ def create_share(**kwargs):
'availability_zone': 'fake_availability_zone',
'status': constants.STATUS_CREATING,
'host': 'fake_host',
'is_soft_deleted': False
'is_soft_deleted': False,
'mount_point_name': 'fake_mp',
}
return _create_db_row(db.share_create, share, kwargs)
@ -112,7 +113,8 @@ def create_share_without_instance(**kwargs):
'availability_zone': 'fake_availability_zone',
'status': constants.STATUS_CREATING,
'host': 'fake_host',
'is_soft_deleted': False
'is_soft_deleted': False,
'mount_point_name': None,
}
share.update(copy.deepcopy(kwargs))
return db.share_create(context.get_admin_context(), share, False)

View File

@ -64,6 +64,7 @@ def fake_share_instance(base_share=None, **kwargs):
'share_network_id': 'fakesharenetworkid',
'share_server_id': 'fakeshareserverid',
'share_type_id': '1',
'mount_point_name': None,
}
for attr in models.ShareInstance._proxified_properties:
@ -82,7 +83,8 @@ def fake_share_type(**kwargs):
'is_public': False,
'extra_specs': {
'driver_handles_share_servers': 'False',
}
},
'mount_point_name_support': False
}
extra_specs = kwargs.pop('extra_specs', {})

View File

@ -55,7 +55,8 @@ SERVICE_STATES_NO_POOLS = {
create_share_from_snapshot_support=False,
revert_to_snapshot_support=True,
mount_snapshot_support=True,
driver_handles_share_servers=False),
driver_handles_share_servers=False,
mount_point_name_support=False),
'host2@back1': dict(share_backend_name='BBB',
total_capacity_gb=256, free_capacity_gb=100,
timestamp=None, reserved_percentage=0,
@ -68,7 +69,8 @@ SERVICE_STATES_NO_POOLS = {
create_share_from_snapshot_support=True,
revert_to_snapshot_support=False,
mount_snapshot_support=False,
driver_handles_share_servers=False),
driver_handles_share_servers=False,
mount_point_name_support=False),
'host2@back2': dict(share_backend_name='CCC',
total_capacity_gb=10000, free_capacity_gb=700,
timestamp=None, reserved_percentage=0,
@ -81,7 +83,8 @@ SERVICE_STATES_NO_POOLS = {
create_share_from_snapshot_support=True,
revert_to_snapshot_support=False,
mount_snapshot_support=False,
driver_handles_share_servers=False),
driver_handles_share_servers=False,
mount_point_name_support=False),
}
SHARE_SERVICES_WITH_POOLS = [
@ -124,7 +127,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0,
provisioned_capacity_gb=10,
max_over_subscription_ratio=1.0,
thin_provisioning=False)]),
thin_provisioning=False,
mount_point_name_support=False,
)]),
'host2@BBB': dict(share_backend_name='BBB',
timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0,
@ -142,7 +147,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0,
provisioned_capacity_gb=60,
max_over_subscription_ratio=2.0,
thin_provisioning=True)]),
thin_provisioning=True,
mount_point_name_support=False,
)]),
'host3@CCC': dict(share_backend_name='CCC',
timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0,
@ -160,7 +167,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0,
provisioned_capacity_gb=100,
max_over_subscription_ratio=20.0,
thin_provisioning=True)]),
thin_provisioning=True,
mount_point_name_support=False,
)]),
'host4@DDD': dict(share_backend_name='DDD',
timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0,
@ -178,7 +187,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0,
provisioned_capacity_gb=800,
max_over_subscription_ratio=2.0,
thin_provisioning=True),
thin_provisioning=True,
mount_point_name_support=False,
),
dict(pool_name='pool4b',
total_capacity_gb=542,
free_capacity_gb=442,
@ -187,7 +198,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0,
provisioned_capacity_gb=2000,
max_over_subscription_ratio=10.0,
thin_provisioning=True)]),
thin_provisioning=True,
mount_point_name_support=False,
)]),
'host5@EEE': dict(share_backend_name='EEE',
timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0,
@ -205,7 +218,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0,
provisioned_capacity_gb=100,
max_over_subscription_ratio=1.0,
thin_provisioning=False),
thin_provisioning=False,
mount_point_name_support=False,
),
dict(pool_name='pool5b',
total_capacity_gb=552,
free_capacity_gb=452,
@ -214,7 +229,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0,
provisioned_capacity_gb=100,
max_over_subscription_ratio=1.0,
thin_provisioning=False)]),
thin_provisioning=False,
mount_point_name_support=False,
)]),
'host6@FFF': dict(share_backend_name='FFF',
timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0,
@ -232,7 +249,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0,
provisioned_capacity_gb=100,
max_over_subscription_ratio=1.0,
thin_provisioning=False),
thin_provisioning=False,
mount_point_name_support=False,
),
dict(pool_name='pool6b',
total_capacity_gb='unknown',
free_capacity_gb='unknown',
@ -241,7 +260,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0,
provisioned_capacity_gb=100,
max_over_subscription_ratio=1.0,
thin_provisioning=False)]),
thin_provisioning=False,
mount_point_name_support=False,
)]),
}
FAKE_ACTIVE_IQ_WEIGHER_LIST = [

View File

@ -45,6 +45,24 @@ class HostFiltersTestCase(test.TestCase):
assertion = self.assertTrue if passes else self.assertFalse
assertion(self.filter.host_passes(host, filter_properties))
def test_mount_point_name_support_pass(self):
capabilities = {'mount_point_name_support': True}
service = {'disabled': False}
filter_properties = {
'resource_type': {
'request_spec': {
'share_properties': {
'mount_point_name': 'fake_mp',
}
}
}
}
host = fakes.FakeHostState('host1',
{'free_capacity_gb': 1024,
'capabilities': capabilities,
'service': service})
self.assertTrue(self.filter.host_passes(host, filter_properties))
def test_capability_filter_passes_extra_specs_simple(self):
self._do_test_type_filter_extra_specs(
ecaps={'opt1': '1', 'opt2': '2'},

View File

@ -217,6 +217,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
}, {
'name': 'host2@back1#BBB',
@ -250,6 +251,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
}, {
'name': 'host2@back2#CCC',
@ -283,6 +285,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
},
]
@ -338,6 +341,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
}, {
'name': 'host2@BBB#pool2',
@ -372,6 +376,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
}, {
'name': 'host3@CCC#pool3',
@ -406,6 +411,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
}, {
'name': 'host4@DDD#pool4a',
@ -440,6 +446,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
}, {
'name': 'host4@DDD#pool4b',
@ -474,6 +481,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
},
]
@ -541,6 +549,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
}, {
'name': 'host2@back1#BBB',
@ -574,6 +583,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
},
]
@ -635,6 +645,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False,
'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
},
},
]

View File

@ -114,6 +114,8 @@ class ContainerShareDriverTestCase(test.TestCase):
self.assertEqual('test-pool', self._driver._stats['pools'])
self.assertTrue(self._driver._stats['ipv4_support'])
self.assertFalse(self._driver._stats['ipv6_support'])
self.assertFalse(self._driver.
_stats['mount_point_name_support'])
def test_create_share(self):

View File

@ -166,6 +166,7 @@ class EMCShareFrameworkTestCase(test.TestCase):
data['replication_domain'] = None
data['filter_function'] = None
data['goodness_function'] = None
data['mount_point_name_support'] = False
data['snapshot_support'] = True
data['create_share_from_snapshot_support'] = True
data['ipv4_support'] = True

View File

@ -181,6 +181,9 @@ class DummyDriver(driver.ShareDriver):
))
def _get_share_name(self, share):
mount_point_name = share.get('mount_point_name')
if mount_point_name is not None:
return mount_point_name
return "share_%(s_id)s_%(si_id)s" % {
"s_id": share["share_id"].replace("-", "_"),
"si_id": share["id"].replace("-", "_")}
@ -516,6 +519,7 @@ class DummyDriver(driver.ShareDriver):
"consistent_snapshot_support": "pool",
},
'share_server_multiple_subnet_support': True,
'mount_point_name_support': True,
}
if self.configuration.replication_domain:
data["replication_type"] = "readable"

View File

@ -268,6 +268,7 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase):
'replication_domain': None,
'filter_function': None,
'goodness_function': None,
'mount_point_name_support': False,
'ipv4_support': True,
'ipv6_support': False,
'security_service_update_support': False,

View File

@ -746,6 +746,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'replication_domain': None,
'filter_function': None,
'goodness_function': None,
'mount_point_name_support': False,
'ipv4_support': True,
'ipv6_support': False,
'max_share_server_size': -1,
@ -839,6 +840,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'replication_domain': None,
'filter_function': None,
'goodness_function': None,
'mount_point_name_support': False,
'ipv4_support': True,
'ipv6_support': False,
}
@ -890,6 +892,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'replication_domain': None,
'filter_function': None,
'goodness_function': None,
'mount_point_name_support': False,
'ipv4_support': True,
'ipv6_support': False,
}

View File

@ -2431,6 +2431,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
"replication_domain": None,
"filter_function": None,
"goodness_function": None,
'mount_point_name_support': False,
"pools": [],
"share_group_stats": {"consistent_snapshot_support": None},
"ipv4_support": True,

View File

@ -186,6 +186,8 @@ class LVMShareDriverTestCase(test.TestCase):
CONF.lvm_share_volume_group, 0, 0]
self.mock_object(privsep_common, 'execute_with_retries')
self.mock_object(filesystem, 'make_filesystem')
self.mock_object(self._driver, '_get_mount_point_name',
mock.Mock(return_value=self.share['name']))
ret = self._driver.create_share(self._context, self.share,
self.share_server)
@ -222,6 +224,8 @@ class LVMShareDriverTestCase(test.TestCase):
self.mock_object(filesystem, 'make_filesystem')
self.mock_object(filesystem, 'e2fsck')
self.mock_object(filesystem, 'tune2fs')
self.mock_object(self._driver, '_get_mount_point_name',
mock.Mock(return_value=self.share['name']))
self._driver.create_share_from_snapshot(self._context,
self.share,
@ -256,6 +260,8 @@ class LVMShareDriverTestCase(test.TestCase):
self._driver._mount_device = mock.Mock()
self.mock_object(privsep_common, 'execute_with_retries')
self.mock_object(filesystem, 'make_filesystem')
self.mock_object(self._driver, '_get_mount_point_name',
mock.Mock(return_value=self.share['name']))
ret = self._driver.create_share(self._context, share,
self.share_server)
@ -402,6 +408,8 @@ class LVMShareDriverTestCase(test.TestCase):
def test_ensure_share(self):
device_name = '/dev/mapper/fakevg-fakename'
self.mock_object(self._driver, '_get_mount_point_name',
mock.Mock(return_value=self.share['name']))
with mock.patch.object(self._driver,
'_mount_device',
mock.Mock(return_value='fake_location')):
@ -413,6 +421,8 @@ class LVMShareDriverTestCase(test.TestCase):
self.server, self.share['name'], recreate=True)
def test_delete_share(self):
self.mock_object(self._driver, '_get_mount_point_name',
mock.Mock(return_value=self.share['name']))
mount_path = self._get_mount_path(self.share)
self._helper_nfs.remove_export(mount_path, self.share['name'])
self._driver._delete_share(self._context, self.share)
@ -437,6 +447,8 @@ class LVMShareDriverTestCase(test.TestCase):
self.mock_object(self._driver, '_deallocate_container')
self._driver._get_helper = mock.Mock(
side_effect=exception.InvalidShare(reason='fake'))
self.mock_object(self._driver, '_get_mount_point_name',
mock.Mock(return_value=self.share['name']))
self._driver.delete_share(self._context, self.share, self.share_server)
@ -450,6 +462,8 @@ class LVMShareDriverTestCase(test.TestCase):
self._helper_nfs,
'remove_export',
mock.Mock(side_effect=exception.ProcessExecutionError))
self.mock_object(self._driver, '_get_mount_point_name',
mock.Mock(return_value=self.share['name']))
self._driver._delete_share(self._context, self.share)
self._helper_nfs.remove_exports.assert_called_once_with(
@ -464,6 +478,8 @@ class LVMShareDriverTestCase(test.TestCase):
'2.2.2.2', access_level), ]
delete_rules = [test_generic.get_fake_access_rule(
'3.3.3.3', access_level), ]
self.mock_object(self._driver, '_get_mount_point_name',
mock.Mock(return_value=self.share['name']))
self._driver.update_access(self._context, self.share, access_rules,
add_rules=add_rules,
delete_rules=delete_rules,
@ -609,6 +625,7 @@ class LVMShareDriverTestCase(test.TestCase):
'reserved_percentage': 0,
'reserved_snapshot_percentage': 0,
'reserved_share_extend_percentage': 0,
'mount_point_name_support': True,
}, ]
out, err = "VSize 33g VFree 22g", None
self.mock_object(
@ -669,6 +686,8 @@ class LVMShareDriverTestCase(test.TestCase):
mock_get_local_path = self.mock_object(
self._driver, '_get_local_path',
mock.Mock(side_effect=[share_local_path, snapshot_local_path]))
self.mock_object(self._driver, '_get_mount_point_name',
mock.Mock(return_value=self.snapshot['name']))
snapshot_parent_share = self.snapshot['share']
self._driver.revert_to_snapshot(self._context, self.snapshot,
@ -784,3 +803,28 @@ class LVMShareDriverTestCase(test.TestCase):
{'export_ips': ','.join(self.server['public_addresses']),
'db_version': mock.ANY},
backend_info)
def test_get_mount_point_name_with_mount_point_name(self):
share = {'mount_point_name': 'fake_mp_name', 'name': 'fakename'}
result = self._driver._get_mount_point_name(share)
self.assertEqual(result, 'fake_mp_name')
def test_get_mount_point_name_without_mount_point_name(self):
share = {'name': 'fakename'}
result = self._driver._get_mount_point_name(share)
self.assertEqual(result, 'fakename')
def test_get_mount_point_name_with_empty_mount_point_name(self):
share = {'mount_point_name': '', 'name': 'fakename'}
result = self._driver._get_mount_point_name(share)
self.assertEqual(result, 'fakename')
def test_get_mount_point_name_with_none_mount_point_name(self):
share = {'mount_point_name': None, 'name': 'fakename'}
result = self._driver._get_mount_point_name(share)
self.assertEqual(result, 'fakename')
def test_get_mount_point_name_without_name(self):
share = {'mount_point_name': 'fake_mp_name'}
result = self._driver._get_mount_point_name(share)
self.assertEqual(result, 'fake_mp_name')

View File

@ -438,6 +438,7 @@ class ACCESSShareDriverTestCase(test.TestCase):
'driver_handles_share_servers': False,
'filter_function': 'Disable',
'goodness_function': 'Disable',
'mount_point_name_support': False,
'ipv4_support': True,
'ipv6_support': False,
'mount_snapshot_support': False,

View File

@ -376,6 +376,7 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
'vendor_name': 'Open Source',
'filter_function': None,
'goodness_function': None,
'mount_point_name_support': False,
'ipv4_support': True,
'ipv6_support': False,
'security_service_update_support': False,

View File

@ -166,7 +166,10 @@ class ShareAPITestCase(test.TestCase):
share_instance = db_utils.create_share_instance(
share_id=share['id'],
share_type_id=share_type_id)
share_type = {'fake': 'fake'}
share_type = {
'fake': 'fake',
'mount_point_name_support': False
}
self.mock_object(db_api, 'share_instance_create',
mock.Mock(return_value=share_instance))
self.mock_object(db_api, 'share_type_get',
@ -801,7 +804,7 @@ class ShareAPITestCase(test.TestCase):
availability_zones=expected_azs,
az_request_multiple_subnet_support_map=compatible_azs_multiple,
snapshot_host=None,
scheduler_hints=None
scheduler_hints=None, mount_point_name=None,
)
db_api.share_get.assert_called_once()
@ -840,6 +843,85 @@ class ShareAPITestCase(test.TestCase):
get_all_azs_sns.assert_called_once_with(
self.context, fake_share_network_id)
def test_prefix_with_missing_extra_spec_mount_point_name_support(self):
share, share_data = self._setup_create_mocks(is_public=True)
az = share_data.pop('availability_zone')
extra_specs = {'replication_type': 'readable',
'mount_point_name_support': False}
self.mock_object(
self.api, 'get_share_attributes_from_share_type',
mock.Mock(return_value=extra_specs))
self.assertRaises(
exception.InvalidInput,
self.api.create,
self.context, share_data['share_proto'], share_data['size'],
share_data['display_name'], share_data['display_description'],
availability_zones=az,
mount_point_name='fake_mp')
def test_prefix_with_valid_mount_point_name(self):
share_type = {
'extra_specs': {
constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX: 'prefix',
}
}
self.context.project_id = 'project_id'
mount_point_name = 'mount_point'
result = self.api._prefix_mount_point_name(
share_type, self.context, mount_point_name
)
self.assertEqual(result, 'prefix_mount_point')
def test_prefix_with_valid_missing_extra_spec_mount_point_name(self):
share_type = {
'extra_specs': {},
}
self.context.project_id = 'project_id'
mount_point_name = 'mount_point'
result = self.api._prefix_mount_point_name(
share_type, self.context, mount_point_name
)
self.assertEqual(result, 'project_id_mount_point')
def test_prefix_with_invalid_mount_point_name(self):
share_type = \
{
'extra_specs':
{
constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX:
'prefix',
}
}
self.context.project_id = 'project_id'
mount_point_name = 'invalid*name'
self.assertRaises(
exception.InvalidInput,
self.api._prefix_mount_point_name,
share_type,
self.context,
mount_point_name
)
def test_prefix_with_too_long_mount_point_name(self):
share_type = \
{
'extra_specs':
{
constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX:
'prefix',
}
}
self.context.project_id = 'project_id'
mount_point_name = 'a' * 256
self.assertRaises(
exception.InvalidInput,
self.api._prefix_mount_point_name,
share_type,
self.context,
mount_point_name
)
@ddt.data(
None, '', 'fake', 'nfsfake', 'cifsfake', 'glusterfsfake', 'hdfsfake')
def test_create_share_invalid_protocol(self, proto):
@ -984,6 +1066,7 @@ class ShareAPITestCase(test.TestCase):
'availability_zone_id': 'fake_id',
'share_type_id': 'fake_share_type',
'cast_rules_to_readonly': False,
'mount_point_name': None,
}
)
db_api.share_type_get.assert_called_once_with(
@ -999,6 +1082,28 @@ class ShareAPITestCase(test.TestCase):
self.assertFalse(
self.api.scheduler_rpcapi.create_share_instance.called)
def test_create_share_instance_with_mount_point_name(self):
host, share, share_instance = self._setup_create_instance_mocks()
self.api.create_instance(self.context, share, host=host,
availability_zone='fake',
share_type_id='fake_share_type',
mount_point_name='fake_mp')
db_api.share_instance_create.assert_called_once_with(
self.context, share['id'],
{
'share_network_id': None,
'status': constants.STATUS_CREATING,
'scheduled_at': self.dt_utc,
'host': host,
'availability_zone_id': 'fake_id',
'share_type_id': 'fake_share_type',
'cast_rules_to_readonly': False,
'mount_point_name': 'fake_mp',
}
)
def test_create_share_instance_without_host(self):
_, share, share_instance = self._setup_create_instance_mocks()
@ -1072,6 +1177,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'replication_type': 'dr',
}
}
@ -1090,6 +1196,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'replication_type': None,
}
self.assertEqual(expected, result)
@ -1140,6 +1247,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': dhss,
},
}
@ -1176,6 +1284,8 @@ class ShareAPITestCase(test.TestCase):
fake_type['extra_specs']['revert_to_snapshot_support'],
'mount_snapshot_support':
fake_type['extra_specs']['mount_snapshot_support'],
'mount_point_name_support':
fake_type['extra_specs']['mount_point_name_support'],
'replication_type': replication_type,
})
@ -1231,6 +1341,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': dhss,
},
}
@ -1278,6 +1389,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': True,
},
}
@ -1329,6 +1441,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': True,
},
}
@ -1412,6 +1525,9 @@ class ShareAPITestCase(test.TestCase):
'mount_snapshot_support': kwargs.get(
'mount_snapshot_support',
share_type['extra_specs'].get('mount_snapshot_support')),
'mount_point_name_support': kwargs.get(
'mount_point_name_support',
share_type['extra_specs'].get('mount_point_name_support')),
'share_proto': kwargs.get('share_proto', share.get('share_proto')),
'share_type_id': share_type['id'],
'is_public': kwargs.get('is_public', share.get('is_public')),
@ -2356,7 +2472,7 @@ class ShareAPITestCase(test.TestCase):
availability_zones=None,
az_request_multiple_subnet_support_map=None,
snapshot_host=snapshot['share']['instance']['host'],
scheduler_hints=None)
scheduler_hints=None, mount_point_name=None)
share_api.policy.check_policy.assert_called_once_with(
self.context, 'share_snapshot', 'get_snapshot')
quota.QUOTAS.reserve.assert_called_once_with(
@ -3508,6 +3624,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': dhss,
},
}
@ -3520,6 +3637,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': dhss,
'availability_zones': 'fake_az1,fake_az2',
},
@ -3603,6 +3721,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': 'true',
'availability_zones': 'fake_az3'
},
@ -3615,6 +3734,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False,
'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': 'true',
'availability_zones': 'fake_az1,fake_az2',
},

View File

@ -144,6 +144,7 @@ class ShareDriverTestCase(test.TestCase):
'reserved_share_extend_percentage',
'vendor_name', 'storage_protocol',
'snapshot_support', 'mount_snapshot_support',
'mount_point_name_support',
]
share_driver = driver.ShareDriver(True, configuration=conf)
fake_stats = {'fake_key': 'fake_value'}

View File

@ -406,14 +406,18 @@ class ShareTypesTestCase(test.TestCase):
(constants.ExtraSpecs.SNAPSHOT_SUPPORT,
constants.ExtraSpecs.CREATE_SHARE_FROM_SNAPSHOT_SUPPORT,
constants.ExtraSpecs.REVERT_TO_SNAPSHOT_SUPPORT,
constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT),
constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT,
constants.ExtraSpecs.MOUNT_POINT_NAME_SUPPORT),
strutils.TRUE_STRINGS + strutils.FALSE_STRINGS)) +
list(itertools.product(
(constants.ExtraSpecs.REPLICATION_TYPE_SPEC,),
constants.ExtraSpecs.REPLICATION_TYPES)) +
[(constants.ExtraSpecs.AVAILABILITY_ZONES, 'zone a, zoneb$c'),
(constants.ExtraSpecs.AVAILABILITY_ZONES, ' zonea, zoneb'),
(constants.ExtraSpecs.AVAILABILITY_ZONES, 'zone1')]
(constants.ExtraSpecs.AVAILABILITY_ZONES, 'zone1')] +
[(constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX, 'gold'),
(constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX, 'silver'),
(constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX, 'bronze')]
))
@ddt.unpack
def test_is_valid_optional_extra_spec_valid(self, key, value):
@ -422,6 +426,18 @@ class ShareTypesTestCase(test.TestCase):
self.assertTrue(result)
def test_valid_string(self):
self.assertTrue(share_types.is_valid_string("This is a valid string"))
def test_empty_string(self):
self.assertFalse(share_types.is_valid_string(""))
def test_string_too_long(self):
self.assertFalse(share_types.is_valid_string("a" * 256))
def test_non_string_input(self):
self.assertFalse(share_types.is_valid_string(123))
def test_is_valid_optional_extra_spec_valid_unknown_key(self):
result = share_types.is_valid_optional_extra_spec('fake', 'fake')

View File

@ -168,6 +168,17 @@ class API(base.Base):
policy.check_policy(context, "share_transfer", "create",
target_obj=share_ref)
share_instance = share_ref['instance']
mount_point_name = share_instance['mount_point_name']
if (mount_point_name and
mount_point_name.startswith(share_ref['project_id'])):
msg = _('Share %s has a custom mount_point_name %s.'
' This has the project_id encoded in it.'
' Transferring such'
' a share isn\'t supported') % (share_ref['name'],
mount_point_name)
raise exception.Invalid(reason=msg)
if share_ref['status'] != "available":
raise exception.InvalidShare(reason=_("Share's status must be "
"available"))

View File

@ -0,0 +1,12 @@
---
features:
- A human readable ``mount_point_name`` can now be specified
while creating shares through the mount_point_name parameter.
Manila will prepend a prefix to the mount point name which
can be configured through the ``provisioning:mount_point_prefix``
share type extra spec. In case this extra spec is not available
in the share type, Manila will prepend a project identification
to the mount point name. Project id will be added to this friendly
name ``provisioning:mount_point_prefix`` share type is not
provided during provisioning. The LVM driver now supports
human readable export locations.