.json => gns3a

This commit is contained in:
Julien Duponchelle 2015-09-11 14:42:01 +02:00
parent 57b1029aac
commit 66ac6fd002
21 changed files with 2 additions and 558 deletions

View File

@ -2,7 +2,8 @@ GNS3-registry
================
This is the GNS3 registry.
This is the GNS3 registry where user can share
appliances configurations.
Add a new appliance
@ -29,22 +30,6 @@ All tools require python3 and the installation of dependencies via:
pip3 install -r requirements.txt
Build website
--------------
.. code:: bash
python3 build.py
Run website
-------------
.. code:: bash
python3 server.py
Check appliance files
-----------------------

163
build.py
View File

@ -1,163 +0,0 @@
#!/usr/bin/env python
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import json
import shutil
import copy
import base64
from jinja2 import Environment, FileSystemLoader
from check import check_schema
import logging
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
log.addHandler(handler)
if os.path.exists('build'):
for file in os.listdir('build'):
if os.path.isdir(os.path.join('build', file)):
shutil.rmtree(os.path.join('build', file))
else:
os.remove(os.path.join('build', file))
else:
os.mkdir('build')
os.mkdir(os.path.join('build', 'appliances'))
os.mkdir(os.path.join('build', 'images'))
def human_filesize(num):
for unit in ['B','KB','MB','GB']:
if abs(num) < 1024.0:
return "%3.1f%s" % (num, unit)
num /= 1024.0
return "%.1f%s" % (num, 'TB')
def render(template_file, out, **kwargs):
log.info('Build %s', out)
directory = os.path.dirname(out)
os.makedirs(os.path.join('build', directory), exist_ok=True)
env = Environment(loader=FileSystemLoader('templates'))
env.filters['nl2br'] = lambda s: s.replace('\n', '<br />')
env.filters['jsonify'] = json.dumps
env.filters['b64encode'] = lambda s: base64.b64encode(s.encode()).decode("utf-8")
env.filters['human_filesize'] = human_filesize
template = env.get_template(template_file)
template.stream(**kwargs).dump(os.path.join('build', out))
def keep_only_version_with_appliance(md5sum, appliance):
"""
Filter appliance version in order to keep only the
version where the image is present.
:param md5sum: Md5sum of the image
:param appliance: Appliance hash
:returns: List of version
"""
new_versions = []
for version in appliance["versions"]:
found = False
for image in version["images"].values():
if image["md5sum"] == md5sum:
found = True
break
if found:
new_versions.append(version)
return new_versions
def generate_appliances_pages(group_by, appliances):
"""
This will generate page by grouping appliances
:param: Group appliance by this key
:param: Array of appliances
"""
keys = set()
for appliance in appliances:
keys.add(appliance[group_by])
for key in keys:
filtered_appliances = []
for appliance in appliances:
if appliance[group_by] == key:
filtered_appliances.append(appliance)
render('appliances.html', os.path.join('appliances', group_by, key + '.html'), appliances=filtered_appliances, title=key)
render('group_by.html', os.path.join('appliances', group_by, 'index.html'), keys=keys, group_by=group_by)
render('index.html', 'index.html')
render('chat.html', 'chat.html')
render('downloads.html', 'downloads.html')
render('myimages.html', 'myimages.html')
appliances = []
for appliance_file in os.listdir('appliances'):
log.info("Check the schema for " + appliance_file)
check_schema(appliance_file)
log.info("Process " + appliance_file)
out_filename = appliance_file[:-5]
with open(os.path.join('appliances', appliance_file)) as f:
appliance = json.load(f)
appliance['id'] = out_filename
# Resolve version image to the corresponding file
for version in appliance['versions']:
for image_type, filename in version['images'].items():
found = False
for file in appliance['images']:
if file['filename'] == filename:
version['images'][image_type] = copy.copy(file)
version['images'][image_type]["type"] = image_type
found = True
break
if not found:
log.critical('Image for {} {} with filename {} is missing'.format(appliance["name"], version["name"], file['filename']))
sys.exit(1)
render('appliance.html', os.path.join('appliances', out_filename + '.html'), appliance=appliance)
appliances.append(appliance)
# Build a page named with the md5sum of each file of the appliance
# it's allow to get the appliance informations via HTTP with just an md5sum
# it's what powered the import feature
for image in appliance['images']:
# We keep only version with this image in the page
image_appliance = copy.copy(appliance)
image_appliance['versions'] = keep_only_version_with_appliance(image['md5sum'], appliance)
render('appliance.html', os.path.join('images', image['md5sum'] + '.html'), appliance=image_appliance)
render('appliances.html', os.path.join('appliances', 'index.html'), appliances=appliances, title="All", show_filter=True)
generate_appliances_pages("category", appliances)
generate_appliances_pages("status", appliances)
generate_appliances_pages("vendor_name", appliances)

View File

@ -1,3 +0,0 @@
-rrequirements.txt
pytest==2.6.4

View File

@ -1,2 +1 @@
Jinja2==2.7.3
json-schema==2.4.0

View File

@ -1,29 +0,0 @@
#!/usr/bin/env python
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import http.server
import socketserver
PORT = 8001
os.chdir('build')
Handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(('', PORT), Handler)
print('Serving at port', PORT)
httpd.serve_forever()

View File

@ -1,98 +0,0 @@
{% extends "layout/default.html" %}
{% block script %}
function download(appliance, md5sum) {
if (gns3_button(function() {
return gns3.download(appliance, md5sum)
})) {
gns3_notif("success", "You can see the download progress in the <a href=\"/downloads.html\">Downloads</a> section");
return true;
}
return false;
}
var installable_versions = gns3.installableVersions("{{appliance|jsonify|b64encode}}");
var images_md5sum = [];
var images = gns3.images();
for (var i in images) {
images_md5sum.push(images[i].md5sum);
}
{% endblock %}
{% block body %}
<div class="jumbotron">
{% if appliance["status"] == "broken" %}
<div class="alert alert-danger" role="alert">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span>
This appliance is actually not working
</div>
{% endif %}
{% if appliance["status"] == "experimental" %}
<div class="alert alert-warning" role="alert">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span>
This appliance is actually experimental
</div>
{% endif %}
<h1>{{ appliance["name"] }}</h1>
Category {{ appliance["category"] }}<br />
Product: <a href="{{ appliance["product_url"] }}">{{ appliance["product_name"] }}</a><br />
Vendor: <a href="{{ appliance["vendor_url"] }}">{{ appliance["vendor_name"] }}</a><br />
Documentation: <a href="{{ appliance["documentation_url"] }}">{{ appliance["documentation_url"] }}</a><br />
Status: {{ appliance["status"] }}<br />
Maintainer: <a href="mailto:{{ appliance["maintainer_email"] }}">{{ appliance["maintainer"] }}</a>
<p>{{ appliance["description"] | nl2br }}</p>
</div>
{% if appliance["usage"] %}
<h2>Usage</h2>
<p>{{ appliance["usage"] | nl2br }}</p>
{% endif %}
{% if "qemu" in appliance %}
<h2>Qemu settings</h2>
{% for key in appliance["qemu"] %}
{{ key }}: {{ appliance["qemu"][key] }}<br />
{% endfor %}
{% endif %}
{% for version in appliance["versions"] | reverse %}
<h2>{{ appliance["name"] }} {{version["name"]}}</h2>
<button class="btn btn-primary btn-lg" id="install_button_{{loop.index}}" type="button">Install</button>
<script>
gns3_show_if("#install_button_{{loop.index}}", installable_versions.indexOf("{{version["name"]}}") != -1)
</script>
<h3>Require files</h3>
{% set version_id = loop.index %}
{% for image in version.images.values() %}
<h4>{{image["filename"]}}</h4>
Slot: {{image["type"]}}<br />
File Version: {{image["version"]}}<br />
Size: {{ image["filesize"] | human_filesize}}<br />
Checksum: {{image["md5sum"]}}<br />
Download url: <a href="{{image["download_url"]}}">{{image["download_url"]}}</a><br />
{% if "direct_download_url" in image %}
Direct download url: <a href="{{image["direct_download_url"]}}">{{image["direct_download_url"]}}</a><br />
<button id="download_button_{{version_id}}_{{loop.index}}" class="btn btn-primary btn-lg" type="button" onclick='return download("{{appliance|jsonify|b64encode}}", "{{image["md5sum"]}}")'>Download</button>
{% endif %}
<button id="import_button_{{version_id}}_{{loop.index}}" class="btn btn-primary btn-lg" type="button" onclick="gns3_button(function() { return gns3.importAppliance(); })">Import file</button>
<script>
gns3_show_if("#download_button_{{version_id}}_{{loop.index}}", images_md5sum.indexOf("{{image["md5sum"]}}") == -1)
gns3_show_if("#import_button_{{version_id}}_{{loop.index}}", images_md5sum.indexOf("{{image["md5sum"]}}") == -1)
</script>
<hr />
{% endfor %}
{% endfor %}
{% endblock %}

View File

@ -1,17 +0,0 @@
{% extends "layout/default.html" %}
{% block body %}
{% if show_filter %}
<h1>Filter by</h1>
<ul>
<li><a href="/appliances/category">Category</a></li>
<li><a href="/appliances/status">Status</a></li>
<li><a href="/appliances/vendor_name">Vendor</a></li>
</ul>
{% endif %}
<h1>{{title | capitalize | replace("_", " ")}} appliances</h1>
<ul>
{% for appliance in appliances|sort(attribute='name') %}
<li><a href="/appliances/{{appliance["id"]}}.html">{{appliance["name"]}}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -1,5 +0,0 @@
{% extends "layout/default.html" %}
{% block body %}
<iframe src="https://kiwiirc.com/client/irc.freenode.org/gns3" frameborder="0" style="width: 100%; height: 100%; position: absolute">
</iframe>
{% endblock %}

View File

@ -1,48 +0,0 @@
{% extends "layout/default.html" %}
{% block script %}
function update_progress_download_div(div, download_info) {
var percent = Math.round(100 / download_info.total * download_info.received)
div.text(percent + "% "+ humanFileSize(download_info.received) + " / " + humanFileSize(download_info.total));
div.attr("aria-valuenow", percent);
div.attr("style", "width: " + percent + "%");
if (percent == 100) {
div.removeClass("active");
div.removeClass("progress-bar-striped");
}
}
$(function() {
var downloads_div = $("#downloads");
var downloads = gns3.downloads().reverse();
for (var i in downloads) {
download_info = downloads[i];
var parent_div = $("<div>");
parent_div.text(download_info.url);
var progress_div = $("<div>");
progress_div.addClass("progress");
var div = $("<div>");
div.addClass("progress-bar progress-bar-striped active");
div.attr("id", "download" + download_info.id);
div.attr("role", "progressbar");
div.attr("aria-valuemin", 0);
div.attr("aria-valuemax", 100);
update_progress_download_div(div, download_info);
progress_div.append(div);
parent_div.append(progress_div);
downloads_div.append(parent_div);
}
gns3.downloadProgress.connect(function(download_info) {
var div = $("#download" + download_info.id);
update_progress_download_div(div, download_info);
});
});
{% endblock %}
{% block body %}
<div id="downloads">
</div>
{% endblock %}

View File

@ -1,9 +0,0 @@
{% extends "layout/default.html" %}
{% block body %}
<h1>{{group_by|replace("_", " ")}}</h1>
<ul>
{% for key in keys|sort %}
<li><a href="/appliances/{{group_by}}/{{key}}.html">{{key|replace("_", " ")}}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -1,12 +0,0 @@
{% extends "layout/default.html" %}
{% block body %}
<h1>Hello Networker!</h1>
<div>
<a href="http://www.spiceworks.com/gns3/download-free-tftp-server-for-network-configuration-management/?utm_function=acq&utm_channel=aff&medium=aff&utm_source=150158&utm_campaign=&utm_content=tftp-cobrand-980x324"><img src="http://adn.impactradius.com/display-ad/2435-216679"></a>
</div
<p>
<a class="btn btn-primary btn-lg" href="/appliances" role="button">Show appliances</a>
<button class="btn btn-primary btn-lg" type="button" onclick="gns3_button(function() { return gns3.importAppliance(); })">Import appliance</button>
</p>
{% endblock %}

View File

@ -1,123 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=appliance-width, initial-scale=1">
<title></title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<style>
#notif {
position: fixed;
left: 0px;
top: 0px;
z-index: 10;
width: 100%;
}
</style>
<script>
function gns3_notif(type, msg) {
var notif = $("#notif")
notif.append("<div class=\"alert alert-" + type + " alert-dismissible fade in\" role=\"alert\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">×</span></button>" + msg + "</div>")
}
if (typeof gns3 !== "undefined") {
gns3.error.connect(function(msg) {
gns3_notif("danger", msg);
});
gns3.success.connect(function(msg) {
gns3_notif("success", msg);
});
if (gns3.marketplaceApiVersion() != 1) {
alert("Your client is out of date please update it");
}
}
/*
Deactivate the button after success click
*/
gns3_button = function(callback) {
var button = event.target;
$(button).removeClass("btn-primary");
if (callback()) {
$(button).addClass("btn-success");
button.onclick = null;
return true;
}
else {
$(button).addClass("btn-danger");
return false;
}
}
/*
* Show or hide element depending of the test
*/
gns3_show_if = function(id, test) {
if (test) {
$(id).show();
}
else {
$(id).hide();
}
}
function humanFileSize(bytes) {
var exp = Math.log(bytes) / Math.log(1024) | 0;
var result = (bytes / Math.pow(1024, exp)).toFixed(2);
return result + ' ' + (exp == 0 ? 'bytes': 'KMGTPEZY'[exp - 1] + 'B');
}
</script>
<script>
{% block script %}{% endblock %}
</script>
</head>
<body>
<div id="notif">
</div>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">GNS3</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="/appliances">Appliances</a></li>
<li><a href="/downloads.html">Downloads</a></li>
<li><a href="/myimages.html">My images</a></li>
<li><a href="/chat.html">Chat</a></li>
<li><a href="https://community.gns3.com">Community</a></li>
</ul>
</div>
</div>
</nav>
<div class="container-fluid">
{% block body %}{% endblock %}
</div>
<small id="powered"></small>
</body>
</html>

View File

@ -1,33 +0,0 @@
{% extends "layout/default.html" %}
{% block script %}
$(function() {
var table = $("#images");
var images = gns3.images();
for (var i in images) {
image_info = images[i];
var tr = $("<tr>");
tr.append($("<td>").text(image_info.filename));
tr.append($("<td>").text(image_info.md5sum));
tr.append($("<td>").text(humanFileSize(image_info.filesize)));
var a = $("<a>");
a.attr("href", "/images/" + image_info.md5sum + ".html");
a.append($("<button>").attr("class", "btn btn-primary btn-lg").text("Search appliance"));
tr.append($("<td>").append(a))
table.append(tr);
}
});
{% endblock %}
{% block body %}
<table id="images" class="table table-striped">
<tr>
<th>Filename</th>
<th>MD5 sum</th>
<th>Size</th>
<th>Action</th>
</tr>
</table>
{% endblock %}