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