diff --git a/nova/conf/virt.py b/nova/conf/virt.py index cc7a9de5f0..d4c67c1524 100644 --- a/nova/conf/virt.py +++ b/nova/conf/virt.py @@ -333,6 +333,9 @@ pointer_model = cfg.StrOpt( Input devices allow interaction with a graphical framebuffer. For example to provide a graphic tablet for absolute cursor movement. +If set, the 'hw_pointer_model' image property takes precedence over +this configuration option. + Possible values: * None: Uses relative movement. Mouse connected by PS2 diff --git a/nova/exception.py b/nova/exception.py index 6af10f2ef2..c60a9cf4d2 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -2153,3 +2153,8 @@ class InvalidReservedMemoryPagesOption(Invalid): class ConcurrentUpdateDetected(NovaException): msg_fmt = _("Another thread concurrently updated the data. " "Please retry your update") + + +class UnsupportedPointerModelRequested(Invalid): + msg_fmt = _("Pointer model '%(model)s' requested is not supported by " + "host.") diff --git a/nova/objects/fields.py b/nova/objects/fields.py index 821d7559ce..4eec0a64ca 100644 --- a/nova/objects/fields.py +++ b/nova/objects/fields.py @@ -567,6 +567,17 @@ class DiskFormat(Enum): valid_values=DiskFormat.ALL) +class PointerModelType(Enum): + + USBTABLET = "usbtablet" + + ALL = (USBTABLET) + + def __init__(self): + super(PointerModelType, self).__init__( + valid_values=PointerModelType.ALL) + + class NotificationPriority(Enum): AUDIT = 'audit' CRITICAL = 'critical' @@ -836,6 +847,10 @@ class DiskFormatField(BaseEnumField): AUTO_TYPE = DiskFormat() +class PointerModelField(BaseEnumField): + AUTO_TYPE = PointerModelType() + + class NotificationPriorityField(BaseEnumField): AUTO_TYPE = NotificationPriority() diff --git a/nova/objects/image_meta.py b/nova/objects/image_meta.py index 7324727756..7e0d811f4c 100644 --- a/nova/objects/image_meta.py +++ b/nova/objects/image_meta.py @@ -162,12 +162,15 @@ class ImageMetaProps(base.NovaObject): # Version 1.11: Added hw_firmware_type field # Version 1.12: Added properties for image signature verification # Version 1.13: added os_secure_boot field - VERSION = '1.13' + # Version 1.14: Added 'hw_pointer_model' field + VERSION = '1.14' def obj_make_compatible(self, primitive, target_version): super(ImageMetaProps, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 14): + primitive.pop('hw_pointer_model', None) if target_version < (1, 13): primitive.pop('os_secure_boot', None) if target_version < (1, 11): @@ -287,6 +290,9 @@ class ImageMetaProps(base.NovaObject): # list value indicates the memory size of that node. 'hw_numa_mem': fields.ListOfIntegersField(), + # Generic property to specify the pointer model type. + 'hw_pointer_model': fields.PointerModelField(), + # boolean 'yes' or 'no' to enable QEMU guest agent 'hw_qemu_guest_agent': fields.FlexibleBooleanField(), diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index bc2a3c6b87..5f428c647a 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1132,7 +1132,7 @@ object_data = { 'HVSpec': '1.2-db672e73304da86139086d003f3977e7', 'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502', 'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d', - 'ImageMetaProps': '1.13-8bc4e3cac2a941991e9adbc54e915698', + 'ImageMetaProps': '1.14-cc8f96454cdb0feadcb2fc9e63974016', 'Instance': '2.2-ab450ec9c1f4d429755c48d492b823f0', 'InstanceAction': '1.1-f9f293e526b66fca0d05c3b3a2d13914', 'InstanceActionEvent': '1.1-e56a64fa4710e43ef7af2ad9d6028b33', diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 14ba080812..cb80f97dd8 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -3866,12 +3866,13 @@ class LibvirtConnTestCase(test.NoDBTestCase): self.assertEqual("none", cfg.devices[7].action) def _test_get_guest_usb_tablet(self, vnc_enabled, spice_enabled, os_type, - agent_enabled=False): + agent_enabled=False, image_meta=None): self.flags(enabled=vnc_enabled, group='vnc') self.flags(enabled=spice_enabled, agent_enabled=agent_enabled, group='spice') drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) - return drvr._get_guest_pointer_model(os_type) + image_meta = objects.ImageMeta.from_dict(image_meta) + return drvr._get_guest_pointer_model(os_type, image_meta) def test_get_guest_usb_tablet_wipe(self): self.flags(use_usb_tablet=True, group='libvirt') @@ -3895,6 +3896,44 @@ class LibvirtConnTestCase(test.NoDBTestCase): False, True, vm_mode.HVM, True) self.assertIsNone(tablet) + def test_get_guest_usb_tablet_image_meta(self): + self.flags(use_usb_tablet=True, group='libvirt') + image_meta = {"properties": {"hw_pointer_model": "usbtablet"}} + + tablet = self._test_get_guest_usb_tablet( + True, True, vm_mode.HVM, image_meta=image_meta) + self.assertIsNotNone(tablet) + + tablet = self._test_get_guest_usb_tablet( + True, False, vm_mode.HVM, image_meta=image_meta) + self.assertIsNotNone(tablet) + + tablet = self._test_get_guest_usb_tablet( + False, True, vm_mode.HVM, image_meta=image_meta) + self.assertIsNotNone(tablet) + + tablet = self._test_get_guest_usb_tablet( + False, False, vm_mode.HVM, image_meta=image_meta) + self.assertIsNone(tablet) + + tablet = self._test_get_guest_usb_tablet( + True, True, "foo", image_meta=image_meta) + self.assertIsNone(tablet) + + tablet = self._test_get_guest_usb_tablet( + False, True, vm_mode.HVM, True, image_meta=image_meta) + self.assertIsNone(tablet) + + def test_get_guest_usb_tablet_image_meta_no_vnc(self): + self.flags(use_usb_tablet=False, group='libvirt') + self.flags(pointer_model=None) + + image_meta = {"properties": {"hw_pointer_model": "usbtablet"}} + self.assertRaises( + exception.UnsupportedPointerModelRequested, + self._test_get_guest_usb_tablet, + False, False, vm_mode.HVM, True, image_meta=image_meta) + def test_get_guest_no_pointer_model_usb_tablet_set(self): self.flags(use_usb_tablet=True, group='libvirt') @@ -3913,6 +3952,21 @@ class LibvirtConnTestCase(test.NoDBTestCase): tablet = self._test_get_guest_usb_tablet(True, True, vm_mode.HVM) self.assertIsNotNone(tablet) + def test_get_guest_pointer_model_usb_tablet_image(self): + image_meta = {"properties": {"hw_pointer_model": "usbtablet"}} + tablet = self._test_get_guest_usb_tablet( + True, True, vm_mode.HVM, image_meta=image_meta) + self.assertIsNotNone(tablet) + + def test_get_guest_pointer_model_usb_tablet_image_no_HVM(self): + self.flags(pointer_model=None) + self.flags(use_usb_tablet=False, group='libvirt') + image_meta = {"properties": {"hw_pointer_model": "usbtablet"}} + self.assertRaises( + exception.UnsupportedPointerModelRequested, + self._test_get_guest_usb_tablet, + True, True, vm_mode.XEN, image_meta=image_meta) + def _test_get_guest_config_with_watchdog_action_flavor(self, hw_watchdog_action="hw:watchdog_action"): self.flags(virt_type='kvm', group='libvirt') diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 27c20fc853..6430b00e98 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -4374,7 +4374,7 @@ class LibvirtDriver(driver.ComputeDriver): consolepty.type = "pty" guest.add_device(consolepty) - pointer = self._get_guest_pointer_model(guest.os_type) + pointer = self._get_guest_pointer_model(guest.os_type, image_meta) if pointer: guest.add_device(pointer) @@ -4457,8 +4457,9 @@ class LibvirtDriver(driver.ComputeDriver): return guest - def _get_guest_pointer_model(self, os_type): - pointer_model = CONF.pointer_model + def _get_guest_pointer_model(self, os_type, image_meta): + pointer_model = image_meta.properties.get( + 'hw_pointer_model', CONF.pointer_model) if pointer_model is None and CONF.libvirt.use_usb_tablet: # TODO(sahid): We set pointer_model to keep compatibility # until the next release O*. It means operators can continue @@ -4479,14 +4480,18 @@ class LibvirtDriver(driver.ComputeDriver): CONF.spice.enabled and not CONF.spice.agent_enabled): return self._get_guest_usb_tablet(os_type) else: - # For backward compatibility We don't want to break - # process of booting an instance if host is configured - # to use USB tablet without VNC or SPICE and SPICE - # agent disable. - LOG.warning(_LW('USB tablet requested for guests by host ' - 'configuration. In order to accept this ' - 'request VNC should be enabled or SPICE ' - 'and SPICE agent disabled on host.')) + if CONF.pointer_model or CONF.libvirt.use_usb_tablet: + # For backward compatibility We don't want to break + # process of booting an instance if host is configured + # to use USB tablet without VNC or SPICE and SPICE + # agent disable. + LOG.warning(_LW('USB tablet requested for guests by host ' + 'configuration. In order to accept this ' + 'request VNC should be enabled or SPICE ' + 'and SPICE agent disabled on host.')) + else: + raise exception.UnsupportedPointerModelRequested( + model="usbtablet") def _get_guest_usb_tablet(self, os_type): tablet = None @@ -4495,12 +4500,17 @@ class LibvirtDriver(driver.ComputeDriver): tablet.type = "tablet" tablet.bus = "usb" else: - # For backward compatibility We don't want to break - # process of booting an instance if virtual machine mode - # is not configured as HVM. - LOG.warning(_LW('USB tablet requested for guests by host ' - 'configuration. In order to accept this request ' - 'the machine mode should be configured as HVM.')) + if CONF.pointer_model or CONF.libvirt.use_usb_tablet: + # For backward compatibility We don't want to break + # process of booting an instance if virtual machine mode + # is not configured as HVM. + LOG.warning(_LW('USB tablet requested for guests by host ' + 'configuration. In order to accept this ' + 'request the machine mode should be ' + 'configured as HVM.')) + else: + raise exception.UnsupportedPointerModelRequested( + model="usbtablet") return tablet def _get_guest_xml(self, context, instance, network_info, disk_info, diff --git a/releasenotes/notes/pointer-model-b4a1828c43e8d523.yaml b/releasenotes/notes/pointer-model-b4a1828c43e8d523.yaml index 3fe4a8755c..3b5c1d5ba1 100644 --- a/releasenotes/notes/pointer-model-b4a1828c43e8d523.yaml +++ b/releasenotes/notes/pointer-model-b4a1828c43e8d523.yaml @@ -1,8 +1,9 @@ --- features: - - The pointer_model configuration option was added to specify - different pointer models for input devices. This replaces - the now deprecated use_usb_tablet option. + - The pointer_model configuration option and hw_pointer_model image + property was added to specify different pointer models for input + devices. This replaces the now deprecated use_usb_tablet + option. deprecations: - Nova option 'use_usb_tablet' will be deprecated in favor of the global 'pointer_model'. \ No newline at end of file