Home | History | Annotate | Download | only in site_utils
      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