• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 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
8from django.db import models as dbmodels
9
10import common
11from autotest_lib.client.common_lib import enum
12from autotest_lib.client.common_lib import error
13from autotest_lib.client.common_lib.cros.network import ping_runner
14from autotest_lib.frontend.afe import model_logic
15
16
17class Server(dbmodels.Model, model_logic.ModelExtensions):
18    """Models a server."""
19    DETAIL_FMT = ('Hostname     : %(hostname)s\n'
20                  'Status       : %(status)s\n'
21                  'Roles        : %(roles)s\n'
22                  'Attributes   : %(attributes)s\n'
23                  'Date Created : %(date_created)s\n'
24                  'Date Modified: %(date_modified)s\n'
25                  'Note         : %(note)s\n')
26
27    STATUS_LIST = ['primary', 'repair_required']
28    STATUS = enum.Enum(*STATUS_LIST, string_values=True)
29
30    hostname = dbmodels.CharField(unique=True, max_length=128)
31    cname = dbmodels.CharField(null=True, blank=True, default=None,
32                               max_length=128)
33    status = dbmodels.CharField(unique=False, max_length=128,
34                                choices=STATUS.choices())
35    date_created = dbmodels.DateTimeField(null=True, blank=True)
36    date_modified = dbmodels.DateTimeField(null=True, blank=True)
37    note = dbmodels.TextField(null=True, blank=True)
38
39    objects = model_logic.ExtendedManager()
40
41    class Meta:
42        """Metadata for class Server."""
43        db_table = 'servers'
44
45
46    def __unicode__(self):
47        """A string representation of the Server object.
48        """
49        roles = ','.join([r.role for r in self.roles.all()])
50        attributes = dict([(a.attribute, a.value)
51                           for a in self.attributes.all()])
52        return self.DETAIL_FMT % {'hostname': self.hostname,
53                                  'status': self.status,
54                                  'roles': roles,
55                                  'attributes': attributes,
56                                  'date_created': self.date_created,
57                                  'date_modified': self.date_modified,
58                                  'note': self.note}
59
60
61    def get_role_names(self):
62        """Get a list of role names of the server.
63
64        @return: A list of role names of the server.
65        """
66        return [r.role for r in self.roles.all()]
67
68
69    def get_details(self):
70        """Get a dictionary with all server details.
71
72        For example:
73        {
74            'hostname': 'server1',
75            'status': 'primary',
76            'roles': ['drone', 'scheduler'],
77            'attributes': {'max_processes': 300}
78        }
79
80        @return: A dictionary with all server details.
81        """
82        details = {}
83        details['hostname'] = self.hostname
84        details['status'] = self.status
85        details['roles'] = self.get_role_names()
86        attributes = dict([(a.attribute, a.value)
87                           for a in self.attributes.all()])
88        details['attributes'] = attributes
89        details['date_created'] = self.date_created
90        details['date_modified'] = self.date_modified
91        details['note'] = self.note
92        return details
93
94
95class ServerRole(dbmodels.Model, model_logic.ModelExtensions):
96    """Role associated with hosts."""
97    # Valid roles for a server.
98    ROLE_LIST = [
99            'afe',
100            'crash_server',
101            'database',
102            'database_slave',
103            'devserver',
104            'drone',
105            'golo_proxy',
106            'host_scheduler',
107            'scheduler',
108            'sentinel',
109            'shard',
110            'skylab_drone',
111
112            'reserve',
113    ]
114    ROLE = enum.Enum(*ROLE_LIST, string_values=True)
115    # Roles that must be assigned to a single primary server in an Autotest
116    # instance
117    ROLES_REQUIRE_UNIQUE_INSTANCE = [ROLE.SCHEDULER,
118                                     ROLE.HOST_SCHEDULER,
119                                     ROLE.DATABASE]
120
121    server = dbmodels.ForeignKey(Server, related_name='roles')
122    role = dbmodels.CharField(max_length=128, choices=ROLE.choices())
123
124    objects = model_logic.ExtendedManager()
125
126    class Meta:
127        """Metadata for the ServerRole class."""
128        db_table = 'server_roles'
129
130
131class ServerAttribute(dbmodels.Model, model_logic.ModelExtensions):
132    """Attribute associated with hosts."""
133    server = dbmodels.ForeignKey(Server, related_name='attributes')
134    attribute = dbmodels.CharField(max_length=128)
135    value = dbmodels.TextField(null=True, blank=True)
136    date_modified = dbmodels.DateTimeField(null=True, blank=True)
137
138    objects = model_logic.ExtendedManager()
139
140    class Meta:
141        """Metadata for the ServerAttribute class."""
142        db_table = 'server_attributes'
143
144
145# Valid values for each type of input.
146RANGE_LIMITS={'role': ServerRole.ROLE_LIST,
147              'status': Server.STATUS_LIST}
148
149def validate(**kwargs):
150    """Verify command line arguments, raise InvalidDataError if any is invalid.
151
152    The function verify following inputs for the database query.
153    1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid
154       role or status.
155    2. hostname. The code will try to resolve given hostname. If the hostname
156       does not exist in the network, InvalidDataError will be raised.
157    Sample usage of this function:
158    validate(role='drone', status='repair_required', hostname='server1')
159
160    @param kwargs: command line arguments, e.g., `status='primary'`
161    @raise InvalidDataError: If any argument value is invalid.
162    """
163    for key, value in kwargs.items():
164        # Ignore any None value, so callers won't need to filter out None
165        # value as it won't be used in queries.
166        if not value:
167            continue
168        if value not in RANGE_LIMITS.get(key, [value]):
169            raise error.InvalidDataError(
170                    '%s %s is not valid, it must be one of %s.' %
171                    (key, value,
172                     ', '.join(RANGE_LIMITS[key])))
173        elif key == 'hostname':
174            if not ping_runner.PingRunner().simple_ping(value):
175                raise error.InvalidDataError('Can not reach server with '
176                                             'hostname "%s".' % value)
177