Add Parallels virtualisation type

This change adds Parallels support to Ironic ssh driver

Change-Id: I5b8ee34f63fd00f9874ce0fe11219224c785c42f
Closes-Bug: #1358401
This commit is contained in:
Davide Guerri 2014-08-16 02:38:40 +01:00
parent edc9bb038a
commit 75b3568edc
2 changed files with 93 additions and 7 deletions

View File

@ -23,6 +23,7 @@ For use in dev and test environments.
Currently supported environments are:
Virtual Box (vbox)
Virsh (virsh)
Parallels (parallels)
"""
import os
@ -60,7 +61,7 @@ REQUIRED_PROPERTIES = {
"Required."),
'ssh_username': _("username to authenticate as. Required."),
'ssh_virt_type': _("virtualization software to use; one of vbox, virsh, "
"vmware. Required.")
"vmware, parallels. Required.")
}
OTHER_PROPERTIES = {
'ssh_key_contents': _("private key(s). One of this, ssh_key_filename, "
@ -76,6 +77,10 @@ OTHER_PROPERTIES = {
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
COMMON_PROPERTIES.update(OTHER_PROPERTIES)
# NOTE(dguerri) Generic boot device map. Virtualisation types that don't define
# a more specific one, will use this.
# This is left for compatibility with other modules and is still valid for
# vbox, virsh and vmware.
_BOOT_DEVICES_MAP = {
boot_devices.DISK: 'hd',
boot_devices.PXE: 'network',
@ -83,6 +88,21 @@ _BOOT_DEVICES_MAP = {
}
def _get_boot_device_map(virt_type):
if virt_type in ('vbox', 'virsh', 'vmware'):
return _BOOT_DEVICES_MAP
elif virt_type == 'parallels':
return {
boot_devices.DISK: 'hdd0',
boot_devices.PXE: 'net0',
boot_devices.CDROM: 'cdrom0',
}
else:
raise exception.InvalidParameterValue(_(
"SSHPowerDriver '%(virt_type)s' is not a valid virt_type.") %
{'virt_type': virt_type})
def _get_command_sets(virt_type):
if virt_type == 'vbox':
return {
@ -153,6 +173,24 @@ def _get_command_sets(virt_type):
virsh_cmds['base_cmd'] += ' --connect %s' % CONF.ssh.libvirt_uri
return virsh_cmds
elif virt_type == 'parallels':
return {
'base_cmd': '/usr/bin/prlctl',
'start_cmd': 'start {_NodeName_}',
'stop_cmd': 'stop {_NodeName_} --kill',
'reboot_cmd': 'reset {_NodeName_}',
'list_all': "list -a -o name |tail -n +2",
'list_running': 'list -o name |tail -n +2',
'get_node_macs': ("list -j -i \"{_NodeName_}\" | "
"awk -F'\"' '/\"mac\":/ {print $4}' | "
"sed 's/\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)/"
"\\1:\\2:\\3:\\4:\\5\\6/' | "
"tr '[:upper:]' '[:lower:]'"),
'set_boot_device': ("{_BaseCmd_} set {_NodeName_} "
"--device-bootorder \"{_BootDevice_}\""),
'get_boot_device': ("{_BaseCmd_} list -i {_NodeName_} | "
"awk '/^Boot order:/ {print $3}'"),
}
else:
raise exception.InvalidParameterValue(_(
"SSHPowerDriver '%(virt_type)s' is not a valid virt_type, ") %
@ -175,12 +213,13 @@ def _get_boot_device(ssh_obj, driver_info):
"""
cmd_to_exec = driver_info['cmd_set'].get('get_boot_device')
if cmd_to_exec:
boot_device_map = _get_boot_device_map(driver_info['virt_type'])
node_name = _get_hosts_name_for_node(ssh_obj, driver_info)
base_cmd = driver_info['cmd_set']['base_cmd']
cmd_to_exec = cmd_to_exec.replace('{_NodeName_}', node_name)
cmd_to_exec = cmd_to_exec.replace('{_BaseCmd_}', base_cmd)
stdout, stderr = _ssh_execute(ssh_obj, cmd_to_exec)
return next((dev for dev, hdev in _BOOT_DEVICES_MAP.items()
return next((dev for dev, hdev in boot_device_map.items()
if hdev == stdout), None)
else:
raise NotImplementedError()
@ -596,8 +635,10 @@ class SSHManagement(base.ManagementInterface):
"Invalid boot device %s specified.") % device)
driver_info['macs'] = driver_utils.get_node_mac_addresses(task)
ssh_obj = _get_connection(node)
boot_device_map = _get_boot_device_map(driver_info['virt_type'])
try:
_set_boot_device(ssh_obj, driver_info, _BOOT_DEVICES_MAP[device])
_set_boot_device(ssh_obj, driver_info, boot_device_map[device])
except NotImplementedError:
LOG.error(_LE("Failed to set boot device for node %(node)s, "
"virt_type %(vtype)s does not support this "

View File

@ -181,6 +181,15 @@ class SSHValidateParametersTestCase(base.TestCase):
info = ssh._parse_driver_info(node)
self.assertEqual(expected_base_cmd, info['cmd_set']['base_cmd'])
def test__get_boot_device_map_ad_hoc(self):
boot_map = ssh._get_boot_device_map('parallels')
self.assertEqual('net0', boot_map[boot_devices.PXE])
def test__get_boot_device_map_exception(self):
self.assertRaises(exception.InvalidParameterValue,
ssh._get_boot_device_map,
'this_doesn_t_exist')
class SSHPrivateMethodsTestCase(base.TestCase):
@ -784,8 +793,25 @@ class SSHDriverTestCase(db_base.DbTestCase):
@mock.patch.object(ssh, '_get_connection')
@mock.patch.object(ssh, '_get_hosts_name_for_node')
@mock.patch.object(ssh, '_ssh_execute')
def test_management_interface_set_boot_device_ok(self, mock_exc, mock_h,
mock_get_conn):
def test_management_interface_set_boot_device_parallels_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'] = 'parallels'
self.driver.management.set_boot_device(task, boot_devices.PXE)
expected_cmd = ('/usr/bin/prlctl set %s '
'--device-bootorder "net0"') % fake_name
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
@mock.patch.object(ssh, '_get_connection')
@mock.patch.object(ssh, '_get_hosts_name_for_node')
@mock.patch.object(ssh, '_ssh_execute')
def test_management_interface_set_boot_device_virsh_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
@ -826,8 +852,27 @@ class SSHDriverTestCase(db_base.DbTestCase):
@mock.patch.object(ssh, '_get_connection')
@mock.patch.object(ssh, '_get_hosts_name_for_node')
@mock.patch.object(ssh, '_ssh_execute')
def test_management_interface_get_boot_device(self, mock_exc, mock_h,
mock_get_conn):
def test_management_interface_get_boot_device_parallels(self, mock_exc,
mock_h,
mock_get_conn):
fake_name = 'fake-name'
mock_h.return_value = fake_name
mock_exc.return_value = ('net0', '')
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'] = 'parallels'
result = self.driver.management.get_boot_device(task)
self.assertEqual(boot_devices.PXE, result['boot_device'])
expected_cmd = ('/usr/bin/prlctl list -i %s '
'| awk \'/^Boot order:/ {print $3}\'') % fake_name
mock_exc.assert_called_once_with(mock.ANY, expected_cmd)
@mock.patch.object(ssh, '_get_connection')
@mock.patch.object(ssh, '_get_hosts_name_for_node')
@mock.patch.object(ssh, '_ssh_execute')
def test_management_interface_get_boot_device_virsh(self, mock_exc,
mock_h,
mock_get_conn):
fake_name = 'fake-name'
mock_h.return_value = fake_name
mock_exc.return_value = ('network', '')