4 # ==================================================================================
5 # Copyright (c) 2019-2020 Nokia
6 # Copyright (c) 2018-2020 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
23 from a1 import a1rmr, data
24 from .a1test_helpers import MockSDLWrapper
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)
37 """for monkeypatching with a good status"""
39 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "OK"}
41 fake_msg = {"payload": pay, "message type": ACK_MT}
42 return [(fake_msg, None)]
45 def _fake_dequeue_none():
46 """for monkeypatching with no waiting messages"""
50 def _fake_dequeue_deleted():
51 """for monkeypatching with a DELETED status"""
53 good_pay = json.dumps(
54 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"}
57 # non existent type id
59 {"policy_type_id": 911, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"}
61 fake_msg = {"payload": pay, "message type": ACK_MT}
62 new_msgs.append((fake_msg, None))
66 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": "darkness", "handler_id": RCV_ID, "status": "DELETED"}
68 fake_msg = {"payload": pay, "message type": ACK_MT}
69 new_msgs.append((fake_msg, None))
71 # good body but bad message type
72 fake_msg = {"payload": good_pay, "message type": ACK_MT * 3}
73 new_msgs.append((fake_msg, None))
75 # insert a bad one with a malformed body to make sure we keep going
76 new_msgs.append(({"payload": "asdf", "message type": ACK_MT}, None))
79 new_msgs.append(("asdf", None))
82 fake_msg = {"payload": good_pay, "message type": ACK_MT}
83 new_msgs.append((fake_msg, None))
88 def _test_put_patch(monkeypatch):
89 rmr_mocks.patch_rmr(monkeypatch)
90 # assert that rmr bad states don't cause problems
91 monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
96 res = client.get(ADM_CTRL_TYPE)
97 assert res.status_code == 404
100 res = client.get("/a1-p/policytypes")
101 assert res.status_code == 200
102 assert res.json == []
104 # instance 404 because type not there yet
105 res = client.get(ADM_CTRL_POLICIES)
106 assert res.status_code == 404
109 def _put_ac_type(client, typedef):
113 res = client.put(ADM_CTRL_TYPE, json=typedef)
114 assert res.status_code == 201
117 res = client.put(ADM_CTRL_TYPE, json=typedef)
118 assert res.status_code == 400
121 res = client.get(ADM_CTRL_TYPE)
122 assert res.status_code == 200
123 assert res.json == typedef
126 res = client.get("/a1-p/policytypes")
127 assert res.status_code == 200
128 assert res.json == [ADM_CRTL_TID]
130 # instance 200 but empty list
131 res = client.get(ADM_CTRL_POLICIES)
132 assert res.status_code == 200
133 assert res.json == []
136 def _delete_ac_type(client):
137 res = client.delete(ADM_CTRL_TYPE)
138 assert res.status_code == 204
141 res = client.get(ADM_CTRL_TYPE)
142 assert res.status_code == 404
144 # cant invoke delete on it again
145 res = client.delete(ADM_CTRL_TYPE)
146 assert res.status_code == 404
151 def _put_ac_instance(client, monkeypatch, instancedef):
152 # no instance there yet
153 res = client.get(ADM_CTRL_INSTANCE)
154 assert res.status_code == 404
155 res = client.get(ADM_CTRL_INSTANCE_STATUS)
156 assert res.status_code == 404
158 # create a good instance
159 _test_put_patch(monkeypatch)
160 res = client.put(ADM_CTRL_INSTANCE, json=instancedef)
161 assert res.status_code == 202
163 # replace is allowed on instances
164 res = client.put(ADM_CTRL_INSTANCE, json=instancedef)
165 assert res.status_code == 202
167 # instance 200 and in list
168 res = client.get(ADM_CTRL_POLICIES)
169 assert res.status_code == 200
170 assert res.json == [ADM_CTRL_IID]
173 def _delete_instance(client):
174 # cant delete type until there are no instances
175 res = client.delete(ADM_CTRL_TYPE)
176 assert res.status_code == 400
179 res = client.delete(ADM_CTRL_INSTANCE)
180 assert res.status_code == 202
182 # should be able to do multiple deletes until it's actually gone
183 res = client.delete(ADM_CTRL_INSTANCE)
184 assert res.status_code == 202
187 def _instance_is_gone(client, seconds_to_try=10):
188 for _ in range(seconds_to_try):
189 # idea here is that we have to wait for the seperate thread to process the event
191 res = client.get(ADM_CTRL_INSTANCE_STATUS)
192 assert res.status_code == 404
193 except AssertionError:
196 res = client.get(ADM_CTRL_INSTANCE_STATUS)
197 assert res.status_code == 404
199 # list still 200 but no instance
200 res = client.get(ADM_CTRL_POLICIES)
201 assert res.status_code == 200
202 assert res.json == []
205 res = client.get(ADM_CTRL_INSTANCE)
206 assert res.status_code == 404
209 def _verify_instance_and_status(client, expected_instance, expected_status, expected_deleted, seconds_to_try=5):
211 res = client.get(ADM_CTRL_INSTANCE)
212 assert res.status_code == 200
213 assert res.json == expected_instance
215 for _ in range(seconds_to_try):
216 # idea here is that we have to wait for the seperate thread to process the event
217 res = client.get(ADM_CTRL_INSTANCE_STATUS)
218 assert res.status_code == 200
219 assert res.json["has_been_deleted"] == expected_deleted
221 assert res.json["instance_status"] == expected_status
223 except AssertionError:
225 assert res.json["instance_status"] == expected_status
232 """module level setup"""
234 data.SDL = MockSDLWrapper() # patch SDL
239 # launch the thread with a fake init func and a patched rcv func; we will "repatch" later
240 a1rmr.start_rmr_thread(init_func_override=noop, rcv_func_override=_fake_dequeue_none)
246 def test_workflow(client, monkeypatch, adm_type_good, adm_instance_good):
248 test a full A1 workflow
251 # put type and instance
252 _put_ac_type(client, adm_type_good)
253 _put_ac_instance(client, monkeypatch, adm_instance_good)
256 we test the state transition diagram of all 5 states here;
257 1. not in effect, not deleted
258 2. in effect, not deleted
259 3. in effect, deleted
260 4. not in effect, deleted
261 5. gone (timeout expires)
264 # try a status get but we didn't get any ACKs yet to test NOT IN EFFECT
265 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
267 # now pretend we did get a good ACK
268 a1rmr.replace_rcv_func(_fake_dequeue)
269 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", False)
271 # delete the instance
272 _delete_instance(client)
274 # status after a delete, but there are no messages yet, should still return
275 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", True)
277 # now pretend we deleted successfully
278 a1rmr.replace_rcv_func(_fake_dequeue_deleted)
280 # status should be reflected first (before delete triggers)
281 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
283 # instance should be totally gone after a few seconds
284 _instance_is_gone(client)
287 _delete_ac_type(client)
290 def test_cleanup_via_t1(client, monkeypatch, adm_type_good, adm_instance_good):
292 create a type, create an instance, but no acks ever come in, delete instance
294 _put_ac_type(client, adm_type_good)
296 a1rmr.replace_rcv_func(_fake_dequeue_none)
298 _put_ac_instance(client, monkeypatch, adm_instance_good)
301 here we test the state transition diagram when it never goes into effect:
302 1. not in effect, not deleted
303 2. not in effect, deleted
304 3. gone (timeout expires)
307 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
309 # delete the instance
310 _delete_instance(client)
312 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
314 # instance should be totally gone after a few seconds
315 _instance_is_gone(client)
318 _delete_ac_type(client)
321 def test_bad_instances(client, monkeypatch, adm_type_good):
323 test various failure modes
325 # put the type (needed for some of the tests below)
326 rmr_mocks.patch_rmr(monkeypatch)
327 res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
328 assert res.status_code == 201
331 res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
332 assert res.status_code == 400
335 res = client.put(ADM_CTRL_INSTANCE, data="notajson")
336 assert res.status_code == 415
338 # delete a non existent instance
339 res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS")
340 assert res.status_code == 404
342 # get a non existent instance
343 a1rmr.replace_rcv_func(_fake_dequeue)
344 res = client.get(ADM_CTRL_INSTANCE + "DARKNESS")
345 assert res.status_code == 404
347 # delete the type (as cleanup)
348 res = client.delete(ADM_CTRL_TYPE)
349 assert res.status_code == 204
352 res = client.put("/a1-p/policytypes/111", json=adm_type_good)
353 assert res.status_code == 503
354 res = client.put("/a1-p/policytypes/112", json=adm_type_good)
355 assert res.status_code == 503
356 res = client.put("/a1-p/policytypes/113", json=adm_type_good)
357 assert res.status_code == 503
360 def test_illegal_types(client, adm_type_good):
364 res = client.put("/a1-p/policytypes/0", json=adm_type_good)
365 assert res.status_code == 400
366 res = client.put("/a1-p/policytypes/2147483648", json=adm_type_good)
367 assert res.status_code == 400
370 def test_healthcheck(client):
374 res = client.get("/a1-p/healthcheck")
375 assert res.status_code == 200
378 def teardown_module():
379 """module teardown"""
380 a1rmr.stop_rmr_thread()