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:
* 8: GNS3 2.1
* 7: GNS3 2.0
* 6: GNS3 2.0 < beta 3
* 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_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")
if startup_config_path:
yield from vm.set_configs(startup_config_path)
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")
if startup_config_content:
self._create_config(vm, default_startup_config_path, startup_config_content)
private_config_content = settings.get("private_config_content")
if private_config_path:
yield from vm.set_configs(vm.startup_config, private_config_path)
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)
if private_config_content:
self._create_config(vm, default_private_config_path, private_config_content)
def _create_config(self, vm, path, content=None):
"""
@ -553,6 +540,11 @@ class Dynamips(BaseManager):
except OSError as 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:
with open(path, "wb") as f:
if content:

View File

@ -78,8 +78,6 @@ class Router(BaseNode):
self._dynamips_id = dynamips_id
self._platform = platform
self._image = ""
self._startup_config = ""
self._private_config = ""
self._ram = 128 # Megabytes
self._nvram = 128 # Kilobytes
self._mmap = True
@ -102,8 +100,6 @@ class Router(BaseNode):
self._slots = []
self._ghost_flag = ghost_flag
self._memory_watcher = None
self._startup_config_content = ""
self._private_config_content = ""
if not ghost_flag:
if not dynamips_id:
@ -152,8 +148,6 @@ class Router(BaseNode):
"platform": self._platform,
"image": self._image,
"image_md5sum": md5sum(self._image),
"startup_config": self._startup_config,
"private_config": self._private_config,
"ram": self._ram,
"nvram": self._nvram,
"mmap": self._mmap,
@ -171,9 +165,7 @@ class Router(BaseNode):
"console_type": "telnet",
"aux": self.aux,
"mac_addr": self._mac_addr,
"system_id": self._system_id,
"startup_config_content": self._startup_config_content,
"private_config_content": self._private_config_content}
"system_id": self._system_id}
# 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)
@ -289,6 +281,16 @@ class Router(BaseNode):
if not self._ghost_flag:
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))
self.status = "started"
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
@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
def set_name(self, new_name):
"""
@ -1486,89 +1468,34 @@ class Router(BaseNode):
:param new_name: new name string
"""
if self._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))
if os.path.isfile(startup_config_path):
try:
with open(startup_config_path, "r+", encoding="utf-8", errors="replace") as f:
old_config = f.read()
new_config = old_config.replace(self.name, new_name)
f.seek(0)
self._startup_config_content = new_config
f.write(new_config)
except OSError as e:
raise DynamipsError("Could not amend the configuration {}: {}".format(startup_config_path, e))
# 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))
if os.path.isfile(startup_config_path):
try:
with open(startup_config_path, "r+", encoding="utf-8", errors="replace") as f:
old_config = f.read()
new_config = old_config.replace(self.name, new_name)
f.seek(0)
f.write(new_config)
except OSError as e:
raise DynamipsError("Could not amend the configuration {}: {}".format(startup_config_path, e))
if self._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))
if os.path.isfile(private_config_path):
try:
with open(private_config_path, "r+", encoding="utf-8", errors="replace") as f:
old_config = f.read()
new_config = old_config.replace(self.name, new_name)
f.seek(0)
self._private_config_content = new_config
f.write(new_config)
except OSError as e:
raise DynamipsError("Could not amend the configuration {}: {}".format(private_config_path, e))
# 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))
if os.path.isfile(private_config_path):
try:
with open(private_config_path, "r+", encoding="utf-8", errors="replace") as f:
old_config = f.read()
new_config = old_config.replace(self.name, new_name)
f.seek(0)
f.write(new_config)
except OSError as e:
raise DynamipsError("Could not amend the configuration {}: {}".format(private_config_path, e))
yield from self._hypervisor.send('vm rename "{name}" "{new_name}"'.format(name=self._name, 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
@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
def extract_config(self):
"""
@ -1594,41 +1521,35 @@ class Router(BaseNode):
Saves the startup-config and private-config to files.
"""
if self.startup_config or self.private_config:
try:
config_path = os.path.join(self._working_directory, "configs")
os.makedirs(config_path, exist_ok=True)
except OSError as e:
raise DynamipsError("Could could not create configuration directory {}: {}".format(config_path, e))
startup_config_base64, private_config_base64 = yield from self.extract_config()
if startup_config_base64:
startup_config = os.path.join("configs", "i{}_startup-config.cfg".format(self._dynamips_id))
try:
config_path = os.path.join(self._working_directory, "configs")
os.makedirs(config_path, exist_ok=True)
except OSError as e:
raise DynamipsError("Could could not create configuration directory {}: {}".format(config_path, e))
config = base64.b64decode(startup_config_base64).decode("utf-8", errors="replace")
config = "!\n" + config.replace("\r", "")
config_path = os.path.join(self._working_directory, startup_config)
with open(config_path, "wb") as f:
log.info("saving startup-config to {}".format(startup_config))
f.write(config.encode("utf-8"))
except (binascii.Error, OSError) as e:
raise DynamipsError("Could not save the startup configuration {}: {}".format(config_path, e))
startup_config_base64, private_config_base64 = yield from self.extract_config()
if startup_config_base64:
if not self.startup_config:
self._startup_config = os.path.join("configs", "i{}_startup-config.cfg".format(self._dynamips_id))
try:
config = base64.b64decode(startup_config_base64).decode("utf-8", errors="replace")
config = "!\n" + config.replace("\r", "")
config_path = os.path.join(self._working_directory, self.startup_config)
with open(config_path, "wb") as f:
log.info("saving startup-config to {}".format(self.startup_config))
self._startup_config_content = config
f.write(config.encode("utf-8"))
except (binascii.Error, OSError) as 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 not self.private_config:
self._private_config = os.path.join("configs", "i{}_private-config.cfg".format(self._dynamips_id))
try:
config = base64.b64decode(private_config_base64).decode("utf-8", errors="replace")
config_path = os.path.join(self._working_directory, self.private_config)
with open(config_path, "wb") as f:
log.info("saving private-config to {}".format(self.private_config))
self._private_config_content = config
f.write(config.encode("utf-8"))
except (binascii.Error, OSError) as e:
raise DynamipsError("Could not save the private configuration {}: {}".format(config_path, e))
if private_config_base64 and base64.b64decode(private_config_base64) != b'\nkerberos password \nend\n':
private_config = os.path.join("configs", "i{}_private-config.cfg".format(self._dynamips_id))
try:
config = base64.b64decode(private_config_base64).decode("utf-8", errors="replace")
config_path = os.path.join(self._working_directory, private_config)
with open(config_path, "wb") as f:
log.info("saving private-config to {}".format(private_config))
f.write(config.encode("utf-8"))
except (binascii.Error, OSError) as e:
raise DynamipsError("Could not save the private configuration {}: {}".format(config_path, e))
def delete(self):
"""

View File

@ -26,8 +26,6 @@ import re
import asyncio
import subprocess
import shutil
import argparse
import threading
import configparser
import struct
import hashlib
@ -207,10 +205,6 @@ class IOUVM(BaseNode):
"ram": self._ram,
"nvram": self._nvram,
"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,
"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")
# This raise an error if ubridge is not available
ubridge_path = self.ubridge_path
self.ubridge_path
if not os.path.isfile(path):
raise VPCSError("VPCS program '{}' is not accessible".format(path))
@ -128,8 +128,6 @@ class VPCSVM(BaseNode):
"console": self._console,
"console_type": "telnet",
"project_id": self.project.id,
"startup_script": self.startup_script,
"startup_script_path": self.relative_startup_script,
"command_line": self.command_line}
@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/>.
import os
import sys
import json
import socket
import shutil
import asyncio
import aiohttp
@ -30,7 +30,7 @@ from .symbols import Symbols
from ..version import __version__
from .topology import load_topology
from .gns3vm import GNS3VM
from ..utils.get_resource import get_resource
import logging
log = logging.getLogger(__name__)
@ -54,6 +54,7 @@ class Controller:
@asyncio.coroutine
def start(self):
log.info("Start controller")
self.load_base_files()
yield from self.load()
server_config = Config.instance().get_section_config("Server")
host = server_config.get("host", "localhost")
@ -163,6 +164,20 @@ class Controller:
except OSError as 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):
"""
Get the image storage directory
@ -172,6 +187,15 @@ class Controller:
os.makedirs(images_path, exist_ok=True)
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
def _import_gns3_gui_conf(self):
"""

View File

@ -146,6 +146,15 @@ class Node:
def properties(self, 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
def project(self):
return self._project
@ -366,8 +375,12 @@ class Node:
self._console_type = value
elif key == "name":
self.name = value
elif key in ["node_id", "project_id", "console_host"]:
pass
elif key in ["node_id", "project_id", "console_host",
"startup_config_content",
"private_config_content",
"startup_script"]:
if key in self._properties:
del self._properties[key]
else:
self._properties[key] = value
self._list_ports()
@ -384,6 +397,17 @@ class Node:
data = copy.copy(properties)
else:
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
if self._console:
# console is optional for builtin nodes
@ -585,17 +609,6 @@ class Node:
return False
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):
"""
:param topology_dump: Filter to keep only properties require for saving on disk
@ -608,7 +621,7 @@ class Node:
"name": self._name,
"console": self._console,
"console_type": self._console_type,
"properties": self._filter_properties(),
"properties": self._properties,
"label": self._label,
"x": self._x,
"y": self._y,
@ -631,7 +644,7 @@ class Node:
"console_host": str(self._compute.console_host),
"console_type": self._console_type,
"command_line": self._command_line,
"properties": self._filter_properties(),
"properties": self._properties,
"status": self._status,
"label": self._label,
"x": self._x,

View File

@ -36,7 +36,7 @@ import logging
log = logging.getLogger(__name__)
GNS3_FILE_FORMAT_REVISION = 7
GNS3_FILE_FORMAT_REVISION = 8
def _check_topology_schema(topo):
@ -138,6 +138,10 @@ def load_topology(path):
if topo["revision"] < 7:
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)
if changed:
@ -146,6 +150,34 @@ def load_topology(path):
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):
"""
Convert topologies from GNS3 2.0.0 beta 2 to beta 3.

View File

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

View File

@ -344,10 +344,7 @@ class NodeHandler:
raise aiohttp.web.HTTPForbidden
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)
response.set_status(200)
@ -384,12 +381,9 @@ class NodeHandler:
raise aiohttp.web.HTTPForbidden
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()
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)

View File

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

View File

@ -78,14 +78,6 @@ IOU_CREATE_SCHEMA = {
"description": "Use default IOU values",
"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": {
"description": "Startup-config of IOU",
"type": ["string", "null"]
@ -94,10 +86,6 @@ IOU_CREATE_SCHEMA = {
"description": "Private-config of IOU",
"type": ["string", "null"]
},
"iourc_content": {
"description": "Content of the iourc file. Ignored if Null",
"type": ["string", "null"]
}
},
"additionalProperties": False,
"required": ["name", "path"]
@ -187,30 +175,10 @@ IOU_OBJECT_SCHEMA = {
"description": "Always up ethernet interface",
"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": {
"description": "Use default IOU values",
"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": {
"description": "Last command line used by GNS3 to start QEMU",
"type": "string"

View File

@ -50,10 +50,6 @@ VPCS_CREATE_SCHEMA = {
"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,
"required": ["name"]
@ -79,14 +75,6 @@ VPCS_UPDATE_SCHEMA = {
"description": "Console type",
"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,
}
@ -133,19 +121,11 @@ VPCS_OBJECT_SCHEMA = {
"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}$"
},
"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": {
"description": "Last command line used by GNS3 to start QEMU",
"type": "string"
}
},
"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"))
assert controller.get_free_project_name("Test") == "Test-2"
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")
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):
assert node.__json__() == {
"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
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):
"""
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}
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
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),
data={'node_id': node.id,
'startup_config': 'test.cfg',
'startup_script': 'test.cfg',
'name': 'test'},
timeout=120)
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}
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={
"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),
data={'node_id': node.id,
'startup_config': 'test.cfg',
'startup_script': 'test.cfg',
'name': 'test'},
timeout=120)
assert compute in project._project_created_on_compute

View File

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

View File

@ -80,7 +80,6 @@ def test_iou_create_with_params(http_compute, project, base_params):
params["l1_keepalives"] = True
params["startup_config_content"] = "hostname test"
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)
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["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"
@ -115,7 +113,6 @@ def test_iou_create_startup_config_already_exist(http_compute, project, base_par
assert response.status == 201
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:
assert f.read() == "echo hello"
@ -183,9 +180,7 @@ def test_iou_update(http_compute, vm, tmpdir, free_console_port, project):
"ethernet_adapters": 4,
"serial_adapters": 0,
"l1_keepalives": True,
"startup_config_content": "hostname test",
"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)
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["l1_keepalives"] 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):

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.json["name"] == "PC TEST 1"
assert response.json["project_id"] == project.id
assert response.json["startup_script_path"] is None
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.json["name"] == "PC TEST 1"
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):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -61,8 +61,6 @@
1,
1
],
"private_config": "",
"private_config_content": "",
"ram": 512,
"sensors": [
22,
@ -78,8 +76,6 @@
"slot5": null,
"slot6": null,
"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"
},
"symbol": ":/symbols/router.svg",
@ -129,8 +125,6 @@
1,
1
],
"private_config": "",
"private_config_content": "",
"ram": 512,
"sensors": [
22,
@ -146,8 +140,6 @@
"slot5": null,
"slot6": null,
"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"
},
"symbol": ":/symbols/router.svg",