| # Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Django model for server database. |
| """ |
| |
| from django.db import models as dbmodels |
| |
| import common |
| from autotest_lib.client.common_lib import enum |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib.cros.network import ping_runner |
| from autotest_lib.frontend.afe import model_logic |
| |
| |
| class Server(dbmodels.Model, model_logic.ModelExtensions): |
| """Models a server.""" |
| DETAIL_FMT = ('Hostname : %(hostname)s\n' |
| 'Status : %(status)s\n' |
| 'Roles : %(roles)s\n' |
| 'Attributes : %(attributes)s\n' |
| 'Date Created : %(date_created)s\n' |
| 'Date Modified: %(date_modified)s\n' |
| 'Note : %(note)s\n') |
| |
| STATUS_LIST = ['primary', 'repair_required'] |
| STATUS = enum.Enum(*STATUS_LIST, string_values=True) |
| |
| hostname = dbmodels.CharField(unique=True, max_length=128) |
| cname = dbmodels.CharField(null=True, blank=True, default=None, |
| max_length=128) |
| status = dbmodels.CharField(unique=False, max_length=128, |
| choices=STATUS.choices()) |
| date_created = dbmodels.DateTimeField(null=True, blank=True) |
| date_modified = dbmodels.DateTimeField(null=True, blank=True) |
| note = dbmodels.TextField(null=True, blank=True) |
| |
| objects = model_logic.ExtendedManager() |
| |
| class Meta: |
| """Metadata for class Server.""" |
| db_table = 'servers' |
| |
| |
| def __unicode__(self): |
| """A string representation of the Server object. |
| """ |
| roles = ','.join([r.role for r in self.roles.all()]) |
| attributes = dict([(a.attribute, a.value) |
| for a in self.attributes.all()]) |
| return self.DETAIL_FMT % {'hostname': self.hostname, |
| 'status': self.status, |
| 'roles': roles, |
| 'attributes': attributes, |
| 'date_created': self.date_created, |
| 'date_modified': self.date_modified, |
| 'note': self.note} |
| |
| |
| def get_role_names(self): |
| """Get a list of role names of the server. |
| |
| @return: A list of role names of the server. |
| """ |
| return [r.role for r in self.roles.all()] |
| |
| |
| def get_details(self): |
| """Get a dictionary with all server details. |
| |
| For example: |
| { |
| 'hostname': 'server1', |
| 'status': 'primary', |
| 'roles': ['drone', 'scheduler'], |
| 'attributes': {'max_processes': 300} |
| } |
| |
| @return: A dictionary with all server details. |
| """ |
| details = {} |
| details['hostname'] = self.hostname |
| details['status'] = self.status |
| details['roles'] = self.get_role_names() |
| attributes = dict([(a.attribute, a.value) |
| for a in self.attributes.all()]) |
| details['attributes'] = attributes |
| details['date_created'] = self.date_created |
| details['date_modified'] = self.date_modified |
| details['note'] = self.note |
| return details |
| |
| |
| class ServerRole(dbmodels.Model, model_logic.ModelExtensions): |
| """Role associated with hosts.""" |
| # Valid roles for a server. |
| ROLE_LIST = [ |
| 'afe', |
| 'crash_server', |
| 'database', |
| 'database_slave', |
| 'devserver', |
| 'drone', |
| 'golo_proxy', |
| 'host_scheduler', |
| 'scheduler', |
| 'sentinel', |
| 'shard', |
| 'skylab_drone', |
| |
| 'reserve', |
| ] |
| ROLE = enum.Enum(*ROLE_LIST, string_values=True) |
| # Roles that must be assigned to a single primary server in an Autotest |
| # instance |
| ROLES_REQUIRE_UNIQUE_INSTANCE = [ROLE.SCHEDULER, |
| ROLE.HOST_SCHEDULER, |
| ROLE.DATABASE] |
| |
| server = dbmodels.ForeignKey(Server, related_name='roles') |
| role = dbmodels.CharField(max_length=128, choices=ROLE.choices()) |
| |
| objects = model_logic.ExtendedManager() |
| |
| class Meta: |
| """Metadata for the ServerRole class.""" |
| db_table = 'server_roles' |
| |
| |
| class ServerAttribute(dbmodels.Model, model_logic.ModelExtensions): |
| """Attribute associated with hosts.""" |
| server = dbmodels.ForeignKey(Server, related_name='attributes') |
| attribute = dbmodels.CharField(max_length=128) |
| value = dbmodels.TextField(null=True, blank=True) |
| date_modified = dbmodels.DateTimeField(null=True, blank=True) |
| |
| objects = model_logic.ExtendedManager() |
| |
| class Meta: |
| """Metadata for the ServerAttribute class.""" |
| db_table = 'server_attributes' |
| |
| |
| # Valid values for each type of input. |
| RANGE_LIMITS={'role': ServerRole.ROLE_LIST, |
| 'status': Server.STATUS_LIST} |
| |
| def validate(**kwargs): |
| """Verify command line arguments, raise InvalidDataError if any is invalid. |
| |
| The function verify following inputs for the database query. |
| 1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid |
| role or status. |
| 2. hostname. The code will try to resolve given hostname. If the hostname |
| does not exist in the network, InvalidDataError will be raised. |
| Sample usage of this function: |
| validate(role='drone', status='repair_required', hostname='server1') |
| |
| @param kwargs: command line arguments, e.g., `status='primary'` |
| @raise InvalidDataError: If any argument value is invalid. |
| """ |
| for key, value in kwargs.items(): |
| # Ignore any None value, so callers won't need to filter out None |
| # value as it won't be used in queries. |
| if not value: |
| continue |
| if value not in RANGE_LIMITS.get(key, [value]): |
| raise error.InvalidDataError( |
| '%s %s is not valid, it must be one of %s.' % |
| (key, value, |
| ', '.join(RANGE_LIMITS[key]))) |
| elif key == 'hostname': |
| if not ping_runner.PingRunner().simple_ping(value): |
| raise error.InvalidDataError('Can not reach server with ' |
| 'hostname "%s".' % value) |