Home | History | Annotate | Download | only in site_utils
      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 
      5 import mox
      6 import unittest
      7 
      8 import common
      9 
     10 import django.core.exceptions
     11 from autotest_lib.client.common_lib.cros.network import ping_runner
     12 from autotest_lib.frontend import setup_django_environment
     13 from autotest_lib.frontend.server import models as server_models
     14 from autotest_lib.site_utils import server_manager
     15 from autotest_lib.site_utils import server_manager_utils
     16 from autotest_lib.site_utils.lib import infra
     17 
     18 
     19 class 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 
     43 class 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 
    394 if __name__ == "__main__":
    395     unittest.main()
    396