
The lab-setup files are refactored, as detailed below. In addition, recovery, administration and logging improvements are implemented. The following lab-setup files are removed: - lab_setup1.sh - lab_setup2.sh The corresponding code, previously run locally in the VM, is now integrated to the main Python code. The files lab_setup.sh and lab_setup.conf are kept, because they are useful to populate the stx-openStack application. These should be reviewed by a new task under the context of stx-openStack. Test Plan - AIO-SX Virtual Deployment (PASS) - AIO-DX Virtual Deployment (PASS) Story: 2005051 Task: 48402 Change-Id: I940e5a16ea98a4325efe1ee0dd45127674d6b192 Signed-off-by: Roger Ferraz <rogerio.ferraz@encora.com>
642 lines
20 KiB
Python
642 lines
20 KiB
Python
import install_vbox
|
|
import unittest
|
|
|
|
from consts.networking import OAM, MGMT
|
|
from dataclasses import dataclass
|
|
from unittest.mock import MagicMock, patch, call, ANY
|
|
|
|
|
|
# Network
|
|
OAM_CONFIG = [getattr(OAM, attr) for attr in dir(OAM) if not attr.startswith('__')]
|
|
MGMT_CONFIG = [getattr(MGMT, attr) for attr in dir(MGMT) if not attr.startswith('__')]
|
|
|
|
|
|
@dataclass
|
|
class VirtualBoxOptions:
|
|
vboxnet_type: str
|
|
labname: str
|
|
|
|
controllers: int
|
|
workers: int
|
|
storages: int
|
|
|
|
username: str
|
|
password: str
|
|
|
|
force_delete_lab: str
|
|
|
|
setup_type: str
|
|
securityprofile: str
|
|
lowlatency: str
|
|
install_mode: str
|
|
|
|
controller0_ip: str
|
|
controller1_ip: str
|
|
vboxnet_ip: str
|
|
|
|
config_controller_ini: str
|
|
ini_oam_cidr: str
|
|
ini_oam_ip_start_address: str
|
|
ini_oam_ip_end_address: str
|
|
|
|
ansible_controller_config: str
|
|
|
|
nat_controller_floating_local_ssh_port: str
|
|
nat_controller0_local_ssh_port: str
|
|
nat_controller_1_local_ssh_port: str
|
|
controller_floating_ip: str
|
|
|
|
config_files_dir: str
|
|
config_files_dir_dont_follow_links: str
|
|
lab_setup_conf: str
|
|
|
|
script1: str
|
|
script2: str
|
|
script3: str
|
|
script4: str
|
|
script5: str
|
|
|
|
hostiocache: str
|
|
|
|
list_stages: str
|
|
logpath: str
|
|
custom_stages: str
|
|
from_stage: str
|
|
to_stage: str
|
|
|
|
snapshot: str
|
|
|
|
|
|
class MenuSelectorTestCase(unittest.TestCase):
|
|
"""
|
|
Class to test menu_selector method
|
|
"""
|
|
|
|
@patch("install_vbox.serial")
|
|
def test_menu_selector(self, mock_serial):
|
|
"""
|
|
Test menu_selector method
|
|
"""
|
|
|
|
# Setup
|
|
mock_stream = MagicMock()
|
|
setup_type_aio_sx = "AIO-SX"
|
|
setup_type_aio_dx = "AIO-DX"
|
|
setup_type_other = "OTHER"
|
|
security_profile_extended = "extended"
|
|
security_profile_other = "other"
|
|
low_latency_true = True
|
|
low_latency_false = False
|
|
install_mode_serial = "serial"
|
|
install_mode_graphical = "graphical"
|
|
|
|
permutations = [
|
|
((setup_type_aio_sx, security_profile_extended, low_latency_true, install_mode_serial), 6),
|
|
((setup_type_aio_sx, security_profile_other, low_latency_false, install_mode_serial), 4),
|
|
((setup_type_aio_dx, security_profile_extended, low_latency_true, install_mode_graphical), 7),
|
|
((setup_type_aio_dx, security_profile_other, low_latency_false, install_mode_graphical), 5),
|
|
((setup_type_other, security_profile_extended, low_latency_true, install_mode_serial), 4),
|
|
((setup_type_other, security_profile_other, low_latency_false, install_mode_graphical), 4)
|
|
]
|
|
|
|
# Run
|
|
accumulated_calls = 0
|
|
for permutation, calls_number in permutations:
|
|
with self.subTest(permutation=permutation):
|
|
install_vbox.menu_selector(mock_stream, *permutation)
|
|
|
|
# Assert
|
|
accumulated_calls += calls_number
|
|
self.assertEqual(mock_serial.send_bytes.call_count, accumulated_calls)
|
|
|
|
|
|
class SetupNetworkingTestCase(unittest.TestCase):
|
|
"""
|
|
Class to test setup_networking method
|
|
"""
|
|
|
|
mock_stream = MagicMock()
|
|
mock_ip = "192.168.1.1"
|
|
mock_gateway_ip = "192.168.1.254"
|
|
mock_password = "password"
|
|
|
|
@patch("install_vbox.serial")
|
|
@patch("install_vbox.LOG")
|
|
@patch("install_vbox.subprocess.call")
|
|
@patch("install_vbox.host_helper")
|
|
def test_setup_networking(
|
|
self,
|
|
mock_host_helper,
|
|
mock_subprocess_call,
|
|
mock_log,
|
|
mock_serial,
|
|
):
|
|
"""
|
|
Test setup_networking
|
|
"""
|
|
|
|
# Setup
|
|
mock_subprocess_call.return_value = 0
|
|
v_box = VirtualBoxOptions
|
|
v_box.vboxnet_type = "hostonly"
|
|
install_vbox.V_BOX_OPTIONS = v_box
|
|
|
|
# Run
|
|
install_vbox.setup_networking(self.mock_stream, self.mock_ip, self.mock_gateway_ip, password=self.mock_password)
|
|
|
|
# Assert
|
|
mock_serial.send_bytes.assert_any_call(self.mock_stream,
|
|
"/sbin/ip address list",
|
|
prompt=self.mock_ip,
|
|
fail_ok=True,
|
|
timeout=10)
|
|
mock_host_helper.check_password.assert_has_calls([
|
|
call(self.mock_stream, password=self.mock_password),
|
|
call(self.mock_stream, password=self.mock_password),
|
|
call(self.mock_stream, password=self.mock_password)
|
|
])
|
|
oam_if = OAM_CONFIG[0]['device']
|
|
mock_serial.send_bytes.assert_any_call(self.mock_stream,
|
|
f"sudo /sbin/ip addr add {self.mock_ip}/24 dev {oam_if}",
|
|
expect_prompt=False)
|
|
mock_serial.send_bytes.assert_any_call(self.mock_stream,
|
|
f"sudo /sbin/ip link set {oam_if} up",
|
|
expect_prompt=False)
|
|
mock_serial.send_bytes.assert_any_call(self.mock_stream,
|
|
f"sudo route add default gw {self.mock_gateway_ip}",
|
|
expect_prompt=False)
|
|
self.assertEqual(mock_subprocess_call.call_args_list, [call(['ping', '-c', '1', self.mock_ip])])
|
|
mock_log.info.assert_any_call("Ping succeeded!")
|
|
|
|
|
|
class FixNetworkingTestCase(unittest.TestCase):
|
|
"""
|
|
Class to test fix_networking method
|
|
"""
|
|
|
|
mock_stream = MagicMock()
|
|
mock_release_r2 = "R2"
|
|
mock_release_r3 = "R3"
|
|
mock_password = "Li69nux*"
|
|
|
|
@patch("install_vbox.serial")
|
|
@patch("install_vbox.host_helper")
|
|
def test_fix_networking_r2(self, mock_host_helper, mock_serial):
|
|
"""
|
|
Test fix_networking for release R2
|
|
"""
|
|
|
|
# Run
|
|
install_vbox.fix_networking(self.mock_stream, self.mock_release_r2, self.mock_password)
|
|
|
|
# Assert
|
|
mock_serial.send_bytes.assert_any_call(self.mock_stream,
|
|
"sudo /sbin/ip link set eth0 down",
|
|
expect_prompt=False)
|
|
mock_host_helper.check_password.assert_called_with(self.mock_stream, password=self.mock_password)
|
|
mock_serial.send_bytes.assert_any_call(
|
|
self.mock_stream,
|
|
"sudo /sbin/ip link set eth0 up",
|
|
expect_prompt=False)
|
|
mock_host_helper.check_password.assert_called_with(self.mock_stream, password=self.mock_password)
|
|
|
|
@patch("install_vbox.serial")
|
|
@patch("install_vbox.host_helper")
|
|
def test_fix_networking_not_r2(self, mock_host_helper, mock_serial):
|
|
"""
|
|
Test fix_networking for releases other than R2
|
|
"""
|
|
|
|
# Run
|
|
install_vbox.fix_networking(self.mock_stream, self.mock_release_r3, self.mock_password)
|
|
|
|
# Assert
|
|
oam_if = OAM_CONFIG[0]['device']
|
|
mock_serial.send_bytes.assert_any_call(self.mock_stream,
|
|
f"sudo /sbin/ip link set {oam_if} down",
|
|
expect_prompt=False)
|
|
mock_host_helper.check_password.assert_called_with(self.mock_stream, password=self.mock_password)
|
|
mock_serial.send_bytes.assert_any_call(
|
|
self.mock_stream,
|
|
f"sudo /sbin/ip link set {oam_if} up",
|
|
expect_prompt=False)
|
|
mock_host_helper.check_password.assert_called_with(self.mock_stream, password=self.mock_password)
|
|
|
|
|
|
class InstallController0TestCase(unittest.TestCase):
|
|
"""
|
|
Class to test install_controller_0 method
|
|
"""
|
|
|
|
mock_stream = MagicMock()
|
|
mock_menu_select_dict = {
|
|
"setup_type": "Duplex",
|
|
"securityprofile": "Standard",
|
|
"lowlatency": False,
|
|
"install_mode": "standard"
|
|
}
|
|
mock_network_dict = {
|
|
"ctrlr0_ip": "192.168.1.2",
|
|
"gateway_ip": "192.168.1.1",
|
|
"username": "wrsroot",
|
|
"password": "Li69nux*"
|
|
}
|
|
|
|
@patch("install_vbox.serial")
|
|
@patch("install_vbox.host_helper")
|
|
@patch("install_vbox.menu_selector")
|
|
@patch("install_vbox.setup_networking")
|
|
def test_install_controller_0(
|
|
self, mock_setup_networking, mock_menu_selector, mock_host_helper, mock_serial
|
|
):
|
|
"""
|
|
Test install_controller_0
|
|
"""
|
|
|
|
# Run
|
|
install_vbox.install_controller_0(self.mock_stream, self.mock_menu_select_dict, self.mock_network_dict)
|
|
|
|
# Assert
|
|
mock_menu_selector.assert_called_once_with(
|
|
self.mock_stream,
|
|
self.mock_menu_select_dict["setup_type"],
|
|
self.mock_menu_select_dict["securityprofile"],
|
|
self.mock_menu_select_dict["lowlatency"],
|
|
self.mock_menu_select_dict["install_mode"]
|
|
)
|
|
mock_serial.expect_bytes.assert_called_with(
|
|
self.mock_stream,
|
|
"login:",
|
|
timeout=ANY)
|
|
mock_host_helper.change_password.assert_called_once_with(
|
|
self.mock_stream,
|
|
username=self.mock_network_dict["username"],
|
|
password=self.mock_network_dict["password"]
|
|
)
|
|
mock_host_helper.disable_logout.assert_called_once_with(self.mock_stream)
|
|
mock_setup_networking.assert_called_once_with(
|
|
self.mock_stream,
|
|
self.mock_network_dict["ctrlr0_ip"],
|
|
self.mock_network_dict["gateway_ip"],
|
|
password=self.mock_network_dict["password"]
|
|
)
|
|
|
|
@patch("serial.LOG.info")
|
|
@patch("install_vbox.serial")
|
|
@patch("install_vbox.host_helper")
|
|
@patch("install_vbox.time")
|
|
@patch("install_vbox.menu_selector")
|
|
@patch("install_vbox.setup_networking")
|
|
def test_install_controller_0_exception(
|
|
self, mock_setup_networking, mock_menu_selector, mock_time, mock_host_helper, mock_serial, mock_log_info
|
|
):
|
|
"""
|
|
Test install_controller_0 when an exception occurs during login
|
|
"""
|
|
|
|
# Setup
|
|
mock_serial.expect_bytes.side_effect = [Exception(), None]
|
|
mock_time.time.return_value = 0
|
|
|
|
# Run
|
|
install_vbox.install_controller_0(self.mock_stream, self.mock_menu_select_dict, self.mock_network_dict)
|
|
|
|
# Assert
|
|
self.assertEqual(mock_serial.expect_bytes.call_count, 2)
|
|
self.assertEqual(mock_serial.expect_bytes.call_args_list[1][1]["timeout"], ANY)
|
|
mock_menu_selector.assert_called_once()
|
|
mock_setup_networking.assert_called_once()
|
|
mock_host_helper.change_password.assert_called_once()
|
|
mock_host_helper.disable_logout.assert_called_once()
|
|
self.assertEqual(mock_log_info.call_count, 4)
|
|
|
|
|
|
class DeleteLabTestCase(unittest.TestCase):
|
|
"""
|
|
Class to test delete_lab method
|
|
"""
|
|
|
|
mock_labname = "test_lab"
|
|
mock_node_list = ["vm1", "vm2", "vm3"]
|
|
|
|
@patch("install_vbox.vboxmanage")
|
|
@patch("install_vbox.LOG")
|
|
@patch("install_vbox.time")
|
|
@patch("install_vbox.input", return_value="y")
|
|
def test_delete_lab_not_force(
|
|
self, mock_input, mock_time, mock_log, mock_vboxmanage
|
|
):
|
|
"""
|
|
Test delete_lab with force=False and user input 'y'
|
|
"""
|
|
|
|
# Setup
|
|
mock_vboxmanage.get_all_vms.return_value = self.mock_node_list
|
|
|
|
# Run
|
|
install_vbox.delete_lab(self.mock_labname, force=False)
|
|
|
|
# Assert
|
|
mock_vboxmanage.get_all_vms.assert_called_once_with(self.mock_labname, option="vms")
|
|
mock_input.assert_called_once_with()
|
|
mock_log.info.assert_has_calls([
|
|
call("This will delete lab %s with vms: %s", self.mock_labname, self.mock_node_list),
|
|
call("Continue? (y/N)"),
|
|
call("#### Deleting lab %s.", self.mock_labname),
|
|
call("VMs in lab: %s.", self.mock_node_list),
|
|
])
|
|
mock_vboxmanage.vboxmanage_controlvms.assert_called_once_with(self.mock_node_list, "poweroff")
|
|
mock_time.sleep.assert_called_once_with(2)
|
|
mock_vboxmanage.vboxmanage_deletevms.assert_called_once_with(self.mock_node_list)
|
|
|
|
@patch("install_vbox.LOG")
|
|
@patch("install_vbox.vboxmanage")
|
|
@patch("install_vbox.input", return_value="n")
|
|
def test_delete_lab_not_force_abort(
|
|
self, mock_input, mock_vboxmanage, mock_log
|
|
):
|
|
"""
|
|
Test delete_lab with force=False and user input 'n'
|
|
"""
|
|
|
|
# Setup
|
|
mock_vboxmanage.get_all_vms.return_value = self.mock_node_list
|
|
|
|
# Run
|
|
with self.assertRaises(SystemExit):
|
|
install_vbox.delete_lab(self.mock_labname, force=False)
|
|
|
|
# Assert
|
|
mock_input.assert_called_once_with()
|
|
mock_log.info.assert_called_with("Aborting!")
|
|
|
|
@patch("install_vbox.vboxmanage")
|
|
@patch("install_vbox.LOG")
|
|
@patch("install_vbox.time")
|
|
def test_delete_lab_force(
|
|
self, mock_time, mock_log, mock_vboxmanage
|
|
):
|
|
"""
|
|
Test delete_lab with force=True
|
|
"""
|
|
|
|
# Setup
|
|
mock_vboxmanage.get_all_vms.return_value = self.mock_node_list
|
|
|
|
# Run
|
|
install_vbox.delete_lab(self.mock_labname, force=True)
|
|
|
|
# Assert
|
|
mock_vboxmanage.get_all_vms.assert_called_once_with(self.mock_labname, option="vms")
|
|
mock_log.info.assert_has_calls([
|
|
call("#### Deleting lab %s.", self.mock_labname),
|
|
call("VMs in lab: %s.", self.mock_node_list),
|
|
])
|
|
mock_vboxmanage.vboxmanage_controlvms.assert_called_once_with(self.mock_node_list, "poweroff")
|
|
mock_time.sleep.assert_called_once_with(2)
|
|
mock_vboxmanage.vboxmanage_deletevms.assert_called_once_with(self.mock_node_list)
|
|
|
|
|
|
class GetDiskSizesTestCase(unittest.TestCase):
|
|
"""
|
|
Class to test get_disk_sizes method
|
|
"""
|
|
|
|
def test_get_disk_sizes_valid_input(self):
|
|
"""
|
|
Test get_disk_sizes with valid input
|
|
"""
|
|
|
|
# Setup
|
|
valid_input = "100,200,300"
|
|
|
|
# Run
|
|
result = install_vbox.get_disk_sizes(valid_input)
|
|
|
|
# Assert
|
|
self.assertEqual(result, ['100', '200', '300'])
|
|
|
|
@patch("install_vbox.LOG")
|
|
def test_get_disk_sizes_invalid_input(self, mock_log):
|
|
"""
|
|
Test get_disk_sizes with invalid input
|
|
"""
|
|
|
|
# Setup
|
|
invalid_input = "-100,200,300"
|
|
|
|
# Assert
|
|
with self.assertRaises(Exception) as context:
|
|
install_vbox.get_disk_sizes(invalid_input)
|
|
|
|
self.assertTrue("Disk sizes must be a comma separated list of positive integers." in str(context.exception))
|
|
mock_log.info.assert_called_once_with("Disk sizes must be a comma separated list of positive integers.")
|
|
|
|
|
|
class TestCreateLab(unittest.TestCase):
|
|
"""
|
|
Class to test create_lab method
|
|
"""
|
|
|
|
# This function needs to be refactored in order to be tested
|
|
pass
|
|
|
|
|
|
class TestGetHostnames(unittest.TestCase):
|
|
"""
|
|
Class to test get_hostnames method
|
|
"""
|
|
|
|
@patch.object(install_vbox, 'V_BOX_OPTIONS', create=True)
|
|
def test_get_hostnames(self, mock_options):
|
|
"""
|
|
Test get_hostnames
|
|
"""
|
|
|
|
# Setup
|
|
mock_options.controllers = 2
|
|
mock_options.workers = 2
|
|
mock_options.storages = 2
|
|
mock_options.labname = "test"
|
|
|
|
expected = {
|
|
'test-controller-0': 'controller-0',
|
|
'test-controller-1': 'controller-1',
|
|
'test-worker-0': f'worker-{id}',
|
|
'test-worker-1': f'worker-{id}',
|
|
'test-storage-0': 'storage-0',
|
|
'test-storage-1': 'storage-1',
|
|
}
|
|
|
|
# Run
|
|
result = install_vbox.get_hostnames()
|
|
|
|
# Assert
|
|
self.assertEqual(result, expected)
|
|
|
|
@patch.object(install_vbox, 'V_BOX_OPTIONS', create=True)
|
|
def test_get_hostnames_with_ignore(self, mock_options):
|
|
"""
|
|
Test get_hostnames with ignore
|
|
"""
|
|
|
|
# Setup
|
|
mock_options.controllers = 2
|
|
mock_options.workers = 2
|
|
mock_options.storages = 2
|
|
mock_options.labname = "test"
|
|
|
|
ignore = ['test-controller-0', 'test-worker-1']
|
|
expected = {
|
|
'test-controller-1': 'controller-1',
|
|
'test-worker-0': f'worker-{id}',
|
|
'test-storage-0': 'storage-0',
|
|
'test-storage-1': 'storage-1',
|
|
}
|
|
|
|
# Run
|
|
result = install_vbox.get_hostnames(ignore=ignore)
|
|
|
|
# Assert
|
|
self.assertEqual(result, expected)
|
|
|
|
@patch.object(install_vbox, 'V_BOX_OPTIONS', create=True)
|
|
def test_get_hostnames_with_selected_personalities(self, mock_options):
|
|
"""
|
|
Test get_hostnames with selected personalities
|
|
"""
|
|
|
|
# Setup
|
|
mock_options.controllers = 2
|
|
mock_options.workers = 2
|
|
mock_options.storages = 2
|
|
mock_options.labname = "test"
|
|
|
|
personalities = ['controller', 'worker']
|
|
expected = {
|
|
'test-controller-0': 'controller-0',
|
|
'test-controller-1': 'controller-1',
|
|
'test-worker-0': f'worker-{id}',
|
|
'test-worker-1': f'worker-{id}',
|
|
}
|
|
|
|
# Run
|
|
result = install_vbox.get_hostnames(personalities=personalities)
|
|
|
|
# Assert
|
|
self.assertEqual(result, expected)
|
|
|
|
|
|
class TestGetPersonalities(unittest.TestCase):
|
|
"""
|
|
Class to test get_personalities method
|
|
"""
|
|
|
|
@patch.object(install_vbox, 'V_BOX_OPTIONS', create=True)
|
|
def test_get_personalities(self, mock_options):
|
|
"""
|
|
Test get_personalities
|
|
"""
|
|
|
|
# Setup
|
|
mock_options.controllers = 2
|
|
mock_options.workers = 2
|
|
mock_options.storages = 2
|
|
mock_options.labname = "test"
|
|
|
|
expected = {
|
|
'test-controller-0': 'controller',
|
|
'test-controller-1': 'controller',
|
|
'test-worker-0': 'worker',
|
|
'test-worker-1': 'worker',
|
|
'test-storage-0': 'storage',
|
|
'test-storage-1': 'storage',
|
|
}
|
|
|
|
# Run and Assert
|
|
self.assertEqual(install_vbox.get_personalities(), expected)
|
|
|
|
@patch.object(install_vbox, 'V_BOX_OPTIONS', create=True)
|
|
def test_get_personalities_with_ignore(self, mock_options):
|
|
"""
|
|
Test get_personalities with ignore
|
|
"""
|
|
|
|
# Setup
|
|
mock_options.controllers = 2
|
|
mock_options.workers = 2
|
|
mock_options.storages = 2
|
|
mock_options.labname = "test"
|
|
|
|
ignore = ['test-controller-0', 'test-worker-1']
|
|
expected = {
|
|
'test-controller-1': 'controller',
|
|
'test-worker-0': 'worker',
|
|
'test-storage-0': 'storage',
|
|
'test-storage-1': 'storage',
|
|
}
|
|
|
|
# Run and Assert
|
|
self.assertEqual(install_vbox.get_personalities(ignore=ignore), expected)
|
|
|
|
|
|
class TestCreateHostBulkAdd(unittest.TestCase):
|
|
"""
|
|
Class to test create_host_bulk_add method
|
|
"""
|
|
|
|
@patch.object(install_vbox, 'V_BOX_OPTIONS', create=True)
|
|
@patch.object(install_vbox, 'vboxmanage', create=True)
|
|
@patch.object(install_vbox, 'get_personalities')
|
|
@patch.object(install_vbox, 'get_hostnames')
|
|
def test_create_host_bulk_add(self, mock_get_hostnames, mock_get_personalities, mock_vboxmanage, mock_options):
|
|
"""
|
|
Test create_host_bulk_add
|
|
"""
|
|
|
|
# Setup
|
|
mock_options.labname = "test"
|
|
mock_vboxmanage.get_all_vms.return_value = ['test-controller-0', 'test-controller-1', 'test-worker-0',
|
|
'test-storage-0']
|
|
mock_vboxmanage.vboxmanage_showinfo.return_value = b'macaddress2="080027C95571"\n'
|
|
mock_get_personalities.return_value = {
|
|
'test-controller-1': 'controller',
|
|
'test-worker-0': 'worker',
|
|
'test-storage-0': 'storage',
|
|
}
|
|
mock_get_hostnames.return_value = {
|
|
'test-controller-1': 'controller-1',
|
|
'test-worker-0': 'worker-0',
|
|
'test-storage-0': 'storage-0',
|
|
}
|
|
expected_xml = (
|
|
'<?xml version="1.0" encoding="UTF-8" ?>\n'
|
|
'<hosts>\n'
|
|
' <host>\n'
|
|
' <hostname>controller-1</hostname>\n'
|
|
' <personality>controller</personality>\n'
|
|
' <mgmt_mac>08:00:27:C9:55:71</mgmt_mac>\n'
|
|
' </host>\n'
|
|
' <host>\n'
|
|
' <hostname>worker-0</hostname>\n'
|
|
' <personality>worker</personality>\n'
|
|
' <mgmt_mac>08:00:27:C9:55:71</mgmt_mac>\n'
|
|
' </host>\n'
|
|
' <host>\n'
|
|
' <hostname>storage-0</hostname>\n'
|
|
' <personality>storage</personality>\n'
|
|
' <mgmt_mac>08:00:27:C9:55:71</mgmt_mac>\n'
|
|
' </host>\n'
|
|
'</hosts>\n'
|
|
)
|
|
|
|
# Run
|
|
actual_xml = install_vbox.create_host_bulk_add()
|
|
|
|
# Assert
|
|
self.assertEqual(actual_xml, expected_xml)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|