1""" 2Interaction with the Supervisor daemon 3====================================== 4 5.. code-block:: yaml 6 7 wsgi_server: 8 supervisord.running: 9 - require: 10 - pkg: supervisor 11 - watch: 12 - file: /etc/nginx/sites-enabled/wsgi_server.conf 13""" 14 15import logging 16 17log = logging.getLogger(__name__) 18 19 20def _check_error(result, success_message): 21 ret = {} 22 23 if "ERROR" in result: 24 if any( 25 substring in result 26 for substring in [ 27 "already started", 28 "not running", 29 "process group already active", 30 ] 31 ): 32 ret["comment"] = success_message 33 else: 34 ret["comment"] = result 35 ret["result"] = False 36 else: 37 ret["comment"] = success_message 38 39 return ret 40 41 42def _is_stopped_state(state): 43 if state in ("STOPPED", "STOPPING", "EXITED", "FATAL", "BACKOFF"): 44 return True 45 if state in ("STARTING", "RUNNING"): 46 return False 47 return False 48 49 50def running( 51 name, restart=False, update=False, user=None, conf_file=None, bin_env=None, **kwargs 52): 53 """ 54 Ensure the named service is running. 55 56 name 57 Service name as defined in the supervisor configuration file 58 59 restart 60 Whether to force a restart 61 62 update 63 Whether to update the supervisor configuration. 64 65 user 66 Name of the user to run the supervisorctl command 67 68 .. versionadded:: 0.17.0 69 70 conf_file 71 path to supervisorctl config file 72 73 bin_env 74 path to supervisorctl bin or path to virtualenv with supervisor 75 installed 76 77 """ 78 if name.endswith(":*"): 79 name = name[:-1] 80 81 ret = {"name": name, "result": True, "comment": "", "changes": {}} 82 83 if "supervisord.status" not in __salt__: 84 ret["result"] = False 85 ret[ 86 "comment" 87 ] = "Supervisord module not activated. Do you need to install supervisord?" 88 return ret 89 90 all_processes = __salt__["supervisord.status"]( 91 user=user, conf_file=conf_file, bin_env=bin_env 92 ) 93 94 # parse process groups 95 process_groups = set() 96 for proc in all_processes: 97 if ":" in proc: 98 process_groups.add(proc[: proc.index(":") + 1]) 99 process_groups = sorted(process_groups) 100 101 matches = {} 102 if name in all_processes: 103 matches[name] = all_processes[name]["state"].lower() == "running" 104 elif name in process_groups: 105 for process in (x for x in all_processes if x.startswith(name)): 106 matches[process] = all_processes[process]["state"].lower() == "running" 107 to_add = not bool(matches) 108 109 if __opts__["test"]: 110 if not to_add: 111 # Process/group already present, check if any need to be started 112 to_start = [x for x, y in matches.items() if y is False] 113 if to_start: 114 ret["result"] = None 115 if name.endswith(":"): 116 # Process group 117 if len(to_start) == len(matches): 118 ret[ 119 "comment" 120 ] = "All services in group '{}' will be started".format(name) 121 else: 122 ret[ 123 "comment" 124 ] = "The following services will be started: {}".format( 125 " ".join(to_start) 126 ) 127 else: 128 # Single program 129 ret["comment"] = "Service {} will be started".format(name) 130 else: 131 if name.endswith(":"): 132 # Process group 133 ret[ 134 "comment" 135 ] = "All services in group '{}' are already running".format(name) 136 else: 137 ret["comment"] = "Service {} is already running".format(name) 138 else: 139 ret["result"] = None 140 # Process/group needs to be added 141 if name.endswith(":"): 142 _type = "Group '{}'".format(name) 143 else: 144 _type = "Service {}".format(name) 145 ret["comment"] = "{} will be added and started".format(_type) 146 return ret 147 148 changes = [] 149 just_updated = False 150 151 if update: 152 # If the state explicitly asks to update, we don't care if the process 153 # is being added or not, since it'll take care of this for us, 154 # so give this condition priority in order 155 # 156 # That is, unless `to_add` somehow manages to contain processes 157 # we don't want running, in which case adding them may be a mistake 158 comment = "Updating supervisor" 159 result = __salt__["supervisord.update"]( 160 user=user, conf_file=conf_file, bin_env=bin_env 161 ) 162 ret.update(_check_error(result, comment)) 163 log.debug(comment) 164 165 if "{}: updated".format(name) in result: 166 just_updated = True 167 elif to_add: 168 # Not sure if this condition is precise enough. 169 comment = "Adding service: {}".format(name) 170 __salt__["supervisord.reread"](user=user, conf_file=conf_file, bin_env=bin_env) 171 # Causes supervisorctl to throw `ERROR: process group already active` 172 # if process group exists. At this moment, I'm not sure how to handle 173 # this outside of grepping out the expected string in `_check_error`. 174 result = __salt__["supervisord.add"]( 175 name, user=user, conf_file=conf_file, bin_env=bin_env 176 ) 177 178 ret.update(_check_error(result, comment)) 179 changes.append(comment) 180 log.debug(comment) 181 182 is_stopped = None 183 184 process_type = None 185 if name in process_groups: 186 process_type = "group" 187 188 # check if any processes in this group are stopped 189 is_stopped = False 190 for proc in all_processes: 191 if proc.startswith(name) and _is_stopped_state( 192 all_processes[proc]["state"] 193 ): 194 is_stopped = True 195 break 196 197 elif name in all_processes: 198 process_type = "service" 199 200 if _is_stopped_state(all_processes[name]["state"]): 201 is_stopped = True 202 else: 203 is_stopped = False 204 205 if is_stopped is False: 206 if restart and not just_updated: 207 comment = "Restarting{}: {}".format( 208 process_type is not None and " {}".format(process_type) or "", name 209 ) 210 log.debug(comment) 211 result = __salt__["supervisord.restart"]( 212 name, user=user, conf_file=conf_file, bin_env=bin_env 213 ) 214 ret.update(_check_error(result, comment)) 215 changes.append(comment) 216 elif just_updated: 217 comment = "Not starting updated{}: {}".format( 218 process_type is not None and " {}".format(process_type) or "", name 219 ) 220 result = comment 221 ret.update({"comment": comment}) 222 else: 223 comment = "Not starting already running{}: {}".format( 224 process_type is not None and " {}".format(process_type) or "", name 225 ) 226 result = comment 227 ret.update({"comment": comment}) 228 229 elif not just_updated: 230 comment = "Starting{}: {}".format( 231 process_type is not None and " {}".format(process_type) or "", name 232 ) 233 changes.append(comment) 234 log.debug(comment) 235 result = __salt__["supervisord.start"]( 236 name, user=user, conf_file=conf_file, bin_env=bin_env 237 ) 238 239 ret.update(_check_error(result, comment)) 240 log.debug(str(result)) 241 242 if ret["result"] and changes: 243 ret["changes"][name] = " ".join(changes) 244 return ret 245 246 247def dead(name, user=None, conf_file=None, bin_env=None, **kwargs): 248 """ 249 Ensure the named service is dead (not running). 250 251 name 252 Service name as defined in the supervisor configuration file 253 254 user 255 Name of the user to run the supervisorctl command 256 257 .. versionadded:: 0.17.0 258 259 conf_file 260 path to supervisorctl config file 261 262 bin_env 263 path to supervisorctl bin or path to virtualenv with supervisor 264 installed 265 266 """ 267 ret = {"name": name, "result": True, "comment": "", "changes": {}} 268 269 if __opts__["test"]: 270 ret["result"] = None 271 ret["comment"] = "Service {} is set to be stopped".format(name) 272 else: 273 comment = "Stopping service: {}".format(name) 274 log.debug(comment) 275 276 all_processes = __salt__["supervisord.status"]( 277 user=user, conf_file=conf_file, bin_env=bin_env 278 ) 279 280 # parse process groups 281 process_groups = [] 282 for proc in all_processes: 283 if ":" in proc: 284 process_groups.append(proc[: proc.index(":") + 1]) 285 process_groups = list(set(process_groups)) 286 287 is_stopped = None 288 289 if name in process_groups: 290 # check if any processes in this group are stopped 291 is_stopped = False 292 for proc in all_processes: 293 if proc.startswith(name) and _is_stopped_state( 294 all_processes[proc]["state"] 295 ): 296 is_stopped = True 297 break 298 299 elif name in all_processes: 300 if _is_stopped_state(all_processes[name]["state"]): 301 is_stopped = True 302 else: 303 is_stopped = False 304 else: 305 # process name doesn't exist 306 ret["comment"] = "Service {} doesn't exist".format(name) 307 return ret 308 309 if is_stopped is True: 310 ret["comment"] = "Service {} is not running".format(name) 311 else: 312 result = { 313 name: __salt__["supervisord.stop"]( 314 name, user=user, conf_file=conf_file, bin_env=bin_env 315 ) 316 } 317 ret.update(_check_error(result, comment)) 318 ret["changes"][name] = comment 319 log.debug(str(result)) 320 return ret 321 322 323def mod_watch( 324 name, restart=True, update=False, user=None, conf_file=None, bin_env=None, **kwargs 325): 326 """ 327 The supervisord watcher, called to invoke the watch command. 328 Always restart on watch 329 330 .. note:: 331 This state exists to support special handling of the ``watch`` 332 :ref:`requisite <requisites>`. It should not be called directly. 333 334 Parameters for this function should be set by the state being triggered. 335 """ 336 return running( 337 name, 338 restart=restart, 339 update=update, 340 user=user, 341 conf_file=conf_file, 342 bin_env=bin_env, 343 ) 344