blob: 2aac5a98e164edee74c89ca654ce044b09b2ed4e [file] [log] [blame]
Tommy Carpenter0a3f6762019-11-06 09:24:16 -05001"""
2tests for controller
3"""
Tommy Carpenter5ad8f032019-05-30 14:33:21 -04004# ==================================================================================
Tommy Carpenterccb4a692020-01-02 14:38:21 -05005# Copyright (c) 2019-2020 Nokia
6# Copyright (c) 2018-2020 AT&T Intellectual Property.
Tommy Carpenter5ad8f032019-05-30 14:33:21 -04007#
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
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
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# ==================================================================================
Tommy Carpenter6b699102019-10-17 13:35:59 -040020import time
Tommy Carpentera7672bf2019-11-21 10:49:56 -050021import json
Tommy Carpenter2c1c4e92020-04-07 08:36:42 -040022from ricxappframe.rmr.rmr_mocks import rmr_mocks
Tommy Carpenter45f8f982020-03-06 09:41:57 -050023from ricxappframe.xapp_sdl import SDLWrapper
Tommy Carpenter0e3bc642020-01-13 09:51:27 -050024from ricsdl.exceptions import RejectedByBackend, NotConnected, BackendError
Tommy Carpenterccb4a692020-01-02 14:38:21 -050025from a1 import a1rmr, data
Tommy Carpenter5ad8f032019-05-30 14:33:21 -040026
Tommy Carpentera7672bf2019-11-21 10:49:56 -050027RCV_ID = "test_receiver"
28ADM_CRTL_TID = 6660666
29ADM_CTRL_IID = "admission_control_policy"
30ADM_CTRL_POLICIES = "/a1-p/policytypes/{0}/policies".format(ADM_CRTL_TID)
31ADM_CTRL_INSTANCE = ADM_CTRL_POLICIES + "/" + ADM_CTRL_IID
Tommy Carpenter40caa312019-09-12 16:24:10 -040032ADM_CTRL_INSTANCE_STATUS = ADM_CTRL_INSTANCE + "/status"
Tommy Carpentera7672bf2019-11-21 10:49:56 -050033ADM_CTRL_TYPE = "/a1-p/policytypes/{0}".format(ADM_CRTL_TID)
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050034ACK_MT = 20011
Tommy Carpenter40caa312019-09-12 16:24:10 -040035
36
Tommy Carpenter8bcc51a2019-10-21 16:07:31 -040037def _fake_dequeue():
Tommy Carpenter6b699102019-10-17 13:35:59 -040038 """for monkeypatching with a good status"""
Tommy Carpentera7672bf2019-11-21 10:49:56 -050039 pay = json.dumps(
40 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "OK"}
41 ).encode()
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050042 fake_msg = {"payload": pay, "message type": ACK_MT}
43 return [(fake_msg, None)]
Tommy Carpenter5ad8f032019-05-30 14:33:21 -040044
45
Tommy Carpenter8bcc51a2019-10-21 16:07:31 -040046def _fake_dequeue_none():
Tommy Carpenter6b699102019-10-17 13:35:59 -040047 """for monkeypatching with no waiting messages"""
Tommy Carpenterbfa46142019-09-26 11:14:16 -040048 return []
49
50
Tommy Carpenter8bcc51a2019-10-21 16:07:31 -040051def _fake_dequeue_deleted():
Tommy Carpenter6b699102019-10-17 13:35:59 -040052 """for monkeypatching with a DELETED status"""
Tommy Carpenter7cec82d2019-10-14 16:18:21 -040053 new_msgs = []
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050054 good_pay = json.dumps(
55 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"}
56 ).encode()
Tommy Carpenter7cec82d2019-10-14 16:18:21 -040057
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050058 # non existent type id
Tommy Carpentera7672bf2019-11-21 10:49:56 -050059 pay = json.dumps(
60 {"policy_type_id": 911, "policy_instance_id": ADM_CTRL_IID, "handler_id": RCV_ID, "status": "DELETED"}
61 ).encode()
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050062 fake_msg = {"payload": pay, "message type": ACK_MT}
63 new_msgs.append((fake_msg, None))
Tommy Carpenter7cec82d2019-10-14 16:18:21 -040064
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050065 # bad instance id
Tommy Carpentera7672bf2019-11-21 10:49:56 -050066 pay = json.dumps(
67 {"policy_type_id": ADM_CRTL_TID, "policy_instance_id": "darkness", "handler_id": RCV_ID, "status": "DELETED"}
68 ).encode()
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050069 fake_msg = {"payload": pay, "message type": ACK_MT}
70 new_msgs.append((fake_msg, None))
71
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))
Tommy Carpenter7cec82d2019-10-14 16:18:21 -040075
Tommy Carpenter8bcc51a2019-10-21 16:07:31 -040076 # insert a bad one with a malformed body to make sure we keep going
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050077 new_msgs.append(({"payload": "asdf", "message type": ACK_MT}, None))
Tommy Carpenteraa4ffa72019-11-14 15:54:49 -050078
79 # not even a json
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050080 new_msgs.append(("asdf", None))
Tommy Carpenter8bcc51a2019-10-21 16:07:31 -040081
Tommy Carpenter9d5ad712019-12-02 11:02:01 -050082 # good
83 fake_msg = {"payload": good_pay, "message type": ACK_MT}
84 new_msgs.append((fake_msg, None))
Tommy Carpenter7cec82d2019-10-14 16:18:21 -040085
86 return new_msgs
Tommy Carpenterbfa46142019-09-26 11:14:16 -040087
88
Tommy Carpenter5ad8f032019-05-30 14:33:21 -040089def _test_put_patch(monkeypatch):
Tommy Carpenter91ae8892019-09-18 10:45:50 -040090 rmr_mocks.patch_rmr(monkeypatch)
Tommy Carpenter6b699102019-10-17 13:35:59 -040091 # assert that rmr bad states don't cause problems
Tommy Carpenter2c1c4e92020-04-07 08:36:42 -040092 monkeypatch.setattr("ricxappframe.rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
Tommy Carpenter5ad8f032019-05-30 14:33:21 -040093
Tommy Carpenter5ad8f032019-05-30 14:33:21 -040094
Tommy Carpenter0a3f6762019-11-06 09:24:16 -050095def _no_ac(client):
96 # no type there yet
97 res = client.get(ADM_CTRL_TYPE)
98 assert res.status_code == 404
99
100 # no types at all
101 res = client.get("/a1-p/policytypes")
102 assert res.status_code == 200
103 assert res.json == []
104
105 # instance 404 because type not there yet
106 res = client.get(ADM_CTRL_POLICIES)
107 assert res.status_code == 404
108
109
110def _put_ac_type(client, typedef):
111 _no_ac(client)
112
113 # put the type
114 res = client.put(ADM_CTRL_TYPE, json=typedef)
115 assert res.status_code == 201
116
117 # cant replace types
118 res = client.put(ADM_CTRL_TYPE, json=typedef)
119 assert res.status_code == 400
120
121 # type there now
122 res = client.get(ADM_CTRL_TYPE)
123 assert res.status_code == 200
124 assert res.json == typedef
125
126 # type in type list
127 res = client.get("/a1-p/policytypes")
128 assert res.status_code == 200
Tommy Carpentera7672bf2019-11-21 10:49:56 -0500129 assert res.json == [ADM_CRTL_TID]
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500130
131 # instance 200 but empty list
132 res = client.get(ADM_CTRL_POLICIES)
133 assert res.status_code == 200
134 assert res.json == []
135
136
137def _delete_ac_type(client):
138 res = client.delete(ADM_CTRL_TYPE)
139 assert res.status_code == 204
140
141 # cant get
142 res = client.get(ADM_CTRL_TYPE)
143 assert res.status_code == 404
144
145 # cant invoke delete on it again
146 res = client.delete(ADM_CTRL_TYPE)
147 assert res.status_code == 404
148
149 _no_ac(client)
150
151
152def _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
158
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
163
164 # replace is allowed on instances
165 res = client.put(ADM_CTRL_INSTANCE, json=instancedef)
166 assert res.status_code == 202
167
168 # instance 200 and in list
169 res = client.get(ADM_CTRL_POLICIES)
170 assert res.status_code == 200
Tommy Carpentera7672bf2019-11-21 10:49:56 -0500171 assert res.json == [ADM_CTRL_IID]
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500172
173
174def _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
178
179 # delete it
180 res = client.delete(ADM_CTRL_INSTANCE)
181 assert res.status_code == 202
182
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
186
187
188def _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
191 try:
192 res = client.get(ADM_CTRL_INSTANCE_STATUS)
193 assert res.status_code == 404
194 except AssertionError:
195 time.sleep(1)
196
197 res = client.get(ADM_CTRL_INSTANCE_STATUS)
198 assert res.status_code == 404
199
200 # list still 200 but no instance
201 res = client.get(ADM_CTRL_POLICIES)
202 assert res.status_code == 200
203 assert res.json == []
204
205 # cant get instance
206 res = client.get(ADM_CTRL_INSTANCE)
207 assert res.status_code == 404
208
209
210def _verify_instance_and_status(client, expected_instance, expected_status, expected_deleted, seconds_to_try=5):
211 # get the instance
212 res = client.get(ADM_CTRL_INSTANCE)
213 assert res.status_code == 200
214 assert res.json == expected_instance
215
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
221 try:
222 assert res.json["instance_status"] == expected_status
223 return
224 except AssertionError:
225 time.sleep(1)
226 assert res.json["instance_status"] == expected_status
227
228
Tommy Carpenter6b699102019-10-17 13:35:59 -0400229# Module level Hack
230
231
Tommy Carpenter6b699102019-10-17 13:35:59 -0400232def setup_module():
233 """module level setup"""
Tommy Carpenter8bcc51a2019-10-21 16:07:31 -0400234
Tommy Carpenter45f8f982020-03-06 09:41:57 -0500235 # swap sdl for the fake backend
236 data.SDL = SDLWrapper(use_fake_sdl=True)
Tommy Carpenterccb4a692020-01-02 14:38:21 -0500237
Tommy Carpenter8bcc51a2019-10-21 16:07:31 -0400238 def noop():
239 pass
240
241 # launch the thread with a fake init func and a patched rcv func; we will "repatch" later
Tommy Carpenter0b42dfc2019-10-24 10:13:54 -0400242 a1rmr.start_rmr_thread(init_func_override=noop, rcv_func_override=_fake_dequeue_none)
Tommy Carpenter6b699102019-10-17 13:35:59 -0400243
244
Tommy Carpenterfdf05042019-07-18 20:21:21 +0000245# Actual Tests
246
247
Tommy Carpenter7cec82d2019-10-14 16:18:21 -0400248def test_workflow(client, monkeypatch, adm_type_good, adm_instance_good):
Tommy Carpenter6b699102019-10-17 13:35:59 -0400249 """
250 test a full A1 workflow
251 """
Tommy Carpenterccb4a692020-01-02 14:38:21 -0500252
253 # put type and instance
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500254 _put_ac_type(client, adm_type_good)
255 _put_ac_instance(client, monkeypatch, adm_instance_good)
Tommy Carpenter91ae8892019-09-18 10:45:50 -0400256
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500257 """
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)
264 """
Tommy Carpenterbfa46142019-09-26 11:14:16 -0400265
Tommy Carpenter6b699102019-10-17 13:35:59 -0400266 # try a status get but we didn't get any ACKs yet to test NOT IN EFFECT
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500267 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
Tommy Carpenterbfa46142019-09-26 11:14:16 -0400268
269 # now pretend we did get a good ACK
Tommy Carpenter0b42dfc2019-10-24 10:13:54 -0400270 a1rmr.replace_rcv_func(_fake_dequeue)
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500271 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", False)
Tommy Carpenter40caa312019-09-12 16:24:10 -0400272
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500273 # delete the instance
274 _delete_instance(client)
Tommy Carpenterbfa46142019-09-26 11:14:16 -0400275
276 # status after a delete, but there are no messages yet, should still return
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500277 _verify_instance_and_status(client, adm_instance_good, "IN EFFECT", True)
Tommy Carpenterbfa46142019-09-26 11:14:16 -0400278
279 # now pretend we deleted successfully
Tommy Carpenter0b42dfc2019-10-24 10:13:54 -0400280 a1rmr.replace_rcv_func(_fake_dequeue_deleted)
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500281
282 # status should be reflected first (before delete triggers)
283 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
284
285 # instance should be totally gone after a few seconds
286 _instance_is_gone(client)
Tommy Carpenterbfa46142019-09-26 11:14:16 -0400287
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400288 # delete the type
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500289 _delete_ac_type(client)
Tommy Carpenterbfa46142019-09-26 11:14:16 -0400290
Tommy Carpenter0a3f6762019-11-06 09:24:16 -0500291
292def test_cleanup_via_t1(client, monkeypatch, adm_type_good, adm_instance_good):
293 """
294 create a type, create an instance, but no acks ever come in, delete instance
295 """
296 _put_ac_type(client, adm_type_good)
297
298 a1rmr.replace_rcv_func(_fake_dequeue_none)
299
300 _put_ac_instance(client, monkeypatch, adm_instance_good)
301
302 """
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)
307 """
308
309 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", False)
310
311 # delete the instance
312 _delete_instance(client)
313
314 _verify_instance_and_status(client, adm_instance_good, "NOT IN EFFECT", True)
315
316 # instance should be totally gone after a few seconds
317 _instance_is_gone(client)
318
319 # delete the type
320 _delete_ac_type(client)
Tommy Carpenter24514462019-07-16 11:25:52 -0400321
322
Tommy Carpenter91ae8892019-09-18 10:45:50 -0400323def test_bad_instances(client, monkeypatch, adm_type_good):
Tommy Carpenter5ad8f032019-05-30 14:33:21 -0400324 """
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400325 test various failure modes
Tommy Carpenter5ad8f032019-05-30 14:33:21 -0400326 """
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400327 # put the type (needed for some of the tests below)
Tommy Carpenter91ae8892019-09-18 10:45:50 -0400328 rmr_mocks.patch_rmr(monkeypatch)
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400329 res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
330 assert res.status_code == 201
Tommy Carpenter91ae8892019-09-18 10:45:50 -0400331
332 # bad body
Tommy Carpenter40caa312019-09-12 16:24:10 -0400333 res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
Tommy Carpenter24514462019-07-16 11:25:52 -0400334 assert res.status_code == 400
335
Tommy Carpenter5ad8f032019-05-30 14:33:21 -0400336 # bad media type
Tommy Carpenter40caa312019-09-12 16:24:10 -0400337 res = client.put(ADM_CTRL_INSTANCE, data="notajson")
Tommy Carpenter5ad8f032019-05-30 14:33:21 -0400338 assert res.status_code == 415
339
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400340 # delete a non existent instance
341 res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS")
342 assert res.status_code == 404
343
344 # get a non existent instance
Tommy Carpenter0b42dfc2019-10-24 10:13:54 -0400345 a1rmr.replace_rcv_func(_fake_dequeue)
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400346 res = client.get(ADM_CTRL_INSTANCE + "DARKNESS")
347 assert res.status_code == 404
348
349 # delete the type (as cleanup)
350 res = client.delete(ADM_CTRL_TYPE)
351 assert res.status_code == 204
352
Tommy Carpenterccb4a692020-01-02 14:38:21 -0500353 # test 503 handlers
Tommy Carpenter0e3bc642020-01-13 09:51:27 -0500354
Tommy Carpenter45f8f982020-03-06 09:41:57 -0500355 def monkey_set(ns, key, value):
Tommy Carpenter0e3bc642020-01-13 09:51:27 -0500356 # 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":
360 raise NotConnected()
361 if key == "a1.policy_type.113":
362 raise BackendError()
363
364 monkeypatch.setattr("a1.data.SDL.set", monkey_set)
365
Tommy Carpenterccb4a692020-01-02 14:38:21 -0500366 res = client.put("/a1-p/policytypes/111", json=adm_type_good)
367 assert res.status_code == 503
368 res = client.put("/a1-p/policytypes/112", json=adm_type_good)
369 assert res.status_code == 503
370 res = client.put("/a1-p/policytypes/113", json=adm_type_good)
371 assert res.status_code == 503
372
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400373
Tommy Carpenter0b42dfc2019-10-24 10:13:54 -0400374def test_illegal_types(client, adm_type_good):
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400375 """
376 Test illegal types
377 """
Tommy Carpentera7672bf2019-11-21 10:49:56 -0500378 res = client.put("/a1-p/policytypes/0", json=adm_type_good)
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400379 assert res.status_code == 400
Tommy Carpentera7672bf2019-11-21 10:49:56 -0500380 res = client.put("/a1-p/policytypes/2147483648", json=adm_type_good)
Tommy Carpenterf87c8572019-10-07 11:15:45 -0400381 assert res.status_code == 400
382
Tommy Carpenterfdf05042019-07-18 20:21:21 +0000383
384def test_healthcheck(client):
385 """
386 test healthcheck
387 """
388 res = client.get("/a1-p/healthcheck")
389 assert res.status_code == 200
Tommy Carpenter6b699102019-10-17 13:35:59 -0400390
391
392def teardown_module():
393 """module teardown"""
Tommy Carpenter0b42dfc2019-10-24 10:13:54 -0400394 a1rmr.stop_rmr_thread()