Pārlūkot izejas kodu

scripts/qemu.py: introduce set_console() method

The set_console() method is intended to ease higher level use cases
that require a console device.

The amount of intelligence is limited on purpose, requiring either the
device type explicitly, or the existence of a machine (pattern)
definition.

Because of the console device type selection criteria (by machine
type), users should also be able to define that.  It'll then be used
for both '-machine' and for the console device type selection.

Users of the set_console() method will certainly be interested in
accessing the console device, and for that a console_socket property
has been added.

Signed-off-by: Cleber Rosa <crosa@redhat.com>
Message-Id: <20180530184156.15634-5-crosa@redhat.com>
Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Cleber Rosa 7 gadi atpakaļ
vecāks
revīzija
22dea9db2b
1 mainītis faili ar 96 papildinājumiem un 1 dzēšanām
  1. 96 1
      scripts/qemu.py

+ 96 - 1
scripts/qemu.py

@@ -17,19 +17,41 @@
 import os
 import os
 import subprocess
 import subprocess
 import qmp.qmp
 import qmp.qmp
+import re
 import shutil
 import shutil
+import socket
 import tempfile
 import tempfile
 
 
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
 
 
+#: Maps machine types to the preferred console device types
+CONSOLE_DEV_TYPES = {
+    r'^clipper$': 'isa-serial',
+    r'^malta': 'isa-serial',
+    r'^(pc.*|q35.*|isapc)$': 'isa-serial',
+    r'^(40p|powernv|prep)$': 'isa-serial',
+    r'^pseries.*': 'spapr-vty',
+    r'^s390-ccw-virtio.*': 'sclpconsole',
+    }
+
+
 class QEMUMachineError(Exception):
 class QEMUMachineError(Exception):
     """
     """
     Exception called when an error in QEMUMachine happens.
     Exception called when an error in QEMUMachine happens.
     """
     """
 
 
 
 
+class QEMUMachineAddDeviceError(QEMUMachineError):
+    """
+    Exception raised when a request to add a device can not be fulfilled
+
+    The failures are caused by limitations, lack of information or conflicting
+    requests on the QEMUMachine methods.  This exception does not represent
+    failures reported by the QEMU binary itself.
+    """
+
 class MonitorResponseError(qmp.qmp.QMPError):
 class MonitorResponseError(qmp.qmp.QMPError):
     '''
     '''
     Represents erroneous QMP monitor reply
     Represents erroneous QMP monitor reply
@@ -91,6 +113,10 @@ def __init__(self, binary, args=None, wrapper=None, name=None,
         self._test_dir = test_dir
         self._test_dir = test_dir
         self._temp_dir = None
         self._temp_dir = None
         self._launched = False
         self._launched = False
+        self._machine = None
+        self._console_device_type = None
+        self._console_address = None
+        self._console_socket = None
 
 
         # just in case logging wasn't configured by the main script:
         # just in case logging wasn't configured by the main script:
         logging.basicConfig()
         logging.basicConfig()
@@ -175,9 +201,19 @@ def _base_args(self):
                 self._monitor_address[1])
                 self._monitor_address[1])
         else:
         else:
             moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
             moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
-        return ['-chardev', moncdev,
+        args = ['-chardev', moncdev,
                 '-mon', 'chardev=mon,mode=control',
                 '-mon', 'chardev=mon,mode=control',
                 '-display', 'none', '-vga', 'none']
                 '-display', 'none', '-vga', 'none']
+        if self._machine is not None:
+            args.extend(['-machine', self._machine])
+        if self._console_device_type is not None:
+            self._console_address = os.path.join(self._temp_dir,
+                                                 self._name + "-console.sock")
+            chardev = ('socket,id=console,path=%s,server,nowait' %
+                       self._console_address)
+            device = '%s,chardev=console' % self._console_device_type
+            args.extend(['-chardev', chardev, '-device', device])
+        return args
 
 
     def _pre_launch(self):
     def _pre_launch(self):
         self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
         self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
@@ -202,6 +238,10 @@ def _post_shutdown(self):
 
 
         self._qemu_log_path = None
         self._qemu_log_path = None
 
 
+        if self._console_socket is not None:
+            self._console_socket.close()
+            self._console_socket = None
+
         if self._temp_dir is not None:
         if self._temp_dir is not None:
             shutil.rmtree(self._temp_dir)
             shutil.rmtree(self._temp_dir)
             self._temp_dir = None
             self._temp_dir = None
@@ -365,3 +405,58 @@ def add_args(self, *args):
         Adds to the list of extra arguments to be given to the QEMU binary
         Adds to the list of extra arguments to be given to the QEMU binary
         '''
         '''
         self._args.extend(args)
         self._args.extend(args)
+
+    def set_machine(self, machine_type):
+        '''
+        Sets the machine type
+
+        If set, the machine type will be added to the base arguments
+        of the resulting QEMU command line.
+        '''
+        self._machine = machine_type
+
+    def set_console(self, device_type=None):
+        '''
+        Sets the device type for a console device
+
+        If set, the console device and a backing character device will
+        be added to the base arguments of the resulting QEMU command
+        line.
+
+        This is a convenience method that will either use the provided
+        device type, of if not given, it will used the device type set
+        on CONSOLE_DEV_TYPES.
+
+        The actual setting of command line arguments will be be done at
+        machine launch time, as it depends on the temporary directory
+        to be created.
+
+        @param device_type: the device type, such as "isa-serial"
+        @raises: QEMUMachineAddDeviceError if the device type is not given
+                 and can not be determined.
+        '''
+        if device_type is None:
+            if self._machine is None:
+                raise QEMUMachineAddDeviceError("Can not add a console device:"
+                                                " QEMU instance without a "
+                                                "defined machine type")
+            for regex, device in CONSOLE_DEV_TYPES.items():
+                if re.match(regex, self._machine):
+                    device_type = device
+                    break
+            if device_type is None:
+                raise QEMUMachineAddDeviceError("Can not add a console device:"
+                                                " no matching console device "
+                                                "type definition")
+        self._console_device_type = device_type
+
+    @property
+    def console_socket(self):
+        """
+        Returns a socket connected to the console
+        """
+        if self._console_socket is None:
+            self._console_socket = socket.socket(socket.AF_UNIX,
+                                                 socket.SOCK_STREAM)
+            self._console_socket.connect(self._console_address)
+        return self._console_socket