From b5e0ed248fe0f3250d7f1648f23147d175096fe2 Mon Sep 17 00:00:00 2001 From: Manuel Bentele Date: Mon, 7 Feb 2022 10:30:38 +0100 Subject: [PATCH] libvirt: Add configuration options to set SPICE compression settings This patch adds the following SPICE-related options to the 'spice' configuration group of a Nova configuration: - image_compression - jpeg_compression - zlib_compression - playback_compression - streaming_mode These configuration options can be used to enable and set the SPICE compression settings for libvirt (QEMU/KVM) provisioned instances. Each configuration option is optional and can be set explictly to configure the associated SPICE compression setting for libvirt. If all configuration options are not set, then none of the SPICE compression settings will be configured for libvirt, which corresponds to the behavior before this change. In this case, the built-in defaults from the libvirt backend (e.g. QEMU) are used. Note that those options are only taken into account if SPICE support is enabled (and the VNC support is disabled). Implements: blueprint nova-support-spice-compression-algorithm Change-Id: Ia7efeb1b1a04504721e1a5bdd1b5fa7a87cdb810 --- doc/source/admin/remote-console-access.rst | 10 +++ nova/conf/spice.py | 53 +++++++++++++++ nova/tests/unit/virt/libvirt/test_config.py | 26 +++++++- nova/tests/unit/virt/libvirt/test_driver.py | 64 +++++++++++++++++++ nova/virt/libvirt/config.py | 24 +++++++ nova/virt/libvirt/driver.py | 5 ++ ...-compression-support-e41676f445544e8d.yaml | 23 +++++++ 7 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-spice-compression-support-e41676f445544e8d.yaml diff --git a/doc/source/admin/remote-console-access.rst b/doc/source/admin/remote-console-access.rst index 015c6522d05f..9b28646d2784 100644 --- a/doc/source/admin/remote-console-access.rst +++ b/doc/source/admin/remote-console-access.rst @@ -366,6 +366,16 @@ Replace ``IP_ADDRESS`` with the IP address from which the proxy is accessible by the outside world. For example, this may be the management interface IP address of the controller or the VIP. +Optionally, the :program:`nova-compute` service supports the following +additional options to configure compression settings (algorithms and modes) +for SPICE consoles. + +- :oslo.config:option:`spice.image_compression` +- :oslo.config:option:`spice.jpeg_compression` +- :oslo.config:option:`spice.zlib_compression` +- :oslo.config:option:`spice.playback_compression` +- :oslo.config:option:`spice.streaming_mode` + Serial ------ diff --git a/nova/conf/spice.py b/nova/conf/spice.py index 59ed4e80a03b..e5854946f15d 100644 --- a/nova/conf/spice.py +++ b/nova/conf/spice.py @@ -84,6 +84,59 @@ Agent. With the Spice agent installed the following features are enabled: * Better mouse integration - The mouse can be captured and released without needing to click inside the console or press keys to release it. The performance of mouse movement is also improved. +"""), + cfg.StrOpt('image_compression', + advanced=True, + choices=[ + ('auto_glz', 'enable image compression mode to choose between glz ' + 'and quic algorithm, based on image properties'), + ('auto_lz', 'enable image compression mode to choose between lz ' + 'and quic algorithm, based on image properties'), + ('quic', 'enable image compression based on the SFALIC algorithm'), + ('glz', 'enable image compression using lz with history based ' + 'global dictionary'), + ('lz', 'enable image compression with the Lempel-Ziv algorithm'), + ('off', 'disable image compression') + ], + help=""" +Configure the SPICE image compression (lossless). +"""), + cfg.StrOpt('jpeg_compression', + advanced=True, + choices=[ + ('auto', 'enable JPEG image compression automatically'), + ('never', 'disable JPEG image compression'), + ('always', 'enable JPEG image compression') + ], + help=""" +Configure the SPICE wan image compression (lossy for slow links). +"""), + cfg.StrOpt('zlib_compression', + advanced=True, + choices=[ + ('auto', 'enable zlib image compression automatically'), + ('never', 'disable zlib image compression'), + ('always', 'enable zlib image compression') + ], + help=""" +Configure the SPICE wan image compression (lossless for slow links). +"""), + cfg.BoolOpt('playback_compression', + advanced=True, + help=""" +Enable the SPICE audio stream compression (using celt). +"""), + cfg.StrOpt('streaming_mode', + advanced=True, + choices=[ + ('filter', 'SPICE server adds additional filters to decide if ' + 'video streaming should be activated'), + ('all', 'any fast-refreshing window can be encoded into a video ' + 'stream'), + ('off', 'no video detection and (lossy) compression is performed') + ], + help=""" +Configure the SPICE video stream detection and (lossy) compression. """), cfg.URIOpt('html5proxy_base_url', default='http://127.0.0.1:6082/spice_auto.html', diff --git a/nova/tests/unit/virt/libvirt/test_config.py b/nova/tests/unit/virt/libvirt/test_config.py index 8f840e885999..3d0b5ae6853d 100644 --- a/nova/tests/unit/virt/libvirt/test_config.py +++ b/nova/tests/unit/virt/libvirt/test_config.py @@ -1537,7 +1537,7 @@ class LibvirtConfigGuestInputTest(LibvirtConfigBaseTest): class LibvirtConfigGuestGraphicsTest(LibvirtConfigBaseTest): - def test_config_graphics(self): + def test_config_graphics_vnc(self): obj = config.LibvirtConfigGuestGraphics() obj.type = "vnc" obj.autoport = True @@ -1549,6 +1549,30 @@ class LibvirtConfigGuestGraphicsTest(LibvirtConfigBaseTest): """) + def test_config_graphics_spice(self): + obj = config.LibvirtConfigGuestGraphics() + obj.type = "spice" + obj.autoport = False + obj.keymap = "en_US" + obj.listen = "127.0.0.1" + + obj.image_compression = "auto_glz" + obj.jpeg_compression = "auto" + obj.zlib_compression = "always" + obj.playback_compression = True + obj.streaming_mode = "filter" + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + + + + """) + class LibvirtConfigGuestHostdev(LibvirtConfigBaseTest): diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 1c5f79dc896f..b7d46c8ca275 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -5793,6 +5793,11 @@ class LibvirtConnTestCase(test.NoDBTestCase, self.assertEqual(cfg.devices[3].type, 'vnc') self.assertEqual(cfg.devices[3].listen, '10.0.0.1') self.assertIsNone(cfg.devices[3].keymap) + self.assertIsNone(cfg.devices[3].image_compression) + self.assertIsNone(cfg.devices[3].jpeg_compression) + self.assertIsNone(cfg.devices[3].zlib_compression) + self.assertIsNone(cfg.devices[3].playback_compression) + self.assertIsNone(cfg.devices[3].streaming_mode) def test_get_guest_config_with_vnc_and_tablet(self): self.flags(enabled=True, group='vnc') @@ -5823,6 +5828,11 @@ class LibvirtConnTestCase(test.NoDBTestCase, vconfig.LibvirtConfigMemoryBalloon) self.assertEqual(cfg.devices[3].type, 'vnc') + self.assertIsNone(cfg.devices[3].image_compression) + self.assertIsNone(cfg.devices[3].jpeg_compression) + self.assertIsNone(cfg.devices[3].zlib_compression) + self.assertIsNone(cfg.devices[3].playback_compression) + self.assertIsNone(cfg.devices[3].streaming_mode) self.assertEqual(cfg.devices[5].type, 'tablet') def test_get_guest_config_with_spice_and_tablet(self): @@ -5859,6 +5869,11 @@ class LibvirtConnTestCase(test.NoDBTestCase, self.assertEqual(cfg.devices[3].type, 'spice') self.assertEqual(cfg.devices[3].listen, '10.0.0.1') self.assertIsNone(cfg.devices[3].keymap) + self.assertIsNone(cfg.devices[3].image_compression) + self.assertIsNone(cfg.devices[3].jpeg_compression) + self.assertIsNone(cfg.devices[3].zlib_compression) + self.assertIsNone(cfg.devices[3].playback_compression) + self.assertIsNone(cfg.devices[3].streaming_mode) self.assertEqual(cfg.devices[5].type, 'tablet') @mock.patch.object(host.Host, "_check_machine_type", new=mock.Mock()) @@ -5918,8 +5933,57 @@ class LibvirtConnTestCase(test.NoDBTestCase, self.assertEqual(cfg.devices[3].target_name, "com.redhat.spice.0") self.assertEqual(cfg.devices[3].type, 'spicevmc') self.assertEqual(cfg.devices[4].type, "spice") + self.assertIsNone(cfg.devices[4].image_compression) + self.assertIsNone(cfg.devices[4].jpeg_compression) + self.assertIsNone(cfg.devices[4].zlib_compression) + self.assertIsNone(cfg.devices[4].playback_compression) + self.assertIsNone(cfg.devices[4].streaming_mode) self.assertEqual(cfg.devices[5].type, video_type) + def test_get_guest_config_with_spice_compression(self): + self.flags(enabled=False, group='vnc') + self.flags(virt_type='kvm', group='libvirt') + self.flags(enabled=True, + agent_enabled=False, + image_compression='auto_lz', + jpeg_compression='never', + zlib_compression='always', + playback_compression=False, + streaming_mode='all', + server_listen='10.0.0.1', + group='spice') + self.flags(pointer_model='usbtablet') + + cfg = self._get_guest_config_with_graphics() + + self.assertEqual(len(cfg.devices), 9) + self.assertIsInstance(cfg.devices[0], + vconfig.LibvirtConfigGuestDisk) + self.assertIsInstance(cfg.devices[1], + vconfig.LibvirtConfigGuestDisk) + self.assertIsInstance(cfg.devices[2], + vconfig.LibvirtConfigGuestSerial) + self.assertIsInstance(cfg.devices[3], + vconfig.LibvirtConfigGuestGraphics) + self.assertIsInstance(cfg.devices[4], + vconfig.LibvirtConfigGuestVideo) + self.assertIsInstance(cfg.devices[5], + vconfig.LibvirtConfigGuestInput) + self.assertIsInstance(cfg.devices[6], + vconfig.LibvirtConfigGuestRng) + self.assertIsInstance(cfg.devices[7], + vconfig.LibvirtConfigGuestUSBHostController) + self.assertIsInstance(cfg.devices[8], + vconfig.LibvirtConfigMemoryBalloon) + + self.assertEqual(cfg.devices[3].type, 'spice') + self.assertEqual(cfg.devices[3].listen, '10.0.0.1') + self.assertEqual(cfg.devices[3].image_compression, 'auto_lz') + self.assertEqual(cfg.devices[3].jpeg_compression, 'never') + self.assertEqual(cfg.devices[3].zlib_compression, 'always') + self.assertFalse(cfg.devices[3].playback_compression) + self.assertEqual(cfg.devices[3].streaming_mode, 'all') + @mock.patch.object(host.Host, 'get_guest') @mock.patch.object(libvirt_driver.LibvirtDriver, '_get_serial_ports_from_guest') diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py index 0db2dc6b679f..231283b8ddb6 100644 --- a/nova/virt/libvirt/config.py +++ b/nova/virt/libvirt/config.py @@ -2047,6 +2047,12 @@ class LibvirtConfigGuestGraphics(LibvirtConfigGuestDevice): self.keymap = None self.listen = None + self.image_compression = None + self.jpeg_compression = None + self.zlib_compression = None + self.playback_compression = None + self.streaming_mode = None + def format_dom(self): dev = super(LibvirtConfigGuestGraphics, self).format_dom() @@ -2057,6 +2063,24 @@ class LibvirtConfigGuestGraphics(LibvirtConfigGuestDevice): if self.listen: dev.set("listen", self.listen) + if self.type == "spice": + if self.image_compression is not None: + dev.append(etree.Element( + 'image', compression=self.image_compression)) + if self.jpeg_compression is not None: + dev.append(etree.Element( + 'jpeg', compression=self.jpeg_compression)) + if self.zlib_compression is not None: + dev.append(etree.Element( + 'zlib', compression=self.zlib_compression)) + if self.playback_compression is not None: + dev.append(etree.Element( + 'playback', compression=self.get_on_off_str( + self.playback_compression))) + if self.streaming_mode is not None: + dev.append(etree.Element( + 'streaming', mode=self.streaming_mode)) + return dev diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index d7fe0fcc93d1..8b36fea3c96b 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -7302,6 +7302,11 @@ class LibvirtDriver(driver.ComputeDriver): graphics = vconfig.LibvirtConfigGuestGraphics() graphics.type = "spice" graphics.listen = CONF.spice.server_listen + graphics.image_compression = CONF.spice.image_compression + graphics.jpeg_compression = CONF.spice.jpeg_compression + graphics.zlib_compression = CONF.spice.zlib_compression + graphics.playback_compression = CONF.spice.playback_compression + graphics.streaming_mode = CONF.spice.streaming_mode guest.add_device(graphics) add_video_driver = True diff --git a/releasenotes/notes/add-spice-compression-support-e41676f445544e8d.yaml b/releasenotes/notes/add-spice-compression-support-e41676f445544e8d.yaml new file mode 100644 index 000000000000..b370889171ce --- /dev/null +++ b/releasenotes/notes/add-spice-compression-support-e41676f445544e8d.yaml @@ -0,0 +1,23 @@ +--- +features: + - | + The following SPICE-related options are added to the ``spice`` + configuration group of a Nova configuration: + + - ``image_compression`` + - ``jpeg_compression`` + - ``zlib_compression`` + - ``playback_compression`` + - ``streaming_mode`` + + These configuration options can be used to enable and set the + SPICE compression settings for libvirt (QEMU/KVM) provisioned + instances. Each configuration option is optional and can be set + explictly to configure the associated SPICE compression setting + for libvirt. If all configuration options are not set, then none + of the SPICE compression settings will be configured for libvirt, + which corresponds to the behavior before this change. In this case, + the built-in defaults from the libvirt backend (e.g. QEMU) are used. + + Note that those options are only taken into account if SPICE support + is enabled (and the VNC support is disabled).