libvirt: enhance libvirt to set admin password
Libvirt 1.2.16 provides to set to the domain an user password. This commit enhances Nova to use this new API and let users request to set the admin password. By default we will use root as user but if os_type windows is specified we will use Administrator Change-Id: I6bd2e67716222b3aa0d777d4a9411dc7c4a93a84 Implement: blueprint libvirt-set-admin-password
This commit is contained in:
parent
cd8e67b31f
commit
520a6b9285
|
@ -1824,6 +1824,10 @@ class QemuGuestAgentNotEnabled(Invalid):
|
||||||
msg_fmt = _('QEMU guest agent is not enabled')
|
msg_fmt = _('QEMU guest agent is not enabled')
|
||||||
|
|
||||||
|
|
||||||
|
class SetAdminPasswdNotSupported(Invalid):
|
||||||
|
msg_fmt = _('Set admin password is not supported')
|
||||||
|
|
||||||
|
|
||||||
class MemoryPageSizeInvalid(Invalid):
|
class MemoryPageSizeInvalid(Invalid):
|
||||||
msg_fmt = _("Invalid memory page size '%(pagesize)s'")
|
msg_fmt = _("Invalid memory page size '%(pagesize)s'")
|
||||||
|
|
||||||
|
|
|
@ -615,6 +615,9 @@ class Domain(object):
|
||||||
def detachDeviceFlags(self, xml, flags):
|
def detachDeviceFlags(self, xml, flags):
|
||||||
self.detachDevice(xml)
|
self.detachDevice(xml)
|
||||||
|
|
||||||
|
def setUserPassword(self, user, password, flags=0):
|
||||||
|
pass
|
||||||
|
|
||||||
def XMLDesc(self, flags):
|
def XMLDesc(self, flags):
|
||||||
disks = ''
|
disks = ''
|
||||||
for disk in self._def['devices']['disks']:
|
for disk in self._def['devices']['disks']:
|
||||||
|
|
|
@ -680,6 +680,93 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||||
break
|
break
|
||||||
self.assertFalse(version_arg_found)
|
self.assertFalse(version_arg_found)
|
||||||
|
|
||||||
|
@mock.patch('nova.utils.get_image_from_system_metadata')
|
||||||
|
@mock.patch.object(host.Host,
|
||||||
|
'has_min_version', return_value=True)
|
||||||
|
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
|
||||||
|
def test_set_admin_password(self, mock_get_guest, ver, mock_image):
|
||||||
|
self.flags(virt_type='kvm', group='libvirt')
|
||||||
|
instance = objects.Instance(**self.test_instance)
|
||||||
|
mock_image.return_value = {"properties": {
|
||||||
|
"hw_qemu_guest_agent": "yes"}}
|
||||||
|
mock_guest = mock.Mock(spec=libvirt_guest.Guest)
|
||||||
|
mock_get_guest.return_value = mock_guest
|
||||||
|
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
drvr.set_admin_password(instance, "123")
|
||||||
|
|
||||||
|
mock_guest.set_user_password.assert_called_once_with("root", "123")
|
||||||
|
|
||||||
|
@mock.patch('nova.utils.get_image_from_system_metadata')
|
||||||
|
@mock.patch.object(host.Host,
|
||||||
|
'has_min_version', return_value=True)
|
||||||
|
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
|
||||||
|
def test_set_admin_password_windows(self, mock_get_guest, ver, mock_image):
|
||||||
|
self.flags(virt_type='kvm', group='libvirt')
|
||||||
|
instance = objects.Instance(**self.test_instance)
|
||||||
|
instance.os_type = "windows"
|
||||||
|
mock_image.return_value = {"properties": {
|
||||||
|
"hw_qemu_guest_agent": "yes"}}
|
||||||
|
mock_guest = mock.Mock(spec=libvirt_guest.Guest)
|
||||||
|
mock_get_guest.return_value = mock_guest
|
||||||
|
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
drvr.set_admin_password(instance, "123")
|
||||||
|
|
||||||
|
mock_guest.set_user_password.assert_called_once_with(
|
||||||
|
"Administrator", "123")
|
||||||
|
|
||||||
|
@mock.patch('nova.utils.get_image_from_system_metadata')
|
||||||
|
@mock.patch.object(host.Host,
|
||||||
|
'has_min_version', return_value=False)
|
||||||
|
def test_set_admin_password_bad_version(self, mock_svc, mock_image):
|
||||||
|
self.flags(virt_type='kvm', group='libvirt')
|
||||||
|
instance = objects.Instance(**self.test_instance)
|
||||||
|
mock_image.return_value = {"properties": {
|
||||||
|
"hw_qemu_guest_agent": "yes"}}
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
self.assertRaises(exception.SetAdminPasswdNotSupported,
|
||||||
|
drvr.set_admin_password, instance, "123")
|
||||||
|
|
||||||
|
@mock.patch('nova.utils.get_image_from_system_metadata')
|
||||||
|
@mock.patch.object(host.Host,
|
||||||
|
'has_min_version', return_value=True)
|
||||||
|
def test_set_admin_password_bad_hyp(self, mock_svc, mock_image):
|
||||||
|
self.flags(virt_type='foo', group='libvirt')
|
||||||
|
instance = objects.Instance(**self.test_instance)
|
||||||
|
mock_image.return_value = {"properties": {
|
||||||
|
"hw_qemu_guest_agent": "yes"}}
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
self.assertRaises(exception.SetAdminPasswdNotSupported,
|
||||||
|
drvr.set_admin_password, instance, "123")
|
||||||
|
|
||||||
|
@mock.patch.object(host.Host,
|
||||||
|
'has_min_version', return_value=True)
|
||||||
|
def test_set_admin_password_guest_agent_not_running(self, mock_svc):
|
||||||
|
self.flags(virt_type='kvm', group='libvirt')
|
||||||
|
instance = objects.Instance(**self.test_instance)
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
self.assertRaises(exception.QemuGuestAgentNotEnabled,
|
||||||
|
drvr.set_admin_password, instance, "123")
|
||||||
|
|
||||||
|
@mock.patch('nova.utils.get_image_from_system_metadata')
|
||||||
|
@mock.patch.object(host.Host,
|
||||||
|
'has_min_version', return_value=True)
|
||||||
|
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
|
||||||
|
def test_set_admin_password_error(self, mock_get_guest, ver, mock_image):
|
||||||
|
self.flags(virt_type='kvm', group='libvirt')
|
||||||
|
instance = objects.Instance(**self.test_instance)
|
||||||
|
mock_image.return_value = {"properties": {
|
||||||
|
"hw_qemu_guest_agent": "yes"}}
|
||||||
|
mock_guest = mock.Mock(spec=libvirt_guest.Guest)
|
||||||
|
mock_guest.set_user_password.side_effect = (
|
||||||
|
fakelibvirt.libvirtError("error"))
|
||||||
|
mock_get_guest.return_value = mock_guest
|
||||||
|
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
self.assertRaises(exception.NovaException,
|
||||||
|
drvr.set_admin_password, instance, "123")
|
||||||
|
|
||||||
@mock.patch.object(objects.Service, 'get_by_compute_host')
|
@mock.patch.object(objects.Service, 'get_by_compute_host')
|
||||||
def test_set_host_enabled_with_disable(self, mock_svc):
|
def test_set_host_enabled_with_disable(self, mock_svc):
|
||||||
# Tests disabling an enabled host.
|
# Tests disabling an enabled host.
|
||||||
|
|
|
@ -253,6 +253,10 @@ class GuestTestCase(test.NoDBTestCase):
|
||||||
self.assertEqual(disk, gblock._disk)
|
self.assertEqual(disk, gblock._disk)
|
||||||
self.assertEqual(self.guest, gblock._guest)
|
self.assertEqual(self.guest, gblock._guest)
|
||||||
|
|
||||||
|
def test_set_user_password(self):
|
||||||
|
self.guest.set_user_password("foo", "123")
|
||||||
|
self.domain.setUserPassword.assert_called_once_with("foo", "123", 0)
|
||||||
|
|
||||||
|
|
||||||
class GuestBlockTestCase(test.NoDBTestCase):
|
class GuestBlockTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
|
|
|
@ -919,3 +919,13 @@ class LibvirtConnTestCase(_VirtDriverTestCase, test.TestCase):
|
||||||
|
|
||||||
def test_get_device_name_for_instance(self):
|
def test_get_device_name_for_instance(self):
|
||||||
self.skipTest("Tested by the nova.tests.unit.virt.libvirt suite")
|
self.skipTest("Tested by the nova.tests.unit.virt.libvirt suite")
|
||||||
|
|
||||||
|
@catch_notimplementederror
|
||||||
|
@mock.patch('nova.utils.get_image_from_system_metadata')
|
||||||
|
@mock.patch("nova.virt.libvirt.host.Host.has_min_version")
|
||||||
|
def test_set_admin_password(self, ver, mock_image):
|
||||||
|
self.flags(virt_type='kvm', group='libvirt')
|
||||||
|
mock_image.return_value = {"properties": {
|
||||||
|
"hw_qemu_guest_agent": "yes"}}
|
||||||
|
instance, network_info = self._get_running_instance(obj=True)
|
||||||
|
self.connection.set_admin_password(instance, 'p4ssw0rd')
|
||||||
|
|
|
@ -394,6 +394,9 @@ MIN_QEMU_HYPERV_FEATURE_VERSION = (1, 1, 0)
|
||||||
# parallels driver support
|
# parallels driver support
|
||||||
MIN_LIBVIRT_PARALLELS_VERSION = (1, 2, 12)
|
MIN_LIBVIRT_PARALLELS_VERSION = (1, 2, 12)
|
||||||
|
|
||||||
|
# Ability to set the user guest password with Qemu
|
||||||
|
MIN_LIBVIRT_SET_ADMIN_PASSWD = (1, 2, 16)
|
||||||
|
|
||||||
|
|
||||||
class LibvirtDriver(driver.ComputeDriver):
|
class LibvirtDriver(driver.ComputeDriver):
|
||||||
capabilities = {
|
capabilities = {
|
||||||
|
@ -1413,6 +1416,34 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
LOG.info(_LI("Snapshot image upload complete"),
|
LOG.info(_LI("Snapshot image upload complete"),
|
||||||
instance=instance)
|
instance=instance)
|
||||||
|
|
||||||
|
def _can_set_admin_password(self, image_meta):
|
||||||
|
if (CONF.libvirt.virt_type not in ('kvm', 'qemu') or
|
||||||
|
not self._host.has_min_version(MIN_LIBVIRT_SET_ADMIN_PASSWD)):
|
||||||
|
raise exception.SetAdminPasswdNotSupported()
|
||||||
|
|
||||||
|
hw_qga = image_meta.properties.get('hw_qemu_guest_agent', '')
|
||||||
|
if not strutils.bool_from_string(hw_qga):
|
||||||
|
raise exception.QemuGuestAgentNotEnabled()
|
||||||
|
|
||||||
|
def set_admin_password(self, instance, new_pass):
|
||||||
|
image_meta = objects.ImageMeta.from_instance(instance)
|
||||||
|
self._can_set_admin_password(image_meta)
|
||||||
|
|
||||||
|
guest = self._host.get_guest(instance)
|
||||||
|
if instance.os_type == "windows":
|
||||||
|
user = "Administrator"
|
||||||
|
else:
|
||||||
|
user = "root"
|
||||||
|
|
||||||
|
try:
|
||||||
|
guest.set_user_password(user, new_pass)
|
||||||
|
except libvirt.libvirtError as ex:
|
||||||
|
error_code = ex.get_error_code()
|
||||||
|
msg = (_('Error from libvirt while set password for username '
|
||||||
|
'"%(user)s": [Error Code %(error_code)s] %(ex)s')
|
||||||
|
% {'user': user, 'error_code': error_code, 'ex': ex})
|
||||||
|
raise exception.NovaException(msg)
|
||||||
|
|
||||||
def _can_quiesce(self, instance, image_meta):
|
def _can_quiesce(self, instance, image_meta):
|
||||||
if (CONF.libvirt.virt_type not in ('kvm', 'qemu') or
|
if (CONF.libvirt.virt_type not in ('kvm', 'qemu') or
|
||||||
not self._host.has_min_version(MIN_LIBVIRT_FSFREEZE_VERSION)):
|
not self._host.has_min_version(MIN_LIBVIRT_FSFREEZE_VERSION)):
|
||||||
|
|
|
@ -256,6 +256,10 @@ class Guest(object):
|
||||||
"""Returns a block device wrapper for disk."""
|
"""Returns a block device wrapper for disk."""
|
||||||
return BlockDevice(self, disk)
|
return BlockDevice(self, disk)
|
||||||
|
|
||||||
|
def set_user_password(self, user, new_pass):
|
||||||
|
"""Configures a new user password."""
|
||||||
|
self._domain.setUserPassword(user, new_pass, 0)
|
||||||
|
|
||||||
|
|
||||||
class BlockDevice(object):
|
class BlockDevice(object):
|
||||||
"""Wrapper around block device API"""
|
"""Wrapper around block device API"""
|
||||||
|
|
Loading…
Reference in New Issue