19a535c9f3144690cf85d88a51e07b98c21454159Dan Shi# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 29a535c9f3144690cf85d88a51e07b98c21454159Dan Shi# Use of this source code is governed by a BSD-style license that can be 39a535c9f3144690cf85d88a51e07b98c21454159Dan Shi# found in the LICENSE file. 49a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 59a535c9f3144690cf85d88a51e07b98c21454159Dan Shi"""Django model for server database. 69a535c9f3144690cf85d88a51e07b98c21454159Dan Shi""" 79a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 89a535c9f3144690cf85d88a51e07b98c21454159Dan Shifrom django.db import models as dbmodels 99a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 109a535c9f3144690cf85d88a51e07b98c21454159Dan Shiimport common 119a535c9f3144690cf85d88a51e07b98c21454159Dan Shifrom autotest_lib.client.common_lib import enum 129a535c9f3144690cf85d88a51e07b98c21454159Dan Shifrom autotest_lib.client.common_lib import error 130697ccd2453b9eafc087e1e6a7bdb289b81b45edDan Shifrom autotest_lib.client.common_lib.cros.network import ping_runner 149a535c9f3144690cf85d88a51e07b98c21454159Dan Shifrom autotest_lib.frontend.afe import model_logic 159a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 169a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 179a535c9f3144690cf85d88a51e07b98c21454159Dan Shiclass Server(dbmodels.Model, model_logic.ModelExtensions): 189a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Models a server.""" 199a535c9f3144690cf85d88a51e07b98c21454159Dan Shi DETAIL_FMT = ('Hostname : %(hostname)s\n' 209a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'Status : %(status)s\n' 219a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'Roles : %(roles)s\n' 229a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'Attributes : %(attributes)s\n' 239a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'Date Created : %(date_created)s\n' 249a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'Date Modified: %(date_modified)s\n' 259a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'Note : %(note)s\n') 269a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 2791f58e141f3773acf16ae4af3173366b299f76bbXixuan Wu STATUS_LIST = ['primary', 'repair_required'] 289a535c9f3144690cf85d88a51e07b98c21454159Dan Shi STATUS = enum.Enum(*STATUS_LIST, string_values=True) 299a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 309a535c9f3144690cf85d88a51e07b98c21454159Dan Shi hostname = dbmodels.CharField(unique=True, max_length=128) 319a535c9f3144690cf85d88a51e07b98c21454159Dan Shi cname = dbmodels.CharField(null=True, blank=True, default=None, 329a535c9f3144690cf85d88a51e07b98c21454159Dan Shi max_length=128) 339a535c9f3144690cf85d88a51e07b98c21454159Dan Shi status = dbmodels.CharField(unique=False, max_length=128, 349a535c9f3144690cf85d88a51e07b98c21454159Dan Shi choices=STATUS.choices()) 359a535c9f3144690cf85d88a51e07b98c21454159Dan Shi date_created = dbmodels.DateTimeField(null=True, blank=True) 369a535c9f3144690cf85d88a51e07b98c21454159Dan Shi date_modified = dbmodels.DateTimeField(null=True, blank=True) 379a535c9f3144690cf85d88a51e07b98c21454159Dan Shi note = dbmodels.TextField(null=True, blank=True) 389a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 399a535c9f3144690cf85d88a51e07b98c21454159Dan Shi objects = model_logic.ExtendedManager() 409a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 419a535c9f3144690cf85d88a51e07b98c21454159Dan Shi class Meta: 429a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Metadata for class Server.""" 439a535c9f3144690cf85d88a51e07b98c21454159Dan Shi db_table = 'servers' 449a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 459a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 469a535c9f3144690cf85d88a51e07b98c21454159Dan Shi def __unicode__(self): 479a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """A string representation of the Server object. 489a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """ 499a535c9f3144690cf85d88a51e07b98c21454159Dan Shi roles = ','.join([r.role for r in self.roles.all()]) 509a535c9f3144690cf85d88a51e07b98c21454159Dan Shi attributes = dict([(a.attribute, a.value) 519a535c9f3144690cf85d88a51e07b98c21454159Dan Shi for a in self.attributes.all()]) 529a535c9f3144690cf85d88a51e07b98c21454159Dan Shi return self.DETAIL_FMT % {'hostname': self.hostname, 539a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'status': self.status, 549a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'roles': roles, 559a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'attributes': attributes, 569a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'date_created': self.date_created, 579a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'date_modified': self.date_modified, 589a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'note': self.note} 599a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 609a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 6156f1ba77e4b8b4c13d5bc72b0ebaeabda9f9d0bcDan Shi def get_role_names(self): 6256f1ba77e4b8b4c13d5bc72b0ebaeabda9f9d0bcDan Shi """Get a list of role names of the server. 6356f1ba77e4b8b4c13d5bc72b0ebaeabda9f9d0bcDan Shi 6456f1ba77e4b8b4c13d5bc72b0ebaeabda9f9d0bcDan Shi @return: A list of role names of the server. 6556f1ba77e4b8b4c13d5bc72b0ebaeabda9f9d0bcDan Shi """ 6656f1ba77e4b8b4c13d5bc72b0ebaeabda9f9d0bcDan Shi return [r.role for r in self.roles.all()] 6756f1ba77e4b8b4c13d5bc72b0ebaeabda9f9d0bcDan Shi 6856f1ba77e4b8b4c13d5bc72b0ebaeabda9f9d0bcDan Shi 69d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi def get_details(self): 70d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi """Get a dictionary with all server details. 71d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi 72d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi For example: 73d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi { 74d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi 'hostname': 'server1', 7591f58e141f3773acf16ae4af3173366b299f76bbXixuan Wu 'status': 'primary', 76d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi 'roles': ['drone', 'scheduler'], 77d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi 'attributes': {'max_processes': 300} 78d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi } 79d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi 80d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi @return: A dictionary with all server details. 81d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi """ 82d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi details = {} 83d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi details['hostname'] = self.hostname 84d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi details['status'] = self.status 85d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi details['roles'] = self.get_role_names() 86d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi attributes = dict([(a.attribute, a.value) 87d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi for a in self.attributes.all()]) 88d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi details['attributes'] = attributes 89d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi details['date_created'] = self.date_created 90d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi details['date_modified'] = self.date_modified 91d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi details['note'] = self.note 92d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi return details 93d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi 94d7bb4f16fde2f29efac95b0f7b8122b05ea4bf03Dan Shi 959a535c9f3144690cf85d88a51e07b98c21454159Dan Shiclass ServerRole(dbmodels.Model, model_logic.ModelExtensions): 969a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Role associated with hosts.""" 979a535c9f3144690cf85d88a51e07b98c21454159Dan Shi # Valid roles for a server. 98a09a37a6208ad09b0dbcd2ad0da5ad4c41c24144Fang Deng ROLE_LIST = ['afe', 'scheduler', 'host_scheduler', 'drone', 'devserver', 9993e646cbcab150b4ae11945592ff969e464ad31aXixuan Wu 'database', 'database_slave', 'crash_server', 'shard', 10093e646cbcab150b4ae11945592ff969e464ad31aXixuan Wu 'golo_proxy', 'sentinel', 'reserve'] 1019a535c9f3144690cf85d88a51e07b98c21454159Dan Shi ROLE = enum.Enum(*ROLE_LIST, string_values=True) 1029a535c9f3144690cf85d88a51e07b98c21454159Dan Shi # Roles that must be assigned to a single primary server in an Autotest 1039a535c9f3144690cf85d88a51e07b98c21454159Dan Shi # instance 1049a535c9f3144690cf85d88a51e07b98c21454159Dan Shi ROLES_REQUIRE_UNIQUE_INSTANCE = [ROLE.SCHEDULER, 1059a535c9f3144690cf85d88a51e07b98c21454159Dan Shi ROLE.HOST_SCHEDULER, 10693e646cbcab150b4ae11945592ff969e464ad31aXixuan Wu ROLE.DATABASE] 1079a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1089a535c9f3144690cf85d88a51e07b98c21454159Dan Shi server = dbmodels.ForeignKey(Server, related_name='roles') 1099a535c9f3144690cf85d88a51e07b98c21454159Dan Shi role = dbmodels.CharField(max_length=128, choices=ROLE.choices()) 1109a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1119a535c9f3144690cf85d88a51e07b98c21454159Dan Shi objects = model_logic.ExtendedManager() 1129a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1139a535c9f3144690cf85d88a51e07b98c21454159Dan Shi class Meta: 1149a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Metadata for the ServerRole class.""" 1159a535c9f3144690cf85d88a51e07b98c21454159Dan Shi db_table = 'server_roles' 1169a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1179a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1189a535c9f3144690cf85d88a51e07b98c21454159Dan Shiclass ServerAttribute(dbmodels.Model, model_logic.ModelExtensions): 1199a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Attribute associated with hosts.""" 1209a535c9f3144690cf85d88a51e07b98c21454159Dan Shi server = dbmodels.ForeignKey(Server, related_name='attributes') 1219a535c9f3144690cf85d88a51e07b98c21454159Dan Shi attribute = dbmodels.CharField(max_length=128) 1229a535c9f3144690cf85d88a51e07b98c21454159Dan Shi value = dbmodels.TextField(null=True, blank=True) 1239a535c9f3144690cf85d88a51e07b98c21454159Dan Shi date_modified = dbmodels.DateTimeField(null=True, blank=True) 1249a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1259a535c9f3144690cf85d88a51e07b98c21454159Dan Shi objects = model_logic.ExtendedManager() 1269a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1279a535c9f3144690cf85d88a51e07b98c21454159Dan Shi class Meta: 1289a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Metadata for the ServerAttribute class.""" 1299a535c9f3144690cf85d88a51e07b98c21454159Dan Shi db_table = 'server_attributes' 1309a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1319a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1329a535c9f3144690cf85d88a51e07b98c21454159Dan Shi# Valid values for each type of input. 1339a535c9f3144690cf85d88a51e07b98c21454159Dan ShiRANGE_LIMITS={'role': ServerRole.ROLE_LIST, 1349a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'status': Server.STATUS_LIST} 1359a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1369a535c9f3144690cf85d88a51e07b98c21454159Dan Shidef validate(**kwargs): 1379a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Verify command line arguments, raise InvalidDataError if any is invalid. 1389a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1399a535c9f3144690cf85d88a51e07b98c21454159Dan Shi The function verify following inputs for the database query. 1409a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid 1419a535c9f3144690cf85d88a51e07b98c21454159Dan Shi role or status. 1429a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 2. hostname. The code will try to resolve given hostname. If the hostname 1439a535c9f3144690cf85d88a51e07b98c21454159Dan Shi does not exist in the network, InvalidDataError will be raised. 1449a535c9f3144690cf85d88a51e07b98c21454159Dan Shi Sample usage of this function: 14591f58e141f3773acf16ae4af3173366b299f76bbXixuan Wu validate(role='drone', status='repair_required', hostname='server1') 1469a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1479a535c9f3144690cf85d88a51e07b98c21454159Dan Shi @param kwargs: command line arguments, e.g., `status='primary'` 1489a535c9f3144690cf85d88a51e07b98c21454159Dan Shi @raise InvalidDataError: If any argument value is invalid. 1499a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """ 1509a535c9f3144690cf85d88a51e07b98c21454159Dan Shi for key, value in kwargs.items(): 1519a535c9f3144690cf85d88a51e07b98c21454159Dan Shi # Ignore any None value, so callers won't need to filter out None 1529a535c9f3144690cf85d88a51e07b98c21454159Dan Shi # value as it won't be used in queries. 1539a535c9f3144690cf85d88a51e07b98c21454159Dan Shi if not value: 1549a535c9f3144690cf85d88a51e07b98c21454159Dan Shi continue 1559a535c9f3144690cf85d88a51e07b98c21454159Dan Shi if value not in RANGE_LIMITS.get(key, [value]): 1569a535c9f3144690cf85d88a51e07b98c21454159Dan Shi raise error.InvalidDataError( 1579a535c9f3144690cf85d88a51e07b98c21454159Dan Shi '%s %s is not valid, it must be one of %s.' % 1589a535c9f3144690cf85d88a51e07b98c21454159Dan Shi (key, value, 1599a535c9f3144690cf85d88a51e07b98c21454159Dan Shi ', '.join(RANGE_LIMITS[key]))) 1609a535c9f3144690cf85d88a51e07b98c21454159Dan Shi elif key == 'hostname': 1610697ccd2453b9eafc087e1e6a7bdb289b81b45edDan Shi if not ping_runner.PingRunner().simple_ping(value): 1620697ccd2453b9eafc087e1e6a7bdb289b81b45edDan Shi raise error.InvalidDataError('Can not reach server with ' 1630697ccd2453b9eafc087e1e6a7bdb289b81b45edDan Shi 'hostname "%s".' % value) 164