• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Lint as: python2, python3
2# Copyright 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This module provides functions to manage servers in server database
7(defined in global config section AUTOTEST_SERVER_DB).
8
9create(hostname, role=None, note=None)
10    Create a server with given role, with status primary.
11
12delete(hostname)
13    Delete a server from the database.
14
15modify(hostname, role=None, status=None, note=None, delete=False,
16       attribute=None, value=None)
17    Modify a server's role, status, note, or attribute:
18    1. Add role to a server. If the server is in primary status, proper actions
19       like service restart will be executed to enable the role.
20    2. Delete a role from a server. If the server is in primary status, proper
21       actions like service restart will be executed to disable the role.
22    3. Change status of a server. If the server is changed from or to primary
23       status, proper actions like service restart will be executed to enable
24       or disable each role of the server.
25    4. Change note of a server. Note is a field you can add description about
26       the server.
27    5. Change/delete attribute of a server. Attribute can be used to store
28       information about a server. For example, the max_processes count for a
29       drone.
30
31"""
32
33
34from __future__ import absolute_import
35from __future__ import division
36from __future__ import print_function
37
38import datetime
39
40import common
41
42from autotest_lib.frontend.server import models as server_models
43from autotest_lib.site_utils import server_manager_actions
44from autotest_lib.site_utils import server_manager_utils
45
46
47def _add_role(server, role, action):
48    """Add a role to the server.
49
50    @param server: An object of server_models.Server.
51    @param role: Role to be added to the server.
52    @param action: Execute actions after role or status is changed. Default to
53                   False.
54
55    @raise ServerActionError: If role is failed to be added.
56    """
57    server_models.validate(role=role)
58    if role in server.get_role_names():
59        raise server_manager_utils.ServerActionError(
60                'Server %s already has role %s.' % (server.hostname, role))
61
62    # Verify server
63    if not server_manager_utils.check_server(server.hostname, role):
64        raise server_manager_utils.ServerActionError(
65                'Server %s is not ready for role %s.' % (server.hostname, role))
66
67    if (role in server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE and
68        server.status == server_models.Server.STATUS.PRIMARY):
69        servers = server_models.Server.objects.filter(
70                roles__role=role, status=server_models.Server.STATUS.PRIMARY)
71        if len(servers) >= 1:
72            raise server_manager_utils.ServerActionError(
73                'Role %s must be unique. Server %s already has role %s.' %
74                (role, servers[0].hostname, role))
75
76    server_models.ServerRole.objects.create(server=server, role=role)
77
78    # If needed, apply actions to enable the role for the server.
79    server_manager_actions.try_execute(server, [role], enable=True,
80                                       post_change=True, do_action=action)
81
82    print('Role %s is added to server %s.' % (role, server.hostname))
83
84
85def _delete_role(server, role, action=False):
86    """Delete a role from the server.
87
88    @param server: An object of server_models.Server.
89    @param role: Role to be deleted from the server.
90    @param action: Execute actions after role or status is changed. Default to
91                   False.
92
93    @raise ServerActionError: If role is failed to be deleted.
94    """
95    server_models.validate(role=role)
96    if role not in server.get_role_names():
97        raise server_manager_utils.ServerActionError(
98                'Server %s does not have role %s.' % (server.hostname, role))
99
100    if server.status == server_models.Server.STATUS.PRIMARY:
101        server_manager_utils.warn_missing_role(role, server)
102
103    # Apply actions to disable the role for the server before the role is
104    # removed from the server.
105    server_manager_actions.try_execute(server, [role], enable=False,
106                                       post_change=False, do_action=action)
107
108    print('Deleting role %s from server %s...' % (role, server.hostname))
109    server.roles.get(role=role).delete()
110
111    # Apply actions to disable the role for the server after the role is
112    # removed from the server.
113    server_manager_actions.try_execute(server, [role], enable=False,
114                                       post_change=True, do_action=action)
115
116    if (not server.get_role_names() and
117        server.status == server_models.Server.STATUS.PRIMARY):
118        print ('Server %s has no role.')
119
120    print('Role %s is deleted from server %s.' % (role, server.hostname))
121
122
123def _change_status(server, status, action):
124    """Change the status of the server.
125
126    @param server: An object of server_models.Server.
127    @param status: New status of the server.
128    @param action: Execute actions after role or status is changed. Default to
129                   False.
130
131    @raise ServerActionError: If status is failed to be changed.
132    """
133    server_models.validate(status=status)
134    if server.status == status:
135        raise server_manager_utils.ServerActionError(
136                'Server %s already has status of %s.' %
137                (server.hostname, status))
138    if (not server.roles.all() and
139        status == server_models.Server.STATUS.PRIMARY):
140        raise server_manager_utils.ServerActionError(
141                'Server %s has no role associated. Server must have a role to '
142                'be in status primary.' % server.hostname)
143
144    # Abort the action if the server's status will be changed to primary and
145    # the Autotest instance already has another server running an unique role.
146    # For example, a scheduler server is already running, and a repair_required
147    # server with role scheduler should not be changed to status primary.
148    unique_roles = server.roles.filter(
149            role__in=server_models.ServerRole.ROLES_REQUIRE_UNIQUE_INSTANCE)
150    if unique_roles and status == server_models.Server.STATUS.PRIMARY:
151        for role in unique_roles:
152            servers = server_models.Server.objects.filter(
153                    roles__role=role.role,
154                    status=server_models.Server.STATUS.PRIMARY)
155            if len(servers) == 1:
156                raise server_manager_utils.ServerActionError(
157                        'Role %s must be unique. Server %s already has the '
158                        'role.' % (role.role, servers[0].hostname))
159
160    # Post a warning if the server's status will be changed from primary to
161    # other value and the server is running a unique role across database, e.g.
162    # scheduler.
163    if server.status == server_models.Server.STATUS.PRIMARY:
164        for role in server.get_role_names():
165            server_manager_utils.warn_missing_role(role, server)
166
167    enable = status == server_models.Server.STATUS.PRIMARY
168    server_manager_actions.try_execute(server, server.get_role_names(),
169                                       enable=enable, post_change=False,
170                                       do_action=action)
171
172    prev_status = server.status
173    server.status = status
174    server.save()
175
176    # Apply actions to enable/disable roles of the server after the status is
177    # changed.
178    server_manager_actions.try_execute(server, server.get_role_names(),
179                                       enable=enable, post_change=True,
180                                       prev_status=prev_status,
181                                       do_action=action)
182
183    print('Status of server %s is changed from %s to %s. Affected roles: %s' %
184          (server.hostname, prev_status, status,
185           ', '.join(server.get_role_names())))
186
187
188@server_manager_utils.verify_server(exist=False)
189def create(hostname, role=None, note=None):
190    """Create a new server.
191
192    The status of new server will always be primary.
193
194    @param hostname: hostname of the server.
195    @param role: role of the new server, default to None.
196    @param note: notes about the server, default to None.
197
198    @return: A Server object that contains the server information.
199    """
200    server_models.validate(hostname=hostname, role=role)
201    server = server_models.Server.objects.create(
202            hostname=hostname, status=server_models.Server.STATUS.PRIMARY,
203            note=note, date_created=datetime.datetime.now())
204    server_models.ServerRole.objects.create(server=server, role=role)
205    return server
206
207
208@server_manager_utils.verify_server()
209def delete(hostname, server=None):
210    """Delete given server from server database.
211
212    @param hostname: hostname of the server to be deleted.
213    @param server: Server object from database query, this argument should be
214                   injected by the verify_server_exists decorator.
215
216    @raise ServerActionError: If delete server action failed, e.g., server is
217            not found in database.
218    """
219    print('Deleting server %s from server database.' % hostname)
220
221    if (server_manager_utils.use_server_db() and
222        server.status == server_models.Server.STATUS.PRIMARY):
223        print('Server %s is in status primary, need to disable its '
224              'current roles first.' % hostname)
225        for role in server.roles.all():
226            _delete_role(server, role.role)
227
228    server.delete()
229    print('Server %s is deleted from server database.' % hostname)
230
231
232@server_manager_utils.verify_server()
233def modify(hostname, role=None, status=None, delete=False, note=None,
234           attribute=None, value=None, action=False, server=None):
235    """Modify given server with specified actions.
236
237    @param hostname: hostname of the server to be modified.
238    @param role: Role to be added to the server.
239    @param status: Modify server status.
240    @param delete: True to delete given role from the server, default to False.
241    @param note: Note of the server.
242    @param attribute: Name of an attribute of the server.
243    @param value: Value of an attribute of the server.
244    @param action: Execute actions after role or status is changed. Default to
245                   False.
246    @param server: Server object from database query, this argument should be
247                   injected by the verify_server_exists decorator.
248
249    @raise InvalidDataError: If the operation failed with any wrong value of
250                             the arguments.
251    @raise ServerActionError: If any operation failed.
252    """
253    if role:
254        if not delete:
255            _add_role(server, role, action)
256        else:
257            _delete_role(server, role, action)
258
259    if status:
260        _change_status(server, status, action)
261
262    if note is not None:
263        server.note = note
264        server.save()
265
266    if attribute and value:
267        server_manager_utils.change_attribute(server, attribute, value)
268    elif attribute and delete:
269        server_manager_utils.delete_attribute(server, attribute)
270
271    return server
272