blob: 43379c776877bbc7474aab16af0ca3f32e9aa96d [file] [log] [blame]
Dan Shi9a535c92014-11-24 08:52:40 -08001# 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
8import socket
9from django.db import models as dbmodels
10
11import common
12from autotest_lib.client.common_lib import base_utils as utils
13from autotest_lib.client.common_lib import enum
14from autotest_lib.client.common_lib import error
15from autotest_lib.frontend.afe import model_logic
16
17
18class Server(dbmodels.Model, model_logic.ModelExtensions):
19 """Models a server."""
20 DETAIL_FMT = ('Hostname : %(hostname)s\n'
21 'Status : %(status)s\n'
22 'Roles : %(roles)s\n'
23 'Attributes : %(attributes)s\n'
24 'Date Created : %(date_created)s\n'
25 'Date Modified: %(date_modified)s\n'
26 'Note : %(note)s\n')
27
28 STATUS_LIST = ['primary', 'backup', 'repair_required']
29 STATUS = enum.Enum(*STATUS_LIST, string_values=True)
30
31 hostname = dbmodels.CharField(unique=True, max_length=128)
32 cname = dbmodels.CharField(null=True, blank=True, default=None,
33 max_length=128)
34 status = dbmodels.CharField(unique=False, max_length=128,
35 choices=STATUS.choices())
36 date_created = dbmodels.DateTimeField(null=True, blank=True)
37 date_modified = dbmodels.DateTimeField(null=True, blank=True)
38 note = dbmodels.TextField(null=True, blank=True)
39
40 objects = model_logic.ExtendedManager()
41
42 class Meta:
43 """Metadata for class Server."""
44 db_table = 'servers'
45
46
47 def __unicode__(self):
48 """A string representation of the Server object.
49 """
50 roles = ','.join([r.role for r in self.roles.all()])
51 attributes = dict([(a.attribute, a.value)
52 for a in self.attributes.all()])
53 return self.DETAIL_FMT % {'hostname': self.hostname,
54 'status': self.status,
55 'roles': roles,
56 'attributes': attributes,
57 'date_created': self.date_created,
58 'date_modified': self.date_modified,
59 'note': self.note}
60
61
Dan Shi56f1ba72014-12-03 19:16:53 -080062 def get_role_names(self):
63 """Get a list of role names of the server.
64
65 @return: A list of role names of the server.
66 """
67 return [r.role for r in self.roles.all()]
68
69
Dan Shi9a535c92014-11-24 08:52:40 -080070class ServerRole(dbmodels.Model, model_logic.ModelExtensions):
71 """Role associated with hosts."""
72 # Valid roles for a server.
73 ROLE_LIST = ['scheduler', 'host_scheduler', 'drone', 'devserver',
Dan Shibf0414d2015-01-05 10:56:02 -080074 'database', 'suite_scheduler', 'crash_server', 'shard',
75 'golo_proxy']
Dan Shi9a535c92014-11-24 08:52:40 -080076 ROLE = enum.Enum(*ROLE_LIST, string_values=True)
77 # When deleting any of following roles from a primary server, a working
78 # backup must be available if user_server_db is enabled in global config.
79 ROLES_REQUIRE_BACKUP = [ROLE.SCHEDULER, ROLE.HOST_SCHEDULER,
80 ROLE.DATABASE, ROLE.SUITE_SCHEDULER,
81 ROLE.DRONE]
82 # Roles that must be assigned to a single primary server in an Autotest
83 # instance
84 ROLES_REQUIRE_UNIQUE_INSTANCE = [ROLE.SCHEDULER,
85 ROLE.HOST_SCHEDULER,
86 ROLE.DATABASE,
87 ROLE.SUITE_SCHEDULER]
88
89 server = dbmodels.ForeignKey(Server, related_name='roles')
90 role = dbmodels.CharField(max_length=128, choices=ROLE.choices())
91
92 objects = model_logic.ExtendedManager()
93
94 class Meta:
95 """Metadata for the ServerRole class."""
96 db_table = 'server_roles'
97
98
99class ServerAttribute(dbmodels.Model, model_logic.ModelExtensions):
100 """Attribute associated with hosts."""
101 server = dbmodels.ForeignKey(Server, related_name='attributes')
102 attribute = dbmodels.CharField(max_length=128)
103 value = dbmodels.TextField(null=True, blank=True)
104 date_modified = dbmodels.DateTimeField(null=True, blank=True)
105
106 objects = model_logic.ExtendedManager()
107
108 class Meta:
109 """Metadata for the ServerAttribute class."""
110 db_table = 'server_attributes'
111
112
113# Valid values for each type of input.
114RANGE_LIMITS={'role': ServerRole.ROLE_LIST,
115 'status': Server.STATUS_LIST}
116
117def validate(**kwargs):
118 """Verify command line arguments, raise InvalidDataError if any is invalid.
119
120 The function verify following inputs for the database query.
121 1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid
122 role or status.
123 2. hostname. The code will try to resolve given hostname. If the hostname
124 does not exist in the network, InvalidDataError will be raised.
125 Sample usage of this function:
126 validate(role='drone', status='backup', hostname='server1')
127
128 @param kwargs: command line arguments, e.g., `status='primary'`
129 @raise InvalidDataError: If any argument value is invalid.
130 """
131 for key, value in kwargs.items():
132 # Ignore any None value, so callers won't need to filter out None
133 # value as it won't be used in queries.
134 if not value:
135 continue
136 if value not in RANGE_LIMITS.get(key, [value]):
137 raise error.InvalidDataError(
138 '%s %s is not valid, it must be one of %s.' %
139 (key, value,
140 ', '.join(RANGE_LIMITS[key])))
141 elif key == 'hostname':
142 try:
143 utils.normalize_hostname(value)
144 except socket.error:
145 raise error.InvalidDataError('Can not resolve hostname "%s".' %
146 value)