4 # ==================================================================================
5 # Copyright (c) 2019 Nokia
6 # Copyright (c) 2018-2019 AT&T Intellectual Property.
8 # Licensed under the Apache License, Version 2.0 (the "License");
9 # you may not use this file except in compliance with the License.
10 # You may obtain a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS,
16 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 # See the License for the specific language governing permissions and
18 # limitations under the License.
19 # ==================================================================================
23 from rmr.rmr_mocks import rmr_mocks
26 RCV_ID = "test_receiver"
27 ADM_CRTL_TID = 6660666
28 ADM_CTRL_IID = "admission_control_policy"
29 ADM_CTRL_POLICIES = "/a1-p/policytypes/{0}/policies".format(ADM_CRTL_TID)
30 ADM_CTRL_INSTANCE = ADM_CTRL_POLICIES + "/" + ADM_CTRL_IID
31 ADM_CTRL_INSTANCE_STATUS = ADM_CTRL_INSTANCE + "/status"
32 ADM_CTRL_TYPE = "/a1-p/policytypes/{0}".format(ADM_CRTL_TID)
36 """for monkeypatching with a good status"""
38 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "OK"}
40 fake_msg = {"payload": pay}
44 def _fake_dequeue_none():
45 """for monkeypatching with no waiting messages"""
49 def _fake_dequeue_deleted():
50 """for monkeypatching with a DELETED status"""
55 {"policy_type_id": 911, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"}
57 fake_msg = {"payload": pay}
58 new_msgs.append(fake_msg)
61 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": "darkness", "handler_id": RCV_ID, "status": "DELETED"}
63 fake_msg = {"payload": pay}
64 new_msgs.append(fake_msg)
66 # insert a bad one with a malformed body to make sure we keep going
67 new_msgs.append({"payload": "asdf"})
70 new_msgs.append("asdf")
73 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"}
75 fake_msg = {"payload": pay}
76 new_msgs.append(fake_msg)
81 def _test_put_patch(monkeypatch):
82 rmr_mocks.patch_rmr(monkeypatch)
83 # assert that rmr bad states don't cause problems
84 monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
86 # we need this because free expects a real sbuf
87 # TODO: move this into rmr_mocks
91 monkeypatch.setattr("rmr.rmr.rmr_free_msg", noop)
93 # we need to repatch alloc (already patched in patch_rmr) to fix the transactionid, alloc is called in send and recieve
94 def fake_alloc(_unused1, _unused2, _unused3, _unused4, _unused5):
95 sbuf = rmr_mocks.Rmr_mbuf_t()
96 sbuf.contents.xaction = b"d49b53e478b711e9a1130242ac110002"
99 # we also need to repatch set, since in the send function, we alloc, then set a new transid
100 def fake_set_transactionid(sbuf):
101 sbuf.contents.xaction = b"d49b53e478b711e9a1130242ac110002"
103 # Note, we could have just patched summary, but this patches at a "lower level" so is a better test
104 monkeypatch.setattr("rmr.rmr.rmr_alloc_msg", fake_alloc)
105 monkeypatch.setattr("rmr.rmr.generate_and_set_transaction_id", fake_set_transactionid)
110 res = client.get(ADM_CTRL_TYPE)
111 assert res.status_code == 404
114 res = client.get("/a1-p/policytypes")
115 assert res.status_code == 200
116 assert res.json == []
118 # instance 404 because type not there yet
119 res = client.get(ADM_CTRL_POLICIES)
120 assert res.status_code == 404
123 def _put_ac_type(client, typedef):
127 res = client.put(ADM_CTRL_TYPE, json=typedef)
128 assert res.status_code == 201
131 res = client.put(ADM_CTRL_TYPE, json=typedef)
132 assert res.status_code == 400
135 res = client.get(ADM_CTRL_TYPE)
136 assert res.status_code == 200
137 assert res.json == typedef
140 res = client.get("/a1-p/policytypes")
141 assert res.status_code == 200
142 assert res.json == [ADM_CRTL_TID]
144 # instance 200 but empty list
145 res = client.get(ADM_CTRL_POLICIES)
146 assert res.status_code == 200
147 assert res.json == []
150 def _delete_ac_type(client):
151 res = client.delete(ADM_CTRL_TYPE)
152 assert res.status_code == 204
155 res = client.get(ADM_CTRL_TYPE)
156 assert res.status_code == 404
158 # cant invoke delete on it again
159 res = client.delete(ADM_CTRL_TYPE)
160 assert res.status_code == 404
165 def _put_ac_instance(client, monkeypatch, instancedef):
166 # no instance there yet
167 res = client.get(ADM_CTRL_INSTANCE)
168 assert res.status_code == 404
169 res = client.get(ADM_CTRL_INSTANCE_STATUS)
170 assert res.status_code == 404
172 # create a good instance
173 _test_put_patch(monkeypatch)
174 res = client.put(ADM_CTRL_INSTANCE, json=instancedef)
175 assert res.status_code == 202
177 # replace is allowed on instances
178 res = client.put(ADM_CTRL_INSTANCE, json=instancedef)
179 assert res.status_code == 202
181 # instance 200 and in list
182 res = client.get(ADM_CTRL_POLICIES)
183 assert res.status_code == 200
184 assert res.json == [ADM_CTRL_IID]
187 def _delete_instance(client):
188 # cant delete type until there are no instances
189 res = client.delete(ADM_CTRL_TYPE)
190 assert res.status_code == 400
193 res = client.delete(ADM_CTRL_INSTANCE)
194 assert res.status_code == 202
196 # should be able to do multiple deletes until it's actually gone
197 res = client.delete(ADM_CTRL_INSTANCE)
198 assert res.status_code == 202
201 def _instance_is_gone(client, seconds_to_try=10):
202 for _ in range(seconds_to_try):
203 # idea here is that we have to wait for the seperate thread to process the event
205 res = client.get(ADM_CTRL_INSTANCE_STATUS)
206 assert res.status_code == 404
207 except AssertionError:
210 res = client.get(ADM_CTRL_INSTANCE_STATUS)
211 assert res.status_code == 404
213 # list still 200 but no instance
214 res = client.get(ADM_CTRL_POLICIES)
215 assert res.status_code == 200
216 assert res.json == []
219 res = client.get(ADM_CTRL_INSTANCE)
220 assert res.status_code == 404
223 def _verify_instance_and_status(client, expected_instance, expected_status, expected_deleted, seconds_to_try=5):
225 res = client.get(ADM_CTRL_INSTANCE)
226 assert res.status_code == 200
227 assert res.json == expected_instance
229 for _ in range(seconds_to_try):
230 # idea here is that we have to wait for the seperate thread to process the event
231 res = client.get(ADM_CTRL_INSTANCE_STATUS)
232 assert res.status_code == 200
233 assert res.json["has_been_deleted"] == expected_deleted
235 assert res.json["instance_status"] == expected_status
237 except AssertionError:
239 assert res.json["instance_status"] == expected_status
246 """module level setup"""
251 # launch the thread with a fake init func and a patched rcv func; we will "repatch" later
252 a1rmr.start_rmr_thread(init_func_override=noop, rcv_func_override=_fake_dequeue_none)
258 def test_workflow(client, monkeypatch, adm_type_good, adm_instance_good):
260 test a full A1 workflow
262 _put_ac_type(client, adm_type_good)
263 _put_ac_instance(client, monkeypatch, adm_instance_good)
266 we test the state transition diagram of all 5 states here;
267 1. not in effect, not deleted
268 2. in effect, not deleted
269 3. in effect, deleted
270 4. not in effect, deleted
271 5. gone (timeout expires)
274 # try a status get but we didn't get any ACKs yet to test NOT IN EFFECT
275 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
277 # now pretend we did get a good ACK
278 a1rmr.replace_rcv_func(_fake_dequeue)
279 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", False)
281 # delete the instance
282 _delete_instance(client)
284 # status after a delete, but there are no messages yet, should still return
285 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", True)
287 # now pretend we deleted successfully
288 a1rmr.replace_rcv_func(_fake_dequeue_deleted)
290 # status should be reflected first (before delete triggers)
291 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
293 # instance should be totally gone after a few seconds
294 _instance_is_gone(client)
297 _delete_ac_type(client)
300 def test_cleanup_via_t1(client, monkeypatch, adm_type_good, adm_instance_good):
302 create a type, create an instance, but no acks ever come in, delete instance
304 _put_ac_type(client, adm_type_good)
306 a1rmr.replace_rcv_func(_fake_dequeue_none)
308 _put_ac_instance(client, monkeypatch, adm_instance_good)
311 here we test the state transition diagram when it never goes into effect:
312 1. not in effect, not deleted
313 2. not in effect, deleted
314 3. gone (timeout expires)
317 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
319 # delete the instance
320 _delete_instance(client)
322 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
324 # instance should be totally gone after a few seconds
325 _instance_is_gone(client)
328 _delete_ac_type(client)
331 def test_bad_instances(client, monkeypatch, adm_type_good):
333 test various failure modes
335 # put the type (needed for some of the tests below)
336 rmr_mocks.patch_rmr(monkeypatch)
337 res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
338 assert res.status_code == 201
341 res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
342 assert res.status_code == 400
345 res = client.put(ADM_CTRL_INSTANCE, data="notajson")
346 assert res.status_code == 415
348 # delete a non existent instance
349 res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS")
350 assert res.status_code == 404
352 # get a non existent instance
353 a1rmr.replace_rcv_func(_fake_dequeue)
354 res = client.get(ADM_CTRL_INSTANCE + "DARKNESS")
355 assert res.status_code == 404
357 # delete the type (as cleanup)
358 res = client.delete(ADM_CTRL_TYPE)
359 assert res.status_code == 204
362 def test_illegal_types(client, adm_type_good):
366 res = client.put("/a1-p/policytypes/0", json=adm_type_good)
367 assert res.status_code == 400
368 res = client.put("/a1-p/policytypes/2147483648", json=adm_type_good)
369 assert res.status_code == 400
372 def test_healthcheck(client):
376 res = client.get("/a1-p/healthcheck")
377 assert res.status_code == 200
380 def teardown_module():
381 """module teardown"""
382 a1rmr.stop_rmr_thread()