Coverage for manila/tests/scheduler/filters/test_json.py: 100%

114 statements  

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

1# Copyright 2011 OpenStack Foundation. 

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

17Tests For JsonFilter. 

18""" 

19 

20from oslo_serialization import jsonutils 

21 

22from manila.scheduler.filters import json 

23from manila import test 

24from manila.tests.scheduler import fakes 

25 

26 

27class HostFiltersTestCase(test.TestCase): 

28 """Test case for JsonFilter.""" 

29 

30 def setUp(self): 

31 super(HostFiltersTestCase, self).setUp() 

32 self.json_query = jsonutils.dumps( 

33 ['and', ['>=', '$free_ram_mb', 1024], 

34 ['>=', '$free_disk_mb', 200 * 1024]]) 

35 self.filter = json.JsonFilter() 

36 

37 def test_json_filter_passes(self): 

38 filter_properties = {'resource_type': {'memory_mb': 1024, 

39 'root_gb': 200, 

40 'ephemeral_gb': 0}, 

41 'scheduler_hints': {'query': self.json_query}} 

42 capabilities = {'enabled': True} 

43 host = fakes.FakeHostState('host1', 

44 {'free_ram_mb': 1024, 

45 'free_disk_mb': 200 * 1024, 

46 'capabilities': capabilities}) 

47 self.assertTrue(self.filter.host_passes(host, filter_properties)) 

48 

49 def test_json_filter_passes_with_no_query(self): 

50 filter_properties = {'resource_type': {'memory_mb': 1024, 

51 'root_gb': 200, 

52 'ephemeral_gb': 0}} 

53 capabilities = {'enabled': True} 

54 host = fakes.FakeHostState('host1', 

55 {'free_ram_mb': 0, 

56 'free_disk_mb': 0, 

57 'capabilities': capabilities}) 

58 self.assertTrue(self.filter.host_passes(host, filter_properties)) 

59 

60 def test_json_filter_fails_on_memory(self): 

61 filter_properties = {'resource_type': {'memory_mb': 1024, 

62 'root_gb': 200, 

63 'ephemeral_gb': 0}, 

64 'scheduler_hints': {'query': self.json_query}} 

65 capabilities = {'enabled': True} 

66 host = fakes.FakeHostState('host1', 

67 {'free_ram_mb': 1023, 

68 'free_disk_mb': 200 * 1024, 

69 'capabilities': capabilities}) 

70 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

71 

72 def test_json_filter_fails_on_disk(self): 

73 filter_properties = {'resource_type': {'memory_mb': 1024, 

74 'root_gb': 200, 

75 'ephemeral_gb': 0}, 

76 'scheduler_hints': {'query': self.json_query}} 

77 capabilities = {'enabled': True} 

78 host = fakes.FakeHostState('host1', 

79 {'free_ram_mb': 1024, 

80 'free_disk_mb': (200 * 1024) - 1, 

81 'capabilities': capabilities}) 

82 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

83 

84 def test_json_filter_fails_on_caps_disabled(self): 

85 json_query = jsonutils.dumps( 

86 ['and', ['>=', '$free_ram_mb', 1024], 

87 ['>=', '$free_disk_mb', 200 * 1024], 

88 '$capabilities.enabled']) 

89 filter_properties = {'resource_type': {'memory_mb': 1024, 

90 'root_gb': 200, 

91 'ephemeral_gb': 0}, 

92 'scheduler_hints': {'query': json_query}} 

93 capabilities = {'enabled': False} 

94 host = fakes.FakeHostState('host1', 

95 {'free_ram_mb': 1024, 

96 'free_disk_mb': 200 * 1024, 

97 'capabilities': capabilities}) 

98 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

99 

100 def test_json_filter_fails_on_service_disabled(self): 

101 json_query = jsonutils.dumps( 

102 ['and', ['>=', '$free_ram_mb', 1024], 

103 ['>=', '$free_disk_mb', 200 * 1024], 

104 ['not', '$service.disabled']]) 

105 filter_properties = {'resource_type': {'memory_mb': 1024, 

106 'local_gb': 200}, 

107 'scheduler_hints': {'query': json_query}} 

108 capabilities = {'enabled': True} 

109 host = fakes.FakeHostState('host1', 

110 {'free_ram_mb': 1024, 

111 'free_disk_mb': 200 * 1024, 

112 'capabilities': capabilities}) 

113 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

114 

115 def test_json_filter_happy_day(self): 

116 """Test json filter more thoroughly.""" 

117 raw = ['and', 

118 '$capabilities.enabled', 

119 ['=', '$capabilities.opt1', 'match'], 

120 ['or', 

121 ['and', 

122 ['<', '$free_ram_mb', 30], 

123 ['<', '$free_disk_mb', 300]], 

124 ['and', 

125 ['>', '$free_ram_mb', 30], 

126 ['>', '$free_disk_mb', 300]]]] 

127 filter_properties = { 

128 'scheduler_hints': { 

129 'query': jsonutils.dumps(raw), 

130 }, 

131 } 

132 

133 # Passes 

134 capabilities = {'enabled': True, 'opt1': 'match'} 

135 service = {'disabled': False} 

136 host = fakes.FakeHostState('host1', 

137 {'free_ram_mb': 10, 

138 'free_disk_mb': 200, 

139 'capabilities': capabilities, 

140 'service': service}) 

141 self.assertTrue(self.filter.host_passes(host, filter_properties)) 

142 

143 # Passes 

144 capabilities = {'enabled': True, 'opt1': 'match'} 

145 service = {'disabled': False} 

146 host = fakes.FakeHostState('host1', 

147 {'free_ram_mb': 40, 

148 'free_disk_mb': 400, 

149 'capabilities': capabilities, 

150 'service': service}) 

151 self.assertTrue(self.filter.host_passes(host, filter_properties)) 

152 

153 # Fails due to capabilities being disabled 

154 capabilities = {'enabled': False, 'opt1': 'match'} 

155 service = {'disabled': False} 

156 host = fakes.FakeHostState('host1', 

157 {'free_ram_mb': 40, 

158 'free_disk_mb': 400, 

159 'capabilities': capabilities, 

160 'service': service}) 

161 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

162 

163 # Fails due to being exact memory/disk we don't want 

164 capabilities = {'enabled': True, 'opt1': 'match'} 

165 service = {'disabled': False} 

166 host = fakes.FakeHostState('host1', 

167 {'free_ram_mb': 30, 

168 'free_disk_mb': 300, 

169 'capabilities': capabilities, 

170 'service': service}) 

171 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

172 

173 # Fails due to memory lower but disk higher 

174 capabilities = {'enabled': True, 'opt1': 'match'} 

175 service = {'disabled': False} 

176 host = fakes.FakeHostState('host1', 

177 {'free_ram_mb': 20, 

178 'free_disk_mb': 400, 

179 'capabilities': capabilities, 

180 'service': service}) 

181 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

182 

183 # Fails due to capabilities 'opt1' not equal 

184 capabilities = {'enabled': True, 'opt1': 'no-match'} 

185 service = {'enabled': True} 

186 host = fakes.FakeHostState('host1', 

187 {'free_ram_mb': 20, 

188 'free_disk_mb': 400, 

189 'capabilities': capabilities, 

190 'service': service}) 

191 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

192 

193 def test_json_filter_basic_operators(self): 

194 host = fakes.FakeHostState('host1', 

195 {'capabilities': {'enabled': True}}) 

196 # (operator, arguments, expected_result) 

197 ops_to_test = [ 

198 ['=', [1, 1], True], 

199 ['=', [1, 2], False], 

200 ['<', [1, 2], True], 

201 ['<', [1, 1], False], 

202 ['<', [2, 1], False], 

203 ['>', [2, 1], True], 

204 ['>', [2, 2], False], 

205 ['>', [2, 3], False], 

206 ['<=', [1, 2], True], 

207 ['<=', [1, 1], True], 

208 ['<=', [2, 1], False], 

209 ['>=', [2, 1], True], 

210 ['>=', [2, 2], True], 

211 ['>=', [2, 3], False], 

212 ['in', [1, 1], True], 

213 ['in', [1, 1, 2, 3], True], 

214 ['in', [4, 1, 2, 3], False], 

215 ['not', [True], False], 

216 ['not', [False], True], 

217 ['or', [True, False], True], 

218 ['or', [False, False], False], 

219 ['and', [True, True], True], 

220 ['and', [False, False], False], 

221 ['and', [True, False], False], 

222 # Nested ((True or False) and (2 > 1)) == Passes 

223 ['and', [['or', True, False], ['>', 2, 1]], True]] 

224 

225 for (op, args, expected) in ops_to_test: 

226 raw = [op] + args 

227 filter_properties = { 

228 'scheduler_hints': { 

229 'query': jsonutils.dumps(raw), 

230 }, 

231 } 

232 self.assertEqual(expected, 

233 self.filter.host_passes(host, filter_properties)) 

234 

235 # This results in [False, True, False, True] and if any are True 

236 # then it passes... 

237 raw = ['not', True, False, True, False] 

238 filter_properties = { 

239 'scheduler_hints': { 

240 'query': jsonutils.dumps(raw), 

241 }, 

242 } 

243 self.assertTrue(self.filter.host_passes(host, filter_properties)) 

244 

245 # This results in [False, False, False] and if any are True 

246 # then it passes...which this doesn't 

247 raw = ['not', True, True, True] 

248 filter_properties = { 

249 'scheduler_hints': { 

250 'query': jsonutils.dumps(raw), 

251 }, 

252 } 

253 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

254 

255 def test_json_filter_unknown_operator_raises(self): 

256 raw = ['!=', 1, 2] 

257 filter_properties = { 

258 'scheduler_hints': { 

259 'query': jsonutils.dumps(raw), 

260 }, 

261 } 

262 host = fakes.FakeHostState('host1', 

263 {'capabilities': {'enabled': True}}) 

264 self.assertRaises(KeyError, 

265 self.filter.host_passes, host, filter_properties) 

266 

267 def test_json_filter_type_errror_passes(self): 

268 filter_properties = { 

269 'scheduler_hints': None 

270 } 

271 host = fakes.FakeHostState('host1', 

272 {'capabilities': {'enabled': True}}) 

273 self.assertTrue(self.filter.host_passes(host, filter_properties)) 

274 

275 def test_json_filter_empty_filters_pass(self): 

276 host = fakes.FakeHostState('host1', 

277 {'capabilities': {'enabled': True}}) 

278 

279 raw = [] 

280 filter_properties = { 

281 'scheduler_hints': { 

282 'query': jsonutils.dumps(raw), 

283 }, 

284 } 

285 self.assertTrue(self.filter.host_passes(host, filter_properties)) 

286 raw = {} 

287 filter_properties = { 

288 'scheduler_hints': { 

289 'query': jsonutils.dumps(raw), 

290 }, 

291 } 

292 self.assertTrue(self.filter.host_passes(host, filter_properties)) 

293 

294 def test_json_filter_invalid_num_arguments_fails(self): 

295 host = fakes.FakeHostState('host1', 

296 {'capabilities': {'enabled': True}}) 

297 

298 raw = ['>', ['and', ['or', ['not', ['<', ['>=', ['<=', ['in', ]]]]]]]] 

299 filter_properties = { 

300 'scheduler_hints': { 

301 'query': jsonutils.dumps(raw), 

302 }, 

303 } 

304 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

305 

306 raw = ['>', 1] 

307 filter_properties = { 

308 'scheduler_hints': { 

309 'query': jsonutils.dumps(raw), 

310 }, 

311 } 

312 self.assertFalse(self.filter.host_passes(host, filter_properties)) 

313 

314 def test_json_filter_unknown_variable_ignored(self): 

315 host = fakes.FakeHostState('host1', 

316 {'capabilities': {'enabled': True}}) 

317 

318 raw = ['=', '$........', 1, 1] 

319 filter_properties = { 

320 'scheduler_hints': { 

321 'query': jsonutils.dumps(raw), 

322 }, 

323 } 

324 self.assertTrue(self.filter.host_passes(host, filter_properties)) 

325 

326 raw = ['=', '$foo', 2, 2] 

327 filter_properties = { 

328 'scheduler_hints': { 

329 'query': jsonutils.dumps(raw), 

330 }, 

331 } 

332 self.assertTrue(self.filter.host_passes(host, filter_properties))