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'. 36 """ 37 38 # Obtain the actual scheduling functions with arguments. 39 # Also obtain functions to lock resources. 40 41 schedulers = get_function_calls(functions, scheduling_functions) 42 locks = get_function_calls(functions, locking_functions) 43 44 # First, lock the resources to be used. 45 46 for fn, args in locks: 47 48 # Not all scheduling functions require compound locking. 49 50 if fn: 51 fn(handler, args) 52 53 # Then, invoke the scheduling functions. 54 55 response = "ACCEPTED" 56 57 for fn, args in schedulers: 58 59 # NOTE: Should signal an error for incorrectly configured resources. 60 61 if not fn: 62 return "DECLINED" 63 64 # Keep evaluating scheduling functions, stopping only if one 65 # declines or gives a null response. 66 67 else: 68 result = fn(handler, args) 69 70 # Return a negative result immediately. 71 72 if not result or result == "DECLINED": 73 return result 74 75 # Modify the eventual response from acceptance if a countering 76 # result is obtained. 77 78 elif response == "ACCEPTED": 79 response = result 80 81 return response 82 83 def confirm_scheduling(functions, handler): 84 85 """ 86 Confirm scheduling using the given listener 'functions' for the current 87 object of the given 'handler'. 88 """ 89 90 # Obtain the actual listener functions with arguments. 91 92 functions = get_function_calls(functions, confirmation_functions) 93 apply_functions(functions, handler) 94 95 def finish_scheduling(functions, handler): 96 97 """ 98 Finish scheduling using the given scheduling 'functions' for the current 99 object of the given 'handler'. 100 """ 101 102 # Obtain functions to unlock resources. 103 104 locks = get_function_calls(functions, unlocking_functions) 105 106 # Unlock the resources that were used. 107 108 for fn, args in locks: 109 110 # Not all scheduling functions require compound locking. 111 112 if fn: 113 fn(handler, args) 114 115 def retract_scheduling(functions, handler): 116 117 """ 118 Retract scheduling using the given listener 'functions' for the current 119 object of the given 'handler'. 120 """ 121 122 # Obtain the actual listener functions with arguments. 123 124 functions = get_function_calls(functions, retraction_functions) 125 apply_functions(functions, handler) 126 127 def apply_functions(functions, handler): 128 129 """ 130 Apply the given notification 'functions' for the current object of the given 131 'handler'. 132 """ 133 134 for fn, args in functions: 135 136 # NOTE: Should signal an error for incorrectly configured resources. 137 138 if not fn: 139 continue 140 141 fn(handler, args) 142 143 # Function lookup. 144 145 def get_function_calls(lines, registry): 146 147 """ 148 Parse the given 'lines', returning a list of (function, arguments) tuples, 149 with each function being a genuine function object and with the arguments 150 being a list of strings. 151 152 Each of the 'lines' should employ the function name and argument strings 153 separated by whitespace, with any whitespace inside arguments quoted using 154 single or double quotes. 155 156 The given 'registry' indicates the mapping from function names to actual 157 functions. 158 """ 159 160 functions = [] 161 162 for line in lines: 163 parts = parse_line(line) 164 functions.append((registry.get(parts[0]), parts[1:])) 165 166 return functions 167 168 # vim: tabstop=4 expandtab shiftwidth=4