manila/manila/share/drivers/dell_emc/plugins/unity/client.py

348 lines
13 KiB
Python

# Copyright (c) 2016 EMC Corporation.
# 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.
import six
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import importutils
storops = importutils.try_import('storops')
if storops:
# pylint: disable=import-error
from storops import exception as storops_ex
from storops.unity import enums
from manila.common import constants as const
from manila import exception
from manila.i18n import _
from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
from manila.share.drivers.dell_emc.plugins.unity import utils
LOG = log.getLogger(__name__)
class UnityClient(object):
def __init__(self, host, username, password):
if storops is None:
LOG.error('StorOps is required to run EMC Unity driver.')
self.system = storops.UnitySystem(host, username, password)
def create_cifs_share(self, resource, share_name):
"""Create CIFS share from the resource.
:param resource: either UnityFilesystem or UnitySnap object
:param share_name: CIFS share name
:return: UnityCifsShare object
"""
try:
return resource.create_cifs_share(share_name)
except storops_ex.UnitySmbShareNameExistedError:
return self.get_share(share_name, 'CIFS')
def create_nfs_share(self, resource, share_name):
"""Create NFS share from the resource.
:param resource: either UnityFilesystem or UnitySnap object
:param share_name: NFS share name
:return: UnityNfsShare object
"""
try:
return resource.create_nfs_share(share_name)
except storops_ex.UnityNfsShareNameExistedError:
return self.get_share(share_name, 'NFS')
def create_nfs_filesystem_and_share(self, pool, nas_server,
share_name, size_gb):
"""Create filesystem and share from pool/NAS server.
:param pool: pool for file system creation
:param nas_server: nas server for file system creation
:param share_name: file system and share name
:param size_gb: file system size
"""
size = utils.gib_to_byte(size_gb)
pool.create_nfs_share(
nas_server, share_name, size, user_cap=True)
def get_share(self, name, share_proto):
# Validate the share protocol
proto = share_proto.upper()
if proto == 'CIFS':
return self.system.get_cifs_share(name=name)
elif proto == 'NFS':
return self.system.get_nfs_share(name=name)
else:
raise exception.BadConfigurationException(
reason=_('Invalid NAS protocol supplied: %s.') % share_proto)
@staticmethod
def delete_share(share):
share.delete()
def create_filesystem(self, pool, nas_server, share_name, size_gb, proto):
try:
size = utils.gib_to_byte(size_gb)
return pool.create_filesystem(nas_server,
share_name,
size,
proto=proto,
user_cap=True)
except storops_ex.UnityFileSystemNameAlreadyExisted:
LOG.debug('Filesystem %s already exists, '
'ignoring filesystem creation.', share_name)
return self.system.get_filesystem(name=share_name)
@staticmethod
def delete_filesystem(filesystem):
try:
filesystem.delete()
except storops_ex.UnityResourceNotFoundError:
LOG.info('Filesystem %s is already removed.', filesystem.name)
def create_nas_server(self, name, sp, pool, tenant=None):
try:
return self.system.create_nas_server(name, sp, pool,
tenant=tenant)
except storops_ex.UnityNasServerNameUsedError:
LOG.info('Share server %s already exists, ignoring share '
'server creation.', name)
return self.get_nas_server(name)
def get_nas_server(self, name):
try:
return self.system.get_nas_server(name=name)
except storops_ex.UnityResourceNotFoundError:
LOG.info('NAS server %s not found.', name)
raise
def delete_nas_server(self, name, username=None, password=None):
tenant = None
try:
nas_server = self.get_nas_server(name=name)
tenant = nas_server.tenant
nas_server.delete(username=username, password=password)
except storops_ex.UnityResourceNotFoundError:
LOG.info('NAS server %s not found.', name)
if tenant is not None:
self._delete_tenant(tenant)
@staticmethod
def _delete_tenant(tenant):
if tenant.nas_servers:
LOG.debug('There are NAS servers belonging to the tenant %s. '
'Do not delete it.',
tenant.get_id())
return
try:
tenant.delete(delete_hosts=True)
except storops_ex.UnityException as ex:
LOG.warning('Delete tenant %(tenant)s failed with error: '
'%(ex)s. Leave the tenant on the system.',
{'tenant': tenant.get_id(),
'ex': ex})
@staticmethod
def create_dns_server(nas_server, domain, dns_ip):
try:
nas_server.create_dns_server(domain, dns_ip)
except storops_ex.UnityOneDnsPerNasServerError:
LOG.info('DNS server %s already exists, '
'ignoring DNS server creation.', domain)
@staticmethod
def create_interface(nas_server, ip_addr, netmask, gateway, port_id,
vlan_id=None, prefix_length=None):
try:
nas_server.create_file_interface(port_id,
ip_addr,
netmask=netmask,
v6_prefix_length=prefix_length,
gateway=gateway,
vlan_id=vlan_id)
except storops_ex.UnityIpAddressUsedError:
raise exception.IPAddressInUse(ip=ip_addr)
@staticmethod
def enable_cifs_service(nas_server, domain, username, password):
try:
nas_server.enable_cifs_service(
nas_server.file_interface,
domain=domain,
domain_username=username,
domain_password=password)
except storops_ex.UnitySmbNameInUseError:
LOG.info('CIFS service on NAS server %s is '
'already enabled.', nas_server.name)
@staticmethod
def enable_nfs_service(nas_server):
try:
nas_server.enable_nfs_service()
except storops_ex.UnityNfsAlreadyEnabledError:
LOG.info('NFS service on NAS server %s is '
'already enabled.', nas_server.name)
@staticmethod
def create_snapshot(filesystem, name):
access_type = enums.FilesystemSnapAccessTypeEnum.CHECKPOINT
try:
return filesystem.create_snap(name, fs_access_type=access_type)
except storops_ex.UnitySnapNameInUseError:
LOG.info('Snapshot %(snap)s on Filesystem %(fs)s already '
'exists.', {'snap': name, 'fs': filesystem.name})
def create_snap_of_snap(self, src_snap, dst_snap_name):
if isinstance(src_snap, six.string_types):
snap = self.get_snapshot(name=src_snap)
else:
snap = src_snap
try:
return snap.create_snap(dst_snap_name)
except storops_ex.UnitySnapNameInUseError:
return self.get_snapshot(dst_snap_name)
def get_snapshot(self, name):
return self.system.get_snap(name=name)
@staticmethod
def delete_snapshot(snap):
try:
snap.delete()
except storops_ex.UnityResourceNotFoundError:
LOG.info('Snapshot %s is already removed.', snap.name)
def get_pool(self, name=None):
return self.system.get_pool(name=name)
def get_storage_processor(self, sp_id=None):
sp = self.system.get_sp(sp_id)
if sp_id is None:
# `sp` is a list of SPA and SPB.
return [s for s in sp if s is not None and s.existed]
else:
return sp if sp.existed else None
def cifs_clear_access(self, share_name, white_list=None):
share = self.system.get_cifs_share(name=share_name)
share.clear_access(white_list)
def nfs_clear_access(self, share_name, white_list=None):
share = self.system.get_nfs_share(name=share_name)
share.clear_access(white_list, force_create_host=True)
def cifs_allow_access(self, share_name, user_name, access_level):
share = self.system.get_cifs_share(name=share_name)
if access_level == const.ACCESS_LEVEL_RW:
cifs_access = enums.ACEAccessLevelEnum.WRITE
else:
cifs_access = enums.ACEAccessLevelEnum.READ
share.add_ace(user=user_name, access_level=cifs_access)
def nfs_allow_access(self, share_name, host_ip, access_level):
share = self.system.get_nfs_share(name=share_name)
host_ip = enas_utils.convert_ipv6_format_if_needed(host_ip)
if access_level == const.ACCESS_LEVEL_RW:
share.allow_read_write_access(host_ip, force_create_host=True)
share.allow_root_access(host_ip, force_create_host=True)
else:
share.allow_read_only_access(host_ip, force_create_host=True)
def cifs_deny_access(self, share_name, user_name):
share = self.system.get_cifs_share(name=share_name)
try:
share.delete_ace(user=user_name)
except storops_ex.UnityAclUserNotFoundError:
LOG.debug('ACL User "%(user)s" does not exist.',
{'user': user_name})
def nfs_deny_access(self, share_name, host_ip):
share = self.system.get_nfs_share(name=share_name)
try:
share.delete_access(host_ip)
except storops_ex.UnityHostNotFoundException:
LOG.info('%(host)s access to %(share)s is already removed.',
{'host': host_ip, 'share': share_name})
def get_file_ports(self):
ports = self.system.get_file_port()
link_up_ports = []
for port in ports:
if port.is_link_up and self._is_external_port(port.id):
link_up_ports.append(port)
return link_up_ports
def extend_filesystem(self, fs, new_size_gb):
size = utils.gib_to_byte(new_size_gb)
try:
fs.extend(size, user_cap=True)
except storops_ex.UnityNothingToModifyError:
LOG.debug('The size of the file system %(id)s is %(size)s '
'bytes.', {'id': fs.get_id(), 'size': size})
return size
def shrink_filesystem(self, share_id, fs, new_size_gb):
size = utils.gib_to_byte(new_size_gb)
try:
fs.shrink(size, user_cap=True)
except storops_ex.UnityNothingToModifyError:
LOG.debug('The size of the file system %(id)s is %(size)s '
'bytes.', {'id': fs.get_id(), 'size': size})
except storops_ex.UnityShareShrinkSizeTooSmallError:
LOG.error('The used size of the file system %(id)s is '
'bigger than input shrink size,'
'it may cause date loss.', {'id': fs.get_id()})
raise exception.ShareShrinkingPossibleDataLoss(share_id=share_id)
return size
@staticmethod
def _is_external_port(port_id):
return 'eth' in port_id or '_la' in port_id
def get_tenant(self, name, vlan_id):
if not vlan_id:
# Do not create vlan for flat network
return None
tenant = None
try:
tenant_name = "vlan_%(vlan_id)s_%(name)s" % {'vlan_id': vlan_id,
'name': name}
tenant = self.system.create_tenant(tenant_name, vlans=[vlan_id])
except (storops_ex.UnityVLANUsedByOtherTenantError,
storops_ex.UnityTenantNameInUseError,
storops_ex.UnityVLANAlreadyHasInterfaceError):
with excutils.save_and_reraise_exception() as exc:
tenant = self.system.get_tenant_use_vlan(vlan_id)
if tenant is not None:
LOG.debug("The VLAN %s is already added into a tenant. "
"Use the existing VLAN tenant.", vlan_id)
exc.reraise = False
except storops_ex.SystemAPINotSupported:
LOG.info("This system doesn't support tenant.")
return tenant
def restore_snapshot(self, snap_name):
snap = self.get_snapshot(snap_name)
return snap.restore(delete_backup=True)