mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-10-24 20:02:35 +03:00
fix(controller): make node names from template names valid hostnames
Creating a node from a template whose name has characters invalid in a hostname, such as "MikroTik CCR1072-1G-8S+", failed on the compute with "'MikroTikCCR1072-1G-8S+-1' is an invalid name to create a Qemu node": the template name was only stripped of spaces. Replace the other invalid characters by hyphens, following the rules the compute validates node names with (RFC 1123 for Qemu and Docker, IOS for Dynamips and IOU), as project import already does for existing nodes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b9cf9158ca
commit
dd69a01fba
@ -38,6 +38,7 @@ from ..config import Config
|
||||
from ..utils.application_id import get_next_application_id, is_iol_runner_environment
|
||||
from ..utils.asyncio import aiozipstream, locking, wait_run_in_executor
|
||||
from ..utils.asyncio.pool import Pool
|
||||
from ..utils.hostname import template_name_to_node_name
|
||||
from ..utils.packet_filter_validation import validate_bpf_syntax
|
||||
from ..utils.path import check_path_allowed, get_default_project_directory
|
||||
from .compute import ComputeError
|
||||
@ -622,7 +623,7 @@ class Project:
|
||||
log.info(f'Creating node from template "{template_name}" on compute "{compute.name}" [{compute.id}]')
|
||||
default_name_format = template.pop("default_name_format", "{name}-{0}")
|
||||
if name is None:
|
||||
name = default_name_format.replace("{name}", template_name)
|
||||
name = default_name_format.replace("{name}", template_name_to_node_name(template_name, node_type))
|
||||
# the appliance metadata stays template level: only the default
|
||||
# credentials are seeded on the node (where they can be overridden)
|
||||
appliance_metadata = template.pop("appliance_metadata", None) or {}
|
||||
|
||||
@ -110,3 +110,22 @@ def to_rfc1123_hostname(name: str) -> str:
|
||||
name = name[:253]
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def template_name_to_node_name(name: str, node_type: str) -> str:
|
||||
"""
|
||||
Convert a template name to the base of the names of the nodes created from it.
|
||||
|
||||
Spaces are removed. For the node types whose names the compute validates as
|
||||
hostnames, the other invalid characters (e.g. "+" in a model name) are replaced
|
||||
by hyphens: RFC 1123 for Qemu and Docker, IOS for Dynamips and IOU.
|
||||
"""
|
||||
|
||||
name = name.replace(" ", "")
|
||||
if node_type in ("qemu", "docker"):
|
||||
name = re.sub(r"[^a-zA-Z0-9.-]", "-", name)
|
||||
elif node_type in ("dynamips", "iou"):
|
||||
name = re.sub(r"[^a-zA-Z0-9-]", "-", name)
|
||||
else:
|
||||
return name
|
||||
return name.strip("-.") or node_type
|
||||
|
||||
@ -253,6 +253,35 @@ async def test_add_node_from_template_seeds_default_credentials(controller):
|
||||
assert "default_password" not in node.properties
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_node_from_template_name_invalid_in_hostname(controller):
|
||||
"""
|
||||
A template name with characters invalid in a hostname (e.g. "+" in a model name)
|
||||
gives node names the compute accepts.
|
||||
"""
|
||||
|
||||
compute = MagicMock()
|
||||
compute.id = "local"
|
||||
controller._computes["local"] = compute
|
||||
project = Project(controller=controller, name="Test")
|
||||
project.emit_notification = MagicMock()
|
||||
|
||||
response = MagicMock()
|
||||
response.json = {"console": 2048}
|
||||
compute.post = AsyncioMagicMock(return_value=response)
|
||||
|
||||
template = {
|
||||
"name": "MikroTik CCR1072-1G-8S+",
|
||||
"template_type": "qemu",
|
||||
"compute_id": "local",
|
||||
"default_name_format": "{name}-{0}",
|
||||
"properties": {},
|
||||
}
|
||||
|
||||
node = await project.add_node_from_template(template)
|
||||
assert node.name == "MikroTikCCR1072-1G-8S-1"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_node_non_local(controller):
|
||||
"""
|
||||
|
||||
@ -14,6 +14,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 pytest
|
||||
|
||||
from gns3server.utils import hostname
|
||||
|
||||
|
||||
@ -118,3 +120,20 @@ def test_rfc1123_conversion_hostname_with_all_numeric_tld_replaced():
|
||||
|
||||
def rfc1123_hostname_with_multiple_consecutive_invalid_characters():
|
||||
assert hostname.to_rfc1123_hostname("hostname!!!.example..com") == "hostname---.example.com"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name,node_type,expected",
|
||||
[
|
||||
("MikroTik CCR1072-1G-8S+", "qemu", "MikroTikCCR1072-1G-8S"),
|
||||
("MikroTik RB450G", "qemu", "MikroTikRB450G"),
|
||||
("Cisco IOSv 15.9", "qemu", "CiscoIOSv15.9"),
|
||||
("ubuntu:22.04", "docker", "ubuntu-22.04"),
|
||||
("IOU L2 15.2", "iou", "IOUL215-2"),
|
||||
("c7200+", "dynamips", "c7200"),
|
||||
("PC 1+", "vpcs", "PC1+"),
|
||||
("+++", "qemu", "qemu"),
|
||||
],
|
||||
)
|
||||
def test_template_name_to_node_name(name, node_type, expected):
|
||||
assert hostname.template_name_to_node_name(name, node_type) == expected
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user