271 lines
8.4 KiB
Python
271 lines
8.4 KiB
Python
from itertools import groupby
|
|
import os.path
|
|
|
|
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, SelectMultipleField
|
|
from wtforms.validators import DataRequired
|
|
import wtforms_json
|
|
from pymongo import MongoClient
|
|
|
|
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__, static_folder='config-validator-ui-concept', static_url_path='/static')
|
|
Bootstrap(app)
|
|
app.debug = True
|
|
app.config.update(
|
|
WTF_CSRF_SECRET_KEY='foo bar baz'
|
|
)
|
|
app.secret_key = 'A0Zr98j/3fooN]LWX/,?RT'
|
|
|
|
wtforms_json.init()
|
|
|
|
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]]
|
|
|
|
def get_db():
|
|
db = connect_to_db()
|
|
return db
|
|
|
|
class Cluster(object):
|
|
@classmethod
|
|
def from_doc(klass, doc):
|
|
return Cluster(doc['id'], doc['name'], description=doc['description'], status=doc['status'], seed_nodes=doc['seed_nodes'], nodes=doc['nodes'], private_key=doc['private_key'])
|
|
|
|
def __init__(self, id, name, description=None, status='Unknown', seed_nodes=[], private_key=None, nodes=[]):
|
|
super(Cluster, self).__init__()
|
|
self.id = id
|
|
self.name = name
|
|
self.description = description
|
|
self.status = status
|
|
self.seed_nodes = seed_nodes
|
|
self.private_key = private_key
|
|
self.nodes = nodes
|
|
|
|
# JSON serialization helper
|
|
def _asdict(self):
|
|
return dict(id=self.id, name=self.name, description=self.description, status=self.status, nodes=self.nodes, seed_nodes=self.seed_nodes, private_key=self.private_key)
|
|
|
|
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
|
|
|
|
# JSON serialization helper
|
|
def _asdict(self):
|
|
return dict(id=self.id, group=self.group, name=self.name, text=self.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')
|
|
|
|
wtforms_json.init()
|
|
|
|
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]]
|
|
|
|
def get_db():
|
|
db = connect_to_db()
|
|
return db
|
|
|
|
class Cluster(object):
|
|
@classmethod
|
|
def from_doc(klass, doc):
|
|
return Cluster(doc['id'], doc['name'], description=doc['description'], status=doc['status'], seed_nodes=doc['seed_nodes'], nodes=doc['nodes'], private_key=doc['private_key'])
|
|
|
|
def __init__(self, id, name, description=None, status='Unknown', seed_nodes=[], private_key=None, nodes=[]):
|
|
super(Cluster, self).__init__()
|
|
self.id = id
|
|
self.name = name
|
|
self.description = description
|
|
self.status = status
|
|
self.seed_nodes = seed_nodes
|
|
self.private_key = private_key
|
|
self.nodes = nodes
|
|
|
|
# JSON serialization helper
|
|
def _asdict(self):
|
|
return dict(id=self.id, name=self.name, description=self.description, status=self.status, nodes=self.nodes, seed_nodes=self.seed_nodes, private_key=self.private_key)
|
|
|
|
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
|
|
|
|
# JSON serialization helper
|
|
def _asdict(self):
|
|
return dict(id=self.id, group=self.group, name=self.name, text=self.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()
|
|
def to_label(s):
|
|
if s in [Issue.FATAL, Issue.ERROR]:
|
|
return 'label-danger'
|
|
elif s == Issue.WARNING:
|
|
return 'label-warning'
|
|
else:
|
|
return 'label-info'
|
|
|
|
|
|
@app.route('/')
|
|
def index():
|
|
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()])
|
|
return json.dumps([
|
|
Cluster('cluster1', "Kirill's DevStack", description="Grizzly-based devstack with Quantum and oVS, deployed on Kirill's laptop", status='Available'),
|
|
#Cluster('cluster2', "Peter's DevStack", description="Grizzly-based devstack deployed on Peter Lomakin's workstation with nova-network and FlatDHCP manager", status='Broken')
|
|
])
|
|
|
|
@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():
|
|
db = get_db()
|
|
rules = [Rule.from_doc(doc) for doc in db['rules'].find()]
|
|
rules = [
|
|
Rule('rule1', RuleGroup.VALIDITY, 'Nova has proper Keystone host',
|
|
"""Given I use OpenStack Grizzly 2013.1
|
|
And Nova has "auth_strategy" equal to "keystone"
|
|
And Keystone addresses are @X
|
|
Then Nova should have "keystone_authtoken.auth_host" in "$X" """
|
|
),
|
|
Rule('rule1', RuleGroup.VALIDITY, 'Nova has proper Keystone host',
|
|
"""Given I use OpenStack Grizzly 2013.1
|
|
And Nova has "auth_strategy" equal to "keystone"
|
|
And Keystone addresses are @X
|
|
Then Nova should have "keystone_authtoken.auth_host" in "$X" """
|
|
)
|
|
]
|
|
return json.dumps(rules)
|
|
|
|
@app.route('/rules/<group>')
|
|
def get_rules_group(group):
|
|
if not group in RuleGroup.all:
|
|
return 'Unknown rule group "%s"' % group, 404
|
|
|
|
db = get_db()
|
|
#rules = [Rule.from_doc(doc) for doc in db['rules'].find({'group': group})]
|
|
#return json.dumps(rules)
|
|
return get_rules()
|
|
|
|
|
|
@app.route('/validation', methods=['GET', 'POST'])
|
|
def launch_validation():
|
|
form = ValidationLaunchForm()
|
|
if form.validate_on_submit():
|
|
request = InspectionRequest(
|
|
form.nodes.data.split(
|
|
' '),
|
|
form.username.data,
|
|
private_key=form.private_key.data)
|
|
|
|
job = ostack_inspect_task.delay(request)
|
|
|
|
return redirect('/validation/%s' % job.id)
|
|
else:
|
|
return render_template('validation_form.html', form=form)
|
|
|
|
|
|
@app.route('/validation/<id>')
|
|
def job(id):
|
|
job = celery.AsyncResult(id)
|
|
if job.ready():
|
|
r = job.result.request
|
|
|
|
form = ValidationLaunchForm()
|
|
form.nodes.data = ' '.join(r.nodes)
|
|
form.username.data = r.username
|
|
form.private_key.data = r.private_key
|
|
|
|
openstack = job.result.value
|
|
|
|
if isinstance(openstack, Openstack):
|
|
issue_source_f = lambda i: i.mark.source if isinstance(
|
|
i, MarkedIssue) else None
|
|
source_groupped_issues = groupby(
|
|
sorted(openstack.issues,
|
|
key=issue_source_f),
|
|
key=issue_source_f)
|
|
|
|
return (
|
|
render_template(
|
|
'validation_result.html',
|
|
form=form,
|
|
openstack=openstack,
|
|
grouped_issues=source_groupped_issues)
|
|
)
|
|
else:
|
|
return (
|
|
render_template(
|
|
'validation_error.html',
|
|
form=form,
|
|
message=openstack)
|
|
)
|
|
else:
|
|
return render_template('validation_state.html', state=job.state)
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0', port=8000, debug=True)
|