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)
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"""
237 # launch the thread with a fake init func and a patched rcv func; we will "repatch" later
238 a1rmr.start_rmr_thread(init_func_override=noop, rcv_func_override=_fake_dequeue_none)
244 def test_workflow(client, monkeypatch, adm_type_good, adm_instance_good):
246 test a full A1 workflow
248 _put_ac_type(client, adm_type_good)
249 _put_ac_instance(client, monkeypatch, adm_instance_good)
252 we test the state transition diagram of all 5 states here;
253 1. not in effect, not deleted
254 2. in effect, not deleted
255 3. in effect, deleted
256 4. not in effect, deleted
257 5. gone (timeout expires)
260 # try a status get but we didn't get any ACKs yet to test NOT IN EFFECT
261 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
263 # now pretend we did get a good ACK
264 a1rmr.replace_rcv_func(_fake_dequeue)
265 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", False)
267 # delete the instance
268 _delete_instance(client)
270 # status after a delete, but there are no messages yet, should still return
271 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", True)
273 # now pretend we deleted successfully
274 a1rmr.replace_rcv_func(_fake_dequeue_deleted)
276 # status should be reflected first (before delete triggers)
277 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
279 # instance should be totally gone after a few seconds
280 _instance_is_gone(client)
283 _delete_ac_type(client)
286 def test_cleanup_via_t1(client, monkeypatch, adm_type_good, adm_instance_good):
288 create a type, create an instance, but no acks ever come in, delete instance
290 _put_ac_type(client, adm_type_good)
292 a1rmr.replace_rcv_func(_fake_dequeue_none)
294 _put_ac_instance(client, monkeypatch, adm_instance_good)
297 here we test the state transition diagram when it never goes into effect:
298 1. not in effect, not deleted
299 2. not in effect, deleted
300 3. gone (timeout expires)
303 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
305 # delete the instance
306 _delete_instance(client)
308 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
310 # instance should be totally gone after a few seconds
311 _instance_is_gone(client)
314 _delete_ac_type(client)
317 def test_bad_instances(client, monkeypatch, adm_type_good):
319 test various failure modes
321 # put the type (needed for some of the tests below)
322 rmr_mocks.patch_rmr(monkeypatch)
323 res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
324 assert res.status_code == 201
327 res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
328 assert res.status_code == 400
331 res = client.put(ADM_CTRL_INSTANCE, data="notajson")
332 assert res.status_code == 415
334 # delete a non existent instance
335 res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS")
336 assert res.status_code == 404
338 # get a non existent instance
339 a1rmr.replace_rcv_func(_fake_dequeue)
340 res = client.get(ADM_CTRL_INSTANCE + "DARKNESS")
341 assert res.status_code == 404
343 # delete the type (as cleanup)
344 res = client.delete(ADM_CTRL_TYPE)
345 assert res.status_code == 204
348 def test_illegal_types(client, adm_type_good):
352 res = client.put("/a1-p/policytypes/0", json=adm_type_good)
353 assert res.status_code == 400
354 res = client.put("/a1-p/policytypes/2147483648", json=adm_type_good)
355 assert res.status_code == 400
358 def test_healthcheck(client):
362 res = client.get("/a1-p/healthcheck")
363 assert res.status_code == 200
366 def teardown_module():
367 """module teardown"""
368 a1rmr.stop_rmr_thread()