209 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			6.5 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')
 | 
						|
 | 
						|
 | 
						|
@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', debug=True)
 |