imip-agent

Annotated imiptools/handlers/scheduling/__init__.py

1053:068aa85f0c45
2016-02-08 Paul Boddie Made the retraction operation a complete transaction. Tidied up the locking and unlocking function application.
paul@938 1
#!/usr/bin/env python
paul@938 2
paul@938 3
"""
paul@938 4
Common scheduling functionality.
paul@938 5
paul@1025 6
Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk>
paul@938 7
paul@938 8
This program is free software; you can redistribute it and/or modify it under
paul@938 9
the terms of the GNU General Public License as published by the Free Software
paul@938 10
Foundation; either version 3 of the License, or (at your option) any later
paul@938 11
version.
paul@938 12
paul@938 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@938 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@938 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@938 16
details.
paul@938 17
paul@938 18
You should have received a copy of the GNU General Public License along with
paul@938 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@938 20
"""
paul@938 21
paul@1031 22
from imiptools.text import parse_line
paul@1039 23
from imiptools.handlers.scheduling.manifest import confirmation_functions, \
paul@1040 24
                                                   locking_functions, \
paul@1039 25
                                                   retraction_functions, \
paul@1040 26
                                                   scheduling_functions, \
paul@1040 27
                                                   unlocking_functions
paul@1039 28
paul@1039 29
# Function application/invocation.
paul@938 30
paul@1025 31
def apply_scheduling_functions(functions, handler):
paul@1025 32
paul@1025 33
    """
paul@1025 34
    Apply the given scheduling 'functions' in the current object of the given
paul@1053 35
    'handler'. This function starts a transaction that should be finalised using
paul@1053 36
    the 'finish_scheduling' function.
paul@1025 37
    """
paul@1025 38
paul@1040 39
    # First, lock the resources to be used.
paul@1040 40
paul@1053 41
    start_scheduling(functions, handler)
paul@1040 42
paul@1053 43
    # Obtain the actual scheduling functions with arguments.
paul@1040 44
paul@1053 45
    schedulers = get_function_calls(functions, scheduling_functions)
paul@1040 46
paul@1040 47
    # Then, invoke the scheduling functions.
paul@1031 48
paul@1025 49
    response = "ACCEPTED"
paul@1025 50
paul@1040 51
    for fn, args in schedulers:
paul@1025 52
paul@1025 53
        # NOTE: Should signal an error for incorrectly configured resources.
paul@1025 54
paul@1025 55
        if not fn:
paul@1025 56
            return "DECLINED"
paul@1025 57
paul@1025 58
        # Keep evaluating scheduling functions, stopping only if one
paul@1025 59
        # declines or gives a null response.
paul@1025 60
paul@1025 61
        else:
paul@1031 62
            result = fn(handler, args)
paul@1025 63
paul@1025 64
            # Return a negative result immediately.
paul@1025 65
paul@1025 66
            if not result or result == "DECLINED":
paul@1025 67
                return result
paul@1025 68
paul@1025 69
            # Modify the eventual response from acceptance if a countering
paul@1025 70
            # result is obtained.
paul@1025 71
paul@1025 72
            elif response == "ACCEPTED":
paul@1025 73
                response = result
paul@1025 74
paul@1025 75
    return response
paul@1025 76
paul@1039 77
def confirm_scheduling(functions, handler):
paul@1039 78
paul@1039 79
    """
paul@1039 80
    Confirm scheduling using the given listener 'functions' for the current
paul@1053 81
    object of the given 'handler'. This function continues a transaction that
paul@1053 82
    should be finalised using the 'finish_scheduling' function.
paul@1039 83
    """
paul@1039 84
paul@1039 85
    # Obtain the actual listener functions with arguments.
paul@1039 86
paul@1039 87
    functions = get_function_calls(functions, confirmation_functions)
paul@1039 88
    apply_functions(functions, handler)
paul@1039 89
paul@1053 90
def retract_scheduling(functions, handler):
paul@1053 91
paul@1053 92
    """
paul@1053 93
    Retract scheduling using the given listener 'functions' for the current
paul@1053 94
    object of the given 'handler'. This function is a complete transaction in
paul@1053 95
    itself.
paul@1053 96
    """
paul@1053 97
paul@1053 98
    # First, lock the resources to be used.
paul@1053 99
paul@1053 100
    start_scheduling(functions, handler)
paul@1053 101
paul@1053 102
    # Obtain the actual listener functions with arguments.
paul@1053 103
paul@1053 104
    retractors = get_function_calls(functions, retraction_functions)
paul@1053 105
    apply_functions(retractors, handler)
paul@1053 106
paul@1053 107
    # Finally, unlock the resources.
paul@1053 108
paul@1053 109
    finish_scheduling(functions, handler)
paul@1053 110
paul@1053 111
def start_scheduling(functions, handler):
paul@1053 112
paul@1053 113
    """
paul@1053 114
    Apply locking functions for the given scheduling 'functions' and for the
paul@1053 115
    current object of the given 'handler'.
paul@1053 116
    """
paul@1053 117
paul@1053 118
    # Obtain functions to lock resources.
paul@1053 119
paul@1053 120
    locks = get_function_calls(functions, locking_functions)
paul@1053 121
    apply_functions(locks, handler)
paul@1053 122
paul@1040 123
def finish_scheduling(functions, handler):
paul@1040 124
paul@1040 125
    """
paul@1040 126
    Finish scheduling using the given scheduling 'functions' for the current
paul@1040 127
    object of the given 'handler'.
paul@1040 128
    """
paul@1040 129
paul@1040 130
    # Obtain functions to unlock resources.
paul@1040 131
paul@1040 132
    locks = get_function_calls(functions, unlocking_functions)
paul@1053 133
    apply_functions(locks, handler)
paul@1039 134
paul@1039 135
def apply_functions(functions, handler):
paul@1039 136
paul@1039 137
    """
paul@1039 138
    Apply the given notification 'functions' for the current object of the given
paul@1039 139
    'handler'.
paul@1039 140
    """
paul@1039 141
paul@1039 142
    for fn, args in functions:
paul@1039 143
paul@1039 144
        # NOTE: Should signal an error for incorrectly configured resources.
paul@1039 145
paul@1039 146
        if not fn:
paul@1039 147
            continue
paul@1039 148
paul@1039 149
        fn(handler, args)
paul@1039 150
paul@1039 151
# Function lookup.
paul@1039 152
paul@1039 153
def get_function_calls(lines, registry):
paul@1031 154
paul@1031 155
    """
paul@1031 156
    Parse the given 'lines', returning a list of (function, arguments) tuples,
paul@1031 157
    with each function being a genuine function object and with the arguments
paul@1031 158
    being a list of strings.
paul@1031 159
paul@1031 160
    Each of the 'lines' should employ the function name and argument strings
paul@1031 161
    separated by whitespace, with any whitespace inside arguments quoted using
paul@1031 162
    single or double quotes.
paul@1039 163
paul@1039 164
    The given 'registry' indicates the mapping from function names to actual
paul@1039 165
    functions.
paul@1031 166
    """
paul@1031 167
paul@1031 168
    functions = []
paul@1031 169
paul@1031 170
    for line in lines:
paul@1031 171
        parts = parse_line(line)
paul@1039 172
        functions.append((registry.get(parts[0]), parts[1:]))
paul@1031 173
paul@1031 174
    return functions
paul@1031 175
paul@938 176
# vim: tabstop=4 expandtab shiftwidth=4