1# Copyright 2014 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import mox 6import unittest 7 8import common 9 10import django.core.exceptions 11from autotest_lib.client.common_lib.cros.network import ping_runner 12from autotest_lib.frontend import setup_django_environment 13from autotest_lib.frontend.server import models as server_models 14from autotest_lib.site_utils import server_manager 15from autotest_lib.site_utils import server_manager_utils 16from autotest_lib.site_utils.lib import infra 17 18 19class QueriableList(list): 20 """A mock list object supports queries including filter and all. 21 """ 22 23 def filter(self, **kwargs): 24 """Mock the filter call in django model. 25 """ 26 raise NotImplementedError() 27 28 29 def get(self, **kwargs): 30 """Mock the get call in django model. 31 """ 32 raise NotImplementedError() 33 34 35 def all(self): 36 """Return all items in the list. 37 38 @return: All items in the list. 39 """ 40 return [item for item in self] 41 42 43class ServerManagerUnittests(mox.MoxTestBase): 44 """Unittest for testing server_manager module. 45 """ 46 47 def setUp(self): 48 """Initialize the unittest.""" 49 super(ServerManagerUnittests, self).setUp() 50 51 # Initialize test objects. 52 self.DRONE_ROLE = mox.MockObject( 53 server_models.ServerRole, 54 attrs={'role': server_models.ServerRole.ROLE.DRONE}) 55 self.SCHEDULER_ROLE = mox.MockObject( 56 server_models.ServerRole, 57 attrs={'role': server_models.ServerRole.ROLE.SCHEDULER}) 58 self.DRONE_ATTRIBUTE = mox.MockObject( 59 server_models.ServerAttribute, 60 attrs={'attribute': 'max_processes', 'value':1}) 61 self.PRIMARY_DRONE = mox.MockObject( 62 server_models.Server, 63 attrs={'hostname': 'primary_drone_hostname', 64 'status': server_models.Server.STATUS.PRIMARY, 65 'roles': QueriableList([self.DRONE_ROLE]), 66 'attributes': QueriableList([self.DRONE_ATTRIBUTE])}) 67 self.REPAIR_REQUIRED_DRONE = mox.MockObject( 68 server_models.Server, 69 attrs={'hostname': 'repair_required_drone_hostname', 70 'status': server_models.Server.STATUS.REPAIR_REQUIRED, 71 'roles': QueriableList([self.DRONE_ROLE]), 72 'attributes': QueriableList([self.DRONE_ATTRIBUTE])}) 73 self.PRIMARY_SCHEDULER = mox.MockObject( 74 server_models.Server, 75 attrs={'hostname': 'primary_scheduler_hostname', 76 'status': server_models.Server.STATUS.PRIMARY, 77 'roles': QueriableList([self.SCHEDULER_ROLE]), 78 'attributes': QueriableList([])}) 79 self.REPAIR_REQUIRED_SCHEDULER = mox.MockObject( 80 server_models.Server, 81 attrs={'hostname': 'repair_required_scheduler_hostname', 82 'status': server_models.Server.STATUS.REPAIR_REQUIRED, 83 'roles': QueriableList([self.SCHEDULER_ROLE]), 84 'attributes': QueriableList([])}) 85 86 self.mox.StubOutWithMock(server_manager_utils, 'check_server') 87 self.mox.StubOutWithMock(server_manager_utils, 'warn_missing_role') 88 self.mox.StubOutWithMock(server_manager_utils, 'use_server_db') 89 self.mox.StubOutWithMock(server_models.Server, 'get_role_names') 90 self.mox.StubOutWithMock(server_models.Server.objects, 'create') 91 self.mox.StubOutWithMock(server_models.Server.objects, 'filter') 92 self.mox.StubOutWithMock(server_models.Server.objects, 'get') 93 self.mox.StubOutWithMock(server_models.ServerRole, 'delete') 94 self.mox.StubOutWithMock(server_models.ServerRole.objects, 'create') 95 self.mox.StubOutWithMock(server_models.ServerRole.objects, 'filter') 96 self.mox.StubOutWithMock(server_models.ServerAttribute.objects, 97 'create') 98 self.mox.StubOutWithMock(server_models.ServerAttribute.objects, 99 'filter') 100 self.mox.StubOutWithMock(infra, 'execute_command') 101 self.mox.StubOutWithMock(ping_runner.PingRunner, 'simple_ping') 102 103 104 def testCreateServerSuccess(self): 105 """Test create method can create a server successfully. 106 """ 107 ping_runner.PingRunner().simple_ping(self.PRIMARY_DRONE.hostname 108 ).AndReturn(True) 109 server_models.Server.objects.get( 110 hostname=self.PRIMARY_DRONE.hostname 111 ).AndRaise(django.core.exceptions.ObjectDoesNotExist) 112 server_models.Server.objects.create( 113 hostname=mox.IgnoreArg(), status=mox.IgnoreArg(), 114 date_created=mox.IgnoreArg(), note=mox.IgnoreArg() 115 ).AndReturn(self.PRIMARY_DRONE) 116 server_models.ServerRole.objects.create( 117 server=mox.IgnoreArg(), role=server_models.ServerRole.ROLE.DRONE 118 ).AndReturn(self.DRONE_ROLE) 119 self.mox.ReplayAll() 120 drone = server_manager.create(hostname=self.PRIMARY_DRONE.hostname, 121 role=server_models.ServerRole.ROLE.DRONE) 122 123 124 def testAddRoleToRepairRequiredSuccess(self): 125 """Test manager can add a role to a repair_failed server successfully. 126 127 Confirm that database call is made, and no action is taken, e.g., 128 restart scheduler to activate a new devserver. 129 """ 130 server_models.validate(role=server_models.ServerRole.ROLE.DEVSERVER) 131 server_manager_utils.check_server(mox.IgnoreArg(), 132 mox.IgnoreArg()).AndReturn(True) 133 server_manager_utils.use_server_db().MultipleTimes( 134 ).AndReturn(True) 135 self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') 136 self.REPAIR_REQUIRED_DRONE.get_role_names().AndReturn( 137 [server_models.ServerRole.ROLE.DRONE]) 138 server_models.ServerRole.objects.create( 139 server=mox.IgnoreArg(), 140 role=server_models.ServerRole.ROLE.DEVSERVER 141 ).AndReturn(self.DRONE_ROLE) 142 self.mox.ReplayAll() 143 server_manager._add_role(server=self.REPAIR_REQUIRED_DRONE, 144 role=server_models.ServerRole.ROLE.DEVSERVER, 145 action=True) 146 147 148 def testAddRoleToRepairRequiredFail_RoleAlreadyExists(self): 149 """Test manager fails to add a role to a repair_required server if 150 server already has the given role. 151 """ 152 server_models.validate(role=server_models.ServerRole.ROLE.DRONE) 153 self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') 154 self.REPAIR_REQUIRED_DRONE.get_role_names().AndReturn( 155 [server_models.ServerRole.ROLE.DRONE]) 156 self.mox.ReplayAll() 157 self.assertRaises(server_manager_utils.ServerActionError, 158 server_manager._add_role, 159 server=self.REPAIR_REQUIRED_DRONE, 160 role=server_models.ServerRole.ROLE.DRONE, 161 action=True) 162 163 164 def testDeleteRoleFromRepairRequiredSuccess(self): 165 """Test manager can delete a role from a repair_required server 166 successfully. 167 168 Confirm that database call is made, and no action is taken, e.g., 169 restart scheduler to delete an existing devserver. 170 """ 171 server_models.validate(role=server_models.ServerRole.ROLE.DRONE) 172 server_manager_utils.use_server_db().MultipleTimes( 173 ).AndReturn(True) 174 self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') 175 self.REPAIR_REQUIRED_DRONE.get_role_names().MultipleTimes().AndReturn( 176 [server_models.ServerRole.ROLE.DRONE]) 177 self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE.roles, 'get') 178 self.REPAIR_REQUIRED_DRONE.roles.get( 179 role=server_models.ServerRole.ROLE.DRONE 180 ).AndReturn(self.DRONE_ROLE) 181 self.mox.ReplayAll() 182 server_manager._delete_role(server=self.REPAIR_REQUIRED_DRONE, 183 role=server_models.ServerRole.ROLE.DRONE, 184 action=True) 185 186 187 def testDeleteRoleFromRepairRequiredFail_RoleNotExist(self): 188 """Test manager fails to delete a role from a repair_required server if 189 the server does not have the given role. 190 """ 191 server_models.validate(role=server_models.ServerRole.ROLE.DEVSERVER) 192 self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') 193 self.REPAIR_REQUIRED_DRONE.get_role_names().AndReturn( 194 [server_models.ServerRole.ROLE.DRONE]) 195 self.mox.ReplayAll() 196 self.assertRaises(server_manager_utils.ServerActionError, 197 server_manager._delete_role, 198 server=self.REPAIR_REQUIRED_DRONE, 199 role=server_models.ServerRole.ROLE.DEVSERVER, 200 action=True) 201 202 203 def testChangeStatusSuccess_RepairFailedToPrimary(self): 204 """Test manager can change the status of a repair_required server to 205 primary. 206 """ 207 server_models.validate(status=server_models.Server.STATUS.PRIMARY) 208 server_manager_utils.use_server_db().MultipleTimes( 209 ).AndReturn(True) 210 self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') 211 self.REPAIR_REQUIRED_DRONE.get_role_names().MultipleTimes().AndReturn( 212 [server_models.ServerRole.ROLE.DRONE]) 213 self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE.roles, 'filter') 214 self.REPAIR_REQUIRED_DRONE.roles.filter( 215 role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE 216 ).AndReturn(None) 217 server_models.Server.objects.filter( 218 roles__role=server_models.ServerRole.ROLE.SCHEDULER, 219 status=server_models.Server.STATUS.PRIMARY 220 ).AndReturn([self.PRIMARY_SCHEDULER]) 221 infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg()) 222 self.mox.ReplayAll() 223 server_manager._change_status( 224 server=self.REPAIR_REQUIRED_DRONE, 225 status=server_models.Server.STATUS.PRIMARY, 226 action=True) 227 228 229 def testChangeStatusSuccess_PrimaryToRepairFailed(self): 230 """Test manager can change the status of a primary server to 231 repair_required. 232 """ 233 server_models.validate( 234 status=server_models.Server.STATUS.REPAIR_REQUIRED) 235 self.mox.StubOutWithMock(self.PRIMARY_DRONE.roles, 'filter') 236 self.mox.StubOutWithMock(self.PRIMARY_DRONE, 'get_role_names') 237 self.PRIMARY_DRONE.get_role_names().MultipleTimes().AndReturn( 238 [server_models.ServerRole.ROLE.DRONE]) 239 self.PRIMARY_DRONE.roles.filter( 240 role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE 241 ).AndReturn(None) 242 server_manager_utils.use_server_db().MultipleTimes().AndReturn(True) 243 server_manager_utils.warn_missing_role( 244 server_models.ServerRole.ROLE.DRONE, self.PRIMARY_DRONE) 245 server_models.Server.objects.filter( 246 roles__role=server_models.ServerRole.ROLE.SCHEDULER, 247 status=server_models.Server.STATUS.PRIMARY 248 ).AndReturn([self.PRIMARY_SCHEDULER]) 249 infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg()) 250 self.mox.ReplayAll() 251 server_manager._change_status( 252 server=self.PRIMARY_DRONE, 253 status=server_models.Server.STATUS.REPAIR_REQUIRED, 254 action=True) 255 256 257 def testChangeStatusFail_StatusNoChange(self): 258 """Test manager cannot change the status of a server with the same 259 status. 260 """ 261 server_models.validate( 262 status=server_models.Server.STATUS.REPAIR_REQUIRED) 263 self.mox.ReplayAll() 264 self.assertRaises(server_manager_utils.ServerActionError, 265 server_manager._change_status, 266 server=self.REPAIR_REQUIRED_DRONE, 267 status=server_models.Server.STATUS.REPAIR_REQUIRED, 268 action=True) 269 270 271 def testChangeStatusFail_UniqueInstance(self): 272 """Test manager cannot change the status of a server from 273 repair_required to primary if there is already a primary exists for 274 role doesn't allow multiple instances. 275 """ 276 server_models.validate(status=server_models.Server.STATUS.PRIMARY) 277 self.mox.StubOutWithMock(self.REPAIR_REQUIRED_SCHEDULER.roles, 'filter') 278 self.REPAIR_REQUIRED_SCHEDULER.roles.filter( 279 role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE 280 ).AndReturn(QueriableList([self.SCHEDULER_ROLE])) 281 server_models.Server.objects.filter( 282 roles__role=self.SCHEDULER_ROLE.role, 283 status=server_models.Server.STATUS.PRIMARY 284 ).AndReturn(QueriableList([self.PRIMARY_SCHEDULER])) 285 self.mox.ReplayAll() 286 self.assertRaises(server_manager_utils.ServerActionError, 287 server_manager._change_status, 288 server=self.REPAIR_REQUIRED_SCHEDULER, 289 status=server_models.Server.STATUS.PRIMARY, 290 action=True) 291 292 293 def testAddRoleToRepairFailedFail_CheckServerFail(self): 294 """Test manager fails to add a role to a repair_required server if check 295 server is failed. 296 """ 297 server_manager_utils.check_server(mox.IgnoreArg(), 298 mox.IgnoreArg()).AndReturn(False) 299 server_models.validate(role=server_models.ServerRole.ROLE.DRONE) 300 self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') 301 self.REPAIR_REQUIRED_DRONE.get_role_names().MultipleTimes().AndReturn( 302 [server_models.ServerRole.ROLE.DRONE]) 303 self.mox.ReplayAll() 304 self.assertRaises(server_manager_utils.ServerActionError, 305 server_manager._add_role, 306 server=self.REPAIR_REQUIRED_DRONE, 307 role=server_models.ServerRole.ROLE.SCHEDULER, 308 action=True) 309 310 311 def testAddRoleToPrimarySuccess(self): 312 """Test manager can add a role to a primary server successfully. 313 314 Confirm that actions needs to be taken, e.g., restart scheduler for 315 new drone to be added. 316 """ 317 server_models.validate(role=server_models.ServerRole.ROLE.DRONE) 318 server_manager_utils.check_server(mox.IgnoreArg(), 319 mox.IgnoreArg()).AndReturn(True) 320 server_manager_utils.use_server_db().MultipleTimes().AndReturn(True) 321 self.mox.StubOutWithMock(self.PRIMARY_SCHEDULER, 'get_role_names') 322 self.PRIMARY_SCHEDULER.get_role_names().AndReturn( 323 [server_models.ServerRole.ROLE.SCHEDULER]) 324 server_models.ServerRole.objects.create( 325 server=self.PRIMARY_SCHEDULER, 326 role=server_models.ServerRole.ROLE.DRONE 327 ).AndReturn(self.DRONE_ROLE) 328 server_models.Server.objects.filter( 329 roles__role=server_models.ServerRole.ROLE.SCHEDULER, 330 status=server_models.Server.STATUS.PRIMARY 331 ).AndReturn([self.PRIMARY_SCHEDULER]) 332 infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg()) 333 self.mox.ReplayAll() 334 server_manager._add_role(self.PRIMARY_SCHEDULER, 335 server_models.ServerRole.ROLE.DRONE, 336 action=True) 337 338 339 def testDeleteRoleFromPrimarySuccess(self): 340 """Test manager can delete a role from a primary server successfully. 341 342 Confirm that database call is made, and actions are taken, e.g., 343 restart scheduler to delete an existing drone. 344 """ 345 server_manager_utils.use_server_db().MultipleTimes().AndReturn(True) 346 server_models.validate(role=server_models.ServerRole.ROLE.DRONE) 347 self.mox.StubOutWithMock(self.PRIMARY_DRONE, 'get_role_names') 348 self.PRIMARY_DRONE.get_role_names().MultipleTimes().AndReturn( 349 [server_models.ServerRole.ROLE.DRONE]) 350 351 self.mox.StubOutWithMock(self.PRIMARY_DRONE.roles, 'get') 352 self.PRIMARY_DRONE.roles.get( 353 role=server_models.ServerRole.ROLE.DRONE 354 ).AndReturn(self.DRONE_ROLE) 355 356 server_models.Server.objects.filter( 357 roles__role=server_models.ServerRole.ROLE.SCHEDULER, 358 status=server_models.Server.STATUS.PRIMARY 359 ).AndReturn([self.PRIMARY_SCHEDULER]) 360 server_manager.server_manager_utils.warn_missing_role( 361 server_models.ServerRole.ROLE.DRONE, self.PRIMARY_DRONE) 362 infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg()) 363 self.mox.ReplayAll() 364 server_manager._delete_role(self.PRIMARY_DRONE, 365 server_models.ServerRole.ROLE.DRONE, 366 action=True) 367 368 369 def testDeleteRoleFromPrimarySuccess_NoAction(self): 370 """Test manager can delete a role from a primary server successfully. 371 372 Confirm that database call is made, and no action is taken as action 373 is set to False. 374 """ 375 server_manager_utils.use_server_db().MultipleTimes().AndReturn(True) 376 server_models.validate(role=server_models.ServerRole.ROLE.DRONE) 377 self.mox.StubOutWithMock(self.PRIMARY_DRONE, 'get_role_names') 378 self.PRIMARY_DRONE.get_role_names().MultipleTimes().AndReturn( 379 [server_models.ServerRole.ROLE.DRONE]) 380 381 self.mox.StubOutWithMock(self.PRIMARY_DRONE.roles, 'get') 382 self.PRIMARY_DRONE.roles.get( 383 role=server_models.ServerRole.ROLE.DRONE 384 ).AndReturn(self.DRONE_ROLE) 385 386 server_manager.server_manager_utils.warn_missing_role( 387 server_models.ServerRole.ROLE.DRONE, self.PRIMARY_DRONE) 388 self.mox.ReplayAll() 389 server_manager._delete_role(self.PRIMARY_DRONE, 390 server_models.ServerRole.ROLE.DRONE, 391 action=False) 392 393 394if __name__ == "__main__": 395 unittest.main() 396