Skip to content

Commit dd94ba6

Browse files
Merge pull request #4 from maricaantonacci/devel
Adding SLAs management
2 parents 0ff666e + fb7e87c commit dd94ba6

19 files changed

+1270
-31
lines changed

README.md

+16-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ Create the `config.json` file (see the [example](app/config-sample.json)):
2929
"IAM_CLIENT_SECRET": "*****",
3030
"IAM_BASE_URL": "https://iam-test.indigo-datacloud.eu",
3131
"ORCHESTRATOR_URL": "https://indigo-paas.cloud.ba.infn.it/orchestrator",
32-
"TOSCA_TEMPLATES_DIR": "/opt/tosca-templates"
32+
"TOSCA_TEMPLATES_DIR": "/opt/tosca-templates",
33+
"SLAM_URL": "https://indigo-slam.cloud.ba.infn.it:8443",
34+
"CMDB_URL": "https://indigo-paas.cloud.ba.infn.it/cmdb"
3335
}
3436
````
3537
Clone the tosca-templates repository to get a set of tosca templates that the dashboard will load, e.g.:
@@ -124,6 +126,19 @@ cd orchestrator-dashboard
124126
docker build -f docker/Dockerfile -t orchestrator-dashboard .
125127
```
126128

129+
## How to setup a development environment
127130

131+
```
132+
git clone https://github.com/maricaantonacci/orchestrator-dashboard.git
133+
cd orchestrator-dashboard
134+
python3 -m venv venv
135+
source venv/source/activate
136+
pip install -r requirements.txt
137+
```
138+
139+
Start the dashboard app:
140+
```
141+
FLASK_app=orchdashboard flask run --host=0.0.0.0 --cert cert.pem --key privkey.pem --port 443
142+
```
128143

129144

app/config-sample.json

+2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
"IAM_CLIENT_SECRET": "my_client_secret",
44
"IAM_BASE_URL": "https://iam-test.indigo-datacloud.eu",
55
"ORCHESTRATOR_URL": "https://indigo-paas.cloud.ba.infn.it/orchestrator",
6+
"SLAM_URL": "https://indigo-slam.cloud.ba.infn.it:8443",
7+
"CMDB_URL": "https://indigo-paas.cloud.ba.infn.it/cmdb",
68
"TOSCA_TEMPLATES_DIR": "/opt/tosca-templates"
79
}

app/routes.py

+127-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from app import app, iam_blueprint
1+
from app import app, iam_blueprint, iam_base_url
22
from flask import json, current_app, render_template, request, redirect, url_for, flash, session
33
import requests, json
44
import yaml
@@ -29,40 +29,111 @@ def avatar(email, size):
2929
toscaTemplates.append( os.path.relpath(os.path.join(path, name), toscaDir ))
3030

3131
orchestratorUrl = app.config.get('ORCHESTRATOR_URL')
32+
slamUrl = app.config.get('SLAM_URL')
33+
cmdbUrl = app.config.get('CMDB_URL')
34+
slam_cert = app.config.get('SLAM_CERT')
35+
36+
@app.route('/settings')
37+
def show_settings():
38+
if not iam_blueprint.session.authorized:
39+
return redirect(url_for('login'))
40+
return render_template('settings.html', orchestrator_url=orchestratorUrl, iam_url=iam_base_url)
3241

33-
#@app.route('/')
3442
@app.route('/login')
3543
def login():
3644
session.clear()
3745
return render_template('home.html')
3846

39-
@app.route('/dashboard')
47+
48+
def get_sla_extra_info(access_token, service_id):
49+
headers = {'Authorization': 'bearer %s' % (access_token)}
50+
url = cmdbUrl + "/service/id/" + service_id
51+
response = requests.get(url, headers=headers, timeout=20)
52+
response.raise_for_status()
53+
app.logger.info(json.dumps(response.json()['data']['service_type']))
54+
55+
service_type=response.json()['data']['service_type']
56+
sitename=response.json()['data']['sitename']
57+
if 'properties' in response.json()['data']:
58+
if 'gpu_support' in response.json()['data']['properties']:
59+
service_type = service_type + " (gpu_support: " + str(response.json()['data']['properties']['gpu_support']) + ")"
60+
61+
return sitename, service_type
62+
63+
def get_slas(access_token):
64+
65+
headers = {'Authorization': 'bearer %s' % (access_token)}
66+
67+
url = slamUrl + "/rest/slam/preferences/" + session['organisation_name']
68+
verify = True
69+
if slam_cert:
70+
verify = slam_cert
71+
response = requests.get(url, headers=headers, timeout=20, verify=verify)
72+
app.logger.info("SLA response status: " + str(response.status_code))
73+
74+
response.raise_for_status()
75+
app.logger.info("SLA response: " + json.dumps(response.json()))
76+
slas = response.json()['sla']
77+
78+
for i in range(len(slas)):
79+
sitename, service_type = get_sla_extra_info(access_token,slas[i]['services'][0]['service_id'])
80+
slas[i]['service_type']=service_type
81+
slas[i]['sitename']=sitename
82+
83+
return slas
84+
85+
@app.route('/slas')
86+
def getslas():
87+
88+
if not iam_blueprint.session.authorized:
89+
return redirect(url_for('login'))
90+
91+
slas={}
92+
93+
try:
94+
access_token = iam_blueprint.token['access_token']
95+
slas = get_slas(access_token)
96+
97+
except Exception as e:
98+
flash("Error retrieving SLAs list: \n" + str(e), 'warning')
99+
return redirect(url_for('home'))
100+
101+
return render_template('sla.html', slas=slas)
102+
103+
104+
@app.route('/dashboard/<page>')
105+
@app.route('/<page>')
40106
@app.route('/')
41-
def home():
107+
def home(page=0):
42108

43-
if not iam_blueprint.session.authorized:
44-
return redirect(url_for('login'))
45-
109+
if not iam_blueprint.session.authorized:
110+
return redirect(url_for('login'))
111+
try:
46112
account_info=iam_blueprint.session.get("/userinfo")
47113

48114
if account_info.ok:
49115
account_info_json = account_info.json()
50116
session['username'] = account_info_json['name']
51117
session['gravatar'] = avatar(account_info_json['email'], 26)
118+
session['organisation_name']=account_info_json['organisation_name']
52119
access_token = iam_blueprint.token['access_token']
53120

54121
headers = {'Authorization': 'bearer %s' % (access_token)}
55122

56-
url = orchestratorUrl + "/deployments?createdBy=me"
123+
url = orchestratorUrl + "/deployments?createdBy=me&page=" + str(page)
57124
response = requests.get(url, headers=headers)
58125

59126
deployments = {}
60127
if not response.ok:
61128
flash("Error retrieving deployment list: \n" + response.text, 'warning')
62129
else:
63130
deployments = response.json()["content"]
64-
return render_template('deployments.html', deployments=deployments)
65-
131+
pages=response.json()['page']['totalPages']
132+
app.logger.debug(pages)
133+
return render_template('deployments.html', deployments=deployments, tot_pages=pages, current_page=page)
134+
except Exception:
135+
app.logger.info("error")
136+
return redirect(url_for('logout'))
66137

67138

68139
@app.route('/template/<depid>')
@@ -125,7 +196,23 @@ def depcreate():
125196
description = "N/A"
126197
if 'description' in template:
127198
description = template['description']
128-
return render_template('createdep.html', templates=toscaTemplates, selectedTemplate=selected_tosca, description=description, inputs=inputs)
199+
200+
slas = get_slas(access_token)
201+
return render_template('createdep.html', templates=toscaTemplates, selectedTemplate=selected_tosca, description=description, inputs=inputs, slas=slas)
202+
203+
def add_sla_to_template(template, sla_id):
204+
# Add the placement policy
205+
206+
nodes=template['topology_template']['node_templates']
207+
compute_nodes = []
208+
# for key, dict in nodes.items():
209+
# node_type=dict["type"]
210+
# if node_type == "tosca.nodes.indigo.Compute" or node_type == "tosca.nodes.indigo.Container.Application.Docker.Chronos" :
211+
# compute_nodes.append(key)
212+
# template['topology_template']['policies']=[{ "deploy_on_specific_site": { "type": "tosca.policies.Placement", "properties": { "sla_id": sla_id }, "targets": compute_nodes } }]
213+
template['topology_template']['policies']=[{ "deploy_on_specific_site": { "type": "tosca.policies.Placement", "properties": { "sla_id": sla_id } } }]
214+
app.logger.info(yaml.dump(template,default_flow_style=False))
215+
return template
129216
#
130217
#
131218
@app.route('/submit', methods=['POST'])
@@ -136,23 +223,43 @@ def createdep():
136223

137224
access_token = iam_blueprint.session.token['access_token']
138225

226+
app.logger.debug("Form data: " + json.dumps(request.form.to_dict()))
139227

140228
try:
141-
with io.open( toscaDir + request.args.get('template')) as stream:
142-
payload = { "template" : stream.read(), "parameters": request.form.to_dict() }
143-
144-
body= json.dumps(payload)
229+
with io.open( toscaDir + request.args.get('template')) as stream:
230+
template = yaml.load(stream)
231+
232+
form_data = request.form.to_dict()
233+
234+
params={}
235+
if 'extra_opts.keepLastAttempt' in form_data:
236+
params['keepLastAttempt'] = 'true'
237+
else:
238+
params['keepLastAttempt'] = 'false'
239+
240+
if form_data['extra_opts.schedtype'] == "man":
241+
template = add_sla_to_template(template, form_data['extra_opts.selectedSLA'])
242+
243+
inputs = { k:v for (k,v) in form_data.items() if not k.startswith("extra_opts.") }
244+
245+
app.logger.debug("Parameters: " + json.dumps(inputs))
246+
247+
payload = { "template" : yaml.dump(template,default_flow_style=False), "parameters": inputs }
248+
249+
#body= json.dumps(payload)
145250

146251
url = orchestratorUrl + "/deployments/"
147252
headers = {'Content-Type': 'application/json', 'Authorization': 'bearer %s' % (access_token)}
148-
response = requests.post(url, data=body, headers=headers)
253+
#response = requests.post(url, data=body, headers=headers)
254+
response = requests.post(url, json=payload, params=params, headers=headers)
149255

150256
if not response.ok:
151257
flash("Error submitting deployment: \n" + response.text)
152-
258+
153259
return redirect(url_for('home'))
260+
154261
except Exception as e:
155-
app.logger.error("Error submitting deployment:" + str(e))
262+
flash("Error submitting deployment:" + str(e) + ". Please retry")
156263
return redirect(url_for('home'))
157264

158265

@@ -162,3 +269,5 @@ def logout():
162269
iam_blueprint.session.get("/logout")
163270
# del iam_blueprint.session.token
164271
return redirect(url_for('login'))
272+
273+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
insert_final_newline = true
6+
indent_style = space
7+
indent_size = 2
8+
trim_trailing_whitespace = true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/.idea
2+
/*.iml
3+
/bower_components
4+
/node_modules
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
module.exports = function(grunt) {
2+
require('jit-grunt')(grunt);
3+
4+
grunt.initConfig({
5+
pkg: grunt.file.readJSON('package.json'),
6+
7+
banner: '/*!\n' +
8+
' * Bootstrap Confirmation <%= pkg.version %>\n' +
9+
' * Copyright 2013 Nimit Suwannagate <ethaizone@hotmail.com>\n' +
10+
' * Copyright 2014-<%= grunt.template.today("yyyy") %> Damien "Mistic" Sorel <contact@git.strangeplanet.fr>\n' +
11+
' * Licensed under the Apache License, Version 2.0\n' +
12+
' */',
13+
14+
// serve folder content
15+
connect: {
16+
dev: {
17+
options: {
18+
port: 9000,
19+
livereload: true
20+
}
21+
}
22+
},
23+
24+
// watchers
25+
watch: {
26+
options: {
27+
livereload: true
28+
},
29+
dev: {
30+
files: ['bootstrap-confirmation.js', 'example/**'],
31+
tasks: []
32+
}
33+
},
34+
35+
// open example
36+
open: {
37+
dev: {
38+
path: 'http://localhost:<%= connect.dev.options.port%>/example/index.html'
39+
}
40+
},
41+
42+
// replace version number
43+
replace: {
44+
dist: {
45+
options: {
46+
patterns: [
47+
{
48+
match: /(Confirmation\.VERSION = ').*(';)/,
49+
replacement: '$1<%= pkg.version %>$2'
50+
}
51+
]
52+
},
53+
files: {
54+
'bootstrap-confirmation.js': [
55+
'bootstrap-confirmation.js'
56+
]
57+
}
58+
}
59+
},
60+
61+
// compress js
62+
uglify: {
63+
options: {
64+
banner: '<%= banner %>\n',
65+
mangle: {
66+
except: ['$']
67+
}
68+
},
69+
dist: {
70+
files: {
71+
'bootstrap-confirmation.min.js': [
72+
'bootstrap-confirmation.js'
73+
]
74+
}
75+
}
76+
},
77+
78+
// jshint tests
79+
jshint: {
80+
lib: {
81+
files: {
82+
src: [
83+
'bootstrap-confirmation.js'
84+
]
85+
}
86+
}
87+
}
88+
}
89+
);
90+
91+
grunt.registerTask('default', [
92+
'replace',
93+
'uglify'
94+
]);
95+
96+
grunt.registerTask('test', [
97+
'jshint'
98+
]);
99+
100+
grunt.registerTask('serve', [
101+
'connect',
102+
'open',
103+
'watch'
104+
]);
105+
106+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Bootstrap-Confirmation
2+
3+
[![npm version](https://img.shields.io/npm/v/bootstrap-confirmation2.svg?style=flat-square)](https://www.npmjs.com/package/bootstrap-confirmation2)
4+
[![jsDelivr CDN](https://data.jsdelivr.com/v1/package/npm/bootstrap-confirmation2/badge)](https://www.jsdelivr.com/package/npm/bootstrap-confirmation2)
5+
[![Build Status](https://img.shields.io/travis/mistic100/Bootstrap-Confirmation/master.svg?style=flat-square)](https://travis-ci.org/mistic100/Bootstrap-Confirmation)
6+
[![Dependencies Status](https://david-dm.org/mistic100/Bootstrap-Confirmation/status.svg?style=flat-square)](https://david-dm.org/mistic100/Bootstrap-Confirmation)
7+
8+
Bootstrap plugin for on-place confirm boxes using Popover.
9+
10+
## Documentation
11+
12+
[bootstrap-confirmation.js.org](http://bootstrap-confirmation.js.org)
13+
14+
## Installation
15+
16+
#### Bootstrap 4
17+
18+
```
19+
npm install bootstrap-confirmation2
20+
```
21+
22+
#### Bootstrap 3
23+
24+
```
25+
npm install bootstrap-confirmation2@2.x.x
26+
```

0 commit comments

Comments
 (0)