1import hashlib 2import logging 3import os 4 5import salt.auth 6from salt.utils.versions import StrictVersion as _StrictVersion 7 8__virtualname__ = os.path.abspath(__file__).rsplit(os.sep)[-2] or "rest_tornado" 9 10log = logging.getLogger(__virtualname__) 11 12# we require at least 4.0, as that includes all the Future's stuff we use 13min_tornado_version = "4.0" 14has_tornado = False 15try: 16 import salt.ext.tornado 17 18 if _StrictVersion(salt.ext.tornado.version) >= _StrictVersion(min_tornado_version): 19 has_tornado = True 20 else: 21 log.error("rest_tornado requires at least tornado %s", min_tornado_version) 22except (ImportError, TypeError) as err: 23 has_tornado = False 24 log.error("ImportError! %s", err) 25 26 27def __virtual__(): 28 mod_opts = __opts__.get(__virtualname__, {}) 29 30 if has_tornado and "port" in mod_opts: 31 return __virtualname__ 32 33 return False 34 35 36def get_application(opts): 37 try: 38 from . import saltnado 39 except ImportError as err: 40 log.error("ImportError! %s", err) 41 return None 42 43 mod_opts = opts.get(__virtualname__, {}) 44 45 paths = [ 46 (r"/", saltnado.SaltAPIHandler), 47 (r"/login", saltnado.SaltAuthHandler), 48 (r"/minions/(.*)", saltnado.MinionSaltAPIHandler), 49 (r"/minions", saltnado.MinionSaltAPIHandler), 50 (r"/jobs/(.*)", saltnado.JobsSaltAPIHandler), 51 (r"/jobs", saltnado.JobsSaltAPIHandler), 52 (r"/run", saltnado.RunSaltAPIHandler), 53 (r"/events", saltnado.EventsSaltAPIHandler), 54 (r"/hook(/.*)?", saltnado.WebhookSaltAPIHandler), 55 ] 56 57 # if you have enabled websockets, add them! 58 if mod_opts.get("websockets", False): 59 from . import saltnado_websockets 60 61 token_pattern = r"([0-9A-Fa-f]{{{0}}})".format( 62 len(getattr(hashlib, opts.get("hash_type", "md5"))().hexdigest()) 63 ) 64 all_events_pattern = r"/all_events/{}".format(token_pattern) 65 formatted_events_pattern = r"/formatted_events/{}".format(token_pattern) 66 log.debug("All events URL pattern is %s", all_events_pattern) 67 paths += [ 68 # Matches /all_events/[0-9A-Fa-f]{n} 69 # Where n is the length of hexdigest 70 # for the current hashing algorithm. 71 # This algorithm is specified in the 72 # salt master config file. 73 (all_events_pattern, saltnado_websockets.AllEventsHandler), 74 (formatted_events_pattern, saltnado_websockets.FormattedEventsHandler), 75 ] 76 77 application = salt.ext.tornado.web.Application( 78 paths, debug=mod_opts.get("debug", False) 79 ) 80 81 application.opts = opts 82 application.mod_opts = mod_opts 83 application.auth = salt.auth.LoadAuth(opts) 84 return application 85 86 87def start(): 88 """ 89 Start the saltnado! 90 """ 91 mod_opts = __opts__.get(__virtualname__, {}) 92 93 if "num_processes" not in mod_opts: 94 mod_opts["num_processes"] = 1 95 96 if mod_opts["num_processes"] > 1 and mod_opts.get("debug", False) is True: 97 raise Exception( 98 "Tornado's debug implementation is not compatible with multiprocess. " 99 "Either disable debug, or set num_processes to 1." 100 ) 101 102 # the kwargs for the HTTPServer 103 kwargs = {} 104 if not mod_opts.get("disable_ssl", False): 105 if "ssl_crt" not in mod_opts: 106 log.error( 107 "Not starting '%s'. Options 'ssl_crt' and " 108 "'ssl_key' are required if SSL is not disabled.", 109 __name__, 110 ) 111 112 return None 113 # cert is required, key may be optional 114 # https://docs.python.org/2/library/ssl.html#ssl.wrap_socket 115 ssl_opts = {"certfile": mod_opts["ssl_crt"]} 116 if mod_opts.get("ssl_key", False): 117 ssl_opts.update({"keyfile": mod_opts["ssl_key"]}) 118 kwargs["ssl_options"] = ssl_opts 119 120 import salt.ext.tornado.httpserver 121 122 http_server = salt.ext.tornado.httpserver.HTTPServer( 123 get_application(__opts__), **kwargs 124 ) 125 try: 126 http_server.bind( 127 mod_opts["port"], 128 address=mod_opts.get("address"), 129 backlog=mod_opts.get("backlog", 128), 130 ) 131 http_server.start(mod_opts["num_processes"]) 132 except Exception: # pylint: disable=broad-except 133 log.error( 134 "Rest_tornado unable to bind to port %s", mod_opts["port"], exc_info=True 135 ) 136 raise SystemExit(1) 137 138 try: 139 salt.ext.tornado.ioloop.IOLoop.current().start() 140 except KeyboardInterrupt: 141 raise SystemExit(0) 142