Home | History | Annotate | Download | only in afe
      1 #!/usr/bin/python
      2 # pylint: disable=missing-docstring
      3 
      4 import unittest
      5 
      6 import common
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.frontend import setup_django_environment
      9 from autotest_lib.frontend.afe import frontend_test_utils
     10 from autotest_lib.frontend.afe import models, model_logic
     11 
     12 
     13 class AclGroupTest(unittest.TestCase,
     14                    frontend_test_utils.FrontendTestMixin):
     15     def setUp(self):
     16         self._frontend_common_setup()
     17 
     18 
     19     def tearDown(self):
     20         self._frontend_common_teardown()
     21 
     22 
     23     def _check_acls(self, host, acl_name_list):
     24         actual_acl_names = [acl_group.name for acl_group
     25                             in host.aclgroup_set.all()]
     26         self.assertEquals(set(actual_acl_names), set(acl_name_list))
     27 
     28 
     29     def test_on_host_membership_change(self):
     30         host1, host2 = self.hosts[1:3]
     31         everyone_acl = models.AclGroup.objects.get(name='Everyone')
     32 
     33         host1.aclgroup_set.clear()
     34         self._check_acls(host1, [])
     35         host2.aclgroup_set.add(everyone_acl)
     36         self._check_acls(host2, ['Everyone', 'my_acl'])
     37 
     38         models.AclGroup.on_host_membership_change()
     39 
     40         self._check_acls(host1, ['Everyone'])
     41         self._check_acls(host2, ['my_acl'])
     42 
     43 
     44 class HostTest(unittest.TestCase,
     45                frontend_test_utils.FrontendTestMixin):
     46     def setUp(self):
     47         self._frontend_common_setup()
     48 
     49 
     50     def tearDown(self):
     51         self._frontend_common_teardown()
     52 
     53 
     54     def _get_attributes(self, host):
     55         models.Host.objects.populate_relationships(
     56                 [host], models.HostAttribute, 'attribute_list')
     57         return dict((attribute.attribute, attribute.value)
     58                     for attribute in host.attribute_list)
     59 
     60     def test_delete_attribute(self):
     61         previous_config = models.RESPECT_STATIC_ATTRIBUTES
     62         models.RESPECT_STATIC_ATTRIBUTES = False
     63         host1 = models.Host.objects.create(hostname='test_host1')
     64         host1.set_attribute('test_attribute1', 'test_value1')
     65 
     66         attributes = self._get_attributes(host1)
     67         self.assertEquals(attributes['test_attribute1'], 'test_value1')
     68 
     69         host1.set_or_delete_attribute('test_attribute1', None)
     70         attributes = self._get_attributes(host1)
     71         self.assertNotIn('test_attribute1', attributes.keys())
     72 
     73         models.RESPECT_STATIC_ATTRIBUTES = previous_config
     74 
     75 
     76     def test_delete_static_attribute(self):
     77         previous_config = models.RESPECT_STATIC_ATTRIBUTES
     78         models.RESPECT_STATIC_ATTRIBUTES = True
     79         host1 = models.Host.objects.create(hostname='test_host1')
     80         host1.set_attribute('test_attribute1', 'test_value1')
     81         self._set_static_attribute(host1, 'test_attribute1', 'test_value1')
     82 
     83         self.assertRaises(
     84                 error.UnmodifiableAttributeException,
     85                 host1.set_or_delete_attribute,
     86                 'test_attribute1', None)
     87 
     88         models.RESPECT_STATIC_ATTRIBUTES = previous_config
     89 
     90 
     91     def test_set_attribute(self):
     92         previous_config = models.RESPECT_STATIC_ATTRIBUTES
     93         models.RESPECT_STATIC_ATTRIBUTES = False
     94         host1 = models.Host.objects.create(hostname='test_host1')
     95         host1.set_attribute('test_attribute1', 'test_value1')
     96 
     97         host1.set_or_delete_attribute('test_attribute1', 'test_new_value1')
     98 
     99         attributes = self._get_attributes(host1)
    100         self.assertEquals(attributes['test_attribute1'], 'test_new_value1')
    101 
    102         models.RESPECT_STATIC_ATTRIBUTES = previous_config
    103 
    104 
    105     def test_set_static_attribute(self):
    106         previous_config = models.RESPECT_STATIC_ATTRIBUTES
    107         models.RESPECT_STATIC_ATTRIBUTES = True
    108         host1 = models.Host.objects.create(hostname='test_host1')
    109         host1.set_attribute('test_attribute1', 'test_value1')
    110         self._set_static_attribute(host1, 'test_attribute1', 'test_value1')
    111 
    112         self.assertRaises(
    113                 error.UnmodifiableAttributeException,
    114                 host1.set_or_delete_attribute,
    115                 'test_attribute1', 'test_value2')
    116 
    117         models.RESPECT_STATIC_ATTRIBUTES = previous_config
    118 
    119 
    120     def test_add_host_previous_one_time_host(self):
    121         # ensure that when adding a host which was previously used as a one-time
    122         # host, the status isn't reset, since this can interfere with the
    123         # scheduler.
    124         host = models.Host.create_one_time_host('othost')
    125         self.assertEquals(host.invalid, True)
    126         self.assertEquals(host.status, models.Host.Status.READY)
    127 
    128         host.status = models.Host.Status.RUNNING
    129         host.save()
    130 
    131         host2 = models.Host.add_object(hostname='othost')
    132         self.assertEquals(host2.id, host.id)
    133         self.assertEquals(host2.status, models.Host.Status.RUNNING)
    134 
    135 
    136     def test_check_board_labels_allowed(self):
    137         host = models.Host.create_one_time_host('othost')
    138         # First check with host with no board label.
    139         self.assertEqual(host.check_board_labels_allowed([host]), None)
    140 
    141         # Second check with host with board label
    142         label = models.Label.add_object(name='board:test')
    143         label.host_set.add(host)
    144         self.assertRaises(model_logic.ValidationError,
    145                           host.check_board_labels_allowed, [host],
    146                           ['board:new_board'])
    147 
    148 
    149 class SpecialTaskUnittest(unittest.TestCase,
    150                           frontend_test_utils.FrontendTestMixin):
    151     def setUp(self):
    152         self._frontend_common_setup()
    153 
    154 
    155     def tearDown(self):
    156         self._frontend_common_teardown()
    157 
    158 
    159     def _create_task(self):
    160         return models.SpecialTask.objects.create(
    161                 host=self.hosts[0], task=models.SpecialTask.Task.VERIFY,
    162                 requested_by=models.User.current_user())
    163 
    164 
    165     def test_execution_path(self):
    166         task = self._create_task()
    167         self.assertEquals(task.execution_path(), 'hosts/host1/1-verify')
    168 
    169 
    170     def test_status(self):
    171         task = self._create_task()
    172         self.assertEquals(task.status, 'Queued')
    173 
    174         task.update_object(is_active=True)
    175         self.assertEquals(task.status, 'Running')
    176 
    177         task.update_object(is_active=False, is_complete=True, success=True)
    178         self.assertEquals(task.status, 'Completed')
    179 
    180         task.update_object(success=False)
    181         self.assertEquals(task.status, 'Failed')
    182 
    183 
    184     def test_activate(self):
    185         task = self._create_task()
    186         task.activate()
    187         self.assertTrue(task.is_active)
    188         self.assertFalse(task.is_complete)
    189 
    190 
    191     def test_finish(self):
    192         task = self._create_task()
    193         task.activate()
    194         task.finish(True)
    195         self.assertFalse(task.is_active)
    196         self.assertTrue(task.is_complete)
    197         self.assertTrue(task.success)
    198 
    199 
    200     def test_requested_by_from_queue_entry(self):
    201         job = self._create_job(hosts=[0])
    202         task = models.SpecialTask.objects.create(
    203                 host=self.hosts[0], task=models.SpecialTask.Task.VERIFY,
    204                 queue_entry=job.hostqueueentry_set.all()[0])
    205         self.assertEquals(task.requested_by.login, 'autotest_system')
    206 
    207 
    208 class HostQueueEntryUnittest(unittest.TestCase,
    209                              frontend_test_utils.FrontendTestMixin):
    210     def setUp(self):
    211         self._frontend_common_setup()
    212 
    213 
    214     def tearDown(self):
    215         self._frontend_common_teardown()
    216 
    217 
    218     def test_execution_path(self):
    219         entry = self._create_job(hosts=[1]).hostqueueentry_set.all()[0]
    220         entry.execution_subdir = 'subdir'
    221         entry.save()
    222 
    223         self.assertEquals(entry.execution_path(), '1-autotest_system/subdir')
    224 
    225 
    226 class ModelWithInvalidTest(unittest.TestCase,
    227                            frontend_test_utils.FrontendTestMixin):
    228     def setUp(self):
    229         self._frontend_common_setup()
    230 
    231 
    232     def tearDown(self):
    233         self._frontend_common_teardown()
    234 
    235 
    236     def test_model_with_invalid_delete(self):
    237         self.assertFalse(self.hosts[0].invalid)
    238         self.hosts[0].delete()
    239         self.assertTrue(self.hosts[0].invalid)
    240         self.assertTrue(models.Host.objects.get(id=self.hosts[0].id))
    241 
    242 
    243     def test_model_with_invalid_delete_queryset(self):
    244         for host in self.hosts:
    245             self.assertFalse(host.invalid)
    246 
    247         hosts = models.Host.objects.all()
    248         hosts.delete()
    249         self.assertEqual(hosts.count(), len(self.hosts))
    250 
    251         for host in hosts:
    252             self.assertTrue(host.invalid)
    253 
    254 
    255     def test_cloned_queryset_delete(self):
    256         """
    257         Make sure that a cloned queryset maintains the custom delete()
    258         """
    259         to_delete = ('host1', 'host2')
    260 
    261         for host in self.hosts:
    262             self.assertFalse(host.invalid)
    263 
    264         hosts = models.Host.objects.all().filter(hostname__in=to_delete)
    265         hosts.delete()
    266         all_hosts = models.Host.objects.all()
    267         self.assertEqual(all_hosts.count(), len(self.hosts))
    268 
    269         for host in all_hosts:
    270             if host.hostname in to_delete:
    271                 self.assertTrue(
    272                         host.invalid,
    273                         '%s.invalid expected to be True' % host.hostname)
    274             else:
    275                 self.assertFalse(
    276                         host.invalid,
    277                         '%s.invalid expected to be False' % host.hostname)
    278 
    279 
    280     def test_normal_delete(self):
    281         job = self._create_job(hosts=[1])
    282         self.assertEqual(1, models.Job.objects.all().count())
    283 
    284         job.delete()
    285         self.assertEqual(0, models.Job.objects.all().count())
    286 
    287 
    288     def test_normal_delete_queryset(self):
    289         self._create_job(hosts=[1])
    290         self._create_job(hosts=[2])
    291 
    292         self.assertEqual(2, models.Job.objects.all().count())
    293 
    294         models.Job.objects.all().delete()
    295         self.assertEqual(0, models.Job.objects.all().count())
    296 
    297 
    298 class SerializationTest(unittest.TestCase,
    299                         frontend_test_utils.FrontendTestMixin):
    300     def setUp(self):
    301         self._frontend_common_setup(fill_data=False)
    302 
    303 
    304     def tearDown(self):
    305         self._frontend_common_teardown()
    306 
    307 
    308     def _get_example_response(self):
    309         return {'hosts': [{'aclgroup_set': [{'description': '',
    310                                              'id': 1,
    311                                              'name': 'Everyone',
    312                                              'users': [{
    313                                                  'access_level': 100,
    314                                                  'id': 1,
    315                                                  'login': 'autotest_system',
    316                                                  'reboot_after': 0,
    317                                                  'reboot_before': 1,
    318                                                  'show_experimental': False}]}],
    319                            'dirty': True,
    320                            'hostattribute_set': [],
    321                            'hostname': '100.107.2.163',
    322                            'id': 2,
    323                            'invalid': False,
    324                            'labels': [{'id': 7,
    325                                        'invalid': False,
    326                                        'kernel_config': '',
    327                                        'name': 'power:battery',
    328                                        'only_if_needed': False,
    329                                        'platform': False},
    330                                       {'id': 9,
    331                                        'invalid': False,
    332                                        'kernel_config': '',
    333                                        'name': 'hw_video_acc_h264',
    334                                        'only_if_needed': False,
    335                                        'platform': False},
    336                                       {'id': 10,
    337                                        'invalid': False,
    338                                        'kernel_config': '',
    339                                        'name': 'hw_video_acc_enc_h264',
    340                                        'only_if_needed': False,
    341                                        'platform': False},
    342                                       {'id': 11,
    343                                        'invalid': False,
    344                                        'kernel_config': '',
    345                                        'name': 'webcam',
    346                                        'only_if_needed': False,
    347                                        'platform': False},
    348                                       {'id': 12,
    349                                        'invalid': False,
    350                                        'kernel_config': '',
    351                                        'name': 'touchpad',
    352                                        'only_if_needed': False,
    353                                        'platform': False},
    354                                       {'id': 13,
    355                                        'invalid': False,
    356                                        'kernel_config': '',
    357                                        'name': 'spring',
    358                                        'only_if_needed': False,
    359                                        'platform': False},
    360                                       {'id': 14,
    361                                        'invalid': False,
    362                                        'kernel_config': '',
    363                                        'name': 'board:daisy',
    364                                        'only_if_needed': False,
    365                                        'platform': True},
    366                                       {'id': 15,
    367                                        'invalid': False,
    368                                        'kernel_config': '',
    369                                        'name': 'board_freq_mem:daisy_1.7GHz',
    370                                        'only_if_needed': False,
    371                                        'platform': False},
    372                                       {'id': 16,
    373                                        'invalid': False,
    374                                        'kernel_config': '',
    375                                        'name': 'bluetooth',
    376                                        'only_if_needed': False,
    377                                        'platform': False},
    378                                       {'id': 17,
    379                                        'invalid': False,
    380                                        'kernel_config': '',
    381                                        'name': 'gpu_family:mali',
    382                                        'only_if_needed': False,
    383                                        'platform': False},
    384                                       {'id': 19,
    385                                        'invalid': False,
    386                                        'kernel_config': '',
    387                                        'name': 'ec:cros',
    388                                        'only_if_needed': False,
    389                                        'platform': False},
    390                                       {'id': 20,
    391                                        'invalid': False,
    392                                        'kernel_config': '',
    393                                        'name': 'storage:mmc',
    394                                        'only_if_needed': False,
    395                                        'platform': False},
    396                                       {'id': 21,
    397                                        'invalid': False,
    398                                        'kernel_config': '',
    399                                        'name': 'hw_video_acc_vp8',
    400                                        'only_if_needed': False,
    401                                        'platform': False},
    402                                       {'id': 22,
    403                                        'invalid': False,
    404                                        'kernel_config': '',
    405                                        'name': 'video_glitch_detection',
    406                                        'only_if_needed': False,
    407                                        'platform': False},
    408                                       {'id': 23,
    409                                        'invalid': False,
    410                                        'kernel_config': '',
    411                                        'name': 'pool:suites',
    412                                        'only_if_needed': False,
    413                                        'platform': False},
    414                                       {'id': 25,
    415                                        'invalid': False,
    416                                        'kernel_config': '',
    417                                        'name': 'daisy-board-name',
    418                                        'only_if_needed': False,
    419                                        'platform': False}],
    420                            'leased': False,
    421                            'lock_reason': '',
    422                            'lock_time': None,
    423                            'locked': False,
    424                            'protection': 0,
    425                            'shard': {'hostname': '1', 'id': 1},
    426                            'status': 'Ready',
    427                            'synch_id': None}],
    428                 'jobs': [{'control_file': 'some control file\n\n\n',
    429                           'control_type': 2,
    430                           'created_on': '2014-09-04T13:09:35',
    431                           'dependency_labels': [{'id': 14,
    432                                                  'invalid': False,
    433                                                  'kernel_config': '',
    434                                                  'name': 'board:daisy',
    435                                                  'only_if_needed': False,
    436                                                  'platform': True},
    437                                                 {'id': 23,
    438                                                  'invalid': False,
    439                                                  'kernel_config': '',
    440                                                  'name': 'pool:suites',
    441                                                  'only_if_needed': False,
    442                                                  'platform': False},
    443                                                 {'id': 25,
    444                                                  'invalid': False,
    445                                                  'kernel_config': '',
    446                                                  'name': 'daisy-board-name',
    447                                                  'only_if_needed': False,
    448                                                  'platform': False}],
    449                           'email_list': '',
    450                           'hostqueueentry_set': [{'aborted': False,
    451                                                   'active': False,
    452                                                   'complete': False,
    453                                                   'deleted': False,
    454                                                   'execution_subdir': '',
    455                                                   'finished_on': None,
    456                                                   'id': 5,
    457                                                   'meta_host': {
    458                                                       'id': 14,
    459                                                       'invalid': False,
    460                                                       'kernel_config': '',
    461                                                       'name': 'board:daisy',
    462                                                       'only_if_needed': False,
    463                                                       'platform': True},
    464                                                   'host_id': None,
    465                                                   'started_on': None,
    466                                                   'status': 'Queued'}],
    467                           'id': 5,
    468                           'jobkeyval_set': [{'id': 10,
    469                                              'job_id': 5,
    470                                              'key': 'suite',
    471                                              'value': 'dummy'},
    472                                             {'id': 11,
    473                                              'job_id': 5,
    474                                              'key': 'build',
    475                                              'value': 'daisy-release'},
    476                                             {'id': 12,
    477                                              'job_id': 5,
    478                                              'key': 'experimental',
    479                                              'value': 'False'}],
    480                           'max_runtime_hrs': 72,
    481                           'max_runtime_mins': 1440,
    482                           'name': 'daisy-experimental',
    483                           'owner': 'autotest',
    484                           'parse_failed_repair': True,
    485                           'priority': 40,
    486                           'reboot_after': 0,
    487                           'reboot_before': 1,
    488                           'run_reset': True,
    489                           'run_verify': False,
    490                           'shard': {'hostname': '1', 'id': 1},
    491                           'synch_count': 1,
    492                           'test_retry': 0,
    493                           'timeout': 24,
    494                           'timeout_mins': 1440,
    495                           'require_ssp': None},
    496                          {'control_file': 'some control file\n\n\n',
    497                           'control_type': 2,
    498                           'created_on': '2014-09-04T13:09:35',
    499                           'dependency_labels': [{'id': 14,
    500                                                  'invalid': False,
    501                                                  'kernel_config': '',
    502                                                  'name': 'board:daisy',
    503                                                  'only_if_needed': False,
    504                                                  'platform': True},
    505                                                 {'id': 23,
    506                                                  'invalid': False,
    507                                                  'kernel_config': '',
    508                                                  'name': 'pool:suites',
    509                                                  'only_if_needed': False,
    510                                                  'platform': False},
    511                                                 {'id': 25,
    512                                                  'invalid': False,
    513                                                  'kernel_config': '',
    514                                                  'name': 'daisy-board-name',
    515                                                  'only_if_needed': False,
    516                                                  'platform': False}],
    517                           'email_list': '',
    518                           'hostqueueentry_set': [{'aborted': False,
    519                                                   'active': False,
    520                                                   'complete': False,
    521                                                   'deleted': False,
    522                                                   'execution_subdir': '',
    523                                                   'finished_on': None,
    524                                                   'id': 7,
    525                                                   'meta_host': {
    526                                                       'id': 14,
    527                                                       'invalid': False,
    528                                                       'kernel_config': '',
    529                                                       'name': 'board:daisy',
    530                                                       'only_if_needed': False,
    531                                                       'platform': True},
    532                                                   'host_id': None,
    533                                                   'started_on': None,
    534                                                   'status': 'Queued'}],
    535                           'id': 7,
    536                           'jobkeyval_set': [{'id': 16,
    537                                              'job_id': 7,
    538                                              'key': 'suite',
    539                                              'value': 'dummy'},
    540                                             {'id': 17,
    541                                              'job_id': 7,
    542                                              'key': 'build',
    543                                              'value': 'daisy-release'},
    544                                             {'id': 18,
    545                                              'job_id': 7,
    546                                              'key': 'experimental',
    547                                              'value': 'False'}],
    548                           'max_runtime_hrs': 72,
    549                           'max_runtime_mins': 1440,
    550                           'name': 'daisy-experimental',
    551                           'owner': 'autotest',
    552                           'parse_failed_repair': True,
    553                           'priority': 40,
    554                           'reboot_after': 0,
    555                           'reboot_before': 1,
    556                           'run_reset': True,
    557                           'run_verify': False,
    558                           'shard': {'hostname': '1', 'id': 1},
    559                           'synch_count': 1,
    560                           'test_retry': 0,
    561                           'timeout': 24,
    562                           'timeout_mins': 1440,
    563                           'require_ssp': None}]}
    564 
    565 
    566     def test_response(self):
    567         heartbeat_response = self._get_example_response()
    568         hosts_serialized = heartbeat_response['hosts']
    569         jobs_serialized = heartbeat_response['jobs']
    570 
    571         # Persisting is automatically done inside deserialize
    572         hosts = [models.Host.deserialize(host) for host in hosts_serialized]
    573         jobs = [models.Job.deserialize(job) for job in jobs_serialized]
    574 
    575         generated_heartbeat_response = {
    576             'hosts': [host.serialize() for host in hosts],
    577             'jobs': [job.serialize() for job in jobs]
    578         }
    579         example_response = self._get_example_response()
    580         # For attribute-like objects, we don't care about its id.
    581         for r in [generated_heartbeat_response, example_response]:
    582             for job in r['jobs']:
    583                 for keyval in job['jobkeyval_set']:
    584                     keyval.pop('id')
    585             for host in r['hosts']:
    586                 for attribute in host['hostattribute_set']:
    587                     keyval.pop('id')
    588         self.assertEqual(generated_heartbeat_response, example_response)
    589 
    590 
    591     def test_update(self):
    592         job = self._create_job(hosts=[1])
    593         serialized = job.serialize(include_dependencies=False)
    594         serialized['owner'] = 'some_other_owner'
    595 
    596         job.update_from_serialized(serialized)
    597         self.assertEqual(job.owner, 'some_other_owner')
    598 
    599         serialized = job.serialize()
    600         self.assertRaises(
    601             ValueError,
    602             job.update_from_serialized, serialized)
    603 
    604 
    605     def test_sync_aborted(self):
    606         job = self._create_job(hosts=[1])
    607         serialized = job.serialize()
    608 
    609         serialized['hostqueueentry_set'][0]['aborted'] = True
    610         serialized['hostqueueentry_set'][0]['status'] = 'Running'
    611 
    612         models.Job.deserialize(serialized)
    613 
    614         job = models.Job.objects.get(pk=job.id)
    615         self.assertTrue(job.hostqueueentry_set.all()[0].aborted)
    616         self.assertEqual(job.hostqueueentry_set.all()[0].status, 'Queued')
    617 
    618 
    619 if __name__ == '__main__':
    620     unittest.main()
    621