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