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', 'backup', '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': 'backup',
     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 = ['afe', 'scheduler', 'host_scheduler', 'drone', 'devserver',
     99                  'database', 'database_slave', 'suite_scheduler',
    100                  'crash_server', 'shard', 'golo_proxy', 'reserve']
    101     ROLE = enum.Enum(*ROLE_LIST, string_values=True)
    102     # When deleting any of following roles from a primary server, a working
    103     # backup must be available if user_server_db is enabled in global config.
    104     ROLES_REQUIRE_BACKUP = [ROLE.SCHEDULER, ROLE.HOST_SCHEDULER,
    105                             ROLE.DATABASE, ROLE.SUITE_SCHEDULER,
    106                             ROLE.DRONE]
    107     # Roles that must be assigned to a single primary server in an Autotest
    108     # instance
    109     ROLES_REQUIRE_UNIQUE_INSTANCE = [ROLE.SCHEDULER,
    110                                      ROLE.HOST_SCHEDULER,
    111                                      ROLE.DATABASE,
    112                                      ROLE.SUITE_SCHEDULER]
    113 
    114     server = dbmodels.ForeignKey(Server, related_name='roles')
    115     role = dbmodels.CharField(max_length=128, choices=ROLE.choices())
    116 
    117     objects = model_logic.ExtendedManager()
    118 
    119     class Meta:
    120         """Metadata for the ServerRole class."""
    121         db_table = 'server_roles'
    122 
    123 
    124 class ServerAttribute(dbmodels.Model, model_logic.ModelExtensions):
    125     """Attribute associated with hosts."""
    126     server = dbmodels.ForeignKey(Server, related_name='attributes')
    127     attribute = dbmodels.CharField(max_length=128)
    128     value = dbmodels.TextField(null=True, blank=True)
    129     date_modified = dbmodels.DateTimeField(null=True, blank=True)
    130 
    131     objects = model_logic.ExtendedManager()
    132 
    133     class Meta:
    134         """Metadata for the ServerAttribute class."""
    135         db_table = 'server_attributes'
    136 
    137 
    138 # Valid values for each type of input.
    139 RANGE_LIMITS={'role': ServerRole.ROLE_LIST,
    140               'status': Server.STATUS_LIST}
    141 
    142 def validate(**kwargs):
    143     """Verify command line arguments, raise InvalidDataError if any is invalid.
    144 
    145     The function verify following inputs for the database query.
    146     1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid
    147        role or status.
    148     2. hostname. The code will try to resolve given hostname. If the hostname
    149        does not exist in the network, InvalidDataError will be raised.
    150     Sample usage of this function:
    151     validate(role='drone', status='backup', hostname='server1')
    152 
    153     @param kwargs: command line arguments, e.g., `status='primary'`
    154     @raise InvalidDataError: If any argument value is invalid.
    155     """
    156     for key, value in kwargs.items():
    157         # Ignore any None value, so callers won't need to filter out None
    158         # value as it won't be used in queries.
    159         if not value:
    160             continue
    161         if value not in RANGE_LIMITS.get(key, [value]):
    162             raise error.InvalidDataError(
    163                     '%s %s is not valid, it must be one of %s.' %
    164                     (key, value,
    165                      ', '.join(RANGE_LIMITS[key])))
    166         elif key == 'hostname':
    167             if not ping_runner.PingRunner().simple_ping(value):
    168                 raise error.InvalidDataError('Can not reach server with '
    169                                              'hostname "%s".' % value)
    170