1"""
2Control Modjk via the Apache Tomcat "Status" worker
3(http://tomcat.apache.org/connectors-doc/reference/status.html)
4
5Below is an example of the configuration needed for this module. This
6configuration data can be placed either in :ref:`grains
7<targeting-grains>` or :ref:`pillar <salt-pillars>`.
8
9If using grains, this can be accomplished :ref:`statically
10<static-custom-grains>` or via a :ref:`grain module <writing-grains>`.
11
12If using pillar, the yaml configuration can be placed directly into a pillar
13SLS file, making this both the easier and more dynamic method of configuring
14this module.
15
16.. code-block:: yaml
17
18    modjk:
19      default:
20        url: http://localhost/jkstatus
21        user: modjk
22        pass: secret
23        realm: authentication realm for digest passwords
24        timeout: 5
25      otherVhost:
26        url: http://otherVhost/jkstatus
27        user: modjk
28        pass: secret2
29        realm: authentication realm2 for digest passwords
30        timeout: 600
31"""
32import urllib.parse
33import urllib.request
34
35
36def __virtual__():
37    """
38    Always load
39    """
40    return True
41
42
43def _auth(url, user, passwd, realm):
44    """
45    returns a authentication handler.
46    """
47
48    basic = urllib.request.HTTPBasicAuthHandler()
49    basic.add_password(realm=realm, uri=url, user=user, passwd=passwd)
50    digest = urllib.request.HTTPDigestAuthHandler()
51    digest.add_password(realm=realm, uri=url, user=user, passwd=passwd)
52    return urllib.request.build_opener(basic, digest)
53
54
55def _do_http(opts, profile="default"):
56    """
57    Make the http request and return the data
58    """
59
60    ret = {}
61
62    url = __salt__["config.get"]("modjk:{}:url".format(profile), "")
63    user = __salt__["config.get"]("modjk:{}:user".format(profile), "")
64    passwd = __salt__["config.get"]("modjk:{}:pass".format(profile), "")
65    realm = __salt__["config.get"]("modjk:{}:realm".format(profile), "")
66    timeout = __salt__["config.get"]("modjk:{}:timeout".format(profile), "")
67
68    if not url:
69        raise Exception("missing url in profile {}".format(profile))
70
71    if user and passwd:
72        auth = _auth(url=url, realm=realm, user=user, passwd=passwd)
73        urllib.request.install_opener(auth)
74
75    url += "?{}".format(urllib.parse.urlencode(opts))
76
77    for line in urllib.request.urlopen(url, timeout=timeout).read().splitlines():
78        splt = line.split("=", 1)
79        if splt[0] in ret:
80            ret[splt[0]] += ",{}".format(splt[1])
81        else:
82            ret[splt[0]] = splt[1]
83
84    return ret
85
86
87def _worker_ctl(worker, lbn, vwa, profile="default"):
88    """
89    enable/disable/stop a worker
90    """
91
92    cmd = {
93        "cmd": "update",
94        "mime": "prop",
95        "w": lbn,
96        "sw": worker,
97        "vwa": vwa,
98    }
99    return _do_http(cmd, profile)["worker.result.type"] == "OK"
100
101
102def version(profile="default"):
103    """
104    Return the modjk version
105
106    CLI Examples:
107
108    .. code-block:: bash
109
110        salt '*' modjk.version
111        salt '*' modjk.version other-profile
112    """
113
114    cmd = {
115        "cmd": "version",
116        "mime": "prop",
117    }
118    return _do_http(cmd, profile)["worker.jk_version"].split("/")[-1]
119
120
121def get_running(profile="default"):
122    """
123    Get the current running config (not from disk)
124
125    CLI Examples:
126
127    .. code-block:: bash
128
129        salt '*' modjk.get_running
130        salt '*' modjk.get_running other-profile
131    """
132
133    cmd = {
134        "cmd": "list",
135        "mime": "prop",
136    }
137    return _do_http(cmd, profile)
138
139
140def dump_config(profile="default"):
141    """
142    Dump the original configuration that was loaded from disk
143
144    CLI Examples:
145
146    .. code-block:: bash
147
148        salt '*' modjk.dump_config
149        salt '*' modjk.dump_config other-profile
150    """
151
152    cmd = {
153        "cmd": "dump",
154        "mime": "prop",
155    }
156    return _do_http(cmd, profile)
157
158
159def list_configured_members(lbn, profile="default"):
160    """
161    Return a list of member workers from the configuration files
162
163    CLI Examples:
164
165    .. code-block:: bash
166
167        salt '*' modjk.list_configured_members loadbalancer1
168        salt '*' modjk.list_configured_members loadbalancer1 other-profile
169    """
170
171    config = dump_config(profile)
172
173    try:
174        ret = config["worker.{}.balance_workers".format(lbn)]
175    except KeyError:
176        return []
177
178    return [_f for _f in ret.strip().split(",") if _f]
179
180
181def workers(profile="default"):
182    """
183    Return a list of member workers and their status
184
185    CLI Examples:
186
187    .. code-block:: bash
188
189        salt '*' modjk.workers
190        salt '*' modjk.workers other-profile
191    """
192
193    config = get_running(profile)
194    lbn = config["worker.list"].split(",")
195    worker_list = []
196    ret = {}
197
198    for lb in lbn:
199        try:
200            worker_list.extend(
201                config["worker.{}.balance_workers".format(lb)].split(",")
202            )
203        except KeyError:
204            pass
205
206    worker_list = list(set(worker_list))
207
208    for worker in worker_list:
209        ret[worker] = {
210            "activation": config["worker.{}.activation".format(worker)],
211            "state": config["worker.{}.state".format(worker)],
212        }
213
214    return ret
215
216
217def recover_all(lbn, profile="default"):
218    """
219    Set the all the workers in lbn to recover and activate them if they are not
220
221    CLI Examples:
222
223    .. code-block:: bash
224
225        salt '*' modjk.recover_all loadbalancer1
226        salt '*' modjk.recover_all loadbalancer1 other-profile
227    """
228
229    ret = {}
230    config = get_running(profile)
231    try:
232        workers_ = config["worker.{}.balance_workers".format(lbn)].split(",")
233    except KeyError:
234        return ret
235
236    for worker in workers_:
237        curr_state = worker_status(worker, profile)
238        if curr_state["activation"] != "ACT":
239            worker_activate(worker, lbn, profile)
240        if not curr_state["state"].startswith("OK"):
241            worker_recover(worker, lbn, profile)
242        ret[worker] = worker_status(worker, profile)
243
244    return ret
245
246
247def reset_stats(lbn, profile="default"):
248    """
249    Reset all runtime statistics for the load balancer
250
251    CLI Examples:
252
253    .. code-block:: bash
254
255        salt '*' modjk.reset_stats loadbalancer1
256        salt '*' modjk.reset_stats loadbalancer1 other-profile
257    """
258
259    cmd = {
260        "cmd": "reset",
261        "mime": "prop",
262        "w": lbn,
263    }
264    return _do_http(cmd, profile)["worker.result.type"] == "OK"
265
266
267def lb_edit(lbn, settings, profile="default"):
268    """
269    Edit the loadbalancer settings
270
271    Note: http://tomcat.apache.org/connectors-doc/reference/status.html
272    Data Parameters for the standard Update Action
273
274    CLI Examples:
275
276    .. code-block:: bash
277
278        salt '*' modjk.lb_edit loadbalancer1 "{'vlr': 1, 'vlt': 60}"
279        salt '*' modjk.lb_edit loadbalancer1 "{'vlr': 1, 'vlt': 60}" other-profile
280    """
281
282    settings["cmd"] = "update"
283    settings["mime"] = "prop"
284    settings["w"] = lbn
285
286    return _do_http(settings, profile)["worker.result.type"] == "OK"
287
288
289def bulk_stop(workers, lbn, profile="default"):
290    """
291    Stop all the given workers in the specific load balancer
292
293    CLI Examples:
294
295    .. code-block:: bash
296
297        salt '*' modjk.bulk_stop node1,node2,node3 loadbalancer1
298        salt '*' modjk.bulk_stop node1,node2,node3 loadbalancer1 other-profile
299
300        salt '*' modjk.bulk_stop ["node1","node2","node3"] loadbalancer1
301        salt '*' modjk.bulk_stop ["node1","node2","node3"] loadbalancer1 other-profile
302    """
303
304    ret = {}
305
306    if isinstance(workers, str):
307        workers = workers.split(",")
308
309    for worker in workers:
310        try:
311            ret[worker] = worker_stop(worker, lbn, profile)
312        except Exception:  # pylint: disable=broad-except
313            ret[worker] = False
314
315    return ret
316
317
318def bulk_activate(workers, lbn, profile="default"):
319    """
320    Activate all the given workers in the specific load balancer
321
322    CLI Examples:
323
324    .. code-block:: bash
325
326        salt '*' modjk.bulk_activate node1,node2,node3 loadbalancer1
327        salt '*' modjk.bulk_activate node1,node2,node3 loadbalancer1 other-profile
328
329        salt '*' modjk.bulk_activate ["node1","node2","node3"] loadbalancer1
330        salt '*' modjk.bulk_activate ["node1","node2","node3"] loadbalancer1 other-profile
331    """
332
333    ret = {}
334
335    if isinstance(workers, str):
336        workers = workers.split(",")
337
338    for worker in workers:
339        try:
340            ret[worker] = worker_activate(worker, lbn, profile)
341        except Exception:  # pylint: disable=broad-except
342            ret[worker] = False
343
344    return ret
345
346
347def bulk_disable(workers, lbn, profile="default"):
348    """
349    Disable all the given workers in the specific load balancer
350
351    CLI Examples:
352
353    .. code-block:: bash
354
355        salt '*' modjk.bulk_disable node1,node2,node3 loadbalancer1
356        salt '*' modjk.bulk_disable node1,node2,node3 loadbalancer1 other-profile
357
358        salt '*' modjk.bulk_disable ["node1","node2","node3"] loadbalancer1
359        salt '*' modjk.bulk_disable ["node1","node2","node3"] loadbalancer1 other-profile
360    """
361
362    ret = {}
363
364    if isinstance(workers, str):
365        workers = workers.split(",")
366
367    for worker in workers:
368        try:
369            ret[worker] = worker_disable(worker, lbn, profile)
370        except Exception:  # pylint: disable=broad-except
371            ret[worker] = False
372
373    return ret
374
375
376def bulk_recover(workers, lbn, profile="default"):
377    """
378    Recover all the given workers in the specific load balancer
379
380    CLI Examples:
381
382    .. code-block:: bash
383
384        salt '*' modjk.bulk_recover node1,node2,node3 loadbalancer1
385        salt '*' modjk.bulk_recover node1,node2,node3 loadbalancer1 other-profile
386
387        salt '*' modjk.bulk_recover ["node1","node2","node3"] loadbalancer1
388        salt '*' modjk.bulk_recover ["node1","node2","node3"] loadbalancer1 other-profile
389    """
390
391    ret = {}
392
393    if isinstance(workers, str):
394        workers = workers.split(",")
395
396    for worker in workers:
397        try:
398            ret[worker] = worker_recover(worker, lbn, profile)
399        except Exception:  # pylint: disable=broad-except
400            ret[worker] = False
401
402    return ret
403
404
405def worker_status(worker, profile="default"):
406    """
407    Return the state of the worker
408
409    CLI Examples:
410
411    .. code-block:: bash
412
413        salt '*' modjk.worker_status node1
414        salt '*' modjk.worker_status node1 other-profile
415    """
416
417    config = get_running(profile)
418    try:
419        return {
420            "activation": config["worker.{}.activation".format(worker)],
421            "state": config["worker.{}.state".format(worker)],
422        }
423    except KeyError:
424        return False
425
426
427def worker_recover(worker, lbn, profile="default"):
428    """
429    Set the worker to recover
430    this module will fail if it is in OK state
431
432    CLI Examples:
433
434    .. code-block:: bash
435
436        salt '*' modjk.worker_recover node1 loadbalancer1
437        salt '*' modjk.worker_recover node1 loadbalancer1 other-profile
438    """
439
440    cmd = {
441        "cmd": "recover",
442        "mime": "prop",
443        "w": lbn,
444        "sw": worker,
445    }
446    return _do_http(cmd, profile)
447
448
449def worker_disable(worker, lbn, profile="default"):
450    """
451    Set the worker to disable state in the lbn load balancer
452
453    CLI Examples:
454
455    .. code-block:: bash
456
457        salt '*' modjk.worker_disable node1 loadbalancer1
458        salt '*' modjk.worker_disable node1 loadbalancer1 other-profile
459    """
460
461    return _worker_ctl(worker, lbn, "d", profile)
462
463
464def worker_activate(worker, lbn, profile="default"):
465    """
466    Set the worker to activate state in the lbn load balancer
467
468    CLI Examples:
469
470    .. code-block:: bash
471
472        salt '*' modjk.worker_activate node1 loadbalancer1
473        salt '*' modjk.worker_activate node1 loadbalancer1 other-profile
474    """
475
476    return _worker_ctl(worker, lbn, "a", profile)
477
478
479def worker_stop(worker, lbn, profile="default"):
480    """
481    Set the worker to stopped state in the lbn load balancer
482
483    CLI Examples:
484
485    .. code-block:: bash
486
487        salt '*' modjk.worker_activate node1 loadbalancer1
488        salt '*' modjk.worker_activate node1 loadbalancer1 other-profile
489    """
490
491    return _worker_ctl(worker, lbn, "s", profile)
492
493
494def worker_edit(worker, lbn, settings, profile="default"):
495    """
496    Edit the worker settings
497
498    Note: http://tomcat.apache.org/connectors-doc/reference/status.html
499    Data Parameters for the standard Update Action
500
501    CLI Examples:
502
503    .. code-block:: bash
504
505        salt '*' modjk.worker_edit node1 loadbalancer1 "{'vwf': 500, 'vwd': 60}"
506        salt '*' modjk.worker_edit node1 loadbalancer1 "{'vwf': 500, 'vwd': 60}" other-profile
507    """
508
509    settings["cmd"] = "update"
510    settings["mime"] = "prop"
511    settings["w"] = lbn
512    settings["sw"] = worker
513
514    return _do_http(settings, profile)["worker.result.type"] == "OK"
515