Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
====================================
=====================
Vastdata Share Driver
====================================
=====================

VAST Share Driver integrates OpenStack with
`VAST Data <https://www.vastdata.com>`__'s Storage System.
Expand Down Expand Up @@ -46,14 +46,21 @@ share driver.


VAST Share Driver configuration example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following example shows parameters in the ``manila.conf`` file
that are used to configure VAST Share Driver.
They include two options under ``[DEFAULT]`` and parameters under ``[vast]``.
Note that a real ``manila.conf`` file would also include
other parameters that are not specific to VAST Share Driver.

.. note::

The ``vast_vippool_name`` parameter can be omitted from ``manila.conf``
if you plan to specify different VIP pools per share type using the
``vast:vippool_name`` extra spec. See the Multitenancy support section
for more details.

.. code-block:: ini

[DEFAULT]
Expand All @@ -79,7 +86,7 @@ changes to take effect.


Pre-configurations for share support
--------------------------------------------------
------------------------------------

To create a file share, you need to:

Expand All @@ -96,8 +103,75 @@ Create an NFS share:

openstack share create NFS ${size} --name ${share_name} --share-type ${share_type_name}

Multitenancy support via Share Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The VAST Share Driver supports multitenancy by allowing different share types
to use different VIP pools. This enables tenant isolation and provides
flexibility in network configuration.

VIP Pool Configuration
----------------------

The VIP pool can be specified in two ways:

1. **Global default** in ``manila.conf`` using ``vast_vippool_name``
2. **Per share type** using the ``vast:vippool_name`` extra spec

When both are specified, the share type extra spec takes precedence over
the configuration file setting.

Creating Share Types with Different VIP Pools
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can create multiple share types, each using a different VIP pool for
multitenancy:

.. code-block:: console

# Tenant A with dedicated VIP pool
openstack share type create vast-tenant-a false \
--extra-specs share_backend_name=vast \
--extra-specs vast:vippool_name=vippool-tenant-a

# Tenant B with dedicated VIP pool
openstack share type create vast-tenant-b false \
--extra-specs share_backend_name=vast \
--extra-specs vast:vippool_name=vippool-tenant-b

Creating Shares with Specific VIP Pools
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When creating a share, specify the share type to determine which VIP pool
will be used:

.. code-block:: console

# Create a share for Tenant A (uses vippool-tenant-a)
openstack share create NFS 100 \
--name tenant-a-share \
--share-type vast-tenant-a

# Create a share for Tenant B (uses vippool-tenant-b)
openstack share create NFS 50 \
--name tenant-b-share \
--share-type vast-tenant-b

This approach enables:

- **Network isolation** between different tenants or projects
- **Service differentiation** with different network configurations per tenant or environment
- **Flexible deployment** without modifying ``manila.conf``

.. note::

If ``vast_vippool_name`` is not specified in ``manila.conf`` and a share
is created without a share type that specifies ``vast:vippool_name``,
the share creation will fail with an error indicating that a VIP pool
must be specified.

Pre-Configurations for Snapshot support
--------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The share type must have the following parameter specified:

Expand All @@ -119,7 +193,7 @@ Or you can add it to an existing share type:


To snapshot a share and create share from the snapshot
------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Create a share using a share type with snapshot_support=True.
Then, create a snapshot of the share using the command:
Expand Down
84 changes: 70 additions & 14 deletions manila/share/drivers/vastdata/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@
),
cfg.StrOpt(
"vast_vippool_name",
help="Name of Virtual IP pool"
help="Default name of Virtual IP pool. Can be overridden per share "
"using share type extra spec 'vast:vippool_name'."
),
cfg.StrOpt(
"vast_root_export",
Expand Down Expand Up @@ -104,9 +105,21 @@
driver_util.verbose_driver_trace
)
class VASTShareDriver(driver.ShareDriver):
"""Driver for the VastData Filesystem."""
"""Driver for the VastData Filesystem.

Version history::

1.0 - Initial version.
- Support for NFS shares.
- Support for snapshots (create, delete, revert).
- Support for share creation from snapshots.
- Support for share access rules (IP-based).
- Support for share resize (extend/shrink).
1.1 - Added multi-tenancy support.
- Support for per-share VIP pool selection via extra specs.
"""

VERSION = "1.0" # driver version
VERSION = "1.1"

def __init__(self, *args, **kwargs):
super().__init__(False, *args, config_opts=[OPTS], **kwargs)
Expand All @@ -116,10 +129,7 @@ def do_setup(self, context):
backend_name = self.configuration.safe_get("share_backend_name")
root_export = self.configuration.vast_root_export
vip_pool_name = self.configuration.safe_get("vast_vippool_name")
if not vip_pool_name:
raise exception.VastDriverException(
reason="vast_vippool_name must be set"
)

self._backend_name = backend_name or self.__class__.__name__
self._vippool_name = vip_pool_name
self._root_export = "/" + root_export.strip("/")
Expand Down Expand Up @@ -276,9 +286,14 @@ def shrink_share(self, share, new_size, share_server=None):
self._resize_share(share, new_size)

def create_snapshot(self, context, snapshot, share_server=None):
"""Is called to create snapshot."""
"""Is called to create a snapshot."""
path = self._to_volume_path(snapshot["share_instance_id"])
self.rest.snapshots.create(path=path, name=snapshot["name"])
tenant_id = self._get_tenant_id_for_share(snapshot["share"])
self.rest.snapshots.create(
name=snapshot["name"],
path=path,
tenant_id=tenant_id,
)

def delete_snapshot(self, context, snapshot, share_server=None):
"""Is called to remove share."""
Expand Down Expand Up @@ -316,6 +331,33 @@ def _resize_share(self, share, new_size):
share_id=share['id'])
self.rest.quotas.update(quota.id, hard_limit=requested_capacity)

def _get_vip_pool_for_share(self, share):
# Get extra specs from share type
extra_specs = driver_util.get_share_extra_specs_params(share)
# Use vippool_name from extra specs if provided, otherwise use config
vippool_name = extra_specs.get('vippool_name') or self._vippool_name

if not vippool_name:
raise exception.VastDriverException(
reason="VIP pool name must be specified either in manila.conf "
"(vast_vippool_name) or in share type extra specs "
"(vast:vippool_name)"
)

LOG.debug(
"Using VIP pool '%s' for share %s (from %s)",
vippool_name, share["id"],
"extra_specs" if extra_specs.get('vippool_name') else "config"
)

return self.rest.vip_pools.one(
name=vippool_name,
fail_if_missing=True,
)

def _get_tenant_id_for_share(self, share):
return self._get_vip_pool_for_share(share).tenant_id

def _ensure_share(self, share):
share_proto = share["share_proto"]
if share_proto != "NFS":
Expand All @@ -325,23 +367,37 @@ def _ensure_share(self, share):
)
)

vips = self.rest.vip_pools.vips(pool_name=self._vippool_name)
vippool = self._get_vip_pool_for_share(share)
vips = driver_util.generate_ip_range(vippool.ip_ranges)
if not vips:
raise exception.VastDriverException(
reason=f"Pool {vippool.name} has no available vips"
)

share_id = share["id"]
requested_capacity = share["size"] * units.Gi
path = self._to_volume_path(share_id)
policy = self.rest.view_policies.ensure(name=share_id)
policy = self.rest.view_policies.ensure(
name=share_id,
tenant_id=vippool.tenant_id,
)
quota = self.rest.quotas.ensure(
name=share_id, path=path,
create_dir=True, hard_limit=requested_capacity
name=share_id,
path=path,
create_dir=True,
hard_limit=requested_capacity,
tenant_id=vippool.tenant_id,
)
if quota.hard_limit != requested_capacity:
raise exception.VastDriverException(
reason=f"Share already exists with different capacity"
f" (requested={requested_capacity}, exists={quota.hard_limit})"
)
view = self.rest.views.ensure(
name=share_id, path=path, policy_id=policy.id
name=share_id,
path=path,
policy_id=policy.id,
tenant_id=vippool.tenant_id,
)
if view.policy != share_id:
self.rest.views.update(view.id, policy_id=policy.id)
Expand Down
71 changes: 71 additions & 0 deletions manila/share/drivers/vastdata/driver_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,88 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
import ipaddress
import types

from oslo_config import cfg
from oslo_log import log
from oslo_utils import timeutils

from manila.share import share_types


CONF = cfg.CONF
LOG = log.getLogger(__name__)

# VAST driver namespace for extra specs
VAST_EXTRA_SPEC_NAMESPACE = 'vast'

# Supported VAST extra specs options
VAST_EXTRA_SPECS_OPTS = {
'vippool_name': None,
}


def get_share_extra_specs_params(share):
"""Return the VAST-specific parameters from share extra specs.

Args:
share: The share object containing share_type_id

Returns:
dict: Dictionary of VAST-specific options extracted from extra specs
"""
specs = share_types.get_extra_specs_from_share(share)
return get_opts_from_specs(specs)


def get_opts_from_specs(specs):
"""Parse extra specs and extract VAST-specific options.

This function extracts options with the 'vast:' namespace prefix.
For example: vast:vippool_name=pool-1

Args:
specs: Dictionary of extra specs from a share type

Returns:
dict: Dictionary of parsed VAST options
"""
opts = copy.deepcopy(VAST_EXTRA_SPECS_OPTS)

for key, value in specs.items():
# Get the scope (namespace), if using scope format
scope = None
key_split = key.split(':')

# Skip invalid format (more than 2 parts)
if len(key_split) not in (1, 2):
continue

if len(key_split) == 1:
# No namespace, skip for VAST-specific options
continue
else:
scope = key_split[0]
option_key = key_split[1]

# Normalize to lowercase for comparison
if scope:
scope = scope.lower()
if option_key:
option_key = option_key.lower()

# Extract options with 'vast:' namespace
if scope == VAST_EXTRA_SPEC_NAMESPACE and option_key in opts:
opts[option_key] = value
LOG.debug(
"Found VAST extra spec: %s:%s = %s",
scope, option_key, value
)

return opts


class Bunch(dict):
# from https://github.com/real-easypy/easypy
Expand Down
Loading
Loading