blob: fa3c7749e8e50393a848b884bbe6759a94692050 [file] [log] [blame]
Scott Bakerbba67b62019-01-28 17:38:21 -08001# 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
15
16import unittest
17from mock import patch
18import mock
19import pdb
20
21import os
22import sys
23from xosconfig import Config
24
25test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
26sync_lib_dir = os.path.join(test_path, "..", "xossynchronizer")
27xos_dir = os.path.join(test_path, "..", "..", "..", "xos")
28
29class TestModelPolicyTenantWithContainer(unittest.TestCase):
30 def setUp(self):
31 global TenantWithContainerPolicy, LeastLoadedNodeScheduler, MockObjectList
32
33 self.sys_path_save = sys.path
34 self.cwd_save = os.getcwd()
35
36 config = basic_conf = os.path.abspath(
37 os.path.dirname(os.path.realpath(__file__)) + "/test_config.yaml"
38 )
39 Config.clear() # in case left unclean by a previous test case
40 Config.init(config, "synchronizer-config-schema.yaml")
41
42 from xossynchronizer.mock_modelaccessor_build import (
43 build_mock_modelaccessor,
44 )
45
46 build_mock_modelaccessor(sync_lib_dir, xos_dir, services_dir=None, service_xprotos=[])
47
48 import xossynchronizer.model_policies.model_policy_tenantwithcontainer
49 from xossynchronizer.model_policies.model_policy_tenantwithcontainer import (
50 TenantWithContainerPolicy,
51 LeastLoadedNodeScheduler,
52 )
53
54 from xossynchronizer.mock_modelaccessor import MockObjectList
55
56 # import all class names to globals
57 for (
58 k,
59 v,
60 ) in xossynchronizer.model_policies.model_policy_tenantwithcontainer.model_accessor.all_model_classes.items():
61 globals()[k] = v
62
63 # TODO: Mock_model_accessor lacks save or delete methods
64 # Instance.save = mock.Mock
65 # Instance.delete = mock.Mock
66 # TenantWithContainer.save = mock.Mock
67
68 self.policy = TenantWithContainerPolicy()
69 self.user = User(email="testadmin@test.org")
70 self.tenant = TenantWithContainer(creator=self.user)
71 self.flavor = Flavor(name="m1.small")
72
73 def tearDown(self):
74 Config.clear()
75 sys.path = self.sys_path_save
76 os.chdir(self.cwd_save)
77
78 def test_manage_container_no_slices(self):
79 with patch.object(TenantWithContainer, "owner") as owner:
80 owner.slices.count.return_value = 0
81 with self.assertRaises(Exception) as e:
82 self.policy.manage_container(self.tenant)
83 self.assertEqual(e.exception.message, "The service has no slices")
84
85 def test_manage_container(self):
86 with patch.object(TenantWithContainer, "owner") as owner, patch.object(
87 TenantWithContainer, "save"
88 ) as tenant_save, patch.object(
89 Node, "site_deployment"
90 ) as site_deployment, patch.object(
91 Instance, "save"
92 ) as instance_save, patch.object(
93 Instance, "delete"
94 ) as instance_delete, patch.object(
95 TenantWithContainerPolicy, "get_image"
96 ) as get_image, patch.object(
97 LeastLoadedNodeScheduler, "pick"
98 ) as pick:
99 # setup mocks
100 node = Node(hostname="my.node.com")
101 slice = Slice(
102 name="mysite_test1", default_flavor=self.flavor, default_isolation="vm"
103 )
104 image = Image(name="trusty-server-multi-nic")
105 deployment = Deployment(name="testdeployment")
106 owner.slices.count.return_value = 1
107 owner.slices.all.return_value = [slice]
108 owner.slices.first.return_value = slice
109 get_image.return_value = image
110 pick.return_value = (node, None)
111 site_deployment.deployment = deployment
112 # done setup mocks
113
114 # call manage_container
115 self.policy.manage_container(self.tenant)
116
117 # make sure manage_container did what it is supposed to do
118 self.assertNotEqual(self.tenant.instance, None)
119 self.assertEqual(self.tenant.instance.creator.email, "testadmin@test.org")
120 self.assertEqual(self.tenant.instance.image.name, "trusty-server-multi-nic")
121 self.assertEqual(self.tenant.instance.flavor.name, "m1.small")
122 self.assertEqual(self.tenant.instance.isolation, "vm")
123 self.assertEqual(self.tenant.instance.node.hostname, "my.node.com")
124 self.assertEqual(self.tenant.instance.slice.name, "mysite_test1")
125 self.assertEqual(self.tenant.instance.parent, None)
126 instance_save.assert_called()
127 instance_delete.assert_not_called()
128 tenant_save.assert_called()
129
130 def test_manage_container_delete(self):
131 self.tenant.deleted = True
132
133 # call manage_container
134 self.policy.manage_container(self.tenant)
135
136 # make sure manage_container did what it is supposed to do
137 self.assertEqual(self.tenant.instance, None)
138
139 def test_manage_container_no_m1_small(self):
140 with patch.object(TenantWithContainer, "owner") as owner, patch.object(
141 Node, "site_deployment"
142 ) as site_deployment, patch.object(
143 Flavor, "objects"
144 ) as flavor_objects, patch.object(
145 TenantWithContainerPolicy, "get_image"
146 ) as get_image, patch.object(
147 LeastLoadedNodeScheduler, "pick"
148 ) as pick:
149 # setup mocks
150 node = Node(hostname="my.node.com")
151 slice = Slice(
152 name="mysite_test1", default_flavor=None, default_isolation="vm"
153 )
154 image = Image(name="trusty-server-multi-nic")
155 deployment = Deployment(name="testdeployment")
156 owner.slices.count.return_value = 1
157 owner.slices.all.return_value = [slice]
158 owner.slices.first.return_value = slice
159 get_image.return_value = image
160 pick.return_value = (node, None)
161 site_deployment.deployment = deployment
162 flavor_objects.filter.return_value = []
163 # done setup mocks
164
165 with self.assertRaises(Exception) as e:
166 self.policy.manage_container(self.tenant)
167 self.assertEqual(e.exception.message, "No m1.small flavor")
168
169 def test_least_loaded_node_scheduler(self):
170 with patch.object(Node.objects, "get_items") as node_objects:
171 slice = Slice(
172 name="mysite_test1", default_flavor=None, default_isolation="vm"
173 )
174 node = Node(hostname="my.node.com", id=4567)
175 node.instances = MockObjectList(initial=[])
176 node_objects.return_value = [node]
177
178 sched = LeastLoadedNodeScheduler(slice)
179 (picked_node, parent) = sched.pick()
180
181 self.assertNotEqual(picked_node, None)
182 self.assertEqual(picked_node.id, node.id)
183
184 def test_least_loaded_node_scheduler_two_nodes(self):
185 with patch.object(Node.objects, "get_items") as node_objects:
186 slice = Slice(
187 name="mysite_test1", default_flavor=None, default_isolation="vm"
188 )
189 instance1 = Instance(id=1)
190 node1 = Node(hostname="my.node.com", id=4567)
191 node1.instances = MockObjectList(initial=[])
192 node2 = Node(hostname="my.node.com", id=8910)
193 node2.instances = MockObjectList(initial=[instance1])
194 node_objects.return_value = [node1, node2]
195
196 # should pick the node with the fewest instance (node1)
197
198 sched = LeastLoadedNodeScheduler(slice)
199 (picked_node, parent) = sched.pick()
200
201 self.assertNotEqual(picked_node, None)
202 self.assertEqual(picked_node.id, node1.id)
203
204 def test_least_loaded_node_scheduler_two_nodes_multi(self):
205 with patch.object(Node.objects, "get_items") as node_objects:
206 slice = Slice(
207 name="mysite_test1", default_flavor=None, default_isolation="vm"
208 )
209 instance1 = Instance(id=1)
210 instance2 = Instance(id=2)
211 instance3 = Instance(id=3)
212 node1 = Node(hostname="my.node.com", id=4567)
213 node1.instances = MockObjectList(initial=[instance2, instance3])
214 node2 = Node(hostname="my.node.com", id=8910)
215 node2.instances = MockObjectList(initial=[instance1])
216 node_objects.return_value = [node1, node2]
217
218 # should pick the node with the fewest instance (node2)
219
220 sched = LeastLoadedNodeScheduler(slice)
221 (picked_node, parent) = sched.pick()
222
223 self.assertNotEqual(picked_node, None)
224 self.assertEqual(picked_node.id, node2.id)
225
226 def test_least_loaded_node_scheduler_with_label(self):
227 with patch.object(Node.objects, "get_items") as node_objects:
228 slice = Slice(
229 name="mysite_test1", default_flavor=None, default_isolation="vm"
230 )
231 instance1 = Instance(id=1)
232 node1 = Node(hostname="my.node.com", id=4567)
233 node1.instances = MockObjectList(initial=[])
234 node2 = Node(hostname="my.node.com", id=8910)
235 node2.instances = MockObjectList(initial=[instance1])
236 # Fake out the existence of a NodeLabel object. TODO: Extend the mock framework to support the model__field
237 # syntax.
238 node1.nodelabels__name = None
239 node2.nodelabels__name = "foo"
240 node_objects.return_value = [node1, node2]
241
242 # should pick the node with the label, even if it has a greater number of instances
243
244 sched = LeastLoadedNodeScheduler(slice, label="foo")
245 (picked_node, parent) = sched.pick()
246
247 self.assertNotEqual(picked_node, None)
248 self.assertEqual(picked_node.id, node2.id)
249
250 def test_least_loaded_node_scheduler_create_label(self):
251 with patch.object(Node.objects, "get_items") as node_objects, patch.object(
252 NodeLabel, "save", autospec=True
253 ) as nodelabel_save, patch.object(NodeLabel, "node") as nodelabel_node_add:
254 slice = Slice(
255 name="mysite_test1", default_flavor=None, default_isolation="vm"
256 )
257 instance1 = Instance(id=1)
258 node1 = Node(hostname="my.node.com", id=4567)
259 node1.instances = MockObjectList(initial=[])
260 node2 = Node(hostname="my.node.com", id=8910)
261 node2.instances = MockObjectList(initial=[instance1])
262 # Fake out the existence of a NodeLabel object. TODO: Extend the mock framework to support the model__field
263 # syntax.
264 node1.nodelabels__name = None
265 node2.nodelabels__name = None
266 node_objects.return_value = [node1, node2]
267
268 # should pick the node with the least number of instances
269
270 sched = LeastLoadedNodeScheduler(
271 slice, label="foo", constrain_by_service_instance=True
272 )
273 (picked_node, parent) = sched.pick()
274
275 self.assertNotEqual(picked_node, None)
276 self.assertEqual(picked_node.id, node1.id)
277
278 # NodeLabel should have been created and saved
279
280 self.assertEqual(nodelabel_save.call_count, 1)
281 self.assertEqual(nodelabel_save.call_args[0][0].name, "foo")
282
283 # The NodeLabel's node field should have been added to
284
285 NodeLabel.node.add.assert_called_with(node1)
286
287
288if __name__ == "__main__":
289 unittest.main()