ironic-inspector/ironic_inspector/test/test_plugins_standard.py
Dmitry Tantsur bef76170cb Don't rely on dict ordering in test_plugins_standard
Change-Id: I63b5b2d9dfafe237a9d9111f231adb42c2b231d3
2015-09-25 15:07:01 +02:00

397 lines
16 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import base64
import os
import shutil
import tempfile
import mock
from oslo_config import cfg
from oslo_utils import units
from ironic_inspector import node_cache
from ironic_inspector.plugins import base
from ironic_inspector.plugins import standard as std_plugins
from ironic_inspector import process
from ironic_inspector.test import base as test_base
from ironic_inspector import utils
CONF = cfg.CONF
class TestSchedulerHook(test_base.NodeTest):
def setUp(self):
super(TestSchedulerHook, self).setUp()
self.hook = std_plugins.SchedulerHook()
self.data = {
'local_gb': 20,
'memory_mb': 1024,
'cpus': 2,
'cpu_arch': 'x86_64'
}
self.node_patches = []
self.ports_patches = {}
self.node_info = node_cache.NodeInfo(uuid=self.uuid, started_at=0,
node=self.node)
def test_hook_loadable_by_name(self):
CONF.set_override('processing_hooks', 'scheduler', 'processing')
ext = base.processing_hooks_manager()['scheduler']
self.assertIsInstance(ext.obj, std_plugins.SchedulerHook)
def test_missing(self):
for key in self.data:
new_data = self.data.copy()
del new_data[key]
self.assertRaisesRegexp(utils.Error, key,
self.hook.before_update, new_data,
self.node_info)
@mock.patch.object(node_cache.NodeInfo, 'patch')
def test_ok(self, mock_patch):
patch = [
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
{'path': '/properties/memory_mb', 'value': '1024', 'op': 'add'},
{'path': '/properties/local_gb', 'value': '20', 'op': 'add'}
]
self.hook.before_update(self.data, self.node_info)
self.assertCalledWithPatch(patch, mock_patch)
@mock.patch.object(node_cache.NodeInfo, 'patch')
def test_no_overwrite(self, mock_patch):
CONF.set_override('overwrite_existing', False, 'processing')
self.node.properties = {
'memory_mb': '4096',
'cpu_arch': 'i686'
}
patch = [
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
{'path': '/properties/local_gb', 'value': '20', 'op': 'add'}
]
self.hook.before_update(self.data, self.node_info)
self.assertCalledWithPatch(patch, mock_patch)
@mock.patch.object(node_cache.NodeInfo, 'patch')
def test_root_disk(self, mock_patch):
self.data['root_disk'] = {'name': '/dev/sda', 'size': 42 * units.Gi}
patch = [
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
{'path': '/properties/memory_mb', 'value': '1024', 'op': 'add'},
{'path': '/properties/local_gb', 'value': '41', 'op': 'add'}
]
self.hook.before_update(self.data, self.node_info)
self.assertCalledWithPatch(patch, mock_patch)
@mock.patch.object(node_cache.NodeInfo, 'patch')
def test_root_disk_no_spacing(self, mock_patch):
CONF.set_override('disk_partitioning_spacing', False, 'processing')
self.data['root_disk'] = {'name': '/dev/sda', 'size': 42 * units.Gi}
patch = [
{'path': '/properties/cpus', 'value': '2', 'op': 'add'},
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
{'path': '/properties/memory_mb', 'value': '1024', 'op': 'add'},
{'path': '/properties/local_gb', 'value': '42', 'op': 'add'}
]
self.hook.before_update(self.data, self.node_info)
self.assertCalledWithPatch(patch, mock_patch)
class TestValidateInterfacesHook(test_base.NodeTest):
def setUp(self):
super(TestValidateInterfacesHook, self).setUp()
self.hook = std_plugins.ValidateInterfacesHook()
self.data = {
'interfaces': {
'em1': {'mac': '11:11:11:11:11:11', 'ip': '1.1.1.1'},
'em2': {'mac': '22:22:22:22:22:22', 'ip': '2.2.2.2'},
'em3': {'mac': '33:33:33:33:33:33'}
},
'boot_interface': '01-22-22-22-22-22-22',
}
self.orig_interfaces = self.data['interfaces'].copy()
self.pxe_interface = self.data['interfaces']['em2']
self.active_interfaces = {
'em1': {'mac': '11:11:11:11:11:11', 'ip': '1.1.1.1'},
'em2': {'mac': '22:22:22:22:22:22', 'ip': '2.2.2.2'},
}
self.existing_ports = [mock.Mock(spec=['address', 'uuid'],
address=a)
for a in ('11:11:11:11:11:11',
'44:44:44:44:44:44')]
self.node_info = node_cache.NodeInfo(uuid=self.uuid, started_at=0,
node=self.node,
ports=self.existing_ports)
def test_hook_loadable_by_name(self):
CONF.set_override('processing_hooks', 'validate_interfaces',
'processing')
ext = base.processing_hooks_manager()['validate_interfaces']
self.assertIsInstance(ext.obj, std_plugins.ValidateInterfacesHook)
def test_wrong_add_ports(self):
CONF.set_override('add_ports', 'foobar', 'processing')
self.assertRaises(SystemExit, std_plugins.ValidateInterfacesHook)
def test_wrong_keep_ports(self):
CONF.set_override('keep_ports', 'foobar', 'processing')
self.assertRaises(SystemExit, std_plugins.ValidateInterfacesHook)
def test_no_interfaces(self):
self.assertRaisesRegexp(utils.Error, 'No interfaces',
self.hook.before_processing, {})
def test_only_pxe(self):
self.hook.before_processing(self.data)
self.assertEqual({'em2': self.pxe_interface}, self.data['interfaces'])
self.assertEqual([self.pxe_interface['mac']], self.data['macs'])
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
def test_only_pxe_mac_format(self):
self.data['boot_interface'] = '22:22:22:22:22:22'
self.hook.before_processing(self.data)
self.assertEqual({'em2': self.pxe_interface}, self.data['interfaces'])
self.assertEqual([self.pxe_interface['mac']], self.data['macs'])
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
def test_only_pxe_not_found(self):
self.data['boot_interface'] = 'aa:bb:cc:dd:ee:ff'
self.assertRaisesRegexp(utils.Error, 'No valid interfaces',
self.hook.before_processing, self.data)
def test_only_pxe_no_boot_interface(self):
del self.data['boot_interface']
self.hook.before_processing(self.data)
self.assertEqual(self.active_interfaces, self.data['interfaces'])
self.assertEqual(sorted(i['mac'] for i in
self.active_interfaces.values()),
sorted(self.data['macs']))
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
def test_only_active(self):
CONF.set_override('add_ports', 'active', 'processing')
self.hook.before_processing(self.data)
self.assertEqual(self.active_interfaces, self.data['interfaces'])
self.assertEqual(sorted(i['mac'] for i in
self.active_interfaces.values()),
sorted(self.data['macs']))
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
def test_all(self):
CONF.set_override('add_ports', 'all', 'processing')
self.hook.before_processing(self.data)
self.assertEqual(self.orig_interfaces, self.data['interfaces'])
self.assertEqual(sorted(i['mac'] for i in
self.orig_interfaces.values()),
sorted(self.data['macs']))
self.assertEqual(self.orig_interfaces, self.data['all_interfaces'])
@mock.patch.object(node_cache.NodeInfo, 'delete_port', autospec=True)
def test_keep_all(self, mock_delete_port):
self.hook.before_update(self.data, self.node_info)
self.assertFalse(mock_delete_port.called)
@mock.patch.object(node_cache.NodeInfo, 'delete_port')
def test_keep_present(self, mock_delete_port):
CONF.set_override('keep_ports', 'present', 'processing')
self.data['all_interfaces'] = self.orig_interfaces
self.hook.before_update(self.data, self.node_info)
mock_delete_port.assert_called_once_with(self.existing_ports[1])
@mock.patch.object(node_cache.NodeInfo, 'delete_port')
def test_keep_added(self, mock_delete_port):
CONF.set_override('keep_ports', 'added', 'processing')
self.data['macs'] = [self.pxe_interface['mac']]
self.hook.before_update(self.data, self.node_info)
mock_delete_port.assert_any_call(self.existing_ports[0])
mock_delete_port.assert_any_call(self.existing_ports[1])
class TestRootDiskSelection(test_base.NodeTest):
def setUp(self):
super(TestRootDiskSelection, self).setUp()
self.hook = std_plugins.RootDiskSelectionHook()
self.data = {
'inventory': {
'disks': [
{'model': 'Model 1', 'size': 20 * units.Gi,
'name': '/dev/sdb'},
{'model': 'Model 2', 'size': 5 * units.Gi,
'name': '/dev/sda'},
{'model': 'Model 3', 'size': 10 * units.Gi,
'name': '/dev/sdc'},
{'model': 'Model 4', 'size': 4 * units.Gi,
'name': '/dev/sdd'},
{'model': 'Too Small', 'size': 1 * units.Gi,
'name': '/dev/sde'},
]
}
}
self.matched = self.data['inventory']['disks'][2].copy()
self.node_info = mock.Mock(spec=node_cache.NodeInfo,
uuid=self.uuid,
**{'node.return_value': self.node})
def test_no_hints(self):
self.hook.before_update(self.data, self.node_info, None, None)
self.assertNotIn('local_gb', self.data)
self.assertNotIn('root_disk', self.data)
@mock.patch.object(std_plugins.LOG, 'error')
def test_no_inventory(self, mock_log):
self.node.properties['root_device'] = {'model': 'foo'}
del self.data['inventory']
self.hook.before_update(self.data, self.node_info, None, None)
self.assertNotIn('local_gb', self.data)
self.assertNotIn('root_disk', self.data)
self.assertTrue(mock_log.called)
def test_no_disks(self):
self.node.properties['root_device'] = {'size': 10}
self.data['inventory']['disks'] = []
self.assertRaisesRegexp(utils.Error,
'No disks found',
self.hook.before_update,
self.data, self.node_info, None, None)
def test_one_matches(self):
self.node.properties['root_device'] = {'size': 10}
self.hook.before_update(self.data, self.node_info, None, None)
self.assertEqual(self.matched, self.data['root_disk'])
def test_all_match(self):
self.node.properties['root_device'] = {'size': 10,
'model': 'Model 3'}
self.hook.before_update(self.data, self.node_info, None, None)
self.assertEqual(self.matched, self.data['root_disk'])
def test_one_fails(self):
self.node.properties['root_device'] = {'size': 10,
'model': 'Model 42'}
self.assertRaisesRegexp(utils.Error,
'No disks satisfied root device hints',
self.hook.before_update,
self.data, self.node_info, None, None)
self.assertNotIn('local_gb', self.data)
self.assertNotIn('root_disk', self.data)
class TestRamdiskError(test_base.BaseTest):
def setUp(self):
super(TestRamdiskError, self).setUp()
self.msg = 'BOOM'
self.bmc_address = '1.2.3.4'
self.data = {
'error': self.msg,
'ipmi_address': self.bmc_address,
}
self.tempdir = tempfile.mkdtemp()
self.addCleanup(lambda: shutil.rmtree(self.tempdir))
CONF.set_override('ramdisk_logs_dir', self.tempdir, 'processing')
def test_no_logs(self):
self.assertRaisesRegexp(utils.Error,
self.msg,
process.process, self.data)
self.assertEqual([], os.listdir(self.tempdir))
def test_logs_disabled(self):
self.data['logs'] = 'some log'
CONF.set_override('ramdisk_logs_dir', None, 'processing')
self.assertRaisesRegexp(utils.Error,
self.msg,
process.process, self.data)
self.assertEqual([], os.listdir(self.tempdir))
def test_logs(self):
log = b'log contents'
self.data['logs'] = base64.b64encode(log)
self.assertRaisesRegexp(utils.Error,
self.msg,
process.process, self.data)
files = os.listdir(self.tempdir)
self.assertEqual(1, len(files))
filename = files[0]
self.assertTrue(filename.startswith('bmc_%s_' % self.bmc_address),
'%s does not start with bmc_%s'
% (filename, self.bmc_address))
with open(os.path.join(self.tempdir, filename), 'rb') as fp:
self.assertEqual(log, fp.read())
def test_logs_create_dir(self):
shutil.rmtree(self.tempdir)
self.data['logs'] = base64.b64encode(b'log')
self.assertRaisesRegexp(utils.Error,
self.msg,
process.process, self.data)
files = os.listdir(self.tempdir)
self.assertEqual(1, len(files))
def test_logs_without_error(self):
log = b'log contents'
del self.data['error']
self.data['logs'] = base64.b64encode(log)
std_plugins.RamdiskErrorHook().before_processing(self.data)
files = os.listdir(self.tempdir)
self.assertFalse(files)
def test_always_store_logs(self):
CONF.set_override('always_store_ramdisk_logs', True, 'processing')
log = b'log contents'
del self.data['error']
self.data['logs'] = base64.b64encode(log)
std_plugins.RamdiskErrorHook().before_processing(self.data)
files = os.listdir(self.tempdir)
self.assertEqual(1, len(files))
filename = files[0]
self.assertTrue(filename.startswith('bmc_%s_' % self.bmc_address),
'%s does not start with bmc_%s'
% (filename, self.bmc_address))
with open(os.path.join(self.tempdir, filename), 'rb') as fp:
self.assertEqual(log, fp.read())