Merge pull request #11 from Saviq/improve-coverage

Improve coverage
This commit is contained in:
zulcss 2015-07-15 14:23:29 -04:00
commit 660a563e42
6 changed files with 388 additions and 15 deletions

View File

@ -1,7 +1,7 @@
[run]
branch = True
source = nova-compute-lxd
omit = nova-compute-lxd/tests/*,nova-compute-lxd/openstack/*
source = nclxd
[report]
ignore-errors = True
ignore-errors = True
omit = nclxd/tests/*

View File

@ -62,15 +62,10 @@ class LXDContainerImage(object):
''' Upload the image to LXD '''
with fileutils.remove_path_on_error(container_image):
try:
self.lxd.image_defined(instance.image_ref)
except lxd_exceptions.APIError as e:
if e.status_code == 404:
pass
else:
raise exception.ImageUnacceptable(
image_id=instance.image_ref,
reason=_('Image already exists.'))
if self.lxd.image_defined(instance.image_ref):
raise exception.ImageUnacceptable(
image_id=instance.image_ref,
reason=_('Image already exists.'))
try:
LOG.debug('Uploading image: %s' % container_image)

View File

@ -0,0 +1,60 @@
# Copyright (c) 2015 Canonical Ltd
#
# 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 ddt
import mock
class MockConf(mock.Mock):
def __init__(self, lxd_args=(), lxd_kwargs={}, *args, **kwargs):
default = {
'config_drive_format': None,
'instances_path': '/fake/instances/path',
'image_cache_subdirectory_name': '/fake/image/cache',
}
default.update(kwargs)
super(MockConf, self).__init__(*args, **default)
lxd_default = {
'lxd_default_profile': 'fake_profile',
'lxd_root_dir': '/fake/lxd/root',
}
lxd_default.update(lxd_kwargs)
self.lxd = mock.Mock(lxd_args, **lxd_default)
class MockInstance(mock.Mock):
def __init__(self, name='mock_instance', image_ref='mock_image',
memory_mb=-1, vcpus=0, *args, **kwargs):
super(MockInstance, self).__init__(
*args, **kwargs)
self.name = name
self.image_ref = image_ref
self.flavor = mock.Mock(memory_mb=memory_mb, vcpus=vcpus)
def annotated_data(*args):
class List(list):
pass
new_args = []
for arg in args:
new_arg = List(arg)
new_arg.__name__ = arg[0]
new_args.append(new_arg)
return lambda func: ddt.data(*new_args)(ddt.unpack(func))

View File

@ -13,16 +13,167 @@
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import mock
from nova import exception
from nova import test
from oslo_config import cfg
from nclxd.nova.virt.lxd import container_config
CONF = cfg.CONF
from nclxd.nova.virt.lxd import container_utils
from nclxd import tests
@ddt.ddt
@mock.patch.object(container_config, 'CONF', tests.MockConf())
@mock.patch.object(container_utils, 'CONF', tests.MockConf())
class LXDTestContainerConfig(test.NoDBTestCase):
def setUp(self):
super(LXDTestContainerConfig, self).setUp()
self.container_config = container_config.LXDContainerConfig()
def test_init_config(self):
self.assertEqual({'config': {}, 'devices': {}},
self.container_config._init_container_config())
@mock.patch('nclxd.nova.virt.lxd.container_image'
'.LXDContainerImage.fetch_image')
@tests.annotated_data(
('no_rescue', {}, 'mock_instance'),
('rescue', {'name_label': 'rescued', 'rescue': True}, 'rescued'),
)
def test_configure_container(self, tag, kwargs, expected, mf):
instance = tests.MockInstance()
context = {}
network_info = []
image_meta = {}
self.assertEqual(
{'config': {'raw.lxc':
'lxc.console.logfile=/fake/lxd/root/lxc/'
'mock_instance/console.log\n'},
'devices': {},
'name': expected,
'profiles': ['fake_profile'],
'source': {'alias': 'None', 'type': 'image'}},
(self.container_config
.configure_container(context,
instance,
network_info,
image_meta,
**kwargs)))
mf.assert_called_once_with(context, instance, image_meta)
@tests.annotated_data(
('no_limits', {'memory_mb': -1, 'vcpus': 0},
{}),
('mem_limit', {'memory_mb': 2048, 'vcpus': 0},
{'limits.memory': '2147483648'}),
('cpu_limit', {'memory_mb': -1, 'vcpus': 10},
{'limits.cpus': '10'}),
('both_limits', {'memory_mb': 4096, 'vcpus': 20},
{'limits.memory': '4294967296', 'limits.cpus': '20'}),
)
def test_configure_container_config(self, tag, flavor, expected):
instance = tests.MockInstance(**flavor)
config = {'raw.lxc': 'lxc.console.logfile=/fake/lxd/root/lxc/'
'mock_instance/console.log\n'}
config.update(expected)
self.assertEqual(
{'config': config},
self.container_config.configure_container_config({},
instance))
def test_configure_network_devices(self):
instance = tests.MockInstance()
network_info = (
{
'id': '0123456789abcdef',
'address': '00:11:22:33:44:55',
},
{
'id': 'fedcba9876543210',
'address': '66:77:88:99:aa:bb',
})
self.assertEqual({
'devices': {
'qbr0123456789a': {
'nictype': 'bridged',
'hwaddr': '00:11:22:33:44:55',
'parent': 'qbr0123456789a',
'type': 'nic'
},
'qbrfedcba98765': {
'nictype': 'bridged',
'hwaddr': '66:77:88:99:aa:bb',
'parent': 'qbrfedcba98765',
'type': 'nic'
}}},
self.container_config.configure_network_devices(
{}, instance, network_info))
def test_configure_container_rescuedisk(self):
instance = tests.MockInstance()
self.assertEqual({
'devices':
{'rescue': {'path': 'mnt',
'source': '/fake/lxd/root/lxc/mock_instance/rootfs',
'type': 'disk'}}},
self.container_config.configure_container_rescuedisk(
{}, instance))
def test_configure_container_configdrive_wrong_format(self):
instance = tests.MockInstance()
with mock.patch.object(container_config.CONF, 'config_drive_format',
new='fake-format'):
self.assertRaises(
exception.InstancePowerOnFailure,
self.container_config.configure_container_configdrive,
{}, instance, {}, 'secret')
@mock.patch('nova.api.metadata.base.InstanceMetadata')
def test_configure_container_configdrive_fail(self, mi):
instance = None
injected_files = mock.Mock()
self.assertRaises(
AttributeError,
self.container_config.configure_container_configdrive,
{}, instance, injected_files, 'secret')
mi.assert_called_once_with(
instance, content=injected_files, extra_md={})
@mock.patch('nova.api.metadata.base.InstanceMetadata')
@mock.patch('nova.virt.configdrive.ConfigDriveBuilder')
def test_configure_container_configdrive_fail_dir(self, md, mi):
instance = tests.MockInstance()
injected_files = mock.Mock()
self.assertRaises(
AttributeError,
self.container_config.configure_container_configdrive,
None, instance, injected_files, 'secret')
md.assert_called_once_with(instance_md=mi.return_value)
(md.return_value.__enter__.return_value
.make_drive.assert_called_once_with(
'/fake/instances/path/mock_instance/config-drive'))
mi.assert_called_once_with(
instance, content=injected_files, extra_md={})
@mock.patch('nova.api.metadata.base.InstanceMetadata')
@mock.patch('nova.virt.configdrive.ConfigDriveBuilder')
def test_configure_container_configdrive(self, md, mi):
instance = tests.MockInstance()
injected_files = mock.Mock()
self.assertEqual(
{'devices': {'configdrive':
{'path': 'mnt',
'type': 'disk',
'source': '/fake/instances/path/'
'mock_instance/config-drive'}}},
self.container_config.configure_container_configdrive(
{}, instance, injected_files, 'secret'))
md.assert_called_once_with(instance_md=mi.return_value)
(md.return_value.__enter__.return_value
.make_drive.assert_called_once_with(
'/fake/instances/path/mock_instance/config-drive'))
mi.assert_called_once_with(
instance, content=injected_files, extra_md={})

View File

@ -0,0 +1,166 @@
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# 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 ddt
import mock
from nova import exception
from nova import test
from pylxd import exceptions as lxd_exceptions
from nclxd.nova.virt.lxd import container_image
from nclxd.nova.virt.lxd import container_utils
from nclxd import tests
@ddt.ddt
@mock.patch.object(container_image, 'CONF', tests.MockConf())
@mock.patch.object(container_utils, 'CONF', tests.MockConf())
class LXDTestContainerImage(test.NoDBTestCase):
@mock.patch.object(container_utils, 'CONF', tests.MockConf())
def setUp(self):
super(LXDTestContainerImage, self).setUp()
self.container_image = container_image.LXDContainerImage()
alias_patcher = mock.patch.object(self.container_image.lxd,
'alias_list',
return_value=['alias'])
alias_patcher.start()
self.addCleanup(alias_patcher.stop)
def test_fetch_image_existing_alias(self):
instance = tests.MockInstance()
context = {}
image_meta = {'name': 'alias'}
self.assertEqual(None,
self.container_image.fetch_image(context,
instance,
image_meta))
@mock.patch('os.path.exists')
@mock.patch('nova.openstack.common.fileutils.ensure_tree')
@ddt.data(True, False)
def test_fetch_image_existing_file(self, base_exists, mt, mo):
mo.side_effect = [base_exists, True]
instance = tests.MockInstance()
context = {}
image_meta = {'name': 'new_image'}
self.assertEqual(None,
self.container_image.fetch_image(context,
instance,
image_meta))
if base_exists:
self.assertFalse(mt.called)
else:
mt.assert_called_once_with('/fake/image/cache')
self.assertEqual([mock.call('/fake/image/cache'),
mock.call('/fake/image/cache/new_image.tar.gz')],
mo.call_args_list)
@mock.patch('os.path.exists', mock.Mock(return_value=False))
@mock.patch('nova.openstack.common.fileutils.ensure_tree', mock.Mock())
@mock.patch('nova.openstack.common.fileutils.remove_path_on_error')
def test_fetch_image_new_defined(self, mf):
instance = tests.MockInstance()
context = {}
image_meta = {'name': 'new_image'}
with (
mock.patch.object(container_image.IMAGE_API,
'download')) as mi, (
mock.patch.object(self.container_image.lxd,
'image_defined', return_value=True)) as ml:
self.assertRaises(exception.ImageUnacceptable,
self.container_image.fetch_image,
context, instance, image_meta)
ml.assert_called_once_with('mock_image')
mf.assert_called_once_with('/fake/image/cache/new_image.tar.gz')
mi.assert_called_once_with(
context, 'mock_image',
dest_path='/fake/image/cache/new_image.tar.gz')
@mock.patch('os.path.exists', mock.Mock(return_value=False))
@mock.patch('nova.openstack.common.fileutils.ensure_tree', mock.Mock())
@mock.patch('nova.openstack.common.fileutils.remove_path_on_error',
mock.MagicMock())
def test_fetch_image_new_upload_failed(self):
instance = tests.MockInstance()
context = {}
image_meta = {'name': 'new_image'}
with (
mock.patch.object(container_image.IMAGE_API,
'download')), (
mock.patch.object(self.container_image.lxd,
'image_defined', return_value=False)), (
mock.patch.object(self.container_image.lxd,
'image_upload',
side_effect=lxd_exceptions.APIError(
'Fake error', 500))) as mu:
self.assertRaises(exception.ImageUnacceptable,
self.container_image.fetch_image,
context, instance, image_meta)
mu.assert_called_once_with(
path='/fake/image/cache/new_image.tar.gz')
@mock.patch('os.path.exists', mock.Mock(return_value=False))
@mock.patch('nova.openstack.common.fileutils.ensure_tree', mock.Mock())
@mock.patch('nova.openstack.common.fileutils.remove_path_on_error',
mock.MagicMock())
def test_fetch_image_new_alias_failed(self):
instance = tests.MockInstance()
context = {}
image_meta = {'name': 'new_image'}
with (
mock.patch.object(container_image.IMAGE_API,
'download')), (
mock.patch.object(self.container_image.lxd,
'image_defined', return_value=False)), (
mock.patch.object(self.container_image.lxd,
'image_upload')), (
mock.patch.object(self.container_image.lxd,
'alias_create',
side_effect=lxd_exceptions.APIError(
'Fake error', 500))), (
mock.patch('six.moves.builtins.open')) as mo:
mo.return_value.__enter__.return_value.read.return_value = b'image'
self.assertRaises(exception.ImageUnacceptable,
self.container_image.fetch_image,
context, instance, image_meta)
@mock.patch('os.path.exists', mock.Mock(return_value=False))
@mock.patch('nova.openstack.common.fileutils.ensure_tree', mock.Mock())
@mock.patch('nova.openstack.common.fileutils.remove_path_on_error',
mock.MagicMock())
def test_fetch_image_new(self):
instance = tests.MockInstance()
context = {}
image_meta = {'name': 'new_image'}
with (
mock.patch.object(container_image.IMAGE_API,
'download')), (
mock.patch.object(self.container_image.lxd,
'image_defined', return_value=False)), (
mock.patch.object(self.container_image.lxd,
'image_upload')), (
mock.patch.object(self.container_image.lxd,
'alias_create')) as ma, (
mock.patch('six.moves.builtins.open')) as mo:
mo.return_value.__enter__.return_value.read.return_value = b'image'
self.assertEqual(None,
self.container_image.fetch_image(context,
instance,
image_meta))
ma.assert_called_with(
{'name': 'new_image',
'target': '6105d6cc76af400325e94d588ce511be'
'5bfdbb73b437dc51eca43917d7a43e3d'})

View File

@ -6,6 +6,7 @@ hacking<0.10,>=0.9.2
coverage>=3.6
discover
ddt
python-subunit>=0.0.18
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
oslosphinx>=2.5.0 # Apache-2.0