Merge branch '2.0' into 2.1

This commit is contained in:
Julien Duponchelle committed 2017-03-30 10:16:16 +02:00
commit 4f1b738ef5
275 files changed
+5384 -272

No files matched your search

+39 -19
View File
@@ -16,7 +16,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import json
import uuid
import socket
import shutil
import asyncio
@@ -55,7 +57,7 @@ class Controller:
# Store settings shared by the different GUI will be replace by dedicated API later
self._settings = None
self._local_server = None
self._config_file = os.path.join(Config.instance().config_dir, "gns3_controller.conf")
log.info("Load controller configuration file {}".format(self._config_file))
@@ -75,6 +77,7 @@ class Controller:
log.info("Start controller")
self.load_base_files()
server_config = Config.instance().get_section_config("Server")
Config.instance().listen_for_config_changes(self._update_config)
host = server_config.get("host", "localhost")
# If console_host is 0.0.0.0 client will use the ip they use
@@ -88,15 +91,19 @@ class Controller:
name = "Main server"
computes = yield from self._load_controller_settings()
yield from self.add_compute(compute_id="local",
name=name,
protocol=server_config.get("protocol", "http"),
host=host,
console_host=console_host,
port=server_config.getint("port", 3080),
user=server_config.get("user", ""),
password=server_config.get("password", ""),
force=True)
try:
self._local_server = yield from self.add_compute(compute_id="local",
name=name,
protocol=server_config.get("protocol", "http"),
host=host,
console_host=console_host,
port=server_config.getint("port", 3080),
user=server_config.get("user", ""),
password=server_config.get("password", ""),
force=True)
except aiohttp.web_exceptions.HTTPConflict as e:
log.fatal("Can't acces to the local server, make sure anything else is not running on the same port")
sys.exit(1)
for c in computes:
try:
yield from self.add_compute(**c)
@@ -106,6 +113,16 @@ class Controller:
yield from self.gns3vm.auto_start_vm()
yield from self._project_auto_open()
def _update_config(self):
"""
Call this when the server configuration file
change
"""
if self._local_server:
server_config = Config.instance().get_section_config("Server")
self._local_server.user = server_config.get("user")
self._local_server.password = server_config.get("password")
@asyncio.coroutine
def stop(self):
log.info("Stop controller")
@@ -239,14 +256,17 @@ class Controller:
data = json.load(f)
server_settings = data.get("Servers", {})
for remote in server_settings.get("remote_servers", []):
yield from self.add_compute(
host=remote.get("host", "localhost"),
port=remote.get("port", 3080),
protocol=remote.get("protocol", "http"),
name=remote.get("url"),
user=remote.get("user"),
password=remote.get("password")
)
try:
yield from self.add_compute(
host=remote.get("host", "localhost"),
port=remote.get("port", 3080),
protocol=remote.get("protocol", "http"),
name=remote.get("url"),
user=remote.get("user"),
password=remote.get("password")
)
except aiohttp.web.HTTPConflict:
pass # if the server is broken we skip it
if "vm" in server_settings:
vmname = None
vm_settings = server_settings["vm"]
@@ -282,12 +302,12 @@ class Controller:
"""
Store settings shared by the different GUI will be replace by dedicated API later. Dictionnary
"""
assert self._settings is not None
return self._settings
@settings.setter
def settings(self, val):
self._settings = val
self._settings["modification_uuid"] = str(uuid.uuid4()) # We add a modification id to the settings it's help the gui to detect changes
self.save()
self.notification.emit("settings.updated", val)
+4 -2
View File
@@ -128,7 +128,7 @@ class Compute:
self._user = user.strip()
if password:
self._password = password.strip()
self._auth = aiohttp.BasicAuth(self._user, self._password)
self._auth = aiohttp.BasicAuth(self._user, self._password, "utf-8")
else:
self._password = None
self._auth = aiohttp.BasicAuth(self._user, "")
@@ -397,6 +397,8 @@ class Compute:
raise aiohttp.web.HTTPConflict(text="Invalid auth for server {}".format(self._id))
except aiohttp.web.HTTPServiceUnavailable:
raise aiohttp.web.HTTPConflict(text="The server {} is unavailable".format(self._id))
except ValueError:
raise aiohttp.web.HTTPConflict(text="Invalid server url for server {}".format(self._id))
if "version" not in response.json:
self._http_session.close()
@@ -502,7 +504,7 @@ class Compute:
response = yield from self._session().request(method, url, headers=headers, data=data, auth=self._auth, chunked=chunked, timeout=timeout)
except asyncio.TimeoutError as e:
raise ComputeError("Timeout error when connecting to {}".format(url))
except (aiohttp.errors.ClientOSError, aiohttp.errors.ClientRequestError, aiohttp.ClientResponseError) as e:
except (aiohttp.errors.ClientOSError, aiohttp.errors.ClientRequestError, aiohttp.errors.ServerDisconnectedError, aiohttp.ClientResponseError, ValueError) as e:
raise ComputeError(str(e))
body = yield from response.read()
if body and not raw:
+2
View File
@@ -191,3 +191,5 @@ def _export_images(project, image, z):
arcname = os.path.join("images", directory, os.path.basename(image))
z.write(path, arcname)
break
else:
raise aiohttp.web.HTTPConflict(text="Topology could not be exported because the image {} is not available. If you use multiple server, we need a copy of the image on the main server.".format(path))
+4 -1
View File
@@ -75,7 +75,10 @@ def import_project(controller, project_id, stream, location=None, name=None, kee
else:
projects_path = controller.projects_directory()
path = os.path.join(projects_path, project_name)
os.makedirs(path, exist_ok=True)
try:
os.makedirs(path, exist_ok=True)
except UnicodeEncodeError as e:
raise aiohttp.web.HTTPConflict(text="The project name contain non supported or invalid characters")
myzip.extractall(path)
topology = load_topology(os.path.join(path, "project.gns3"))
+2 -1
View File
@@ -18,6 +18,7 @@
import os
import re
import uuid
import html
import asyncio
import aiohttp
@@ -83,7 +84,7 @@ class Link:
"x": -10,
"y": -10,
"rotation": 0,
"text": "{}/{}".format(adapter_number, port_number),
"text": html.escape("{}/{}".format(adapter_number, port_number)),
"style": "font-size: 10; font-style: Verdana"
}
+2 -1
View File
@@ -17,6 +17,7 @@
import aiohttp
import asyncio
import html
import copy
import uuid
import os
@@ -238,7 +239,7 @@ class Node:
self._label = {
"y": round(self._height / 2 + 10) * -1,
"text": self._name,
"text": html.escape(self._name),
"style": style,
"x": None, # None: mean the client should center it
"rotation": 0
+1 -1
View File
@@ -66,7 +66,7 @@ class Port:
# If port name format has change we use the port name as the short name (1.X behavior)
if self._short_name:
return self._short_name
elif not self._name.startswith(self.long_name_type()):
elif not self._name.startswith("{}{}".format(self.long_name_type(), self._interface_number)):
return self._name
return self.short_name_type + "{}/{}".format(self._interface_number, self._port_number)
+6 -2
View File
@@ -637,7 +637,7 @@ class Project:
for node in topology.get("nodes", []):
compute = self.controller.get_compute(node.pop("compute_id"))
name = node.pop("name")
node_id = node.pop("node_id")
node_id = node.pop("node_id", str(uuid.uuid4()))
yield from self.add_node(compute, name, node_id, dump=False, **node)
for link_data in topology.get("links", []):
link = yield from self.add_link(link_id=link_data["link_id"])
@@ -663,7 +663,10 @@ class Project:
pass
self._status = "closed"
self._loading = False
raise e
if isinstance(e, ComputeError):
raise aiohttp.web.HTTPConflict(text=str(e))
else:
raise e
try:
os.remove(path + ".backup")
except OSError:
@@ -699,6 +702,7 @@ class Project:
if self._status == "closed":
yield from self.open()
self.dump()
try:
with tempfile.TemporaryDirectory() as tmpdir:
zipstream = yield from export_project(self, tmpdir, keep_compute_id=True, allow_all_nodes=True)
+11 -5
View File
@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import html
import json
import copy
import uuid
@@ -285,7 +286,10 @@ def _convert_1_3_later(topo, topo_path):
for old_node in topo.get("nodes", []):
node = {}
node["console"] = old_node["properties"].get("console", None)
node["compute_id"] = server_id_to_compute_id[old_node["server_id"]]
try:
node["compute_id"] = server_id_to_compute_id[old_node["server_id"]]
except KeyError:
node["compute_id"] = "local"
node["console_type"] = old_node["properties"].get("console_type", "telnet")
node["name"] = old_node["label"]["text"]
node["label"] = _convert_label(old_node["label"])
@@ -376,9 +380,11 @@ def _convert_1_3_later(topo, topo_path):
node["symbol"] = ":/symbols/router.svg"
elif old_node["type"] == "Cloud":
old_node["ports"] = _create_cloud(node, old_node, ":/symbols/cloud.svg")
symbol = old_node.get("symbol", ":/symbols/cloud.svg")
old_node["ports"] = _create_cloud(node, old_node, symbol)
elif old_node["type"] == "Host":
old_node["ports"] = _create_cloud(node, old_node, ":/symbols/computer.svg")
symbol = old_node.get("symbol", ":/symbols/computer.svg")
old_node["ports"] = _create_cloud(node, old_node, symbol)
else:
raise NotImplementedError("Conversion of {} is not supported".format(old_node["type"]))
@@ -463,7 +469,7 @@ def _convert_1_3_later(topo, topo_path):
size=int(font_info[1]),
weight=weight,
style=style,
text=note["text"]
text=html.escape(note["text"])
)
new_note = {
"drawing_id": str(uuid.uuid4()),
@@ -550,7 +556,7 @@ def _convert_label(label):
"""
style = qt_font_to_style(label.get("font"), label.get("color"))
return {
"text": label["text"],
"text": html.escape(label["text"]),
"rotation": 0,
"style": style,
"x": int(label["x"]),