Compare commits

...
12 Commits
Author SHA1 Message Date
grossmj 5c8abdc6fd Release v2.2.44 2023-11-06 16:02:23 +10:00
grossmj f80f41b5b3 Sync appliances 2023-11-06 15:58:21 +10:00
grossmj 503373a6e9 Bundle web-ui v2.2.44 2023-11-06 15:53:51 +10:00
grossmj 8236c85b5f Upgrade sentry-sdk 2023-11-06 15:44:30 +10:00
grossmj ae825b0080 Non-blocking checksums computation when server starts. Fixes #2228 2023-11-06 12:32:23 +10:00
grossmj 191feed669 Fix timeout issue when creating Qemu disk image. Fixes https://github.com/GNS3/gns3-server/issues/2313 2023-11-05 15:41:46 +10:00
grossmj 3b22bcfe96 Support for web socket console over HTTPS 2023-11-03 14:40:40 +10:00
grossmj 0f07b9e019 Add back script create_cert.sh 2023-11-03 12:31:34 +10:00
grossmj 8676a3dccf Bundle web-ui 2023-11-02 17:40:47 +10:00
grossmj b0dbf59ac4 Use Python 3.8 in appveyor.yml 2023-10-31 14:48:08 +10:00
Jeremy Grossmann 14fd165f2a Merge pull request #2307 from GNS3/fix/3521
Allow disabling hardware virtualization check
2023-10-30 15:03:50 +10:00
grossmj 7c49a9160c Allow disabling hardware virtualization check 2023-10-30 14:57:11 +10:00
14 changed files with 127 additions and 25 deletions

No files matched your search

+14
View File
@@ -1,5 +1,19 @@
# Change Log
## 2.2.44 06/11/2023
* Bundle web-ui v2.2.44
* Non-blocking checksums computation when server starts. Fixes #2228
* Fix timeout issue when creating Qemu disk image. Fixes https://github.com/GNS3/gns3-server/issues/2313
* Support for web socket console over HTTPS
* Add back script create_cert.sh
* Allow disabling hardware virtualization check
* Fix L2IOU "failed code signing checks" when IOU base file name is >= 63 characters
* Change "ip cef" to "no ip cef" in IOU default configs. Fixes #2298
* Add Qemu IGB network device
* Add Python 3.12 support.
* Fix issue with importlib.resources.files() and Python 3.9
## 2.2.43 19/09/2023
* Force English output for VBoxManage. Fixes #2266
+1 -1
View File
@@ -5,7 +5,7 @@ image: Visual Studio 2022
platform: x64
environment:
PYTHON: "C:\\Python37-x64"
PYTHON: "C:\\Python38-x64"
DISTUTILS_USE_SDK: "1"
API_TOKEN:
secure: VEKn4bYH3QO0ixtQW5ni4Enmn8cS1NlZV246ludBDgQ=
+3
View File
@@ -62,6 +62,9 @@ default_nat_interface = vmnet10
; Enable the built-in templates
enable_builtin_templates = True
; check if hardware virtualization is used by other emulators (KVM, VMware or VirtualBox)
hardware_virtualization_check = True
[VPCS]
; VPCS executable location, default: search in PATH
;vpcs_path = vpcs
+14
View File
@@ -63,6 +63,13 @@
"filesize": 86704128,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v7.0.13.M-build0566-FORTINET.out.kvm.qcow2",
"version": "7.0.13",
"md5sum": "47a4bab29407153a635c54b64d6a226e",
"filesize": 89325568,
"download_url": "https://support.fortinet.com/Download/FirmwareImages.aspx"
},
{
"filename": "FGT_VM64_KVM-v7.0.12.M-build0523-FORTINET.out.kvm.qcow2",
"version": "7.0.12",
@@ -367,6 +374,13 @@
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.13",
"images": {
"hda_disk_image": "FGT_VM64_KVM-v7.0.13.M-build0566-FORTINET.out.kvm.qcow2",
"hdb_disk_image": "empty30G.qcow2"
}
},
{
"name": "7.0.12",
"images": {
+10 -7
View File
@@ -16,12 +16,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import aiohttp
import asyncio
import psutil
import platform
from .project import Project
from ..config import Config
from uuid import UUID
import logging
log = logging.getLogger(__name__)
@@ -128,10 +130,11 @@ class ProjectManager:
:returns: boolean
"""
for project in self._projects.values():
for node in project.nodes:
if node == source_node:
continue
if node.hw_virtualization and node.__class__.__name__ != source_node.__class__.__name__:
return False
if Config.instance().get_section_config("Server").getboolean("hardware_virtualization_check", True):
for project in self._projects.values():
for node in project.nodes:
if node == source_node:
continue
if node.hw_virtualization and node.__class__.__name__ != source_node.__class__.__name__:
return False
return True
+3
View File
@@ -26,6 +26,7 @@ import sys
import re
import subprocess
from ...utils import shlex_quote
from ...utils.asyncio import subprocess_check_output
from ...utils.get_resource import get_resource
from ..base_manager import BaseManager
@@ -337,6 +338,8 @@ class Qemu(BaseManager):
command.append(path)
command.append("{}M".format(img_size))
command_string = " ".join(shlex_quote(s) for s in command)
log.info("Creating disk image with {}".format(command_string))
process = await asyncio.create_subprocess_exec(*command)
await process.wait()
except (OSError, subprocess.SubprocessError) as e:
+1 -1
View File
@@ -57,7 +57,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "https://8dcaf668c2f31af6028fb4130bf2f58e@o19455.ingest.sentry.io/38482"
DSN = "https://dcbac52ef824b8386b67cc8f07c4de70@o19455.ingest.sentry.io/38482"
_instance = None
def __init__(self):
@@ -487,11 +487,18 @@ class NodeHandler:
except ValueError:
pass
ws_console_compute_url = "ws://{compute_host}:{compute_port}/v2/compute/projects/{project_id}/{node_type}/nodes/{node_id}/console/ws".format(compute_host=compute_host,
compute_port=compute.port,
project_id=project.id,
node_type=node.node_type,
node_id=node.id)
ws_protocol = "ws"
if request.scheme == "https":
ws_protocol = "wss"
ws_console_compute_url = "{protocol}://{compute_host}:{compute_port}/v2/compute/projects/{project_id}/{node_type}/nodes/{node_id}/console/ws".format(
protocol=ws_protocol,
compute_host=compute_host,
compute_port=compute.port,
project_id=project.id,
node_type=node.node_type,
node_id=node.id
)
async def ws_forward(ws_client):
async for msg in ws:
@@ -504,7 +511,7 @@ class NodeHandler:
try:
async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=None, force_close=True)) as session:
async with session.ws_connect(ws_console_compute_url) as ws_client:
async with session.ws_connect(ws_console_compute_url, ssl=False) as ws_client:
asyncio.ensure_future(ws_forward(ws_client))
async for msg in ws_client:
if msg.type == aiohttp.WSMsgType.TEXT:
@@ -515,6 +522,8 @@ class NodeHandler:
break
except ConnectionResetError:
log.info("Websocket console connection with compute disconnected")
except aiohttp.ClientError as e:
log.error("Websocket console connection with compute failed: {}".format(e))
finally:
if not ws.closed:
await ws.close()
+1 -1
View File
@@ -46,6 +46,6 @@
gtag('config', 'G-5D6FZL9923');
</script>
<script src="runtime.baa1121a4737aeb68bb7.js" defer></script><script src="polyfills-es5.865074f5cd9a121111a2.js" nomodule defer></script><script src="polyfills.2f91a039d848e57ff02e.js" defer></script><script src="main.6b11a28a9dc4ce54a2ab.js" defer></script>
<script src="runtime.baa1121a4737aeb68bb7.js" defer></script><script src="polyfills-es5.865074f5cd9a121111a2.js" nomodule defer></script><script src="polyfills.2f91a039d848e57ff02e.js" defer></script><script src="main.383fdade2fd9dbccffbc.js" defer></script>
</body></html>
@@ -1 +1 @@
(self.webpackChunkgns3_web_ui=self.webpackChunkgns3_web_ui||[]).push([[179],{98255:function(ue){function q(f){return Promise.resolve().then(function(){var U=new Error("Cannot find module '"+f+"'");throw U.code="MODULE_NOT_FOUND",U})}q.keys=function(){return[]},q.resolve=q,q.id=98255,ue.exports=q},82908:function(ue){ue.exports=function(f,U){(null==U||U>f.length)&&(U=f.length);for(var B=0,V=new Array(U);B<U;B++)V[B]=f[B];return V},ue.exports.default=ue.exports,ue.exports.__esModule=!0},36457:function(ue){ue.exports=function(f){if(Array.isArray(f))return f},ue.exports.default=ue.exports,ue.exports.__esModule=!0},35036:function(ue){ue.exports=function(f,U){var B=null==f?null:"undefined"!=typeof Symbol&&f[Symbol.iterator]||f["@@iterator"];if(null!=B){var R,b,V=[],Z=!0,T=!1;try{for(B=B.call(f);!(Z=(R=B.next()).done)&&(V.push(R.value),!U||V.length!==U);Z=!0);}catch(v){T=!0,b=v}finally{try{!Z&&null!=B.return&&B.return()}finally{if(T)throw b}}return V}},ue.exports.default=ue.exports,ue.exports.__esModule=!0},13969:function(ue){ue.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},ue.exports.default=ue.exports,ue.exports.__esModule=!0},6326:function(ue,q,f){var U=f(36457),B=f(35036),V=f(54850),Z=f(13969);ue.exports=function(R,b){return U(R)||B(R,b)||V(R,b)||Z()},ue.exports.default=ue.exports,ue.exports.__esModule=!0},54850:function(ue,q,f){var U=f(82908);ue.exports=function(V,Z){if(V){if("string"==typeof V)return U(V,Z);var T=Object.prototype.toString.call(V).slice(8,-1);if("Object"===T&&V.constructor&&(T=V.constructor.name),"Map"===T||"Set"===T)return Array.from(V);if("Arguments"===T||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(T))return U(V,Z)}},ue.exports.default=ue.exports,ue.exports.__esModule=!0},26552:function(ue,q,f){ue.exports=f(55590)},61855:function(ue,q,f){"use strict";f.d(q,{ZT:function(){return B},mG:function(){return v},Jh:function(){return I},ev:function(){return N}});var U=function($,ae){return(U=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(se,ce){se.__proto__=ce}||function(se,ce){for(var le in ce)Object.prototype.hasOwnProperty.call(ce,le)&&(se[le]=ce[le])})($,ae)};function B($,ae){if("function"!=typeof ae&&null!==ae)throw new TypeError("Class extends value "+String(ae)+" is not a constructor or null");function se(){this.constructor=$}U($,ae),$.prototype=null===ae?Object.create(ae):(se.prototype=ae.prototype,new se)}function v($,ae,se,ce){return new(se||(se=Promise))(function(oe,Ae){function be(_t){try{qe(ce.next(_t))}catch(yt){Ae(yt)}}function it(_t){try{qe(ce.throw(_t))}catch(yt){Ae(yt)}}function qe(_t){_t.done?oe(_t.value):function(oe){return oe instanceof se?oe:new se(function(Ae){Ae(oe)})}(_t.value).then(be,it)}qe((ce=ce.apply($,ae||[])).next())})}function I($,ae){var ce,le,oe,Ae,se={label:0,sent:function(){if(1&oe[0])throw oe[1];return oe[1]},trys:[],ops:[]};return Ae={next:be(0),throw:be(1),return:be(2)},"function"==typeof Symbol&&(Ae[Symbol.iterator]=function(){return this}),Ae;function be(qe){return function(_t){return function(qe){if(ce)throw new TypeError("Generator is already executing.");for(;se;)try{if(ce=1,le&&(oe=2&qe[0]?le.return:qe[0]?le.throw||((oe=le.return)&&oe.call(le),0):le.next)&&!(oe=oe.call(le,qe[1])).done)return oe;switch(le=0,oe&&(qe=[2&qe[0],oe.value]),qe[0]){case 0:case 1:oe=qe;break;case 4:return se.label++,{value:qe[1],done:!1};case 5:se.label++,le=qe[1],qe=[0];continue;case 7:qe=se.ops.pop(),se.trys.pop();continue;default:if(!(oe=(oe=se.trys).length>0&&oe[oe.length-1])&&(6===qe[0]||2===qe[0])){se=0;continue}if(3===qe[0]&&(!oe||qe[1]>oe[0]&&qe[1]<oe[3])){se.label=qe[1];break}if(6===qe[0]&&se.label<oe[1]){se.label=oe[1],oe=qe;break}if(oe&&se.label<oe[2]){se.label=oe[2],se.ops.push(qe);break}oe[2]&&se.ops.pop(),se.trys.pop();continue}qe=ae.call($,se)}catch(_t){qe=[6,_t],le=0}finally{ce=oe=0}if(5&qe[0])throw qe[1];return{value:qe[0]?qe[1]:void 0,done:!0}}([qe,_t])}}}function N($,ae,se){if(se||2===arguments.length)for(var oe,ce=0,le=ae.length;ce<le;ce++)(oe||!(ce in ae))&&(oe||(oe=Array.prototype.slice.call(ae,0,ce)),oe[ce]=ae[ce]);return $.concat(oe||Array.prototype.slice.call(ae))}},739:function(ue,q,f){"use strict";f.d(q,{l3:function(){return T},_j:function(){return V},LC:function(){return Z},ZN:function(){return O},jt:function(){return b},pV:function(){return E},F4:function(){return M},IO:function(){return A},vP:function(){return I},SB:function(){return k},oB:function(){return D},eR:function(){return _},X$:function(){return R},ZE:function(){return F},k1:function(){return z}});var U=f(14105),B=f(18967),V=function K(){(0,B.Z)(this,K)},Z=function K(){(0,B.Z)(this,K)},T="*";function R(K,j){return{type:7,name:K,definitions:j,options:{}}}function b(K){var j=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return{type:4,styles:j,timings:K}}function I(K){var j=arguments.length>1&&void 0!==arguments[1]?arguments[1]Line truncated
(self.webpackChunkgns3_web_ui=self.webpackChunkgns3_web_ui||[]).push([[179],{98255:function(ue){function q(f){return Promise.resolve().then(function(){var U=new Error("Cannot find module '"+f+"'");throw U.code="MODULE_NOT_FOUND",U})}q.keys=function(){return[]},q.resolve=q,q.id=98255,ue.exports=q},82908:function(ue){ue.exports=function(f,U){(null==U||U>f.length)&&(U=f.length);for(var B=0,V=new Array(U);B<U;B++)V[B]=f[B];return V},ue.exports.default=ue.exports,ue.exports.__esModule=!0},36457:function(ue){ue.exports=function(f){if(Array.isArray(f))return f},ue.exports.default=ue.exports,ue.exports.__esModule=!0},35036:function(ue){ue.exports=function(f,U){var B=null==f?null:"undefined"!=typeof Symbol&&f[Symbol.iterator]||f["@@iterator"];if(null!=B){var R,b,V=[],Z=!0,T=!1;try{for(B=B.call(f);!(Z=(R=B.next()).done)&&(V.push(R.value),!U||V.length!==U);Z=!0);}catch(v){T=!0,b=v}finally{try{!Z&&null!=B.return&&B.return()}finally{if(T)throw b}}return V}},ue.exports.default=ue.exports,ue.exports.__esModule=!0},13969:function(ue){ue.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},ue.exports.default=ue.exports,ue.exports.__esModule=!0},6326:function(ue,q,f){var U=f(36457),B=f(35036),V=f(54850),Z=f(13969);ue.exports=function(R,b){return U(R)||B(R,b)||V(R,b)||Z()},ue.exports.default=ue.exports,ue.exports.__esModule=!0},54850:function(ue,q,f){var U=f(82908);ue.exports=function(V,Z){if(V){if("string"==typeof V)return U(V,Z);var T=Object.prototype.toString.call(V).slice(8,-1);if("Object"===T&&V.constructor&&(T=V.constructor.name),"Map"===T||"Set"===T)return Array.from(V);if("Arguments"===T||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(T))return U(V,Z)}},ue.exports.default=ue.exports,ue.exports.__esModule=!0},26552:function(ue,q,f){ue.exports=f(55590)},61855:function(ue,q,f){"use strict";f.d(q,{ZT:function(){return B},mG:function(){return v},Jh:function(){return I},ev:function(){return N}});var U=function($,ae){return(U=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(se,ce){se.__proto__=ce}||function(se,ce){for(var le in ce)Object.prototype.hasOwnProperty.call(ce,le)&&(se[le]=ce[le])})($,ae)};function B($,ae){if("function"!=typeof ae&&null!==ae)throw new TypeError("Class extends value "+String(ae)+" is not a constructor or null");function se(){this.constructor=$}U($,ae),$.prototype=null===ae?Object.create(ae):(se.prototype=ae.prototype,new se)}function v($,ae,se,ce){return new(se||(se=Promise))(function(oe,Ae){function be(_t){try{qe(ce.next(_t))}catch(yt){Ae(yt)}}function it(_t){try{qe(ce.throw(_t))}catch(yt){Ae(yt)}}function qe(_t){_t.done?oe(_t.value):function(oe){return oe instanceof se?oe:new se(function(Ae){Ae(oe)})}(_t.value).then(be,it)}qe((ce=ce.apply($,ae||[])).next())})}function I($,ae){var ce,le,oe,Ae,se={label:0,sent:function(){if(1&oe[0])throw oe[1];return oe[1]},trys:[],ops:[]};return Ae={next:be(0),throw:be(1),return:be(2)},"function"==typeof Symbol&&(Ae[Symbol.iterator]=function(){return this}),Ae;function be(qe){return function(_t){return function(qe){if(ce)throw new TypeError("Generator is already executing.");for(;se;)try{if(ce=1,le&&(oe=2&qe[0]?le.return:qe[0]?le.throw||((oe=le.return)&&oe.call(le),0):le.next)&&!(oe=oe.call(le,qe[1])).done)return oe;switch(le=0,oe&&(qe=[2&qe[0],oe.value]),qe[0]){case 0:case 1:oe=qe;break;case 4:return se.label++,{value:qe[1],done:!1};case 5:se.label++,le=qe[1],qe=[0];continue;case 7:qe=se.ops.pop(),se.trys.pop();continue;default:if(!(oe=(oe=se.trys).length>0&&oe[oe.length-1])&&(6===qe[0]||2===qe[0])){se=0;continue}if(3===qe[0]&&(!oe||qe[1]>oe[0]&&qe[1]<oe[3])){se.label=qe[1];break}if(6===qe[0]&&se.label<oe[1]){se.label=oe[1],oe=qe;break}if(oe&&se.label<oe[2]){se.label=oe[2],se.ops.push(qe);break}oe[2]&&se.ops.pop(),se.trys.pop();continue}qe=ae.call($,se)}catch(_t){qe=[6,_t],le=0}finally{ce=oe=0}if(5&qe[0])throw qe[1];return{value:qe[0]?qe[1]:void 0,done:!0}}([qe,_t])}}}function N($,ae,se){if(se||2===arguments.length)for(var oe,ce=0,le=ae.length;ce<le;ce++)(oe||!(ce in ae))&&(oe||(oe=Array.prototype.slice.call(ae,0,ce)),oe[ce]=ae[ce]);return $.concat(oe||Array.prototype.slice.call(ae))}},739:function(ue,q,f){"use strict";f.d(q,{l3:function(){return T},_j:function(){return V},LC:function(){return Z},ZN:function(){return O},jt:function(){return b},pV:function(){return E},F4:function(){return M},IO:function(){return A},vP:function(){return I},SB:function(){return k},oB:function(){return D},eR:function(){return _},X$:function(){return R},ZE:function(){return F},k1:function(){return z}});var U=f(14105),B=f(18967),V=function K(){(0,B.Z)(this,K)},Z=function K(){(0,B.Z)(this,K)},T="*";function R(K,j){return{type:7,name:K,definitions:j,options:{}}}function b(K){var j=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return{type:4,styles:j,timings:K}}function I(K){var j=arguments.length>1&&void 0!==arguments[1]?arguments[1]Line truncated
+2 -2
View File
@@ -23,8 +23,8 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "2.2.44.dev1"
__version_info__ = (2, 2, 44, 99)
__version__ = "2.2.44"
__version_info__ = (2, 2, 44, 0)
if "dev" in __version__:
try:
+21 -5
View File
@@ -29,6 +29,7 @@ import functools
import time
import atexit
import weakref
import concurrent.futures
# Import encoding now, to avoid implicit import later.
# Implicit import within threads may cause LookupError when standard library is in a ZIP
@@ -38,7 +39,7 @@ from .route import Route
from ..config import Config
from ..compute import MODULES
from ..compute.port_manager import PortManager
from ..compute.qemu import Qemu
from ..utils.images import list_images
from ..controller import Controller
# do not delete this import
@@ -87,6 +88,8 @@ class WebServer:
except (RuntimeError, OSError, asyncio.CancelledError) as e:
log.critical("Could not start the server: {}".format(e))
return False
except KeyboardInterrupt:
return False
return True
async def reload_server(self):
@@ -230,16 +233,27 @@ class WebServer:
atexit.register(close_asyncio_loop)
async def _compute_image_checksums(self):
"""
Compute image checksums.
"""
loop = asyncio.get_event_loop()
with concurrent.futures.ProcessPoolExecutor(max_workers=1) as pool:
log.info("Computing image checksums...")
await loop.run_in_executor(pool, list_images, "qemu")
log.info("Finished computing image checksums")
async def _on_startup(self, *args):
"""
Called when the HTTP server start
"""
await Controller.instance().start()
# Because with a large image collection
# without md5sum already computed we start the
# computing with server start
asyncio.ensure_future(Qemu.instance().list_images())
# Start computing checksums now because it can take a long time
# for a large image collection
await self._compute_image_checksums()
def run(self):
"""
@@ -330,6 +344,8 @@ class WebServer:
try:
self._loop.run_forever()
except ConnectionResetError:
log.warning("Connection reset by peer")
except TypeError as e:
# This is to ignore an asyncio.windows_events exception
# on Windows when the process gets the SIGBREAK signal
+1 -1
View File
@@ -7,7 +7,7 @@ aiofiles>=23.2.1,<23.3; python_version >= '3.7'
aiofiles==0.8.0; python_version < '3.7' # v0.8.0 is the last version to support Python 3.6
Jinja2>=3.1.2,<3.2; python_version >= '3.7'
Jinja2==3.0.3; python_version < '3.7' # v3.0.3 is the last version to support Python 3.6
sentry-sdk==1.32.0,<1.33
sentry-sdk==1.34.0,<1.35
psutil==5.9.6
async-timeout>=4.0.2,<4.1
distro>=1.8.0
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Bash shell script for generating self-signed certs.
# The certificate is automatically put in your GNS3 config
DST_DIR="$HOME/.config/GNS3/ssl"
OLD_DIR=`pwd`
fail_if_error() {
[ $1 != 0 ] && {
unset PASSPHRASE
cd $OLD_DIR
exit 10
}
}
mkdir -p $DST_DIR
fail_if_error $?
cd $DST_DIR
SUBJ="/C=US/ST=Texas/O=GNS3SELF/localityName=Austin/commonName=localhost/organizationalUnitName=GNS3Server/emailAddress=gns3cert@gns3.com"
openssl req -nodes -new -x509 -keyout server.key -out server.cert -subj "$SUBJ"