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 ricxappframe.rmr.rmr_mocks import rmr_mocks
23 from ricxappframe.xapp_sdl import SDLWrapper
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("ricxappframe.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 # swap sdl for the fake backend
236 data.SDL = SDLWrapper(use_fake_sdl=True)
241 # launch the thread with a fake init func and a patched rcv func; we will "repatch" later
242 a1rmr.start_rmr_thread(init_func_override=noop, rcv_func_override=_fake_dequeue_none)
248 def test_workflow(client, monkeypatch, adm_type_good, adm_instance_good):
250 test a full A1 workflow
253 # put type and instance
254 _put_ac_type(client, adm_type_good)
255 _put_ac_instance(client, monkeypatch, adm_instance_good)
258 we test the state transition diagram of all 5 states here;
259 1. not in effect, not deleted
260 2. in effect, not deleted
261 3. in effect, deleted
262 4. not in effect, deleted
263 5. gone (timeout expires)
266 # try a status get but we didn't get any ACKs yet to test NOT IN EFFECT
267 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
269 # now pretend we did get a good ACK
270 a1rmr.replace_rcv_func(_fake_dequeue)
271 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", False)
273 # delete the instance
274 _delete_instance(client)
276 # status after a delete, but there are no messages yet, should still return
277 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", True)
279 # now pretend we deleted successfully
280 a1rmr.replace_rcv_func(_fake_dequeue_deleted)
282 # status should be reflected first (before delete triggers)
283 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
285 # instance should be totally gone after a few seconds
286 _instance_is_gone(client)
289 _delete_ac_type(client)
292 def test_cleanup_via_t1(client, monkeypatch, adm_type_good, adm_instance_good):
294 create a type, create an instance, but no acks ever come in, delete instance
296 _put_ac_type(client, adm_type_good)
298 a1rmr.replace_rcv_func(_fake_dequeue_none)
300 _put_ac_instance(client, monkeypatch, adm_instance_good)
303 here we test the state transition diagram when it never goes into effect:
304 1. not in effect, not deleted
305 2. not in effect, deleted
306 3. gone (timeout expires)
309 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
311 # delete the instance
312 _delete_instance(client)
314 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
316 # instance should be totally gone after a few seconds
317 _instance_is_gone(client)
320 _delete_ac_type(client)
323 def test_bad_instances(client, monkeypatch, adm_type_good):
325 test various failure modes
327 # put the type (needed for some of the tests below)
328 rmr_mocks.patch_rmr(monkeypatch)
329 res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
330 assert res.status_code == 201
333 res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
334 assert res.status_code == 400
337 res = client.put(ADM_CTRL_INSTANCE, data="notajson")
338 assert res.status_code == 415
340 # delete a non existent instance
341 res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS")
342 assert res.status_code == 404
344 # get a non existent instance
345 a1rmr.replace_rcv_func(_fake_dequeue)
346 res = client.get(ADM_CTRL_INSTANCE + "DARKNESS")
347 assert res.status_code == 404
349 # delete the type (as cleanup)
350 res = client.delete(ADM_CTRL_TYPE)
351 assert res.status_code == 204
355 def monkey_set(ns, key, value):
356 # set a key override function that throws sdl errors on certain keys
357 if key == "a1.policy_type.111":
358 raise RejectedByBackend()
359 if key == "a1.policy_type.112":
361 if key == "a1.policy_type.113":
364 monkeypatch.setattr("a1.data.SDL.set", monkey_set)
366 def create_alt_id(json, id):
368 Overwrites the json's policy type ID, attempts create and tests for 503
370 json['policy_type_id'] = id
371 url = "/a1-p/policytypes/{0}".format(id)
372 res = client.put(url, json=json)
373 assert res.status_code == 503
375 create_alt_id(adm_type_good, 111)
376 create_alt_id(adm_type_good, 112)
377 create_alt_id(adm_type_good, 113)
380 def test_illegal_types(client, adm_type_good):
385 res = client.put("/a1-p/policytypes/0", json=adm_type_good)
386 assert res.status_code == 400
388 res = client.put("/a1-p/policytypes/1", json=adm_type_good)
389 assert res.status_code == 400
391 res = client.put("/a1-p/policytypes/2147483648", json=adm_type_good)
392 assert res.status_code == 400
395 def test_healthcheck(client):
399 res = client.get("/a1-p/healthcheck")
400 assert res.status_code == 200
403 def test_metrics(client):
405 test Prometheus metrics
407 res = client.get("/a1-p/metrics")
408 assert res.status_code == 200
411 def teardown_module():
412 """module teardown"""
413 a1rmr.stop_rmr_thread()