Skip to content

Commit 5c32d5e

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "libvirt: Add configuration options to set SPICE compression settings"
2 parents 59ac941 + b5e0ed2 commit 5c32d5e

File tree

7 files changed

+204
-1
lines changed

7 files changed

+204
-1
lines changed

doc/source/admin/remote-console-access.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,16 @@ Replace ``IP_ADDRESS`` with the IP address from which the proxy is accessible
366366
by the outside world. For example, this may be the management interface IP
367367
address of the controller or the VIP.
368368

369+
Optionally, the :program:`nova-compute` service supports the following
370+
additional options to configure compression settings (algorithms and modes)
371+
for SPICE consoles.
372+
373+
- :oslo.config:option:`spice.image_compression`
374+
- :oslo.config:option:`spice.jpeg_compression`
375+
- :oslo.config:option:`spice.zlib_compression`
376+
- :oslo.config:option:`spice.playback_compression`
377+
- :oslo.config:option:`spice.streaming_mode`
378+
369379

370380
Serial
371381
------

nova/conf/spice.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,59 @@
8484
* Better mouse integration - The mouse can be captured and released without
8585
needing to click inside the console or press keys to release it. The
8686
performance of mouse movement is also improved.
87+
"""),
88+
cfg.StrOpt('image_compression',
89+
advanced=True,
90+
choices=[
91+
('auto_glz', 'enable image compression mode to choose between glz '
92+
'and quic algorithm, based on image properties'),
93+
('auto_lz', 'enable image compression mode to choose between lz '
94+
'and quic algorithm, based on image properties'),
95+
('quic', 'enable image compression based on the SFALIC algorithm'),
96+
('glz', 'enable image compression using lz with history based '
97+
'global dictionary'),
98+
('lz', 'enable image compression with the Lempel-Ziv algorithm'),
99+
('off', 'disable image compression')
100+
],
101+
help="""
102+
Configure the SPICE image compression (lossless).
103+
"""),
104+
cfg.StrOpt('jpeg_compression',
105+
advanced=True,
106+
choices=[
107+
('auto', 'enable JPEG image compression automatically'),
108+
('never', 'disable JPEG image compression'),
109+
('always', 'enable JPEG image compression')
110+
],
111+
help="""
112+
Configure the SPICE wan image compression (lossy for slow links).
113+
"""),
114+
cfg.StrOpt('zlib_compression',
115+
advanced=True,
116+
choices=[
117+
('auto', 'enable zlib image compression automatically'),
118+
('never', 'disable zlib image compression'),
119+
('always', 'enable zlib image compression')
120+
],
121+
help="""
122+
Configure the SPICE wan image compression (lossless for slow links).
123+
"""),
124+
cfg.BoolOpt('playback_compression',
125+
advanced=True,
126+
help="""
127+
Enable the SPICE audio stream compression (using celt).
128+
"""),
129+
cfg.StrOpt('streaming_mode',
130+
advanced=True,
131+
choices=[
132+
('filter', 'SPICE server adds additional filters to decide if '
133+
'video streaming should be activated'),
134+
('all', 'any fast-refreshing window can be encoded into a video '
135+
'stream'),
136+
('off', 'no video detection and (lossy) compression is performed')
137+
],
138+
help="""
139+
Configure the SPICE video stream detection and (lossy) compression.
87140
"""),
88141
cfg.URIOpt('html5proxy_base_url',
89142
default='http://127.0.0.1:6082/spice_auto.html',

nova/tests/unit/virt/libvirt/test_config.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ def test_config_input(self):
15371537

15381538
class LibvirtConfigGuestGraphicsTest(LibvirtConfigBaseTest):
15391539

1540-
def test_config_graphics(self):
1540+
def test_config_graphics_vnc(self):
15411541
obj = config.LibvirtConfigGuestGraphics()
15421542
obj.type = "vnc"
15431543
obj.autoport = True
@@ -1549,6 +1549,30 @@ def test_config_graphics(self):
15491549
<graphics type="vnc" autoport="yes" keymap="en_US" listen="127.0.0.1"/>
15501550
""")
15511551

1552+
def test_config_graphics_spice(self):
1553+
obj = config.LibvirtConfigGuestGraphics()
1554+
obj.type = "spice"
1555+
obj.autoport = False
1556+
obj.keymap = "en_US"
1557+
obj.listen = "127.0.0.1"
1558+
1559+
obj.image_compression = "auto_glz"
1560+
obj.jpeg_compression = "auto"
1561+
obj.zlib_compression = "always"
1562+
obj.playback_compression = True
1563+
obj.streaming_mode = "filter"
1564+
1565+
xml = obj.to_xml()
1566+
self.assertXmlEqual(xml, """
1567+
<graphics type="spice" autoport="no" keymap="en_US" listen="127.0.0.1">
1568+
<image compression="auto_glz"/>
1569+
<jpeg compression="auto"/>
1570+
<zlib compression="always"/>
1571+
<playback compression="on"/>
1572+
<streaming mode="filter"/>
1573+
</graphics>
1574+
""")
1575+
15521576

15531577
class LibvirtConfigGuestHostdev(LibvirtConfigBaseTest):
15541578

nova/tests/unit/virt/libvirt/test_driver.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5839,6 +5839,11 @@ def test_get_guest_config_with_vnc(self):
58395839
self.assertEqual(cfg.devices[3].type, 'vnc')
58405840
self.assertEqual(cfg.devices[3].listen, '10.0.0.1')
58415841
self.assertIsNone(cfg.devices[3].keymap)
5842+
self.assertIsNone(cfg.devices[3].image_compression)
5843+
self.assertIsNone(cfg.devices[3].jpeg_compression)
5844+
self.assertIsNone(cfg.devices[3].zlib_compression)
5845+
self.assertIsNone(cfg.devices[3].playback_compression)
5846+
self.assertIsNone(cfg.devices[3].streaming_mode)
58425847

58435848
def test_get_guest_config_with_vnc_and_tablet(self):
58445849
self.flags(enabled=True, group='vnc')
@@ -5869,6 +5874,11 @@ def test_get_guest_config_with_vnc_and_tablet(self):
58695874
vconfig.LibvirtConfigMemoryBalloon)
58705875

58715876
self.assertEqual(cfg.devices[3].type, 'vnc')
5877+
self.assertIsNone(cfg.devices[3].image_compression)
5878+
self.assertIsNone(cfg.devices[3].jpeg_compression)
5879+
self.assertIsNone(cfg.devices[3].zlib_compression)
5880+
self.assertIsNone(cfg.devices[3].playback_compression)
5881+
self.assertIsNone(cfg.devices[3].streaming_mode)
58725882
self.assertEqual(cfg.devices[5].type, 'tablet')
58735883

58745884
def test_get_guest_config_with_spice_and_tablet(self):
@@ -5905,6 +5915,11 @@ def test_get_guest_config_with_spice_and_tablet(self):
59055915
self.assertEqual(cfg.devices[3].type, 'spice')
59065916
self.assertEqual(cfg.devices[3].listen, '10.0.0.1')
59075917
self.assertIsNone(cfg.devices[3].keymap)
5918+
self.assertIsNone(cfg.devices[3].image_compression)
5919+
self.assertIsNone(cfg.devices[3].jpeg_compression)
5920+
self.assertIsNone(cfg.devices[3].zlib_compression)
5921+
self.assertIsNone(cfg.devices[3].playback_compression)
5922+
self.assertIsNone(cfg.devices[3].streaming_mode)
59085923
self.assertEqual(cfg.devices[5].type, 'tablet')
59095924

59105925
@mock.patch.object(host.Host, "_check_machine_type", new=mock.Mock())
@@ -5964,8 +5979,57 @@ def test_get_guest_config_with_spice_and_agent(self):
59645979
self.assertEqual(cfg.devices[3].target_name, "com.redhat.spice.0")
59655980
self.assertEqual(cfg.devices[3].type, 'spicevmc')
59665981
self.assertEqual(cfg.devices[4].type, "spice")
5982+
self.assertIsNone(cfg.devices[4].image_compression)
5983+
self.assertIsNone(cfg.devices[4].jpeg_compression)
5984+
self.assertIsNone(cfg.devices[4].zlib_compression)
5985+
self.assertIsNone(cfg.devices[4].playback_compression)
5986+
self.assertIsNone(cfg.devices[4].streaming_mode)
59675987
self.assertEqual(cfg.devices[5].type, video_type)
59685988

5989+
def test_get_guest_config_with_spice_compression(self):
5990+
self.flags(enabled=False, group='vnc')
5991+
self.flags(virt_type='kvm', group='libvirt')
5992+
self.flags(enabled=True,
5993+
agent_enabled=False,
5994+
image_compression='auto_lz',
5995+
jpeg_compression='never',
5996+
zlib_compression='always',
5997+
playback_compression=False,
5998+
streaming_mode='all',
5999+
server_listen='10.0.0.1',
6000+
group='spice')
6001+
self.flags(pointer_model='usbtablet')
6002+
6003+
cfg = self._get_guest_config_with_graphics()
6004+
6005+
self.assertEqual(len(cfg.devices), 9)
6006+
self.assertIsInstance(cfg.devices[0],
6007+
vconfig.LibvirtConfigGuestDisk)
6008+
self.assertIsInstance(cfg.devices[1],
6009+
vconfig.LibvirtConfigGuestDisk)
6010+
self.assertIsInstance(cfg.devices[2],
6011+
vconfig.LibvirtConfigGuestSerial)
6012+
self.assertIsInstance(cfg.devices[3],
6013+
vconfig.LibvirtConfigGuestGraphics)
6014+
self.assertIsInstance(cfg.devices[4],
6015+
vconfig.LibvirtConfigGuestVideo)
6016+
self.assertIsInstance(cfg.devices[5],
6017+
vconfig.LibvirtConfigGuestInput)
6018+
self.assertIsInstance(cfg.devices[6],
6019+
vconfig.LibvirtConfigGuestRng)
6020+
self.assertIsInstance(cfg.devices[7],
6021+
vconfig.LibvirtConfigGuestUSBHostController)
6022+
self.assertIsInstance(cfg.devices[8],
6023+
vconfig.LibvirtConfigMemoryBalloon)
6024+
6025+
self.assertEqual(cfg.devices[3].type, 'spice')
6026+
self.assertEqual(cfg.devices[3].listen, '10.0.0.1')
6027+
self.assertEqual(cfg.devices[3].image_compression, 'auto_lz')
6028+
self.assertEqual(cfg.devices[3].jpeg_compression, 'never')
6029+
self.assertEqual(cfg.devices[3].zlib_compression, 'always')
6030+
self.assertFalse(cfg.devices[3].playback_compression)
6031+
self.assertEqual(cfg.devices[3].streaming_mode, 'all')
6032+
59696033
@mock.patch.object(host.Host, 'get_guest')
59706034
@mock.patch.object(libvirt_driver.LibvirtDriver,
59716035
'_get_serial_ports_from_guest')

nova/virt/libvirt/config.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,6 +2047,12 @@ def __init__(self, **kwargs):
20472047
self.keymap = None
20482048
self.listen = None
20492049

2050+
self.image_compression = None
2051+
self.jpeg_compression = None
2052+
self.zlib_compression = None
2053+
self.playback_compression = None
2054+
self.streaming_mode = None
2055+
20502056
def format_dom(self):
20512057
dev = super(LibvirtConfigGuestGraphics, self).format_dom()
20522058

@@ -2057,6 +2063,24 @@ def format_dom(self):
20572063
if self.listen:
20582064
dev.set("listen", self.listen)
20592065

2066+
if self.type == "spice":
2067+
if self.image_compression is not None:
2068+
dev.append(etree.Element(
2069+
'image', compression=self.image_compression))
2070+
if self.jpeg_compression is not None:
2071+
dev.append(etree.Element(
2072+
'jpeg', compression=self.jpeg_compression))
2073+
if self.zlib_compression is not None:
2074+
dev.append(etree.Element(
2075+
'zlib', compression=self.zlib_compression))
2076+
if self.playback_compression is not None:
2077+
dev.append(etree.Element(
2078+
'playback', compression=self.get_on_off_str(
2079+
self.playback_compression)))
2080+
if self.streaming_mode is not None:
2081+
dev.append(etree.Element(
2082+
'streaming', mode=self.streaming_mode))
2083+
20602084
return dev
20612085

20622086

nova/virt/libvirt/driver.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7300,6 +7300,11 @@ def _guest_add_video_device(guest):
73007300
graphics = vconfig.LibvirtConfigGuestGraphics()
73017301
graphics.type = "spice"
73027302
graphics.listen = CONF.spice.server_listen
7303+
graphics.image_compression = CONF.spice.image_compression
7304+
graphics.jpeg_compression = CONF.spice.jpeg_compression
7305+
graphics.zlib_compression = CONF.spice.zlib_compression
7306+
graphics.playback_compression = CONF.spice.playback_compression
7307+
graphics.streaming_mode = CONF.spice.streaming_mode
73037308
guest.add_device(graphics)
73047309
add_video_driver = True
73057310

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
features:
3+
- |
4+
The following SPICE-related options are added to the ``spice``
5+
configuration group of a Nova configuration:
6+
7+
- ``image_compression``
8+
- ``jpeg_compression``
9+
- ``zlib_compression``
10+
- ``playback_compression``
11+
- ``streaming_mode``
12+
13+
These configuration options can be used to enable and set the
14+
SPICE compression settings for libvirt (QEMU/KVM) provisioned
15+
instances. Each configuration option is optional and can be set
16+
explictly to configure the associated SPICE compression setting
17+
for libvirt. If all configuration options are not set, then none
18+
of the SPICE compression settings will be configured for libvirt,
19+
which corresponds to the behavior before this change. In this case,
20+
the built-in defaults from the libvirt backend (e.g. QEMU) are used.
21+
22+
Note that those options are only taken into account if SPICE support
23+
is enabled (and the VNC support is disabled).

0 commit comments

Comments
 (0)