1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-26 00:38:10 +00:00

Merge pull request #4 from planctechnologies/gns3-87

Gns3 87 - add server security and startup scripts
This commit is contained in:
jseutter 2014-09-08 09:25:28 -06:00
commit 6ff2c654d9
12 changed files with 654 additions and 22 deletions

View File

@ -24,7 +24,7 @@
# number has been incremented) # number has been incremented)
""" """
Monitors communication with the GNS3 client via tmp file. Will terminate the instance if Monitors communication with the GNS3 client via tmp file. Will terminate the instance if
communication is lost. communication is lost.
""" """
@ -62,7 +62,7 @@ sys.path.append(EXTRA_LIB)
import daemon import daemon
my_daemon = None my_daemon = None
usage = """ usage = """
USAGE: %s USAGE: %s
@ -73,14 +73,14 @@ Options:
-v, --verbose Enable verbose logging -v, --verbose Enable verbose logging
-h, --help Display this menu :) -h, --help Display this menu :)
--cloud_api_key <api_key> Rackspace API key --cloud_api_key <api_key> Rackspace API key
--cloud_user_name --cloud_user_name
--instance_id ID of the Rackspace instance to terminate --instance_id ID of the Rackspace instance to terminate
--deadtime How long in seconds can the communication lose exist before we --deadtime How long in seconds can the communication lose exist before we
shutdown this instance. shutdown this instance.
Default: Default:
Example --deadtime=3600 (60 minutes) Example --deadtime=3600 (60 minutes)
--check-interval Defaults to --deadtime, used for debugging --check-interval Defaults to --deadtime, used for debugging
@ -145,8 +145,7 @@ def parse_cmd_line(argv):
else: else:
cmd_line_option_list['syslog'] = ('localhost',514) cmd_line_option_list['syslog'] = ('localhost',514)
get_gns3config(cmd_line_option_list)
get_gns3secrets(cmd_line_option_list)
for opt, val in opts: for opt, val in opts:
if (opt in ("-h", "--help")): if (opt in ("-h", "--help")):
@ -202,7 +201,7 @@ def parse_cmd_line(argv):
return cmd_line_option_list return cmd_line_option_list
def get_gns3secrets(cmd_line_option_list): def get_gns3config(cmd_line_option_list):
""" """
Load cloud credentials from .gns3secrets Load cloud credentials from .gns3secrets
""" """
@ -225,6 +224,17 @@ def get_gns3secrets(cmd_line_option_list):
except configparser.NoSectionError: except configparser.NoSectionError:
pass pass
cloud_config_file = "%s/.config/GNS3/cloud.conf" % (
os.path.expanduser("~/"))
if os.path.isfile(cloud_config_file):
config.read(cloud_config_file)
try:
for key, value in config.items("CLOUD_SERVER"):
cmd_line_option_list[key] = value.strip()
except configparser.NoSectionError:
pass
def set_logging(cmd_options): def set_logging(cmd_options):
""" """
@ -256,7 +266,7 @@ def set_logging(cmd_options):
) )
syslog_hndlr.setFormatter(sys_formatter) syslog_hndlr.setFormatter(sys_formatter)
log.setLevel(log_level) log.setLevel(log_level)
log.addHandler(console_log) log.addHandler(console_log)
log.addHandler(syslog_hndlr) log.addHandler(syslog_hndlr)
@ -308,7 +318,7 @@ def monitor_loop(options):
if delta.seconds > options["deadtime"]: if delta.seconds > options["deadtime"]:
log.warning("Deadtime exceeded, terminating instance ...") log.warning("Deadtime exceeded, terminating instance ...")
#Terminate involes many layers of HTTP / API calls, lots of #Terminate involes many layers of HTTP / API calls, lots of
#different errors types could occur here. #different errors types could occur here.
try: try:
rksp = Rackspace(options) rksp = Rackspace(options)
@ -341,7 +351,8 @@ def main():
log.info("Received shutdown signal") log.info("Received shutdown signal")
options["shutdown"] = True options["shutdown"] = True
sys.exit(0)
pid_file = "%s/.gns3ias.pid" % (expanduser("~")) pid_file = "%s/.gns3ias.pid" % (expanduser("~"))
if options["shutdown"]: if options["shutdown"]:

View File

@ -0,0 +1,99 @@
#!/bin/bash
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 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. Run this in a folder, as it
# generates a few files. Large portions of this script were taken from the
# following artcile:
#
# http://usrportage.de/archives/919-Batch-generating-SSL-certificates.html
#
# Additional alterations by: Brad Landers
# Date: 2012-01-27
# https://gist.github.com/bradland/1690807
# Script accepts a single argument, the fqdn for the cert
DST_DIR="$HOME/.config/GNS3Certs/"
OLD_DIR=`pwd`
#GNS3 Server expects to find certs with the default FQDN below. If you create
#different certs you will need to update server.py
DOMAIN="$1"
if [ -z "$DOMAIN" ]; then
DOMAIN="gns3server.localdomain.com"
fi
fail_if_error() {
[ $1 != 0 ] && {
unset PASSPHRASE
cd $OLD_DIR
exit 10
}
}
mkdir -p $DST_DIR
fail_if_error $?
cd $DST_DIR
# Generate a passphrase
export PASSPHRASE=$(head -c 500 /dev/urandom | tr -dc a-z0-9A-Z | head -c 128; echo)
# Certificate details; replace items in angle brackets with your own info
subj="
C=CA
ST=Alberta
O=GNS3
localityName=Calgary
commonName=gns3server.localdomain.com
organizationalUnitName=GNS3Server
emailAddress=gns3cert@gns3.com
"
# Generate the server private key
openssl genrsa -aes256 -out $DOMAIN.key -passout env:PASSPHRASE 2048
fail_if_error $?
#openssl rsa -outform der -in $DOMAIN.pem -out $DOMAIN.key -passin env:PASSPHRASE
# Generate the CSR
openssl req \
-new \
-batch \
-subj "$(echo -n "$subj" | tr "\n" "/")" \
-key $DOMAIN.key \
-out $DOMAIN.csr \
-passin env:PASSPHRASE
fail_if_error $?
cp $DOMAIN.key $DOMAIN.key.org
fail_if_error $?
# Strip the password so we don't have to type it every time we restart Apache
openssl rsa -in $DOMAIN.key.org -out $DOMAIN.key -passin env:PASSPHRASE
fail_if_error $?
# Generate the cert (good for 10 years)
openssl x509 -req -days 3650 -in $DOMAIN.csr -signkey $DOMAIN.key -out $DOMAIN.crt
fail_if_error $?
echo "${DST_DIR}${DOMAIN}.key"
echo "${DST_DIR}${DOMAIN}.crt"
cd $OLD_DIR

View File

@ -62,16 +62,21 @@ class Config(object):
# 5: server.conf in the current working directory # 5: server.conf in the current working directory
home = os.path.expanduser("~") home = os.path.expanduser("~")
self._cloud_config = os.path.join(home, ".config", appname, "cloud.conf")
filename = "server.conf" filename = "server.conf"
self._files = [os.path.join(home, ".config", appname, filename), self._files = [os.path.join(home, ".config", appname, filename),
os.path.join(home, ".config", appname + ".conf"), os.path.join(home, ".config", appname + ".conf"),
os.path.join("/etc/xdg", appname, filename), os.path.join("/etc/xdg", appname, filename),
os.path.join("/etc/xdg", appname + ".conf"), os.path.join("/etc/xdg", appname + ".conf"),
filename] filename,
self._cloud_config]
self._config = configparser.ConfigParser() self._config = configparser.ConfigParser()
self.read_config() self.read_config()
def list_cloud_config_file(self):
return self._cloud_config
def read_config(self): def read_config(self):
""" """
Read the configuration files. Read the configuration files.

View File

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 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/>.
"""
Simple file upload & listing handler.
"""
import os
import tornado.web
import tornado.websocket
import logging
log = logging.getLogger(__name__)
class GNS3BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
user = self.get_secure_cookie("user")
if not user:
return None
if self.settings['required_user'] == user.decode("utf-8"):
return user
class GNS3WebSocketBaseHandler(tornado.websocket.WebSocketHandler):
def get_current_user(self):
user = self.get_secure_cookie("user")
if not user:
return None
if self.settings['required_user'] == user.decode("utf-8"):
return user
class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.write('<html><body><form action="/login" method="post">'
'Name: <input type="text" name="name">'
'Password: <input type="text" name="password">'
'<input type="submit" value="Sign in">'
'</form></body></html>')
try:
redirect_to = self.get_argument("next")
self.set_secure_cookie("login_success_redirect_to", redirect_to)
except tornado.web.MissingArgumentError:
pass
def post(self):
user = self.get_argument("name")
password = self.get_argument("password")
if self.settings['required_user'] == user and self.settings['required_pass'] == password:
self.set_secure_cookie("user", user)
auth_status = "successful"
else:
self.set_secure_cookie("user", "None")
auth_status = "failure"
log.info("Authentication attempt %s: %s" %(auth_status, user))
try:
redirect_to = self.get_secure_cookie("login_success_redirect_to")
except tornado.web.MissingArgumentError:
redirect_to = "/"
self.redirect(redirect_to)

View File

@ -23,6 +23,7 @@ Simple file upload & listing handler.
import os import os
import stat import stat
import tornado.web import tornado.web
from .auth_handler import GNS3BaseHandler
from ..version import __version__ from ..version import __version__
from ..config import Config from ..config import Config
@ -30,7 +31,7 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class FileUploadHandler(tornado.web.RequestHandler): class FileUploadHandler(GNS3BaseHandler):
""" """
File upload handler. File upload handler.
@ -54,6 +55,7 @@ class FileUploadHandler(tornado.web.RequestHandler):
except OSError as e: except OSError as e:
log.error("could not create the upload directory {}: {}".format(self._upload_dir, e)) log.error("could not create the upload directory {}: {}".format(self._upload_dir, e))
@tornado.web.authenticated
def get(self): def get(self):
""" """
Invoked on GET request. Invoked on GET request.
@ -70,6 +72,7 @@ class FileUploadHandler(tornado.web.RequestHandler):
path=path, path=path,
items=items) items=items)
@tornado.web.authenticated
def post(self): def post(self):
""" """
Invoked on POST request. Invoked on POST request.

View File

@ -22,6 +22,7 @@ JSON-RPC protocol over Websockets.
import zmq import zmq
import uuid import uuid
import tornado.websocket import tornado.websocket
from .auth_handler import GNS3WebSocketBaseHandler
from tornado.escape import json_decode from tornado.escape import json_decode
from ..jsonrpc import JSONRPCParseError from ..jsonrpc import JSONRPCParseError
from ..jsonrpc import JSONRPCInvalidRequest from ..jsonrpc import JSONRPCInvalidRequest
@ -33,7 +34,7 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class JSONRPCWebSocket(tornado.websocket.WebSocketHandler): class JSONRPCWebSocket(GNS3WebSocketBaseHandler):
""" """
STOMP protocol over Tornado Websockets with message STOMP protocol over Tornado Websockets with message
routing to ZeroMQ dealer clients. routing to ZeroMQ dealer clients.
@ -116,7 +117,15 @@ class JSONRPCWebSocket(tornado.websocket.WebSocketHandler):
""" """
log.info("Websocket client {} connected".format(self.session_id)) log.info("Websocket client {} connected".format(self.session_id))
self.clients.add(self)
authenticated_user = self.get_current_user()
if authenticated_user:
self.clients.add(self)
log.info("Websocket authenticated user: %s" % (authenticated_user))
else:
self.close()
log.info("Websocket non-authenticated user attempt: %s" % (authenticated_user))
def on_message(self, message): def on_message(self, message):
""" """

View File

@ -16,11 +16,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import tornado.web import tornado.web
from .auth_handler import GNS3BaseHandler
from ..version import __version__ from ..version import __version__
class VersionHandler(tornado.web.RequestHandler): class VersionHandler(GNS3BaseHandler):
@tornado.web.authenticated
def get(self): def get(self):
response = {'version': __version__} response = {'version': __version__}
self.write(response) self.write(response)

View File

@ -20,8 +20,9 @@ from .base import IModule
from .dynamips import Dynamips from .dynamips import Dynamips
from .vpcs import VPCS from .vpcs import VPCS
from .virtualbox import VirtualBox from .virtualbox import VirtualBox
from .deadman import DeadMan
MODULES = [Dynamips, VPCS, VirtualBox] MODULES = [Dynamips, VPCS, VirtualBox, DeadMan]
if sys.platform.startswith("linux"): if sys.platform.startswith("linux"):
# IOU runs only on Linux # IOU runs only on Linux

View File

@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 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/>.
"""
DeadMan server module.
"""
import os
import time
import subprocess
from gns3server.modules import IModule
from gns3server.config import Config
import logging
log = logging.getLogger(__name__)
class DeadMan(IModule):
"""
DeadMan module.
:param name: module name
:param args: arguments for the module
:param kwargs: named arguments for the module
"""
def __init__(self, name, *args, **kwargs):
config = Config.instance()
# a new process start when calling IModule
IModule.__init__(self, name, *args, **kwargs)
self._host = kwargs["host"]
self._projects_dir = kwargs["projects_dir"]
self._tempdir = kwargs["temp_dir"]
self._working_dir = self._projects_dir
self._heartbeat_file = "%s/heartbeat_file_for_gnsdms" % (
self._tempdir)
if 'heartbeat_file' in kwargs:
self._heartbeat_file = kwargs['heartbeat_file']
self._deadman_process = None
self.heartbeat()
self.start()
def _start_deadman_process(self):
"""
Start a subprocess and return the object
"""
#gnsserver gets configuration options from cloud.conf. This is where
#the client adds specific cloud information.
#gns3dms also reads in cloud.conf. That is why we don't need to specific
#all the command line arguments here.
cmd = []
cmd.append("gns3dms")
cmd.append("--file")
cmd.append("%s" % (self._heartbeat_file))
cmd.append("--background")
log.debug("Deadman: Running %s"%(cmd))
process = subprocess.Popen(cmd, stderr=subprocess.STDOUT, shell=False)
return process
def _stop_deadman_process(self):
"""
Start a subprocess and return the object
"""
cmd = []
cmd.append("gns3dms")
cmd.append("-k")
log.debug("Deadman: Running %s"%(cmd))
process = subprocess.Popen(cmd, shell=False)
return process
def stop(self, signum=None):
"""
Properly stops the module.
:param signum: signal number (if called by the signal handler)
"""
if self._deadman_process == None:
log.info("Deadman: Can't stop, is not currently running")
log.debug("Deadman: Stopping process")
self._deadman_process = self._stop_deadman_process()
self._deadman_process = None
#Jerry or Jeremy why do we do this? Won't this stop the I/O loop for
#for everyone?
IModule.stop(self, signum) # this will stop the I/O loop
def start(self, request=None):
"""
Start the deadman process on the server
"""
self._deadman_process = self._start_deadman_process()
log.debug("Deadman: Process is starting")
@IModule.route("deadman.reset")
def reset(self, request=None):
"""
Resets the module (JSON-RPC notification).
:param request: JSON request (not used)
"""
self.stop()
self.start()
log.info("Deadman: Module has been reset")
@IModule.route("deadman.heartbeat")
def heartbeat(self, request=None):
"""
Update a file on the server that the deadman switch will monitor
"""
now = time.time()
with open(self._heartbeat_file, 'w') as heartbeat_file:
heartbeat_file.write(str(now))
heartbeat_file.close()
log.debug("Deadman: heartbeat_file updated: %s %s" % (
self._heartbeat_file,
now,
))
self.start()

View File

@ -33,12 +33,16 @@ import tornado.ioloop
import tornado.web import tornado.web
import tornado.autoreload import tornado.autoreload
import pkg_resources import pkg_resources
from os.path import expanduser
import base64
import uuid
from pkg_resources import parse_version from pkg_resources import parse_version
from .config import Config from .config import Config
from .handlers.jsonrpc_websocket import JSONRPCWebSocket from .handlers.jsonrpc_websocket import JSONRPCWebSocket
from .handlers.version_handler import VersionHandler from .handlers.version_handler import VersionHandler
from .handlers.file_upload_handler import FileUploadHandler from .handlers.file_upload_handler import FileUploadHandler
from .handlers.auth_handler import LoginHandler
from .builtins.server_version import server_version from .builtins.server_version import server_version
from .builtins.interfaces import interfaces from .builtins.interfaces import interfaces
from .modules import MODULES from .modules import MODULES
@ -46,12 +50,12 @@ from .modules import MODULES
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Server(object): class Server(object):
# built-in handlers # built-in handlers
handlers = [(r"/version", VersionHandler), handlers = [(r"/version", VersionHandler),
(r"/upload", FileUploadHandler)] (r"/upload", FileUploadHandler),
(r"/login", LoginHandler)]
def __init__(self, host, port, ipc=False): def __init__(self, host, port, ipc=False):
@ -136,11 +140,23 @@ class Server(object):
JSONRPCWebSocket.register_destination(destination, instance.name) JSONRPCWebSocket.register_destination(destination, instance.name)
instance.start() # starts the new process instance.start() # starts the new process
def run(self): def run(self):
""" """
Starts the Tornado web server and ZeroMQ server. Starts the Tornado web server and ZeroMQ server.
""" """
# FIXME: debug mode!
cloud_config = Config.instance().get_section_config("CLOUD_SERVER")
settings = {
"debug":True,
"cookie_secret": base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes),
"login_url": "/login",
"required_user" : cloud_config['WEB_USERNAME'],
"required_pass" : cloud_config['WEB_PASSWORD'],
}
router = self._create_zmq_router() router = self._create_zmq_router()
# Add our JSON-RPC Websocket handler to Tornado # Add our JSON-RPC Websocket handler to Tornado
self.handlers.extend([(r"/", JSONRPCWebSocket, dict(zmq_router=router))]) self.handlers.extend([(r"/", JSONRPCWebSocket, dict(zmq_router=router))])
@ -150,7 +166,7 @@ class Server(object):
templates_dir = pkg_resources.resource_filename("gns3server", "templates") templates_dir = pkg_resources.resource_filename("gns3server", "templates")
tornado_app = tornado.web.Application(self.handlers, tornado_app = tornado.web.Application(self.handlers,
template_path=templates_dir, template_path=templates_dir,
debug=True) # FIXME: debug mode! **settings) # FIXME: debug mode!
try: try:
print("Starting server on {}:{} (Tornado v{}, PyZMQ v{}, ZMQ v{})".format(self._host, print("Starting server on {}:{} (Tornado v{}, PyZMQ v{}, ZMQ v{})".format(self._host,
@ -159,6 +175,16 @@ class Server(object):
zmq.__version__, zmq.__version__,
zmq.zmq_version())) zmq.zmq_version()))
kwargs = {"address": self._host} kwargs = {"address": self._host}
if cloud_config["SSL_ENABLED"] == "yes":
ssl_options = {
"certfile" : cloud_config["SSL_CRT"],
"keyfile" : cloud_config["SSL_KEY"],
}
log.info("Certs found - starting in SSL mode")
kwargs["ssl_options"] = ssl_options
if parse_version(tornado.version) >= parse_version("3.1"): if parse_version(tornado.version) >= parse_version("3.1"):
kwargs["max_buffer_size"] = 524288000 # 500 MB file upload limit kwargs["max_buffer_size"] = 524288000 # 500 MB file upload limit
tornado_app.listen(self._port, **kwargs) tornado_app.listen(self._port, **kwargs)

240
gns3server/start_server.py Normal file
View File

@ -0,0 +1,240 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 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/>.
# __version__ is a human-readable version number.
# __version_info__ is a four-tuple for programmatic comparison. The first
# three numbers are the components of the version number. The fourth
# is zero for an official release, positive for a development branch,
# or negative for a release candidate or beta (after the base version
# number has been incremented)
"""
Startup script for GNS3 Server Cloud Instance
"""
import os
import sys
import configparser
import getopt
import datetime
import signal
from logging.handlers import *
from os.path import expanduser
from gns3server.config import Config
import ast
import subprocess
import uuid
SCRIPT_NAME = os.path.basename(__file__)
#Is the full path when used as an import
SCRIPT_PATH = os.path.dirname(__file__)
if not SCRIPT_PATH:
SCRIPT_PATH = os.path.join(os.path.dirname(os.path.abspath(
sys.argv[0])))
LOG_NAME = "gns3-startup"
log = None
usage = """
USAGE: %s
Options:
-d, --debug Enable debugging
-v, --verbose Enable verbose logging
-h, --help Display this menu :)
--data Python dict of data to be written to the config file:
" { 'gns3' : 'Is AWESOME' } "
""" % (SCRIPT_NAME)
# Parse cmd line options
def parse_cmd_line(argv):
"""
Parse command line arguments
argv: Pass in cmd line arguments
"""
short_args = "dvh"
long_args = ("debug",
"verbose",
"help",
"data=",
)
try:
opts, extra_opts = getopt.getopt(argv[1:], short_args, long_args)
except getopt.GetoptError as e:
print("Unrecognized command line option or missing required argument: %s" %(e))
print(usage)
sys.exit(2)
cmd_line_option_list = {}
cmd_line_option_list["debug"] = False
cmd_line_option_list["verbose"] = True
cmd_line_option_list["data"] = None
if sys.platform == "linux":
cmd_line_option_list['syslog'] = "/dev/log"
elif sys.platform == "osx":
cmd_line_option_list['syslog'] = "/var/run/syslog"
else:
cmd_line_option_list['syslog'] = ('localhost',514)
for opt, val in opts:
if (opt in ("-h", "--help")):
print(usage)
sys.exit(0)
elif (opt in ("-d", "--debug")):
cmd_line_option_list["debug"] = True
elif (opt in ("-v", "--verbose")):
cmd_line_option_list["verbose"] = True
elif (opt in ("--data")):
cmd_line_option_list["data"] = ast.literal_eval(val)
return cmd_line_option_list
def set_logging(cmd_options):
"""
Setup logging and format output for console and syslog
Syslog is using the KERN facility
"""
log = logging.getLogger("%s" % (LOG_NAME))
log_level = logging.INFO
log_level_console = logging.WARNING
if cmd_options['verbose'] == True:
log_level_console = logging.INFO
if cmd_options['debug'] == True:
log_level_console = logging.DEBUG
log_level = logging.DEBUG
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
sys_formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
console_log = logging.StreamHandler()
console_log.setLevel(log_level_console)
console_log.setFormatter(formatter)
syslog_hndlr = SysLogHandler(
address=cmd_options['syslog'],
facility=SysLogHandler.LOG_KERN
)
syslog_hndlr.setFormatter(sys_formatter)
log.setLevel(log_level)
log.addHandler(console_log)
log.addHandler(syslog_hndlr)
return log
def _generate_certs():
cmd = []
cmd.append("%s/cert_utils/create_cert.sh" % (SCRIPT_PATH))
log.debug("Generating certs ...")
output_raw = subprocess.check_output(cmd, shell=False,
stderr=subprocess.STDOUT)
output_str = output_raw.decode("utf-8")
output = output_str.strip().split("\n")
log.debug(output)
return (output[-2], output[-1])
def _start_gns3server():
cmd = []
cmd.append("gns3server")
log.debug("Starting gns3server ...")
subprocess.Popen(cmd, shell=False)
def main():
global log
options = parse_cmd_line(sys.argv)
log = set_logging(options)
def _shutdown(signalnum=None, frame=None):
"""
Handles the SIGINT and SIGTERM event, inside of main so it has access to
the log vars.
"""
log.info("Received shutdown signal")
sys.exit(0)
# Setup signal to catch Control-C / SIGINT and SIGTERM
signal.signal(signal.SIGINT, _shutdown)
signal.signal(signal.SIGTERM, _shutdown)
client_data = {}
config = Config.instance()
cfg = config.list_cloud_config_file()
cfg_path = os.path.dirname(cfg)
try:
os.makedirs(cfg_path)
except FileExistsError:
pass
(server_key, server_crt ) = _generate_certs()
cloud_config = configparser.ConfigParser()
cloud_config['CLOUD_SERVER'] = {}
if options['data']:
cloud_config['CLOUD_SERVER'] = options['data']
cloud_config['CLOUD_SERVER']['SSL_KEY'] = server_key
cloud_config['CLOUD_SERVER']['SSL_CRT'] = server_crt
cloud_config['CLOUD_SERVER']['SSL_ENABLED'] = 'yes'
cloud_config['CLOUD_SERVER']['WEB_USERNAME'] = str(uuid.uuid4()).upper()[0:8]
cloud_config['CLOUD_SERVER']['WEB_PASSWORD'] = str(uuid.uuid4()).upper()[0:8]
with open(cfg, 'w') as cloud_config_file:
cloud_config.write(cloud_config_file)
cloud_config_file.close()
_start_gns3server()
with open(server_crt, 'r') as cert_file:
cert_data = cert_file.readlines()
cert_file.close()
client_data['SSL_CRT_FILE'] = server_crt
client_data['SSL_CRT'] = cert_data
print(client_data)
if __name__ == "__main__":
result = main()
sys.exit(result)

View File

@ -5,4 +5,5 @@ jsonschema
pycurl pycurl
python-dateutil python-dateutil
apache-libcloud apache-libcloud
requests