1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-24 17:28:08 +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): 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 # a new process start when calling IModule
IModule.__init__(self, name, *args, **kwargs) IModule.__init__(self, name, *args, **kwargs)
self._qemu_instances = {} self._qemu_instances = {}
@ -173,13 +153,6 @@ class Qemu(IModule):
self.send_param_error() self.send_param_error()
return 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: if "working_dir" in request:
new_working_dir = request["working_dir"] new_working_dir = request["working_dir"]
log.info("this server is local with working directory path to {}".format(new_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: try:
qemu_instance = QemuVM(name, qemu_instance = QemuVM(name,
qemu_path, qemu_path,
self._qemu_img_path,
self._working_dir, self._working_dir,
self._host, self._host,
qemu_id, qemu_id,

View File

@ -23,6 +23,7 @@ import os
import shutil import shutil
import random import random
import subprocess import subprocess
import shlex
from .qemu_error import QemuError from .qemu_error import QemuError
from .adapters.ethernet_adapter import EthernetAdapter from .adapters.ethernet_adapter import EthernetAdapter
@ -39,7 +40,6 @@ class QemuVM(object):
:param name: name of this QEMU VM :param name: name of this QEMU VM
:param qemu_path: path to the QEMU binary :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 working_dir: path to a working directory
:param host: host/address to bind for console and UDP connections :param host: host/address to bind for console and UDP connections
:param qemu_id: QEMU VM instance ID :param qemu_id: QEMU VM instance ID
@ -54,7 +54,6 @@ class QemuVM(object):
def __init__(self, def __init__(self,
name, name,
qemu_path, qemu_path,
qemu_img_path,
working_dir, working_dir,
host="127.0.0.1", host="127.0.0.1",
qemu_id=None, qemu_id=None,
@ -85,18 +84,21 @@ class QemuVM(object):
self._started = False self._started = False
self._process = None self._process = None
self._stdout_file = "" self._stdout_file = ""
self._qemu_img_path = qemu_img_path
self._console_start_port_range = console_start_port_range self._console_start_port_range = console_start_port_range
self._console_end_port_range = console_end_port_range self._console_end_port_range = console_end_port_range
# QEMU settings # QEMU settings
self._qemu_path = qemu_path self._qemu_path = qemu_path
self._disk_image = "" self._hda_disk_image = ""
self._hdb_disk_image = ""
self._options = "" self._options = ""
self._ram = 256 self._ram = 256
self._console = console self._console = console
self._ethernet_adapters = [] self._ethernet_adapters = []
self._adapter_type = "e1000" 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)) 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_defaults = {"name": self._name,
"qemu_path": self._qemu_path, "qemu_path": self._qemu_path,
"ram": self._ram, "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, "options": self._options,
"adapters": self.adapters, "adapters": self.adapters,
"adapter_type": self._adapter_type, "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 return qemu_defaults
@ -306,50 +312,51 @@ class QemuVM(object):
self._qemu_path = qemu_path self._qemu_path = qemu_path
@property @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 @hda_disk_image.setter
def qemu_img_path(self, qemu_img_path): 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, 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, id=self._id,
qemu_img_path=qemu_img_path)) disk_image=hda_disk_image))
self._qemu_img_path = qemu_img_path self._hda_disk_image = hda_disk_image
@property @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 @hdb_disk_image.setter
def disk_image(self, disk_image): 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, 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, id=self._id,
disk_image=disk_image)) disk_image=hdb_disk_image))
self._disk_image = disk_image self._hdb_disk_image = hdb_disk_image
@property @property
def adapters(self): def adapters(self):
@ -401,6 +408,121 @@ class QemuVM(object):
id=self._id, id=self._id,
adapter_type=adapter_type)) 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): def start(self):
""" """
Starts this QEMU VM. 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): 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)) raise QemuError("QEMU binary '{}' is not accessible".format(self._qemu_path))
#TODO: check binary image is valid?
self._command = self._build_command() self._command = self._build_command()
try: try:
log.info("starting QEMU: {}".format(self._command)) log.info("starting QEMU: {}".format(self._command))
self._stdout_file = os.path.join(self._working_dir, "qemu.log") self._stdout_file = os.path.join(self._working_dir, "qemu.log")
@ -567,36 +687,81 @@ class QemuVM(object):
def _disk_options(self): def _disk_options(self):
hda_disk = os.path.join(self._working_dir, "hda.disk") options = []
if not os.path.exists(hda_disk): qemu_img_path = ""
try: qemu_path_dir = os.path.dirname(self._qemu_path)
retcode = subprocess.call([self._qemu_img_path, "create", "-o", try:
"backing_file={}".format(self._disk_image), for f in os.listdir(qemu_path_dir):
"-f", "qcow2", hda_disk]) if f.startswith("qemu-img"):
log.info("{} returned with {}".format(self._qemu_img_path, retcode)) qemu_img_path = os.path.join(qemu_path_dir, f)
except OSError as e: except OSError as e:
raise QemuError("Could not create disk image {}".format(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): def _network_options(self):
network_options = [] network_options = []
adapter_id = 0 adapter_id = 0
for adapter in self._ethernet_adapters: 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) nio = adapter.get_nio(0)
if nio: if nio and isinstance(nio, NIO_UDP):
#TODO: let users specific the base mac address network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
mac = "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_id) nio.rhost,
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_id)]) nio.rport,
if isinstance(nio, NIO_UDP): self._host,
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id, nio.lport)])
nio.rhost,
nio.rport,
self._host,
nio.lport)])
else: else:
network_options.extend(["-device", "{}".format(self._adapter_type)]) network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_id)])
adapter_id += 1 adapter_id += 1
return network_options return network_options
@ -611,6 +776,10 @@ class QemuVM(object):
command.extend(["-name", self._name]) command.extend(["-name", self._name])
command.extend(["-m", str(self._ram)]) command.extend(["-m", str(self._ram)])
command.extend(self._disk_options()) command.extend(self._disk_options())
command.extend(self._linux_boot_options())
command.extend(self._serial_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()) command.extend(self._network_options())
return command return command

View File

@ -79,8 +79,13 @@ QEMU_UPDATE_SCHEMA = {
"type": "string", "type": "string",
"minLength": 1, "minLength": 1,
}, },
"disk_image": { "hda_disk_image": {
"description": "QEMU disk image path", "description": "QEMU hda disk image path",
"type": "string",
"minLength": 1,
},
"hdb_disk_image": {
"description": "QEMU hdb disk image path",
"type": "string", "type": "string",
"minLength": 1, "minLength": 1,
}, },
@ -105,6 +110,21 @@ QEMU_UPDATE_SCHEMA = {
"maximum": 65535, "maximum": 65535,
"type": "integer" "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": { "options": {
"description": "additional QEMU options", "description": "additional QEMU options",
"type": "string", "type": "string",

View File

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