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

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. 

15 

16from oslo_log import log 

17from oslo_utils import excutils 

18from oslo_utils import importutils 

19 

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 

25 

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 

31 

32LOG = log.getLogger(__name__) 

33 

34 

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) 

40 

41 def create_cifs_share(self, resource, share_name): 

42 """Create CIFS share from the resource. 

43 

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') 

52 

53 def create_nfs_share(self, resource, share_name): 

54 """Create NFS share from the resource. 

55 

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') 

64 

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. 

68 

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) 

77 

78 def get_share(self, name, share_proto): 

79 # Validate the share protocol 

80 proto = share_proto.upper() 

81 

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) 

89 

90 @staticmethod 

91 def delete_share(share): 

92 share.delete() 

93 

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) 

106 

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) 

113 

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) 

122 

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 

129 

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) 

138 

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) 

141 

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}) 

156 

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) 

164 

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) 

177 

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) 

189 

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) 

197 

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}) 

206 

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 

212 

213 try: 

214 return snap.create_snap(dst_snap_name) 

215 except storops_ex.UnitySnapNameInUseError: 

216 return self.get_snapshot(dst_snap_name) 

217 

218 def get_snapshot(self, name): 

219 return self.system.get_snap(name=name) 

220 

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) 

227 

228 def get_pool(self, name=None): 

229 return self.system.get_pool(name=name) 

230 

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 

238 

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) 

242 

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) 

246 

247 def cifs_allow_access(self, share_name, user_name, access_level): 

248 share = self.system.get_cifs_share(name=share_name) 

249 

250 if access_level == const.ACCESS_LEVEL_RW: 

251 cifs_access = enums.ACEAccessLevelEnum.WRITE 

252 else: 

253 cifs_access = enums.ACEAccessLevelEnum.READ 

254 

255 share.add_ace(user=user_name, access_level=cifs_access) 

256 

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) 

265 

266 def cifs_deny_access(self, share_name, user_name): 

267 share = self.system.get_cifs_share(name=share_name) 

268 

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}) 

274 

275 def nfs_deny_access(self, share_name, host_ip): 

276 share = self.system.get_nfs_share(name=share_name) 

277 

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}) 

283 

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) 

290 

291 return link_up_ports 

292 

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 

301 

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 

315 

316 @staticmethod 

317 def _is_external_port(port_id): 

318 return 'eth' in port_id or '_la' in port_id 

319 

320 def get_tenant(self, name, vlan_id): 

321 if not vlan_id: 

322 # Do not create vlan for flat network 

323 return None 

324 

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.") 

341 

342 return tenant 

343 

344 def restore_snapshot(self, snap_name): 

345 snap = self.get_snapshot(snap_name) 

346 return snap.restore(delete_backup=True)