mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
Merge branch '2.0' into 2.1
This commit is contained in:
275 files changed
+5384
-272
No files matched your search
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
@@ -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"))
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"]),
|
||||
|
||||
Reference in new issue
Block a user