puppet node classifier
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

213 lines
7.2 KiB

  1. import os
  2. import cherrypy
  3. import logging
  4. from datetime import datetime, timedelta
  5. from nodepupper.nodeops import NodeOps, NObject, NClass, NClassAttachment
  6. from jinja2 import Environment, FileSystemLoader, select_autoescape
  7. from nodepupper.common import pwhash
  8. import math
  9. from urllib.parse import urlparse
  10. APPROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))
  11. def auth():
  12. """
  13. Return the currently authorized username (per request) or None
  14. """
  15. return cherrypy.session.get('authed', None)
  16. def require_auth(func):
  17. """
  18. Decorator: raise 403 unless session is authed
  19. """
  20. def wrapped(*args, **kwargs):
  21. if not auth():
  22. raise cherrypy.HTTPError(403)
  23. return func(*args, **kwargs)
  24. return wrapped
  25. def slugify(words):
  26. return ''.join(letter for letter in '-'.join(words.lower().split())
  27. if ('a' <= letter <= 'z') or ('0' <= letter <= '9') or letter == '-')
  28. class AppWeb(object):
  29. def __init__(self, nodedb, template_dir):
  30. self.nodes = nodedb
  31. self.tpl = Environment(loader=FileSystemLoader(template_dir),
  32. autoescape=select_autoescape(['html', 'xml']))
  33. self.tpl.filters.update(basename=os.path.basename,
  34. ceil=math.ceil,
  35. statusstr=lambda x: str(x).split(".")[-1])
  36. self.node = NodesWeb(self)
  37. self.classes = ClassWeb(self)
  38. def render(self, template, **kwargs):
  39. """
  40. Render a template
  41. """
  42. return self.tpl.get_template(template).render(**kwargs, **self.get_default_vars())
  43. def get_default_vars(self):
  44. """
  45. Return a dict containing variables expected to be on every page
  46. """
  47. with self.nodes.db.transaction() as c:
  48. ret = {
  49. "classes": c.root.classes,
  50. # "all_albums": [],
  51. "path": cherrypy.request.path_info,
  52. "auth": True or auth()
  53. }
  54. return ret
  55. @cherrypy.expose
  56. def node_edit(self, node=None, op=None, body=None, fqdn=None):
  57. print(op, body, fqdn)
  58. if op in ("Edit", "Create") and body and fqdn:
  59. with self.nodes.db.transaction() as c:
  60. obj = c.root.nodes[fqdn] if fqdn in c.root.nodes else NObject(fqdn, body)
  61. obj.body = body
  62. c.root.nodes[fqdn] = obj
  63. raise cherrypy.HTTPRedirect("node/{}".format(fqdn), 302)
  64. with self.nodes.db.transaction() as c:
  65. yield self.render("node_edit.html", node=c.root.nodes.get(node, None))
  66. @cherrypy.expose
  67. def index(self):
  68. """
  69. """
  70. with self.nodes.db.transaction() as c:
  71. yield self.render("nodes.html", nodes=c.root.nodes.values())
  72. # raise cherrypy.HTTPRedirect('feed', 302)
  73. @cherrypy.expose
  74. def login(self):
  75. """
  76. /login - enable super features by logging into the app
  77. """
  78. cherrypy.session['authed'] = cherrypy.request.login
  79. dest = "/feed" if "Referer" not in cherrypy.request.headers \
  80. else urlparse(cherrypy.request.headers["Referer"]).path
  81. raise cherrypy.HTTPRedirect(dest, 302)
  82. @cherrypy.expose
  83. def logout(self):
  84. """
  85. /logout
  86. """
  87. cherrypy.session.clear()
  88. dest = "/feed" if "Referer" not in cherrypy.request.headers \
  89. else urlparse(cherrypy.request.headers["Referer"]).path
  90. raise cherrypy.HTTPRedirect(dest, 302)
  91. @cherrypy.expose
  92. def error(self, status, message, traceback, version):
  93. yield self.render("error.html", status=status, message=message, traceback=traceback)
  94. @cherrypy.popargs("node")
  95. class NodesWeb(object):
  96. def __init__(self, root):
  97. self.root = root
  98. self.nodes = root.nodes
  99. self.render = root.render
  100. @cherrypy.expose
  101. def index(self, node):
  102. with self.nodes.db.transaction() as c:
  103. yield self.render("node.html", node=c.root.nodes[node])
  104. @cherrypy.popargs("cls")
  105. class ClassWeb(object):
  106. def __init__(self, root):
  107. self.root = root
  108. self.nodes = root.nodes
  109. self.render = root.render
  110. @cherrypy.expose
  111. def index(self, cls):
  112. # with self.nodes.db.transaction() as c:
  113. yield self.render("classes.html")
  114. @cherrypy.expose
  115. def op(self, cls, op=None, name=None):
  116. # with self.nodes.db.transaction() as c:
  117. yield self.render("classes.html")
  118. def main():
  119. import argparse
  120. import signal
  121. parser = argparse.ArgumentParser(description="Photod photo server")
  122. parser.add_argument('-p', '--port', default=8080, type=int, help="tcp port to listen on")
  123. # parser.add_argument('-l', '--library', default="./library", help="library path")
  124. # parser.add_argument('-c', '--cache', default="./cache", help="cache path")
  125. parser.add_argument('-s', '--database', default="./pupper.db", help="path to persistent sqlite database")
  126. parser.add_argument('--debug', action="store_true", help="enable development options")
  127. args = parser.parse_args()
  128. logging.basicConfig(level=logging.INFO if args.debug else logging.WARNING,
  129. format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s")
  130. library = NodeOps(args.database)
  131. tpl_dir = os.path.join(APPROOT, "templates") if not args.debug else "templates"
  132. web = AppWeb(library, tpl_dir)
  133. def validate_password(realm, username, password):
  134. s = library.session()
  135. if s.query(User).filter(User.name == username, User.password == pwhash(password)).first():
  136. return True
  137. return False
  138. cherrypy.tree.mount(web, '/', {'/': {'tools.trailing_slash.on': False,
  139. 'error_page.403': web.error,
  140. 'error_page.404': web.error},
  141. '/static': {"tools.staticdir.on": True,
  142. "tools.staticdir.dir": os.path.join(APPROOT, "styles/dist")
  143. if not args.debug else os.path.abspath("styles/dist")},
  144. '/login': {'tools.auth_basic.on': True,
  145. 'tools.auth_basic.realm': 'webapp',
  146. 'tools.auth_basic.checkpassword': validate_password}})
  147. cherrypy.config.update({
  148. 'tools.sessions.on': True,
  149. 'tools.sessions.locking': 'explicit',
  150. 'tools.sessions.timeout': 525600,
  151. 'request.show_tracebacks': True,
  152. 'server.socket_port': args.port,
  153. 'server.thread_pool': 25,
  154. 'server.socket_host': '0.0.0.0',
  155. 'server.show_tracebacks': True,
  156. 'log.screen': False,
  157. 'engine.autoreload.on': args.debug
  158. })
  159. def signal_handler(signum, stack):
  160. logging.critical('Got sig {}, exiting...'.format(signum))
  161. cherrypy.engine.exit()
  162. signal.signal(signal.SIGINT, signal_handler)
  163. signal.signal(signal.SIGTERM, signal_handler)
  164. try:
  165. cherrypy.engine.start()
  166. cherrypy.engine.block()
  167. finally:
  168. logging.info("API has shut down")
  169. cherrypy.engine.exit()
  170. if __name__ == '__main__':
  171. main()