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