Coverage for manila/share/drivers/dell_emc/plugins/unity/client.py: 92%
216 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright (c) 2016 EMC Corporation.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16from oslo_log import log
17from oslo_utils import excutils
18from oslo_utils import importutils
20storops = importutils.try_import('storops')
21if storops: 21 ↛ 26line 21 didn't jump to line 26 because the condition on line 21 was always true
22 # pylint: disable=import-error
23 from storops import exception as storops_ex
24 from storops.unity import enums
26from manila.common import constants as const
27from manila import exception
28from manila.i18n import _
29from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
30from manila.share.drivers.dell_emc.plugins.unity import utils
32LOG = log.getLogger(__name__)
35class UnityClient(object):
36 def __init__(self, host, username, password):
37 if storops is None: 37 ↛ 38line 37 didn't jump to line 38 because the condition on line 37 was never true
38 LOG.error('StorOps is required to run EMC Unity driver.')
39 self.system = storops.UnitySystem(host, username, password)
41 def create_cifs_share(self, resource, share_name):
42 """Create CIFS share from the resource.
44 :param resource: either UnityFilesystem or UnitySnap object
45 :param share_name: CIFS share name
46 :return: UnityCifsShare object
47 """
48 try:
49 return resource.create_cifs_share(share_name)
50 except storops_ex.UnitySmbShareNameExistedError:
51 return self.get_share(share_name, 'CIFS')
53 def create_nfs_share(self, resource, share_name):
54 """Create NFS share from the resource.
56 :param resource: either UnityFilesystem or UnitySnap object
57 :param share_name: NFS share name
58 :return: UnityNfsShare object
59 """
60 try:
61 return resource.create_nfs_share(share_name)
62 except storops_ex.UnityNfsShareNameExistedError:
63 return self.get_share(share_name, 'NFS')
65 def create_nfs_filesystem_and_share(self, pool, nas_server,
66 share_name, size_gb):
67 """Create filesystem and share from pool/NAS server.
69 :param pool: pool for file system creation
70 :param nas_server: nas server for file system creation
71 :param share_name: file system and share name
72 :param size_gb: file system size
73 """
74 size = utils.gib_to_byte(size_gb)
75 pool.create_nfs_share(
76 nas_server, share_name, size, user_cap=True)
78 def get_share(self, name, share_proto):
79 # Validate the share protocol
80 proto = share_proto.upper()
82 if proto == 'CIFS':
83 return self.system.get_cifs_share(name=name)
84 elif proto == 'NFS':
85 return self.system.get_nfs_share(name=name)
86 else:
87 raise exception.BadConfigurationException(
88 reason=_('Invalid NAS protocol supplied: %s.') % share_proto)
90 @staticmethod
91 def delete_share(share):
92 share.delete()
94 def create_filesystem(self, pool, nas_server, share_name, size_gb, proto):
95 try:
96 size = utils.gib_to_byte(size_gb)
97 return pool.create_filesystem(nas_server,
98 share_name,
99 size,
100 proto=proto,
101 user_cap=True)
102 except storops_ex.UnityFileSystemNameAlreadyExisted:
103 LOG.debug('Filesystem %s already exists, '
104 'ignoring filesystem creation.', share_name)
105 return self.system.get_filesystem(name=share_name)
107 @staticmethod
108 def delete_filesystem(filesystem):
109 try:
110 filesystem.delete()
111 except storops_ex.UnityResourceNotFoundError:
112 LOG.info('Filesystem %s is already removed.', filesystem.name)
114 def create_nas_server(self, name, sp, pool, tenant=None):
115 try:
116 return self.system.create_nas_server(name, sp, pool,
117 tenant=tenant)
118 except storops_ex.UnityNasServerNameUsedError:
119 LOG.info('Share server %s already exists, ignoring share '
120 'server creation.', name)
121 return self.get_nas_server(name)
123 def get_nas_server(self, name):
124 try:
125 return self.system.get_nas_server(name=name)
126 except storops_ex.UnityResourceNotFoundError:
127 LOG.info('NAS server %s not found.', name)
128 raise
130 def delete_nas_server(self, name, username=None, password=None):
131 tenant = None
132 try:
133 nas_server = self.get_nas_server(name=name)
134 tenant = nas_server.tenant
135 nas_server.delete(username=username, password=password)
136 except storops_ex.UnityResourceNotFoundError:
137 LOG.info('NAS server %s not found.', name)
139 if tenant is not None: 139 ↛ 140line 139 didn't jump to line 140 because the condition on line 139 was never true
140 self._delete_tenant(tenant)
142 @staticmethod
143 def _delete_tenant(tenant):
144 if tenant.nas_servers:
145 LOG.debug('There are NAS servers belonging to the tenant %s. '
146 'Do not delete it.',
147 tenant.get_id())
148 return
149 try:
150 tenant.delete(delete_hosts=True)
151 except storops_ex.UnityException as ex:
152 LOG.warning('Delete tenant %(tenant)s failed with error: '
153 '%(ex)s. Leave the tenant on the system.',
154 {'tenant': tenant.get_id(),
155 'ex': ex})
157 @staticmethod
158 def create_dns_server(nas_server, domain, dns_ip):
159 try:
160 nas_server.create_dns_server(domain, dns_ip)
161 except storops_ex.UnityOneDnsPerNasServerError:
162 LOG.info('DNS server %s already exists, '
163 'ignoring DNS server creation.', domain)
165 @staticmethod
166 def create_interface(nas_server, ip_addr, netmask, gateway, port_id,
167 vlan_id=None, prefix_length=None):
168 try:
169 nas_server.create_file_interface(port_id,
170 ip_addr,
171 netmask=netmask,
172 v6_prefix_length=prefix_length,
173 gateway=gateway,
174 vlan_id=vlan_id)
175 except storops_ex.UnityIpAddressUsedError:
176 raise exception.IPAddressInUse(ip=ip_addr)
178 @staticmethod
179 def enable_cifs_service(nas_server, domain, username, password):
180 try:
181 nas_server.enable_cifs_service(
182 nas_server.file_interface,
183 domain=domain,
184 domain_username=username,
185 domain_password=password)
186 except storops_ex.UnitySmbNameInUseError:
187 LOG.info('CIFS service on NAS server %s is '
188 'already enabled.', nas_server.name)
190 @staticmethod
191 def enable_nfs_service(nas_server):
192 try:
193 nas_server.enable_nfs_service()
194 except storops_ex.UnityNfsAlreadyEnabledError:
195 LOG.info('NFS service on NAS server %s is '
196 'already enabled.', nas_server.name)
198 @staticmethod
199 def create_snapshot(filesystem, name):
200 access_type = enums.FilesystemSnapAccessTypeEnum.CHECKPOINT
201 try:
202 return filesystem.create_snap(name, fs_access_type=access_type)
203 except storops_ex.UnitySnapNameInUseError:
204 LOG.info('Snapshot %(snap)s on Filesystem %(fs)s already '
205 'exists.', {'snap': name, 'fs': filesystem.name})
207 def create_snap_of_snap(self, src_snap, dst_snap_name):
208 if isinstance(src_snap, str):
209 snap = self.get_snapshot(name=src_snap)
210 else:
211 snap = src_snap
213 try:
214 return snap.create_snap(dst_snap_name)
215 except storops_ex.UnitySnapNameInUseError:
216 return self.get_snapshot(dst_snap_name)
218 def get_snapshot(self, name):
219 return self.system.get_snap(name=name)
221 @staticmethod
222 def delete_snapshot(snap):
223 try:
224 snap.delete()
225 except storops_ex.UnityResourceNotFoundError:
226 LOG.info('Snapshot %s is already removed.', snap.name)
228 def get_pool(self, name=None):
229 return self.system.get_pool(name=name)
231 def get_storage_processor(self, sp_id=None):
232 sp = self.system.get_sp(sp_id)
233 if sp_id is None: 233 ↛ 235line 233 didn't jump to line 235 because the condition on line 233 was never true
234 # `sp` is a list of SPA and SPB.
235 return [s for s in sp if s is not None and s.existed]
236 else:
237 return sp if sp.existed else None
239 def cifs_clear_access(self, share_name, white_list=None):
240 share = self.system.get_cifs_share(name=share_name)
241 share.clear_access(white_list)
243 def nfs_clear_access(self, share_name, white_list=None):
244 share = self.system.get_nfs_share(name=share_name)
245 share.clear_access(white_list, force_create_host=True)
247 def cifs_allow_access(self, share_name, user_name, access_level):
248 share = self.system.get_cifs_share(name=share_name)
250 if access_level == const.ACCESS_LEVEL_RW:
251 cifs_access = enums.ACEAccessLevelEnum.WRITE
252 else:
253 cifs_access = enums.ACEAccessLevelEnum.READ
255 share.add_ace(user=user_name, access_level=cifs_access)
257 def nfs_allow_access(self, share_name, host_ip, access_level):
258 share = self.system.get_nfs_share(name=share_name)
259 host_ip = enas_utils.convert_ipv6_format_if_needed(host_ip)
260 if access_level == const.ACCESS_LEVEL_RW:
261 share.allow_read_write_access(host_ip, force_create_host=True)
262 share.allow_root_access(host_ip, force_create_host=True)
263 else:
264 share.allow_read_only_access(host_ip, force_create_host=True)
266 def cifs_deny_access(self, share_name, user_name):
267 share = self.system.get_cifs_share(name=share_name)
269 try:
270 share.delete_ace(user=user_name)
271 except storops_ex.UnityAclUserNotFoundError:
272 LOG.debug('ACL User "%(user)s" does not exist.',
273 {'user': user_name})
275 def nfs_deny_access(self, share_name, host_ip):
276 share = self.system.get_nfs_share(name=share_name)
278 try:
279 share.delete_access(host_ip)
280 except storops_ex.UnityHostNotFoundException:
281 LOG.info('%(host)s access to %(share)s is already removed.',
282 {'host': host_ip, 'share': share_name})
284 def get_file_ports(self):
285 ports = self.system.get_file_port()
286 link_up_ports = []
287 for port in ports:
288 if port.is_link_up and self._is_external_port(port.id):
289 link_up_ports.append(port)
291 return link_up_ports
293 def extend_filesystem(self, fs, new_size_gb):
294 size = utils.gib_to_byte(new_size_gb)
295 try:
296 fs.extend(size, user_cap=True)
297 except storops_ex.UnityNothingToModifyError:
298 LOG.debug('The size of the file system %(id)s is %(size)s '
299 'bytes.', {'id': fs.get_id(), 'size': size})
300 return size
302 def shrink_filesystem(self, share_id, fs, new_size_gb):
303 size = utils.gib_to_byte(new_size_gb)
304 try:
305 fs.shrink(size, user_cap=True)
306 except storops_ex.UnityNothingToModifyError:
307 LOG.debug('The size of the file system %(id)s is %(size)s '
308 'bytes.', {'id': fs.get_id(), 'size': size})
309 except storops_ex.UnityShareShrinkSizeTooSmallError:
310 LOG.error('The used size of the file system %(id)s is '
311 'bigger than input shrink size,'
312 'it may cause date loss.', {'id': fs.get_id()})
313 raise exception.ShareShrinkingPossibleDataLoss(share_id=share_id)
314 return size
316 @staticmethod
317 def _is_external_port(port_id):
318 return 'eth' in port_id or '_la' in port_id
320 def get_tenant(self, name, vlan_id):
321 if not vlan_id:
322 # Do not create vlan for flat network
323 return None
325 tenant = None
326 try:
327 tenant_name = "vlan_%(vlan_id)s_%(name)s" % {'vlan_id': vlan_id,
328 'name': name}
329 tenant = self.system.create_tenant(tenant_name, vlans=[vlan_id])
330 except (storops_ex.UnityVLANUsedByOtherTenantError,
331 storops_ex.UnityTenantNameInUseError,
332 storops_ex.UnityVLANAlreadyHasInterfaceError):
333 with excutils.save_and_reraise_exception() as exc:
334 tenant = self.system.get_tenant_use_vlan(vlan_id)
335 if tenant is not None:
336 LOG.debug("The VLAN %s is already added into a tenant. "
337 "Use the existing VLAN tenant.", vlan_id)
338 exc.reraise = False
339 except storops_ex.SystemAPINotSupported:
340 LOG.info("This system doesn't support tenant.")
342 return tenant
344 def restore_snapshot(self, snap_name):
345 snap = self.get_snapshot(snap_name)
346 return snap.restore(delete_backup=True)