mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 22:11:14 +02:00
Allocate application IDs for IOU nodes on the controller.
An application ID is used by IOU to generate its interface Mac addresses. They must be unique across all opened projects sharing the same computes to avoid Mac address collisions.
This commit is contained in:
1 parent
6cb87ef25d
commit
c5412bf970
10 files changed
+194
-119
No files matched your search
@@ -25,7 +25,6 @@ import asyncio
|
||||
from ..base_manager import BaseManager
|
||||
from .iou_error import IOUError
|
||||
from .iou_vm import IOUVM
|
||||
from .utils.application_id import get_next_application_id
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -48,12 +47,7 @@ class IOU(BaseManager):
|
||||
:returns: IOUVM instance
|
||||
"""
|
||||
|
||||
async with self._iou_id_lock:
|
||||
# wait for a node to be completely created before adding a new one
|
||||
# this is important otherwise we allocate the same application ID
|
||||
# when creating multiple IOU node at the same time
|
||||
application_id = get_next_application_id(self.nodes)
|
||||
node = await super().create_node(*args, application_id=application_id, **kwargs)
|
||||
node = await super().create_node(*args, **kwargs)
|
||||
return node
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -70,6 +70,10 @@ class IOUVM(BaseNode):
|
||||
|
||||
super().__init__(name, node_id, project, manager, console=console, console_type=console_type)
|
||||
|
||||
log.info('IOU "{name}" [{id}]: assigned with application ID {application_id}'.format(name=self._name,
|
||||
id=self._id,
|
||||
application_id=application_id))
|
||||
|
||||
self._iou_process = None
|
||||
self._telnet_server = None
|
||||
self._iou_stdout_file = ""
|
||||
|
||||
@@ -481,7 +481,7 @@ class Controller:
|
||||
@property
|
||||
def projects(self):
|
||||
"""
|
||||
:returns: The dictionary of projects managed by GNS3
|
||||
:returns: The dictionary of projects managed by the controller
|
||||
"""
|
||||
|
||||
return self._projects
|
||||
|
||||
@@ -38,6 +38,7 @@ from .topology import project_to_topology, load_topology
|
||||
from .udp_link import UDPLink
|
||||
from ..config import Config
|
||||
from ..utils.path import check_path_allowed, get_default_project_directory
|
||||
from ..utils.application_id import get_next_application_id
|
||||
from ..utils.asyncio.pool import Pool
|
||||
from ..utils.asyncio import locking
|
||||
from ..utils.asyncio import aiozipstream
|
||||
@@ -126,6 +127,8 @@ class Project:
|
||||
assert self._status != "closed"
|
||||
self.dump()
|
||||
|
||||
self._iou_id_lock = asyncio.Lock()
|
||||
|
||||
def emit_notification(self, action, event):
|
||||
"""
|
||||
Emit a notification to all clients using this project.
|
||||
@@ -516,17 +519,7 @@ class Project:
|
||||
node = await self.add_node(compute, name, node_id, node_type=node_type, **template)
|
||||
return node
|
||||
|
||||
@open_required
|
||||
async def add_node(self, compute, name, node_id, dump=True, node_type=None, **kwargs):
|
||||
"""
|
||||
Create a node or return an existing node
|
||||
|
||||
:param dump: Dump topology to disk
|
||||
:param kwargs: See the documentation of node
|
||||
"""
|
||||
|
||||
if node_id in self._nodes:
|
||||
return self._nodes[node_id]
|
||||
async def _create_node(self, compute, name, node_id, node_type=None, **kwargs):
|
||||
|
||||
node = Node(self, compute, name, node_id=node_id, node_type=node_type, **kwargs)
|
||||
if compute not in self._project_created_on_compute:
|
||||
@@ -547,10 +540,39 @@ class Project:
|
||||
data["variables"] = self._variables
|
||||
|
||||
await compute.post("/projects", data=data)
|
||||
|
||||
self._project_created_on_compute.add(compute)
|
||||
|
||||
await node.create()
|
||||
self._nodes[node.id] = node
|
||||
|
||||
return node
|
||||
|
||||
@open_required
|
||||
async def add_node(self, compute, name, node_id, dump=True, node_type=None, **kwargs):
|
||||
"""
|
||||
Create a node or return an existing node
|
||||
|
||||
:param dump: Dump topology to disk
|
||||
:param kwargs: See the documentation of node
|
||||
"""
|
||||
|
||||
if node_id in self._nodes:
|
||||
return self._nodes[node_id]
|
||||
|
||||
if node_type == "iou":
|
||||
async with self._iou_id_lock:
|
||||
# wait for a IOU node to be completely created before adding a new one
|
||||
# this is important otherwise we allocate the same application ID (used
|
||||
# to generate MAC addresses) when creating multiple IOU node at the same time
|
||||
if "properties" in kwargs.keys():
|
||||
# allocate a new application id for nodes loaded from the project
|
||||
kwargs.get("properties")["application_id"] = get_next_application_id(self._controller.projects, compute)
|
||||
elif "application_id" not in kwargs.keys() and not kwargs.get("properties"):
|
||||
# allocate a new application id for nodes added to the project
|
||||
kwargs["application_id"] = get_next_application_id(self._controller.projects, compute)
|
||||
node = await self._create_node(compute, name, node_id, node_type, **kwargs)
|
||||
else:
|
||||
node = await self._create_node(compute, name, node_id, node_type, **kwargs)
|
||||
self.emit_notification("node.created", node.__json__())
|
||||
if dump:
|
||||
self.dump()
|
||||
@@ -1102,12 +1124,11 @@ class Project:
|
||||
data['z'] = z
|
||||
data['locked'] = False # duplicated node must not be locked
|
||||
new_node_uuid = str(uuid.uuid4())
|
||||
new_node = await self.add_node(
|
||||
node.compute,
|
||||
node.name,
|
||||
new_node_uuid,
|
||||
node_type=node_type,
|
||||
**data)
|
||||
new_node = await self.add_node(node.compute,
|
||||
node.name,
|
||||
new_node_uuid,
|
||||
node_type=node_type,
|
||||
**data)
|
||||
try:
|
||||
await node.post("/duplicate", timeout=None, data={
|
||||
"destination_node_id": new_node_uuid
|
||||
|
||||
@@ -58,16 +58,17 @@ class IOUHandler:
|
||||
|
||||
iou = IOU.instance()
|
||||
vm = await iou.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
path=request.json.get("path"),
|
||||
console=request.json.get("console"),
|
||||
console_type=request.json.get("console_type", "telnet"))
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
application_id=request.json.get("application_id"),
|
||||
path=request.json.get("path"),
|
||||
console=request.json.get("console"),
|
||||
console_type=request.json.get("console_type", "telnet"))
|
||||
|
||||
for name, value in request.json.items():
|
||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||
if name == "application_id":
|
||||
continue # we must ignore this to avoid overwriting the application_id allocated by the IOU manager
|
||||
continue # we must ignore this to avoid overwriting the application_id allocated by the controller
|
||||
if name == "startup_config_content" and (vm.startup_config_content and len(vm.startup_config_content) > 0):
|
||||
continue
|
||||
if name == "private_config_content" and (vm.private_config_content and len(vm.private_config_content) > 0):
|
||||
|
||||
@@ -96,7 +96,7 @@ IOU_CREATE_SCHEMA = {
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["name", "path"]
|
||||
"required": ["application_id", "name", "path"]
|
||||
}
|
||||
|
||||
|
||||
|
||||
+14
-6
@@ -15,24 +15,32 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ..iou_error import IOUError
|
||||
import aiohttp
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_next_application_id(nodes):
|
||||
def get_next_application_id(projects, compute):
|
||||
"""
|
||||
Calculates free application_id from given nodes
|
||||
|
||||
:param nodes:
|
||||
:raises IOUError when exceeds number
|
||||
:param projects: all projects managed by controller
|
||||
:param compute: Compute instance
|
||||
:raises HTTPConflict when exceeds number
|
||||
:return: integer first free id
|
||||
"""
|
||||
|
||||
used = set([n.application_id for n in nodes])
|
||||
nodes = []
|
||||
|
||||
# look for application id for in all nodes across all opened projects that share the same compute
|
||||
for project in projects.values():
|
||||
if project.status == "opened" and compute in project.computes:
|
||||
nodes.extend(list(project.nodes.values()))
|
||||
|
||||
used = set([n.properties["application_id"] for n in nodes if n.node_type == "iou"])
|
||||
pool = set(range(1, 512))
|
||||
try:
|
||||
return (pool - used).pop()
|
||||
except KeyError:
|
||||
raise IOUError("Cannot create a new IOU VM (limit of 512 VMs on one host reached)")
|
||||
raise aiohttp.web.HTTPConflict(text="Cannot create a new IOU node (limit of 512 nodes across all opened projects using compute {} reached".format(compute.name))
|
||||
Reference in new issue
Block a user