| # Copyright 2014 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import mox |
| import unittest |
| |
| import common |
| |
| import django.core.exceptions |
| from autotest_lib.client.common_lib.cros.network import ping_runner |
| from autotest_lib.frontend import setup_django_environment |
| from autotest_lib.frontend.server import models as server_models |
| from autotest_lib.site_utils import server_manager |
| from autotest_lib.site_utils import server_manager_utils |
| from autotest_lib.site_utils.lib import infra |
| |
| |
| class QueriableList(list): |
| """A mock list object supports queries including filter and all. |
| """ |
| |
| def filter(self, **kwargs): |
| """Mock the filter call in django model. |
| """ |
| raise NotImplementedError() |
| |
| |
| def get(self, **kwargs): |
| """Mock the get call in django model. |
| """ |
| raise NotImplementedError() |
| |
| |
| def all(self): |
| """Return all items in the list. |
| |
| @return: All items in the list. |
| """ |
| return [item for item in self] |
| |
| |
| class ServerManagerUnittests(mox.MoxTestBase): |
| """Unittest for testing server_manager module. |
| """ |
| |
| def setUp(self): |
| """Initialize the unittest.""" |
| super(ServerManagerUnittests, self).setUp() |
| |
| # Initialize test objects. |
| self.DRONE_ROLE = mox.MockObject( |
| server_models.ServerRole, |
| attrs={'role': server_models.ServerRole.ROLE.DRONE}) |
| self.SCHEDULER_ROLE = mox.MockObject( |
| server_models.ServerRole, |
| attrs={'role': server_models.ServerRole.ROLE.SCHEDULER}) |
| self.DRONE_ATTRIBUTE = mox.MockObject( |
| server_models.ServerAttribute, |
| attrs={'attribute': 'max_processes', 'value':1}) |
| self.PRIMARY_DRONE = mox.MockObject( |
| server_models.Server, |
| attrs={'hostname': 'primary_drone_hostname', |
| 'status': server_models.Server.STATUS.PRIMARY, |
| 'roles': QueriableList([self.DRONE_ROLE]), |
| 'attributes': QueriableList([self.DRONE_ATTRIBUTE])}) |
| self.REPAIR_REQUIRED_DRONE = mox.MockObject( |
| server_models.Server, |
| attrs={'hostname': 'repair_required_drone_hostname', |
| 'status': server_models.Server.STATUS.REPAIR_REQUIRED, |
| 'roles': QueriableList([self.DRONE_ROLE]), |
| 'attributes': QueriableList([self.DRONE_ATTRIBUTE])}) |
| self.PRIMARY_SCHEDULER = mox.MockObject( |
| server_models.Server, |
| attrs={'hostname': 'primary_scheduler_hostname', |
| 'status': server_models.Server.STATUS.PRIMARY, |
| 'roles': QueriableList([self.SCHEDULER_ROLE]), |
| 'attributes': QueriableList([])}) |
| self.REPAIR_REQUIRED_SCHEDULER = mox.MockObject( |
| server_models.Server, |
| attrs={'hostname': 'repair_required_scheduler_hostname', |
| 'status': server_models.Server.STATUS.REPAIR_REQUIRED, |
| 'roles': QueriableList([self.SCHEDULER_ROLE]), |
| 'attributes': QueriableList([])}) |
| |
| self.mox.StubOutWithMock(server_manager_utils, 'check_server') |
| self.mox.StubOutWithMock(server_manager_utils, 'warn_missing_role') |
| self.mox.StubOutWithMock(server_manager_utils, 'use_server_db') |
| self.mox.StubOutWithMock(server_models.Server, 'get_role_names') |
| self.mox.StubOutWithMock(server_models.Server.objects, 'create') |
| self.mox.StubOutWithMock(server_models.Server.objects, 'filter') |
| self.mox.StubOutWithMock(server_models.Server.objects, 'get') |
| self.mox.StubOutWithMock(server_models.ServerRole, 'delete') |
| self.mox.StubOutWithMock(server_models.ServerRole.objects, 'create') |
| self.mox.StubOutWithMock(server_models.ServerRole.objects, 'filter') |
| self.mox.StubOutWithMock(server_models.ServerAttribute.objects, |
| 'create') |
| self.mox.StubOutWithMock(server_models.ServerAttribute.objects, |
| 'filter') |
| self.mox.StubOutWithMock(infra, 'execute_command') |
| self.mox.StubOutWithMock(ping_runner.PingRunner, 'simple_ping') |
| |
| |
| def testCreateServerSuccess(self): |
| """Test create method can create a server successfully. |
| """ |
| ping_runner.PingRunner().simple_ping(self.PRIMARY_DRONE.hostname |
| ).AndReturn(True) |
| server_models.Server.objects.get( |
| hostname=self.PRIMARY_DRONE.hostname |
| ).AndRaise(django.core.exceptions.ObjectDoesNotExist) |
| server_models.Server.objects.create( |
| hostname=mox.IgnoreArg(), status=mox.IgnoreArg(), |
| date_created=mox.IgnoreArg(), note=mox.IgnoreArg() |
| ).AndReturn(self.PRIMARY_DRONE) |
| server_models.ServerRole.objects.create( |
| server=mox.IgnoreArg(), role=server_models.ServerRole.ROLE.DRONE |
| ).AndReturn(self.DRONE_ROLE) |
| self.mox.ReplayAll() |
| drone = server_manager.create(hostname=self.PRIMARY_DRONE.hostname, |
| role=server_models.ServerRole.ROLE.DRONE) |
| |
| |
| def testAddRoleToRepairRequiredSuccess(self): |
| """Test manager can add a role to a repair_failed server successfully. |
| |
| Confirm that database call is made, and no action is taken, e.g., |
| restart scheduler to activate a new devserver. |
| """ |
| server_models.validate(role=server_models.ServerRole.ROLE.DEVSERVER) |
| server_manager_utils.check_server(mox.IgnoreArg(), |
| mox.IgnoreArg()).AndReturn(True) |
| server_manager_utils.use_server_db().MultipleTimes( |
| ).AndReturn(True) |
| self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') |
| self.REPAIR_REQUIRED_DRONE.get_role_names().AndReturn( |
| [server_models.ServerRole.ROLE.DRONE]) |
| server_models.ServerRole.objects.create( |
| server=mox.IgnoreArg(), |
| role=server_models.ServerRole.ROLE.DEVSERVER |
| ).AndReturn(self.DRONE_ROLE) |
| self.mox.ReplayAll() |
| server_manager._add_role(server=self.REPAIR_REQUIRED_DRONE, |
| role=server_models.ServerRole.ROLE.DEVSERVER, |
| action=True) |
| |
| |
| def testAddRoleToRepairRequiredFail_RoleAlreadyExists(self): |
| """Test manager fails to add a role to a repair_required server if |
| server already has the given role. |
| """ |
| server_models.validate(role=server_models.ServerRole.ROLE.DRONE) |
| self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') |
| self.REPAIR_REQUIRED_DRONE.get_role_names().AndReturn( |
| [server_models.ServerRole.ROLE.DRONE]) |
| self.mox.ReplayAll() |
| self.assertRaises(server_manager_utils.ServerActionError, |
| server_manager._add_role, |
| server=self.REPAIR_REQUIRED_DRONE, |
| role=server_models.ServerRole.ROLE.DRONE, |
| action=True) |
| |
| |
| def testDeleteRoleFromRepairRequiredSuccess(self): |
| """Test manager can delete a role from a repair_required server |
| successfully. |
| |
| Confirm that database call is made, and no action is taken, e.g., |
| restart scheduler to delete an existing devserver. |
| """ |
| server_models.validate(role=server_models.ServerRole.ROLE.DRONE) |
| server_manager_utils.use_server_db().MultipleTimes( |
| ).AndReturn(True) |
| self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') |
| self.REPAIR_REQUIRED_DRONE.get_role_names().MultipleTimes().AndReturn( |
| [server_models.ServerRole.ROLE.DRONE]) |
| self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE.roles, 'get') |
| self.REPAIR_REQUIRED_DRONE.roles.get( |
| role=server_models.ServerRole.ROLE.DRONE |
| ).AndReturn(self.DRONE_ROLE) |
| self.mox.ReplayAll() |
| server_manager._delete_role(server=self.REPAIR_REQUIRED_DRONE, |
| role=server_models.ServerRole.ROLE.DRONE, |
| action=True) |
| |
| |
| def testDeleteRoleFromRepairRequiredFail_RoleNotExist(self): |
| """Test manager fails to delete a role from a repair_required server if |
| the server does not have the given role. |
| """ |
| server_models.validate(role=server_models.ServerRole.ROLE.DEVSERVER) |
| self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') |
| self.REPAIR_REQUIRED_DRONE.get_role_names().AndReturn( |
| [server_models.ServerRole.ROLE.DRONE]) |
| self.mox.ReplayAll() |
| self.assertRaises(server_manager_utils.ServerActionError, |
| server_manager._delete_role, |
| server=self.REPAIR_REQUIRED_DRONE, |
| role=server_models.ServerRole.ROLE.DEVSERVER, |
| action=True) |
| |
| |
| def testChangeStatusSuccess_RepairFailedToPrimary(self): |
| """Test manager can change the status of a repair_required server to |
| primary. |
| """ |
| server_models.validate(status=server_models.Server.STATUS.PRIMARY) |
| server_manager_utils.use_server_db().MultipleTimes( |
| ).AndReturn(True) |
| self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') |
| self.REPAIR_REQUIRED_DRONE.get_role_names().MultipleTimes().AndReturn( |
| [server_models.ServerRole.ROLE.DRONE]) |
| self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE.roles, 'filter') |
| self.REPAIR_REQUIRED_DRONE.roles.filter( |
| role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE |
| ).AndReturn(None) |
| server_models.Server.objects.filter( |
| roles__role=server_models.ServerRole.ROLE.SCHEDULER, |
| status=server_models.Server.STATUS.PRIMARY |
| ).AndReturn([self.PRIMARY_SCHEDULER]) |
| infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg()) |
| self.mox.ReplayAll() |
| server_manager._change_status( |
| server=self.REPAIR_REQUIRED_DRONE, |
| status=server_models.Server.STATUS.PRIMARY, |
| action=True) |
| |
| |
| def testChangeStatusSuccess_PrimaryToRepairFailed(self): |
| """Test manager can change the status of a primary server to |
| repair_required. |
| """ |
| server_models.validate( |
| status=server_models.Server.STATUS.REPAIR_REQUIRED) |
| self.mox.StubOutWithMock(self.PRIMARY_DRONE.roles, 'filter') |
| self.mox.StubOutWithMock(self.PRIMARY_DRONE, 'get_role_names') |
| self.PRIMARY_DRONE.get_role_names().MultipleTimes().AndReturn( |
| [server_models.ServerRole.ROLE.DRONE]) |
| self.PRIMARY_DRONE.roles.filter( |
| role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE |
| ).AndReturn(None) |
| server_manager_utils.use_server_db().MultipleTimes().AndReturn(True) |
| server_manager_utils.warn_missing_role( |
| server_models.ServerRole.ROLE.DRONE, self.PRIMARY_DRONE) |
| server_models.Server.objects.filter( |
| roles__role=server_models.ServerRole.ROLE.SCHEDULER, |
| status=server_models.Server.STATUS.PRIMARY |
| ).AndReturn([self.PRIMARY_SCHEDULER]) |
| infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg()) |
| self.mox.ReplayAll() |
| server_manager._change_status( |
| server=self.PRIMARY_DRONE, |
| status=server_models.Server.STATUS.REPAIR_REQUIRED, |
| action=True) |
| |
| |
| def testChangeStatusFail_StatusNoChange(self): |
| """Test manager cannot change the status of a server with the same |
| status. |
| """ |
| server_models.validate( |
| status=server_models.Server.STATUS.REPAIR_REQUIRED) |
| self.mox.ReplayAll() |
| self.assertRaises(server_manager_utils.ServerActionError, |
| server_manager._change_status, |
| server=self.REPAIR_REQUIRED_DRONE, |
| status=server_models.Server.STATUS.REPAIR_REQUIRED, |
| action=True) |
| |
| |
| def testChangeStatusFail_UniqueInstance(self): |
| """Test manager cannot change the status of a server from |
| repair_required to primary if there is already a primary exists for |
| role doesn't allow multiple instances. |
| """ |
| server_models.validate(status=server_models.Server.STATUS.PRIMARY) |
| self.mox.StubOutWithMock(self.REPAIR_REQUIRED_SCHEDULER.roles, 'filter') |
| self.REPAIR_REQUIRED_SCHEDULER.roles.filter( |
| role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE |
| ).AndReturn(QueriableList([self.SCHEDULER_ROLE])) |
| server_models.Server.objects.filter( |
| roles__role=self.SCHEDULER_ROLE.role, |
| status=server_models.Server.STATUS.PRIMARY |
| ).AndReturn(QueriableList([self.PRIMARY_SCHEDULER])) |
| self.mox.ReplayAll() |
| self.assertRaises(server_manager_utils.ServerActionError, |
| server_manager._change_status, |
| server=self.REPAIR_REQUIRED_SCHEDULER, |
| status=server_models.Server.STATUS.PRIMARY, |
| action=True) |
| |
| |
| def testAddRoleToRepairFailedFail_CheckServerFail(self): |
| """Test manager fails to add a role to a repair_required server if check |
| server is failed. |
| """ |
| server_manager_utils.check_server(mox.IgnoreArg(), |
| mox.IgnoreArg()).AndReturn(False) |
| server_models.validate(role=server_models.ServerRole.ROLE.DRONE) |
| self.mox.StubOutWithMock(self.REPAIR_REQUIRED_DRONE, 'get_role_names') |
| self.REPAIR_REQUIRED_DRONE.get_role_names().MultipleTimes().AndReturn( |
| [server_models.ServerRole.ROLE.DRONE]) |
| self.mox.ReplayAll() |
| self.assertRaises(server_manager_utils.ServerActionError, |
| server_manager._add_role, |
| server=self.REPAIR_REQUIRED_DRONE, |
| role=server_models.ServerRole.ROLE.SCHEDULER, |
| action=True) |
| |
| |
| def testAddRoleToPrimarySuccess(self): |
| """Test manager can add a role to a primary server successfully. |
| |
| Confirm that actions needs to be taken, e.g., restart scheduler for |
| new drone to be added. |
| """ |
| server_models.validate(role=server_models.ServerRole.ROLE.DRONE) |
| server_manager_utils.check_server(mox.IgnoreArg(), |
| mox.IgnoreArg()).AndReturn(True) |
| server_manager_utils.use_server_db().MultipleTimes().AndReturn(True) |
| self.mox.StubOutWithMock(self.PRIMARY_SCHEDULER, 'get_role_names') |
| self.PRIMARY_SCHEDULER.get_role_names().AndReturn( |
| [server_models.ServerRole.ROLE.SCHEDULER]) |
| server_models.ServerRole.objects.create( |
| server=self.PRIMARY_SCHEDULER, |
| role=server_models.ServerRole.ROLE.DRONE |
| ).AndReturn(self.DRONE_ROLE) |
| server_models.Server.objects.filter( |
| roles__role=server_models.ServerRole.ROLE.SCHEDULER, |
| status=server_models.Server.STATUS.PRIMARY |
| ).AndReturn([self.PRIMARY_SCHEDULER]) |
| infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg()) |
| self.mox.ReplayAll() |
| server_manager._add_role(self.PRIMARY_SCHEDULER, |
| server_models.ServerRole.ROLE.DRONE, |
| action=True) |
| |
| |
| def testDeleteRoleFromPrimarySuccess(self): |
| """Test manager can delete a role from a primary server successfully. |
| |
| Confirm that database call is made, and actions are taken, e.g., |
| restart scheduler to delete an existing drone. |
| """ |
| server_manager_utils.use_server_db().MultipleTimes().AndReturn(True) |
| server_models.validate(role=server_models.ServerRole.ROLE.DRONE) |
| self.mox.StubOutWithMock(self.PRIMARY_DRONE, 'get_role_names') |
| self.PRIMARY_DRONE.get_role_names().MultipleTimes().AndReturn( |
| [server_models.ServerRole.ROLE.DRONE]) |
| |
| self.mox.StubOutWithMock(self.PRIMARY_DRONE.roles, 'get') |
| self.PRIMARY_DRONE.roles.get( |
| role=server_models.ServerRole.ROLE.DRONE |
| ).AndReturn(self.DRONE_ROLE) |
| |
| server_models.Server.objects.filter( |
| roles__role=server_models.ServerRole.ROLE.SCHEDULER, |
| status=server_models.Server.STATUS.PRIMARY |
| ).AndReturn([self.PRIMARY_SCHEDULER]) |
| server_manager.server_manager_utils.warn_missing_role( |
| server_models.ServerRole.ROLE.DRONE, self.PRIMARY_DRONE) |
| infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg()) |
| self.mox.ReplayAll() |
| server_manager._delete_role(self.PRIMARY_DRONE, |
| server_models.ServerRole.ROLE.DRONE, |
| action=True) |
| |
| |
| def testDeleteRoleFromPrimarySuccess_NoAction(self): |
| """Test manager can delete a role from a primary server successfully. |
| |
| Confirm that database call is made, and no action is taken as action |
| is set to False. |
| """ |
| server_manager_utils.use_server_db().MultipleTimes().AndReturn(True) |
| server_models.validate(role=server_models.ServerRole.ROLE.DRONE) |
| self.mox.StubOutWithMock(self.PRIMARY_DRONE, 'get_role_names') |
| self.PRIMARY_DRONE.get_role_names().MultipleTimes().AndReturn( |
| [server_models.ServerRole.ROLE.DRONE]) |
| |
| self.mox.StubOutWithMock(self.PRIMARY_DRONE.roles, 'get') |
| self.PRIMARY_DRONE.roles.get( |
| role=server_models.ServerRole.ROLE.DRONE |
| ).AndReturn(self.DRONE_ROLE) |
| |
| server_manager.server_manager_utils.warn_missing_role( |
| server_models.ServerRole.ROLE.DRONE, self.PRIMARY_DRONE) |
| self.mox.ReplayAll() |
| server_manager._delete_role(self.PRIMARY_DRONE, |
| server_models.ServerRole.ROLE.DRONE, |
| action=False) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |