Merge "XenAPI: Add support for XenServer VMs"
This commit is contained in:
commit
23ea858539
@ -118,8 +118,17 @@ CIMC driver
|
||||
|
||||
OneView driver
|
||||
--------------
|
||||
|
||||
=======
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
../drivers/oneview
|
||||
|
||||
|
||||
XenServer ssh driver
|
||||
--------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
../drivers/xenserver
|
||||
|
41
doc/source/drivers/xenserver.rst
Normal file
41
doc/source/drivers/xenserver.rst
Normal file
@ -0,0 +1,41 @@
|
||||
.. _xenserver:
|
||||
.. _bug 1498576: https://bugs.launchpad.net/diskimage-builder/+bug/1498576
|
||||
|
||||
=================
|
||||
XenServer drivers
|
||||
=================
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
XenServer drivers can be used to deploy hosts with Ironic by using XenServer
|
||||
VMs to simulate bare metal nodes.
|
||||
|
||||
Ironic provides support via the ``pxe_ssh`` and ``agent_ssh`` drivers for
|
||||
using a XenServer VM as a bare metal target and do provisioning on it. It
|
||||
works by connecting via SSH into the XenServer host and running commands using
|
||||
the 'xe' command.
|
||||
|
||||
This is particularly useful for deploying overclouds that use XenServer for VM
|
||||
hosting as the Compute node must be run as a virtual machine on the XenServer
|
||||
host it will be controlling. In this case, one VM per hypervisor needs to be
|
||||
installed.
|
||||
|
||||
This support has been tested with XenServer 6.5.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
* Install the VMs using the "Other Install Media" template, which will ensure
|
||||
that they are HVM guests
|
||||
|
||||
* Set the HVM guests to boot from network first
|
||||
|
||||
* If your generated initramfs does not have the fix for `bug 1498576`_,
|
||||
disable the Xen PV drivers as a work around
|
||||
|
||||
::
|
||||
|
||||
xe vm-param-set uuid=<uuid> xenstore-data:vm-data="vm_data/disable_pf: 1"
|
||||
|
||||
|
@ -25,6 +25,7 @@ Currently supported environments are:
|
||||
Virsh (virsh)
|
||||
VMware (vmware)
|
||||
Parallels (parallels)
|
||||
XenServer (xenserver)
|
||||
"""
|
||||
|
||||
import os
|
||||
@ -71,7 +72,7 @@ REQUIRED_PROPERTIES = {
|
||||
"Required."),
|
||||
'ssh_username': _("username to authenticate as. Required."),
|
||||
'ssh_virt_type': _("virtualization software to use; one of vbox, virsh, "
|
||||
"vmware, parallels. Required.")
|
||||
"vmware, parallels, xenserver. Required.")
|
||||
}
|
||||
OTHER_PROPERTIES = {
|
||||
'ssh_key_contents': _("private key(s). One of this, ssh_key_filename, "
|
||||
@ -107,6 +108,12 @@ def _get_boot_device_map(virt_type):
|
||||
boot_devices.PXE: 'net',
|
||||
boot_devices.CDROM: 'dvd',
|
||||
}
|
||||
elif virt_type == 'xenserver':
|
||||
return {
|
||||
boot_devices.DISK: 'c',
|
||||
boot_devices.PXE: 'n',
|
||||
boot_devices.CDROM: 'd',
|
||||
}
|
||||
elif virt_type == 'parallels':
|
||||
return {
|
||||
boot_devices.DISK: 'hdd0',
|
||||
@ -228,6 +235,32 @@ def _get_command_sets(virt_type):
|
||||
"{_BaseCmd_} list -i {_NodeName_} | "
|
||||
"awk '/^Boot order:/ {print $3}'"),
|
||||
}
|
||||
elif virt_type == 'xenserver':
|
||||
return {
|
||||
'base_cmd': 'LC_ALL=C /opt/xensource/bin/xe',
|
||||
# Note(bobba): XenServer appears to have a condition where
|
||||
# vm-start can return before the power-state
|
||||
# has been updated to 'running'. Ironic
|
||||
# expects the power-state to be updated
|
||||
# immediately, so may find that power-state
|
||||
# is still 'halted' and attempt to start the
|
||||
# VM a second time. Sleep to avoid the race.
|
||||
'start_cmd': 'vm-start uuid={_NodeName_} && sleep 10s',
|
||||
'stop_cmd': 'vm-shutdown uuid={_NodeName_} force=true',
|
||||
'list_all': "vm-list --minimal | tr ',' '\n'",
|
||||
'list_running': (
|
||||
"vm-list power-state=running --minimal |"
|
||||
" tr ',' '\n'"),
|
||||
'get_node_macs': (
|
||||
"vif-list vm-uuid={_NodeName_}"
|
||||
" params=MAC --minimal | tr ',' '\n'"),
|
||||
'set_boot_device': (
|
||||
"{_BaseCmd_} vm-param-set uuid={_NodeName_}"
|
||||
" HVM-boot-params:order='{_BootDevice_}'"),
|
||||
'get_boot_device': (
|
||||
"{_BaseCmd_} vm-param-get uuid={_NodeName_}"
|
||||
" --param-name=HVM-boot-params param-key=order | cut -b 1"),
|
||||
}
|
||||
else:
|
||||
raise exception.InvalidParameterValue(_(
|
||||
"SSHPowerDriver '%(virt_type)s' is not a valid virt_type, ") %
|
||||
|
@ -185,6 +185,10 @@ class SSHValidateParametersTestCase(db_base.DbTestCase):
|
||||
boot_map = ssh._get_boot_device_map('vbox')
|
||||
self.assertEqual('net', boot_map[boot_devices.PXE])
|
||||
|
||||
def test__get_boot_device_map_xenserver(self):
|
||||
boot_map = ssh._get_boot_device_map('xenserver')
|
||||
self.assertEqual('n', boot_map[boot_devices.PXE])
|
||||
|
||||
def test__get_boot_device_map_exception(self):
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
ssh._get_boot_device_map,
|
||||
@ -859,6 +863,23 @@ class SSHDriverTestCase(db_base.DbTestCase):
|
||||
'edit %s') % fake_name
|
||||
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
|
||||
|
||||
@mock.patch.object(ssh, '_get_connection', autospec=True)
|
||||
@mock.patch.object(ssh, '_get_hosts_name_for_node', autospec=True)
|
||||
@mock.patch.object(ssh, '_ssh_execute', autospec=True)
|
||||
def test_management_interface_set_boot_device_xenserver_ok(self,
|
||||
mock_exc,
|
||||
mock_h,
|
||||
mock_get_conn):
|
||||
fake_name = 'fake-name'
|
||||
mock_h.return_value = fake_name
|
||||
mock_get_conn.return_value = self.sshclient
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node['driver_info']['ssh_virt_type'] = 'xenserver'
|
||||
self.driver.management.set_boot_device(task, boot_devices.PXE)
|
||||
expected_cmd = ("LC_ALL=C /opt/xensource/bin/xe vm-param-set uuid=%s "
|
||||
"HVM-boot-params:order='n'") % fake_name
|
||||
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
|
||||
|
||||
def test_set_boot_device_bad_device(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
@ -941,6 +962,25 @@ class SSHDriverTestCase(db_base.DbTestCase):
|
||||
'print; }\' Q="\'" RS="[<>]" | head -1') % fake_name
|
||||
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
|
||||
|
||||
@mock.patch.object(ssh, '_get_connection', autospec=True)
|
||||
@mock.patch.object(ssh, '_get_hosts_name_for_node', autospec=True)
|
||||
@mock.patch.object(ssh, '_ssh_execute', autospec=True)
|
||||
def test_management_interface_get_boot_device_xenserver(self, mock_exc,
|
||||
mock_h,
|
||||
mock_get_conn):
|
||||
fake_name = 'fake-name'
|
||||
mock_h.return_value = fake_name
|
||||
mock_exc.return_value = ('n', '')
|
||||
mock_get_conn.return_value = self.sshclient
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node['driver_info']['ssh_virt_type'] = 'xenserver'
|
||||
result = self.driver.management.get_boot_device(task)
|
||||
self.assertEqual(boot_devices.PXE, result['boot_device'])
|
||||
expected_cmd = ('LC_ALL=C /opt/xensource/bin/xe vm-param-get '
|
||||
'uuid=%s --param-name=HVM-boot-params '
|
||||
'param-key=order | cut -b 1') % fake_name
|
||||
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
|
||||
|
||||
@mock.patch.object(ssh, '_get_connection', autospec=True)
|
||||
@mock.patch.object(ssh, '_get_hosts_name_for_node', autospec=True)
|
||||
def test_get_boot_device_not_supported(self, mock_h, mock_get_conn):
|
||||
@ -972,6 +1012,57 @@ class SSHDriverTestCase(db_base.DbTestCase):
|
||||
"echo '\"%(node)s\"' || true") % {'node': nodename}
|
||||
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
|
||||
|
||||
@mock.patch.object(ssh, '_get_connection', autospec=True)
|
||||
@mock.patch.object(ssh, '_get_hosts_name_for_node', autospec=True)
|
||||
@mock.patch.object(ssh, '_ssh_execute', autospec=True)
|
||||
def test_get_power_state_xenserver(self, mock_exc, mock_h, mock_get_conn):
|
||||
# To see replacing {_NodeName_} in xenserver's list_running
|
||||
nodename = 'fakevm'
|
||||
mock_h.return_value = nodename
|
||||
mock_get_conn.return_value = self.sshclient
|
||||
mock_exc.return_value = (nodename, '')
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node['driver_info']['ssh_virt_type'] = 'xenserver'
|
||||
power_state = self.driver.power.get_power_state(task)
|
||||
self.assertEqual(states.POWER_ON, power_state)
|
||||
expected_cmd = ("LC_ALL=C /opt/xensource/bin/xe "
|
||||
"vm-list power-state=running --minimal | tr ',' '\n'")
|
||||
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
|
||||
|
||||
@mock.patch.object(ssh, '_get_connection', autospec=True)
|
||||
@mock.patch.object(ssh, '_get_hosts_name_for_node', autospec=True)
|
||||
@mock.patch.object(ssh, '_ssh_execute', autospec=True)
|
||||
@mock.patch.object(ssh, '_get_power_status', autospec=True)
|
||||
def test_start_command_xenserver(self, mock_power, mock_exc, mock_h,
|
||||
mock_get_conn):
|
||||
mock_power.side_effect = [states.POWER_OFF, states.POWER_ON]
|
||||
nodename = 'fakevm'
|
||||
mock_h.return_value = nodename
|
||||
mock_get_conn.return_value = self.sshclient
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node['driver_info']['ssh_virt_type'] = 'xenserver'
|
||||
self.driver.power.set_power_state(task, states.POWER_ON)
|
||||
expected_cmd = ("LC_ALL=C /opt/xensource/bin/xe "
|
||||
"vm-start uuid=fakevm && sleep 10s")
|
||||
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
|
||||
|
||||
@mock.patch.object(ssh, '_get_connection', autospec=True)
|
||||
@mock.patch.object(ssh, '_get_hosts_name_for_node', autospec=True)
|
||||
@mock.patch.object(ssh, '_ssh_execute', autospec=True)
|
||||
@mock.patch.object(ssh, '_get_power_status', autospec=True)
|
||||
def test_stop_command_xenserver(self, mock_power, mock_exc, mock_h,
|
||||
mock_get_conn):
|
||||
mock_power.side_effect = [states.POWER_ON, states.POWER_OFF]
|
||||
nodename = 'fakevm'
|
||||
mock_h.return_value = nodename
|
||||
mock_get_conn.return_value = self.sshclient
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node['driver_info']['ssh_virt_type'] = 'xenserver'
|
||||
self.driver.power.set_power_state(task, states.POWER_OFF)
|
||||
expected_cmd = ("LC_ALL=C /opt/xensource/bin/xe "
|
||||
"vm-shutdown uuid=fakevm force=true")
|
||||
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
|
||||
|
||||
def test_management_interface_validate_good(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.management.validate(task)
|
||||
|
Loading…
x
Reference in New Issue
Block a user