mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
Merge pull request #890 from GNS3/base_config_server_side
Manage base configuration on server
This commit is contained in:
commit
5672b4b748
31 files changed
+649
-324
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
set pcname %h
|
||||
@@ -18,6 +18,7 @@
|
||||
import os
|
||||
import json
|
||||
import socket
|
||||
import shutil
|
||||
import asyncio
|
||||
import aiohttp
|
||||
|
||||
@@ -29,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__)
|
||||
@@ -53,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")
|
||||
@@ -162,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
|
||||
@@ -171,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):
|
||||
"""
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
Reference in new issue
Block a user