blob: ffde43573da12cc95f72286867e46bc465b7ebac [file] [log] [blame]
Matteo Scandolo0b986e22018-06-04 14:07:33 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import unittest
16import json
17import functools
18from mock import patch, call, Mock, PropertyMock
19import requests_mock
20
21import os, sys
22
23# Hack to load synchronizer framework
24test_path=os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
25xos_dir=os.path.join(test_path, "../../..")
26if not os.path.exists(os.path.join(test_path, "new_base")):
27 xos_dir=os.path.join(test_path, "../../../../../../orchestration/xos/xos")
28 services_dir = os.path.join(xos_dir, "../../xos_services")
29sys.path.append(xos_dir)
30sys.path.append(os.path.join(xos_dir, 'synchronizers', 'new_base'))
31# END Hack to load synchronizer framework
32
33# generate model from xproto
34def get_models_fn(service_name, xproto_name):
35 name = os.path.join(service_name, "xos", xproto_name)
36 if os.path.exists(os.path.join(services_dir, name)):
37 return name
38 else:
39 name = os.path.join(service_name, "xos", "synchronizer", "models", xproto_name)
40 if os.path.exists(os.path.join(services_dir, name)):
41 return name
42 raise Exception("Unable to find service=%s xproto=%s" % (service_name, xproto_name))
43# END generate model from xproto
44
45def match_none(req):
46 return req.text == None
47
48def match_json(desired, req):
49 if desired!=req.json():
50 raise Exception("Got request %s, but body is not matching" % req.url)
51 return False
52 return True
53
54class TestSyncOnosApp(unittest.TestCase):
55
56 def setUp(self):
57 global DeferredException
58
59 self.sys_path_save = sys.path
60 sys.path.append(xos_dir)
61 sys.path.append(os.path.join(xos_dir, 'synchronizers', 'new_base'))
62
63 # Setting up the config module
64 from xosconfig import Config
65 config = os.path.join(test_path, "../test_config.yaml")
66 Config.clear()
67 Config.init(config, "synchronizer-config-schema.yaml")
68 # END Setting up the config module
69
70 from synchronizers.new_base.mock_modelaccessor_build import build_mock_modelaccessor
71 build_mock_modelaccessor(xos_dir, services_dir, [
72 get_models_fn("onos-service", "onos.xproto")
73 ])
74 import synchronizers.new_base.modelaccessor
75
76 from sync_onos_app import SyncONOSApp, DeferredException, model_accessor
77
78 # import all class names to globals
79 for (k, v) in model_accessor.all_model_classes.items():
80 globals()[k] = v
81
82
83 self.sync_step = SyncONOSApp
84
85 onos = ONOSService()
86 onos.rest_hostname = "onos-url"
87 onos.rest_port = "8181"
88 onos.rest_username = "karaf"
89 onos.rest_password = "karaf"
90
91 self.onos_app = Mock(spec=[
92 'id',
93 'name',
94 'app_id',
95 'dependencies',
96 'owner',
97 'url',
98 'backend_code',
99 'version',
100 'tologdict'
101 ])
102 self.onos_app.id = 1
103 self.onos_app.name = "vrouter"
104 self.onos_app.app_id = "org.onosproject.vrouter"
105 self.onos_app.dependencies = ""
106 self.onos_app.owner.leaf_model = onos
107 self.onos_app.url = None
108 self.onos_app.class_names = "ONOSApp"
109 self.onos_app.tologdict.return_value = ""
110
111 self.si = Mock()
112 self.si.id = 1
113 self.si.leaf_model = self.onos_app
114
115 self.vrouter_app_response = {
116 "name": "org.onosproject.vrouter",
117 "version": "1.13.1",
118 }
119
120 self.onos_app_attribute = Mock(spec=[
121 'id',
122 'service_instance',
123 'name',
124 'value'
125 ])
126 self.onos_app_attribute.id = 1
127 self.onos_app_attribute.service_instance = self.si
128 self.onos_app_attribute.name = "/onos/v1/network/configuration/apps/org.opencord.olt"
129 self.onos_app_attribute.value = {
130 "kafka" : {
131 "bootstrapServers" : "cord-kafka-kafka.default.svc.cluster.local:9092"
132 }
133 }
134
135 def tearDown(self):
136 self.onos = None
137 sys.path = self.sys_path_save
138
139 @requests_mock.Mocker()
140 def test_defer_app_sync(self, m):
141 self.onos_app.dependencies = "org.onosproject.segmentrouting, org.onosproject.openflow"
142
143 segment_routing = Mock()
144 segment_routing.app_id = "org.onosproject.segmentrouting"
145 segment_routing.backend_code = 1
146
147 openflow = Mock()
148 openflow.app_id = "org.onosproject.openflow"
149 openflow.backend_code = 0
150
151 with patch.object(ONOSApp.objects, "get_items") as app_get, \
152 patch.object(ServiceInstance.objects, "get_items") as mock_si, \
153 self.assertRaises(DeferredException) as e:
154
155 app_get.return_value = [segment_routing, openflow]
156 mock_si.return_value = [self.si]
157 self.sync_step().sync_record(self.onos_app)
158
159 self.assertEqual(e.exception.message, 'Deferring installation of ONOSApp with id 1 as dependencies are not met')
160 self.assertFalse(m.called)
161
162 @requests_mock.Mocker()
Scott Baker9ee45e12018-08-21 16:58:39 -0700163 def test_dependencies_none(self, m):
164 """ App should sync if dependencies is set to None """
165
166 self.onos_app.dependencies = None
167
168 m.post("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter/active",
169 status_code=200,
170 additional_matcher=match_none)
171
172 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
173 status_code=200,
174 json=self.vrouter_app_response)
175
176 self.si.serviceinstanceattribute_dict = {}
177
178 with patch.object(ServiceInstance.objects, "get_items") as mock_si:
179 mock_si.return_value = [self.si]
180 self.sync_step().sync_record(self.onos_app)
181
182 self.assertTrue(m.called)
183 self.assertEqual(m.call_count, 2)
184 self.assertEqual(self.onos_app.version, self.vrouter_app_response["version"])
185
186 @requests_mock.Mocker()
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700187 def test_app_sync_local_app_no_config(self, m):
188 """
189 Activate an application that is already installed in ONOS
190 """
191
192 m.post("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter/active",
193 status_code=200,
194 additional_matcher=match_none)
195
196 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
197 status_code=200,
198 json=self.vrouter_app_response)
199
200 self.si.serviceinstanceattribute_dict = {}
201
202 with patch.object(ServiceInstance.objects, "get_items") as mock_si:
203 mock_si.return_value = [self.si]
204 self.sync_step().sync_record(self.onos_app)
205
206 self.assertTrue(m.called)
207 self.assertEqual(m.call_count, 2)
208 self.assertEqual(self.onos_app.version, self.vrouter_app_response["version"])
209
210 @requests_mock.Mocker()
211 def test_app_sync_local_app_with_config(self, m):
212
213 m.post("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter/active",
214 status_code=200,
215 additional_matcher=match_none)
216
217 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
218 status_code=200,
219 json=self.vrouter_app_response)
220
221 with patch.object(ServiceInstance.objects, "get_items") as mock_si:
222 mock_si.return_value = [self.si]
223 self.sync_step().sync_record(self.onos_app)
224 self.assertTrue(m.called)
225 self.assertEqual(m.call_count, 2)
226 self.assertEqual(self.onos_app.version, self.vrouter_app_response["version"])
227
228 @requests_mock.Mocker()
229 def test_app_install_remote_app_no_config(self, m):
230 """
231 Install an application that has to be downloaded from a remote source
232 """
233
234 self.onos_app.url = 'http://onf.org/maven/...'
235 self.onos_app.version = "1.13.1"
Matteo Scandolo3b672572018-06-25 14:28:36 -0700236 self.onos_app.app_id = "org.onosproject.vrouter"
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700237
238 expected = {
239 'activate': True,
240 'url': self.onos_app.url
241 }
242
243 m.post("/onos/v1/applications",
244 status_code=200,
245 additional_matcher=functools.partial(match_json, expected),
246 json=self.vrouter_app_response)
247
Matteo Scandolo3b672572018-06-25 14:28:36 -0700248 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter", [
249 {'status_code': 404, 'text': "foo"},
250 {'status_code': 200, 'json': self.vrouter_app_response}
251 ])
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700252
253 self.si.serviceinstanceattribute_dict = {}
254
255 with patch.object(ServiceInstance.objects, "get_items") as mock_si:
256 mock_si.return_value = [self.si]
257 self.sync_step().sync_record(self.onos_app)
258 self.assertTrue(m.called)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700259 self.assertEqual(m.call_count, 3)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700260 self.assertEqual(self.onos_app.app_id, self.vrouter_app_response["name"])
261
262 @requests_mock.Mocker()
263 def test_update_remote_app(self, m):
264 self.onos_app.url = 'http://onf.org/maven/...'
265 self.onos_app.version = "1.14.1"
266
267 expected = {
268 'activate': True,
269 'url': self.onos_app.url
270 }
271
272 self.vrouter_app_response_updated = self.vrouter_app_response.copy()
273 self.vrouter_app_response_updated["version"] = "1.14.1"
274
275 m.post("/onos/v1/applications",
276 status_code=200,
277 additional_matcher=functools.partial(match_json, expected),
278 json=self.vrouter_app_response)
279
280
281 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
282 [
283 {"json": self.vrouter_app_response, "status_code": 200},
284 {"json": self.vrouter_app_response_updated, "status_code": 200}
285 ]
286 )
287
288 m.delete("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
289 status_code=204)
290
291 self.si.serviceinstanceattribute_dict = {}
292
293 with patch.object(ServiceInstance.objects, "get_items") as mock_si:
294 mock_si.return_value = [self.si]
295 self.sync_step().sync_record(self.onos_app)
296 self.assertTrue(m.called)
297 self.assertEqual(m.call_count, 4)
298 self.assertEqual(self.onos_app.app_id, self.vrouter_app_response_updated["name"])
299
300 @requests_mock.Mocker()
301 def test_app_sync_remote_app_no_config_fail_version(self, m):
302 """
303 Activate an application that has to be downloaded from a remote source
304 """
305
306 self.onos_app.url = 'http://onf.org/maven/...'
307 self.onos_app.version = "1.14.2"
Matteo Scandolo3b672572018-06-25 14:28:36 -0700308 self.onos_app.app_id = "org.onosproject.vrouter"
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700309
310 expected = {
311 'activate': True,
312 'url': self.onos_app.url
313 }
314
315 m.post("/onos/v1/applications",
316 status_code=200,
317 additional_matcher=functools.partial(match_json, expected),
318 json=self.vrouter_app_response)
319
Matteo Scandolo3b672572018-06-25 14:28:36 -0700320 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter", [
321 {'status_code': 404, 'text': "foo"},
322 {'status_code': 200, 'json': self.vrouter_app_response}
323 ])
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700324
325 self.si.serviceinstanceattribute_dict = {}
326
327 with patch.object(ServiceInstance.objects, "get_items") as mock_si, \
328 self.assertRaises(Exception) as e:
329 mock_si.return_value = [self.si]
330 self.sync_step().sync_record(self.onos_app)
331
332 self.assertTrue(m.called)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700333 self.assertEqual(m.call_count, 3)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700334 self.assertEqual(self.onos_app.app_id, self.vrouter_app_response["name"])
335 self.assertEqual(e.exception.message, "The version of org.onosproject.vrouter you installed (1.13.1) is not the same you requested (1.14.2)")
336
337 @requests_mock.Mocker()
Matteo Scandolo3b672572018-06-25 14:28:36 -0700338 def test_handle_409(self, m):
339 """
340 A 409 "Application Already installed" response is not an error. This should not happen as we check if the app is installed.
341 """
342
343 self.onos_app.url = 'http://onf.org/maven/...'
344 self.onos_app.version = "1.14.2"
345 self.onos_app.app_id = "org.onosproject.vrouter"
346
347 m.post("/onos/v1/applications",
348 status_code=409)
349
350 step = self.sync_step()
351 with patch.object(step, "check_app_installed") as mock_check_installed:
352 mock_check_installed.return_value = False
353
354 step.sync_record(self.onos_app)
355
356 self.assertTrue(m.called)
357 self.assertEqual(m.call_count, 1)
358
359 @requests_mock.Mocker()
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700360 def test_config_delete(self, m):
361 m.delete("http://onos-url:8181%s" % self.onos_app_attribute.name,
362 status_code=204)
363
364 self.sync_step().delete_record(self.onos_app_attribute)
365 self.assertTrue(m.called)
366 self.assertEqual(m.call_count, 1)
367
368 @requests_mock.Mocker()
369 def test_app_deactivate(self, m):
370 m.delete("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter/active",
371 status_code=204)
372
373 self.sync_step().delete_record(self.onos_app)
374 self.assertTrue(m.called)
375 self.assertEqual(m.call_count, 1)
376
377 @requests_mock.Mocker()
378 def test_app_uninstall(self, m):
379 self.onos_app.url = 'http://onf.org/maven/...'
380 self.onos_app.version = "1.14.2"
381 self.onos_app.backend_code = 1
382
383 m.delete("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
384 status_code=204)
385
386 self.sync_step().delete_record(self.onos_app)
387 self.assertTrue(m.called)
388 self.assertEqual(m.call_count, 1)
389
390
391