models.py revision 91f58e141f3773acf16ae4af3173366b299f76bb
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', 99c6b879894cbfefe51bd003d1686a96e4f4d0e6deFang Deng 'database', 'database_slave', 'suite_scheduler', 1003a3045652c43b07e48b226dd5d785117160ff1adPrathmesh Prabhu 'crash_server', 'shard', '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, 1069a535c9f3144690cf85d88a51e07b98c21454159Dan Shi ROLE.DATABASE, 1079a535c9f3144690cf85d88a51e07b98c21454159Dan Shi ROLE.SUITE_SCHEDULER] 1089a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1099a535c9f3144690cf85d88a51e07b98c21454159Dan Shi server = dbmodels.ForeignKey(Server, related_name='roles') 1109a535c9f3144690cf85d88a51e07b98c21454159Dan Shi role = dbmodels.CharField(max_length=128, choices=ROLE.choices()) 1119a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1129a535c9f3144690cf85d88a51e07b98c21454159Dan Shi objects = model_logic.ExtendedManager() 1139a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1149a535c9f3144690cf85d88a51e07b98c21454159Dan Shi class Meta: 1159a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Metadata for the ServerRole class.""" 1169a535c9f3144690cf85d88a51e07b98c21454159Dan Shi db_table = 'server_roles' 1179a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1189a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1199a535c9f3144690cf85d88a51e07b98c21454159Dan Shiclass ServerAttribute(dbmodels.Model, model_logic.ModelExtensions): 1209a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Attribute associated with hosts.""" 1219a535c9f3144690cf85d88a51e07b98c21454159Dan Shi server = dbmodels.ForeignKey(Server, related_name='attributes') 1229a535c9f3144690cf85d88a51e07b98c21454159Dan Shi attribute = dbmodels.CharField(max_length=128) 1239a535c9f3144690cf85d88a51e07b98c21454159Dan Shi value = dbmodels.TextField(null=True, blank=True) 1249a535c9f3144690cf85d88a51e07b98c21454159Dan Shi date_modified = dbmodels.DateTimeField(null=True, blank=True) 1259a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1269a535c9f3144690cf85d88a51e07b98c21454159Dan Shi objects = model_logic.ExtendedManager() 1279a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1289a535c9f3144690cf85d88a51e07b98c21454159Dan Shi class Meta: 1299a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Metadata for the ServerAttribute class.""" 1309a535c9f3144690cf85d88a51e07b98c21454159Dan Shi db_table = 'server_attributes' 1319a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1329a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1339a535c9f3144690cf85d88a51e07b98c21454159Dan Shi# Valid values for each type of input. 1349a535c9f3144690cf85d88a51e07b98c21454159Dan ShiRANGE_LIMITS={'role': ServerRole.ROLE_LIST, 1359a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 'status': Server.STATUS_LIST} 1369a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1379a535c9f3144690cf85d88a51e07b98c21454159Dan Shidef validate(**kwargs): 1389a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """Verify command line arguments, raise InvalidDataError if any is invalid. 1399a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1409a535c9f3144690cf85d88a51e07b98c21454159Dan Shi The function verify following inputs for the database query. 1419a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid 1429a535c9f3144690cf85d88a51e07b98c21454159Dan Shi role or status. 1439a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 2. hostname. The code will try to resolve given hostname. If the hostname 1449a535c9f3144690cf85d88a51e07b98c21454159Dan Shi does not exist in the network, InvalidDataError will be raised. 1459a535c9f3144690cf85d88a51e07b98c21454159Dan Shi Sample usage of this function: 14691f58e141f3773acf16ae4af3173366b299f76bbXixuan Wu validate(role='drone', status='repair_required', hostname='server1') 1479a535c9f3144690cf85d88a51e07b98c21454159Dan Shi 1489a535c9f3144690cf85d88a51e07b98c21454159Dan Shi @param kwargs: command line arguments, e.g., `status='primary'` 1499a535c9f3144690cf85d88a51e07b98c21454159Dan Shi @raise InvalidDataError: If any argument value is invalid. 1509a535c9f3144690cf85d88a51e07b98c21454159Dan Shi """ 1519a535c9f3144690cf85d88a51e07b98c21454159Dan Shi for key, value in kwargs.items(): 1529a535c9f3144690cf85d88a51e07b98c21454159Dan Shi # Ignore any None value, so callers won't need to filter out None 1539a535c9f3144690cf85d88a51e07b98c21454159Dan Shi # value as it won't be used in queries. 1549a535c9f3144690cf85d88a51e07b98c21454159Dan Shi if not value: 1559a535c9f3144690cf85d88a51e07b98c21454159Dan Shi continue 1569a535c9f3144690cf85d88a51e07b98c21454159Dan Shi if value not in RANGE_LIMITS.get(key, [value]): 1579a535c9f3144690cf85d88a51e07b98c21454159Dan Shi raise error.InvalidDataError( 1589a535c9f3144690cf85d88a51e07b98c21454159Dan Shi '%s %s is not valid, it must be one of %s.' % 1599a535c9f3144690cf85d88a51e07b98c21454159Dan Shi (key, value, 1609a535c9f3144690cf85d88a51e07b98c21454159Dan Shi ', '.join(RANGE_LIMITS[key]))) 1619a535c9f3144690cf85d88a51e07b98c21454159Dan Shi elif key == 'hostname': 1620697ccd2453b9eafc087e1e6a7bdb289b81b45edDan Shi if not ping_runner.PingRunner().simple_ping(value): 1630697ccd2453b9eafc087e1e6a7bdb289b81b45edDan Shi raise error.InvalidDataError('Can not reach server with ' 1640697ccd2453b9eafc087e1e6a7bdb289b81b45edDan Shi 'hostname "%s".' % value) 165