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 primary.
     10 
     11 delete(hostname)
     12     Delete a server from the database.
     13 
     14 modify(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 
     33 import datetime
     34 
     35 import common
     36 
     37 from autotest_lib.frontend.server import models as server_models
     38 from autotest_lib.site_utils import server_manager_actions
     39 from autotest_lib.site_utils import server_manager_utils
     40 
     41 
     42 def _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 
     80 def _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 
    118 def _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)
    184 def 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()
    204 def 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()
    228 def 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