Modernize more of LXDDriver
This includes adding an explicit cleanup_host (because it's important to acknowledge explicitly nothing is needed for cleanup), get_info using the new pylxd api, and more tests for list_instances. There were a number of methods that didn't need to be implemented by the lxd driver as they were identical to the parent class ComputeDriver. I also explicitly removed the "catch/log/raise" exception patterns, as I'd rather explicitly catch the exceptions and move on, or allow nova to handle the exception itself (and log it). Change-Id: I8c14da579d51c532e279d95d737b90580a790c03
This commit is contained in:
parent
e880aa6cb3
commit
0774e313dd
|
@ -12,13 +12,22 @@
|
|||
# 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 collections
|
||||
|
||||
import mock
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.compute import power_state
|
||||
from nova.tests.unit import fake_instance
|
||||
from pylxd import exceptions as lxdcore_exceptions
|
||||
|
||||
from nova.virt.lxd import driver
|
||||
|
||||
MockContainer = collections.namedtuple('Container', ['name'])
|
||||
MockContainerState = collections.namedtuple(
|
||||
'ContainerState', ['status_code', 'memory'])
|
||||
|
||||
|
||||
class LXDDriverTest(test.NoDBTestCase):
|
||||
"""Tests for nova.virt.lxd.driver.LXDDriver."""
|
||||
|
@ -52,8 +61,35 @@ class LXDDriverTest(test.NoDBTestCase):
|
|||
|
||||
lxd_driver = driver.LXDDriver(None)
|
||||
|
||||
self.assertRaises(
|
||||
exception.HostNotFound,
|
||||
lxd_driver.init_host,
|
||||
None
|
||||
)
|
||||
self.assertRaises(exception.HostNotFound, lxd_driver.init_host, None)
|
||||
|
||||
def test_get_info(self):
|
||||
container = mock.Mock()
|
||||
container.state.return_value = MockContainerState(
|
||||
100, {'usage': 4000, 'usage_peak': 4500})
|
||||
self.client.containers.get.return_value = container
|
||||
|
||||
ctx = context.get_admin_context()
|
||||
instance = fake_instance.fake_instance_obj(ctx, name='test')
|
||||
lxd_driver = driver.LXDDriver(None)
|
||||
lxd_driver.init_host(None)
|
||||
|
||||
info = lxd_driver.get_info(instance)
|
||||
|
||||
self.assertEqual(power_state.RUNNING, info.state)
|
||||
self.assertEqual(3, info.mem_kb)
|
||||
self.assertEqual(4, info.max_mem_kb)
|
||||
self.assertEqual(1, info.num_cpu)
|
||||
self.assertEqual(0, info.cpu_time_ns)
|
||||
|
||||
def test_list_instances(self):
|
||||
self.client.containers.all.return_value = [
|
||||
MockContainer('mock-instance-1'),
|
||||
MockContainer('mock-instance-2'),
|
||||
]
|
||||
lxd_driver = driver.LXDDriver(None)
|
||||
lxd_driver.init_host(None)
|
||||
|
||||
instances = lxd_driver.list_instances()
|
||||
|
||||
self.assertEqual(['mock-instance-1', 'mock-instance-2'], instances)
|
||||
|
|
|
@ -12,12 +12,10 @@
|
|||
# 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 collections
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
from pylxd import exceptions as lxdcore_exceptions
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
|
@ -27,20 +25,15 @@ from oslo_config import cfg
|
|||
|
||||
from nova.compute import arch
|
||||
from nova.compute import hv_type
|
||||
from nova.compute import power_state
|
||||
from nova.compute import vm_mode
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.virt import fake
|
||||
from nova.virt import hardware
|
||||
|
||||
from nova.virt.lxd import driver
|
||||
from nova.virt.lxd import session
|
||||
from nova.virt.lxd import utils as container_dir
|
||||
import stubs
|
||||
|
||||
MockContainer = collections.namedtuple('Container', ['name'])
|
||||
|
||||
|
||||
class LXDTestConfig(test.NoDBTestCase):
|
||||
|
||||
|
@ -70,13 +63,6 @@ class LXDTestDriver(test.NoDBTestCase):
|
|||
self.driver = driver.LXDDriver(mock.MagicMock())
|
||||
self.driver.container_migrate = mock.MagicMock()
|
||||
|
||||
mock_client = mock.Mock()
|
||||
mock_client.containers.all.return_value = [
|
||||
MockContainer('mock-instance-1'),
|
||||
MockContainer('mock-instance-2'),
|
||||
]
|
||||
self.connection.client = mock_client
|
||||
|
||||
def test_capabilities(self):
|
||||
self.assertFalse(self.connection.capabilities['has_imagecache'])
|
||||
self.assertFalse(self.connection.capabilities['supports_recreate'])
|
||||
|
@ -85,59 +71,6 @@ class LXDTestDriver(test.NoDBTestCase):
|
|||
self.assertTrue(
|
||||
self.connection.capabilities['supports_attach_interface'])
|
||||
|
||||
@stubs.annotated_data(
|
||||
('running', {'state': 200, 'mem': 0, 'max_mem': 0},
|
||||
power_state.RUNNING),
|
||||
('shutdown', {'state': 102, 'mem': 0, 'max_mem': 0},
|
||||
power_state.SHUTDOWN),
|
||||
('crashed', {'state': 108, 'mem': 0, 'max_mem': 0},
|
||||
power_state.CRASHED),
|
||||
('suspend', {'state': 109, 'mem': 0, 'max_mem': 0},
|
||||
power_state.SUSPENDED),
|
||||
('no_state', {'state': 401, 'mem': 0, 'max_mem': 0},
|
||||
power_state.NOSTATE),
|
||||
)
|
||||
def test_get_info(self, tag, side_effect, expected):
|
||||
instance = stubs._fake_instance()
|
||||
with mock.patch.object(session.LXDAPISession,
|
||||
"container_state",
|
||||
) as state:
|
||||
state.return_value = side_effect
|
||||
info = self.connection.get_info(instance)
|
||||
self.assertEqual(dir(hardware.InstanceInfo(state=expected,
|
||||
num_cpu=2)), dir(info))
|
||||
|
||||
@stubs.annotated_data(
|
||||
(True, 'mock-instance-1'),
|
||||
(False, 'fake-instance'),
|
||||
)
|
||||
def test_instance_exists(self, expected, name):
|
||||
self.assertEqual(
|
||||
expected,
|
||||
self.connection.instance_exists(stubs.MockInstance(name=name)))
|
||||
|
||||
def test_estimate_instance_overhead(self):
|
||||
self.assertEqual(
|
||||
{'memory_mb': 0},
|
||||
self.connection.estimate_instance_overhead(mock.Mock()))
|
||||
|
||||
def test_list_instances(self):
|
||||
self.assertEqual(['mock-instance-1', 'mock-instance-2'],
|
||||
self.connection.list_instances())
|
||||
|
||||
def test_list_instances_fail(self):
|
||||
mock_response = mock.Mock()
|
||||
mock_response.json.return_value = {
|
||||
'error': 'Fake',
|
||||
}
|
||||
|
||||
self.connection.client.containers.all.side_effect = (
|
||||
lxdcore_exceptions.LXDAPIException(mock_response))
|
||||
self.assertRaises(
|
||||
exception.NovaException,
|
||||
self.connection.list_instances
|
||||
)
|
||||
|
||||
@mock.patch('nova.virt.configdrive.required_by')
|
||||
def test_spawn(self, mock_configdrive):
|
||||
"""Test spawn method. Ensure that the right calls
|
||||
|
|
|
@ -126,7 +126,7 @@ class LXDDriver(driver.ComputeDriver):
|
|||
assumed to be on the same system as the compute worker
|
||||
running this code. This is by (current) design.
|
||||
|
||||
See `nova.virt.driver.ComputerDriver.init_host for more
|
||||
See `nova.virt.driver.ComputeDriver.init_host` for more
|
||||
information.
|
||||
"""
|
||||
try:
|
||||
|
@ -135,29 +135,37 @@ class LXDDriver(driver.ComputeDriver):
|
|||
msg = _('Unable to connect to LXD daemon: %s') % e
|
||||
raise exception.HostNotFound(msg)
|
||||
|
||||
def cleanup_host(self, host):
|
||||
"""Clean up the host.
|
||||
|
||||
`nova.virt.ComputeDriver` defines this method. It is overridden
|
||||
here to be explicit that there is nothing to be done, as
|
||||
`init_host` does not create any resources that would need to be
|
||||
cleaned up.
|
||||
|
||||
See `nova.virt.driver.ComputeDriver.cleanup_host` for more
|
||||
information.
|
||||
"""
|
||||
|
||||
def get_info(self, instance):
|
||||
"""Return an InstanceInfo object for the instance."""
|
||||
container = self.client.containers.get(instance.name)
|
||||
state = container.state()
|
||||
power_state = session.LXD_POWER_STATES[state.status_code]
|
||||
mem_kb = state.memory['usage'] >> 10
|
||||
max_mem_kb = state.memory['usage_peak'] >> 10
|
||||
return hardware.InstanceInfo(
|
||||
state=power_state, max_mem_kb=max_mem_kb, mem_kb=mem_kb,
|
||||
num_cpu=instance.flavor.vcpus, cpu_time_ns=0)
|
||||
|
||||
def list_instances(self):
|
||||
"""Return a list of all instance names."""
|
||||
return [c.name for c in self.client.containers.all()]
|
||||
|
||||
# XXX: rockstar (5 July 2016) - The methods and code below this line
|
||||
# have not been through the cleanup process. We know the cleanup process
|
||||
# is complete when there is no more code below this comment, and the
|
||||
# comment can be removed.
|
||||
def get_info(self, instance):
|
||||
LOG.debug('get_info called for instance', instance=instance)
|
||||
try:
|
||||
container_state = self.session.container_state(instance)
|
||||
return hardware.InstanceInfo(state=container_state['state'],
|
||||
max_mem_kb=container_state['max_mem'],
|
||||
mem_kb=container_state['mem'],
|
||||
num_cpu=instance.flavor.vcpus,
|
||||
cpu_time_ns=0)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Failed to get container info'
|
||||
' for %(instance)s: %(ex)s'),
|
||||
{'instance': instance.name, 'ex': ex},
|
||||
instance=instance)
|
||||
|
||||
def instance_exists(self, instance):
|
||||
return instance.name in self.list_instances()
|
||||
|
||||
def plug_vifs(self, instance, network_info):
|
||||
"""Plug VIFs into networks."""
|
||||
for vif in network_info:
|
||||
|
@ -175,21 +183,6 @@ class LXDDriver(driver.ComputeDriver):
|
|||
pass
|
||||
self.firewall_driver.unfilter_instance(instance, network_info)
|
||||
|
||||
def estimate_instance_overhead(self, instance_info):
|
||||
return {'memory_mb': 0}
|
||||
|
||||
def list_instances(self):
|
||||
try:
|
||||
return [c.name for c in self.client.containers.all()]
|
||||
except lxd_exceptions.LXDAPIException as ex:
|
||||
msg = _('Failed to communicate with LXD API: %(reason)s') \
|
||||
% {'reason': ex}
|
||||
LOG.error(msg)
|
||||
raise exception.NovaException(msg)
|
||||
|
||||
def list_instance_uuids(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def spawn(self, context, instance, image_meta, injected_files,
|
||||
admin_password, network_info=None, block_device_info=None):
|
||||
msg = ('Spawning container '
|
||||
|
|
Loading…
Reference in New Issue