nova-lxd/nova/tests/unit/virt/lxd/test_config.py

463 lines
22 KiB
Python

# 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 test
from nova.tests.unit import fake_network
from nova.virt.lxd import config
from nova.virt.lxd import session
from nova.virt.lxd import utils as container_dir
from oslo_utils import units
import stubs
@ddt.ddt
@mock.patch.object(config, 'CONF', stubs.MockConf())
@mock.patch.object(container_dir, 'CONF', stubs.MockConf())
class LXDTestContainerConfig(test.NoDBTestCase):
"""LXD Container configuration unit tests."""
def setUp(self):
super(LXDTestContainerConfig, self).setUp()
self.config = config.LXDContainerConfig()
@stubs.annotated_data(
('test_name', 'name', 'instance-00000001'),
('test_source', 'source', {'type': 'image',
'alias': 'fake_image'}),
('test_devices', 'devices', {})
)
def test_create_container(self, tag, key, expected):
"""Tests the create_container methond on LXDContainerConfig.
Inspect that the correct dictionary is returned for a given
instance.
"""
instance = stubs._fake_instance()
container_config = self.config.create_container(instance)
self.assertEqual(container_config[key], expected)
@stubs.annotated_data(
('test_memmoy', 'limits.memory', '512MB')
)
def test_create_config(self, tag, key, expected):
instance = stubs._fake_instance()
instance_name = 'fake_instance'
config = self.config.create_config(instance_name, instance)
self.assertEqual(config[key], expected)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
@mock.patch('nova.virt.configdrive.required_by')
def test_create_container_profile(self, mock_config_drive):
"""Verify the LXD profile is correct. no configdrive."""
instance = stubs._fake_instance()
network_info = fake_network.fake_get_instance_nw_info(self)
mock_config_drive.return_value = False
block_device_info = mock.Mock()
container_profile = self.config.create_profile(
instance, network_info, block_device_info)
self.assertEqual(container_profile['name'], instance.name)
self.assertEqual(container_profile['config'],
{'boot.autostart': 'True',
'limits.cpu': '1',
'limits.memory': '512MB',
'raw.lxc':
'lxc.console.logfile=/var/log/lxd/instance-00000001'
'/console.log\n'})
self.assertEqual(container_profile['devices']['fake_br1'],
{'hwaddr': 'DE:AD:BE:EF:00:01',
'nictype': 'bridged',
'parent': 'fake_br1',
'type': 'nic'},
)
self.assertEqual(container_profile['devices']['root'],
{'path': '/', 'size': '10GB', 'type': 'disk'})
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
@mock.patch('nova.virt.configdrive.required_by')
def test_create_container_profle_without_network(self, mock_config_drive):
"""Verify the LXD profile without network configuration."""
instance = stubs._fake_instance()
mock_config_drive.return_value = False
block_device_info = mock.Mock()
container_profile = self.config.create_profile(
instance, None, block_device_info)
self.assertEqual(len(container_profile['devices']), 1)
self.assertEqual(container_profile['devices']['root'],
{'path': '/', 'size': '10GB', 'type': 'disk'})
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
@mock.patch('nova.virt.configdrive.required_by')
def test_create_contianer_profile_with_configdrive(self, mock_configdrive):
"""Verify the LXD profile with both network and configdrive enabled."""
instance = stubs._fake_instance()
network_info = fake_network.fake_get_instance_nw_info(self)
block_device_info = mock.Mock()
container_profile = self.config.create_profile(
instance, network_info, block_device_info)
mock_configdrive.return_value = True
self.assertEqual(len(container_profile['devices']), 3)
self.assertEqual(container_profile['devices']['configdrive'],
{'optional': 'True',
'path': 'var/lib/cloud/data',
'source': '/fake/instances/path/'
'instance-00000001/configdrive',
'type': 'disk'},
)
self.assertEqual(container_profile['devices']['root'],
{'path': '/', 'size': '10GB', 'type': 'disk'})
self.assertEqual(container_profile['devices']['fake_br1'],
{'hwaddr': 'DE:AD:BE:EF:00:01',
'nictype': 'bridged',
'parent': 'fake_br1',
'type': 'nic'})
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
@mock.patch('nova.virt.configdrive.required_by')
def test_create_contianer_profile_configdrive_network(self, mock_config):
"""Verify the LXD profile with no network and configdrive enabled."""
instance = stubs._fake_instance()
block_device_info = mock.Mock()
container_profile = self.config.create_profile(
instance, None, block_device_info)
mock_config.return_value = True
self.assertEqual(len(container_profile['devices']), 2)
self.assertEqual(container_profile['devices']['configdrive'],
{'optional': 'True',
'path': 'var/lib/cloud/data',
'source': '/fake/instances/path/'
'instance-00000001/configdrive',
'type': 'disk'}
)
self.assertEqual(container_profile['devices']['root'],
{'path': '/', 'size': '10GB', 'type': 'disk'})
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
def test_create_container_profile_with_ephemeral_and_network(self):
"""Verify the LXD profile with ephemeral drive is enabled."""
instance = stubs._fake_instance()
instance.ephemeral_gb = 1
network_info = fake_network.fake_get_instance_nw_info(self)
block_device_info = {'ephemerals': [{'virtual_name': 'ephemeral0'}]}
container_profile = self.config.create_profile(
instance, network_info, block_device_info)
self.assertEqual(len(container_profile['devices']), 3)
self.assertEqual(container_profile['devices']['root'],
{'path': '/', 'size': '10GB', 'type': 'disk'})
self.assertEqual(container_profile['devices']['fake_br1'],
{'hwaddr': 'DE:AD:BE:EF:00:01',
'nictype': 'bridged',
'parent': 'fake_br1',
'type': 'nic'})
self.assertEqual(container_profile['devices']['ephemeral0'],
{'optional': 'True',
'path': '/mnt',
'source': '/fake/instances/path/'
'instance-00000001/storage/ephemeral0',
'type': 'disk'}
)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
@mock.patch('nova.virt.configdrive.required_by')
def test_create_container_profile_with_ephemeral_and_configdrive(
self, mock_configdrive):
"""Verify container profile configuration with ephemeral and
configdrive.
"""
instance = stubs._fake_instance()
instance.ephemeral_gb = 1
network_info = fake_network.fake_get_instance_nw_info(self)
block_device_info = {'ephemerals': [{'virtual_name': 'ephemeral0'}]}
mock_configdrive.return_value = True
container_profile = self.config.create_profile(
instance, network_info, block_device_info)
self.assertEqual(len(container_profile['devices']), 4)
self.assertEqual(container_profile['devices']['root'],
{'path': '/', 'size': '10GB', 'type': 'disk'})
self.assertEqual(container_profile['devices']['fake_br1'],
{'hwaddr': 'DE:AD:BE:EF:00:01',
'nictype': 'bridged',
'parent': 'fake_br1',
'type': 'nic'})
self.assertEqual(container_profile['devices']['ephemeral0'],
{'optional': 'True',
'path': '/mnt',
'source': '/fake/instances/path/'
'instance-00000001/storage/ephemeral0',
'type': 'disk'}
)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
def test_create_container_profile_with_ephemeral_and_no_network(self):
"""Verify container profile configuration with ephemeral and
without network.
"""
instance = stubs._fake_instance()
instance.ephemeral_gb = 1
block_device_info = {'ephemerals': [{'virtual_name': 'ephemeral0'}]}
container_profile = self.config.create_profile(
instance, None, block_device_info)
self.assertEqual(len(container_profile['devices']), 2)
self.assertEqual(container_profile['devices']['root'],
{'path': '/', 'size': '10GB', 'type': 'disk'})
self.assertEqual(container_profile['devices']['ephemeral0'],
{'optional': 'True',
'path': '/mnt',
'source': '/fake/instances/path/'
'instance-00000001/storage/ephemeral0',
'type': 'disk'}
)
def test_create_network(self):
instance = stubs._fake_instance()
instance_name = 'fake_instance'
network_info = fake_network.fake_get_instance_nw_info(self)
config = self.config.create_network(instance_name, instance,
network_info)
self.assertEqual({'fake_br1': {'hwaddr': 'DE:AD:BE:EF:00:01',
'nictype': 'bridged',
'parent': 'fake_br1',
'type': 'nic'}}, config)
@mock.patch('os.path.exists', mock.Mock(return_value=True))
def test_create_disk_path(self):
instance = stubs._fake_instance()
config = self.config.configure_disk_path('/fake/src_path',
'/fake/dest_path',
'fake_disk', instance)
self.assertEqual({'fake_disk': {'path': '/fake/dest_path',
'source': '/fake/src_path',
'type': 'disk',
'optional': 'True'}}, config)
def test_config_instance_options(self):
instance = stubs._fake_instance()
config = {}
container_config = self.config.config_instance_options(config,
instance)
self.assertEqual({'boot.autostart': 'True'}, container_config)
def test_create_container_source(self):
instance = stubs._fake_instance()
config = self.config.get_container_source(instance)
self.assertEqual(config, {'type': 'image', 'alias': 'fake_image'})
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'btrfs'}))
def test_container_root_btrfs(self):
instance = stubs._fake_instance()
config = self.config.configure_container_root(instance)
self.assertEqual({'root': {'path': '/',
'type': 'disk',
'size': '10GB'}}, config)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
def test_container_root_zfs(self):
instance = stubs._fake_instance()
config = self.config.configure_container_root(instance)
self.assertEqual({'root': {'path': '/',
'type': 'disk',
'size': '10GB'}}, config)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'lvm'}))
def test_container_root_lvm(self):
instance = stubs._fake_instance()
config = self.config.configure_container_root(instance)
self.assertEqual({'root': {'path': '/',
'type': 'disk'}}, config)
def test_container_nested_container(self):
instance = stubs._fake_instance()
instance.flavor.extra_specs = {'lxd:nested_allowed': True}
config = self.config.config_instance_options({}, instance)
self.assertEqual({'security.nesting': 'True',
'boot.autostart': 'True'}, config)
def test_container_privileged_container(self):
instance = stubs._fake_instance()
instance.flavor.extra_specs = {'lxd:privileged_allowed': True}
config = self.config.config_instance_options({}, instance)
self.assertEqual({'security.privileged': 'True',
'boot.autostart': 'True'}, config)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
def test_disk_quota_rw_iops(self):
instance = stubs._fake_instance()
instance.flavor.extra_specs = {'quota:disk_read_iops_sec': 10000,
'quota:disk_write_iops_sec': 10000}
config = self.config.configure_container_root(instance)
self.assertEqual({'root': {'path': '/',
'type': 'disk',
'size': '10GB',
'limits.read': '10000iops',
'limits.write': '10000iops'}}, config)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
def test_disk_quota_rw_iops_and_bytes(self):
# Byte values should take precedence
instance = stubs._fake_instance()
instance.flavor.extra_specs = {
'quota:disk_read_iops_sec': 10000,
'quota:disk_write_iops_sec': 10000,
'quota:disk_read_bytes_sec': 13 * units.Mi,
'quota:disk_write_bytes_sec': 5 * units.Mi
}
config = self.config.configure_container_root(instance)
self.assertEqual({'root': {'path': '/',
'type': 'disk',
'size': '10GB',
'limits.read': '13MB',
'limits.write': '5MB'}}, config)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
def test_disk_quota_total_iops(self):
instance = stubs._fake_instance()
instance.flavor.extra_specs = {
'quota:disk_total_iops_sec': 10000
}
config = self.config.configure_container_root(instance)
self.assertEqual({'root': {'path': '/',
'type': 'disk',
'size': '10GB',
'limits.max': '10000iops'}}, config)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
def test_disk_quota_total_iops_and_bytes(self):
instance = stubs._fake_instance()
instance.flavor.extra_specs = {
'quota:disk_total_iops_sec': 10000,
'quota:disk_total_bytes_sec': 11 * units.Mi
}
config = self.config.configure_container_root(instance)
self.assertEqual({'root': {'path': '/',
'type': 'disk',
'size': '10GB',
'limits.max': '11MB'}}, config)
@mock.patch.object(session.LXDAPISession, 'get_host_config',
mock.Mock(return_value={'storage': 'zfs'}))
def test_disk_quota_rw_and_total_iops_and_bytes(self):
# More granular quotas should be set only, moreover
# in MBytes, not iops
instance = stubs._fake_instance()
instance.flavor.extra_specs = {
'quota:disk_read_iops_sec': 10000,
'quota:disk_write_iops_sec': 10000,
'quota:disk_read_bytes_sec': 13 * units.Mi,
'quota:disk_write_bytes_sec': 5 * units.Mi,
'quota:disk_total_iops_sec': 10000,
'quota:disk_total_bytes_sec': 11 * units.Mi
}
config = self.config.configure_container_root(instance)
self.assertEqual({'root': {'path': '/',
'type': 'disk',
'size': '10GB',
'limits.read': '13MB',
'limits.write': '5MB'}}, config)
def test_network_in_out_average(self):
instance = stubs._fake_instance()
# We get KB/s from flavor spec, but expect Mbit/s
# in LXD confix
instance.flavor.extra_specs = {
'quota:vif_inbound_average': 20000,
'quota:vif_outbound_average': 8000
}
instance_name = 'fake_instance'
network_info = fake_network.fake_get_instance_nw_info(self)
config = self.config.create_network(instance_name, instance,
network_info)
self.assertEqual({'fake_br1': {'hwaddr': 'DE:AD:BE:EF:00:01',
'nictype': 'bridged',
'parent': 'fake_br1',
'type': 'nic',
'limits.ingress': '160Mbit',
'limits.egress': '64Mbit'}}, config)
def test_network_in_out_average_and_peak(self):
# Max of the two values should take precedence
instance = stubs._fake_instance()
instance.flavor.extra_specs = {
'quota:vif_inbound_average': 2000,
'quota:vif_outbound_average': 10000,
'quota:vif_inbound_peak': 10000,
'quota:vif_outbound_peak': 2000,
}
instance_name = 'fake_instance'
network_info = fake_network.fake_get_instance_nw_info(self)
config = self.config.create_network(instance_name, instance,
network_info)
self.assertEqual({'fake_br1': {'hwaddr': 'DE:AD:BE:EF:00:01',
'nictype': 'bridged',
'parent': 'fake_br1',
'type': 'nic',
'limits.ingress': '80Mbit',
'limits.egress': '80Mbit'}}, config)
@mock.patch.object(session.LXDAPISession, 'host_certificate')
def test_container_migrate(self, mock_host_certificate):
"""Verify that certificate is passed to the container configuration."""
mock_host_certificate.return_value = 'fake_certificate'
container_migrate = {'operation': 'fake_operation',
'metadata': {'control': 'fake_control',
'fs': 'fake_fs'}
}
host = '10.0.0.1'
instance = mock.MagicMock()
container_image = self.config.get_container_migrate(
container_migrate, host, instance)
self.assertEqual('fake_certificate',
container_image['certificate'])
@mock.patch.object(session.LXDAPISession, 'host_certificate')
def test_container_live_migrate(self, mock_host_certificate):
"""Verify that the migrate websocket information is used
when configuring a container for migration.
"""
mock_host_certificate.return_value = 'fake_certificate'
container_migrate = {'operation': 'fake_operation',
'metadata': {'control': 'fake_control',
'fs': 'fake_fs',
'criu': 'fake_criu'}
}
host = '10.0.0.1'
instance = mock.MagicMock()
container_image = self.config.get_container_migrate(
container_migrate, host, instance)
self.assertEqual({'control': 'fake_control',
'fs': 'fake_fs',
'criu': 'fake_criu'}, container_image['secrets'])