blob: bd63b99b454e8c0e98dbd99a9da6061c16e221bc [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()
163 def test_app_sync_local_app_no_config(self, m):
164 """
165 Activate an application that is already installed in ONOS
166 """
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()
187 def test_app_sync_local_app_with_config(self, m):
188
189 m.post("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter/active",
190 status_code=200,
191 additional_matcher=match_none)
192
193 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
194 status_code=200,
195 json=self.vrouter_app_response)
196
197 with patch.object(ServiceInstance.objects, "get_items") as mock_si:
198 mock_si.return_value = [self.si]
199 self.sync_step().sync_record(self.onos_app)
200 self.assertTrue(m.called)
201 self.assertEqual(m.call_count, 2)
202 self.assertEqual(self.onos_app.version, self.vrouter_app_response["version"])
203
204 @requests_mock.Mocker()
205 def test_app_install_remote_app_no_config(self, m):
206 """
207 Install an application that has to be downloaded from a remote source
208 """
209
210 self.onos_app.url = 'http://onf.org/maven/...'
211 self.onos_app.version = "1.13.1"
Matteo Scandolo3b672572018-06-25 14:28:36 -0700212 self.onos_app.app_id = "org.onosproject.vrouter"
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700213
214 expected = {
215 'activate': True,
216 'url': self.onos_app.url
217 }
218
219 m.post("/onos/v1/applications",
220 status_code=200,
221 additional_matcher=functools.partial(match_json, expected),
222 json=self.vrouter_app_response)
223
Matteo Scandolo3b672572018-06-25 14:28:36 -0700224 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter", [
225 {'status_code': 404, 'text': "foo"},
226 {'status_code': 200, 'json': self.vrouter_app_response}
227 ])
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700228
229 self.si.serviceinstanceattribute_dict = {}
230
231 with patch.object(ServiceInstance.objects, "get_items") as mock_si:
232 mock_si.return_value = [self.si]
233 self.sync_step().sync_record(self.onos_app)
234 self.assertTrue(m.called)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700235 self.assertEqual(m.call_count, 3)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700236 self.assertEqual(self.onos_app.app_id, self.vrouter_app_response["name"])
237
238 @requests_mock.Mocker()
239 def test_update_remote_app(self, m):
240 self.onos_app.url = 'http://onf.org/maven/...'
241 self.onos_app.version = "1.14.1"
242
243 expected = {
244 'activate': True,
245 'url': self.onos_app.url
246 }
247
248 self.vrouter_app_response_updated = self.vrouter_app_response.copy()
249 self.vrouter_app_response_updated["version"] = "1.14.1"
250
251 m.post("/onos/v1/applications",
252 status_code=200,
253 additional_matcher=functools.partial(match_json, expected),
254 json=self.vrouter_app_response)
255
256
257 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
258 [
259 {"json": self.vrouter_app_response, "status_code": 200},
260 {"json": self.vrouter_app_response_updated, "status_code": 200}
261 ]
262 )
263
264 m.delete("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
265 status_code=204)
266
267 self.si.serviceinstanceattribute_dict = {}
268
269 with patch.object(ServiceInstance.objects, "get_items") as mock_si:
270 mock_si.return_value = [self.si]
271 self.sync_step().sync_record(self.onos_app)
272 self.assertTrue(m.called)
273 self.assertEqual(m.call_count, 4)
274 self.assertEqual(self.onos_app.app_id, self.vrouter_app_response_updated["name"])
275
276 @requests_mock.Mocker()
277 def test_app_sync_remote_app_no_config_fail_version(self, m):
278 """
279 Activate an application that has to be downloaded from a remote source
280 """
281
282 self.onos_app.url = 'http://onf.org/maven/...'
283 self.onos_app.version = "1.14.2"
Matteo Scandolo3b672572018-06-25 14:28:36 -0700284 self.onos_app.app_id = "org.onosproject.vrouter"
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700285
286 expected = {
287 'activate': True,
288 'url': self.onos_app.url
289 }
290
291 m.post("/onos/v1/applications",
292 status_code=200,
293 additional_matcher=functools.partial(match_json, expected),
294 json=self.vrouter_app_response)
295
Matteo Scandolo3b672572018-06-25 14:28:36 -0700296 m.get("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter", [
297 {'status_code': 404, 'text': "foo"},
298 {'status_code': 200, 'json': self.vrouter_app_response}
299 ])
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700300
301 self.si.serviceinstanceattribute_dict = {}
302
303 with patch.object(ServiceInstance.objects, "get_items") as mock_si, \
304 self.assertRaises(Exception) as e:
305 mock_si.return_value = [self.si]
306 self.sync_step().sync_record(self.onos_app)
307
308 self.assertTrue(m.called)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700309 self.assertEqual(m.call_count, 3)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700310 self.assertEqual(self.onos_app.app_id, self.vrouter_app_response["name"])
311 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)")
312
313 @requests_mock.Mocker()
Matteo Scandolo3b672572018-06-25 14:28:36 -0700314 def test_handle_409(self, m):
315 """
316 A 409 "Application Already installed" response is not an error. This should not happen as we check if the app is installed.
317 """
318
319 self.onos_app.url = 'http://onf.org/maven/...'
320 self.onos_app.version = "1.14.2"
321 self.onos_app.app_id = "org.onosproject.vrouter"
322
323 m.post("/onos/v1/applications",
324 status_code=409)
325
326 step = self.sync_step()
327 with patch.object(step, "check_app_installed") as mock_check_installed:
328 mock_check_installed.return_value = False
329
330 step.sync_record(self.onos_app)
331
332 self.assertTrue(m.called)
333 self.assertEqual(m.call_count, 1)
334
335 @requests_mock.Mocker()
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700336 def test_config_delete(self, m):
337 m.delete("http://onos-url:8181%s" % self.onos_app_attribute.name,
338 status_code=204)
339
340 self.sync_step().delete_record(self.onos_app_attribute)
341 self.assertTrue(m.called)
342 self.assertEqual(m.call_count, 1)
343
344 @requests_mock.Mocker()
345 def test_app_deactivate(self, m):
346 m.delete("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter/active",
347 status_code=204)
348
349 self.sync_step().delete_record(self.onos_app)
350 self.assertTrue(m.called)
351 self.assertEqual(m.call_count, 1)
352
353 @requests_mock.Mocker()
354 def test_app_uninstall(self, m):
355 self.onos_app.url = 'http://onf.org/maven/...'
356 self.onos_app.version = "1.14.2"
357 self.onos_app.backend_code = 1
358
359 m.delete("http://onos-url:8181/onos/v1/applications/org.onosproject.vrouter",
360 status_code=204)
361
362 self.sync_step().delete_record(self.onos_app)
363 self.assertTrue(m.called)
364 self.assertEqual(m.call_count, 1)
365
366
367