1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-28 03:08:14 +00:00

Manage base configuration on server

Fix #786
This commit is contained in:
Julien Duponchelle 2017-02-02 19:13:47 +01:00
parent afcd27f348
commit e892e5dfab
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
32 changed files with 652 additions and 334 deletions

View File

@ -23,6 +23,7 @@ A minimal version:
The revision is the version of file format: The revision is the version of file format:
* 8: GNS3 2.1
* 7: GNS3 2.0 * 7: GNS3 2.0
* 6: GNS3 2.0 < beta 3 * 6: GNS3 2.0 < beta 3
* 5: GNS3 2.0 < alpha 4 * 5: GNS3 2.0 < alpha 4

View File

@ -515,25 +515,12 @@ class Dynamips(BaseManager):
default_startup_config_path = os.path.join(module_workdir, vm.id, "configs", "i{}_startup-config.cfg".format(vm.dynamips_id)) default_startup_config_path = os.path.join(module_workdir, vm.id, "configs", "i{}_startup-config.cfg".format(vm.dynamips_id))
default_private_config_path = os.path.join(module_workdir, vm.id, "configs", "i{}_private-config.cfg".format(vm.dynamips_id)) default_private_config_path = os.path.join(module_workdir, vm.id, "configs", "i{}_private-config.cfg".format(vm.dynamips_id))
startup_config_path = settings.get("startup_config")
startup_config_content = settings.get("startup_config_content") startup_config_content = settings.get("startup_config_content")
if startup_config_path: if startup_config_content:
yield from vm.set_configs(startup_config_path) self._create_config(vm, default_startup_config_path, startup_config_content)
elif startup_config_content:
startup_config_path = self._create_config(vm, default_startup_config_path, startup_config_content)
yield from vm.set_configs(startup_config_path)
elif os.path.isfile(default_startup_config_path) and os.path.getsize(default_startup_config_path) == 0:
# An empty startup-config may crash Dynamips
startup_config_path = self._create_config(vm, default_startup_config_path, "!\n")
yield from vm.set_configs(startup_config_path)
private_config_path = settings.get("private_config")
private_config_content = settings.get("private_config_content") private_config_content = settings.get("private_config_content")
if private_config_path: if private_config_content:
yield from vm.set_configs(vm.startup_config, private_config_path) self._create_config(vm, default_private_config_path, private_config_content)
elif private_config_content:
private_config_path = self._create_config(vm, default_private_config_path, private_config_content)
yield from vm.set_configs(vm.startup_config, private_config_path)
def _create_config(self, vm, path, content=None): def _create_config(self, vm, path, content=None):
""" """
@ -553,6 +540,11 @@ class Dynamips(BaseManager):
except OSError as e: except OSError as e:
raise DynamipsError("Could not create Dynamips configs directory: {}".format(e)) raise DynamipsError("Could not create Dynamips configs directory: {}".format(e))
if content is None or len(content) == 0:
content = "!\n"
if os.path.exists(path):
return
try: try:
with open(path, "wb") as f: with open(path, "wb") as f:
if content: if content:

View File

@ -78,8 +78,6 @@ class Router(BaseNode):
self._dynamips_id = dynamips_id self._dynamips_id = dynamips_id
self._platform = platform self._platform = platform
self._image = "" self._image = ""
self._startup_config = ""
self._private_config = ""
self._ram = 128 # Megabytes self._ram = 128 # Megabytes
self._nvram = 128 # Kilobytes self._nvram = 128 # Kilobytes
self._mmap = True self._mmap = True
@ -102,8 +100,6 @@ class Router(BaseNode):
self._slots = [] self._slots = []
self._ghost_flag = ghost_flag self._ghost_flag = ghost_flag
self._memory_watcher = None self._memory_watcher = None
self._startup_config_content = ""
self._private_config_content = ""
if not ghost_flag: if not ghost_flag:
if not dynamips_id: if not dynamips_id:
@ -152,8 +148,6 @@ class Router(BaseNode):
"platform": self._platform, "platform": self._platform,
"image": self._image, "image": self._image,
"image_md5sum": md5sum(self._image), "image_md5sum": md5sum(self._image),
"startup_config": self._startup_config,
"private_config": self._private_config,
"ram": self._ram, "ram": self._ram,
"nvram": self._nvram, "nvram": self._nvram,
"mmap": self._mmap, "mmap": self._mmap,
@ -171,9 +165,7 @@ class Router(BaseNode):
"console_type": "telnet", "console_type": "telnet",
"aux": self.aux, "aux": self.aux,
"mac_addr": self._mac_addr, "mac_addr": self._mac_addr,
"system_id": self._system_id, "system_id": self._system_id}
"startup_config_content": self._startup_config_content,
"private_config_content": self._private_config_content}
# return the relative path if the IOS image is in the images_path directory # return the relative path if the IOS image is in the images_path directory
router_info["image"] = self.manager.get_relative_image_path(self._image) router_info["image"] = self.manager.get_relative_image_path(self._image)
@ -289,6 +281,16 @@ class Router(BaseNode):
if not self._ghost_flag: if not self._ghost_flag:
self.check_available_ram(self.ram) self.check_available_ram(self.ram)
startup_config_path = os.path.join("configs", "i{}_startup-config.cfg".format(self._dynamips_id))
private_config_path = os.path.join("configs", "i{}_private-config.cfg".format(self._dynamips_id))
if not os.path.exists(private_config_path) or not os.path.getsize(private_config_path):
# an empty private-config can prevent a router to boot.
private_config_path = ''
yield from self._hypervisor.send('vm set_config "{name}" "{startup}" "{private}"'.format(
name=self._name,
startup=startup_config_path,
private=private_config_path))
yield from self._hypervisor.send('vm start "{name}"'.format(name=self._name)) yield from self._hypervisor.send('vm start "{name}"'.format(name=self._name))
self.status = "started" self.status = "started"
log.info('router "{name}" [{id}] has been started'.format(name=self._name, id=self._id)) log.info('router "{name}" [{id}] has been started'.format(name=self._name, id=self._id))
@ -1458,26 +1460,6 @@ class Router(BaseNode):
return self._slots return self._slots
@property
def startup_config(self):
"""
Returns the startup-config for this router.
:returns: path to startup-config file
"""
return self._startup_config
@property
def private_config(self):
"""
Returns the private-config for this router.
:returns: path to private-config file
"""
return self._private_config
@asyncio.coroutine @asyncio.coroutine
def set_name(self, new_name): def set_name(self, new_name):
""" """
@ -1486,7 +1468,6 @@ class Router(BaseNode):
:param new_name: new name string :param new_name: new name string
""" """
if self._startup_config:
# change the hostname in the startup-config # change the hostname in the startup-config
startup_config_path = os.path.join(self._working_directory, "configs", "i{}_startup-config.cfg".format(self._dynamips_id)) startup_config_path = os.path.join(self._working_directory, "configs", "i{}_startup-config.cfg".format(self._dynamips_id))
if os.path.isfile(startup_config_path): if os.path.isfile(startup_config_path):
@ -1495,12 +1476,10 @@ class Router(BaseNode):
old_config = f.read() old_config = f.read()
new_config = old_config.replace(self.name, new_name) new_config = old_config.replace(self.name, new_name)
f.seek(0) f.seek(0)
self._startup_config_content = new_config
f.write(new_config) f.write(new_config)
except OSError as e: except OSError as e:
raise DynamipsError("Could not amend the configuration {}: {}".format(startup_config_path, e)) raise DynamipsError("Could not amend the configuration {}: {}".format(startup_config_path, e))
if self._private_config:
# change the hostname in the private-config # change the hostname in the private-config
private_config_path = os.path.join(self._working_directory, "configs", "i{}_private-config.cfg".format(self._dynamips_id)) private_config_path = os.path.join(self._working_directory, "configs", "i{}_private-config.cfg".format(self._dynamips_id))
if os.path.isfile(private_config_path): if os.path.isfile(private_config_path):
@ -1509,7 +1488,6 @@ class Router(BaseNode):
old_config = f.read() old_config = f.read()
new_config = old_config.replace(self.name, new_name) new_config = old_config.replace(self.name, new_name)
f.seek(0) f.seek(0)
self._private_config_content = new_config
f.write(new_config) f.write(new_config)
except OSError as e: except OSError as e:
raise DynamipsError("Could not amend the configuration {}: {}".format(private_config_path, e)) raise DynamipsError("Could not amend the configuration {}: {}".format(private_config_path, e))
@ -1518,57 +1496,6 @@ class Router(BaseNode):
log.info('Router "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name, id=self._id, new_name=new_name)) log.info('Router "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name, id=self._id, new_name=new_name))
self._name = new_name self._name = new_name
@asyncio.coroutine
def set_configs(self, startup_config, private_config=''):
"""
Sets the config files that are pushed to startup-config and
private-config in NVRAM when the instance is started.
:param startup_config: path to statup-config file
:param private_config: path to private-config file
(keep existing data when if an empty string)
"""
startup_config = startup_config.replace("\\", '/')
private_config = private_config.replace("\\", '/')
if self._startup_config != startup_config or self._private_config != private_config:
self._startup_config = startup_config
self._private_config = private_config
if private_config:
private_config_path = os.path.join(self._working_directory, private_config)
try:
if not os.path.getsize(private_config_path):
# an empty private-config can prevent a router to boot.
private_config = ''
self._private_config_content = ""
else:
with open(private_config_path) as f:
self._private_config_content = f.read()
except OSError as e:
raise DynamipsError("Cannot access the private-config {}: {}".format(private_config_path, e))
try:
startup_config_path = os.path.join(self._working_directory, startup_config)
with open(startup_config_path) as f:
self._startup_config_content = f.read()
except OSError as e:
raise DynamipsError("Cannot access the startup-config {}: {}".format(startup_config_path, e))
yield from self._hypervisor.send('vm set_config "{name}" "{startup}" "{private}"'.format(name=self._name,
startup=startup_config,
private=private_config))
log.info('Router "{name}" [{id}]: has a new startup-config set: "{startup}"'.format(name=self._name,
id=self._id,
startup=startup_config))
if private_config:
log.info('Router "{name}" [{id}]: has a new private-config set: "{private}"'.format(name=self._name,
id=self._id,
private=private_config))
@asyncio.coroutine @asyncio.coroutine
def extract_config(self): def extract_config(self):
""" """
@ -1594,8 +1521,6 @@ class Router(BaseNode):
Saves the startup-config and private-config to files. Saves the startup-config and private-config to files.
""" """
if self.startup_config or self.private_config:
try: try:
config_path = os.path.join(self._working_directory, "configs") config_path = os.path.join(self._working_directory, "configs")
os.makedirs(config_path, exist_ok=True) os.makedirs(config_path, exist_ok=True)
@ -1604,28 +1529,24 @@ class Router(BaseNode):
startup_config_base64, private_config_base64 = yield from self.extract_config() startup_config_base64, private_config_base64 = yield from self.extract_config()
if startup_config_base64: if startup_config_base64:
if not self.startup_config: startup_config = os.path.join("configs", "i{}_startup-config.cfg".format(self._dynamips_id))
self._startup_config = os.path.join("configs", "i{}_startup-config.cfg".format(self._dynamips_id))
try: try:
config = base64.b64decode(startup_config_base64).decode("utf-8", errors="replace") config = base64.b64decode(startup_config_base64).decode("utf-8", errors="replace")
config = "!\n" + config.replace("\r", "") config = "!\n" + config.replace("\r", "")
config_path = os.path.join(self._working_directory, self.startup_config) config_path = os.path.join(self._working_directory, startup_config)
with open(config_path, "wb") as f: with open(config_path, "wb") as f:
log.info("saving startup-config to {}".format(self.startup_config)) log.info("saving startup-config to {}".format(startup_config))
self._startup_config_content = config
f.write(config.encode("utf-8")) f.write(config.encode("utf-8"))
except (binascii.Error, OSError) as e: except (binascii.Error, OSError) as e:
raise DynamipsError("Could not save the startup configuration {}: {}".format(config_path, e)) raise DynamipsError("Could not save the startup configuration {}: {}".format(config_path, e))
if private_config_base64 and base64.b64decode(private_config_base64) != b'\nkerberos password \nend\n': if private_config_base64 and base64.b64decode(private_config_base64) != b'\nkerberos password \nend\n':
if not self.private_config: private_config = os.path.join("configs", "i{}_private-config.cfg".format(self._dynamips_id))
self._private_config = os.path.join("configs", "i{}_private-config.cfg".format(self._dynamips_id))
try: try:
config = base64.b64decode(private_config_base64).decode("utf-8", errors="replace") config = base64.b64decode(private_config_base64).decode("utf-8", errors="replace")
config_path = os.path.join(self._working_directory, self.private_config) config_path = os.path.join(self._working_directory, private_config)
with open(config_path, "wb") as f: with open(config_path, "wb") as f:
log.info("saving private-config to {}".format(self.private_config)) log.info("saving private-config to {}".format(private_config))
self._private_config_content = config
f.write(config.encode("utf-8")) f.write(config.encode("utf-8"))
except (binascii.Error, OSError) as e: except (binascii.Error, OSError) as e:
raise DynamipsError("Could not save the private configuration {}: {}".format(config_path, e)) raise DynamipsError("Could not save the private configuration {}: {}".format(config_path, e))

View File

@ -26,8 +26,6 @@ import re
import asyncio import asyncio
import subprocess import subprocess
import shutil import shutil
import argparse
import threading
import configparser import configparser
import struct import struct
import hashlib import hashlib
@ -207,10 +205,6 @@ class IOUVM(BaseNode):
"ram": self._ram, "ram": self._ram,
"nvram": self._nvram, "nvram": self._nvram,
"l1_keepalives": self._l1_keepalives, "l1_keepalives": self._l1_keepalives,
"startup_config": self.relative_startup_config_file,
"startup_config_content": self.startup_config_content,
"private_config_content": self.private_config_content,
"private_config": self.relative_private_config_file,
"use_default_iou_values": self._use_default_iou_values, "use_default_iou_values": self._use_default_iou_values,
"command_line": self.command_line} "command_line": self.command_line}

View File

@ -109,7 +109,7 @@ class VPCSVM(BaseNode):
raise VPCSError("No path to a VPCS executable has been set") raise VPCSError("No path to a VPCS executable has been set")
# This raise an error if ubridge is not available # This raise an error if ubridge is not available
ubridge_path = self.ubridge_path self.ubridge_path
if not os.path.isfile(path): if not os.path.isfile(path):
raise VPCSError("VPCS program '{}' is not accessible".format(path)) raise VPCSError("VPCS program '{}' is not accessible".format(path))
@ -128,8 +128,6 @@ class VPCSVM(BaseNode):
"console": self._console, "console": self._console,
"console_type": "telnet", "console_type": "telnet",
"project_id": self.project.id, "project_id": self.project.id,
"startup_script": self.startup_script,
"startup_script_path": self.relative_startup_script,
"command_line": self.command_line} "command_line": self.command_line}
@property @property

View File

@ -0,0 +1,26 @@
!
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
!
hostname %h
!
ip cef
no ip domain-lookup
no ip icmp rate-limit unreachable
ip tcp synwait 5
no cdp log mismatch duplex
!
line con 0
exec-timeout 0 0
logging synchronous
privilege level 15
no login
line aux 0
exec-timeout 0 0
logging synchronous
privilege level 15
no login
!
!
end

View File

@ -0,0 +1,181 @@
!
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
no service dhcp
!
hostname %h
!
ip cef
no ip routing
no ip domain-lookup
no ip icmp rate-limit unreachable
ip tcp synwait 5
no cdp log mismatch duplex
vtp file nvram:vlan.dat
!
!
interface FastEthernet0/0
description *** Unused for Layer2 EtherSwitch ***
no ip address
shutdown
!
interface FastEthernet0/1
description *** Unused for Layer2 EtherSwitch ***
no ip address
shutdown
!
interface FastEthernet1/0
no shutdown
duplex full
speed 100
!
interface FastEthernet1/1
no shutdown
duplex full
speed 100
!
interface FastEthernet1/2
no shutdown
duplex full
speed 100
!
interface FastEthernet1/3
no shutdown
duplex full
speed 100
!
interface FastEthernet1/4
no shutdown
duplex full
speed 100
!
interface FastEthernet1/5
no shutdown
duplex full
speed 100
!
interface FastEthernet1/6
no shutdown
duplex full
speed 100
!
interface FastEthernet1/7
no shutdown
duplex full
speed 100
!
interface FastEthernet1/8
no shutdown
duplex full
speed 100
!
interface FastEthernet1/9
no shutdown
duplex full
speed 100
!
interface FastEthernet1/10
no shutdown
duplex full
speed 100
!
interface FastEthernet1/11
no shutdown
duplex full
speed 100
!
interface FastEthernet1/12
no shutdown
duplex full
speed 100
!
interface FastEthernet1/13
no shutdown
duplex full
speed 100
!
interface FastEthernet1/14
no shutdown
duplex full
speed 100
!
interface FastEthernet1/15
no shutdown
duplex full
speed 100
!
interface Vlan1
no ip address
shutdown
!
!
line con 0
exec-timeout 0 0
logging synchronous
privilege level 15
no login
line aux 0
exec-timeout 0 0
logging synchronous
privilege level 15
no login
!
!
banner exec $
***************************************************************
This is a normal Router with a SW module inside (NM-16ESW)
It has been preconfigured with hard coded speed and duplex
To create vlans use the command "vlan database" from exec mode
After creating all desired vlans use "exit" to apply the config
To view existing vlans use the command "show vlan-switch brief"
Warning: You are using an old IOS image for this router.
Please update the IOS to enable the "macro" command!
***************************************************************
$
!
!Warning: If the IOS is old and doesn't support macro, it will stop the configuration loading from this point!
!
macro name add_vlan
end
vlan database
vlan $v
exit
@
macro name del_vlan
end
vlan database
no vlan $v
exit
@
!
!
banner exec $
***************************************************************
This is a normal Router with a Switch module inside (NM-16ESW)
It has been pre-configured with hard-coded speed and duplex
To create vlans use the command "vlan database" in exec mode
After creating all desired vlans use "exit" to apply the config
To view existing vlans use the command "show vlan-switch brief"
Alias(exec) : vl - "show vlan-switch brief" command
Alias(configure): va X - macro to add vlan X
Alias(configure): vd X - macro to delete vlan X
***************************************************************
$
!
alias configure va macro global trace add_vlan $v
alias configure vd macro global trace del_vlan $v
alias exec vl show vlan-switch brief
!
!
end

View File

@ -0,0 +1,132 @@
!
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
!
hostname %h
!
!
!
logging discriminator EXCESS severity drops 6 msg-body drops EXCESSCOLL
logging buffered 50000
logging console discriminator EXCESS
!
no ip icmp rate-limit unreachable
!
ip cef
no ip domain-lookup
!
!
!
!
!
!
ip tcp synwait-time 5
!
!
!
!
!
!
interface Ethernet0/0
no ip address
no shutdown
duplex auto
!
interface Ethernet0/1
no ip address
no shutdown
duplex auto
!
interface Ethernet0/2
no ip address
no shutdown
duplex auto
!
interface Ethernet0/3
no ip address
no shutdown
duplex auto
!
interface Ethernet1/0
no ip address
no shutdown
duplex auto
!
interface Ethernet1/1
no ip address
no shutdown
duplex auto
!
interface Ethernet1/2
no ip address
no shutdown
duplex auto
!
interface Ethernet1/3
no ip address
no shutdown
duplex auto
!
interface Ethernet2/0
no ip address
no shutdown
duplex auto
!
interface Ethernet2/1
no ip address
no shutdown
duplex auto
!
interface Ethernet2/2
no ip address
no shutdown
duplex auto
!
interface Ethernet2/3
no ip address
no shutdown
duplex auto
!
interface Ethernet3/0
no ip address
no shutdown
duplex auto
!
interface Ethernet3/1
no ip address
no shutdown
duplex auto
!
interface Ethernet3/2
no ip address
no shutdown
duplex auto
!
interface Ethernet3/3
no ip address
no shutdown
duplex auto
!
interface Vlan1
no ip address
shutdown
!
!
!
!
!
!
!
!
!
line con 0
exec-timeout 0 0
privilege level 15
logging synchronous
line aux 0
exec-timeout 0 0
privilege level 15
logging synchronous
!
end

View File

@ -0,0 +1,108 @@
!
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
!
hostname %h
!
!
!
no ip icmp rate-limit unreachable
!
!
!
!
ip cef
no ip domain-lookup
!
!
ip tcp synwait-time 5
!
!
!
!
interface Ethernet0/0
no ip address
shutdown
!
interface Ethernet0/1
no ip address
shutdown
!
interface Ethernet0/2
no ip address
shutdown
!
interface Ethernet0/3
no ip address
shutdown
!
interface Ethernet1/0
no ip address
shutdown
!
interface Ethernet1/1
no ip address
shutdown
!
interface Ethernet1/2
no ip address
shutdown
!
interface Ethernet1/3
no ip address
shutdown
!
interface Serial2/0
no ip address
shutdown
serial restart-delay 0
!
interface Serial2/1
no ip address
shutdown
serial restart-delay 0
!
interface Serial2/2
no ip address
shutdown
serial restart-delay 0
!
interface Serial2/3
no ip address
shutdown
serial restart-delay 0
!
interface Serial3/0
no ip address
shutdown
serial restart-delay 0
!
interface Serial3/1
no ip address
shutdown
serial restart-delay 0
!
interface Serial3/2
no ip address
shutdown
serial restart-delay 0
!
interface Serial3/3
no ip address
shutdown
serial restart-delay 0
!
!
no cdp log mismatch duplex
!
line con 0
exec-timeout 0 0
privilege level 15
logging synchronous
line aux 0
exec-timeout 0 0
privilege level 15
logging synchronous
!
end

View File

@ -0,0 +1 @@
set pcname %h

View File

@ -16,9 +16,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os import os
import sys
import json import json
import socket import socket
import shutil
import asyncio import asyncio
import aiohttp import aiohttp
@ -30,7 +30,7 @@ from .symbols import Symbols
from ..version import __version__ from ..version import __version__
from .topology import load_topology from .topology import load_topology
from .gns3vm import GNS3VM from .gns3vm import GNS3VM
from ..utils.get_resource import get_resource
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -54,6 +54,7 @@ class Controller:
@asyncio.coroutine @asyncio.coroutine
def start(self): def start(self):
log.info("Start controller") log.info("Start controller")
self.load_base_files()
yield from self.load() yield from self.load()
server_config = Config.instance().get_section_config("Server") server_config = Config.instance().get_section_config("Server")
host = server_config.get("host", "localhost") host = server_config.get("host", "localhost")
@ -163,6 +164,20 @@ class Controller:
except OSError as e: except OSError as e:
log.error(str(e)) log.error(str(e))
def load_base_files(self):
"""
At startup we copy base file to the user location to allow
them to customize it
"""
dst_path = self.configs_path()
src_path = get_resource('configs')
try:
for file in os.listdir(src_path):
if not os.path.exists(os.path.join(dst_path, file)):
shutil.copy(os.path.join(src_path, file), os.path.join(dst_path, file))
except OSError:
pass
def images_path(self): def images_path(self):
""" """
Get the image storage directory Get the image storage directory
@ -172,6 +187,15 @@ class Controller:
os.makedirs(images_path, exist_ok=True) os.makedirs(images_path, exist_ok=True)
return images_path return images_path
def configs_path(self):
"""
Get the configs storage directory
"""
server_config = Config.instance().get_section_config("Server")
images_path = os.path.expanduser(server_config.get("configs_path", "~/GNS3/projects"))
os.makedirs(images_path, exist_ok=True)
return images_path
@asyncio.coroutine @asyncio.coroutine
def _import_gns3_gui_conf(self): def _import_gns3_gui_conf(self):
""" """

View File

@ -146,6 +146,15 @@ class Node:
def properties(self, val): def properties(self, val):
self._properties = val self._properties = val
def _base_config_file_content(self, path):
if not os.path.isabs(path):
path = os.path.join(self.project.controller.configs_path(), path)
try:
with open(path) as f:
return f.read()
except (PermissionError, OSError):
return None
@property @property
def project(self): def project(self):
return self._project return self._project
@ -366,8 +375,12 @@ class Node:
self._console_type = value self._console_type = value
elif key == "name": elif key == "name":
self.name = value self.name = value
elif key in ["node_id", "project_id", "console_host"]: elif key in ["node_id", "project_id", "console_host",
pass "startup_config_content",
"private_config_content",
"startup_script"]:
if key in self._properties:
del self._properties[key]
else: else:
self._properties[key] = value self._properties[key] = value
self._list_ports() self._list_ports()
@ -384,6 +397,17 @@ class Node:
data = copy.copy(properties) data = copy.copy(properties)
else: else:
data = copy.copy(self._properties) data = copy.copy(self._properties)
# We replace the startup script name by the content of the file
mapping = {
"base_script_file": "startup_script",
"startup_config": "startup_config_content",
"private_config": "private_config_content",
}
for k, v in mapping.items():
if k in list(self._properties.keys()):
data[v] = self._base_config_file_content(self._properties[k])
del data[k]
del self._properties[k] # We send the file only one time
data["name"] = self._name data["name"] = self._name
if self._console: if self._console:
# console is optional for builtin nodes # console is optional for builtin nodes
@ -585,17 +609,6 @@ class Node:
return False return False
return self.id == other.id and other.project.id == self.project.id return self.id == other.id and other.project.id == self.project.id
def _filter_properties(self):
"""
Some properties are private and should not be exposed
"""
PRIVATE_PROPERTIES = ("iourc_content", )
prop = copy.copy(self._properties)
for k in list(prop.keys()):
if k in PRIVATE_PROPERTIES:
del prop[k]
return prop
def __json__(self, topology_dump=False): def __json__(self, topology_dump=False):
""" """
:param topology_dump: Filter to keep only properties require for saving on disk :param topology_dump: Filter to keep only properties require for saving on disk
@ -608,7 +621,7 @@ class Node:
"name": self._name, "name": self._name,
"console": self._console, "console": self._console,
"console_type": self._console_type, "console_type": self._console_type,
"properties": self._filter_properties(), "properties": self._properties,
"label": self._label, "label": self._label,
"x": self._x, "x": self._x,
"y": self._y, "y": self._y,
@ -631,7 +644,7 @@ class Node:
"console_host": str(self._compute.console_host), "console_host": str(self._compute.console_host),
"console_type": self._console_type, "console_type": self._console_type,
"command_line": self._command_line, "command_line": self._command_line,
"properties": self._filter_properties(), "properties": self._properties,
"status": self._status, "status": self._status,
"label": self._label, "label": self._label,
"x": self._x, "x": self._x,

View File

@ -36,7 +36,7 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
GNS3_FILE_FORMAT_REVISION = 7 GNS3_FILE_FORMAT_REVISION = 8
def _check_topology_schema(topo): def _check_topology_schema(topo):
@ -138,6 +138,10 @@ def load_topology(path):
if topo["revision"] < 7: if topo["revision"] < 7:
topo = _convert_2_0_0_beta_2(topo, path) topo = _convert_2_0_0_beta_2(topo, path)
# Version before GNS3 2.1
if topo["revision"] < 8:
topo = _convert_2_0_0(topo, path)
_check_topology_schema(topo) _check_topology_schema(topo)
if changed: if changed:
@ -146,6 +150,34 @@ def load_topology(path):
return topo return topo
def _convert_2_0_0(topo, topo_path):
"""
Convert topologies from GNS3 2.0.0 to 2.1
Changes:
* Remove startup_script_path from VPCS and base config file for IOU and Dynamips
"""
topo["revision"] = 8
for node in topo.get("topology", {}).get("nodes", []):
if "properties" in node:
if node["node_type"] == "vpcs":
if "startup_script_path" in node["properties"]:
del node["properties"]["startup_script_path"]
if "startup_script" in node["properties"]:
del node["properties"]["startup_script"]
elif node["node_type"] == "dynamips" or node["node_type"] == "iou":
if "startup_config" in node["properties"]:
del node["properties"]["startup_config"]
if "private_config" in node["properties"]:
del node["properties"]["private_config"]
if "startup_config_content" in node["properties"]:
del node["properties"]["startup_config_content"]
if "private_config_content" in node["properties"]:
del node["properties"]["private_config_content"]
return topo
def _convert_2_0_0_beta_2(topo, topo_path): def _convert_2_0_0_beta_2(topo, topo_path):
""" """
Convert topologies from GNS3 2.0.0 beta 2 to beta 3. Convert topologies from GNS3 2.0.0 beta 2 to beta 3.

View File

@ -17,7 +17,6 @@
import os import os
import sys import sys
import base64
from gns3server.web.route import Route from gns3server.web.route import Route
from gns3server.schemas.nio import NIO_SCHEMA from gns3server.schemas.nio import NIO_SCHEMA
@ -78,7 +77,6 @@ class DynamipsVMHandler:
aux=request.json.get("aux"), aux=request.json.get("aux"),
chassis=request.json.pop("chassis", default_chassis), chassis=request.json.pop("chassis", default_chassis),
node_type="dynamips") node_type="dynamips")
yield from dynamips_manager.update_vm_settings(vm, request.json) yield from dynamips_manager.update_vm_settings(vm, request.json)
response.set_status(201) response.set_status(201)
response.json(vm) response.json(vm)

View File

@ -344,9 +344,6 @@ class NodeHandler:
raise aiohttp.web.HTTPForbidden raise aiohttp.web.HTTPForbidden
node_type = node.node_type node_type = node.node_type
if node_type == "dynamips":
path = "/project-files/{}/{}".format(node_type, path)
else:
path = "/project-files/{}/{}/{}".format(node_type, node.id, path) path = "/project-files/{}/{}/{}".format(node_type, node.id, path)
res = yield from node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), timeout=None, raw=True) res = yield from node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), timeout=None, raw=True)
@ -384,12 +381,9 @@ class NodeHandler:
raise aiohttp.web.HTTPForbidden raise aiohttp.web.HTTPForbidden
node_type = node.node_type node_type = node.node_type
if node_type == "dynamips":
path = "/project-files/{}/{}".format(node_type, path)
else:
path = "/project-files/{}/{}/{}".format(node_type, node.id, path) path = "/project-files/{}/{}/{}".format(node_type, node.id, path)
data = yield from request.content.read() data = yield from request.content.read()
res = yield from node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True) yield from node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True)
response.set_status(201) response.set_status(201)

View File

@ -62,18 +62,10 @@ VM_CREATE_SCHEMA = {
"type": ["string", "null"], "type": ["string", "null"],
"minLength": 1, "minLength": 1,
}, },
"startup_config": {
"description": "Path to the IOS startup configuration file",
"type": "string",
},
"startup_config_content": { "startup_config_content": {
"description": "Content of IOS startup configuration file", "description": "Content of IOS startup configuration file",
"type": "string", "type": "string",
}, },
"private_config": {
"description": "Path to the IOS private configuration file",
"type": "string",
},
"private_config_content": { "private_config_content": {
"description": "Content of IOS private configuration file", "description": "Content of IOS private configuration file",
"type": "string", "type": "string",
@ -296,22 +288,6 @@ VM_UPDATE_SCHEMA = {
"description": "Dynamips ID", "description": "Dynamips ID",
"type": "integer" "type": "integer"
}, },
"startup_config": {
"description": "Path to the IOS startup configuration file.",
"type": "string",
},
"private_config": {
"description": "Path to the IOS private configuration file.",
"type": "string",
},
"startup_config_content": {
"description": "Content of IOS startup configuration file",
"type": "string",
},
"private_config_content": {
"description": "Content of IOS private configuration file",
"type": "string",
},
"ram": { "ram": {
"description": "Amount of RAM in MB", "description": "Amount of RAM in MB",
"type": "integer" "type": "integer"
@ -552,14 +528,6 @@ VM_OBJECT_SCHEMA = {
"type": ["string", "null"], "type": ["string", "null"],
"minLength": 1, "minLength": 1,
}, },
"startup_config": {
"description": "Path to the IOS startup configuration file",
"type": "string",
},
"private_config": {
"description": "Path to the IOS private configuration file",
"type": "string",
},
"ram": { "ram": {
"description": "Amount of RAM in MB", "description": "Amount of RAM in MB",
"type": "integer" "type": "integer"
@ -706,14 +674,6 @@ VM_OBJECT_SCHEMA = {
{"type": "null"} {"type": "null"}
] ]
}, },
"startup_config_content": {
"description": "Content of IOS startup configuration file",
"type": "string",
},
"private_config_content": {
"description": "Content of IOS private configuration file",
"type": "string",
},
# C7200 properties # C7200 properties
"npe": { "npe": {
"description": "NPE model", "description": "NPE model",

View File

@ -78,14 +78,6 @@ IOU_CREATE_SCHEMA = {
"description": "Use default IOU values", "description": "Use default IOU values",
"type": ["boolean", "null"] "type": ["boolean", "null"]
}, },
"startup_config": {
"description": "Path to the startup-config of IOU",
"type": ["string", "null"]
},
"private_config": {
"description": "Path to the private-config of IOU",
"type": ["string", "null"]
},
"startup_config_content": { "startup_config_content": {
"description": "Startup-config of IOU", "description": "Startup-config of IOU",
"type": ["string", "null"] "type": ["string", "null"]
@ -94,10 +86,6 @@ IOU_CREATE_SCHEMA = {
"description": "Private-config of IOU", "description": "Private-config of IOU",
"type": ["string", "null"] "type": ["string", "null"]
}, },
"iourc_content": {
"description": "Content of the iourc file. Ignored if Null",
"type": ["string", "null"]
}
}, },
"additionalProperties": False, "additionalProperties": False,
"required": ["name", "path"] "required": ["name", "path"]
@ -187,30 +175,10 @@ IOU_OBJECT_SCHEMA = {
"description": "Always up ethernet interface", "description": "Always up ethernet interface",
"type": "boolean" "type": "boolean"
}, },
"startup_config": {
"description": "Path of the startup-config content relative to project directory",
"type": ["string", "null"]
},
"private_config": {
"description": "Path of the private-config content relative to project directory",
"type": ["string", "null"]
},
"use_default_iou_values": { "use_default_iou_values": {
"description": "Use default IOU values", "description": "Use default IOU values",
"type": ["boolean", "null"] "type": ["boolean", "null"]
}, },
"startup_config_content": {
"description": "Startup-config of IOU",
"type": ["string", "null"]
},
"private_config_content": {
"description": "Private-config of IOU",
"type": ["string", "null"]
},
"iourc_content": {
"description": "Content of the iourc file. Ignored if Null",
"type": ["string", "null"]
},
"command_line": { "command_line": {
"description": "Last command line used by GNS3 to start QEMU", "description": "Last command line used by GNS3 to start QEMU",
"type": "string" "type": "string"

View File

@ -50,10 +50,6 @@ VPCS_CREATE_SCHEMA = {
"description": "Content of the VPCS startup script", "description": "Content of the VPCS startup script",
"type": ["string", "null"] "type": ["string", "null"]
}, },
"startup_script_path": {
"description": "Path of the VPCS startup script relative to project directory (IGNORED)",
"type": ["string", "null"]
}
}, },
"additionalProperties": False, "additionalProperties": False,
"required": ["name"] "required": ["name"]
@ -79,14 +75,6 @@ VPCS_UPDATE_SCHEMA = {
"description": "Console type", "description": "Console type",
"enum": ["telnet"] "enum": ["telnet"]
}, },
"startup_script": {
"description": "Content of the VPCS startup script",
"type": ["string", "null"]
},
"startup_script_path": {
"description": "Path of the VPCS startup script relative to project directory (IGNORED)",
"type": ["string", "null"]
}
}, },
"additionalProperties": False, "additionalProperties": False,
} }
@ -133,19 +121,11 @@ VPCS_OBJECT_SCHEMA = {
"maxLength": 36, "maxLength": 36,
"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
}, },
"startup_script": {
"description": "Content of the VPCS startup script",
"type": ["string", "null"]
},
"startup_script_path": {
"description": "Path of the VPCS startup script relative to project directory",
"type": ["string", "null"]
},
"command_line": { "command_line": {
"description": "Last command line used by GNS3 to start QEMU", "description": "Last command line used by GNS3 to start QEMU",
"type": "string" "type": "string"
} }
}, },
"additionalProperties": False, "additionalProperties": False,
"required": ["name", "node_id", "status", "console", "console_type", "project_id", "startup_script_path", "command_line"] "required": ["name", "node_id", "status", "console", "console_type", "project_id", "command_line"]
} }

View File

@ -465,3 +465,17 @@ def test_get_free_project_name(controller, async_run):
async_run(controller.add_project(project_id=str(uuid.uuid4()), name="Test-1")) async_run(controller.add_project(project_id=str(uuid.uuid4()), name="Test-1"))
assert controller.get_free_project_name("Test") == "Test-2" assert controller.get_free_project_name("Test") == "Test-2"
assert controller.get_free_project_name("Hello") == "Hello" assert controller.get_free_project_name("Hello") == "Hello"
def test_load_base_files(controller, config, tmpdir):
config.set_section_config("Server", {"configs_path": str(tmpdir)})
with open(str(tmpdir / 'iou_l2_base_startup-config.txt'), 'w+') as f:
f.write('test')
controller.load_base_files()
assert os.path.exists(str(tmpdir / 'iou_l3_base_startup-config.txt'))
# Check is the file has not been overwrite
with open(str(tmpdir / 'iou_l2_base_startup-config.txt')) as f:
assert f.read() == 'test'

View File

@ -88,19 +88,6 @@ def test_eq(compute, project, node, controller):
assert node != Node(Project(str(uuid.uuid4()), controller=controller), compute, "demo3", node_id=node.id, node_type="qemu") assert node != Node(Project(str(uuid.uuid4()), controller=controller), compute, "demo3", node_id=node.id, node_type="qemu")
def test_properties_filter(project, compute):
"""
Some properties are private and should not be exposed
"""
node = Node(project, compute, "demo",
node_id=str(uuid.uuid4()),
node_type="vpcs",
console_type="vnc",
properties={"startup_script": "echo test", "iourc_content": "test"})
assert node._properties == {"startup_script": "echo test", "iourc_content": "test"}
assert node._filter_properties() == {"startup_script": "echo test"}
def test_json(node, compute): def test_json(node, compute):
assert node.__json__() == { assert node.__json__() == {
"compute_id": str(compute.id), "compute_id": str(compute.id),
@ -207,6 +194,30 @@ def test_create_image_missing(node, compute, project, async_run):
node._upload_missing_image.called is True node._upload_missing_image.called is True
def test_create_base_script(node, config, compute, tmpdir, async_run):
config.set_section_config("Server", {"configs_path": str(tmpdir)})
with open(str(tmpdir / 'test.txt'), 'w+') as f:
f.write('hostname test')
node._properties = {"base_script_file": "test.txt"}
node._console = 2048
response = MagicMock()
response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response)
assert async_run(node.create()) is True
data = {
"console": 2048,
"console_type": "vnc",
"node_id": node.id,
"startup_script": "hostname test",
"name": "demo"
}
compute.post.assert_called_with("/projects/{}/vpcs/nodes".format(node.project.id), data=data, timeout=120)
def test_symbol(node, symbols_dir): def test_symbol(node, symbols_dir):
""" """
Change symbol should change the node size Change symbol should change the node size

View File

@ -137,7 +137,7 @@ def test_add_node_local(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_script": "test.cfg"}))
assert node.id in project._nodes assert node.id in project._nodes
compute.post.assert_any_call('/projects', data={ compute.post.assert_any_call('/projects', data={
@ -147,7 +147,7 @@ def test_add_node_local(async_run, controller):
}) })
compute.post.assert_any_call('/projects/{}/vpcs/nodes'.format(project.id), compute.post.assert_any_call('/projects/{}/vpcs/nodes'.format(project.id),
data={'node_id': node.id, data={'node_id': node.id,
'startup_config': 'test.cfg', 'startup_script': 'test.cfg',
'name': 'test'}, 'name': 'test'},
timeout=120) timeout=120)
assert compute in project._project_created_on_compute assert compute in project._project_created_on_compute
@ -167,7 +167,7 @@ def test_add_node_non_local(async_run, controller):
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})) node = async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_script": "test.cfg"}))
compute.post.assert_any_call('/projects', data={ compute.post.assert_any_call('/projects', data={
"name": project._name, "name": project._name,
@ -175,7 +175,7 @@ def test_add_node_non_local(async_run, controller):
}) })
compute.post.assert_any_call('/projects/{}/vpcs/nodes'.format(project.id), compute.post.assert_any_call('/projects/{}/vpcs/nodes'.format(project.id),
data={'node_id': node.id, data={'node_id': node.id,
'startup_config': 'test.cfg', 'startup_script': 'test.cfg',
'name': 'test'}, 'name': 'test'},
timeout=120) timeout=120)
assert compute in project._project_created_on_compute assert compute in project._project_created_on_compute

View File

@ -106,7 +106,6 @@ def demo_topology():
"node_type": "vpcs", "node_type": "vpcs",
"properties": { "properties": {
"startup_script": "", "startup_script": "",
"startup_script_path": "startup.vpc"
}, },
"symbol": ":/symbols/computer.svg", "symbol": ":/symbols/computer.svg",
"width": 65, "width": 65,
@ -131,7 +130,6 @@ def demo_topology():
"node_type": "vpcs", "node_type": "vpcs",
"properties": { "properties": {
"startup_script": "", "startup_script": "",
"startup_script_path": "startup.vpc"
}, },
"symbol": ":/symbols/computer.svg", "symbol": ":/symbols/computer.svg",
"width": 65, "width": 65,

View File

@ -80,7 +80,6 @@ def test_iou_create_with_params(http_compute, project, base_params):
params["l1_keepalives"] = True params["l1_keepalives"] = True
params["startup_config_content"] = "hostname test" params["startup_config_content"] = "hostname test"
params["use_default_iou_values"] = True params["use_default_iou_values"] = True
params["iourc_content"] = "test"
response = http_compute.post("/projects/{project_id}/iou/nodes".format(project_id=project.id), params, example=True) response = http_compute.post("/projects/{project_id}/iou/nodes".format(project_id=project.id), params, example=True)
assert response.status == 201 assert response.status == 201
@ -94,7 +93,6 @@ def test_iou_create_with_params(http_compute, project, base_params):
assert response.json["l1_keepalives"] is True assert response.json["l1_keepalives"] is True
assert response.json["use_default_iou_values"] is True assert response.json["use_default_iou_values"] is True
assert "startup-config.cfg" in response.json["startup_config"]
with open(startup_config_file(project, response.json)) as f: with open(startup_config_file(project, response.json)) as f:
assert f.read() == "hostname test" assert f.read() == "hostname test"
@ -115,7 +113,6 @@ def test_iou_create_startup_config_already_exist(http_compute, project, base_par
assert response.status == 201 assert response.status == 201
assert response.route == "/projects/{project_id}/iou/nodes" assert response.route == "/projects/{project_id}/iou/nodes"
assert "startup-config.cfg" in response.json["startup_config"]
with open(startup_config_file(project, response.json)) as f: with open(startup_config_file(project, response.json)) as f:
assert f.read() == "echo hello" assert f.read() == "echo hello"
@ -183,9 +180,7 @@ def test_iou_update(http_compute, vm, tmpdir, free_console_port, project):
"ethernet_adapters": 4, "ethernet_adapters": 4,
"serial_adapters": 0, "serial_adapters": 0,
"l1_keepalives": True, "l1_keepalives": True,
"startup_config_content": "hostname test",
"use_default_iou_values": True, "use_default_iou_values": True,
"iourc_content": "test"
} }
response = http_compute.put("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params, example=True) response = http_compute.put("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params, example=True)
assert response.status == 200 assert response.status == 200
@ -197,9 +192,6 @@ def test_iou_update(http_compute, vm, tmpdir, free_console_port, project):
assert response.json["nvram"] == 2048 assert response.json["nvram"] == 2048
assert response.json["l1_keepalives"] is True assert response.json["l1_keepalives"] is True
assert response.json["use_default_iou_values"] is True assert response.json["use_default_iou_values"] is True
assert "startup-config.cfg" in response.json["startup_config"]
with open(startup_config_file(project, response.json)) as f:
assert f.read() == "hostname test"
def test_iou_nio_create_udp(http_compute, vm): def test_iou_nio_create_udp(http_compute, vm):

View File

@ -43,7 +43,6 @@ def test_vpcs_get(http_compute, project, vm):
assert response.route == "/projects/{project_id}/vpcs/nodes/{node_id}" assert response.route == "/projects/{project_id}/vpcs/nodes/{node_id}"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == project.id
assert response.json["startup_script_path"] is None
assert response.json["status"] == "stopped" assert response.json["status"] == "stopped"
@ -53,8 +52,6 @@ def test_vpcs_create_startup_script(http_compute, project):
assert response.route == "/projects/{project_id}/vpcs/nodes" assert response.route == "/projects/{project_id}/vpcs/nodes"
assert response.json["name"] == "PC TEST 1" assert response.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id assert response.json["project_id"] == project.id
assert response.json["startup_script"] == os.linesep.join(["ip 192.168.1.2", "echo TEST"])
assert response.json["startup_script_path"] == "startup.vpc"
def test_vpcs_create_port(http_compute, project, free_console_port): def test_vpcs_create_port(http_compute, project, free_console_port):

View File

@ -63,7 +63,6 @@
], ],
"slot0": "C7200-IO-FE", "slot0": "C7200-IO-FE",
"sparsemem": true, "sparsemem": true,
"startup_config": "configs/i1_startup-config.cfg",
"system_id": "FTX0945W0MY" "system_id": "FTX0945W0MY"
}, },
"x": -112, "x": -112,

View File

@ -27,7 +27,6 @@
"slot0": "Leopard-2FE", "slot0": "Leopard-2FE",
"idlepc": "0x6057efc8", "idlepc": "0x6057efc8",
"chassis": "3660", "chassis": "3660",
"startup_config": "configs/i1_startup-config.cfg",
"image": "c3660-a3jk9s-mz.124-25c.bin", "image": "c3660-a3jk9s-mz.124-25c.bin",
"mac_addr": "cc01.20b8.0000", "mac_addr": "cc01.20b8.0000",
"aux": 2103, "aux": 2103,

View File

@ -53,7 +53,6 @@
"ram": 256, "ram": 256,
"slot0": "GT96100-FE", "slot0": "GT96100-FE",
"sparsemem": true, "sparsemem": true,
"startup_config": "configs/i1_startup-config.cfg",
"system_id": "FTX0945W0MY" "system_id": "FTX0945W0MY"
}, },
"symbol": ":/symbols/router.svg", "symbol": ":/symbols/router.svg",
@ -100,7 +99,6 @@
"slot0": "Leopard-2FE", "slot0": "Leopard-2FE",
"slot1": "NM-16ESW", "slot1": "NM-16ESW",
"sparsemem": true, "sparsemem": true,
"startup_config": "configs/i2_startup-config.cfg",
"system_id": "FTX0945W0MY" "system_id": "FTX0945W0MY"
}, },
"symbol": ":/symbols/multilayer_switch.svg", "symbol": ":/symbols/multilayer_switch.svg",

View File

@ -76,7 +76,6 @@
"port_segment_size": 0, "port_segment_size": 0,
"first_port_name": null, "first_port_name": null,
"properties": { "properties": {
"startup_script_path": "startup.vpc"
}, },
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"x": -29, "x": -29,

View File

@ -41,7 +41,6 @@
"path": "i86bi-linux-l3-adventerprisek9-15.4.1T.bin", "path": "i86bi-linux-l3-adventerprisek9-15.4.1T.bin",
"ram": 256, "ram": 256,
"serial_adapters": 2, "serial_adapters": 2,
"startup_config": "startup-config.cfg",
"use_default_iou_values": true "use_default_iou_values": true
}, },
"symbol": ":/symbols/router.svg", "symbol": ":/symbols/router.svg",

View File

@ -18,7 +18,6 @@
"port_segment_size": 0, "port_segment_size": 0,
"first_port_name": null, "first_port_name": null,
"properties" : { "properties" : {
"startup_script_path" : "startup.vpc"
}, },
"label" : { "label" : {
"y" : -25, "y" : -25,

View File

@ -50,7 +50,6 @@
"port_segment_size": 0, "port_segment_size": 0,
"first_port_name": null, "first_port_name": null,
"properties": { "properties": {
"startup_script_path": "startup.vpc"
}, },
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"x": -87, "x": -87,
@ -75,7 +74,6 @@
"port_segment_size": 0, "port_segment_size": 0,
"first_port_name": null, "first_port_name": null,
"properties": { "properties": {
"startup_script_path": "startup.vpc"
}, },
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"x": 123, "x": 123,

View File

@ -61,8 +61,6 @@
1, 1,
1 1
], ],
"private_config": "",
"private_config_content": "",
"ram": 512, "ram": 512,
"sensors": [ "sensors": [
22, 22,
@ -78,8 +76,6 @@
"slot5": null, "slot5": null,
"slot6": null, "slot6": null,
"sparsemem": true, "sparsemem": true,
"startup_config": "configs/i1_startup-config.cfg",
"startup_config_content": "!\n!\nservice timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption\n!\nhostname R1\n!\nip cef\nno ip domain-lookup\nno ip icmp rate-limit unreachable\nip tcp synwait 5\nno cdp log mismatch duplex\n!\nline con 0\n exec-timeout 0 0\n logging synchronous\n privilege level 15\n no login\nline aux 0\n exec-timeout 0 0\n logging synchronous\n privilege level 15\n no login\n!\n!\nend\n",
"system_id": "FTX0945W0MY" "system_id": "FTX0945W0MY"
}, },
"symbol": ":/symbols/router.svg", "symbol": ":/symbols/router.svg",
@ -129,8 +125,6 @@
1, 1,
1 1
], ],
"private_config": "",
"private_config_content": "",
"ram": 512, "ram": 512,
"sensors": [ "sensors": [
22, 22,
@ -146,8 +140,6 @@
"slot5": null, "slot5": null,
"slot6": null, "slot6": null,
"sparsemem": true, "sparsemem": true,
"startup_config": "configs/i2_startup-config.cfg",
"startup_config_content": "!\n!\nservice timestamps debug datetime msec\nservice timestamps log datetime msec\nno service password-encryption\n!\nhostname R2\n!\nip cef\nno ip domain-lookup\nno ip icmp rate-limit unreachable\nip tcp synwait 5\nno cdp log mismatch duplex\n!\nline con 0\n exec-timeout 0 0\n logging synchronous\n privilege level 15\n no login\nline aux 0\n exec-timeout 0 0\n logging synchronous\n privilege level 15\n no login\n!\n!\nend\n",
"system_id": "FTX0945W0MY" "system_id": "FTX0945W0MY"
}, },
"symbol": ":/symbols/router.svg", "symbol": ":/symbols/router.svg",