manila/manila/share/drivers/nexenta/ns4/nexenta_nfs_helper.py

228 lines
9.1 KiB
Python

# Copyright 2016 Nexenta Systems, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log
from oslo_utils import excutils
from manila.common import constants as common
from manila import exception
from manila.i18n import _
from manila.share.drivers.nexenta.ns4 import jsonrpc
from manila.share.drivers.nexenta import utils
LOG = log.getLogger(__name__)
NOT_EXIST = 'does not exist'
DEP_CLONES = 'has dependent clones'
class NFSHelper(object):
def __init__(self, configuration):
self.configuration = configuration
self.nfs_mount_point_base = (
self.configuration.nexenta_mount_point_base)
self.dataset_compression = (
self.configuration.nexenta_dataset_compression)
self.dataset_dedupe = self.configuration.nexenta_dataset_dedupe
self.nms = None
self.nms_protocol = self.configuration.nexenta_rest_protocol
self.nms_host = self.configuration.nexenta_host
self.volume = self.configuration.nexenta_volume
self.share = self.configuration.nexenta_nfs_share
self.nms_port = self.configuration.nexenta_rest_port
self.nms_user = self.configuration.nexenta_user
self.nfs = self.configuration.nexenta_nfs
self.nms_password = self.configuration.nexenta_password
self.storage_protocol = 'NFS'
def do_setup(self):
if self.nms_protocol == 'auto':
protocol, auto = 'http', True
else:
protocol, auto = self.nms_protocol, False
path = '/rest/nms/'
self.nms = jsonrpc.NexentaJSONProxy(
protocol, self.nms_host, self.nms_port, path, self.nms_user,
self.nms_password, auto=auto)
def check_for_setup_error(self):
if not self.nms.volume.object_exists(self.volume):
raise exception.NexentaException(reason=_(
"Volume %s does not exist in NexentaStor appliance.") %
self.volume)
folder = '%s/%s' % (self.volume, self.share)
create_folder_props = {
'recordsize': '4K',
'quota': 'none',
'compression': self.dataset_compression,
}
if not self.nms.folder.object_exists(folder):
self.nms.folder.create_with_props(
self.volume, self.share, create_folder_props)
def create_filesystem(self, share):
"""Create file system."""
create_folder_props = {
'recordsize': '4K',
'quota': '%sG' % share['size'],
'compression': self.dataset_compression,
}
if not self.configuration.nexenta_thin_provisioning:
create_folder_props['reservation'] = '%sG' % share['size']
parent_path = '%s/%s' % (self.volume, self.share)
self.nms.folder.create_with_props(
parent_path, share['name'], create_folder_props)
path = self._get_share_path(share['name'])
return [self._get_location_path(path, share['share_proto'])]
def set_quota(self, share_name, new_size):
if self.configuration.nexenta_thin_provisioning:
quota = '%sG' % new_size
self.nms.folder.set_child_prop(
self._get_share_path(share_name), 'quota', quota)
def _get_location_path(self, path, protocol):
location = None
if protocol == 'NFS':
location = {'path': '%s:/volumes/%s' % (self.nms_host, path)}
else:
raise exception.InvalidShare(
reason=(_('Only NFS protocol is currently supported.')))
return location
def delete_share(self, share_name):
"""Delete share."""
folder = self._get_share_path(share_name)
try:
self.nms.folder.destroy(folder.strip(), '-r')
except exception.NexentaException as e:
with excutils.save_and_reraise_exception() as exc:
if NOT_EXIST in e.args[0]:
LOG.info('Folder %s does not exist, it was '
'already deleted.', folder)
exc.reraise = False
def _get_share_path(self, share_name):
return '%s/%s/%s' % (self.volume, self.share, share_name)
def _get_snapshot_name(self, snapshot_name):
return 'snapshot-%s' % snapshot_name
def create_snapshot(self, share_name, snapshot_name):
"""Create a snapshot."""
folder = self._get_share_path(share_name)
self.nms.folder.create_snapshot(folder, snapshot_name, '-r')
model_update = {'provider_location': '%s@%s' % (folder, snapshot_name)}
return model_update
def delete_snapshot(self, share_name, snapshot_name):
"""Deletes snapshot."""
try:
self.nms.snapshot.destroy('%s@%s' % (
self._get_share_path(share_name), snapshot_name), '')
except exception.NexentaException as e:
with excutils.save_and_reraise_exception() as exc:
if NOT_EXIST in e.args[0]:
LOG.info('Snapshot %(folder)s@%(snapshot)s does not '
'exist, it was already deleted.',
{
'folder': share_name,
'snapshot': snapshot_name,
})
exc.reraise = False
elif DEP_CLONES in e.args[0]:
LOG.info(
'Snapshot %(folder)s@%(snapshot)s has dependent '
'clones, it will be deleted later.', {
'folder': share_name,
'snapshot': snapshot_name
})
exc.reraise = False
def create_share_from_snapshot(self, share, snapshot):
snapshot_name = '%s/%s/%s@%s' % (
self.volume, self.share, snapshot['share_name'], snapshot['name'])
self.nms.folder.clone(
snapshot_name,
'%s/%s/%s' % (self.volume, self.share, share['name']))
path = self._get_share_path(share['name'])
return [self._get_location_path(path, share['share_proto'])]
def update_access(self, share_name, access_rules):
"""Update access to the share."""
rw_list = []
ro_list = []
for rule in access_rules:
if rule['access_type'].lower() != 'ip':
msg = _('Only IP access type is supported.')
raise exception.InvalidShareAccess(reason=msg)
else:
if rule['access_level'] == common.ACCESS_LEVEL_RW:
rw_list.append(rule['access_to'])
else:
ro_list.append(rule['access_to'])
share_opts = {
'auth_type': 'none',
'read_write': ':'.join(rw_list),
'read_only': ':'.join(ro_list),
'recursive': 'true',
'anonymous_rw': 'true',
'anonymous': 'true',
'extra_options': 'anon=0',
}
self.nms.netstorsvc.share_folder(
'svc:/network/nfs/server:default',
self._get_share_path(share_name), share_opts)
def _get_capacity_info(self):
"""Calculate available space on the NFS share."""
folder_props = self.nms.folder.get_child_props(
'%s/%s' % (self.volume, self.share), 'used|available')
free = utils.str2gib_size(folder_props['available'])
allocated = utils.str2gib_size(folder_props['used'])
return free + allocated, free, allocated
def update_share_stats(self):
"""Update driver capabilities.
No way of tracking provisioned capacity on this appliance,
not returning any to let the scheduler estimate it.
"""
total, free, allocated = self._get_capacity_info()
compression = not self.dataset_compression == 'off'
dedupe = not self.dataset_dedupe == 'off'
return {
'vendor_name': 'Nexenta',
'storage_protocol': self.storage_protocol,
'nfs_mount_point_base': self.nfs_mount_point_base,
'pools': [{
'pool_name': self.volume,
'total_capacity_gb': total,
'free_capacity_gb': free,
'reserved_percentage':
self.configuration.reserved_share_percentage,
'compression': compression,
'dedupe': dedupe,
'max_over_subscription_ratio': (
self.configuration.safe_get(
'max_over_subscription_ratio')),
'thin_provisioning':
self.configuration.nexenta_thin_provisioning,
}],
}