Commit b443f64a authored by Phelipe Alves's avatar Phelipe Alves
Browse files

Updating slice detail and webterminal

parent e0f03265
......@@ -9,6 +9,11 @@ https://fibre.org.br
Dependencies
===============
### Redis
```
docker run --name fibre-redis -d -p 6379:6379 redis
```
Postgresql
Python3
......@@ -24,11 +29,43 @@ psycopg2
Installation guide
================
## Python environment
[Virtual Environments](http://docs.python-guide.org/en/latest/dev/virtualenvs/)
Install virtualenv via pip:
```
$ pip install virtualenv
```
Active venv:
```
$ cd fibre-terminal
$ virtualenv venv
$ source venv/bin/activate
```
Install dependencies
```
pip install -r requirements.txt
```
## Project
Migrate:
```
python manage.py migrate
```
Run server:
```
python manage.py runserver 8000
```
- Install OS dependencies
```
sudo apt-get update
sudo apt-get install -y python3 python3-pip python3-venv python3-openssl libxml2-dev libxslt1-dev python3-lxml postgresql postgresql-server-dev-9.4 libffi-dev libxmlsec1-dev git vim
sudo apt-get install -y python3 python3-pip python3-venv python3-openssl python-psycopg2 libxml2-dev libxslt1-dev python3-lxml postgresql postgresql-server-dev-9.4 libffi-dev libxmlsec1-dev git vim
```
- Clone the Portal repo:
......@@ -64,6 +101,7 @@ pip3 install cryptography
pip3 install httplib2
pip3 install xmlsec
pip3 install networkx
pip3 install psycopg2-binary
pip3 install urllib3
aptitude -V install python3-urllib3/jessie-backports
......
......@@ -15,7 +15,7 @@ def lease_exist(function):
credential_status, slice_cred = Slice().retrieve_credential(credential=request.session['member_cred'],
slice_urn=kwargs['slice_urn'])
lease = Reservation.get_reservation(request.session['member_cred'], slice_cred,
lease_uuid=kwargs['lease_uuid'])
lease_name=kwargs['lease_name'])
if not lease:
logging.error("Lease not found.")
return redirect('portal:detail_slice', slice_urn=kwargs['slice_urn'])
......@@ -29,12 +29,10 @@ def lease_exist(function):
def slice_exist(function):
def wrapper(request, *args, **kwargs):
if 'member_cred' in request.session:
slice_op = Slice().retrieve_slice(
credential=request.session['member_cred'], slice_urn=kwargs['slice_urn'])
if slice_op['operation_status'] == 'error':
logging.error(slice_op['operation_message'])
slice = Slice.get_slice(credential=request.session['member_cred'], slice_urn=kwargs['slice_urn'])
if not slice:
return redirect('portal:my_workspace')
kwargs['slice'] = slice
return function(request, *args, **kwargs)
else:
return redirect('portal:home')
......
......@@ -4,8 +4,30 @@ from django.http import QueryDict
from django.utils.translation import gettext as _
import json
class SliceForm(forms.Form):
class EnableSliceForm(forms.Form):
slice_expiration_date = forms.DateField(
required=True,
widget=forms.TextInput(attrs={'class': 'datepicker', 'required': 'true'}),
label=_("Expiration date"),
error_messages={
'required': _('The slice expiration date is required'),
'min_length': _('The slice expiration date must be at least %(limit_value)d characters (it has %('
'show_value)d).')
},
)
slice_expiration_time = forms.CharField(
required=False,
label=_("Expiration time")
)
def __init__(self, *args, **kwargs):
super(EnableSliceForm, self).__init__(*args, **kwargs)
class SliceForm(forms.Form):
nodes = forms.Field(required=False)
vms = forms.Field(required=False)
vlans = forms.Field(required=False)
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.7 on 2018-04-30 22:34
from __future__ import unicode_literals
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('portal', '0012_auto_20170313_1729'),
]
operations = [
migrations.AlterField(
model_name='useraccountactivationtoken',
name='request_date',
field=models.DateTimeField(default=datetime.datetime(2018, 5, 1, 1, 34, 29, 543272)),
),
migrations.AlterField(
model_name='userpasswordresettoken',
name='request_date',
field=models.DateTimeField(default=datetime.datetime(2018, 5, 1, 1, 34, 29, 542248)),
),
]
import ast
from base64 import b64encode
from tools.broker_rest_client import Urllib3RestClient
import portal.portal_constants as const
import logging
# Set log config.
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG)
class LeaseResource(object):
RESOURCE_URL = const.BROKER_BASE_URL + '/resources/accounts/{}/leases'
def __init__(self, slice_credential, slice):
self.slice_credential = slice_credential
self.slice = slice
self.vms = None
self.nodes = None
self.vlans = None
self.links = None
self.b64_slice_credential = b64encode(self.slice_credential.encode('utf-8')).decode('utf-8')
@classmethod
def get_resource(cls, credential, slice_urn):
broker_client = Urllib3RestClient()
b64_slice_credential = b64encode(credential.encode('utf-8')).decode('utf-8')
broker_client.add_header('CH-Credential', b64_slice_credential)
status, result = broker_client.get(LeaseResource.RESOURCE_URL.format(slice_urn))
if status == 200:
return result['resource_response']['resources']
return None
\ No newline at end of file
......@@ -198,11 +198,12 @@ class Member(object):
return operation
def retrieve_ssh_keys(self, credential, member_urn):
''' This is the method used to call a CH's method which searches a
"""
This is the method used to call a CH's method which searches a
member's SSH key. Can be cast by any member of the federation, but
the member's private_key will only be delivered if the member itself
cast this method.
'''
"""
# Set parameter dictionary.
params = {
......@@ -210,8 +211,7 @@ class Member(object):
'member_urn': member_urn}
# Call CH update method.
result =\
CH.ch_rest_request_handler('member_keys', 'get', **params)
result = CH.ch_rest_request_handler('member_keys', 'get', **params)
# Set operation dictionary with retrieve status and message.
operation = {
......
......@@ -121,6 +121,17 @@ class Flowvisor:
class OEDL:
VM_TOPIC = {
'ufg-br': 'fed-fibre-ufg-br-urn:publicid:IDN+fibre.ufg.br+node+xen',
'ufgrs-br': 'fed-fibre-ufrgs-br-urn:publicid:IDN+fibre.ufrgs.br+node+xen'
}
FLOWVISOR_TOPIC = {
'noc-fibre-org-br': 'fed-noc-fibre-org-br-flowvisor3-fw',
'ufg-br': 'fed-fibre-ufg-br-flowvisor-fw',
'ufgrs-br': 'fed-fibre-ufrgs-br-flowvisor-fw',
}
def __init__(self):
self.oedl = ''
......
import ast
from base64 import b64encode
from tools.broker_rest_client import Urllib3RestClient
import portal.portal_constants as const
import uuid
import logging
# Set log config.
......@@ -32,7 +32,6 @@ class Reservation(object):
success = 0
for lease in leases:
leases_url = const.BROKER_BASE_URL + '/resources/leases'
logging.info('LEASE CREATION: %s' % lease)
status, result = broker_client.post(leases_url, lease)
if status == 200:
success += 1
......@@ -42,7 +41,7 @@ class Reservation(object):
return success, len(leases)
@staticmethod
def delete_reservation(slice_credential, lease_uuid):
def delete_reservation(slice_credential, lease_name):
broker_client = Urllib3RestClient()
b64_slice_credential = b64encode(
slice_credential.encode('utf-8')).decode('utf-8')
......@@ -51,15 +50,12 @@ class Reservation(object):
delete_url = const.BROKER_BASE_URL + '/resources/leases'
success = 0
status, result = broker_client.delete(delete_url, {'uuid': lease_uuid})
status, result = broker_client.delete(delete_url, {'name': lease_name})
if status == 200:
success += 1
logging.info('Lease deleted.')
elif 'exception' in result:
logging.error(result['exception'])
return success
return status
@classmethod
def get_formatted_lease(cls, lease):
......@@ -81,7 +77,7 @@ class Reservation(object):
return flease
@staticmethod
def get_reservation(member_cred, slice_cred, lease_uuid):
def get_reservation(member_cred, slice_cred, lease_name):
broker_client = Urllib3RestClient()
b64_slice_credential = b64encode(
slice_cred.encode('utf-8')).decode('utf-8')
......@@ -89,22 +85,15 @@ class Reservation(object):
member_cred.encode('utf-8')).decode('utf-8')
broker_client.add_header('CH-Credential', b64_slice_credential)
get_url = const.BROKER_BASE_URL + '/resources/leases'
get_url = const.BROKER_BASE_URL + '/resources/leases/' + lease_name
success = 0
# Search for leases. Attention: The search for uuid is not working.
# The broker returns all the leases, so that one have to look for
# the right one.
status, result = broker_client.get(get_url, {'uuid': lease_uuid})
status, result = broker_client.get(get_url)
if status == 200:
success += 1
# logging.info('Lease deleted.')
elif 'exception' in result:
if status is not 200:
logging.error(result['exception'])
leases = result['resource_response']['resources']
lease = [lease for lease in leases if leases is not None and lease['uuid'] == lease_uuid]
lease = [lease for lease in leases if leases is not None and lease['name'] == lease_name]
lease = lease[0] if len(lease) > 0 else None
if not lease:
......@@ -211,6 +200,7 @@ class Reservation(object):
leases = []
for schedule in self.schedules:
lease = {
"name": str(uuid.uuid4()),
"valid_from": schedule['start'],
"valid_until": schedule['end'],
"policy": "best-effort",
......
......@@ -93,16 +93,20 @@ class Resource(object):
def get_topology_of_testbed(self):
links = self.get_links()
topology = {'nodes': [], 'edges': []}
topology = {'nodes': [], 'edges': [], 'hypervisors': []}
added_nodes = {}
all_nodes = self.get_resources('nodes')
all_nodes = {n['urn']: n for n in all_nodes['resource_response']['resources'] if 'interfaces' in n and 'cpus' in n}
topology['hypervisors'] = all_nodes
for link in links:
edge = {'urn': link['urn'], 'source': {}, 'destination': {}}
if len(link['interfaces']) <= 0: # TODO: see this on REST_API
continue
for node in link['interfaces'][0]['link']['connects']:
if node['urn'] not in added_nodes:
topology['nodes'].append(node)
topology['nodes'].append(all_nodes.get(node['urn'], node))
added_nodes[node['urn']] = ''
if node['resource_type'] == 'openflow_switch':
fid = node['urn'].split('fv_of_switch_')[1]
......@@ -116,8 +120,23 @@ class Resource(object):
origin = 'source' if not edge['source'] else 'destination'
edge[origin]['urn'] = node['urn']
edge[origin]['port'] = 0
edge[origin]['data'] = node
edge[origin]['data'] = all_nodes.get(node['urn'], node)
topology['edges'].append(edge)
nodes = copy.deepcopy(topology['nodes'])
for idx, node in enumerate(nodes):
eq_nodes = [n for n in topology['nodes'] if
n['name'] == node['name'] and n['urn'] != node['urn'] and 'noc.fibre' in n['urn']]
for eq_node in eq_nodes:
for edge in topology['edges']:
if edge['source']['data']['urn'] == eq_node['urn']:
edge['source']['urn'] = node['urn']
edge['source']['data'] = node
elif edge['destination']['data']['urn'] == eq_node['urn']:
edge['destination']['urn'] = node['urn']
edge['destination']['data'] = node
topology['nodes'].remove(eq_node)
return topology
def get_treated_resources_of_testbed(self):
......@@ -135,7 +154,7 @@ class Resource(object):
hypervisors.append(node['urn'])
# treat hypervisor
node['vcpu_count'] = sum([(int(x['cores']) * int(x['threads'])) for x in node['cpus']])
# node['vcpu_count'] = sum([(int(x['cores']) * int(x['threads'])) for x in node['cpus']])
# Get topology
topology['nodes'].append({
......
......@@ -102,11 +102,14 @@
border-color: #ddd;
}
.disable-pointer-events {
pointer-events:none;
}
#graph-container {
width: 100%;
height: 300px;
background-color: #eee;
border-radius: 5px;
position: relative;
}
......
$(document).ready(function() {
// Flowspace Graph
if($('#graph-container').length == 1 && window.topology != null) {
if($('#graph-container').length === 1 && window.topology !== null) {
var topology = window.topology;
g = {nodes: [], edges: []};
$(topology.nodes).each(function(index, elem) {
var color = elem.resource_type === 'node' ? '#0DFF43' : '#1E0CE8';
var id = elem.urn;
var label = '';
var color = '';
if (elem.resource_type === 'virtual_machine') {
id = elem.name;
label = elem.name.split(':').slice(-1)[0];
console.log('VM', label, elem);
if (elem.status === 'DOWN') {
color = '#ff0300';
} else {
color = '#0DFF43';
}
} else if (elem.resource_type === 'node') {
label = elem.urn;
color = '#110fff';
} else {
color = '#e8d400';
}
g.nodes.push({
id: elem.urn,
label: elem.urn,
id: id,
label: label,
x: Math.random(),
y: Math.random(),
size: 2,
......@@ -15,16 +33,21 @@ $(document).ready(function() {
data: elem
})
});
$(topology.edges).each(function(index, elem) {
var count = 0;
g.edges.push({
id: elem.urn,
source: elem.source.urn,
target: elem.destination.urn,
data: elem,
size: 2
size: 0.1,
count: count
});
});
console.log(g);
var selectedEdges = window.selectedEdges;
window.sigma_graph = new sigma({
graph: g,
......@@ -35,8 +58,8 @@ $(document).ready(function() {
container: 'graph-container',
settings: {
doubleClickEnabled: false,
minEdgeSize: 3,
maxEdgeSize: 6,
minEdgeSize: 1,
maxEdgeSize: 1,
enableEdgeHovering: true,
edgeColor: 'default',
defaultEdgeColor: "#999",
......@@ -44,29 +67,44 @@ $(document).ready(function() {
defaultEdgeHoverColor: '#000',
edgeHoverSizeRatio: 1,
edgeHoverExtremities: true,
drawEdgeLabels: false,
zoomMin: 1,
zoomMax: 5
drawEdgeLabels: false
}
});
sigma.plugins.dragNodes(sigma_graph, sigma_graph.renderers[0]);
sigma_graph.bind('clickEdge', function(e) {
edge = e.data.edge;
isSelected = $.inArray(edge, selectedEdges) !== -1;
if(isSelected) {
console.log('edge remove:', edge);
delete edge.color;
removeSelectedEdge(edge);
} else {
console.log('edge add:', edge);
edge.color = 'red';
selectedEdges.push(edge);
}
updateLinks(selectedEdges);
sigma_graph.refresh();
// isSelected = $.inArray(edge, selectedEdges) !== -1;
// if(isSelected) {
// console.log('edge remove:', edge);
// delete edge.color;
// removeSelectedEdge(edge);
// } else {
// console.log('edge add:', edge);
// edge.color = 'red';
// selectedEdges.push(edge);
// }
// updateLinks(selectedEdges);
// sigma_graph.refresh();
});
// sigma_graph.bind('clickNode', function(e) {
// node = e;
// console.log(node);
// // isSelected = $.inArray(edge, selectedEdges) !== -1;
// // if(isSelected) {
// // console.log('edge remove:', edge);
// // delete edge.color;
// // removeSelectedEdge(edge);
// // } else {
// // console.log('edge add:', edge);
// // edge.color = 'red';
// // selectedEdges.push(edge);
// // }
// // updateLinks(selectedEdges);
// // sigma_graph.refresh();
// });
function removeSelectedEdge(edge) {
$.each(selectedEdges, function(i){
if(selectedEdges[i].id === edge.id) {
......@@ -88,12 +126,15 @@ $(document).ready(function() {
' <div class="sigma-tooltip-body">' +
' <table>' +
' <tr><th>Name</th> <td>{{data.name}}</td></tr>' +
' <tr><th>Gender</th> <td>{{data.gender}}</td></tr>' +
' <tr><th>Age</th> <td>{{data.age}}</td></tr>' +
' <tr><th>City</th> <td>{{data.city}}</td></tr>' +
' </table>' +
' </div>' +
' <div class="sigma-tooltip-footer">Number of connections: {{degree}}</div>',
' <div class="sigma-tooltip-footer">' +
'<button type="submit" class="btn btn-warning open_ssh">' +
' <span class="glyphicon glyphicon-ban-circle"></span>' +
' Openssh' +
'</button>' +
'Number of connections: {{degree}}' +
'</div>',
renderer: function(node, template) {
// The function context is s.graph
node.degree = this.degree(node.id);
......@@ -109,23 +150,50 @@ $(document).ready(function() {
position: 'right',
template:
'<div class="arrow"></div>' +
' <div class="sigma-tooltip-header">{{label}}</div>' +
' <div class="sigma-tooltip-body">' +
' <p> Context menu for {{data.name}} </p>' +
' </div>' +
' <div class="sigma-tooltip-footer">Number of connections: {{degree}}</div>',
'<div class="sigma-tooltip-header">{{label}}</div>' +
'<div class="sigma-tooltip-body">' +
' <table>' +
' <tr><th>Status</th> <td>{{#label_status}}{{/label_status}}</td></tr>' +
' </table>' +
'</div>' +
'<div class="sigma-tooltip-footer">' +
' <button type="submit" class="btn btn-warning" id="open_ssh" onclick="{{open_ssh}}">' +
' <span class="glyphicon glyphicon-ban-circle"></span> Openssh' +
' </button>' +
'</div>',
renderer: function(node, template) {
if (node.data.resource_type !== 'virtual_machine') {
return;
}
node.open_ssh = function () {
// $('.nav-tabs').append('<li><a href="#' + this.label + '"><button class="close closeTab" type="button" >×</button>' + this.label + '</a></li>');
// $('.tab-content').append('<div class="tab-pane responsive" id="' + this.label + '"></div>');
// showTab(this.label);
// registerCloseEvent();
// var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
// var ws_path = ws_scheme + '://' + window.location.host + '/ws/';
// make_terminal(document.getElementById(this.label), {rows: 40, cols: 90}, ws_path, '127.0.0.1');
return "alert("+JSON.stringify("ID is " + this.id)+")";
};
node.label_status = function() {
return function (text, render) {
console.log('label_status', this);
var label = this.data.status === 'DOWN' ? 'danger' : 'success';
return '<span class="label label-' + label + '">' + this.data.status + '</span>';
}
};
node.degree = this.degree(node.id);
return Mustache.render(template, node);
}
}],
stage: {
template:
'<div class="arrow"></div>' +
'<div class="sigma-tooltip-header"> Menu </div>'
}
}]
};
// $("button").on('click', function() {
// alert("I was clicked!");
// });