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 # ==================================================================================
22 from rmr.rmr_mocks import rmr_mocks
26 ADM_CTRL = "admission_control_policy"
27 ADM_CTRL_POLICIES = "/a1-p/policytypes/20000/policies"
28 ADM_CTRL_INSTANCE = ADM_CTRL_POLICIES + "/" + ADM_CTRL
29 ADM_CTRL_INSTANCE_STATUS = ADM_CTRL_INSTANCE + "/status"
30 ADM_CTRL_TYPE = "/a1-p/policytypes/20000"
31 TEST_TYPE = "/a1-p/policytypes/20001"
35 """for monkeypatching with a good status"""
36 pay = b'{"policy_type_id": 20000, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "OK"}'
37 fake_msg = {"payload": pay}
41 def _fake_dequeue_none():
42 """for monkeypatching with no waiting messages"""
46 def _fake_dequeue_deleted():
47 """for monkeypatching with a DELETED status"""
50 # insert some that don't exist to make sure nothing blows up
51 pay = b'{"policy_type_id": 20666, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "DELETED"}'
52 fake_msg = {"payload": pay}
53 new_msgs.append(fake_msg)
55 pay = b'{"policy_type_id": 20000, "policy_instance_id": "darkness", "handler_id": "test_receiver", "status": "DELETED"}'
56 fake_msg = {"payload": pay}
57 new_msgs.append(fake_msg)
59 # insert a bad one with a malformed body to make sure we keep going
60 fake_msg = {"payload": "asdf"}
61 new_msgs.append(fake_msg)
63 pay = b'{"policy_type_id": 20000, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "DELETED"}'
64 fake_msg = {"payload": pay}
65 new_msgs.append(fake_msg)
70 def _test_put_patch(monkeypatch):
71 rmr_mocks.patch_rmr(monkeypatch)
72 # assert that rmr bad states don't cause problems
73 monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
75 # we need this because free expects a real sbuf
76 # TODO: move this into rmr_mocks
80 monkeypatch.setattr("rmr.rmr.rmr_free_msg", noop)
82 # we need to repatch alloc (already patched in patch_rmr) to fix the transactionid, alloc is called in send and recieve
83 def fake_alloc(_unused1, _unused2, _unused3, _unused4, _unused5):
84 sbuf = rmr_mocks.Rmr_mbuf_t()
85 sbuf.contents.xaction = b"d49b53e478b711e9a1130242ac110002"
88 # we also need to repatch set, since in the send function, we alloc, then set a new transid
89 def fake_set_transactionid(sbuf):
90 sbuf.contents.xaction = b"d49b53e478b711e9a1130242ac110002"
92 # Note, we could have just patched summary, but this patches at a "lower level" so is a better test
93 monkeypatch.setattr("rmr.rmr.rmr_alloc_msg", fake_alloc)
94 monkeypatch.setattr("rmr.rmr.generate_and_set_transaction_id", fake_set_transactionid)
99 res = client.get(ADM_CTRL_TYPE)
100 assert res.status_code == 404
103 res = client.get("/a1-p/policytypes")
104 assert res.status_code == 200
105 assert res.json == []
107 # instance 404 because type not there yet
108 res = client.get(ADM_CTRL_POLICIES)
109 assert res.status_code == 404
112 def _put_ac_type(client, typedef):
116 res = client.put(ADM_CTRL_TYPE, json=typedef)
117 assert res.status_code == 201
120 res = client.put(ADM_CTRL_TYPE, json=typedef)
121 assert res.status_code == 400
124 res = client.get(ADM_CTRL_TYPE)
125 assert res.status_code == 200
126 assert res.json == typedef
129 res = client.get("/a1-p/policytypes")
130 assert res.status_code == 200
131 assert res.json == [20000]
133 # instance 200 but empty list
134 res = client.get(ADM_CTRL_POLICIES)
135 assert res.status_code == 200
136 assert res.json == []
139 def _delete_ac_type(client):
140 res = client.delete(ADM_CTRL_TYPE)
141 assert res.status_code == 204
144 res = client.get(ADM_CTRL_TYPE)
145 assert res.status_code == 404
147 # cant invoke delete on it again
148 res = client.delete(ADM_CTRL_TYPE)
149 assert res.status_code == 404
154 def _put_ac_instance(client, monkeypatch, instancedef):
155 # no instance there yet
156 res = client.get(ADM_CTRL_INSTANCE)
157 assert res.status_code == 404
158 res = client.get(ADM_CTRL_INSTANCE_STATUS)
159 assert res.status_code == 404
161 # create a good instance
162 _test_put_patch(monkeypatch)
163 res = client.put(ADM_CTRL_INSTANCE, json=instancedef)
164 assert res.status_code == 202
166 # replace is allowed on instances
167 res = client.put(ADM_CTRL_INSTANCE, json=instancedef)
168 assert res.status_code == 202
170 # instance 200 and in list
171 res = client.get(ADM_CTRL_POLICIES)
172 assert res.status_code == 200
173 assert res.json == [ADM_CTRL]
176 def _delete_instance(client):
177 # cant delete type until there are no instances
178 res = client.delete(ADM_CTRL_TYPE)
179 assert res.status_code == 400
182 res = client.delete(ADM_CTRL_INSTANCE)
183 assert res.status_code == 202
185 # should be able to do multiple deletes until it's actually gone
186 res = client.delete(ADM_CTRL_INSTANCE)
187 assert res.status_code == 202
190 def _instance_is_gone(client, seconds_to_try=10):
191 for _ in range(seconds_to_try):
192 # idea here is that we have to wait for the seperate thread to process the event
194 res = client.get(ADM_CTRL_INSTANCE_STATUS)
195 assert res.status_code == 404
196 except AssertionError:
199 res = client.get(ADM_CTRL_INSTANCE_STATUS)
200 assert res.status_code == 404
202 # list still 200 but no instance
203 res = client.get(ADM_CTRL_POLICIES)
204 assert res.status_code == 200
205 assert res.json == []
208 res = client.get(ADM_CTRL_INSTANCE)
209 assert res.status_code == 404
212 def _verify_instance_and_status(client, expected_instance, expected_status, expected_deleted, seconds_to_try=5):
214 res = client.get(ADM_CTRL_INSTANCE)
215 assert res.status_code == 200
216 assert res.json == expected_instance
218 for _ in range(seconds_to_try):
219 # idea here is that we have to wait for the seperate thread to process the event
220 res = client.get(ADM_CTRL_INSTANCE_STATUS)
221 assert res.status_code == 200
222 assert res.json["has_been_deleted"] == expected_deleted
224 assert res.json["instance_status"] == expected_status
226 except AssertionError:
228 assert res.json["instance_status"] == expected_status
235 """module level setup"""
240 # launch the thread with a fake init func and a patched rcv func; we will "repatch" later
241 a1rmr.start_rmr_thread(init_func_override=noop, rcv_func_override=_fake_dequeue_none)
247 def test_workflow(client, monkeypatch, adm_type_good, adm_instance_good):
249 test a full A1 workflow
251 _put_ac_type(client, adm_type_good)
252 _put_ac_instance(client, monkeypatch, adm_instance_good)
255 we test the state transition diagram of all 5 states here;
256 1. not in effect, not deleted
257 2. in effect, not deleted
258 3. in effect, deleted
259 4. not in effect, deleted
260 5. gone (timeout expires)
263 # try a status get but we didn't get any ACKs yet to test NOT IN EFFECT
264 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
266 # now pretend we did get a good ACK
267 a1rmr.replace_rcv_func(_fake_dequeue)
268 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", False)
270 # delete the instance
271 _delete_instance(client)
273 # status after a delete, but there are no messages yet, should still return
274 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", True)
276 # now pretend we deleted successfully
277 a1rmr.replace_rcv_func(_fake_dequeue_deleted)
279 # status should be reflected first (before delete triggers)
280 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
282 # instance should be totally gone after a few seconds
283 _instance_is_gone(client)
286 _delete_ac_type(client)
289 def test_cleanup_via_t1(client, monkeypatch, adm_type_good, adm_instance_good):
291 create a type, create an instance, but no acks ever come in, delete instance
293 _put_ac_type(client, adm_type_good)
295 a1rmr.replace_rcv_func(_fake_dequeue_none)
297 _put_ac_instance(client, monkeypatch, adm_instance_good)
300 here we test the state transition diagram when it never goes into effect:
301 1. not in effect, not deleted
302 2. not in effect, deleted
303 3. gone (timeout expires)
306 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
308 # delete the instance
309 _delete_instance(client)
311 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
313 # instance should be totally gone after a few seconds
314 _instance_is_gone(client)
317 _delete_ac_type(client)
320 def test_bad_instances(client, monkeypatch, adm_type_good):
322 test various failure modes
324 # put the type (needed for some of the tests below)
325 rmr_mocks.patch_rmr(monkeypatch)
326 res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
327 assert res.status_code == 201
330 res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
331 assert res.status_code == 400
334 res = client.put(ADM_CTRL_INSTANCE, data="notajson")
335 assert res.status_code == 415
337 # delete a non existent instance
338 res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS")
339 assert res.status_code == 404
341 # get a non existent instance
342 a1rmr.replace_rcv_func(_fake_dequeue)
343 res = client.get(ADM_CTRL_INSTANCE + "DARKNESS")
344 assert res.status_code == 404
346 # delete the type (as cleanup)
347 res = client.delete(ADM_CTRL_TYPE)
348 assert res.status_code == 204
351 def test_illegal_types(client, adm_type_good):
355 res = client.put("/a1-p/policytypes/19999", json=adm_type_good)
356 assert res.status_code == 400
357 res = client.put("/a1-p/policytypes/21024", json=adm_type_good)
358 assert res.status_code == 400
361 def test_healthcheck(client):
365 res = client.get("/a1-p/healthcheck")
366 assert res.status_code == 200
369 def teardown_module():
370 """module teardown"""
371 a1rmr.stop_rmr_thread()