Some spring cleaning.

This commit is contained in:
grossmj
2015-04-08 11:17:34 -06:00
parent 1c4202187a
commit 7d7972afb3
62 changed files with 459 additions and 363 deletions

View File

@ -16,7 +16,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
QEMU VM instance.
QEMU VM management (creates command line, processes, files etc.) in
order to run a QEMU VM.
"""
import sys
@ -44,26 +45,18 @@ class QemuVM(BaseVM):
"""
QEMU VM implementation.
:param name: name of this Qemu vm
:param vm_id: IOU instance identifier
:param name: Qemu VM name
:param vm_id: Qemu VM identifier
:param project: Project instance
:param manager: parent VM Manager
:param manager: Manager instance
:param console: TCP console port
:param qemu_path: path to the QEMU binary
:param qemu_id: QEMU VM instance ID
:param console: TCP console port
"""
def __init__(self,
name,
vm_id,
project,
manager,
qemu_path=None,
console=None):
def __init__(self, name, vm_id, project, manager, qemu_path=None, console=None):
super().__init__(name, vm_id, project, manager, console=console)
server_config = manager.config.get_section_config("Server")
self._host = server_config.get("host", "127.0.0.1")
self._monitor_host = server_config.get("monitor_host", "127.0.0.1")
@ -74,7 +67,7 @@ class QemuVM(BaseVM):
self._monitor = None
self._stdout_file = ""
# QEMU settings
# QEMU VM settings
self._qemu_path = qemu_path
self._hda_disk_image = ""
self._hdb_disk_image = ""
@ -92,8 +85,7 @@ class QemuVM(BaseVM):
self._process_priority = "low"
self.adapters = 1 # creates 1 adapter by default
log.info("QEMU VM {name} [id={id}] has been created".format(name=self._name,
id=self._id))
log.info('QEMU VM "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
@property
def monitor(self):
@ -134,9 +126,9 @@ class QemuVM(BaseVM):
raise QemuError("QEMU binary '{}' is not executable".format(qemu_path))
self._qemu_path = qemu_path
log.info("QEMU VM {name} [id={id}] has set the QEMU path to {qemu_path}".format(name=self._name,
id=self._id,
qemu_path=qemu_path))
log.info('QEMU VM "{name}" [{id}] has set the QEMU path to {qemu_path}'.format(name=self._name,
id=self._id,
qemu_path=qemu_path))
@property
def hda_disk_image(self):
@ -160,9 +152,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
hda_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hda_disk_image)
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))
log.info('QEMU VM "{name}" [{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
@ -187,9 +179,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
hdb_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdb_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))
log.info('QEMU VM "{name}" [{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
@ -214,9 +206,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
hdc_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdc_disk_image)
log.info("QEMU VM {name} [id={id}] has set the QEMU hdc disk image path to {disk_image}".format(name=self._name,
id=self._id,
disk_image=hdc_disk_image))
log.info('QEMU VM "{name}" [{id}] has set the QEMU hdc disk image path to {disk_image}'.format(name=self._name,
id=self._id,
disk_image=hdc_disk_image))
self._hdc_disk_image = hdc_disk_image
@property
@ -241,15 +233,15 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
hdd_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdd_disk_image)
log.info("QEMU VM {name} [id={id}] has set the QEMU hdd disk image path to {disk_image}".format(name=self._name,
id=self._id,
disk_image=hdd_disk_image))
log.info('QEMU VM "{name}" [{id}] has set the QEMU hdd disk image path to {disk_image}'.format(name=self._name,
id=self._id,
disk_image=hdd_disk_image))
self._hdd_disk_image = hdd_disk_image
@property
def adapters(self):
"""
Returns the number of Ethernet adapters for this QEMU VM instance.
Returns the number of Ethernet adapters for this QEMU VM.
:returns: number of adapters
"""
@ -259,23 +251,23 @@ class QemuVM(BaseVM):
@adapters.setter
def adapters(self, adapters):
"""
Sets the number of Ethernet adapters for this QEMU VM instance.
Sets the number of Ethernet adapters for this QEMU VM.
:param adapters: number of adapters
"""
self._ethernet_adapters.clear()
for adapter_id in range(0, adapters):
for adapter_number in range(0, adapters):
self._ethernet_adapters.append(EthernetAdapter())
log.info("QEMU VM {name} [id={id}]: number of Ethernet adapters changed to {adapters}".format(name=self._name,
id=self._id,
adapters=adapters))
log.info('QEMU VM "{name}" [{id}]: number of Ethernet adapters changed to {adapters}'.format(name=self._name,
id=self._id,
adapters=adapters))
@property
def adapter_type(self):
"""
Returns the adapter type for this QEMU VM instance.
Returns the adapter type for this QEMU VM.
:returns: adapter type (string)
"""
@ -285,16 +277,16 @@ class QemuVM(BaseVM):
@adapter_type.setter
def adapter_type(self, adapter_type):
"""
Sets the adapter type for this QEMU VM instance.
Sets the adapter type for this QEMU VM.
:param adapter_type: adapter type (string)
"""
self._adapter_type = adapter_type
log.info("QEMU VM {name} [id={id}]: adapter type changed to {adapter_type}".format(name=self._name,
id=self._id,
adapter_type=adapter_type))
log.info('QEMU VM "{name}" [{id}]: adapter type changed to {adapter_type}'.format(name=self._name,
id=self._id,
adapter_type=adapter_type))
@property
def legacy_networking(self):
@ -315,9 +307,9 @@ class QemuVM(BaseVM):
"""
if legacy_networking:
log.info("QEMU VM {name} [id={id}] has enabled legacy networking".format(name=self._name, id=self._id))
log.info('QEMU VM "{name}" [{id}] has enabled legacy networking'.format(name=self._name, id=self._id))
else:
log.info("QEMU VM {name} [id={id}] has disabled legacy networking".format(name=self._name, id=self._id))
log.info('QEMU VM "{name}" [{id}] has disabled legacy networking'.format(name=self._name, id=self._id))
self._legacy_networking = legacy_networking
@property
@ -338,9 +330,9 @@ class QemuVM(BaseVM):
:param cpu_throttling: integer
"""
log.info("QEMU VM {name} [id={id}] has set the percentage of CPU allowed to {cpu}".format(name=self._name,
id=self._id,
cpu=cpu_throttling))
log.info('QEMU VM "{name}" [{id}] has set the percentage of CPU allowed to {cpu}'.format(name=self._name,
id=self._id,
cpu=cpu_throttling))
self._cpu_throttling = cpu_throttling
self._stop_cpulimit()
if cpu_throttling:
@ -364,9 +356,9 @@ class QemuVM(BaseVM):
:param process_priority: string
"""
log.info("QEMU VM {name} [id={id}] has set the process priority to {priority}".format(name=self._name,
id=self._id,
priority=process_priority))
log.info('QEMU VM "{name}" [{id}] has set the process priority to {priority}'.format(name=self._name,
id=self._id,
priority=process_priority))
self._process_priority = process_priority
@property
@ -387,9 +379,7 @@ class QemuVM(BaseVM):
: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))
log.info('QEMU VM "{name}" [{id}] has set the RAM to {ram}'.format(name=self._name, id=self._id, ram=ram))
self._ram = ram
@property
@ -410,9 +400,9 @@ class QemuVM(BaseVM):
: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))
log.info('QEMU VM "{name}" [{id}] has set the QEMU options to {options}'.format(name=self._name,
id=self._id,
options=options))
self._options = options
@property
@ -437,9 +427,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
initrd = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", initrd)
log.info("QEMU VM {name} [id={id}] has set the QEMU initrd path to {initrd}".format(name=self._name,
id=self._id,
initrd=initrd))
log.info('QEMU VM "{name}" [{id}] has set the QEMU initrd path to {initrd}'.format(name=self._name,
id=self._id,
initrd=initrd))
self._initrd = initrd
@property
@ -464,9 +454,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
kernel_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", kernel_image)
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))
log.info('QEMU VM "{name}" [{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
@ -487,9 +477,9 @@ class QemuVM(BaseVM):
: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))
log.info('QEMU VM "{name}" [{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
@asyncio.coroutine
@ -538,7 +528,7 @@ class QemuVM(BaseVM):
process = yield from asyncio.create_subprocess_exec('renice', '-n', str(priority), '-p', str(self._process.pid))
yield from process.wait()
except (OSError, subprocess.SubprocessError) as e:
log.error("could not change process priority for QEMU VM {}: {}".format(self._name, e))
log.error('Could not change process priority for QEMU VM "{}": {}'.format(self._name, e))
def _stop_cpulimit(self):
"""
@ -550,7 +540,7 @@ class QemuVM(BaseVM):
try:
self._process.wait(3)
except subprocess.TimeoutExpired:
log.error("could not kill cpulimit process {}".format(self._cpulimit_process.pid))
log.error("Could not kill cpulimit process {}".format(self._cpulimit_process.pid))
def _set_cpu_throttling(self):
"""
@ -579,7 +569,6 @@ class QemuVM(BaseVM):
"""
if self.is_running():
# resume the VM if it is paused
yield from self.resume()
return
@ -597,7 +586,7 @@ class QemuVM(BaseVM):
self._command = yield from self._build_command()
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")
log.info("logging to {}".format(self._stdout_file))
with open(self._stdout_file, "w") as fd:
@ -605,12 +594,12 @@ class QemuVM(BaseVM):
stdout=fd,
stderr=subprocess.STDOUT,
cwd=self.working_dir)
log.info("QEMU VM instance {} started PID={}".format(self._id, self._process.pid))
log.info('QEMU VM "{}" started PID={}'.format(self._name, self._process.pid))
self._started = True
except (OSError, subprocess.SubprocessError) as e:
stdout = self.read_stdout()
log.error("could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
raise QemuError("could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
log.error("Could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
raise QemuError("Could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
self._set_process_priority()
if self._cpu_throttling:
@ -624,15 +613,14 @@ class QemuVM(BaseVM):
# stop the QEMU process
if self.is_running():
log.info("stopping QEMU VM instance {} PID={}".format(self._id, self._process.pid))
log.info('Stopping QEMU VM "{}" PID={}'.format(self._name, self._process.pid))
try:
self._process.terminate()
self._process.wait()
except subprocess.TimeoutExpired:
self._process.kill()
if self._process.returncode is None:
log.warn("QEMU VM instance {} PID={} is still running".format(self._id,
self._process.pid))
log.warn('QEMU VM "{}" PID={} is still running'.format(self._name, self._process.pid))
self._process = None
self._started = False
self._stop_cpulimit()
@ -643,9 +631,9 @@ class QemuVM(BaseVM):
Executes a command with QEMU monitor when this VM is running.
:param command: QEMU monitor command (e.g. info status, stop etc.)
:params expected: An array with the string attended (Default None)
:param expected: An array of expected strings
:returns: result of the command (Match object or None)
:returns: result of the command (matched object or None)
"""
result = None
@ -680,6 +668,9 @@ class QemuVM(BaseVM):
@asyncio.coroutine
def close(self):
"""
Closes this QEMU VM.
"""
log.debug('QEMU VM "{name}" [{id}] is closing'.format(name=self._name, id=self._id))
yield from self.stop()
@ -690,7 +681,7 @@ class QemuVM(BaseVM):
@asyncio.coroutine
def _get_vm_status(self):
"""
Returns this VM suspend status (running|paused)
Returns this VM suspend status.
Status are extracted from:
https://github.com/qemu/qemu/blob/master/qapi-schema.json#L152
@ -749,27 +740,27 @@ class QemuVM(BaseVM):
log.info("QEMU VM is not paused to be resumed, current status is {}".format(vm_status))
@asyncio.coroutine
def adapter_add_nio_binding(self, adapter_id, nio):
def adapter_add_nio_binding(self, adapter_number, nio):
"""
Adds a port NIO binding.
:param adapter_id: adapter ID
:param adapter_number: adapter number
:param nio: NIO instance to add to the adapter
"""
try:
adapter = self._ethernet_adapters[adapter_id]
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
raise QemuError("Adapter {adapter_id} doesn't exist on QEMU VM {name}".format(name=self._name,
adapter_id=adapter_id))
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
adapter_number=adapter_number))
if self.is_running():
# dynamically configure an UDP tunnel on the QEMU VM adapter
if nio and isinstance(nio, NIOUDP):
if self._legacy_networking:
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_id, adapter_id))
yield from self._control_vm("host_net_add udp vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_id,
adapter_id,
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_number, adapter_number))
yield from self._control_vm("host_net_add udp vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number,
adapter_number,
nio.lport,
nio.rport,
nio.rhost))
@ -778,48 +769,48 @@ class QemuVM(BaseVM):
# Apparently there is a bug in Qemu...
# netdev_add [user|tap|socket|hubport|netmap],id=str[,prop=value][,...] -- add host network device
# netdev_del id -- remove host network device
yield from self._control_vm("netdev_del gns3-{}".format(adapter_id))
yield from self._control_vm("netdev_add socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
yield from self._control_vm("netdev_del gns3-{}".format(adapter_number))
yield from self._control_vm("netdev_add socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
nio.rhost,
nio.rport,
self._host,
nio.lport))
adapter.add_nio(0, nio)
log.info("QEMU VM {name} [id={id}]: {nio} added to adapter {adapter_id}".format(name=self._name,
id=self._id,
nio=nio,
adapter_id=adapter_id))
log.info('QEMU VM "{name}" [{id}]: {nio} added to adapter {adapter_number}'.format(name=self._name,
id=self._id,
nio=nio,
adapter_number=adapter_number))
@asyncio.coroutine
def adapter_remove_nio_binding(self, adapter_id):
def adapter_remove_nio_binding(self, adapter_number):
"""
Removes a port NIO binding.
:param adapter_id: adapter ID
:param adapter_number: adapter number
:returns: NIO instance
"""
try:
adapter = self._ethernet_adapters[adapter_id]
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
raise QemuError("Adapter {adapter_id} doesn't exist on QEMU VM {name}".format(name=self._name,
adapter_id=adapter_id))
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
adapter_number=adapter_number))
if self.is_running():
# dynamically disable the QEMU VM adapter
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_id, adapter_id))
yield from self._control_vm("host_net_add user vlan={},name=gns3-{}".format(adapter_id, adapter_id))
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_number, adapter_number))
yield from self._control_vm("host_net_add user vlan={},name=gns3-{}".format(adapter_number, adapter_number))
nio = adapter.get_nio(0)
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
adapter.remove_nio(0)
log.info("QEMU VM {name} [id={id}]: {nio} removed from adapter {adapter_id}".format(name=self._name,
id=self._id,
nio=nio,
adapter_id=adapter_id))
log.info('QEMU VM "{name}" [{id}]: {nio} removed from adapter {adapter_number}'.format(name=self._name,
id=self._id,
nio=nio,
adapter_number=adapter_number))
return nio
@property
@ -844,7 +835,7 @@ class QemuVM(BaseVM):
with open(self._stdout_file, errors="replace") as file:
output = file.read()
except OSError as e:
log.warn("could not read {}: {}".format(self._stdout_file, e))
log.warn("Could not read {}: {}".format(self._stdout_file, e))
return output
def is_running(self):
@ -1004,47 +995,48 @@ class QemuVM(BaseVM):
return options
def _get_random_mac(self, adapter_id):
def _get_random_mac(self, adapter_number):
# TODO: let users specify a base mac address
return "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_id)
return "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_number)
def _network_options(self):
network_options = []
adapter_id = 0
adapter_number = 0
for adapter in self._ethernet_adapters:
mac = self._get_random_mac(adapter_id)
mac = self._get_random_mac(adapter_number)
if self._legacy_networking:
network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_id, mac, self._adapter_type)])
network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_number, mac, self._adapter_type)])
else:
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_id)])
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_number)])
nio = adapter.get_nio(0)
if nio and isinstance(nio, NIOUDP):
if self._legacy_networking:
network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_id,
adapter_id,
network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number,
adapter_number,
nio.lport,
nio.rport,
nio.rhost)])
else:
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
nio.rhost,
nio.rport,
self._host,
nio.lport)])
else:
if self._legacy_networking:
network_options.extend(["-net", "user,vlan={},name=gns3-{}".format(adapter_id, adapter_id)])
network_options.extend(["-net", "user,vlan={},name=gns3-{}".format(adapter_number, adapter_number)])
else:
network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_id)])
adapter_id += 1
network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_number)])
adapter_number += 1
return network_options
def _graphic(self):
"""
Add the correct graphic options depending of the OS
Adds the correct graphic options depending of the OS
"""
if sys.platform.startswith("win"):
return []
if len(os.environ.get("DISPLAY", "")) > 0:
@ -1086,7 +1078,7 @@ class QemuVM(BaseVM):
"""
if disk_image:
# return the relative path if disks images are in the images_path directory
# return the relative path if the disk image is in the images_path directory
server_config = self.manager.config.get_section_config("Server")
relative_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", disk_image)
if os.path.exists(relative_image):