Home | History | Annotate | Download | only in server
      1 # Copyright (c) 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 """Django model for server database.
      6 """
      7 
      8 from django.db import models as dbmodels
      9 
     10 import common
     11 from autotest_lib.client.common_lib import enum
     12 from autotest_lib.client.common_lib import error
     13 from autotest_lib.client.common_lib.cros.network import ping_runner
     14 from autotest_lib.frontend.afe import model_logic
     15 
     16 
     17 class Server(dbmodels.Model, model_logic.ModelExtensions):
     18     """Models a server."""
     19     DETAIL_FMT = ('Hostname     : %(hostname)s\n'
     20                   'Status       : %(status)s\n'
     21                   'Roles        : %(roles)s\n'
     22                   'Attributes   : %(attributes)s\n'
     23                   'Date Created : %(date_created)s\n'
     24                   'Date Modified: %(date_modified)s\n'
     25                   'Note         : %(note)s\n')
     26 
     27     STATUS_LIST = ['primary', 'repair_required']
     28     STATUS = enum.Enum(*STATUS_LIST, string_values=True)
     29 
     30     hostname = dbmodels.CharField(unique=True, max_length=128)
     31     cname = dbmodels.CharField(null=True, blank=True, default=None,
     32                                max_length=128)
     33     status = dbmodels.CharField(unique=False, max_length=128,
     34                                 choices=STATUS.choices())
     35     date_created = dbmodels.DateTimeField(null=True, blank=True)
     36     date_modified = dbmodels.DateTimeField(null=True, blank=True)
     37     note = dbmodels.TextField(null=True, blank=True)
     38 
     39     objects = model_logic.ExtendedManager()
     40 
     41     class Meta:
     42         """Metadata for class Server."""
     43         db_table = 'servers'
     44 
     45 
     46     def __unicode__(self):
     47         """A string representation of the Server object.
     48         """
     49         roles = ','.join([r.role for r in self.roles.all()])
     50         attributes = dict([(a.attribute, a.value)
     51                            for a in self.attributes.all()])
     52         return self.DETAIL_FMT % {'hostname': self.hostname,
     53                                   'status': self.status,
     54                                   'roles': roles,
     55                                   'attributes': attributes,
     56                                   'date_created': self.date_created,
     57                                   'date_modified': self.date_modified,
     58                                   'note': self.note}
     59 
     60 
     61     def get_role_names(self):
     62         """Get a list of role names of the server.
     63 
     64         @return: A list of role names of the server.
     65         """
     66         return [r.role for r in self.roles.all()]
     67 
     68 
     69     def get_details(self):
     70         """Get a dictionary with all server details.
     71 
     72         For example:
     73         {
     74             'hostname': 'server1',
     75             'status': 'primary',
     76             'roles': ['drone', 'scheduler'],
     77             'attributes': {'max_processes': 300}
     78         }
     79 
     80         @return: A dictionary with all server details.
     81         """
     82         details = {}
     83         details['hostname'] = self.hostname
     84         details['status'] = self.status
     85         details['roles'] = self.get_role_names()
     86         attributes = dict([(a.attribute, a.value)
     87                            for a in self.attributes.all()])
     88         details['attributes'] = attributes
     89         details['date_created'] = self.date_created
     90         details['date_modified'] = self.date_modified
     91         details['note'] = self.note
     92         return details
     93 
     94 
     95 class ServerRole(dbmodels.Model, model_logic.ModelExtensions):
     96     """Role associated with hosts."""
     97     # Valid roles for a server.
     98     ROLE_LIST = [
     99             'afe',
    100             'crash_server',
    101             'database',
    102             'database_slave',
    103             'devserver',
    104             'drone',
    105             'golo_proxy',
    106             'host_scheduler',
    107             'scheduler',
    108             'sentinel',
    109             'shard',
    110             'skylab_drone',
    111 
    112             'reserve',
    113     ]
    114     ROLE = enum.Enum(*ROLE_LIST, string_values=True)
    115     # Roles that must be assigned to a single primary server in an Autotest
    116     # instance
    117     ROLES_REQUIRE_UNIQUE_INSTANCE = [ROLE.SCHEDULER,
    118                                      ROLE.HOST_SCHEDULER,
    119                                      ROLE.DATABASE]
    120 
    121     server = dbmodels.ForeignKey(Server, related_name='roles')
    122     role = dbmodels.CharField(max_length=128, choices=ROLE.choices())
    123 
    124     objects = model_logic.ExtendedManager()
    125 
    126     class Meta:
    127         """Metadata for the ServerRole class."""
    128         db_table = 'server_roles'
    129 
    130 
    131 class ServerAttribute(dbmodels.Model, model_logic.ModelExtensions):
    132     """Attribute associated with hosts."""
    133     server = dbmodels.ForeignKey(Server, related_name='attributes')
    134     attribute = dbmodels.CharField(max_length=128)
    135     value = dbmodels.TextField(null=True, blank=True)
    136     date_modified = dbmodels.DateTimeField(null=True, blank=True)
    137 
    138     objects = model_logic.ExtendedManager()
    139 
    140     class Meta:
    141         """Metadata for the ServerAttribute class."""
    142         db_table = 'server_attributes'
    143 
    144 
    145 # Valid values for each type of input.
    146 RANGE_LIMITS={'role': ServerRole.ROLE_LIST,
    147               'status': Server.STATUS_LIST}
    148 
    149 def validate(**kwargs):
    150     """Verify command line arguments, raise InvalidDataError if any is invalid.
    151 
    152     The function verify following inputs for the database query.
    153     1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid
    154        role or status.
    155     2. hostname. The code will try to resolve given hostname. If the hostname
    156        does not exist in the network, InvalidDataError will be raised.
    157     Sample usage of this function:
    158     validate(role='drone', status='repair_required', hostname='server1')
    159 
    160     @param kwargs: command line arguments, e.g., `status='primary'`
    161     @raise InvalidDataError: If any argument value is invalid.
    162     """
    163     for key, value in kwargs.items():
    164         # Ignore any None value, so callers won't need to filter out None
    165         # value as it won't be used in queries.
    166         if not value:
    167             continue
    168         if value not in RANGE_LIMITS.get(key, [value]):
    169             raise error.InvalidDataError(
    170                     '%s %s is not valid, it must be one of %s.' %
    171                     (key, value,
    172                      ', '.join(RANGE_LIMITS[key])))
    173         elif key == 'hostname':
    174             if not ping_runner.PingRunner().simple_ping(value):
    175                 raise error.InvalidDataError('Can not reach server with '
    176                                              'hostname "%s".' % value)
    177