Generate a website with devices

This commit is contained in:
Julien Duponchelle 2015-07-16 15:24:20 +02:00
parent 73231d36a9
commit 3ef5cf875c
18 changed files with 165 additions and 909 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
__pycache__ __pycache__
*.pyc *.pyc
.venv .venv
build

View File

@ -1 +1,2 @@
Jeremy Grossmann Jeremy Grossmann
Julien Duponchelle

View File

@ -4,35 +4,20 @@ GNS3-registry
This is the GNS3 devices registry. This is the GNS3 devices registry.
You need python 3.4 installed Build website
#############
Add an image
************
.. code:: bash .. code:: bash
bin/gns3-get --add ~/Downloads/linux-microcore-3.4.1.img python build.py
Search an image
**************** Run website
#############
.. code:: bash .. code:: bash
bin/gns3-get --search core
Micro Core Linux:
* hda_disk_image:
* 3.4.1 linux-microcore-3.4.1.img: fa2ec4b1fffad67d8103c3391bbf9df2
* 4.0.2 linux-microcore-4.0.2-clean.img: e13d0d1c0b3999ae2386bba70417930c
Install a remote image
**************************
If the image is available from the internet you can download and install it:
.. code:: bash
bin/gns3-get --install e13d0d1c0b3999ae2386bba70417930c
Download http://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/linux-microcore-4.0.2-clean.img to /Users/noplay/GNS3/images/linux-microcore-4.0.2-clean.img python server.py

View File

@ -1,44 +0,0 @@
#!/bin/bash
SCRIPT_PATH="${BASH_SOURCE[0]}";
GNS3_REPOSITORY_DIR=`dirname ${SCRIPT_PATH}`/..
# Detect Python 3.4 installation
PY34_TEST="import sys; sys.exit(1) if sys.version_info[0] != 3 or sys.version_info[1] < 4 else sys.exit(0)"
if python -c "$PY34_TEST" 2> /dev/null
then
PYTHON="python"
elif python3 -c "$PY34_TEST" 2> /dev/null
then
PYTHON="python3"
elif python3.4 -c "$PY34_TEST" 2> /dev/null
then
PYTHON="python3.4"
elif python34 -c "$PY34_TEST" 2> /dev/null
then
PYTHON="python34"
else
echo "No Python 3.4 installation"
echo "On debian based distribution":
echo "apt-get install python3"
echo "On MacOSX:"
echo "brew install python3"
echo "port install python34"
exit 1
fi
$PYTHON -m venv $GNS3_REPOSITORY_DIR/.venv
source $GNS3_REPOSITORY_DIR/.venv/bin/activate
$PYTHON $GNS3_REPOSITORY_DIR/gns3registry/main.py --test
if [ $? -ne 0 ]
then
$PYTHON -m pip install -r $GNS3_REPOSITORY_DIR/requirements.txt
fi
$PYTHON $GNS3_REPOSITORY_DIR/gns3registry/main.py $*

66
build.py Normal file
View File

@ -0,0 +1,66 @@
#!/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
from jinja2 import Environment, FileSystemLoader
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', 'devices'))
def render(template_file, out, **kwargs):
log.info('Build %s', out)
env = Environment(loader=FileSystemLoader('templates'))
env.filters['jsonify'] = json.dumps
template = env.get_template(template_file)
template.stream(**kwargs).dump(os.path.join('build', out))
render('index.html', 'index.html')
devices = []
for file in os.listdir('devices'):
filename = file[:-5]
with open(os.path.join('devices', file)) as f:
device = json.load(f)
device['id'] = filename
render('device.html', os.path.join('devices', filename + '.html'), device=device)
devices.append(device)
render('devices.html', os.path.join('devices', 'index.html'), devices=devices)

View File

@ -1,218 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# 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 json
import sys
import os
import shlex
import subprocess
from gns3registry.image import Image
class ConfigException(Exception):
pass
class Config:
"""
GNS3 config file
"""
def __init__(self, path=None):
"""
:params path: Path of the configuration file otherwise detect it on the system
"""
#TODO: Manage errors
self.path = path
if self.path is None:
self.path = self._get_standard_config_file_path()
with open(self.path) as f:
self._config = json.load(f)
@property
def images_dir(self):
"""
:returns: Localion of the images directory on the server
"""
return self._config["LocalServer"]["images_path"]
def _get_standard_config_file_path(self):
if sys.platform.startswith("win"):
filename = "gns3_gui.ini"
else:
filename = "gns3_gui.conf"
appname = "GNS3"
if sys.platform.startswith("win"):
appdata = os.path.expandvars("%APPDATA%")
return os.path.join(appdata, appname, filename)
else:
home = os.path.expanduser("~")
return os.path.join(home, ".config", appname, filename)
def add_images(self, device_config):
"""
Add images to the user configuration
"""
new_config = {
"server": "local",
"name": device_config["name"]
}
if device_config["category"] == "guest":
new_config["category"] = 2
elif device_config["category"] == "router":
new_config["category"] = 0
if "qemu" in device_config:
self._add_qemu_config(new_config, device_config)
def _add_qemu_config(self, new_config, device_config):
new_config["adapter_type"] = device_config["qemu"]["adapter_type"]
new_config["adapters"] = device_config["qemu"]["adapters"]
new_config["cpu_throttling"] = 0
new_config["ram"] = device_config["qemu"]["ram"]
new_config["legacy_networking"] = False
new_config["process_priority"] = "normal"
new_config["initrd"] = ""
new_config["kernel_command_line"] = ""
new_config["kernel_image"] = ""
if device_config["qemu"].get("graphic", False):
options = ""
else:
options = "-nographic "
options += device_config["qemu"].get("options", "")
new_config["options"] = options.strip()
new_config["hda_disk_image"] = device_config["qemu"].get("hda_disk_image", "")
new_config["hdb_disk_image"] = device_config["qemu"].get("hdb_disk_image", "")
new_config["hdc_disk_image"] = device_config["qemu"].get("hdc_disk_image", "")
new_config["hdd_disk_image"] = device_config["qemu"].get("hdd_disk_image", "")
new_config["qemu_path"] = self._get_qemu_binary(device_config)
if device_config["category"] == "guest":
new_config["default_symbol"] = ":/symbols/qemu_guest.normal.svg"
new_config["hover_symbol"] = ":/symbols/qemu_guest.selected.svg"
elif device_config["category"] == "router":
new_config["default_symbol"] = ":/symbols/router.normal.svg"
new_config["hover_symbol"] = ":/symbols/router.selected.svg"
disks = ["hda_disk_image", "hdb_disk_image", "hdc_disk_image", "hdd_disk_image", "cdrom"]
for disk in disks:
if disk in device_config["images"]:
if isinstance(device_config["images"][disk], list):
require_images = ""
for image in device_config["images"][disk]:
require_images += "* {}\n".format(image["filename"])
raise ConfigException("Missing image for {} you should provide one of the following images:\n{}".format(disk, require_images))
else:
new_config["name"] += " {}".format(device_config["images"][disk].version)
new_config[disk] = self._relative_image_path(device_config["images"][disk].path)
if device_config["qemu"].get("install_cdrom_to_hda", False):
new_config["hda_disk_image"] = self._create_qemu_img(device_config, new_config)
if "cdrom" in new_config:
self._install_qemu_cdrom(device_config, new_config)
del new_config["cdrom"]
# Remove VM with the same Name
self._config["Qemu"]["vms"] = [item for item in self._config["Qemu"]["vms"] if item["name"] != new_config["name"]]
self._config["Qemu"]["vms"].append(new_config)
def _relative_image_path(self, path):
"""
:returns: Path relative to image directory if image is inside or full path
"""
print(os.path.dirname(path))
print(self.images_dir)
if os.path.dirname(path) == self.images_dir:
return os.path.basename(path)
return path
def _get_qemu_binary(self, device_config):
"""
Create a blank hda disk image
:param device_config: The require device configuration
"""
#TODO: Manage Windows
if device_config["qemu"]["processor"] == "i386":
return "qemu-system-i386"
elif device_config["qemu"]["processor"] == "x64":
return "qemu-system-x86_64"
def _create_qemu_img(self, device_config, new_config):
"""
Create a blank hda disk image
:param device_config: The require device configuration
:param new_config: The GNS3 device configuration
:returns: Return new disk path
"""
#TODO: Manage error
image_path = os.path.join(self.images_dir, "QEMU", device_config["qemu"]["hda_disk_image"])
#TODO: raise an error if size is missing
cmd = ["qemu-img", "create", "-f", "qcow2", image_path, device_config["qemu"]["hda_disk_size"]]
print(" ".join(cmd))
subprocess.call(cmd)
return image_path
def _install_qemu_cdrom(self, device_config, new_config):
"""
Install the cdrom to disk
:param device_config: The require device configuration
:param new_config: The GNS3 device configuration
"""
print("Starting cdrom installation. Please follow the instructions in the qemu Windows and close qemu when install is finish in order to finish the process.")
print("\nInstall instructions:")
print(device_config["qemu"]["install_instructions"])
cmd = "{options} -cdrom {cdrom} -m {ram} {hda}".format(
options=device_config.get("options", ""),
cdrom=device_config["images"]["cdrom"].path,
ram=device_config["qemu"]["ram"],
hda=new_config["hda_disk_image"])
self._qemu_run(device_config, cmd.strip())
def _qemu_run(self, device_config, cmd):
"""
Run the qemu command
"""
cmd = shlex.split(cmd)
cmd.insert(0, self._get_qemu_binary(device_config))
print(" ".join(cmd))
subprocess.call(cmd)
def save(self):
"""
Save the configuration file
"""
with open(self.path, "w+") as f:
json.dump(self._config, f, indent=4)

View File

@ -1,65 +0,0 @@
# -*- coding: utf-8 -*-
#
# 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 hashlib
class Image:
"""
A disk image
"""
def __init__(self, path):
"""
:params: path of the image
"""
self.path = path
self._md5sum = None
self._version = None
@property
def version(self):
"""
:returns: Get the file version / release
"""
return self._version
@version.setter
def version(self, version):
"""
:returns: Set the file version / release
"""
self._version = version
@property
def md5sum(self):
"""
Compute a md5 hash for file
:returns: hexadecimal md5
"""
if self._md5sum is None:
m = hashlib.md5()
with open(self.path, "rb") as f:
while True:
buf = f.read(4096)
if not buf:
break
m.update(buf)
self._md5sum = m.hexdigest()
return self._md5sum

View File

@ -1,107 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# 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 argparse
import sys
import os
import math
from distutils.util import strtobool
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from gns3registry.registry import Registry
from gns3registry.config import Config, ConfigException
def yes_no(message):
while True:
try:
return strtobool(input("{} (y/n) ".format(message)).lower())
except ValueError:
pass
def download_progress_callback(count, blockSize, totalSize):
"""
Callback called when a file is downloading
"""
if totalSize == -1:
sys.stdout.write("Unknow size downloading...\n")
percent = int(count * blockSize * 100/totalSize)
sys.stdout.write("\r[{}{}] {} %".format("#" * math.floor(percent / 2), " " * math.ceil(100 / 2 - percent / 2), percent))
if count * blockSize == totalSize:
sys.stdout.write("\n")
sys.stdout.flush()
registry = Registry(download_progress_callback=download_progress_callback)
config = Config()
def add_images(images):
print("WARNING WARNING WARNING")
print("It's experimental")
print("")
confs = registry.detect_images(images)
if len(confs) > 0:
print("Found: {} devices configuration".format(len(confs)))
for conf in confs:
if yes_no("Add {}?".format(conf["name"])):
try:
config.add_images(conf)
except ConfigException as e:
print(e, file=sys.stderr)
sys.exit(1)
config.save()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Manage GNS3 registry")
parser.add_argument("--add", dest="add_images", action="store", nargs='+',
help="Add images to GNS3")
parser.add_argument("--search", dest="search", action="store",
help="Search an image for GNS3")
parser.add_argument("--install", dest="install", action="store",
help="Download and install an image for GNS3")
parser.add_argument("--test", dest="test", action="store_true",
help="Test if installation of gns3 registry is OK")
args = parser.parse_args()
if args.test:
sys.exit(0)
if args.add_images:
add_images(args.add_images)
elif args.search:
print("Available images\n")
for res in registry.search_device(args.search):
print("{}: ".format(res["name"]))
for image_type in res["images"]:
print(" * {}:".format(image_type))
for file in res["images"][image_type]:
print(" * {} {}: {}".format(file["version"], file["filename"], file["md5sum"]))
elif args.install:
image = registry.download_image(args.install, config.images_dir)
if image:
add_images([image])
else:
parser.print_help()
sys.exit(1)

View File

@ -1,121 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# 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 hashlib
import json
import os
import re
import urllib.request
from gns3registry.image import Image
class Registry:
def __init__(self, download_progress_callback=None):
"""
:param download_progress_callback: Callback called when a file is downloaded
"""
self._download_progress_callback = download_progress_callback
def detect_images(self, images_path):
"""
:param images_path: Array of path to images
:returns: Array of configuration corresponding to the image
"""
images = []
for path in images_path:
images.append(Image(path))
configurations = []
#TODO: Manage open error
for config in self._all_configs():
matched = False
for image in images:
if self._image_match(image, config):
matched = True
if matched:
configurations.append(config)
return configurations
def download_image(self, md5sum, images_dir):
for config in self._all_configs():
for image_type in config.get("images", {}):
for file in config["images"].get(image_type, []):
if file["md5sum"] == md5sum:
path = os.path.join(images_dir, "QEMU", file["filename"])
if "direct_download_url" in file:
print("Download {} to {}".format(file["direct_download_url"], path))
#TODO: Skip download if file already exist with same sha1
urllib.request.urlretrieve(file["direct_download_url"], path, reporthook=self._download_progress_callback)
return path
else:
print("You need to manually download the image {filename} from:\n{download_url}\n\nAnd run: ./bin/gns3-get --add {filename}".format(filename=file["filename"], download_url=file["download_url"]))
return None
def search_device(self, query):
results = []
for config in self._all_configs():
if re.match(r".*{}.*".format(query), config["name"], flags=re.IGNORECASE):
results.append(config)
return results
def _all_configs(self):
"""
Iterate on all configs available on devices
"""
devices_path = self._get_devices_path()
for (dirpath, dirnames, filenames) in os.walk(devices_path):
for filename in filenames:
file = os.path.join(dirpath, filename)
if file.endswith(".json"):
with open(os.path.join(devices_path, file)) as f:
config = json.load(f)
yield config
def _image_match(self, image, config):
"""
:returns: True if image is present in configuration
"""
matched = False
for image_type in config.get("images", {}):
images = config["images"].get(image_type, [])
# If it's not a list it's mean we have already detect the image
if not isinstance(images, list):
continue
for file in images:
if isinstance(file, dict):
if file.get("md5sum", None) == image.md5sum:
image.version = file["version"]
config["images"][image_type] = image
matched = True
return matched
def _get_devices_path(self):
"""
Get the path where the registry files are located
"""
path = os.path.abspath(os.path.dirname(__file__))
return os.path.join(path, "..", "devices")

View File

@ -1 +1 @@
jsonschema==2.4.0 Jinja2==2.7.3

View File

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- #!/usr/bin/env python
# #
# Copyright (C) 2015 GNS3 Technologies Inc. # Copyright (C) 2015 GNS3 Technologies Inc.
# #
@ -15,24 +15,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest
import urllib.request
import tempfile
import os import os
import http.server
import socketserver
@pytest.fixture PORT = 8001
def linux_microcore_img():
path = os.path.join(tempfile.tempdir, "linux-microcore-3.4.1.img") os.chdir('build')
if not os.path.exists(path): Handler = http.server.SimpleHTTPRequestHandler
urllib.request.urlretrieve("http://downloads.sourceforge.net/project/gns-3/Qemu%20Appliances/linux-microcore-3.4.1.img?r=&ts=1432209459&use_mirror=heanet", path) httpd = socketserver.TCPServer(('', PORT), Handler)
return path
print('Serving at port', PORT)
@pytest.fixture httpd.serve_forever()
def empty_file(tmpdir):
path = str(tmpdir / "a")
open(path, "w+").close()
return path

20
templates/device.html Normal file
View File

@ -0,0 +1,20 @@
{% extends "layout/default.html" %}
{% block body %}
<div class="jumbotron">
<h1>{{ device["name"] }}</h1>
Vendor: <a href="{{ device["vendor_url"] }}">{{ device["vendor_name"] }}</a>
<br />
Documentation: <a href="{{ device["documentation_url"] }}">{{ device["documentation_url"] }}</a>
</div>
{% for image_type in device["images"] %}
<h2>Images for {{image_type}}</h2>
<ul>
{% for image in device["images"][image_type] %}
<h3>{{image["filename"]}}</h3>
Version: {{image["version"]}}<br />
Checksum: {{image["md5sum"]}}<br />
<button class="btn btn-primary btn-lg" type="button" onclick='gns3.install({{device|jsonify}}, "{{image["md5sum"]}}")'>Install</button>
{% endfor %}
</ul>
{% endfor %}
{% endblock %}

8
templates/devices.html Normal file
View File

@ -0,0 +1,8 @@
{% extends "layout/default.html" %}
{% block body %}
<ul>
{% for device in devices %}
<li><a href="/devices/{{device["id"]}}.html">{{device["name"]}}</a></li>
{% endfor %}
</ul>
{% endblock %}

7
templates/index.html Normal file
View File

@ -0,0 +1,7 @@
{% extends "layout/default.html" %}
{% block body %}
<div class="jumbotron">
<h1>Hello Networker!</h1>
<p><a class="btn btn-primary btn-lg" href="/devices" role="button">Show devices</a></p>
</div>
{% endblock %}

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-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">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
</head>
<body>
<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="/devices">Devices</a></li>
</ul>
</div>
</div>
</nav>
<div class="container-fluid">
{% block body %}{% endblock %}
</div>
</body>
</html>

View File

@ -1,253 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# 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 pytest
import json
import os
from unittest.mock import MagicMock, patch
from gns3registry.config import Config, ConfigException
from gns3registry.image import Image
@pytest.fixture(scope="function")
def empty_config(tmpdir):
config = {
"LocalServer": {
"allow_console_from_anywhere": False,
"auto_start": False,
"console_end_port_range": 5000,
"console_start_port_range": 2001,
"host": "127.0.0.1",
"images_path": str(tmpdir),
"path": "",
"port": 8000,
"projects_path": str(tmpdir),
"report_errors": False,
"udp_end_port_range": 20000,
"udp_start_port_range": 10000
},
"Dynamips": {
"allocate_aux_console_ports": False,
"dynamips_path": "/Applications/GNS3.app/Contents/Resources/dynamips",
"ghost_ios_support": True,
"mmap_support": True,
"routers": [
{
}
],
"sparse_memory_support": True,
"use_local_server": True
},
"IOU": {
"devices": [
{
}
],
"iourc_path": "/Users/noplay/code/gns3/gns3-vagrant/images/iou/iourc.txt",
"iouyap_path": "",
"license_check": True,
"use_local_server": False
},
"Qemu": {
"use_local_server": True,
"vms": [
]
}
}
path = str(tmpdir / "config")
with open(path, "w+") as f:
json.dump(config, f)
return Config(path)
def test_add_images_guest(empty_config, linux_microcore_img):
with open("devices/microcore-linux.json") as f:
config = json.load(f)
image = Image(linux_microcore_img)
image.version = "3.4.1"
config["images"]["hda_disk_image"] = image
empty_config.add_images(config)
assert empty_config._config["Qemu"]["vms"][0] == {
"adapter_type": "e1000",
"adapters": 1,
"category": 2,
"cpu_throttling": 0,
"default_symbol": ":/symbols/qemu_guest.normal.svg",
"hda_disk_image": image.path,
"hdb_disk_image": "",
"hdc_disk_image": "",
"hdd_disk_image": "",
"hover_symbol": ":/symbols/qemu_guest.selected.svg",
"initrd": "",
"kernel_command_line": "",
"kernel_image": "",
"legacy_networking": False,
"name": "Micro Core Linux 3.4.1",
"options": "-nographic",
"process_priority": "normal",
"qemu_path": "qemu-system-i386",
"ram": 32,
"server": "local"
}
def test_add_images_cdrom(empty_config, linux_microcore_img):
with open("devices/hp-vsr1001.json") as f:
config = json.load(f)
hda = os.path.join(empty_config.images_dir, "QEMU", "vsr1000-hp.img")
image = Image(linux_microcore_img)
image.version = "7.10.R0204P01"
config["images"]["cdrom"] = image
with patch("subprocess.call") as mock_qemu_img:
with patch("gns3registry.config.Config._qemu_run") as mock_qemu:
mock_qemu.return_value = hda
empty_config.add_images(config)
assert mock_qemu_img.called
args, kwargs = mock_qemu_img.call_args
assert args[0] == ["qemu-img", "create", "-f", "qcow2", hda, "8G"]
assert mock_qemu.called
args, kwargs = mock_qemu.call_args
assert args[1] == "-cdrom {} -m 1024 {}".format(image.path, hda)
assert empty_config._config["Qemu"]["vms"][0] == {
"adapter_type": "e1000",
"adapters": 16,
"category": 0,
"cpu_throttling": 0,
"default_symbol": ":/symbols/router.normal.svg",
"hda_disk_image": hda,
"hdb_disk_image": "",
"hdc_disk_image": "",
"hdd_disk_image": "",
"hover_symbol": ":/symbols/router.selected.svg",
"initrd": "",
"kernel_command_line": "",
"kernel_image": "",
"legacy_networking": False,
"name": "HP VSR1001 7.10.R0204P01",
"options": "",
"process_priority": "normal",
"qemu_path": "qemu-system-x86_64",
"ram": 1024,
"server": "local"
}
def test_add_images_router_two_disk(empty_config):
with open("devices/arista-veos.json") as f:
config = json.load(f)
image = MagicMock()
image.version = "2.1.0"
image.md5sum = "ea9dc1989764fc6db1d388b061340743016214a7"
image.path = "/a"
config["images"]["hda_disk_image"] = image
image = MagicMock()
image.version = "4.13.8M"
image.md5sum = "ff50656fe817c420e9f7fbb0c0ee41f1ca52fee2"
image.path = "/b"
config["images"]["hdb_disk_image"] = image
empty_config.add_images(config)
assert empty_config._config["Qemu"]["vms"][0]["name"] == "Arista vEOS 2.1.0 4.13.8M"
assert empty_config._config["Qemu"]["vms"][0] == {
"adapter_type": "e1000",
"adapters": 8,
"category": 0,
"cpu_throttling": 0,
"default_symbol": ":/symbols/router.normal.svg",
"hda_disk_image": "/a",
"hdb_disk_image": "/b",
"hdc_disk_image": "",
"hdd_disk_image": "",
"hover_symbol": ":/symbols/router.selected.svg",
"initrd": "",
"kernel_command_line": "",
"kernel_image": "",
"legacy_networking": False,
"name": "Arista vEOS 2.1.0 4.13.8M",
"options": "-nographic",
"process_priority": "normal",
"qemu_path": "qemu-system-x86_64",
"ram": 2048,
"server": "local"
}
def test_add_images_uniq(empty_config, linux_microcore_img):
with open("devices/microcore-linux.json") as f:
config = json.load(f)
image = Image(linux_microcore_img)
image.version = "3.4.1"
config["images"]["hda_disk_image"] = image
empty_config.add_images(config)
config["qemu"]["adapters"] = 2
empty_config.add_images(config)
assert len(empty_config._config["Qemu"]["vms"]) == 1
assert empty_config._config["Qemu"]["vms"][0]["adapters"] == 2
def test_add_images_two_disk_one_missing(empty_config):
with open("devices/arista-veos.json") as f:
config = json.load(f)
image = MagicMock()
image.version = "2.1.0"
image.md5sum = "ea9dc1989764fc6db1d388b061340743016214a7"
config["images"]["hda_disk_image"] = image
with pytest.raises(ConfigException):
empty_config.add_images(config)
assert len(empty_config._config["Qemu"]["vms"]) == 0
def test_add_image_path_relative_to_images_dir(empty_config, tmpdir, linux_microcore_img):
with open("devices/microcore-linux.json") as f:
config = json.load(f)
image = Image(linux_microcore_img)
image.version = "3.4.1"
image.path = str(tmpdir / "linux-microcore-3.4.1.img")
config["images"]["hda_disk_image"] = image
empty_config.add_images(config)
assert empty_config._config["Qemu"]["vms"][0]["hda_disk_image"] == "linux-microcore-3.4.1.img"
def test_save(empty_config, linux_microcore_img):
with open("devices/microcore-linux.json") as f:
config = json.load(f)
image = Image(linux_microcore_img)
image.version = "3.4.1"
config["images"]["hda_disk_image"] = image
empty_config.add_images(config)
empty_config.save()
with open(empty_config.path) as f:
assert "Micro Core" in f.read()

View File

@ -1,56 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# 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 pytest
import json
from gns3registry.registry import Registry
def test_detect_image(linux_microcore_img):
with open("devices/microcore-linux.json") as f:
config = json.load(f)
registry = Registry()
detected = registry.detect_images([linux_microcore_img])
assert detected[0]["name"] == "Micro Core Linux"
assert detected[0]["images"]["hda_disk_image"].version == "3.4.1"
def test_detect_two_image(linux_microcore_img):
with open("devices/microcore-linux.json") as f:
config = json.load(f)
registry = Registry()
detected = registry.detect_images([linux_microcore_img])
assert detected[0]["name"] == "Micro Core Linux"
assert detected[0]["images"]["hda_disk_image"].version == "3.4.1"
def test_detect_unknow_image(empty_file):
registry = Registry()
assert registry.detect_images([empty_file]) == []
def test_search_device():
registry = Registry()
results = registry.search_device("Micro Core Linux")
assert len(results) == 1