1""" 2Provide the service module for system supervisord or supervisord in a 3virtualenv 4""" 5 6 7import configparser 8import os 9 10import salt.utils.stringutils 11from salt.exceptions import CommandExecutionError, CommandNotFoundError 12 13 14def __virtual__(): 15 # We can't decide at load time whether supervisorctl is present. The 16 # function _get_supervisorctl_bin does a much more thorough job and can 17 # only be accurate at call time. 18 return True 19 20 21def _get_supervisorctl_bin(bin_env): 22 """ 23 Return supervisorctl command to call, either from a virtualenv, an argument 24 passed in, or from the global modules options 25 """ 26 cmd = "supervisorctl" 27 if not bin_env: 28 which_result = __salt__["cmd.which_bin"]([cmd]) 29 if which_result is None: 30 raise CommandNotFoundError("Could not find a `{}` binary".format(cmd)) 31 return which_result 32 33 # try to get binary from env 34 if os.path.isdir(bin_env): 35 cmd_bin = os.path.join(bin_env, "bin", cmd) 36 if os.path.isfile(cmd_bin): 37 return cmd_bin 38 raise CommandNotFoundError("Could not find a `{}` binary".format(cmd)) 39 40 return bin_env 41 42 43def _ctl_cmd(cmd, name, conf_file, bin_env): 44 """ 45 Return the command list to use 46 """ 47 ret = [_get_supervisorctl_bin(bin_env)] 48 if conf_file is not None: 49 ret += ["-c", conf_file] 50 ret.append(cmd) 51 if name: 52 ret.append(name) 53 return ret 54 55 56def _get_return(ret): 57 retmsg = ret["stdout"] 58 if ret["retcode"] != 0: 59 # This is a non 0 exit code 60 if "ERROR" not in retmsg: 61 retmsg = "ERROR: {}".format(retmsg) 62 return retmsg 63 64 65def start(name="all", user=None, conf_file=None, bin_env=None): 66 """ 67 Start the named service. 68 Process group names should not include a trailing asterisk. 69 70 user 71 user to run supervisorctl as 72 conf_file 73 path to supervisord config file 74 bin_env 75 path to supervisorctl bin or path to virtualenv with supervisor 76 installed 77 78 CLI Example: 79 80 .. code-block:: bash 81 82 salt '*' supervisord.start <service> 83 salt '*' supervisord.start <group>: 84 """ 85 if name.endswith(":*"): 86 name = name[:-1] 87 ret = __salt__["cmd.run_all"]( 88 _ctl_cmd("start", name, conf_file, bin_env), 89 runas=user, 90 python_shell=False, 91 ) 92 return _get_return(ret) 93 94 95def restart(name="all", user=None, conf_file=None, bin_env=None): 96 """ 97 Restart the named service. 98 Process group names should not include a trailing asterisk. 99 100 user 101 user to run supervisorctl as 102 conf_file 103 path to supervisord config file 104 bin_env 105 path to supervisorctl bin or path to virtualenv with supervisor 106 installed 107 108 CLI Example: 109 110 .. code-block:: bash 111 112 salt '*' supervisord.restart <service> 113 salt '*' supervisord.restart <group>: 114 """ 115 if name.endswith(":*"): 116 name = name[:-1] 117 ret = __salt__["cmd.run_all"]( 118 _ctl_cmd("restart", name, conf_file, bin_env), 119 runas=user, 120 python_shell=False, 121 ) 122 return _get_return(ret) 123 124 125def stop(name="all", user=None, conf_file=None, bin_env=None): 126 """ 127 Stop the named service. 128 Process group names should not include a trailing asterisk. 129 130 user 131 user to run supervisorctl as 132 conf_file 133 path to supervisord config file 134 bin_env 135 path to supervisorctl bin or path to virtualenv with supervisor 136 installed 137 138 CLI Example: 139 140 .. code-block:: bash 141 142 salt '*' supervisord.stop <service> 143 salt '*' supervisord.stop <group>: 144 """ 145 if name.endswith(":*"): 146 name = name[:-1] 147 ret = __salt__["cmd.run_all"]( 148 _ctl_cmd("stop", name, conf_file, bin_env), 149 runas=user, 150 python_shell=False, 151 ) 152 return _get_return(ret) 153 154 155def add(name, user=None, conf_file=None, bin_env=None): 156 """ 157 Activates any updates in config for process/group. 158 159 user 160 user to run supervisorctl as 161 conf_file 162 path to supervisord config file 163 bin_env 164 path to supervisorctl bin or path to virtualenv with supervisor 165 installed 166 167 CLI Example: 168 169 .. code-block:: bash 170 171 salt '*' supervisord.add <name> 172 """ 173 if name.endswith(":"): 174 name = name[:-1] 175 elif name.endswith(":*"): 176 name = name[:-2] 177 ret = __salt__["cmd.run_all"]( 178 _ctl_cmd("add", name, conf_file, bin_env), 179 runas=user, 180 python_shell=False, 181 ) 182 return _get_return(ret) 183 184 185def remove(name, user=None, conf_file=None, bin_env=None): 186 """ 187 Removes process/group from active config 188 189 user 190 user to run supervisorctl as 191 conf_file 192 path to supervisord config file 193 bin_env 194 path to supervisorctl bin or path to virtualenv with supervisor 195 installed 196 197 CLI Example: 198 199 .. code-block:: bash 200 201 salt '*' supervisord.remove <name> 202 """ 203 if name.endswith(":"): 204 name = name[:-1] 205 elif name.endswith(":*"): 206 name = name[:-2] 207 ret = __salt__["cmd.run_all"]( 208 _ctl_cmd("remove", name, conf_file, bin_env), 209 runas=user, 210 python_shell=False, 211 ) 212 return _get_return(ret) 213 214 215def reread(user=None, conf_file=None, bin_env=None): 216 """ 217 Reload the daemon's configuration files 218 219 user 220 user to run supervisorctl as 221 conf_file 222 path to supervisord config file 223 bin_env 224 path to supervisorctl bin or path to virtualenv with supervisor 225 installed 226 227 CLI Example: 228 229 .. code-block:: bash 230 231 salt '*' supervisord.reread 232 """ 233 ret = __salt__["cmd.run_all"]( 234 _ctl_cmd("reread", None, conf_file, bin_env), 235 runas=user, 236 python_shell=False, 237 ) 238 return _get_return(ret) 239 240 241def update(user=None, conf_file=None, bin_env=None, name=None): 242 """ 243 Reload config and add/remove/update as necessary 244 245 user 246 user to run supervisorctl as 247 conf_file 248 path to supervisord config file 249 bin_env 250 path to supervisorctl bin or path to virtualenv with supervisor 251 installed 252 name 253 name of the process group to update. if none then update any 254 process group that has changes 255 256 CLI Example: 257 258 .. code-block:: bash 259 260 salt '*' supervisord.update 261 """ 262 263 if isinstance(name, str): 264 if name.endswith(":"): 265 name = name[:-1] 266 elif name.endswith(":*"): 267 name = name[:-2] 268 269 ret = __salt__["cmd.run_all"]( 270 _ctl_cmd("update", name, conf_file, bin_env), 271 runas=user, 272 python_shell=False, 273 ) 274 return _get_return(ret) 275 276 277def status(name=None, user=None, conf_file=None, bin_env=None): 278 """ 279 List programs and its state 280 281 user 282 user to run supervisorctl as 283 conf_file 284 path to supervisord config file 285 bin_env 286 path to supervisorctl bin or path to virtualenv with supervisor 287 installed 288 289 CLI Example: 290 291 .. code-block:: bash 292 293 salt '*' supervisord.status 294 """ 295 all_process = {} 296 for line in status_raw(name, user, conf_file, bin_env).splitlines(): 297 if len(line.split()) > 2: 298 process, state, reason = line.split(None, 2) 299 else: 300 process, state, reason = line.split() + [""] 301 all_process[process] = {"state": state, "reason": reason} 302 return all_process 303 304 305def status_raw(name=None, user=None, conf_file=None, bin_env=None): 306 """ 307 Display the raw output of status 308 309 user 310 user to run supervisorctl as 311 conf_file 312 path to supervisord config file 313 bin_env 314 path to supervisorctl bin or path to virtualenv with supervisor 315 installed 316 317 CLI Example: 318 319 .. code-block:: bash 320 321 salt '*' supervisord.status_raw 322 """ 323 ret = __salt__["cmd.run_all"]( 324 _ctl_cmd("status", name, conf_file, bin_env), 325 runas=user, 326 python_shell=False, 327 ) 328 return _get_return(ret) 329 330 331def custom(command, user=None, conf_file=None, bin_env=None): 332 """ 333 Run any custom supervisord command 334 335 user 336 user to run supervisorctl as 337 conf_file 338 path to supervisord config file 339 bin_env 340 path to supervisorctl bin or path to virtualenv with supervisor 341 installed 342 343 CLI Example: 344 345 .. code-block:: bash 346 347 salt '*' supervisord.custom "mstop '*gunicorn*'" 348 """ 349 ret = __salt__["cmd.run_all"]( 350 _ctl_cmd(command, None, conf_file, bin_env), 351 runas=user, 352 python_shell=False, 353 ) 354 return _get_return(ret) 355 356 357# TODO: try to find a way to use the supervisor python module to read the 358# config information 359def _read_config(conf_file=None): 360 """ 361 Reads the config file using configparser 362 """ 363 if conf_file is None: 364 paths = ("/etc/supervisor/supervisord.conf", "/etc/supervisord.conf") 365 for path in paths: 366 if os.path.exists(path): 367 conf_file = path 368 break 369 if conf_file is None: 370 raise CommandExecutionError("No suitable config file found") 371 config = configparser.ConfigParser() 372 try: 373 config.read(conf_file) 374 except OSError as exc: 375 raise CommandExecutionError("Unable to read from {}: {}".format(conf_file, exc)) 376 return config 377 378 379def options(name, conf_file=None): 380 """ 381 .. versionadded:: 2014.1.0 382 383 Read the config file and return the config options for a given process 384 385 name 386 Name of the configured process 387 conf_file 388 path to supervisord config file 389 390 CLI Example: 391 392 .. code-block:: bash 393 394 salt '*' supervisord.options foo 395 """ 396 config = _read_config(conf_file) 397 section_name = "program:{}".format(name) 398 if section_name not in config.sections(): 399 raise CommandExecutionError("Process '{}' not found".format(name)) 400 ret = {} 401 for key, val in config.items(section_name): 402 val = salt.utils.stringutils.to_num(val.split(";")[0].strip()) 403 # pylint: disable=maybe-no-member 404 if isinstance(val, str): 405 if val.lower() == "true": 406 val = True 407 elif val.lower() == "false": 408 val = False 409 # pylint: enable=maybe-no-member 410 ret[key] = val 411 return ret 412 413 414def status_bool(name, expected_state=None, user=None, conf_file=None, bin_env=None): 415 """ 416 Check for status of a specific supervisord process and return boolean result. 417 418 name 419 name of the process to check 420 421 expected_state 422 search for a specific process state. If set to ``None`` - any process state will match. 423 424 user 425 user to run supervisorctl as 426 427 conf_file 428 path to supervisord config file 429 430 bin_env 431 path to supervisorctl bin or path to virtualenv with supervisor 432 installed 433 434 CLI Example: 435 436 .. code-block:: bash 437 438 salt '*' supervisord.status_bool nginx expected_state='RUNNING' 439 """ 440 441 cmd = "status {}".format(name) 442 for line in custom(cmd, user, conf_file, bin_env).splitlines(): 443 if len(line.split()) > 2: 444 process, state, reason = line.split(None, 2) 445 else: 446 process, state, reason = line.split() + [""] 447 448 if reason == "(no such process)" or process != name: 449 return False 450 451 if expected_state is None or state == expected_state: 452 return True 453 else: 454 return False 455