blob: aca3d97c0f43f5066839b3de6c34b547e16b9113 [file] [log] [blame]
Scott Baker5bbdcac2017-11-29 11:39:24 -08001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
17import unittest
18from mock import patch, call, Mock, MagicMock, PropertyMock
19import mock
20
21import os, sys
22
23test_path=os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
24service_dir=os.path.join(test_path, "../../../..")
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")
29
Scott Baker2f654942018-01-25 10:12:03 -080030# While transitioning from static to dynamic load, the path to find neighboring xproto files has changed. So check
31# both possible locations...
32def get_models_fn(service_name, xproto_name):
33 name = os.path.join(service_name, "xos", xproto_name)
34 if os.path.exists(os.path.join(services_dir, name)):
35 return name
36 else:
37 name = os.path.join(service_name, "xos", "synchronizer", "models", xproto_name)
38 if os.path.exists(os.path.join(services_dir, name)):
39 return name
40 raise Exception("Unable to find service=%s xproto=%s" % (service_name, xproto_name))
41
Scott Baker5bbdcac2017-11-29 11:39:24 -080042class TestSyncVEGServiceInstance(unittest.TestCase):
43 def setUp(self):
44 global SyncVEGTenant, LeastLoadedNodeScheduler, MockObjectList
45
46 self.sys_path_save = sys.path
47 sys.path.append(xos_dir)
48 sys.path.append(os.path.join(xos_dir, 'synchronizers', 'new_base'))
49
50 config = os.path.join(test_path, "test_config.yaml")
51 from xosconfig import Config
52 Config.clear()
53 Config.init(config, 'synchronizer-config-schema.yaml')
54
55 from synchronizers.new_base.mock_modelaccessor_build import build_mock_modelaccessor
Scott Baker2f654942018-01-25 10:12:03 -080056 build_mock_modelaccessor(xos_dir, services_dir, [get_models_fn("vEG", "veg.xproto"),
57 get_models_fn("addressmanager", "addressmanager.xproto")])
Scott Baker5bbdcac2017-11-29 11:39:24 -080058
59 import synchronizers.new_base.modelaccessor
60 import synchronizers.new_base.model_policies.model_policy_tenantwithcontainer
61 import sync_vegtenant
62 from sync_vegtenant import SyncVEGTenant, model_accessor
63
64 from mock_modelaccessor import MockObjectList
65
66 # import all class names to globals
67 for (k, v) in model_accessor.all_model_classes.items():
68 globals()[k] = v
69
70 # Some of the functions we call have side-effects. For example, creating a VEGTenant may lead to creation of
71 # tags. Ideally, this wouldn't happen, but it does. So make sure we reset the world.
72 model_accessor.reset_all_object_stores()
73
74 # attic functions that are not present in the mock model accessor
75 AddressManagerServiceInstance.set_attribute = Mock()
76
77 self.syncstep = SyncVEGTenant()
78
79 # set up an object hierarchy that represents a Service and ServiceInstance
80
81 self.user = User(email="testadmin@test.org")
82 self.service = VEGService(name="the_veg_service",
83 id=1,
84 docker_image_name="reg/veg_docker",
85 docker_insecure_registry=True,
86 dns_servers="dnsone,dnstwo",
87 url_filter_kind=None,
88 private_key_fn=os.path.join(test_path, "test_private_key"))
89 # E-CORD / vEG doesn't support these subscriber fields at this time, but keep them in the test for now since the
90 # synchronizer supports them to maintain similarity with vSG. If/when E-CORD introduces its own subscriber
91 # object, deal with the differences here.
92 self.subscriber = MagicMock(firewall_rules = "rule1",
93 firewall_enable = True,
94 url_filter_enable = True,
95 url_filter_level="R",
96 cdn_enable=True,
97 uplink_speed=1234,
98 downlink_speed=5678,
99 enable_uverse=False,
100 status="suspended",
101 sync_attributes=["firewall_rules", "firewall_enable", "url_filter_enable",
102 "url_filter_level", "cdn_enable", "uplink_speed",
103 "downlink_speed", "enable_uverse", "status"])
104 self.volt = MagicMock(s_tag=111, c_tag=222, subscriber=self.subscriber)
105 self.tenant = VEGTenant(creator=self.user,
106 id=401,
107 volt=self.volt,
108 owner=self.service,
109 wan_container_ip="10.7.1.3",
110 wan_container_netbits="24",
111 wan_container_mac="02:42:0a:07:01:03",
112 wan_container_gateway_ip="10.7.1.1",
113 wan_vm_ip="10.7.1.2",
114 wan_vm_mac="02:42:0a:07:01:02",
115 sync_attributes = ["wan_container_ip", "wan_container_netbits", "wan_container_mac",
116 "wan_container_gateway_ip", "wan_vm_ip", "wan_vm_mac"])
117 self.flavor = Flavor(name="m1.small")
118 self.npt_ctag = NetworkParameterType(name="c_tag", id=1)
119 self.npt_stag = NetworkParameterType(name="s_tag", id=2)
120 self.npt_neutron_port_name = NetworkParameterType(name="neutron_port_name", id=501)
121 self.priv_template = NetworkTemplate(name="access_network", visibility="private")
122 self.priv_network = Network(name="mysite_test1_private", template=self.priv_template)
123 self.image = Image(name="trusty-server-multi-nic")
124 self.deployment = Deployment(name="testdeployment")
125 self.user = User(email="smbaker", id=701)
126 self.controller = Controller(id=101)
127 self.node = Node(name="testnode")
128 self.slice = Slice(name="mysite_test1", default_flavor=self.flavor, default_isolation="vm", service=self.service, id=301)
129 self.instance = Instance(slice=self.slice,
130 instance_name="testinstance1_instance_name",
131 instance_id="testinstance1_instance_id",
132 name="testinstance1_name",
133 node=self.node,
134 creator=self.user,
135 controller=self.controller)
136 self.tenant.instance = self.instance
137 self.instance.get_ssh_ip = Mock(return_value="1.2.3.4")
138 self.controllerslice = ControllerSlice(slice_id=self.slice.id, controller_id=self.controller.id, id=201)
139 self.controlleruser = ControllerUser(user_id=self.user.id, controller_id=self.controller.id, id=601)
140
141 def tearDown(self):
142 sys.path = self.sys_path_save
143
144 def test_get_veg_service(self):
145 with patch.object(VEGService.objects, "get_items") as vegservice_objects:
146 vegservice_objects.return_value = [self.service]
147
148 self.tenant.owner = self.service
149
150 self.assertEqual(self.syncstep.get_veg_service(self.tenant), self.service)
151
152 def test_get_extra_attributes(self):
153 with patch.object(VEGService.objects, "get_items") as vegservice_objects:
154 vegservice_objects.return_value = [self.service]
155
156 attrs = self.syncstep.get_extra_attributes(self.tenant)
157
158 desired_attrs = {"s_tags": [111],
159 "c_tags": [222],
160 "docker_remote_image_name": "reg/veg_docker",
161 "docker_local_image_name": "reg/veg_docker",
162 "docker_opts": "--insecure-registry reg",
163 "dnsdemux_ip": "none",
164 "cdn_prefixes": [],
165 "full_setup": True,
166 "isolation": "vm",
167 "safe_browsing_macs": [],
168 "container_name": "veg-111-222",
169 "dns_servers": ["dnsone", "dnstwo"],
170 "url_filter_kind": None,
171
172 "firewall_rules": "rule1",
173 "firewall_enable": True,
174 "url_filter_enable": True,
175 "url_filter_level": "R",
176 "cdn_enable": True,
177 "uplink_speed": 1234,
178 "downlink_speed": 5678,
179 "enable_uverse": False,
180 "status": "suspended"}
181
182 self.assertDictContainsSubset(desired_attrs, attrs)
183
184
185 def test_sync_record(self):
186 with patch.object(VEGService.objects, "get_items") as vegservice_objects, \
187 patch.object(Slice.objects, "get_items") as slice_objects, \
188 patch.object(User.objects, "get_items") as user_objects, \
189 patch.object(ControllerSlice.objects, "get_items") as controllerslice_objects, \
190 patch.object(ControllerUser.objects, "get_items") as controlleruser_objects, \
191 patch.object(SyncVEGTenant, "run_playbook") as run_playbook:
192 slice_objects.return_value = [self.slice]
193 vegservice_objects.return_value = [self.service]
194 controllerslice_objects.return_value = [self.controllerslice]
195 controlleruser_objects.return_value = [self.controlleruser]
196 user_objects.return_value = [self.user]
197
198 self.tenant.updated = 10
199 self.tenant.policed = 20
200 self.tenant.enacted = None
201
202 run_playbook.return_value = True
203
204 self.syncstep.sync_record(self.tenant)
205
206 run_playbook.assert_called()
207
208 attrs = run_playbook.call_args[0][1]
209
210 desired_attrs = {"username": "ubuntu",
211 "ansible_tag": "VEGTenant_401",
212 "instance_name": "testinstance1_name",
213 "hostname": "testnode",
214 "private_key": "some_key\n",
215 "ssh_ip": "1.2.3.4",
216 "instance_id": "testinstance1_instance_id",
217
218 "wan_container_ip": "10.7.1.3",
219 "wan_container_netbits": "24",
220 "wan_container_mac": "02:42:0a:07:01:03",
221 "wan_container_gateway_ip": "10.7.1.1",
222 "wan_vm_ip": "10.7.1.2",
223 "wan_vm_mac": "02:42:0a:07:01:02",
224
225 "s_tags": [111],
226 "c_tags": [222],
227 "docker_remote_image_name": "reg/veg_docker",
228 "docker_local_image_name": "reg/veg_docker",
229 "docker_opts": "--insecure-registry reg",
230 "dnsdemux_ip": "none",
231 "cdn_prefixes": [],
232 "full_setup": True,
233 "isolation": "vm",
234 "safe_browsing_macs": [],
235 "container_name": "veg-111-222",
236 "dns_servers": ["dnsone", "dnstwo"],
237 "url_filter_kind": None,
238
239 "firewall_rules": "rule1",
240 "firewall_enable": True,
241 "url_filter_enable": True,
242 "url_filter_level": "R",
243 "cdn_enable": True,
244 "uplink_speed": 1234,
245 "downlink_speed": 5678,
246 "enable_uverse": False,
247 "status": "suspended"}
248
249 self.assertDictContainsSubset(desired_attrs, attrs)
250
251 def test_sync_record_emptysubscriber(self):
252 with patch.object(VEGService.objects, "get_items") as vegservice_objects, \
253 patch.object(Slice.objects, "get_items") as slice_objects, \
254 patch.object(User.objects, "get_items") as user_objects, \
255 patch.object(ControllerSlice.objects, "get_items") as controllerslice_objects, \
256 patch.object(ControllerUser.objects, "get_items") as controlleruser_objects, \
257 patch.object(SyncVEGTenant, "run_playbook") as run_playbook:
258 slice_objects.return_value = [self.slice]
259 vegservice_objects.return_value = [self.service]
260 controllerslice_objects.return_value = [self.controllerslice]
261 controlleruser_objects.return_value = [self.controlleruser]
262 user_objects.return_value = [self.user]
263
264 self.tenant.updated = 10
265 self.tenant.policed = 20
266 self.tenant.enacted = None
267
268 self.volt.subscriber = MagicMock()
269
270 run_playbook.return_value = True
271
272 self.syncstep.sync_record(self.tenant)
273
274 run_playbook.assert_called()
275
276 attrs = run_playbook.call_args[0][1]
277
278 desired_attrs = {"firewall_rules": "",
279 "firewall_enable": False,
280 "url_filter_enable": False,
281 "url_filter_level": "PG",
282 "cdn_enable": False,
283 "uplink_speed": 1000000000,
284 "downlink_speed": 1000000000,
285 "enable_uverse": True,
286 "status": "enabled"}
287
288 self.assertDictContainsSubset(desired_attrs, attrs)
289
290 def test_sync_record_no_policy(self):
291 with patch.object(SyncVEGTenant, "run_playbook") as run_playbook:
292
293 self.tenant.updated = 10
294 self.tenant.policed = 5 # policies need to be run
295 self.tenant.enacted = None
296
297 with self.assertRaises(Exception) as e:
298 self.syncstep.sync_record(self.tenant)
299 self.assertIn("due to waiting on model policy", e.exception.message)
300
301 run_playbook.assert_not_called()
302
303 def test_sync_record_instance_not_ready(self):
304 with patch.object(SyncVEGTenant, "run_playbook") as run_playbook:
305
306 self.tenant.updated = 10
307 self.tenant.policed = 20
308 self.tenant.enacted = None
309
310 self.instance.instance_name = None # no instance_name means instance is not ready
311
312 with self.assertRaises(Exception) as e:
313 self.syncstep.sync_record(self.tenant)
314 self.assertIn("due to waiting on instance.instance_name", e.exception.message)
315
316 run_playbook.assert_not_called()
317
318 def test_delete_record_no_policy(self):
319 self.tenant.updated = 10
320 self.tenant.policed = 20
321 self.tenant.enacted = None
322
323 self.syncstep.delete_record(self.tenant)
324
325 # delete doesn't actually do anything, so nothing to further test.
326
327 def test_delete_record_no_policy(self):
328 self.tenant.updated = 10
329 self.tenant.policed = 5 # policies need to be run
330 self.tenant.enacted = None
331
332 with self.assertRaises(Exception) as e:
333 self.syncstep.delete_record(self.tenant)
334 self.assertIn("due to waiting on model policy", e.exception.message)
335
336if __name__ == '__main__':
337 unittest.main()
338
339