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.BACKUP_DRONE = mox.MockObject(
68                server_models.Server,
69                attrs={'hostname': 'backup_drone_hostname',
70                       'status': server_models.Server.STATUS.BACKUP,
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.BACKUP_SCHEDULER = mox.MockObject(
80                server_models.Server,
81                attrs={'hostname': 'backup_scheduler_hostname',
82                       'status': server_models.Server.STATUS.BACKUP,
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.BACKUP_DRONE.hostname
108                                             ).AndReturn(True)
109        server_models.Server.objects.get(
110                hostname=self.BACKUP_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.BACKUP_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.BACKUP_DRONE.hostname,
121                                      role=server_models.ServerRole.ROLE.DRONE)
122
123
124    def testAddRoleToBackupSuccess(self):
125        """Test manager can add a role to a backup 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.BACKUP_DRONE, 'get_role_names')
136        self.BACKUP_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.BACKUP_DRONE,
144                                 role=server_models.ServerRole.ROLE.DEVSERVER,
145                                 action=True)
146
147
148    def testAddRoleToBackupFail_RoleAlreadyExists(self):
149        """Test manager fails to add a role to a backup server if server already
150        has the given role.
151        """
152        server_models.validate(role=server_models.ServerRole.ROLE.DRONE)
153        self.mox.StubOutWithMock(self.BACKUP_DRONE, 'get_role_names')
154        self.BACKUP_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.BACKUP_DRONE,
160                          role=server_models.ServerRole.ROLE.DRONE,
161                          action=True)
162
163
164    def testDeleteRoleFromBackupSuccess(self):
165        """Test manager can delete a role from a backup server successfully.
166
167        Confirm that database call is made, and no action is taken, e.g.,
168        restart scheduler to delete an existing devserver.
169        """
170        server_models.validate(role=server_models.ServerRole.ROLE.DRONE)
171        server_manager_utils.use_server_db().MultipleTimes(
172                ).AndReturn(True)
173        self.mox.StubOutWithMock(self.BACKUP_DRONE, 'get_role_names')
174        self.BACKUP_DRONE.get_role_names().MultipleTimes().AndReturn(
175                [server_models.ServerRole.ROLE.DRONE])
176        self.mox.StubOutWithMock(self.BACKUP_DRONE.roles, 'get')
177        self.BACKUP_DRONE.roles.get(
178                role=server_models.ServerRole.ROLE.DRONE
179                ).AndReturn(self.DRONE_ROLE)
180        self.mox.ReplayAll()
181        server_manager._delete_role(server=self.BACKUP_DRONE,
182                                    role=server_models.ServerRole.ROLE.DRONE,
183                                    action=True)
184
185
186    def testDeleteRoleFromBackupFail_RoleNotExist(self):
187        """Test manager fails to delete a role from a backup server if the
188        server does not have the given role.
189        """
190        server_models.validate(role=server_models.ServerRole.ROLE.DEVSERVER)
191        self.mox.StubOutWithMock(self.BACKUP_DRONE, 'get_role_names')
192        self.BACKUP_DRONE.get_role_names().AndReturn(
193                [server_models.ServerRole.ROLE.DRONE])
194        self.mox.ReplayAll()
195        self.assertRaises(server_manager_utils.ServerActionError,
196                          server_manager._delete_role, server=self.BACKUP_DRONE,
197                          role=server_models.ServerRole.ROLE.DEVSERVER,
198                          action=True)
199
200
201    def testChangeStatusSuccess_BackupToPrimary(self):
202        """Test manager can change the status of a backup server to primary.
203        """
204        server_models.validate(status=server_models.Server.STATUS.PRIMARY)
205        server_manager_utils.use_server_db().MultipleTimes(
206                ).AndReturn(True)
207        self.mox.StubOutWithMock(self.BACKUP_DRONE, 'get_role_names')
208        self.BACKUP_DRONE.get_role_names().MultipleTimes().AndReturn(
209                [server_models.ServerRole.ROLE.DRONE])
210        self.mox.StubOutWithMock(self.BACKUP_DRONE.roles, 'filter')
211        self.BACKUP_DRONE.roles.filter(
212                role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE
213                ).AndReturn(None)
214        server_models.Server.objects.filter(
215                roles__role=server_models.ServerRole.ROLE.SCHEDULER,
216                status=server_models.Server.STATUS.PRIMARY
217                ).AndReturn([self.PRIMARY_SCHEDULER])
218        infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg())
219        self.mox.ReplayAll()
220        server_manager._change_status(
221                server=self.BACKUP_DRONE,
222                status=server_models.Server.STATUS.PRIMARY,
223                action=True)
224
225
226    def testChangeStatusSuccess_PrimaryToBackup(self):
227        """Test manager can change the status of a primary server to backup.
228        """
229        server_models.validate(status=server_models.Server.STATUS.BACKUP)
230        self.mox.StubOutWithMock(self.PRIMARY_DRONE.roles, 'filter')
231        self.mox.StubOutWithMock(self.PRIMARY_DRONE, 'get_role_names')
232        self.PRIMARY_DRONE.get_role_names().MultipleTimes().AndReturn(
233                [server_models.ServerRole.ROLE.DRONE])
234        self.PRIMARY_DRONE.roles.filter(
235                role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE
236                ).AndReturn(None)
237        server_manager_utils.use_server_db().MultipleTimes().AndReturn(True)
238        server_manager_utils.warn_missing_role(
239                server_models.ServerRole.ROLE.DRONE, self.PRIMARY_DRONE)
240        server_models.Server.objects.filter(
241                roles__role=server_models.ServerRole.ROLE.SCHEDULER,
242                status=server_models.Server.STATUS.PRIMARY
243                ).AndReturn([self.PRIMARY_SCHEDULER])
244        infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg())
245        self.mox.ReplayAll()
246        server_manager._change_status(
247                server=self.PRIMARY_DRONE,
248                status=server_models.Server.STATUS.BACKUP,
249                action=True)
250
251
252    def testChangeStatusFail_StatusNoChange(self):
253        """Test manager cannot change the status of a server with the same
254        status.
255        """
256        server_models.validate(status=server_models.Server.STATUS.BACKUP)
257        self.mox.ReplayAll()
258        self.assertRaises(server_manager_utils.ServerActionError,
259                          server_manager._change_status,
260                          server=self.BACKUP_DRONE,
261                          status=server_models.Server.STATUS.BACKUP,
262                          action=True)
263
264
265    def testChangeStatusFail_UniqueInstance(self):
266        """Test manager cannot change the status of a server from backup to
267        primary if there is already a primary exists for role doesn't allow
268        multiple instances.
269        """
270        server_models.validate(status=server_models.Server.STATUS.PRIMARY)
271        self.mox.StubOutWithMock(self.BACKUP_SCHEDULER.roles, 'filter')
272        self.BACKUP_SCHEDULER.roles.filter(
273                role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE
274                ).AndReturn(QueriableList([self.SCHEDULER_ROLE]))
275        server_models.Server.objects.filter(
276                roles__role=self.SCHEDULER_ROLE.role,
277                status=server_models.Server.STATUS.PRIMARY
278                ).AndReturn(QueriableList([self.PRIMARY_SCHEDULER]))
279        self.mox.ReplayAll()
280        self.assertRaises(server_manager_utils.ServerActionError,
281                          server_manager._change_status,
282                          server=self.BACKUP_SCHEDULER,
283                          status=server_models.Server.STATUS.PRIMARY,
284                          action=True)
285
286
287    def testAddRoleToBackupFail_CheckServerFail(self):
288        """Test manager fails to add a role to a backup server if check_server
289        is failed.
290        """
291        server_manager_utils.check_server(mox.IgnoreArg(),
292                                          mox.IgnoreArg()).AndReturn(False)
293        server_models.validate(role=server_models.ServerRole.ROLE.DRONE)
294        self.mox.StubOutWithMock(self.BACKUP_DRONE, 'get_role_names')
295        self.BACKUP_DRONE.get_role_names().MultipleTimes().AndReturn(
296                [server_models.ServerRole.ROLE.DRONE])
297        self.mox.ReplayAll()
298        self.assertRaises(server_manager_utils.ServerActionError,
299                          server_manager._add_role, server=self.BACKUP_DRONE,
300                          role=server_models.ServerRole.ROLE.SCHEDULER,
301                          action=True)
302
303
304    def testAddRoleToPrimarySuccess(self):
305        """Test manager can add a role to a primary server successfully.
306
307        Confirm that actions needs to be taken, e.g., restart scheduler for
308        new drone to be added.
309        """
310        server_models.validate(role=server_models.ServerRole.ROLE.DRONE)
311        server_manager_utils.check_server(mox.IgnoreArg(),
312                                          mox.IgnoreArg()).AndReturn(True)
313        server_manager_utils.use_server_db().MultipleTimes().AndReturn(True)
314        self.mox.StubOutWithMock(self.PRIMARY_SCHEDULER, 'get_role_names')
315        self.PRIMARY_SCHEDULER.get_role_names().AndReturn(
316                [server_models.ServerRole.ROLE.SCHEDULER])
317        server_models.ServerRole.objects.create(
318                server=self.PRIMARY_SCHEDULER,
319                role=server_models.ServerRole.ROLE.DRONE
320                ).AndReturn(self.DRONE_ROLE)
321        server_models.Server.objects.filter(
322                roles__role=server_models.ServerRole.ROLE.SCHEDULER,
323                status=server_models.Server.STATUS.PRIMARY
324                ).AndReturn([self.PRIMARY_SCHEDULER])
325        infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg())
326        self.mox.ReplayAll()
327        server_manager._add_role(self.PRIMARY_SCHEDULER,
328                                 server_models.ServerRole.ROLE.DRONE,
329                                 action=True)
330
331
332    def testDeleteRoleFromPrimarySuccess(self):
333        """Test manager can delete a role from a primary server successfully.
334
335        Confirm that database call is made, and actions are taken, e.g.,
336        restart scheduler to delete an existing drone.
337        """
338        server_manager_utils.use_server_db().MultipleTimes().AndReturn(True)
339        server_models.validate(role=server_models.ServerRole.ROLE.DRONE)
340        self.mox.StubOutWithMock(self.PRIMARY_DRONE, 'get_role_names')
341        self.PRIMARY_DRONE.get_role_names().MultipleTimes().AndReturn(
342                [server_models.ServerRole.ROLE.DRONE])
343
344        self.mox.StubOutWithMock(self.PRIMARY_DRONE.roles, 'get')
345        self.PRIMARY_DRONE.roles.get(
346                role=server_models.ServerRole.ROLE.DRONE
347                ).AndReturn(self.DRONE_ROLE)
348
349        server_models.Server.objects.filter(
350                roles__role=server_models.ServerRole.ROLE.SCHEDULER,
351                status=server_models.Server.STATUS.PRIMARY
352                ).AndReturn([self.PRIMARY_SCHEDULER])
353        server_manager.server_manager_utils.warn_missing_role(
354                server_models.ServerRole.ROLE.DRONE, self.PRIMARY_DRONE)
355        infra.execute_command(mox.IgnoreArg(), mox.IgnoreArg())
356        self.mox.ReplayAll()
357        server_manager._delete_role(self.PRIMARY_DRONE,
358                                    server_models.ServerRole.ROLE.DRONE,
359                                    action=True)
360
361
362    def testDeleteRoleFromPrimarySuccess_NoAction(self):
363        """Test manager can delete a role from a primary server successfully.
364
365        Confirm that database call is made, and no action is taken as action
366        is set to False.
367        """
368        server_manager_utils.use_server_db().MultipleTimes().AndReturn(True)
369        server_models.validate(role=server_models.ServerRole.ROLE.DRONE)
370        self.mox.StubOutWithMock(self.PRIMARY_DRONE, 'get_role_names')
371        self.PRIMARY_DRONE.get_role_names().MultipleTimes().AndReturn(
372                [server_models.ServerRole.ROLE.DRONE])
373
374        self.mox.StubOutWithMock(self.PRIMARY_DRONE.roles, 'get')
375        self.PRIMARY_DRONE.roles.get(
376                role=server_models.ServerRole.ROLE.DRONE
377                ).AndReturn(self.DRONE_ROLE)
378
379        server_manager.server_manager_utils.warn_missing_role(
380                server_models.ServerRole.ROLE.DRONE, self.PRIMARY_DRONE)
381        self.mox.ReplayAll()
382        server_manager._delete_role(self.PRIMARY_DRONE,
383                                    server_models.ServerRole.ROLE.DRONE,
384                                    action=False)
385
386
387if '__main__':
388    unittest.main()
389