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