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 ricsdl.syncstorage import SyncStorage
24 from ricsdl.exceptions import RejectedByBackend, NotConnected, BackendError
25 from a1 import a1rmr, data
27 RCV_ID = "test_receiver"
28 ADM_CRTL_TID = 6660666
29 ADM_CTRL_IID = "admission_control_policy"
30 ADM_CTRL_POLICIES = "/a1-p/policytypes/{0}/policies".format(ADM_CRTL_TID)
31 ADM_CTRL_INSTANCE = ADM_CTRL_POLICIES + "/" + ADM_CTRL_IID
32 ADM_CTRL_INSTANCE_STATUS = ADM_CTRL_INSTANCE + "/status"
33 ADM_CTRL_TYPE = "/a1-p/policytypes/{0}".format(ADM_CRTL_TID)
38 """for monkeypatching with a good status"""
40 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "OK"}
42 fake_msg = {"payload": pay, "message type": ACK_MT}
43 return [(fake_msg, None)]
46 def _fake_dequeue_none():
47 """for monkeypatching with no waiting messages"""
51 def _fake_dequeue_deleted():
52 """for monkeypatching with a DELETED status"""
54 good_pay = json.dumps(
55 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"}
58 # non existent type id
60 {"policy_type_id": 911, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"}
62 fake_msg = {"payload": pay, "message type": ACK_MT}
63 new_msgs.append((fake_msg, None))
67 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": "darkness", "handler_id": RCV_ID, "status": "DELETED"}
69 fake_msg = {"payload": pay, "message type": ACK_MT}
70 new_msgs.append((fake_msg, None))
72 # good body but bad message type
73 fake_msg = {"payload": good_pay, "message type": ACK_MT * 3}
74 new_msgs.append((fake_msg, None))
76 # insert a bad one with a malformed body to make sure we keep going
77 new_msgs.append(({"payload": "asdf", "message type": ACK_MT}, None))
80 new_msgs.append(("asdf", None))
83 fake_msg = {"payload": good_pay, "message type": ACK_MT}
84 new_msgs.append((fake_msg, None))
89 def _test_put_patch(monkeypatch):
90 rmr_mocks.patch_rmr(monkeypatch)
91 # assert that rmr bad states don't cause problems
92 monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
97 res = client.get(ADM_CTRL_TYPE)
98 assert res.status_code == 404
101 res = client.get("/a1-p/policytypes")
102 assert res.status_code == 200
103 assert res.json == []
105 # instance 404 because type not there yet
106 res = client.get(ADM_CTRL_POLICIES)
107 assert res.status_code == 404
110 def _put_ac_type(client, typedef):
114 res = client.put(ADM_CTRL_TYPE, json=typedef)
115 assert res.status_code == 201
118 res = client.put(ADM_CTRL_TYPE, json=typedef)
119 assert res.status_code == 400
122 res = client.get(ADM_CTRL_TYPE)
123 assert res.status_code == 200
124 assert res.json == typedef
127 res = client.get("/a1-p/policytypes")
128 assert res.status_code == 200
129 assert res.json == [ADM_CRTL_TID]
131 # instance 200 but empty list
132 res = client.get(ADM_CTRL_POLICIES)
133 assert res.status_code == 200
134 assert res.json == []
137 def _delete_ac_type(client):
138 res = client.delete(ADM_CTRL_TYPE)
139 assert res.status_code == 204
142 res = client.get(ADM_CTRL_TYPE)
143 assert res.status_code == 404
145 # cant invoke delete on it again
146 res = client.delete(ADM_CTRL_TYPE)
147 assert res.status_code == 404
152 def _put_ac_instance(client, monkeypatch, instancedef):
153 # no instance there yet
154 res = client.get(ADM_CTRL_INSTANCE)
155 assert res.status_code == 404
156 res = client.get(ADM_CTRL_INSTANCE_STATUS)
157 assert res.status_code == 404
159 # create a good instance
160 _test_put_patch(monkeypatch)
161 res = client.put(ADM_CTRL_INSTANCE, json=instancedef)
162 assert res.status_code == 202
164 # replace is allowed on instances
165 res = client.put(ADM_CTRL_INSTANCE, json=instancedef)
166 assert res.status_code == 202
168 # instance 200 and in list
169 res = client.get(ADM_CTRL_POLICIES)
170 assert res.status_code == 200
171 assert res.json == [ADM_CTRL_IID]
174 def _delete_instance(client):
175 # cant delete type until there are no instances
176 res = client.delete(ADM_CTRL_TYPE)
177 assert res.status_code == 400
180 res = client.delete(ADM_CTRL_INSTANCE)
181 assert res.status_code == 202
183 # should be able to do multiple deletes until it's actually gone
184 res = client.delete(ADM_CTRL_INSTANCE)
185 assert res.status_code == 202
188 def _instance_is_gone(client, seconds_to_try=10):
189 for _ in range(seconds_to_try):
190 # idea here is that we have to wait for the seperate thread to process the event
192 res = client.get(ADM_CTRL_INSTANCE_STATUS)
193 assert res.status_code == 404
194 except AssertionError:
197 res = client.get(ADM_CTRL_INSTANCE_STATUS)
198 assert res.status_code == 404
200 # list still 200 but no instance
201 res = client.get(ADM_CTRL_POLICIES)
202 assert res.status_code == 200
203 assert res.json == []
206 res = client.get(ADM_CTRL_INSTANCE)
207 assert res.status_code == 404
210 def _verify_instance_and_status(client, expected_instance, expected_status, expected_deleted, seconds_to_try=5):
212 res = client.get(ADM_CTRL_INSTANCE)
213 assert res.status_code == 200
214 assert res.json == expected_instance
216 for _ in range(seconds_to_try):
217 # idea here is that we have to wait for the seperate thread to process the event
218 res = client.get(ADM_CTRL_INSTANCE_STATUS)
219 assert res.status_code == 200
220 assert res.json["has_been_deleted"] == expected_deleted
222 assert res.json["instance_status"] == expected_status
224 except AssertionError:
226 assert res.json["instance_status"] == expected_status
233 """module level setup"""
235 data.SDL.sdl = SyncStorage(fake_db_backend="dict")
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
252 # put type and instance
253 _put_ac_type(client, adm_type_good)
254 _put_ac_instance(client, monkeypatch, adm_instance_good)
257 we test the state transition diagram of all 5 states here;
258 1. not in effect, not deleted
259 2. in effect, not deleted
260 3. in effect, deleted
261 4. not in effect, deleted
262 5. gone (timeout expires)
265 # try a status get but we didn't get any ACKs yet to test NOT IN EFFECT
266 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
268 # now pretend we did get a good ACK
269 a1rmr.replace_rcv_func(_fake_dequeue)
270 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", False)
272 # delete the instance
273 _delete_instance(client)
275 # status after a delete, but there are no messages yet, should still return
276 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", True)
278 # now pretend we deleted successfully
279 a1rmr.replace_rcv_func(_fake_dequeue_deleted)
281 # status should be reflected first (before delete triggers)
282 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
284 # instance should be totally gone after a few seconds
285 _instance_is_gone(client)
288 _delete_ac_type(client)
291 def test_cleanup_via_t1(client, monkeypatch, adm_type_good, adm_instance_good):
293 create a type, create an instance, but no acks ever come in, delete instance
295 _put_ac_type(client, adm_type_good)
297 a1rmr.replace_rcv_func(_fake_dequeue_none)
299 _put_ac_instance(client, monkeypatch, adm_instance_good)
302 here we test the state transition diagram when it never goes into effect:
303 1. not in effect, not deleted
304 2. not in effect, deleted
305 3. gone (timeout expires)
308 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
310 # delete the instance
311 _delete_instance(client)
313 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
315 # instance should be totally gone after a few seconds
316 _instance_is_gone(client)
319 _delete_ac_type(client)
322 def test_bad_instances(client, monkeypatch, adm_type_good):
324 test various failure modes
326 # put the type (needed for some of the tests below)
327 rmr_mocks.patch_rmr(monkeypatch)
328 res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
329 assert res.status_code == 201
332 res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
333 assert res.status_code == 400
336 res = client.put(ADM_CTRL_INSTANCE, data="notajson")
337 assert res.status_code == 415
339 # delete a non existent instance
340 res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS")
341 assert res.status_code == 404
343 # get a non existent instance
344 a1rmr.replace_rcv_func(_fake_dequeue)
345 res = client.get(ADM_CTRL_INSTANCE + "DARKNESS")
346 assert res.status_code == 404
348 # delete the type (as cleanup)
349 res = client.delete(ADM_CTRL_TYPE)
350 assert res.status_code == 204
354 def monkey_set(key, value):
355 # set a key override function that throws sdl errors on certain keys
356 if key == "a1.policy_type.111":
357 raise RejectedByBackend()
358 if key == "a1.policy_type.112":
360 if key == "a1.policy_type.113":
363 monkeypatch.setattr("a1.data.SDL.set", monkey_set)
365 res = client.put("/a1-p/policytypes/111", json=adm_type_good)
366 assert res.status_code == 503
367 res = client.put("/a1-p/policytypes/112", json=adm_type_good)
368 assert res.status_code == 503
369 res = client.put("/a1-p/policytypes/113", json=adm_type_good)
370 assert res.status_code == 503
373 def test_illegal_types(client, adm_type_good):
377 res = client.put("/a1-p/policytypes/0", json=adm_type_good)
378 assert res.status_code == 400
379 res = client.put("/a1-p/policytypes/2147483648", json=adm_type_good)
380 assert res.status_code == 400
383 def test_healthcheck(client):
387 res = client.get("/a1-p/healthcheck")
388 assert res.status_code == 200
391 def teardown_module():
392 """module teardown"""
393 a1rmr.stop_rmr_thread()