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(handler): 32 33 """ 34 Apply the scheduling functions for 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(handler) 42 43 # Obtain the actual scheduling functions with arguments. 44 45 schedulers = get_function_calls(handler.get_scheduling_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(handler): 78 79 """ 80 Confirm scheduling using confirmation functions for the current object of 81 the given 'handler'. This function continues a transaction that should be 82 finalised using the 'finish_scheduling' function. 83 """ 84 85 # Obtain the actual confirmation functions with arguments. 86 87 functions = get_function_calls(handler.get_scheduling_functions(), confirmation_functions) 88 apply_functions(functions, handler) 89 90 def retract_scheduling(handler): 91 92 """ 93 Retract scheduling using retraction functions for the current object of the 94 given 'handler'. This function is a complete transaction in itself. 95 """ 96 97 # First, lock the resources to be used. 98 99 start_scheduling(handler) 100 101 # Obtain the actual retraction functions with arguments. 102 103 functions = get_function_calls(handler.get_scheduling_functions(), retraction_functions) 104 apply_functions(functions, handler) 105 106 # Finally, unlock the resources. 107 108 finish_scheduling(handler) 109 110 def start_scheduling(handler): 111 112 """ 113 Apply locking functions for the given scheduling 'functions' and for the 114 current object of the given 'handler'. 115 """ 116 117 # Obtain functions to lock resources. 118 119 functions = get_function_calls(handler.get_scheduling_functions(), locking_functions) 120 apply_functions(functions, handler) 121 122 def finish_scheduling(handler): 123 124 """ 125 Finish scheduling using the given scheduling 'functions' for the current 126 object of the given 'handler'. 127 """ 128 129 # Obtain functions to unlock resources. 130 131 functions = get_function_calls(handler.get_scheduling_functions(), unlocking_functions) 132 apply_functions(functions, handler) 133 134 def apply_functions(functions, handler): 135 136 """ 137 Apply the given notification 'functions' for the current object of the given 138 'handler'. Where functions are provided more than once, they will be called 139 only once for each distinct set of arguments. 140 """ 141 142 applied = set() 143 144 for fn, args in functions: 145 146 # NOTE: Should signal an error for incorrectly configured resources. 147 148 if not fn or (fn, args) in applied: 149 continue 150 151 fn(handler, args) 152 applied.add((fn, args)) 153 154 # Function lookup. 155 156 def get_function_calls(lines, registry): 157 158 """ 159 Parse the given 'lines', returning a list of (function, arguments) tuples, 160 with each function being a genuine function object and with the arguments 161 being a list of strings. 162 163 Each of the 'lines' should employ the function name and argument strings 164 separated by whitespace, with any whitespace inside arguments quoted using 165 single or double quotes. 166 167 The given 'registry' indicates the mapping from function names to actual 168 functions. 169 """ 170 171 functions = [] 172 173 for line in lines: 174 parts = parse_line(line) 175 functions.append((registry.get(parts[0]), tuple(parts[1:]))) 176 177 return functions 178 179 # vim: tabstop=4 expandtab shiftwidth=4