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