mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
Controller side unique node name allocation. Ref #541.
This commit is contained in:
1 parent
6cd136b423
commit
2bde02d459
11 files changed
+128
-47
No files matched your search
@@ -332,7 +332,7 @@ class Compute:
|
||||
if body:
|
||||
try:
|
||||
msg = json.loads(body)["message"]
|
||||
except (KeyError, json.decoder.JSONDecodeError):
|
||||
except (KeyError, ValueError):
|
||||
msg = body
|
||||
else:
|
||||
msg = ""
|
||||
@@ -349,7 +349,7 @@ class Compute:
|
||||
try:
|
||||
raise ComputeConflict(json.loads(body))
|
||||
# If the 409 doesn't come from a GNS3 server
|
||||
except json.decoder.JSONDecodeError:
|
||||
except ValueError:
|
||||
raise aiohttp.web.HTTPConflict(text=msg)
|
||||
elif response.status == 500:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Internal server error {}".format(url))
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
# 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 aiohttp
|
||||
import asyncio
|
||||
import copy
|
||||
import uuid
|
||||
@@ -30,10 +29,11 @@ class Node:
|
||||
# This properties are used only on controller and are not forwarded to the compute
|
||||
CONTROLLER_ONLY_PROPERTIES = ["x", "y", "z", "symbol", "label", "console_host"]
|
||||
|
||||
def __init__(self, project, compute, node_id=None, node_type=None, **kwargs):
|
||||
def __init__(self, project, compute, name, node_id=None, node_type=None, **kwargs):
|
||||
"""
|
||||
:param project: Project of the node
|
||||
:param compute: Compute server where the server will run
|
||||
:param name: Node name
|
||||
:param node_id: UUID of the node (integer)
|
||||
:param node_type: Type of emulator
|
||||
:param kwargs: Node properties
|
||||
@@ -48,7 +48,7 @@ class Node:
|
||||
self._compute = compute
|
||||
self._node_type = node_type
|
||||
|
||||
self._name = None
|
||||
self._name = name
|
||||
self._console = None
|
||||
self._console_type = None
|
||||
self._properties = {}
|
||||
@@ -83,10 +83,10 @@ class Node:
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, val):
|
||||
self._name = val
|
||||
def name(self, new_name):
|
||||
self._name = new_name
|
||||
# The text in label need to be always the node name
|
||||
self._label["text"] = val
|
||||
self._label["text"] = new_name
|
||||
|
||||
@property
|
||||
def node_type(self):
|
||||
@@ -205,6 +205,9 @@ class Node:
|
||||
# When updating properties used only on controller we don't need to call the compute
|
||||
update_compute = False
|
||||
|
||||
# update the node name if present
|
||||
self._project.update_node_name(self, kwargs.get("name"))
|
||||
|
||||
# Update node properties with additional elements
|
||||
for prop in kwargs:
|
||||
if getattr(self, prop) != kwargs[prop]:
|
||||
|
||||
@@ -54,6 +54,7 @@ class Project:
|
||||
self.path = path
|
||||
|
||||
self._computes = set()
|
||||
self._allocated_node_names = set()
|
||||
self._nodes = {}
|
||||
self._links = {}
|
||||
|
||||
@@ -105,15 +106,89 @@ class Project:
|
||||
def add_compute(self, compute):
|
||||
self._computes.add(compute)
|
||||
|
||||
def allocate_node_name(self, base_name):
|
||||
"""
|
||||
Allocates a new unique name for a node in this project.
|
||||
|
||||
:param base_name: base name for the node which will be completed with a unique number.
|
||||
|
||||
:returns: allocated name or None if one could not be found
|
||||
"""
|
||||
|
||||
if '{0}' in base_name or '{id}' in base_name:
|
||||
# base name is a template, replace {0} or {id} by an unique identifier
|
||||
for number in range(1, 1000000):
|
||||
name = base_name.format(number, id=number)
|
||||
if name not in self._allocated_node_names:
|
||||
self._allocated_node_names.add(name)
|
||||
return name
|
||||
else:
|
||||
if base_name not in self._allocated_node_names:
|
||||
return base_name
|
||||
# base name is not unique, let's find a unique name by appending a number
|
||||
for number in range(1, 1000000):
|
||||
name = base_name + str(number)
|
||||
if name not in self._allocated_node_names:
|
||||
self._allocated_node_names.add(name)
|
||||
return name
|
||||
return None
|
||||
|
||||
def remove_allocated_node_name(self, name):
|
||||
"""
|
||||
Removes an allocated node name
|
||||
|
||||
:param name: allocated node name
|
||||
"""
|
||||
|
||||
if name in self._allocated_node_names:
|
||||
self._allocated_node_names.remove(name)
|
||||
|
||||
def update_allocated_node_name(self, name):
|
||||
"""
|
||||
Updates a node name
|
||||
|
||||
:param name: new node name
|
||||
"""
|
||||
|
||||
self.remove_allocated_node_name(name)
|
||||
self._allocated_node_names.add(name)
|
||||
|
||||
def has_allocated_node_name(self, name):
|
||||
"""
|
||||
Returns either a node name is already allocated or not.
|
||||
|
||||
:param name: node name
|
||||
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
if name in self._allocated_node_names:
|
||||
return True
|
||||
return False
|
||||
|
||||
def update_node_name(self, node, new_name):
|
||||
|
||||
if new_name and node.name != new_name:
|
||||
if self.has_allocated_node_name(new_name):
|
||||
raise aiohttp.web.HTTPConflict(text="{} node name is already allocated in this project".format(new_name))
|
||||
self.update_allocated_node_name(new_name)
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_node(self, compute, node_id, **kwargs):
|
||||
def add_node(self, compute, name, node_id, **kwargs):
|
||||
"""
|
||||
Create a node or return an existing node
|
||||
|
||||
:param kwargs: See the documentation of node
|
||||
"""
|
||||
if node_id not in self._nodes:
|
||||
node = Node(self, compute, node_id=node_id, **kwargs)
|
||||
|
||||
name = self.allocate_node_name(name)
|
||||
if not name:
|
||||
raise aiohttp.web.HTTPConflict(text="A node name could not be allocated (node limit reached?)")
|
||||
|
||||
node = Node(self, compute, name, node_id=node_id, **kwargs)
|
||||
if compute not in self._project_created_on_compute:
|
||||
# For a local server we send the project path
|
||||
if compute.id == "local":
|
||||
@@ -137,7 +212,9 @@ class Project:
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete_node(self, node_id):
|
||||
|
||||
node = self.get_node(node_id)
|
||||
self.remove_allocated_node_name(node.name)
|
||||
del self._nodes[node.id]
|
||||
yield from node.destroy()
|
||||
self.controller.notification.emit("node.deleted", node.__json__())
|
||||
@@ -194,6 +271,7 @@ class Project:
|
||||
def close(self):
|
||||
for compute in self._project_created_on_compute:
|
||||
yield from compute.post("/projects/{}/close".format(self._id))
|
||||
self._allocated_node_names.clear()
|
||||
|
||||
@asyncio.coroutine
|
||||
def commit(self):
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# 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 aiohttp
|
||||
|
||||
from gns3server.web.route import Route
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.utils.asyncio.pool import Pool
|
||||
@@ -47,7 +49,7 @@ class NodeHandler:
|
||||
controller = Controller.instance()
|
||||
compute = controller.get_compute(request.json.pop("compute_id"))
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
node = yield from project.add_node(compute, request.json.pop("node_id", None), **request.json)
|
||||
node = yield from project.add_node(compute, request.json.pop("name"), request.json.pop("node_id", None), **request.json)
|
||||
response.set_status(201)
|
||||
response.json(node)
|
||||
|
||||
|
||||
Reference in new issue
Block a user