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 8from django.db import models as dbmodels 9 10import common 11from autotest_lib.client.common_lib import autotest_enum 12from autotest_lib.client.common_lib import error 13from autotest_lib.client.common_lib.cros.network import ping_runner 14from autotest_lib.frontend.afe import model_logic 15 16 17class 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 = autotest_enum.AutotestEnum(*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 95class ServerRole(dbmodels.Model, model_logic.ModelExtensions): 96 """Role associated with hosts.""" 97 # Valid roles for a server. 98 # TODO b:169251326 terms below are set outside of this codebase 99 # and should be updated when possible. 100 ROLE_LIST = [ 101 'afe', 102 'crash_server', 103 'database', 104 'database_slave', 105 'devserver', 106 'drone', 107 'golo_proxy', 108 'host_scheduler', 109 'scheduler', 110 'sentinel', 111 'shard', 112 'skylab_drone', 113 114 'reserve', 115 ] 116 ROLE = autotest_enum.AutotestEnum(*ROLE_LIST, string_values=True) 117 # Roles that must be assigned to a single primary server in an Autotest 118 # instance 119 ROLES_REQUIRE_UNIQUE_INSTANCE = [ROLE.SCHEDULER, 120 ROLE.HOST_SCHEDULER, 121 ROLE.DATABASE] 122 123 server = dbmodels.ForeignKey(Server, related_name='roles') 124 role = dbmodels.CharField(max_length=128, choices=ROLE.choices()) 125 126 objects = model_logic.ExtendedManager() 127 128 class Meta: 129 """Metadata for the ServerRole class.""" 130 db_table = 'server_roles' 131 132 133class ServerAttribute(dbmodels.Model, model_logic.ModelExtensions): 134 """Attribute associated with hosts.""" 135 server = dbmodels.ForeignKey(Server, related_name='attributes') 136 attribute = dbmodels.CharField(max_length=128) 137 value = dbmodels.TextField(null=True, blank=True) 138 date_modified = dbmodels.DateTimeField(null=True, blank=True) 139 140 objects = model_logic.ExtendedManager() 141 142 class Meta: 143 """Metadata for the ServerAttribute class.""" 144 db_table = 'server_attributes' 145 146 147# Valid values for each type of input. 148RANGE_LIMITS={'role': ServerRole.ROLE_LIST, 149 'status': Server.STATUS_LIST} 150 151def validate(**kwargs): 152 """Verify command line arguments, raise InvalidDataError if any is invalid. 153 154 The function verify following inputs for the database query. 155 1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid 156 role or status. 157 2. hostname. The code will try to resolve given hostname. If the hostname 158 does not exist in the network, InvalidDataError will be raised. 159 Sample usage of this function: 160 validate(role='drone', status='repair_required', hostname='server1') 161 162 @param kwargs: command line arguments, e.g., `status='primary'` 163 @raise InvalidDataError: If any argument value is invalid. 164 """ 165 for key, value in kwargs.items(): 166 # Ignore any None value, so callers won't need to filter out None 167 # value as it won't be used in queries. 168 if not value: 169 continue 170 if value not in RANGE_LIMITS.get(key, [value]): 171 raise error.InvalidDataError( 172 '%s %s is not valid, it must be one of %s.' % 173 (key, value, 174 ', '.join(RANGE_LIMITS[key]))) 175 elif key == 'hostname': 176 if not ping_runner.PingRunner().simple_ping(value): 177 raise error.InvalidDataError('Can not reach server with ' 178 'hostname "%s".' % value) 179