Compare commits

...

No commits in common. "3cb8bacb31e250d7d228335c92bf138727c91a6f" and "444cca48de6c5b366f941d492eceed901d5f048f" have entirely different histories.

5 changed files with 83 additions and 116 deletions

View File

@ -3,11 +3,28 @@
import ZODB
import ZODB.FileStorage
# def main():
# storage = ZODB.FileStorage.FileStorage("pupper.db")
# db = ZODB.DB(storage)
# for k, v in db.open().root.nodes.items():
# print(k, v.name, ":", v, "\n\t", v.body, "\n")
# def main():
# storage = ZODB.FileStorage.FileStorage("pupper.db")
# db = ZODB.DB(storage)
# for k, v in db.open().root.nodes["foo2"].classes.items():
# # print(k, v.name, ":", v, "\n\t", v.body, "\n")
# print(v.conf)
def main():
storage = ZODB.FileStorage.FileStorage("pupper.db")
db = ZODB.DB(storage)
for k, v in db.open().root.nodes.items():
print(k, v.name, ":", v, "\n\t", v.body, "\n")
with db.transaction() as c:
del c.root.nodes["puppettest5.scc.net.davepedu.com"].classes["base2"]
# for k, v in db.open().root.nodes["foo2"].classes.items():
# # print(k, v.name, ":", v, "\n\t", v.body, "\n")
# print(v.conf
if __name__ == "__main__":
main()

View File

@ -7,6 +7,7 @@ from jinja2 import Environment, FileSystemLoader, select_autoescape
from nodepupper.common import pwhash
import math
from urllib.parse import urlparse
import yaml
APPROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))
@ -58,7 +59,7 @@ class AppWeb(object):
"""
with self.nodes.db.transaction() as c:
ret = {
"classes": c.root.classes,
"classnames": c.root.classes.keys(),
# "all_albums": [],
"path": cherrypy.request.path_info,
"auth": True or auth()
@ -67,7 +68,6 @@ class AppWeb(object):
@cherrypy.expose
def node_edit(self, node=None, op=None, body=None, fqdn=None):
print(op, body, fqdn)
if op in ("Edit", "Create") and body and fqdn:
with self.nodes.db.transaction() as c:
obj = c.root.nodes[fqdn] if fqdn in c.root.nodes else NObject(fqdn, body)
@ -85,6 +85,20 @@ class AppWeb(object):
yield self.render("nodes.html", nodes=c.root.nodes.values())
# raise cherrypy.HTTPRedirect('feed', 302)
@cherrypy.expose
def puppet(self, fqdn):
with self.nodes.db.transaction() as c:
node = c.root.nodes[fqdn]
doc = {"environment": "production",
"classes": {k: yaml.load(v.conf) or {} for k, v in node.classes.items()},
"parameters": {}}
for name, attachment in node.classes.items():
print(name)
yield "---\n"
yield yaml.dump(doc, default_flow_style=False)
@cherrypy.expose
def login(self):
"""
@ -122,6 +136,13 @@ class NodesWeb(object):
with self.nodes.db.transaction() as c:
yield self.render("node.html", node=c.root.nodes[node])
@cherrypy.expose
def op(self, node, op, clsname, config):
with self.nodes.db.transaction() as c:
# TODO validate yaml
c.root.nodes[node].classes[clsname] = NClassAttachment(c.root.classes[clsname], config)
raise cherrypy.HTTPRedirect("/node/{}".format(node), 302)
@cherrypy.popargs("cls")
class ClassWeb(object):
@ -131,7 +152,7 @@ class ClassWeb(object):
self.render = root.render
@cherrypy.expose
def index(self, cls):
def index(self, cls=None):
# with self.nodes.db.transaction() as c:
yield self.render("classes.html")
@ -140,6 +161,14 @@ class ClassWeb(object):
# with self.nodes.db.transaction() as c:
yield self.render("classes.html")
@cherrypy.expose
def add(self, op, name):
with self.nodes.db.transaction() as c:
if op == "Create":
if name not in c.root.classes:
c.root.classes[name] = NClass(name)
raise cherrypy.HTTPRedirect("/classes/{}".format(name), 302)
def main():
import argparse

View File

@ -15,7 +15,6 @@ def pmap():
class NObject(persistent.Persistent):
def __init__(self, fqdn, body):
self.fqdn = fqdn
self.parents = plist()
@ -24,13 +23,11 @@ class NObject(persistent.Persistent):
class NClass(persistent.Persistent):
def __init__(self, name):
self.name = name
class NClassAttachment(persistent.Persistent):
def __init__(self, cls, conf):
self.cls = cls
self.conf = conf
@ -45,101 +42,4 @@ class NodeOps(object):
if "nodes" not in c.root():
c.root.nodes = BTrees.OOBTree.BTree()
if "classes" not in c.root():
c.root.classes = persistent.list.PersistentList()
# def add_photoset(self, photoset):
# """
# Commit a populated photoset object to the library. The paths in the photoset's file list entries will be updated
# as the file is moved to the library path.
# """
# # Create target directory
# path = os.path.join(self.path, self.get_datedir_path(photoset.date))
# os.makedirs(path, exist_ok=True)
# moves = [] # Track files moved. If the sql transaction files, we'll undo these
# for file in photoset.files:
# dest = os.path.join(path, os.path.basename(file.path))
# # Check if the name is already in use, rename new file if needed
# dupe_rename = 1
# while os.path.exists(dest):
# fname = os.path.basename(file.path).split(".")
# fname[-2] += "_{}".format(dupe_rename)
# dest = os.path.join(path, '.'.join(fname))
# dupe_rename += 1
# os.rename(file.path, dest)
# moves.append((file.path, dest))
# file.path = dest.lstrip(self.path)
# s = self.session()
# s.add(photoset)
# try:
# s.commit()
# except IntegrityError:
# # Commit failed, undo the moves
# for move in moves:
# os.rename(move[1], move[0])
# raise
# def get_datedir_path(self, date):
# """
# Return a path like 2018/3/31 given a datetime object representing the same date
# """
# return os.path.join(str(date.year), str(date.month), str(date.day))
# def make_thumb(self, photo, style):
# """
# Create a thumbnail of the given photo, scaled/cropped to the given named style
# :return: local path to thumbnail file or None if creation failed or was blocked
# """
# # style tuples: max x, max y, rotate ok)
# # rotate ok means x and y maxes can be swapped if it fits the image's aspect ratio better
# styles = {"tiny": (80, 80, False),
# "small": (100, 100, False),
# "feed": (250, 250, False),
# "preview": (1024, 768, True),
# "big": (2048, 1536, True)}
# dest = os.path.join(self.cache_path, "thumbs", style, "{}.jpg".format(photo.uuid))
# if os.path.exists(dest):
# return os.path.abspath(dest)
# if photo.width is None: # todo better detection of images that PIL can't open
# return None
# if photo.uuid not in self._failed_thumbs_cache[style]:
# thumb_width, thumb_height, flip_ok = styles[style]
# i_width = photo.width
# i_height = photo.height
# im_is_rotated = photo.orientation % 2 != 0 or i_height > i_width
# if im_is_rotated and flip_ok:
# thumb_width, thumb_height = thumb_height, thumb_width
# thumb_width = min(thumb_width, i_width if i_width > 0 else 999999999) # TODO do we even have photo.width if PIL can't read the image?
# thumb_height = min(thumb_height, i_height if i_height > 0 else 999999999) # TODO this seems bad
# p = Process(target=self.gen_thumb, args=(os.path.join(self.path, photo.path), dest, thumb_width, thumb_height, photo.orientation))
# p.start()
# p.join()
# if p.exitcode != 0:
# self._failed_thumbs_cache[style][photo.uuid] = True # dont retry failed generations
# return None
# return os.path.abspath(dest)
# return None
# @staticmethod
# def gen_thumb(src_img, dest_img, width, height, rotation):
# try:
# start = time()
# # TODO lock around the dir creation
# os.makedirs(os.path.split(dest_img)[0], exist_ok=True)
# image = Image.open(src_img)
# image = image.rotate(90 * rotation, expand=True)
# thumb = ImageOps.fit(image, (width, height), Image.ANTIALIAS)
# thumb.save(dest_img, 'JPEG')
# print("Generated {} in {}s".format(dest_img, round(time() - start, 4)))
# except:
# traceback.print_exc()
# if os.path.exists(dest_img):
# os.unlink(dest_img)
# sys.exit(1)
c.root.classes = BTrees.OOBTree.BTree()

View File

@ -1,22 +1,29 @@
{% extends "page.html" %}
{% block title %}All classes{% endblock %}
{% block subtitle %}<a href="/node_edit">Add</a>{% endblock %}
{% block subtitle %}{% endblock %}
{% block buttons %}{% endblock %}
{% block body %}
<div class="classes-all">
{% for node in classes %}
{% for cls in classnames %}
<div class="class">
<h2>{{ node.fqdn }}</h2>
<p>
<a href="/node/{{ node.fqdn }}">view</a> <a href="/node_edit?node={{ node.fqdn }}">edit</a>
</p>
<pre>
{{ node.body }}
</pre>
<h2>{{ cls }}</h2>
</div>
{% endfor %}
</div>
<div class="class-add">
<form action="/classes/add" method="post" class="pure-form pure-form-stacked">
<fieldset>
<fieldset class="pure-group pure-u-1">
<input name="name" type="text" class="pure-input-1" placeholder="Name" value="" />
</fieldset>
<div class="pure-u-1">
<input type="submit" name="op" class="pure-button pure-button-primary" value="Create" />
</div>
</fieldset>
</form>
</div>
{% endblock %}

View File

@ -27,12 +27,26 @@
<div class="node-classes">
<h2>Classes</h2>
<div class="class-list">
{% for class in node.classes %}
{% for name, class in node.classes.items() %}
<div class="class">
{{ class.cls.name }}
</div>
<div class="class-conf">
<pre>{{ class.conf }}</pre>
</div>
{% endfor %}
</div>
<div class="node-add-class">
<form action="/node/{{ node.fqdn }}/op" method="post" class="pure-form pure-form-stacked">
<select name="clsname">
{% for cls in classnames %}
<option value="{{ cls }}">{{ cls }}</option>
{% endfor %}
</select>
<textarea name="config" cols="30" rows="10" placeholder="Yaml config"></textarea>
<input type="submit" name="op" class="pure-button pure-button-primary" value="Attach"/>
</form>
</div>
</div>
</div>
</div>