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

Qemu integration stage 2, support for ASA and IDS.

This commit is contained in:
grossmj 2014-09-22 21:24:55 -06:00
parent d1715baae1
commit aca9e0de56
4 changed files with 245 additions and 84 deletions

View File

@ -67,26 +67,6 @@ class Qemu(IModule):
def __init__(self, name, *args, **kwargs):
# get the qemu-img location
config = Config.instance()
qemu_config = config.get_section_config(name.upper())
self._qemu_img_path = qemu_config.get("qemu_img_path")
if not self._qemu_img_path or not os.path.isfile(self._qemu_img_path):
paths = [os.getcwd()] + os.environ["PATH"].split(":")
# look for qemu-img in the current working directory and $PATH
for path in paths:
try:
if "qemu-img" in os.listdir(path) and os.access(os.path.join(path, "qemu-img"), os.X_OK):
self._qemu_img_path = os.path.join(path, "qemu-img")
break
except OSError:
continue
if not self._qemu_img_path:
log.warning("qemu-img couldn't be found!")
elif not os.access(self._qemu_img_path, os.X_OK):
log.warning("qemu-img is not executable")
# a new process start when calling IModule
IModule.__init__(self, name, *args, **kwargs)
self._qemu_instances = {}
@ -173,13 +153,6 @@ class Qemu(IModule):
self.send_param_error()
return
if "qemu_img_path" in request and request["qemu_img_path"]:
self._qemu_img_path = request["qemu_img_path"]
log.info("QEMU image utility path set to {}".format(self._qemu_img_path))
for qemu_id in self._qemu_instances:
qemu_instance = self._qemu_instances[qemu_id]
qemu_instance.qemu_img_path = self._qemu_img_path
if "working_dir" in request:
new_working_dir = request["working_dir"]
log.info("this server is local with working directory path to {}".format(new_working_dir))
@ -245,7 +218,6 @@ class Qemu(IModule):
try:
qemu_instance = QemuVM(name,
qemu_path,
self._qemu_img_path,
self._working_dir,
self._host,
qemu_id,

View File

@ -23,6 +23,7 @@ import os
import shutil
import random
import subprocess
import shlex
from .qemu_error import QemuError
from .adapters.ethernet_adapter import EthernetAdapter
@ -39,7 +40,6 @@ class QemuVM(object):
:param name: name of this QEMU VM
:param qemu_path: path to the QEMU binary
:param qemu_img_path: path to the QEMU IMG binary
:param working_dir: path to a working directory
:param host: host/address to bind for console and UDP connections
:param qemu_id: QEMU VM instance ID
@ -54,7 +54,6 @@ class QemuVM(object):
def __init__(self,
name,
qemu_path,
qemu_img_path,
working_dir,
host="127.0.0.1",
qemu_id=None,
@ -85,18 +84,21 @@ class QemuVM(object):
self._started = False
self._process = None
self._stdout_file = ""
self._qemu_img_path = qemu_img_path
self._console_start_port_range = console_start_port_range
self._console_end_port_range = console_end_port_range
# QEMU settings
self._qemu_path = qemu_path
self._disk_image = ""
self._hda_disk_image = ""
self._hdb_disk_image = ""
self._options = ""
self._ram = 256
self._console = console
self._ethernet_adapters = []
self._adapter_type = "e1000"
self._initrd = ""
self._kernel_image = ""
self._kernel_command_line = ""
working_dir_path = os.path.join(working_dir, "qemu", "vm-{}".format(self._id))
@ -134,11 +136,15 @@ class QemuVM(object):
qemu_defaults = {"name": self._name,
"qemu_path": self._qemu_path,
"ram": self._ram,
"disk_image": self._disk_image,
"hda_disk_image": self._hda_disk_image,
"hdb_disk_image": self._hdb_disk_image,
"options": self._options,
"adapters": self.adapters,
"adapter_type": self._adapter_type,
"console": self._console}
"console": self._console,
"initrd": self._initrd,
"kernel_image": self._kernel_image,
"kernel_command_line": self._kernel_command_line}
return qemu_defaults
@ -306,50 +312,51 @@ class QemuVM(object):
self._qemu_path = qemu_path
@property
def qemu_img_path(self):
def hda_disk_image(self):
"""
Returns the QEMU IMG binary path for this QEMU VM.
Returns the hda disk image path for this QEMU VM.
:returns: QEMU IMG path
:returns: QEMU hda disk image path
"""
return self._qemu_img_path
return self._hda_disk_image
@qemu_img_path.setter
def qemu_img_path(self, qemu_img_path):
@hda_disk_image.setter
def hda_disk_image(self, hda_disk_image):
"""
Sets the QEMU IMG binary path this QEMU VM.
Sets the hda disk image for this QEMU VM.
:param qemu_img_path: QEMU IMG path
:param hda_disk_image: QEMU hda disk image path
"""
log.info("QEMU VM {name} [id={id}] has set the QEMU IMG path to {qemu_img_path}".format(name=self._name,
id=self._id,
qemu_img_path=qemu_img_path))
self._qemu_img_path = qemu_img_path
log.info("QEMU VM {name} [id={id}] has set the QEMU hda disk image path to {disk_image}".format(name=self._name,
id=self._id,
disk_image=hda_disk_image))
self._hda_disk_image = hda_disk_image
@property
def disk_image(self):
def hdb_disk_image(self):
"""
Returns the disk image path for this QEMU VM.
Returns the hdb disk image path for this QEMU VM.
:returns: QEMU disk image path
:returns: QEMU hdb disk image path
"""
return self._disk_image
return self._hdb_disk_image
@disk_image.setter
def disk_image(self, disk_image):
@hdb_disk_image.setter
def hdb_disk_image(self, hdb_disk_image):
"""
Sets the disk image for this QEMU VM.
Sets the hdb disk image for this QEMU VM.
:param disk_image: QEMU disk image path
:param hdb_disk_image: QEMU hdb disk image path
"""
log.info("QEMU VM {name} [id={id}] has set the QEMU disk image path to {disk_image}".format(name=self._name,
id=self._id,
disk_image=disk_image))
self._disk_image = disk_image
log.info("QEMU VM {name} [id={id}] has set the QEMU hdb disk image path to {disk_image}".format(name=self._name,
id=self._id,
disk_image=hdb_disk_image))
self._hdb_disk_image = hdb_disk_image
@property
def adapters(self):
@ -401,6 +408,121 @@ class QemuVM(object):
id=self._id,
adapter_type=adapter_type))
@property
def ram(self):
"""
Returns the RAM amount for this QEMU VM.
:returns: RAM amount in MB
"""
return self._ram
@ram.setter
def ram(self, ram):
"""
Sets the amount of RAM for this QEMU VM.
:param ram: RAM amount in MB
"""
log.info("QEMU VM {name} [id={id}] has set the RAM to {ram}".format(name=self._name,
id=self._id,
ram=ram))
self._ram = ram
@property
def options(self):
"""
Returns the options for this QEMU VM.
:returns: QEMU options
"""
return self._options
@options.setter
def options(self, options):
"""
Sets the options for this QEMU VM.
:param options: QEMU options
"""
log.info("QEMU VM {name} [id={id}] has set the QEMU options to {options}".format(name=self._name,
id=self._id,
options=options))
self._options = options
@property
def initrd(self):
"""
Returns the initrd path for this QEMU VM.
:returns: QEMU initrd path
"""
return self._initrd
@initrd.setter
def initrd(self, initrd):
"""
Sets the initrd path for this QEMU VM.
:param initrd: QEMU initrd path
"""
log.info("QEMU VM {name} [id={id}] has set the QEMU initrd path to {initrd}".format(name=self._name,
id=self._id,
initrd=initrd))
self._initrd = initrd
@property
def kernel_image(self):
"""
Returns the kernel image path for this QEMU VM.
:returns: QEMU kernel image path
"""
return self._kernel_image
@kernel_image.setter
def kernel_image(self, kernel_image):
"""
Sets the kernel image path for this QEMU VM.
:param kernel_image: QEMU kernel image path
"""
log.info("QEMU VM {name} [id={id}] has set the QEMU kernel image path to {kernel_image}".format(name=self._name,
id=self._id,
kernel_image=kernel_image))
self._kernel_image = kernel_image
@property
def kernel_command_line(self):
"""
Returns the kernel command line for this QEMU VM.
:returns: QEMU kernel command line
"""
return self._kernel_command_line
@kernel_command_line.setter
def kernel_command_line(self, kernel_command_line):
"""
Sets the kernel command line for this QEMU VM.
:param kernel_command_line: QEMU kernel command line
"""
log.info("QEMU VM {name} [id={id}] has set the QEMU kernel command line to {kernel_command_line}".format(name=self._name,
id=self._id,
kernel_command_line=kernel_command_line))
self._kernel_command_line = kernel_command_line
def start(self):
"""
Starts this QEMU VM.
@ -411,9 +533,7 @@ class QemuVM(object):
if not os.path.isfile(self._qemu_path) or not os.path.exists(self._qemu_path):
raise QemuError("QEMU binary '{}' is not accessible".format(self._qemu_path))
#TODO: check binary image is valid?
self._command = self._build_command()
try:
log.info("starting QEMU: {}".format(self._command))
self._stdout_file = os.path.join(self._working_dir, "qemu.log")
@ -567,36 +687,81 @@ class QemuVM(object):
def _disk_options(self):
hda_disk = os.path.join(self._working_dir, "hda.disk")
if not os.path.exists(hda_disk):
try:
retcode = subprocess.call([self._qemu_img_path, "create", "-o",
"backing_file={}".format(self._disk_image),
"-f", "qcow2", hda_disk])
log.info("{} returned with {}".format(self._qemu_img_path, retcode))
except OSError as e:
raise QemuError("Could not create disk image {}".format(e))
options = []
qemu_img_path = ""
qemu_path_dir = os.path.dirname(self._qemu_path)
try:
for f in os.listdir(qemu_path_dir):
if f.startswith("qemu-img"):
qemu_img_path = os.path.join(qemu_path_dir, f)
except OSError as e:
raise QemuError("Error while looking for qemu-img in {}: {}".format(qemu_path_dir, e))
return ["-hda", hda_disk]
if not qemu_img_path:
raise QemuError("Could not find qemu-img in {}".format(qemu_path_dir))
try:
if self._hda_disk_image:
hda_disk = os.path.join(self._working_dir, "hda_disk.qcow2")
if not os.path.exists(hda_disk):
retcode = subprocess.call([qemu_img_path, "create", "-o",
"backing_file={}".format(self._hda_disk_image),
"-f", "qcow2", hda_disk])
log.info("{} returned with {}".format(qemu_img_path, retcode))
else:
# create a "FLASH" with 256MB if no disk image has been specified
hda_disk = os.path.join(self._working_dir, "flash.qcow2")
if not os.path.exists(hda_disk):
retcode = subprocess.call([qemu_img_path, "create", "-f", "qcow2", hda_disk, "256M"])
log.info("{} returned with {}".format(qemu_img_path, retcode))
except OSError as e:
raise QemuError("Could not create disk image {}".format(e))
options.extend(["-hda", hda_disk])
if self._hdb_disk_image:
hdb_disk = os.path.join(self._working_dir, "hdb_disk.qcow2")
if not os.path.exists(hdb_disk):
try:
retcode = subprocess.call([qemu_img_path, "create", "-o",
"backing_file={}".format(self._hdb_disk_image),
"-f", "qcow2", hdb_disk])
log.info("{} returned with {}".format(qemu_img_path, retcode))
except OSError as e:
raise QemuError("Could not create disk image {}".format(e))
options.extend(["-hdb", hdb_disk])
return options
def _linux_boot_options(self):
options = []
if self._initrd:
options.extend(["-initrd", self._initrd])
if self._kernel_image:
options.extend(["-kernel", self._kernel_image])
if self._kernel_command_line:
options.extend(["-append", self._kernel_command_line])
return options
def _network_options(self):
network_options = []
adapter_id = 0
for adapter in self._ethernet_adapters:
#TODO: let users specify a base mac address
mac = "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_id)
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_id)])
nio = adapter.get_nio(0)
if nio:
#TODO: let users specific the base mac address
mac = "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_id)
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_id)])
if isinstance(nio, NIO_UDP):
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
nio.rhost,
nio.rport,
self._host,
nio.lport)])
if nio and isinstance(nio, NIO_UDP):
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
nio.rhost,
nio.rport,
self._host,
nio.lport)])
else:
network_options.extend(["-device", "{}".format(self._adapter_type)])
network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_id)])
adapter_id += 1
return network_options
@ -611,6 +776,10 @@ class QemuVM(object):
command.extend(["-name", self._name])
command.extend(["-m", str(self._ram)])
command.extend(self._disk_options())
command.extend(self._linux_boot_options())
command.extend(self._serial_options())
additional_options = self._options.strip()
if additional_options:
command.extend(shlex.split(additional_options))
command.extend(self._network_options())
return command

View File

@ -79,8 +79,13 @@ QEMU_UPDATE_SCHEMA = {
"type": "string",
"minLength": 1,
},
"disk_image": {
"description": "QEMU disk image path",
"hda_disk_image": {
"description": "QEMU hda disk image path",
"type": "string",
"minLength": 1,
},
"hdb_disk_image": {
"description": "QEMU hdb disk image path",
"type": "string",
"minLength": 1,
},
@ -105,6 +110,21 @@ QEMU_UPDATE_SCHEMA = {
"maximum": 65535,
"type": "integer"
},
"initrd": {
"description": "QEMU initrd path",
"type": "string",
"minLength": 1,
},
"kernel_image": {
"description": "QEMU kernel image path",
"type": "string",
"minLength": 1,
},
"kernel_command_line": {
"description": "QEMU kernel command line",
"type": "string",
"minLength": 1,
},
"options": {
"description": "additional QEMU options",
"type": "string",

View File

@ -23,5 +23,5 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "1.0beta3.dev1"
__version__ = "1.0beta3.dev2"
__version_info__ = (1, 0, 0, -99)