Coverage for manila/tests/share/drivers/zfssa/test_zfssarest.py: 100%

197 statements  

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

1# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); you may 

4# not use this file except in compliance with the License. You may obtain 

5# a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

12# License for the specific language governing permissions and limitations 

13# under the License. 

14""" 

15Unit tests for Oracle's ZFSSA REST API. 

16""" 

17from unittest import mock 

18 

19from manila import exception 

20from manila.share.drivers.zfssa import restclient 

21from manila.share.drivers.zfssa import zfssarest 

22from manila import test 

23from manila.tests import fake_zfssa 

24 

25 

26class ZFSSAApiTestCase(test.TestCase): 

27 """Tests ZFSSAApi.""" 

28 

29 @mock.patch.object(zfssarest, 'factory_restclient') 

30 def setUp(self, _restclient): 

31 super(ZFSSAApiTestCase, self).setUp() 

32 self.host = 'fakehost' 

33 self.user = 'fakeuser' 

34 self.url = None 

35 self.pool = 'fakepool' 

36 self.project = 'fakeproject' 

37 self.share = 'fakeshare' 

38 self.snap = 'fakesnapshot' 

39 _restclient.return_value = fake_zfssa.FakeRestClient() 

40 self._zfssa = zfssarest.ZFSSAApi() 

41 self._zfssa.set_host('fakehost') 

42 

43 self.schema = { 

44 'property': 'manila_managed', 

45 'description': 'Managed by Manila', 

46 'type': 'Boolean', 

47 } 

48 

49 def _create_response(self, status): 

50 response = fake_zfssa.FakeResponse(status) 

51 return response 

52 

53 def test_enable_service(self): 

54 self.mock_object(self._zfssa.rclient, 'put') 

55 self._zfssa.rclient.put.return_value = self._create_response( 

56 restclient.Status.ACCEPTED) 

57 

58 self._zfssa.enable_service('nfs') 

59 self.assertEqual(1, self._zfssa.rclient.put.call_count) 

60 

61 self._zfssa.rclient.put.return_value = self._create_response( 

62 restclient.Status.OK) 

63 self.assertRaises(exception.ShareBackendException, 

64 self._zfssa.enable_service, 

65 'nfs') 

66 

67 def test_verify_avail_space(self): 

68 self.mock_object(self._zfssa, 'verify_project') 

69 self.mock_object(self._zfssa, 'get_project_stats') 

70 self._zfssa.get_project_stats.return_value = 2000 

71 

72 self._zfssa.verify_avail_space(self.pool, 

73 self.project, 

74 self.share, 

75 1000) 

76 self.assertEqual(1, self._zfssa.verify_project.call_count) 

77 self.assertEqual(1, self._zfssa.get_project_stats.call_count) 

78 self._zfssa.verify_project.assert_called_with(self.pool, self.project) 

79 self._zfssa.get_project_stats.assert_called_with(self.pool, 

80 self.project) 

81 

82 self._zfssa.get_project_stats.return_value = 900 

83 self.assertRaises(exception.ShareBackendException, 

84 self._zfssa.verify_avail_space, 

85 self.pool, 

86 self.project, 

87 self.share, 

88 1000) 

89 

90 def test_create_project(self): 

91 self.mock_object(self._zfssa, 'verify_pool') 

92 self.mock_object(self._zfssa.rclient, 'get') 

93 self.mock_object(self._zfssa.rclient, 'post') 

94 arg = { 

95 'name': self.project, 

96 'sharesmb': 'off', 

97 'sharenfs': 'off', 

98 'mountpoint': 'fakemnpt', 

99 } 

100 self._zfssa.rclient.get.return_value = self._create_response( 

101 restclient.Status.NOT_FOUND) 

102 self._zfssa.rclient.post.return_value = self._create_response( 

103 restclient.Status.CREATED) 

104 

105 self._zfssa.create_project(self.pool, self.project, arg) 

106 self.assertEqual(1, self._zfssa.rclient.get.call_count) 

107 self.assertEqual(1, self._zfssa.rclient.post.call_count) 

108 self.assertEqual(1, self._zfssa.verify_pool.call_count) 

109 self._zfssa.verify_pool.assert_called_with(self.pool) 

110 

111 self._zfssa.rclient.post.return_value = self._create_response( 

112 restclient.Status.NOT_FOUND) 

113 self.assertRaises(exception.ShareBackendException, 

114 self._zfssa.create_project, 

115 self.pool, 

116 self.project, 

117 arg) 

118 

119 def test_create_share(self): 

120 self.mock_object(self._zfssa, 'verify_avail_space') 

121 self.mock_object(self._zfssa.rclient, 'get') 

122 self.mock_object(self._zfssa.rclient, 'post') 

123 self._zfssa.rclient.get.return_value = self._create_response( 

124 restclient.Status.NOT_FOUND) 

125 self._zfssa.rclient.post.return_value = self._create_response( 

126 restclient.Status.CREATED) 

127 arg = { 

128 "name": self.share, 

129 "quota": 1, 

130 } 

131 

132 self._zfssa.create_share(self.pool, self.project, arg) 

133 self.assertEqual(1, self._zfssa.rclient.get.call_count) 

134 self.assertEqual(1, self._zfssa.rclient.post.call_count) 

135 self.assertEqual(1, self._zfssa.verify_avail_space.call_count) 

136 self._zfssa.verify_avail_space.assert_called_with(self.pool, 

137 self.project, 

138 arg, 

139 arg['quota']) 

140 

141 self._zfssa.rclient.post.return_value = self._create_response( 

142 restclient.Status.NOT_FOUND) 

143 self.assertRaises(exception.ShareBackendException, 

144 self._zfssa.create_share, 

145 self.pool, 

146 self.project, 

147 arg) 

148 

149 self._zfssa.rclient.get.return_value = self._create_response( 

150 restclient.Status.OK) 

151 self.assertRaises(exception.ShareBackendException, 

152 self._zfssa.create_share, 

153 self.pool, 

154 self.project, 

155 arg) 

156 

157 def test_modify_share(self): 

158 self.mock_object(self._zfssa.rclient, 'put') 

159 self._zfssa.rclient.put.return_value = self._create_response( 

160 restclient.Status.ACCEPTED) 

161 arg = {"name": "dummyname"} 

162 svc = self._zfssa.share_path % (self.pool, self.project, self.share) 

163 

164 self._zfssa.modify_share(self.pool, self.project, self.share, arg) 

165 self.assertEqual(1, self._zfssa.rclient.put.call_count) 

166 self._zfssa.rclient.put.assert_called_with(svc, arg) 

167 

168 self._zfssa.rclient.put.return_value = self._create_response( 

169 restclient.Status.BAD_REQUEST) 

170 self.assertRaises(exception.ShareBackendException, 

171 self._zfssa.modify_share, 

172 self.pool, 

173 self.project, 

174 self.share, 

175 arg) 

176 

177 def test_delete_share(self): 

178 self.mock_object(self._zfssa.rclient, 'delete') 

179 self._zfssa.rclient.delete.return_value = self._create_response( 

180 restclient.Status.NO_CONTENT) 

181 svc = self._zfssa.share_path % (self.pool, self.project, self.share) 

182 

183 self._zfssa.delete_share(self.pool, self.project, self.share) 

184 self.assertEqual(1, self._zfssa.rclient.delete.call_count) 

185 self._zfssa.rclient.delete.assert_called_with(svc) 

186 

187 def test_create_snapshot(self): 

188 self.mock_object(self._zfssa.rclient, 'post') 

189 self._zfssa.rclient.post.return_value = self._create_response( 

190 restclient.Status.CREATED) 

191 arg = {"name": self.snap} 

192 svc = self._zfssa.snapshots_path % (self.pool, 

193 self.project, 

194 self.share) 

195 

196 self._zfssa.create_snapshot(self.pool, 

197 self.project, 

198 self.share, 

199 self.snap) 

200 self.assertEqual(1, self._zfssa.rclient.post.call_count) 

201 self._zfssa.rclient.post.assert_called_with(svc, arg) 

202 

203 self._zfssa.rclient.post.return_value = self._create_response( 

204 restclient.Status.BAD_REQUEST) 

205 self.assertRaises(exception.ShareBackendException, 

206 self._zfssa.create_snapshot, 

207 self.pool, 

208 self.project, 

209 self.share, 

210 self.snap) 

211 

212 def test_delete_snapshot(self): 

213 self.mock_object(self._zfssa.rclient, 'delete') 

214 self._zfssa.rclient.delete.return_value = self._create_response( 

215 restclient.Status.NO_CONTENT) 

216 svc = self._zfssa.snapshot_path % (self.pool, 

217 self.project, 

218 self.share, 

219 self.snap) 

220 

221 self._zfssa.delete_snapshot(self.pool, 

222 self.project, 

223 self.share, 

224 self.snap) 

225 self.assertEqual(1, self._zfssa.rclient.delete.call_count) 

226 self._zfssa.rclient.delete.assert_called_with(svc) 

227 

228 self._zfssa.rclient.delete.return_value = self._create_response( 

229 restclient.Status.BAD_REQUEST) 

230 self.assertRaises(exception.ShareBackendException, 

231 self._zfssa.delete_snapshot, 

232 self.pool, 

233 self.project, 

234 self.share, 

235 self.snap) 

236 

237 def test_clone_snapshot(self): 

238 self.mock_object(self._zfssa, 'verify_avail_space') 

239 self.mock_object(self._zfssa.rclient, 'put') 

240 self._zfssa.rclient.put.return_value = self._create_response( 

241 restclient.Status.CREATED) 

242 snapshot = { 

243 "id": self.snap, 

244 "share_id": self.share, 

245 } 

246 clone = { 

247 "id": "cloneid", 

248 "size": 1, 

249 } 

250 arg = { 

251 "name": "dummyname", 

252 "quota": 1, 

253 } 

254 

255 self._zfssa.clone_snapshot(self.pool, 

256 self.project, 

257 snapshot, 

258 clone, 

259 arg) 

260 self.assertEqual(1, self._zfssa.rclient.put.call_count) 

261 self.assertEqual(1, self._zfssa.verify_avail_space.call_count) 

262 self._zfssa.verify_avail_space.assert_called_with(self.pool, 

263 self.project, 

264 clone['id'], 

265 clone['size']) 

266 

267 self._zfssa.rclient.put.return_value = self._create_response( 

268 restclient.Status.NOT_FOUND) 

269 self.assertRaises(exception.ShareBackendException, 

270 self._zfssa.clone_snapshot, 

271 self.pool, 

272 self.project, 

273 snapshot, 

274 clone, 

275 arg) 

276 

277 def _create_entry(self, sharenfs, ip): 

278 if sharenfs == 'off': 

279 sharenfs = 'sec=sys' 

280 entry = (',rw=@%s' % ip) 

281 if '/' not in ip: 

282 entry = entry + '/32' 

283 arg = {'sharenfs': sharenfs + entry} 

284 return arg 

285 

286 def test_allow_access_nfs(self): 

287 self.mock_object(self._zfssa, 'get_share') 

288 self.mock_object(self._zfssa, 'modify_share') 

289 details = {"sharenfs": "off"} 

290 access = { 

291 "access_type": "nonip", 

292 "access_to": "foo", 

293 } 

294 

295 # invalid access type 

296 self.assertRaises(exception.InvalidShareAccess, 

297 self._zfssa.allow_access_nfs, 

298 self.pool, 

299 self.project, 

300 self.share, 

301 access) 

302 

303 # valid entry 

304 access.update({"access_type": "ip"}) 

305 arg = self._create_entry("off", access['access_to']) 

306 self._zfssa.get_share.return_value = details 

307 self._zfssa.allow_access_nfs(self.pool, 

308 self.project, 

309 self.share, 

310 access) 

311 self.assertEqual(1, self._zfssa.get_share.call_count) 

312 self.assertEqual(1, self._zfssa.modify_share.call_count) 

313 self._zfssa.get_share.assert_called_with(self.pool, 

314 self.project, 

315 self.share) 

316 self._zfssa.modify_share.assert_called_with(self.pool, 

317 self.project, 

318 self.share, 

319 arg) 

320 

321 # add another entry 

322 access.update({"access_to": "10.0.0.1/24"}) 

323 arg = self._create_entry("off", access['access_to']) 

324 self._zfssa.allow_access_nfs(self.pool, 

325 self.project, 

326 self.share, 

327 access) 

328 self.assertEqual(2, self._zfssa.modify_share.call_count) 

329 self._zfssa.modify_share.assert_called_with(self.pool, 

330 self.project, 

331 self.share, 

332 arg) 

333 

334 # verify modify_share is not called if sharenfs='on' 

335 details = {"sharenfs": "on"} 

336 self._zfssa.get_share.return_value = details 

337 self._zfssa.allow_access_nfs(self.pool, 

338 self.project, 

339 self.share, 

340 access) 

341 self.assertEqual(2, self._zfssa.modify_share.call_count) 

342 

343 # verify modify_share is not called if ip is already in the list 

344 access.update({"access_to": "10.0.0.1/24"}) 

345 details = self._create_entry("off", access['access_to']) 

346 self._zfssa.get_share.return_value = details 

347 self._zfssa.allow_access_nfs(self.pool, 

348 self.project, 

349 self.share, 

350 access) 

351 self.assertEqual(2, self._zfssa.modify_share.call_count) 

352 

353 def test_deny_access_nfs(self): 

354 self.mock_object(self._zfssa, 'get_share') 

355 self.mock_object(self._zfssa, 'modify_share') 

356 data1 = self._create_entry("off", "10.0.0.1") 

357 access = { 

358 "access_type": "nonip", 

359 "access_to": "foo", 

360 } 

361 

362 # invalid access_type 

363 self.assertRaises(exception.InvalidShareAccess, 

364 self._zfssa.deny_access_nfs, 

365 self.pool, 

366 self.project, 

367 self.share, 

368 access) 

369 

370 # valid entry 

371 access.update({"access_type": "ip"}) 

372 self._zfssa.get_share.return_value = data1 

373 self._zfssa.deny_access_nfs(self.pool, 

374 self.project, 

375 self.share, 

376 access) 

377 self.assertEqual(1, self._zfssa.get_share.call_count) 

378 self.assertEqual(0, self._zfssa.modify_share.call_count) 

379 self._zfssa.get_share.assert_called_with(self.pool, 

380 self.project, 

381 self.share) 

382 # another valid entry 

383 data1 = self._create_entry(data1['sharenfs'], '10.0.0.2/24') 

384 data2 = self._create_entry(data1['sharenfs'], access['access_to']) 

385 self._zfssa.get_share.return_value = data2 

386 self._zfssa.deny_access_nfs(self.pool, 

387 self.project, 

388 self.share, 

389 access) 

390 self.assertEqual(2, self._zfssa.get_share.call_count) 

391 self.assertEqual(1, self._zfssa.modify_share.call_count) 

392 self._zfssa.get_share.assert_called_with(self.pool, 

393 self.project, 

394 self.share) 

395 self._zfssa.modify_share.assert_called_with(self.pool, 

396 self.project, 

397 self.share, 

398 data1) 

399 

400 def test_create_schema_negative(self): 

401 self.mock_object(self._zfssa.rclient, 'get') 

402 self.mock_object(self._zfssa.rclient, 'post') 

403 self._zfssa.rclient.post.return_value = self._create_response( 

404 restclient.Status.NOT_FOUND) 

405 

406 self.assertRaises(exception.ShareBackendException, 

407 self._zfssa.create_schema, 

408 self.schema) 

409 

410 def test_create_schema_property_exists(self): 

411 self.mock_object(self._zfssa.rclient, 'get') 

412 self.mock_object(self._zfssa.rclient, 'post') 

413 self._zfssa.rclient.get.return_value = self._create_response( 

414 restclient.Status.OK) 

415 

416 self._zfssa.create_schema(self.schema) 

417 

418 self.assertEqual(1, self._zfssa.rclient.get.call_count) 

419 self.assertEqual(0, self._zfssa.rclient.post.call_count) 

420 

421 def test_create_schema(self): 

422 self.mock_object(self._zfssa.rclient, 'get') 

423 self.mock_object(self._zfssa.rclient, 'post') 

424 self._zfssa.rclient.get.return_value = self._create_response( 

425 restclient.Status.NOT_FOUND) 

426 self._zfssa.rclient.post.return_value = self._create_response( 

427 restclient.Status.CREATED) 

428 

429 self._zfssa.create_schema(self.schema) 

430 

431 self.assertEqual(1, self._zfssa.rclient.get.call_count) 

432 self.assertEqual(1, self._zfssa.rclient.post.call_count)