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