From f1155f53ec0e4495e439e5509934bd42726d143f Mon Sep 17 00:00:00 2001 From: Maxim Kulkin Date: Wed, 16 Oct 2013 16:41:58 +0400 Subject: [PATCH] New UI implementation (WIP) --- ProcfileHoncho | 4 +- config-validator-ui-concept/index.html | 12 ++-- ostack_validator/webui.py | 91 ++++++++++++++++++++++---- requirements.txt | 1 + 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/ProcfileHoncho b/ProcfileHoncho index 9768081..969daaa 100644 --- a/ProcfileHoncho +++ b/ProcfileHoncho @@ -1,2 +1,2 @@ -webui: gunicorn --error-logfile - --log-level info ostack_validator.webui:app -worker: celery worker --app=ostack_validator.celery:app \ No newline at end of file +webui: gunicorn --error-logfile - --log-level info ostack_validator.webui:app --bind 0.0.0.0:8000 +worker: celery worker --app=ostack_validator.celery:app diff --git a/config-validator-ui-concept/index.html b/config-validator-ui-concept/index.html index d941b1b..9ec6160 100644 --- a/config-validator-ui-concept/index.html +++ b/config-validator-ui-concept/index.html @@ -10,16 +10,16 @@ Feed Example - Semantic - + - - - + + + - - + + diff --git a/ostack_validator/webui.py b/ostack_validator/webui.py index bb4da80..51c4cec 100644 --- a/ostack_validator/webui.py +++ b/ostack_validator/webui.py @@ -1,16 +1,18 @@ from itertools import groupby +import os.path -from flask import Flask, request, redirect, render_template, json +from flask import Flask, request, redirect, render_template, json, send_file from flask_bootstrap import Bootstrap from flask_wtf import Form -from wtforms import StringField, TextAreaField, SubmitField +from wtforms import StringField, TextAreaField, SubmitField, SelectMultipleField from wtforms.validators import DataRequired +import wtforms_json from ostack_validator.celery import app as celery, ostack_inspect_task, InspectionRequest from ostack_validator.common import Issue, MarkedIssue from ostack_validator.model import Openstack -app = Flask(__name__) +app = Flask(__name__, static_folder='../config-validator-ui-concept', static_url_path='/static') Bootstrap(app) app.debug = True app.config.update( @@ -18,16 +20,57 @@ app.config.update( ) app.secret_key = 'A0Zr98j/3fooN]LWX/,?RT' +wtforms_json.init() -class ValidationLaunchForm(Form): - nodes = StringField('Nodes', validators=[DataRequired()]) - username = StringField( - 'Username', - default='root', - validators=[DataRequired()]) - private_key = TextAreaField('Private Key', validators=[DataRequired()]) +def connect_to_db(): + mongo_url = os.environ.get("MONGODB_URI") or "mongodb://localhost/rubick" + client = MongoClient(mongo_url) + return client[mongo_url.split('/')[-1]] - launch = SubmitField('Launch validation') +def get_db(): + db = connect_to_db() + return db + +class Cluster(object): + @classmethod + def from_doc(klass, doc): + return Cluster(doc['name'], doc['seed_nodes'], doc['nodes'], doc['private_key']) + + def __init__(self, id, name, seed_nodes, private_key, nodes): + super(Cluster, self).__init__() + self.id = id + self.name = name + self.seed_nodes = seed_nodes + self.private_key = private_key + self.nodes = nodes + +class RuleGroup: + VALIDITY='validity' + HA='high-availability' + BEST_PRACTICES='best-practices' + + all = [VALIDITY, HA, BEST_PRACTICES] + +class Rule(object): + @classmethod + def from_doc(klass, doc): + return Rule(doc['id'], doc['group'], doc['name'], doc['text']) + + def __init__(self, id, group, name, text): + super(Rule, self).__init__() + self.id = id + self.group = group + self.name = name + self.text = text + +class ClusterForm(Form): + name = StringField('Name', validators=[DataRequired()]) + nodes = StringField('Nodes', validators=[DataRequired()]) + private_key = TextAreaField('Private Key', validators=[DataRequired()]) + +class ValidateClusterForm(Form): + cluster_id = StringField('Cluster', validators=[DataRequired()]) + rules = SelectMultipleField('Rules') @app.template_filter() @@ -42,7 +85,31 @@ def to_label(s): @app.route('/') def index(): - return redirect('/validation') + return send_file(os.path.join(app.static_folder, 'index.html')) + +@app.route('/clusters') +def get_clusters(): + db = get_db() + return json.dumps([Cluster.from_doc(doc) for doc in db['clusters'].find()]) + +@app.route('/clusters', methods=['POST']) +def add_cluster(): + form = ClusterForm.from_json(json.loads(request.data)) + if form.validate(): + cluster = Cluster() + form.populate_obj(cluster) + get_db()['clusters'].save(cluster) + return '', 201 + else: + return json.dumps(dict(errors=form.errors)), 422 + +@app.route('/rules/') +def get_rules(group): + if not group in RuleGroup.all: + return 'Unknown rule group "%s"' % group, 404 + + rules = [Rule.from_doc(doc) for doc in db['rules'].find({'group': group})] + return json.dumps(rules) @app.route('/validation', methods=['GET', 'POST']) diff --git a/requirements.txt b/requirements.txt index c1852b8..8529b3d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ spur==0.3.5 Flask==0.10.1 Flask-Bootstrap>=3.0.0 Flask-WTF==0.9.2 +WTForms-JSON>=0.2.2 gunicorn==18.0 celery-with-redis>=3.0 # Jinja2==2.7.1