• 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', 'backup', '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': 'backup',
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 = ['afe', 'scheduler', 'host_scheduler', 'drone', 'devserver',
99                 'database', 'database_slave', 'suite_scheduler',
100                 'crash_server', 'shard', 'golo_proxy', 'reserve']
101    ROLE = enum.Enum(*ROLE_LIST, string_values=True)
102    # When deleting any of following roles from a primary server, a working
103    # backup must be available if user_server_db is enabled in global config.
104    ROLES_REQUIRE_BACKUP = [ROLE.SCHEDULER, ROLE.HOST_SCHEDULER,
105                            ROLE.DATABASE, ROLE.SUITE_SCHEDULER,
106                            ROLE.DRONE]
107    # Roles that must be assigned to a single primary server in an Autotest
108    # instance
109    ROLES_REQUIRE_UNIQUE_INSTANCE = [ROLE.SCHEDULER,
110                                     ROLE.HOST_SCHEDULER,
111                                     ROLE.DATABASE,
112                                     ROLE.SUITE_SCHEDULER]
113
114    server = dbmodels.ForeignKey(Server, related_name='roles')
115    role = dbmodels.CharField(max_length=128, choices=ROLE.choices())
116
117    objects = model_logic.ExtendedManager()
118
119    class Meta:
120        """Metadata for the ServerRole class."""
121        db_table = 'server_roles'
122
123
124class ServerAttribute(dbmodels.Model, model_logic.ModelExtensions):
125    """Attribute associated with hosts."""
126    server = dbmodels.ForeignKey(Server, related_name='attributes')
127    attribute = dbmodels.CharField(max_length=128)
128    value = dbmodels.TextField(null=True, blank=True)
129    date_modified = dbmodels.DateTimeField(null=True, blank=True)
130
131    objects = model_logic.ExtendedManager()
132
133    class Meta:
134        """Metadata for the ServerAttribute class."""
135        db_table = 'server_attributes'
136
137
138# Valid values for each type of input.
139RANGE_LIMITS={'role': ServerRole.ROLE_LIST,
140              'status': Server.STATUS_LIST}
141
142def validate(**kwargs):
143    """Verify command line arguments, raise InvalidDataError if any is invalid.
144
145    The function verify following inputs for the database query.
146    1. Any key in RANGE_LIMITS, i.e., role and status. Value should be a valid
147       role or status.
148    2. hostname. The code will try to resolve given hostname. If the hostname
149       does not exist in the network, InvalidDataError will be raised.
150    Sample usage of this function:
151    validate(role='drone', status='backup', hostname='server1')
152
153    @param kwargs: command line arguments, e.g., `status='primary'`
154    @raise InvalidDataError: If any argument value is invalid.
155    """
156    for key, value in kwargs.items():
157        # Ignore any None value, so callers won't need to filter out None
158        # value as it won't be used in queries.
159        if not value:
160            continue
161        if value not in RANGE_LIMITS.get(key, [value]):
162            raise error.InvalidDataError(
163                    '%s %s is not valid, it must be one of %s.' %
164                    (key, value,
165                     ', '.join(RANGE_LIMITS[key])))
166        elif key == 'hostname':
167            if not ping_runner.PingRunner().simple_ping(value):
168                raise error.InvalidDataError('Can not reach server with '
169                                             'hostname "%s".' % value)
170