blob: e2659c31c6e03b39811c07a225b57365718d70c2 [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
Scott Bakerf0d7e5c2019-02-05 08:35:31 -080054 from mock_modelaccessor import MockObjectList
Scott Bakerbba67b62019-01-28 17:38:21 -080055
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
Scott Bakerc2fddaa2019-01-30 15:45:03 -080063 from xossynchronizer.modelaccessor import model_accessor
Scott Bakerbba67b62019-01-28 17:38:21 -080064
Scott Bakerc2fddaa2019-01-30 15:45:03 -080065 self.policy = TenantWithContainerPolicy(model_accessor=model_accessor)
Scott Bakerbba67b62019-01-28 17:38:21 -080066 self.user = User(email="testadmin@test.org")
67 self.tenant = TenantWithContainer(creator=self.user)
68 self.flavor = Flavor(name="m1.small")
69
70 def tearDown(self):
71 Config.clear()
72 sys.path = self.sys_path_save
73 os.chdir(self.cwd_save)
74
75 def test_manage_container_no_slices(self):
76 with patch.object(TenantWithContainer, "owner") as owner:
77 owner.slices.count.return_value = 0
78 with self.assertRaises(Exception) as e:
79 self.policy.manage_container(self.tenant)
80 self.assertEqual(e.exception.message, "The service has no slices")
81
82 def test_manage_container(self):
83 with patch.object(TenantWithContainer, "owner") as owner, patch.object(
84 TenantWithContainer, "save"
85 ) as tenant_save, patch.object(
86 Node, "site_deployment"
87 ) as site_deployment, patch.object(
88 Instance, "save"
89 ) as instance_save, patch.object(
90 Instance, "delete"
91 ) as instance_delete, patch.object(
92 TenantWithContainerPolicy, "get_image"
93 ) as get_image, patch.object(
94 LeastLoadedNodeScheduler, "pick"
95 ) as pick:
96 # setup mocks
97 node = Node(hostname="my.node.com")
98 slice = Slice(
99 name="mysite_test1", default_flavor=self.flavor, default_isolation="vm"
100 )
101 image = Image(name="trusty-server-multi-nic")
102 deployment = Deployment(name="testdeployment")
103 owner.slices.count.return_value = 1
104 owner.slices.all.return_value = [slice]
105 owner.slices.first.return_value = slice
106 get_image.return_value = image
107 pick.return_value = (node, None)
108 site_deployment.deployment = deployment
109 # done setup mocks
110
111 # call manage_container
112 self.policy.manage_container(self.tenant)
113
114 # make sure manage_container did what it is supposed to do
115 self.assertNotEqual(self.tenant.instance, None)
116 self.assertEqual(self.tenant.instance.creator.email, "testadmin@test.org")
117 self.assertEqual(self.tenant.instance.image.name, "trusty-server-multi-nic")
118 self.assertEqual(self.tenant.instance.flavor.name, "m1.small")
119 self.assertEqual(self.tenant.instance.isolation, "vm")
120 self.assertEqual(self.tenant.instance.node.hostname, "my.node.com")
121 self.assertEqual(self.tenant.instance.slice.name, "mysite_test1")
122 self.assertEqual(self.tenant.instance.parent, None)
123 instance_save.assert_called()
124 instance_delete.assert_not_called()
125 tenant_save.assert_called()
126
127 def test_manage_container_delete(self):
128 self.tenant.deleted = True
129
130 # call manage_container
131 self.policy.manage_container(self.tenant)
132
133 # make sure manage_container did what it is supposed to do
134 self.assertEqual(self.tenant.instance, None)
135
136 def test_manage_container_no_m1_small(self):
137 with patch.object(TenantWithContainer, "owner") as owner, patch.object(
138 Node, "site_deployment"
139 ) as site_deployment, patch.object(
140 Flavor, "objects"
141 ) as flavor_objects, patch.object(
142 TenantWithContainerPolicy, "get_image"
143 ) as get_image, patch.object(
144 LeastLoadedNodeScheduler, "pick"
145 ) as pick:
146 # setup mocks
147 node = Node(hostname="my.node.com")
148 slice = Slice(
149 name="mysite_test1", default_flavor=None, default_isolation="vm"
150 )
151 image = Image(name="trusty-server-multi-nic")
152 deployment = Deployment(name="testdeployment")
153 owner.slices.count.return_value = 1
154 owner.slices.all.return_value = [slice]
155 owner.slices.first.return_value = slice
156 get_image.return_value = image
157 pick.return_value = (node, None)
158 site_deployment.deployment = deployment
159 flavor_objects.filter.return_value = []
160 # done setup mocks
161
162 with self.assertRaises(Exception) as e:
163 self.policy.manage_container(self.tenant)
164 self.assertEqual(e.exception.message, "No m1.small flavor")
165
166 def test_least_loaded_node_scheduler(self):
167 with patch.object(Node.objects, "get_items") as node_objects:
168 slice = Slice(
169 name="mysite_test1", default_flavor=None, default_isolation="vm"
170 )
171 node = Node(hostname="my.node.com", id=4567)
172 node.instances = MockObjectList(initial=[])
173 node_objects.return_value = [node]
174
175 sched = LeastLoadedNodeScheduler(slice)
176 (picked_node, parent) = sched.pick()
177
178 self.assertNotEqual(picked_node, None)
179 self.assertEqual(picked_node.id, node.id)
180
181 def test_least_loaded_node_scheduler_two_nodes(self):
182 with patch.object(Node.objects, "get_items") as node_objects:
183 slice = Slice(
184 name="mysite_test1", default_flavor=None, default_isolation="vm"
185 )
186 instance1 = Instance(id=1)
187 node1 = Node(hostname="my.node.com", id=4567)
188 node1.instances = MockObjectList(initial=[])
189 node2 = Node(hostname="my.node.com", id=8910)
190 node2.instances = MockObjectList(initial=[instance1])
191 node_objects.return_value = [node1, node2]
192
193 # should pick the node with the fewest instance (node1)
194
195 sched = LeastLoadedNodeScheduler(slice)
196 (picked_node, parent) = sched.pick()
197
198 self.assertNotEqual(picked_node, None)
199 self.assertEqual(picked_node.id, node1.id)
200
201 def test_least_loaded_node_scheduler_two_nodes_multi(self):
202 with patch.object(Node.objects, "get_items") as node_objects:
203 slice = Slice(
204 name="mysite_test1", default_flavor=None, default_isolation="vm"
205 )
206 instance1 = Instance(id=1)
207 instance2 = Instance(id=2)
208 instance3 = Instance(id=3)
209 node1 = Node(hostname="my.node.com", id=4567)
210 node1.instances = MockObjectList(initial=[instance2, instance3])
211 node2 = Node(hostname="my.node.com", id=8910)
212 node2.instances = MockObjectList(initial=[instance1])
213 node_objects.return_value = [node1, node2]
214
215 # should pick the node with the fewest instance (node2)
216
217 sched = LeastLoadedNodeScheduler(slice)
218 (picked_node, parent) = sched.pick()
219
220 self.assertNotEqual(picked_node, None)
221 self.assertEqual(picked_node.id, node2.id)
222
223 def test_least_loaded_node_scheduler_with_label(self):
224 with patch.object(Node.objects, "get_items") as node_objects:
225 slice = Slice(
226 name="mysite_test1", default_flavor=None, default_isolation="vm"
227 )
228 instance1 = Instance(id=1)
229 node1 = Node(hostname="my.node.com", id=4567)
230 node1.instances = MockObjectList(initial=[])
231 node2 = Node(hostname="my.node.com", id=8910)
232 node2.instances = MockObjectList(initial=[instance1])
233 # Fake out the existence of a NodeLabel object. TODO: Extend the mock framework to support the model__field
234 # syntax.
235 node1.nodelabels__name = None
236 node2.nodelabels__name = "foo"
237 node_objects.return_value = [node1, node2]
238
239 # should pick the node with the label, even if it has a greater number of instances
240
241 sched = LeastLoadedNodeScheduler(slice, label="foo")
242 (picked_node, parent) = sched.pick()
243
244 self.assertNotEqual(picked_node, None)
245 self.assertEqual(picked_node.id, node2.id)
246
247 def test_least_loaded_node_scheduler_create_label(self):
248 with patch.object(Node.objects, "get_items") as node_objects, patch.object(
249 NodeLabel, "save", autospec=True
250 ) as nodelabel_save, patch.object(NodeLabel, "node") as nodelabel_node_add:
251 slice = Slice(
252 name="mysite_test1", default_flavor=None, default_isolation="vm"
253 )
254 instance1 = Instance(id=1)
255 node1 = Node(hostname="my.node.com", id=4567)
256 node1.instances = MockObjectList(initial=[])
257 node2 = Node(hostname="my.node.com", id=8910)
258 node2.instances = MockObjectList(initial=[instance1])
259 # Fake out the existence of a NodeLabel object. TODO: Extend the mock framework to support the model__field
260 # syntax.
261 node1.nodelabels__name = None
262 node2.nodelabels__name = None
263 node_objects.return_value = [node1, node2]
264
265 # should pick the node with the least number of instances
266
267 sched = LeastLoadedNodeScheduler(
268 slice, label="foo", constrain_by_service_instance=True
269 )
270 (picked_node, parent) = sched.pick()
271
272 self.assertNotEqual(picked_node, None)
273 self.assertEqual(picked_node.id, node1.id)
274
275 # NodeLabel should have been created and saved
276
277 self.assertEqual(nodelabel_save.call_count, 1)
278 self.assertEqual(nodelabel_save.call_args[0][0].name, "foo")
279
280 # The NodeLabel's node field should have been added to
281
282 NodeLabel.node.add.assert_called_with(node1)
283
284
285if __name__ == "__main__":
286 unittest.main()