blob: 876a78ec32f93a5bf197526fc8a86ced566274f9 [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',
74 'database', 'suite_scheduler', 'crash_server']
75 ROLE = enum.Enum(*ROLE_LIST, string_values=True)
76 # When deleting any of following roles from a primary server, a working
77 # backup must be available if user_server_db is enabled in global config.
78 ROLES_REQUIRE_BACKUP = [ROLE.SCHEDULER, ROLE.HOST_SCHEDULER,
79 ROLE.DATABASE, ROLE.SUITE_SCHEDULER,
80 ROLE.DRONE]
81 # Roles that must be assigned to a single primary server in an Autotest
82 # instance
83 ROLES_REQUIRE_UNIQUE_INSTANCE = [ROLE.SCHEDULER,
84 ROLE.HOST_SCHEDULER,
85 ROLE.DATABASE,
86 ROLE.SUITE_SCHEDULER]
87
88 server = dbmodels.ForeignKey(Server, related_name='roles')
89 role = dbmodels.CharField(max_length=128, choices=ROLE.choices())
90
91 objects = model_logic.ExtendedManager()
92
93 class Meta:
94 """Metadata for the ServerRole class."""
95 db_table = 'server_roles'
96
97
98class ServerAttribute(dbmodels.Model, model_logic.ModelExtensions):
99 """Attribute associated with hosts."""
100 server = dbmodels.ForeignKey(Server, related_name='attributes')
101 attribute = dbmodels.CharField(max_length=128)
102 value = dbmodels.TextField(null=True, blank=True)
103 date_modified = dbmodels.DateTimeField(null=True, blank=True)
104
105 objects = model_logic.ExtendedManager()
106
107 class Meta:
108 """Metadata for the ServerAttribute class."""
109 db_table = 'server_attributes'
110
111
112# Valid values for each type of input.
113RANGE_LIMITS={'role': ServerRole.ROLE_LIST,
114 'status': Server.STATUS_LIST}
115
116def validate(**kwargs):
117 """Verify command line arguments, raise InvalidDataError if any is invalid.
118
119 The function verify following inputs for the database query.
120 1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid
121 role or status.
122 2. hostname. The code will try to resolve given hostname. If the hostname
123 does not exist in the network, InvalidDataError will be raised.
124 Sample usage of this function:
125 validate(role='drone', status='backup', hostname='server1')
126
127 @param kwargs: command line arguments, e.g., `status='primary'`
128 @raise InvalidDataError: If any argument value is invalid.
129 """
130 for key, value in kwargs.items():
131 # Ignore any None value, so callers won't need to filter out None
132 # value as it won't be used in queries.
133 if not value:
134 continue
135 if value not in RANGE_LIMITS.get(key, [value]):
136 raise error.InvalidDataError(
137 '%s %s is not valid, it must be one of %s.' %
138 (key, value,
139 ', '.join(RANGE_LIMITS[key])))
140 elif key == 'hostname':
141 try:
142 utils.normalize_hostname(value)
143 except socket.error:
144 raise error.InvalidDataError('Can not resolve hostname "%s".' %
145 value)