Coverage for manila/share/drivers/dell_emc/driver.py: 92%

137 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright (c) 2019 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 

16""" 

17EMC specific NAS storage driver. This driver is a pluggable driver 

18that allows specific EMC NAS devices to be plugged-in as the underlying 

19backend. Use the Manila configuration variable "share_backend_name" 

20to specify, which backend plugins to use. 

21""" 

22 

23from oslo_config import cfg 

24from oslo_log import log 

25 

26from manila.share import driver 

27from manila.share.drivers.dell_emc import plugin_manager as manager 

28 

29EMC_NAS_OPTS = [ 

30 cfg.StrOpt('emc_nas_login', 

31 help='User name for the EMC server.'), 

32 cfg.StrOpt('emc_nas_password', 

33 secret=True, 

34 help='Password for the EMC server.'), 

35 cfg.HostAddressOpt('emc_nas_server', 

36 help='EMC server hostname or IP address.'), 

37 cfg.PortOpt('emc_nas_server_port', 

38 default=8080, 

39 help='Port number for the EMC server.'), 

40 cfg.BoolOpt('emc_nas_server_secure', 

41 default=True, 

42 help='Use secure connection to server.'), 

43 cfg.StrOpt('emc_share_backend', 

44 ignore_case=True, 

45 choices=['powerscale', 'isilon', 'vnx', 'unity', 'powermax', 

46 'powerstore', 'powerflex'], 

47 help='Share backend.'), 

48 cfg.StrOpt('emc_nas_root_dir', 

49 help='The root directory where shares will be located.'), 

50 cfg.BoolOpt('emc_ssl_cert_verify', 

51 default=True, 

52 help='If set to False the https client will not validate the ' 

53 'SSL certificate of the backend endpoint.'), 

54 cfg.StrOpt('emc_ssl_cert_path', 

55 help='Can be used to specify a non default path to a ' 

56 'CA_BUNDLE file or directory with certificates of trusted ' 

57 'CAs, which will be used to validate the backend.') 

58] 

59 

60LOG = log.getLogger(__name__) 

61 

62CONF = cfg.CONF 

63CONF.register_opts(EMC_NAS_OPTS) 

64 

65 

66class EMCShareDriver(driver.ShareDriver): 

67 """EMC specific NAS driver. Allows for NFS and CIFS NAS storage usage.""" 

68 

69 def __init__(self, *args, **kwargs): 

70 self.configuration = kwargs.get('configuration', None) 

71 if self.configuration: 71 ↛ 76line 71 didn't jump to line 76 because the condition on line 71 was always true

72 self.configuration.append_config_values(EMC_NAS_OPTS) 

73 self.backend_name = self.configuration.safe_get( 

74 'emc_share_backend') 

75 else: 

76 self.backend_name = CONF.emc_share_backend 

77 self.backend_name = self.backend_name or 'EMC_NAS_Storage' 

78 self.plugin_manager = manager.EMCPluginManager( 

79 namespace='manila.share.drivers.dell_emc.plugins') 

80 LOG.info("BACKEND IS: %s", self.backend_name) 

81 if self.backend_name == 'vnx': 81 ↛ 82line 81 didn't jump to line 82 because the condition on line 81 was never true

82 LOG.warning('Dell EMC VNX share driver has been deprecated and is ' 

83 'expected to be removed in a future release.') 

84 if self.backend_name == 'isilon': 84 ↛ 85line 84 didn't jump to line 85 because the condition on line 84 was never true

85 self.backend_name = 'powerscale' 

86 LOG.warning('Dell EMC isilon share driver has been deprecated and ' 

87 'is renamed to powerscale. It is expected ' 

88 'to be removed in a future release.') 

89 self.plugin = self.plugin_manager.load_plugin( 

90 self.backend_name, 

91 configuration=self.configuration) 

92 LOG.info(f"PLUGIN HAS: {self.plugin.__dict__}") 

93 super(EMCShareDriver, self).__init__( 

94 self.plugin.driver_handles_share_servers, *args, **kwargs) 

95 

96 self.dhss_mandatory_security_service_association = ( 

97 self.plugin.dhss_mandatory_security_service_association) 

98 

99 self.ipv6_implemented = getattr(self.plugin, 'ipv6_implemented', False) 

100 

101 self.revert_to_snap_support = getattr( 

102 self.plugin, 'revert_to_snap_support', False) 

103 self.shrink_share_support = getattr( 

104 self.plugin, 'shrink_share_support', False) 

105 self.manage_existing_support = getattr( 

106 self.plugin, 'manage_existing_support', False) 

107 self.manage_existing_with_server_support = getattr( 

108 self.plugin, 'manage_existing_with_server_support', False) 

109 self.manage_existing_snapshot_support = getattr( 

110 self.plugin, 'manage_existing_snapshot_support', False) 

111 self.manage_snapshot_with_server_support = getattr( 

112 self.plugin, 'manage_snapshot_with_server_support', False) 

113 self.manage_server_support = getattr( 

114 self.plugin, 'manage_server_support', False) 

115 self.get_share_server_network_info_support = getattr( 

116 self.plugin, 'get_share_server_network_info_support', False) 

117 

118 def manage_existing(self, share, driver_options): 

119 """manage an existing share""" 

120 if self.manage_existing_support: 

121 return self.plugin.manage_existing(share, driver_options) 

122 else: 

123 raise NotImplementedError() 

124 

125 def manage_existing_with_server(self, share, driver_options, 

126 share_server=None): 

127 """manage an existing share""" 

128 if self.manage_existing_with_server_support: 

129 return self.plugin.manage_existing_with_server( 

130 share, driver_options, share_server) 

131 else: 

132 return NotImplementedError() 

133 

134 def manage_existing_snapshot(self, snapshot, driver_options): 

135 """manage an existing share snapshot""" 

136 if self.manage_existing_snapshot_support: 

137 return self.plugin.manage_existing_snapshot(snapshot, 

138 driver_options) 

139 else: 

140 return NotImplementedError() 

141 

142 def manage_existing_snapshot_with_server(self, snapshot, driver_options, 

143 share_server=None): 

144 """manage an existing share snapshot""" 

145 if self.manage_snapshot_with_server_support: 

146 return self.plugin.manage_existing_snapshot_with_server( 

147 snapshot, driver_options, share_server=None) 

148 else: 

149 return NotImplementedError() 

150 

151 def manage_server(self, context, share_server, identifier, 

152 driver_options): 

153 if self.manage_server_support: 

154 return self.plugin.manage_server(context, share_server, 

155 identifier, driver_options) 

156 else: 

157 return NotImplementedError() 

158 

159 def get_share_server_network_info( 

160 self, context, share_server, identifier, driver_options): 

161 if self.get_share_server_network_info_support: 

162 return self.plugin.get_share_server_network_info( 

163 context, share_server, identifier, driver_options) 

164 else: 

165 return NotImplementedError() 

166 

167 def unmanage_server(self, server_details, security_services=None): 

168 LOG.info('Dell EMC driver will unmanage share server: %s out of ' 

169 'OpenStack.', server_details.get('server_id')) 

170 

171 def unmanage(self, share): 

172 LOG.info('Dell EMC driver will unmanage share: %s out of ' 

173 'OpenStack.', share.get('id')) 

174 

175 def unmanage_with_server(self, share, share_server=None): 

176 LOG.info('Dell EMC driver will unmanage share: %s out of ' 

177 'OpenStack.', share.get('id')) 

178 

179 def unmanage_snapshot(self, snapshot): 

180 LOG.info('Dell EMC driver will unmanage snapshot: %s out of ' 

181 'OpenStack.', snapshot.get('id')) 

182 

183 def unmanage_snapshot_with_server(self, snapshot, share_server=None): 

184 LOG.info('Dell EMC driver will unmanage snapshot: %s out of ' 

185 'OpenStack.', snapshot.get('id')) 

186 

187 def create_share(self, context, share, share_server=None): 

188 """Is called to create share.""" 

189 location = self.plugin.create_share(context, share, share_server) 

190 

191 return location 

192 

193 def create_share_from_snapshot(self, context, share, snapshot, 

194 share_server=None, parent_share=None): 

195 """Is called to create share from snapshot.""" 

196 location = self.plugin.create_share_from_snapshot( 

197 context, share, snapshot, share_server) 

198 

199 return location 

200 

201 def extend_share(self, share, new_size, share_server=None): 

202 """Is called to extend share.""" 

203 self.plugin.extend_share(share, new_size, share_server) 

204 

205 def shrink_share(self, share, new_size, share_server=None): 

206 """Is called to shrink share.""" 

207 if self.shrink_share_support: 

208 self.plugin.shrink_share(share, new_size, share_server) 

209 else: 

210 raise NotImplementedError() 

211 

212 def create_snapshot(self, context, snapshot, share_server=None): 

213 """Is called to create snapshot.""" 

214 return self.plugin.create_snapshot(context, snapshot, share_server) 

215 

216 def delete_share(self, context, share, share_server=None): 

217 """Is called to remove share.""" 

218 self.plugin.delete_share(context, share, share_server) 

219 

220 def delete_snapshot(self, context, snapshot, share_server=None): 

221 """Is called to remove snapshot.""" 

222 self.plugin.delete_snapshot(context, snapshot, share_server) 

223 

224 def ensure_share(self, context, share, share_server=None): 

225 """Invoked to sure that share is exported.""" 

226 self.plugin.ensure_share(context, share, share_server) 

227 

228 def allow_access(self, context, share, access, share_server=None): 

229 """Allow access to the share.""" 

230 self.plugin.allow_access(context, share, access, share_server) 

231 

232 def deny_access(self, context, share, access, share_server=None): 

233 """Deny access to the share.""" 

234 self.plugin.deny_access(context, share, access, share_server) 

235 

236 def update_access(self, context, share, access_rules, add_rules, 

237 delete_rules, update_rules, share_server=None): 

238 """Update access to the share.""" 

239 return self.plugin.update_access(context, share, access_rules, 

240 add_rules, delete_rules, share_server) 

241 

242 def check_for_setup_error(self): 

243 """Check for setup error.""" 

244 self.plugin.check_for_setup_error() 

245 

246 def do_setup(self, context): 

247 """Any initialization the share driver does while starting.""" 

248 self.plugin.connect(self, context) 

249 

250 def _update_share_stats(self): 

251 """Retrieve stats info from share.""" 

252 

253 backend_name = self.configuration.safe_get( 

254 'share_backend_name') or "EMC_NAS_Storage" 

255 data = dict( 

256 share_backend_name=backend_name, 

257 vendor_name='Dell EMC', 

258 storage_protocol='NFS_CIFS', 

259 snapshot_support=True, 

260 create_share_from_snapshot_support=True, 

261 revert_to_snapshot_support=self.revert_to_snap_support) 

262 self.plugin.update_share_stats(data) 

263 super(EMCShareDriver, self)._update_share_stats(data) 

264 LOG.info(f"Updated share stats: {self._stats}") 

265 

266 def get_network_allocations_number(self): 

267 """Returns number of network allocations for creating VIFs.""" 

268 return self.plugin.get_network_allocations_number() 

269 

270 def _setup_server(self, network_info, metadata=None): 

271 """Set up and configures share server with given network parameters.""" 

272 # NOTE(felipe_rodrigues): keep legacy network_info support as a dict. 

273 network_info = network_info[0] 

274 

275 return self.plugin.setup_server(network_info, metadata) 

276 

277 def _teardown_server(self, server_details, security_services=None): 

278 """Teardown share server.""" 

279 return self.plugin.teardown_server(server_details, security_services) 

280 

281 def get_configured_ip_versions(self): 

282 if self.ipv6_implemented: 

283 return [4, 6] 

284 else: 

285 return [4] 

286 

287 def revert_to_snapshot(self, context, snapshot, share_access_rules, 

288 snapshot_access_rules, share_server=None): 

289 if self.revert_to_snap_support: 

290 return self.plugin.revert_to_snapshot(context, snapshot, 

291 share_access_rules, 

292 snapshot_access_rules, 

293 share_server) 

294 else: 

295 raise NotImplementedError() 

296 

297 def get_default_filter_function(self): 

298 if hasattr(self.plugin, 'get_default_filter_function'): 

299 return self.plugin.get_default_filter_function() 

300 return None 

301 

302 def get_backend_info(self, context): 

303 """Get driver and array configuration parameters.""" 

304 if hasattr(self.plugin, 'get_backend_info'): 304 ↛ 306line 304 didn't jump to line 306 because the condition on line 304 was always true

305 return self.plugin.get_backend_info(context) 

306 raise NotImplementedError() 

307 

308 def ensure_shares(self, context, shares): 

309 """Invoked to ensure that shares are exported.""" 

310 if hasattr(self.plugin, 'ensure_shares'): 310 ↛ 312line 310 didn't jump to line 312 because the condition on line 310 was always true

311 return self.plugin.ensure_shares(context, shares) 

312 raise NotImplementedError() 

313 

314 def snapshot_update_access(self, context, snapshot, access_rules, 

315 add_rules, delete_rules, share_server=None): 

316 """Update snapshot access""" 

317 if hasattr(self.plugin, 'snapshot_update_access'): 317 ↛ 326line 317 didn't jump to line 326 because the condition on line 317 was always true

318 return self.plugin.snapshot_update_access( 

319 context=context, 

320 snapshot=snapshot, 

321 access_rules=access_rules, 

322 add_rules=add_rules, 

323 delete_rules=delete_rules, 

324 share_server=share_server, 

325 ) 

326 raise NotImplementedError()