Human readable export location core implementation

Export locations are usually too difficult to memo
rize.Currently, there is no way to determine the
export location before the share is created, so
users wait until the share creation request gets
completed, and then they check the export
locations to mount the share. The generated
export locations are often not human readable
 and it is hard to memorize and control them.

Implements: bp/human-readable-export-locations
Change-Id: I72ac7e24ddd4330d76cafd5e7f78bac2b0174883
This commit is contained in:
jayaanand.borra@netapp.com 2024-02-13 17:17:34 +05:30 committed by jayaanand borra
parent f4c77bfe71
commit ea1ac5f448
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 this extra-spec, the share type is assumed to be serviceable in all
availability zones known to the Shared File Systems service. 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: 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 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 is ignored for regular users and the "provisioning:max_share_size" is the
only effective limit. 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` 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 | | 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 | \- | \- | | ZFSonLinux | \- | M | M | M | M | \- | \- | M | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Container | N | \- | \- | \- | \- | N | \- | \- | \- | \- | P | \- | Y | | Container | N | \- | \- | \- | \- | N | \- | \- | \- | \- | P | \- | Y | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Generic (Cinder as back-end) | J | K | \- | \- | \- | L | \- | J | \- | \- | P | \- | \- | | 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 | \- | | NetApp Clustered Data ONTAP | J | K | M | M | M | L | P | J | O | \- | P | Q | \- | Y |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Dell EMC PowerMax | O | \- | \- | \- | \- | \- | \- | O | \- | \- | P | R | \- | | Dell EMC PowerMax | O | \- | \- | \- | \- | \- | \- | O | \- | \- | P | R | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| EMC VNX | J | \- | \- | \- | \- | L | \- | J | \- | \- | P | Q | \- | | EMC VNX | J | \- | \- | \- | \- | L | \- | J | \- | \- | P | Q | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| EMC Unity | N | T | \- | \- | N | \- | \- | N | S | \- | P | Q | \- | | EMC Unity | N | T | \- | \- | N | \- | \- | N | S | \- | P | Q | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| EMC Isilon | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- | | EMC Isilon | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Dell EMC PowerStore | \- | B | \- | \- | B | \- | \- | B | B | \- | B | \- | \- | | Dell EMC PowerStore | \- | B | \- | \- | B | \- | \- | B | B | \- | B | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Dell EMC PowerFlex | \- | B | \- | \- | B | \- | \- | \- | \- | \- | B | \- | \- | | Dell EMC PowerFlex | \- | B | \- | \- | B | \- | \- | \- | \- | \- | B | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| GlusterFS | \- | J | \- | \- | \- | L | \- | volume layout (L) | \- | \- | P | \- | \- | | GlusterFS | \- | J | \- | \- | \- | L | \- | volume layout (L) | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| GlusterFS-Native | \- | J | \- | \- | \- | L | \- | L | \- | \- | P | \- | \- | | GlusterFS-Native | \- | J | \- | \- | \- | L | \- | L | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| HDFS | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- | | HDFS | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Hitachi HNAS | \- | L | N | \- | L | \- | \- | L | O | O | P | \- | \- | | Hitachi HNAS | \- | L | N | \- | L | \- | \- | L | O | O | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Hitachi HSP | \- | N | \- | \- | N | \- | \- | \- | \- | \- | P | \- | \- | | Hitachi HSP | \- | N | \- | \- | N | \- | \- | \- | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| HPE 3PAR | L | K | L | \- | L | L | \- | K | \- | \- | P | \- | \- | | HPE 3PAR | L | K | L | \- | L | L | \- | K | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Huawei | M | K | L | L | L | L | M | M | \- | \- | P | \- | \- | | Huawei | M | K | L | L | L | L | M | M | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| INFINIDAT | \- | Q | \- | \- | Q | Q | \- | Q | Q | Q | Q | \- | \- | | INFINIDAT | \- | Q | \- | \- | Q | Q | \- | Q | Q | Q | Q | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Infortrend | \- | T | \- | \- | \- | \- | \- | \- | \- | \- | T | \- | \- | | Infortrend | \- | T | \- | \- | \- | \- | \- | \- | \- | \- | T | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| LVM | \- | M | \- | \- | \- | M | \- | K | O | O | P | P | \- | | LVM | \- | M | \- | \- | \- | M | \- | K | O | O | P | P | \- | Y |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Macrosan | \- | Z | \- | \- | \- | Z | \- | \- | \- | \- | Z | \- | \- | | Macrosan | \- | Z | \- | \- | \- | Z | \- | \- | \- | \- | Z | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Quobyte | \- | K | \- | \- | \- | L | \- | M | \- | \- | P | \- | \- | | Quobyte | \- | K | \- | \- | \- | L | \- | M | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Windows SMB | L | L | \- | \- | \- | L | \- | \- | \- | \- | P | \- | \- | | Windows SMB | L | L | \- | \- | \- | L | \- | \- | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| IBM GPFS | \- | K | \- | \- | \- | L | \- | L | \- | \- | P | \- | \- | | IBM GPFS | \- | K | \- | \- | \- | L | \- | L | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Oracle ZFSSA | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- | | Oracle ZFSSA | \- | K | \- | \- | \- | L | \- | K | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| CephFS | \- | M | \- | \- | \- | M | \- | \- | \- | \- | P | \- | \- | | CephFS | \- | M | \- | \- | \- | M | \- | \- | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Tegile | \- | M | M | M | M | \- | \- | M | \- | \- | P | \- | \- | | Tegile | \- | M | M | M | M | \- | \- | M | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| NexentaStor4 | \- | N | N | N | N | N | \- | N | \- | \- | P | \- | \- | | NexentaStor4 | \- | N | N | N | N | N | \- | N | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| NexentaStor5 | \- | N | \- | N | N | N | \- | N | T | \- | P | \- | \- | | NexentaStor5 | \- | N | \- | N | N | N | \- | N | T | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| MapRFS | \- | N | \- | \- | \- | N | \- | O | \- | \- | P | \- | \- | | MapRFS | \- | N | \- | \- | \- | N | \- | O | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| QNAP | \- | O | Q | Q | O | Q | \- | O | \- | \- | P | \- | \- | | QNAP | \- | O | Q | Q | O | Q | \- | O | \- | \- | P | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| INSPUR AS13000 | \- | R | \- | \- | R | \- | \- | R | \- | \- | R | \- | \- | | INSPUR AS13000 | \- | R | \- | \- | R | \- | \- | R | \- | \- | R | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| INSPUR InStorage | \- | T | \- | \- | \- | T | \- | \- | \- | \- | T | \- | \- | | INSPUR InStorage | \- | T | \- | \- | \- | T | \- | \- | \- | \- | T | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
| Pure Storage FlashBlade | \- | X | \- | \- | X | \- | \- | \- | X | \- | X | \- | \- | | Pure Storage FlashBlade | \- | X | \- | \- | X | \- | \- | \- | X | \- | X | \- | \- | \- |
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+ +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+-------------------------+--------------------------+
.. note:: .. note::

View File

@ -201,13 +201,14 @@ REST_API_VERSION_HISTORY = """
* 2.81 - Added API methods, endpoint /resource-locks. * 2.81 - Added API methods, endpoint /resource-locks.
* 2.82 - Added lock and restriction to share access rules. * 2.82 - Added lock and restriction to share access rules.
* 2.83 - Added 'disabled_reason' field to services. * 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 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.83" _MAX_API_VERSION = "2.84"
DEFAULT_API_VERSION = _MIN_API_VERSION 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 ``disabled_reason`` field was added to the service to mark the reason why
the user disabled the service. ``disabled`` field will be replaced by the user disabled the service. ``disabled`` field will be replaced by
``status`` field. ``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 webob import exc
from manila.api import common 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.openstack import wsgi
from manila.api.views import share_accesses as share_access_views from manila.api.views import share_accesses as share_access_views
from manila.api.views import shares as share_views from manila.api.views import shares as share_views
@ -445,6 +446,9 @@ class ShareMixin(object):
kwargs['scheduler_hints'] = scheduler_hints 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, new_share = self.share_api.create(context,
share_proto, share_proto,
size, size,

View File

@ -300,10 +300,12 @@ class ExtraSpecs(object):
CREATE_SHARE_FROM_SNAPSHOT_SUPPORT = "create_share_from_snapshot_support" CREATE_SHARE_FROM_SNAPSHOT_SUPPORT = "create_share_from_snapshot_support"
REVERT_TO_SNAPSHOT_SUPPORT = "revert_to_snapshot_support" REVERT_TO_SNAPSHOT_SUPPORT = "revert_to_snapshot_support"
MOUNT_SNAPSHOT_SUPPORT = "mount_snapshot_support" MOUNT_SNAPSHOT_SUPPORT = "mount_snapshot_support"
MOUNT_POINT_NAME_SUPPORT = "mount_point_name_support"
AVAILABILITY_ZONES = "availability_zones" AVAILABILITY_ZONES = "availability_zones"
PROVISIONING_MAX_SHARE_SIZE = "provisioning:max_share_size" PROVISIONING_MAX_SHARE_SIZE = "provisioning:max_share_size"
PROVISIONING_MIN_SHARE_SIZE = "provisioning:min_share_size" PROVISIONING_MIN_SHARE_SIZE = "provisioning:min_share_size"
PROVISIONING_MAX_SHARE_EXTEND_SIZE = "provisioning:max_share_extend_size" PROVISIONING_MAX_SHARE_EXTEND_SIZE = "provisioning:max_share_extend_size"
PROVISIONING_MOUNT_POINT_PREFIX = "provisioning:mount_point_prefix"
# Extra specs containers # Extra specs containers
REQUIRED = ( REQUIRED = (
@ -316,10 +318,12 @@ class ExtraSpecs(object):
REVERT_TO_SNAPSHOT_SUPPORT, REVERT_TO_SNAPSHOT_SUPPORT,
REPLICATION_TYPE_SPEC, REPLICATION_TYPE_SPEC,
MOUNT_SNAPSHOT_SUPPORT, MOUNT_SNAPSHOT_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,
) )
# NOTE(cknight): Some extra specs are necessary parts of the Manila API and # 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', 'status', 'host', 'scheduled_at', 'launched_at', 'terminated_at',
'share_server_id', 'share_network_id', 'availability_zone_id', 'share_server_id', 'share_network_id', 'availability_zone_id',
'replica_state', 'share_type_id', 'share_type', 'access_rules_status', 'replica_state', 'share_type_id', 'share_type', 'access_rules_status',
'mount_point_name',
] ]
share_instance_values, share_values = ( share_instance_values, share_values = (
_extract_subdict_by_fields(values, share_instance_model_fields) _extract_subdict_by_fields(values, share_instance_model_fields)

View File

@ -371,7 +371,7 @@ class ShareInstance(BASE, ManilaBase):
host = Column(String(255)) host = Column(String(255))
status = Column(String(255)) status = Column(String(255))
progress = Column(String(32)) progress = Column(String(32))
mount_point_name = Column(String(255))
ACCESS_STATUS_PRIORITIES = { ACCESS_STATUS_PRIORITIES = {
constants.STATUS_ACTIVE: 0, constants.STATUS_ACTIVE: 0,
constants.SHARE_INSTANCE_RULES_SYNCING: 1, 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): def host_passes(self, host_state, filter_properties):
"""Return a list of hosts that can create resource_type.""" """Return a list of hosts that can create resource_type."""
resource_type = filter_properties.get('resource_type') resource_type = filter_properties.get('resource_type')
if not self._satisfies_extra_specs(host_state.capabilities, if not self._satisfies_extra_specs(host_state.capabilities,
resource_type): resource_type):
LOG.debug("%(host_state)s fails resource_type extra_specs " 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.security_service_update_support = False
self.network_allocation_update_support = False self.network_allocation_update_support = False
self.share_server_multiple_subnet_support = False self.share_server_multiple_subnet_support = False
self.mount_point_name_support = False
# PoolState for all pools # PoolState for all pools
self.pools = {} self.pools = {}

View File

@ -66,7 +66,9 @@ def generate_stats(host_state, properties):
'network_allocation_update_support': ( 'network_allocation_update_support': (
host_state.network_allocation_update_support), host_state.network_allocation_update_support),
'share_server_multiple_subnet_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 host_caps = host_state.capabilities

View File

@ -21,6 +21,7 @@ Handles all requests relating to shares.
""" """
import functools import functools
import json import json
import re
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log from oslo_log import log
@ -270,7 +271,8 @@ class API(base.Base):
share_network_id=None, share_type=None, is_public=False, share_network_id=None, share_type=None, is_public=False,
share_group_id=None, share_group_snapshot_member=None, share_group_id=None, share_group_snapshot_member=None,
availability_zones=None, scheduler_hints=None, availability_zones=None, scheduler_hints=None,
az_request_multiple_subnet_support_map=None): az_request_multiple_subnet_support_map=None,
mount_point_name=None):
"""Create new share.""" """Create new share."""
api_common.check_metadata_properties(metadata) api_common.check_metadata_properties(metadata)
@ -348,6 +350,18 @@ class API(base.Base):
deltas = {'shares': 1, 'gigabytes': size} deltas = {'shares': 1, 'gigabytes': size}
share_type_attributes = self.get_share_attributes_from_share_type( share_type_attributes = self.get_share_attributes_from_share_type(
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( share_type_supports_replication = share_type_attributes.get(
'replication_type', None) 'replication_type', None)
if share_type_supports_replication: if share_type_supports_replication:
@ -482,7 +496,8 @@ class API(base.Base):
share_type_id=share_type_id, availability_zones=availability_zones, share_type_id=share_type_id, availability_zones=availability_zones,
snapshot_host=snapshot_host, scheduler_hints=scheduler_hints, snapshot_host=snapshot_host, scheduler_hints=scheduler_hints,
az_request_multiple_subnet_support_map=( az_request_multiple_subnet_support_map=(
az_request_multiple_subnet_support_map)) az_request_multiple_subnet_support_map),
mount_point_name=mount_point_name)
# Retrieve the share with instance details # Retrieve the share with instance details
share = self.db.share_get(context, share['id']) share = self.db.share_get(context, share['id'])
@ -505,6 +520,9 @@ class API(base.Base):
constants.ExtraSpecs.REVERT_TO_SNAPSHOT_SUPPORT) constants.ExtraSpecs.REVERT_TO_SNAPSHOT_SUPPORT)
mount_snapshot_support_key = ( mount_snapshot_support_key = (
constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT) 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) snapshot_support_default = inferred_map.get(snapshot_support_key)
create_share_from_snapshot_support_default = inferred_map.get( create_share_from_snapshot_support_default = inferred_map.get(
@ -513,6 +531,7 @@ class API(base.Base):
revert_to_snapshot_key) revert_to_snapshot_key)
mount_snapshot_support_default = inferred_map.get( mount_snapshot_support_default = inferred_map.get(
constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT) constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT)
mount_point_name_support_default = False
if share_type: if share_type:
snapshot_support = share_types.parse_boolean_extra_spec( snapshot_support = share_types.parse_boolean_extra_spec(
@ -536,6 +555,11 @@ class API(base.Base):
'extra_specs', {}).get( 'extra_specs', {}).get(
mount_snapshot_support_key, mount_snapshot_support_key,
mount_snapshot_support_default)) 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 = share_type.get('extra_specs', {}).get(
'replication_type') 'replication_type')
else: else:
@ -544,6 +568,7 @@ class API(base.Base):
create_share_from_snapshot_support_default) create_share_from_snapshot_support_default)
revert_to_snapshot_support = revert_to_snapshot_support_default revert_to_snapshot_support = revert_to_snapshot_support_default
mount_snapshot_support = mount_snapshot_support_default mount_snapshot_support = mount_snapshot_support_default
mount_point_name_support = mount_point_name_support_default
replication_type = None replication_type = None
return { return {
@ -553,6 +578,7 @@ class API(base.Base):
'revert_to_snapshot_support': revert_to_snapshot_support, 'revert_to_snapshot_support': revert_to_snapshot_support,
'replication_type': replication_type, 'replication_type': replication_type,
'mount_snapshot_support': mount_snapshot_support, 'mount_snapshot_support': mount_snapshot_support,
'mount_point_name_support': mount_point_name_support,
} }
def create_instance(self, context, share, share_network_id=None, 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_group=None, share_group_snapshot_member=None,
share_type_id=None, availability_zones=None, share_type_id=None, availability_zones=None,
snapshot_host=None, scheduler_hints=None, snapshot_host=None, scheduler_hints=None,
az_request_multiple_subnet_support_map=None): az_request_multiple_subnet_support_map=None,
mount_point_name=None):
request_spec, share_instance = ( request_spec, share_instance = (
self.create_share_instance_and_get_request_spec( self.create_share_instance_and_get_request_spec(
context, share, availability_zone=availability_zone, context, share, availability_zone=availability_zone,
@ -570,7 +597,8 @@ class API(base.Base):
availability_zones=availability_zones, availability_zones=availability_zones,
snapshot_host=snapshot_host, snapshot_host=snapshot_host,
az_request_multiple_subnet_support_map=( az_request_multiple_subnet_support_map=(
az_request_multiple_subnet_support_map))) az_request_multiple_subnet_support_map),
mount_point_name=mount_point_name))
if share_group_snapshot_member: if share_group_snapshot_member:
# Inherit properties from the share_group_snapshot_member # Inherit properties from the share_group_snapshot_member
@ -613,7 +641,8 @@ class API(base.Base):
share_group=None, host=None, share_network_id=None, share_group=None, host=None, share_network_id=None,
share_type_id=None, cast_rules_to_readonly=False, share_type_id=None, cast_rules_to_readonly=False,
availability_zones=None, snapshot_host=None, availability_zones=None, snapshot_host=None,
az_request_multiple_subnet_support_map=None): az_request_multiple_subnet_support_map=None,
mount_point_name=None):
availability_zone_id = None availability_zone_id = None
if availability_zone: if availability_zone:
@ -633,6 +662,7 @@ class API(base.Base):
'availability_zone_id': availability_zone_id, 'availability_zone_id': availability_zone_id,
'share_type_id': share_type_id, 'share_type_id': share_type_id,
'cast_rules_to_readonly': cast_rules_to_readonly, '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'], 'host': share_instance['host'],
'status': share_instance['status'], 'status': share_instance['status'],
'replica_state': share_instance['replica_state'], '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 share_type = None
@ -686,7 +716,7 @@ class API(base.Base):
'availability_zone_id': availability_zone_id, 'availability_zone_id': availability_zone_id,
'availability_zones': availability_zones, 'availability_zones': availability_zones,
'az_request_multiple_subnet_support_map': ( 'az_request_multiple_subnet_support_map': (
az_request_multiple_subnet_support_map), az_request_multiple_subnet_support_map)
} }
return request_spec, share_instance return request_spec, share_instance
@ -1063,6 +1093,11 @@ class API(base.Base):
share_type.get('extra_specs', {}).get( share_type.get('extra_specs', {}).get(
'mount_snapshot_support') '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_proto': kwargs.get('share_proto', share.get('share_proto')),
'share_type_id': share_type['id'], 'share_type_id': share_type['id'],
'is_public': kwargs.get('is_public', share.get('is_public')), 'is_public': kwargs.get('is_public', share.get('is_public')),
@ -1094,6 +1129,25 @@ class API(base.Base):
} }
return request_spec 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') @prevent_locked_action_on_share('delete')
def unmanage(self, context, share): def unmanage(self, context, share):
policy.check_policy(context, 'share', 'unmanage') policy.check_policy(context, 'share', 'unmanage')

View File

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

View File

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

View File

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

View File

@ -330,6 +330,10 @@ def is_valid_csv(extra_spec_value):
return all([v.strip() for v in values]) 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): def sanitize_csv(csv_string):
return ','.join(value.strip() for value in csv_string.split(',') return ','.join(value.strip() for value in csv_string.split(',')
if (csv_string and value)) if (csv_string and value))
@ -356,8 +360,12 @@ def is_valid_optional_extra_spec(key, value):
return value in constants.ExtraSpecs.REPLICATION_TYPES return value in constants.ExtraSpecs.REPLICATION_TYPES
elif key == constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT: elif key == constants.ExtraSpecs.MOUNT_SNAPSHOT_SUPPORT:
return parse_boolean_extra_spec(key, value) is not None 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: elif key == constants.ExtraSpecs.AVAILABILITY_ZONES:
return is_valid_csv(value) 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, elif key in [constants.ExtraSpecs.PROVISIONING_MAX_SHARE_SIZE,
constants.ExtraSpecs.PROVISIONING_MIN_SHARE_SIZE, constants.ExtraSpecs.PROVISIONING_MIN_SHARE_SIZE,
constants.ExtraSpecs.PROVISIONING_MAX_SHARE_EXTEND_SIZE]: constants.ExtraSpecs.PROVISIONING_MAX_SHARE_EXTEND_SIZE]:

View File

@ -261,6 +261,37 @@ class ShareAPITest(test.TestCase):
self.assertEqual("fakenetid", self.assertEqual("fakenetid",
create_mock.call_args[1]['share_network_id']) 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): def test_share_create_with_share_net_not_active(self):
shr = { shr = {
"size": 100, "size": 100,
@ -459,6 +490,53 @@ class ShareAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with( self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'create') 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( @ddt.data(
{'name': 'name1', 'description': 'x' * 256}, {'name': 'name1', 'description': 'x' * 256},
{'name': 'x' * 256, 'description': 'description1'}, {'name': 'x' * 256, 'description': 'description1'},

View File

@ -61,17 +61,40 @@ class ShareTransferAPITestCase(test.TestCase):
size=1, size=1,
project_id='fake_project_id', project_id='fake_project_id',
user_id='fake_user_id', user_id='fake_user_id',
share_network_id=None): share_network_id=None,
mount_point_name=None):
"""Create a share object.""" """Create a share object."""
share_type = db_utils.create_share_type() share_type = db_utils.create_share_type()
share = db_utils.create_share(display_name=display_name, if mount_point_name:
display_description=display_description, instance_list = [
status=status, size=size, db_utils.create_share_instance(
project_id=project_id, status=status,
user_id=user_id, share_id='fake_id',
share_type_id=share_type['id'], mount_point_name=mount_point_name
share_network_id=share_network_id )
) ]
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'] share_id = share['id']
return share_id return share_id
@ -232,6 +255,35 @@ class ShareTransferAPITestCase(test.TestCase):
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.v2_controller.create, req, body) 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): def test_create_transfer_share_with_network_id(self):
share_id = self._create_share(share_network_id='fake_id') share_id = self._create_share(share_network_id='fake_id')
body = {"transfer": {"name": "transfer1", body = {"transfer": {"name": "transfer1",

View File

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

View File

@ -94,7 +94,8 @@ def create_share(**kwargs):
'availability_zone': 'fake_availability_zone', 'availability_zone': 'fake_availability_zone',
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'host': 'fake_host', '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) return _create_db_row(db.share_create, share, kwargs)
@ -112,7 +113,8 @@ def create_share_without_instance(**kwargs):
'availability_zone': 'fake_availability_zone', 'availability_zone': 'fake_availability_zone',
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'host': 'fake_host', 'host': 'fake_host',
'is_soft_deleted': False 'is_soft_deleted': False,
'mount_point_name': None,
} }
share.update(copy.deepcopy(kwargs)) share.update(copy.deepcopy(kwargs))
return db.share_create(context.get_admin_context(), share, False) 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_network_id': 'fakesharenetworkid',
'share_server_id': 'fakeshareserverid', 'share_server_id': 'fakeshareserverid',
'share_type_id': '1', 'share_type_id': '1',
'mount_point_name': None,
} }
for attr in models.ShareInstance._proxified_properties: for attr in models.ShareInstance._proxified_properties:
@ -82,7 +83,8 @@ def fake_share_type(**kwargs):
'is_public': False, 'is_public': False,
'extra_specs': { 'extra_specs': {
'driver_handles_share_servers': 'False', 'driver_handles_share_servers': 'False',
} },
'mount_point_name_support': False
} }
extra_specs = kwargs.pop('extra_specs', {}) extra_specs = kwargs.pop('extra_specs', {})

View File

@ -55,7 +55,8 @@ SERVICE_STATES_NO_POOLS = {
create_share_from_snapshot_support=False, create_share_from_snapshot_support=False,
revert_to_snapshot_support=True, revert_to_snapshot_support=True,
mount_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', 'host2@back1': dict(share_backend_name='BBB',
total_capacity_gb=256, free_capacity_gb=100, total_capacity_gb=256, free_capacity_gb=100,
timestamp=None, reserved_percentage=0, timestamp=None, reserved_percentage=0,
@ -68,7 +69,8 @@ SERVICE_STATES_NO_POOLS = {
create_share_from_snapshot_support=True, create_share_from_snapshot_support=True,
revert_to_snapshot_support=False, revert_to_snapshot_support=False,
mount_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', 'host2@back2': dict(share_backend_name='CCC',
total_capacity_gb=10000, free_capacity_gb=700, total_capacity_gb=10000, free_capacity_gb=700,
timestamp=None, reserved_percentage=0, timestamp=None, reserved_percentage=0,
@ -81,7 +83,8 @@ SERVICE_STATES_NO_POOLS = {
create_share_from_snapshot_support=True, create_share_from_snapshot_support=True,
revert_to_snapshot_support=False, revert_to_snapshot_support=False,
mount_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 = [ SHARE_SERVICES_WITH_POOLS = [
@ -124,7 +127,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0, reserved_share_extend_percentage=0,
provisioned_capacity_gb=10, provisioned_capacity_gb=10,
max_over_subscription_ratio=1.0, max_over_subscription_ratio=1.0,
thin_provisioning=False)]), thin_provisioning=False,
mount_point_name_support=False,
)]),
'host2@BBB': dict(share_backend_name='BBB', 'host2@BBB': dict(share_backend_name='BBB',
timestamp=None, reserved_percentage=0, timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0, reserved_snapshot_percentage=0,
@ -142,7 +147,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0, reserved_share_extend_percentage=0,
provisioned_capacity_gb=60, provisioned_capacity_gb=60,
max_over_subscription_ratio=2.0, max_over_subscription_ratio=2.0,
thin_provisioning=True)]), thin_provisioning=True,
mount_point_name_support=False,
)]),
'host3@CCC': dict(share_backend_name='CCC', 'host3@CCC': dict(share_backend_name='CCC',
timestamp=None, reserved_percentage=0, timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0, reserved_snapshot_percentage=0,
@ -160,7 +167,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0, reserved_share_extend_percentage=0,
provisioned_capacity_gb=100, provisioned_capacity_gb=100,
max_over_subscription_ratio=20.0, max_over_subscription_ratio=20.0,
thin_provisioning=True)]), thin_provisioning=True,
mount_point_name_support=False,
)]),
'host4@DDD': dict(share_backend_name='DDD', 'host4@DDD': dict(share_backend_name='DDD',
timestamp=None, reserved_percentage=0, timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0, reserved_snapshot_percentage=0,
@ -178,7 +187,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0, reserved_share_extend_percentage=0,
provisioned_capacity_gb=800, provisioned_capacity_gb=800,
max_over_subscription_ratio=2.0, max_over_subscription_ratio=2.0,
thin_provisioning=True), thin_provisioning=True,
mount_point_name_support=False,
),
dict(pool_name='pool4b', dict(pool_name='pool4b',
total_capacity_gb=542, total_capacity_gb=542,
free_capacity_gb=442, free_capacity_gb=442,
@ -187,7 +198,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0, reserved_share_extend_percentage=0,
provisioned_capacity_gb=2000, provisioned_capacity_gb=2000,
max_over_subscription_ratio=10.0, max_over_subscription_ratio=10.0,
thin_provisioning=True)]), thin_provisioning=True,
mount_point_name_support=False,
)]),
'host5@EEE': dict(share_backend_name='EEE', 'host5@EEE': dict(share_backend_name='EEE',
timestamp=None, reserved_percentage=0, timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0, reserved_snapshot_percentage=0,
@ -205,7 +218,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0, reserved_share_extend_percentage=0,
provisioned_capacity_gb=100, provisioned_capacity_gb=100,
max_over_subscription_ratio=1.0, max_over_subscription_ratio=1.0,
thin_provisioning=False), thin_provisioning=False,
mount_point_name_support=False,
),
dict(pool_name='pool5b', dict(pool_name='pool5b',
total_capacity_gb=552, total_capacity_gb=552,
free_capacity_gb=452, free_capacity_gb=452,
@ -214,7 +229,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0, reserved_share_extend_percentage=0,
provisioned_capacity_gb=100, provisioned_capacity_gb=100,
max_over_subscription_ratio=1.0, max_over_subscription_ratio=1.0,
thin_provisioning=False)]), thin_provisioning=False,
mount_point_name_support=False,
)]),
'host6@FFF': dict(share_backend_name='FFF', 'host6@FFF': dict(share_backend_name='FFF',
timestamp=None, reserved_percentage=0, timestamp=None, reserved_percentage=0,
reserved_snapshot_percentage=0, reserved_snapshot_percentage=0,
@ -232,7 +249,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0, reserved_share_extend_percentage=0,
provisioned_capacity_gb=100, provisioned_capacity_gb=100,
max_over_subscription_ratio=1.0, max_over_subscription_ratio=1.0,
thin_provisioning=False), thin_provisioning=False,
mount_point_name_support=False,
),
dict(pool_name='pool6b', dict(pool_name='pool6b',
total_capacity_gb='unknown', total_capacity_gb='unknown',
free_capacity_gb='unknown', free_capacity_gb='unknown',
@ -241,7 +260,9 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_share_extend_percentage=0, reserved_share_extend_percentage=0,
provisioned_capacity_gb=100, provisioned_capacity_gb=100,
max_over_subscription_ratio=1.0, max_over_subscription_ratio=1.0,
thin_provisioning=False)]), thin_provisioning=False,
mount_point_name_support=False,
)]),
} }
FAKE_ACTIVE_IQ_WEIGHER_LIST = [ 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.assertTrue if passes else self.assertFalse
assertion(self.filter.host_passes(host, filter_properties)) 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): def test_capability_filter_passes_extra_specs_simple(self):
self._do_test_type_filter_extra_specs( self._do_test_type_filter_extra_specs(
ecaps={'opt1': '1', 'opt2': '2'}, ecaps={'opt1': '1', 'opt2': '2'},

View File

@ -217,6 +217,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False, 'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
}, },
}, { }, {
'name': 'host2@back1#BBB', 'name': 'host2@back1#BBB',
@ -250,6 +251,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False, 'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
}, },
}, { }, {
'name': 'host2@back2#CCC', 'name': 'host2@back2#CCC',
@ -283,6 +285,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_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, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False, 'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
}, },
}, { }, {
'name': 'host2@BBB#pool2', 'name': 'host2@BBB#pool2',
@ -372,6 +376,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False, 'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
}, },
}, { }, {
'name': 'host3@CCC#pool3', 'name': 'host3@CCC#pool3',
@ -406,6 +411,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False, 'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
}, },
}, { }, {
'name': 'host4@DDD#pool4a', 'name': 'host4@DDD#pool4a',
@ -440,6 +446,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False, 'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
}, },
}, { }, {
'name': 'host4@DDD#pool4b', 'name': 'host4@DDD#pool4b',
@ -474,6 +481,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_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, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_support': False, 'share_server_multiple_subnet_support': False,
'mount_point_name_support': False,
}, },
}, { }, {
'name': 'host2@back1#BBB', 'name': 'host2@back1#BBB',
@ -574,6 +583,7 @@ class HostManagerTestCase(test.TestCase):
'security_service_update_support': False, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_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, 'security_service_update_support': False,
'network_allocation_update_support': False, 'network_allocation_update_support': False,
'share_server_multiple_subnet_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.assertEqual('test-pool', self._driver._stats['pools'])
self.assertTrue(self._driver._stats['ipv4_support']) self.assertTrue(self._driver._stats['ipv4_support'])
self.assertFalse(self._driver._stats['ipv6_support']) self.assertFalse(self._driver._stats['ipv6_support'])
self.assertFalse(self._driver.
_stats['mount_point_name_support'])
def test_create_share(self): def test_create_share(self):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -376,6 +376,7 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
'vendor_name': 'Open Source', 'vendor_name': 'Open Source',
'filter_function': None, 'filter_function': None,
'goodness_function': None, 'goodness_function': None,
'mount_point_name_support': False,
'ipv4_support': True, 'ipv4_support': True,
'ipv6_support': False, 'ipv6_support': False,
'security_service_update_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_instance = db_utils.create_share_instance(
share_id=share['id'], share_id=share['id'],
share_type_id=share_type_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', self.mock_object(db_api, 'share_instance_create',
mock.Mock(return_value=share_instance)) mock.Mock(return_value=share_instance))
self.mock_object(db_api, 'share_type_get', self.mock_object(db_api, 'share_type_get',
@ -801,7 +804,7 @@ class ShareAPITestCase(test.TestCase):
availability_zones=expected_azs, availability_zones=expected_azs,
az_request_multiple_subnet_support_map=compatible_azs_multiple, az_request_multiple_subnet_support_map=compatible_azs_multiple,
snapshot_host=None, snapshot_host=None,
scheduler_hints=None scheduler_hints=None, mount_point_name=None,
) )
db_api.share_get.assert_called_once() db_api.share_get.assert_called_once()
@ -840,6 +843,85 @@ class ShareAPITestCase(test.TestCase):
get_all_azs_sns.assert_called_once_with( get_all_azs_sns.assert_called_once_with(
self.context, fake_share_network_id) 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( @ddt.data(
None, '', 'fake', 'nfsfake', 'cifsfake', 'glusterfsfake', 'hdfsfake') None, '', 'fake', 'nfsfake', 'cifsfake', 'glusterfsfake', 'hdfsfake')
def test_create_share_invalid_protocol(self, proto): def test_create_share_invalid_protocol(self, proto):
@ -984,6 +1066,7 @@ class ShareAPITestCase(test.TestCase):
'availability_zone_id': 'fake_id', 'availability_zone_id': 'fake_id',
'share_type_id': 'fake_share_type', 'share_type_id': 'fake_share_type',
'cast_rules_to_readonly': False, 'cast_rules_to_readonly': False,
'mount_point_name': None,
} }
) )
db_api.share_type_get.assert_called_once_with( db_api.share_type_get.assert_called_once_with(
@ -999,6 +1082,28 @@ class ShareAPITestCase(test.TestCase):
self.assertFalse( self.assertFalse(
self.api.scheduler_rpcapi.create_share_instance.called) 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): def test_create_share_instance_without_host(self):
_, share, share_instance = self._setup_create_instance_mocks() _, share, share_instance = self._setup_create_instance_mocks()
@ -1072,6 +1177,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'replication_type': 'dr', 'replication_type': 'dr',
} }
} }
@ -1090,6 +1196,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'replication_type': None, 'replication_type': None,
} }
self.assertEqual(expected, result) self.assertEqual(expected, result)
@ -1140,6 +1247,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': dhss, 'driver_handles_share_servers': dhss,
}, },
} }
@ -1176,6 +1284,8 @@ class ShareAPITestCase(test.TestCase):
fake_type['extra_specs']['revert_to_snapshot_support'], fake_type['extra_specs']['revert_to_snapshot_support'],
'mount_snapshot_support': 'mount_snapshot_support':
fake_type['extra_specs']['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, 'replication_type': replication_type,
}) })
@ -1231,6 +1341,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': dhss, 'driver_handles_share_servers': dhss,
}, },
} }
@ -1278,6 +1389,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': True, 'driver_handles_share_servers': True,
}, },
} }
@ -1329,6 +1441,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': True, 'driver_handles_share_servers': True,
}, },
} }
@ -1412,6 +1525,9 @@ class ShareAPITestCase(test.TestCase):
'mount_snapshot_support': kwargs.get( 'mount_snapshot_support': kwargs.get(
'mount_snapshot_support', 'mount_snapshot_support',
share_type['extra_specs'].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_proto': kwargs.get('share_proto', share.get('share_proto')),
'share_type_id': share_type['id'], 'share_type_id': share_type['id'],
'is_public': kwargs.get('is_public', share.get('is_public')), 'is_public': kwargs.get('is_public', share.get('is_public')),
@ -2356,7 +2472,7 @@ class ShareAPITestCase(test.TestCase):
availability_zones=None, availability_zones=None,
az_request_multiple_subnet_support_map=None, az_request_multiple_subnet_support_map=None,
snapshot_host=snapshot['share']['instance']['host'], 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( share_api.policy.check_policy.assert_called_once_with(
self.context, 'share_snapshot', 'get_snapshot') self.context, 'share_snapshot', 'get_snapshot')
quota.QUOTAS.reserve.assert_called_once_with( quota.QUOTAS.reserve.assert_called_once_with(
@ -3508,6 +3624,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': dhss, 'driver_handles_share_servers': dhss,
}, },
} }
@ -3520,6 +3637,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': dhss, 'driver_handles_share_servers': dhss,
'availability_zones': 'fake_az1,fake_az2', 'availability_zones': 'fake_az1,fake_az2',
}, },
@ -3603,6 +3721,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': 'true', 'driver_handles_share_servers': 'true',
'availability_zones': 'fake_az3' 'availability_zones': 'fake_az3'
}, },
@ -3615,6 +3734,7 @@ class ShareAPITestCase(test.TestCase):
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'mount_point_name_support': False,
'driver_handles_share_servers': 'true', 'driver_handles_share_servers': 'true',
'availability_zones': 'fake_az1,fake_az2', 'availability_zones': 'fake_az1,fake_az2',
}, },

View File

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

View File

@ -406,14 +406,18 @@ class ShareTypesTestCase(test.TestCase):
(constants.ExtraSpecs.SNAPSHOT_SUPPORT, (constants.ExtraSpecs.SNAPSHOT_SUPPORT,
constants.ExtraSpecs.CREATE_SHARE_FROM_SNAPSHOT_SUPPORT, constants.ExtraSpecs.CREATE_SHARE_FROM_SNAPSHOT_SUPPORT,
constants.ExtraSpecs.REVERT_TO_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)) + strutils.TRUE_STRINGS + strutils.FALSE_STRINGS)) +
list(itertools.product( list(itertools.product(
(constants.ExtraSpecs.REPLICATION_TYPE_SPEC,), (constants.ExtraSpecs.REPLICATION_TYPE_SPEC,),
constants.ExtraSpecs.REPLICATION_TYPES)) + constants.ExtraSpecs.REPLICATION_TYPES)) +
[(constants.ExtraSpecs.AVAILABILITY_ZONES, 'zone a, zoneb$c'), [(constants.ExtraSpecs.AVAILABILITY_ZONES, 'zone a, zoneb$c'),
(constants.ExtraSpecs.AVAILABILITY_ZONES, ' zonea, zoneb'), (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 @ddt.unpack
def test_is_valid_optional_extra_spec_valid(self, key, value): def test_is_valid_optional_extra_spec_valid(self, key, value):
@ -422,6 +426,18 @@ class ShareTypesTestCase(test.TestCase):
self.assertTrue(result) 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): def test_is_valid_optional_extra_spec_valid_unknown_key(self):
result = share_types.is_valid_optional_extra_spec('fake', 'fake') 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", policy.check_policy(context, "share_transfer", "create",
target_obj=share_ref) target_obj=share_ref)
share_instance = share_ref['instance'] 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": if share_ref['status'] != "available":
raise exception.InvalidShare(reason=_("Share's status must be " raise exception.InvalidShare(reason=_("Share's status must be "
"available")) "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.