astara/astara/test/unit/test_instance_manager.py

1164 lines
44 KiB
Python

# Copyright 2014 DreamHost, LLC
#
# Author: DreamHost, LLC
#
# 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 collections
import mock
import six
import uuid
from datetime import datetime, timedelta
from six.moves import range
from astara import instance_manager
from astara.api import nova
from astara.drivers import states
from astara.test.unit import base
from astara.test.unit import fakes
from oslo_config import cfg
states.RETRY_DELAY = 0.4
states.BOOT_WAIT = 1
class FakeModel(object):
def __init__(self, id_, **kwargs):
self.id = id_
self.__dict__.update(kwargs)
fake_mgt_port = FakeModel(
'1',
mac_address='aa:bb:cc:dd:ee:ff',
network_id='mgt-net',
fixed_ips=[FakeModel('', ip_address='9.9.9.9', subnet_id='s2')])
fake_int_port = FakeModel(
'2',
mac_address='bb:cc:cc:dd:ee:ff',
network_id='int-net',
fixed_ips=[FakeModel('', ip_address='10.10.10.10', subnet_id='s3')])
fake_ext_port = FakeModel(
'3',
mac_address='cc:cc:cc:dd:ee:ff',
network_id='ext-net',
fixed_ips=[FakeModel('', ip_address='192.168.1.1', subnet_id='s4')])
fake_add_port = FakeModel(
'4',
mac_address='aa:bb:cc:dd:ff:ff',
network_id='additional-net',
fixed_ips=[FakeModel('', ip_address='8.8.8.8', subnet_id='s3')])
def instance_info(mgt_port=fake_mgt_port, name=None):
if not name:
name = 'ak-router-' + str(uuid.uuid4())
return nova.InstanceInfo(
instance_id=str(uuid.uuid4()),
name=name,
management_port=mgt_port,
ports=[fake_int_port, fake_ext_port],
image_uuid='9f3dbe8e-66d8-11e5-9952-525400cfc326',
status='ACTIVE',
last_boot=(datetime.utcnow() - timedelta(minutes=15)),
)
class TestInstanceManager(base.RugTestBase):
def setUp(self):
super(TestInstanceManager, self).setUp()
self.conf = cfg.CONF
self.fake_driver = fakes.fake_driver()
self.ctx = fakes.fake_worker_context()
self.neutron = self.ctx.neutron
self.neutron.api_client = mock.Mock()
self.config(boot_timeout=30)
self.config(astara_mgt_service_port=5000)
self.config(max_retries=3)
self.addCleanup(mock.patch.stopall)
self.log = mock.Mock()
self.update_state_p = mock.patch.object(
instance_manager.InstanceManager,
'update_state'
)
ports = [fake_int_port, fake_ext_port]
self.fake_driver.get_interfaces.return_value = [
{'ifname': 'ge0', 'lladdr': fake_mgt_port.mac_address},
{'ifname': 'ge1', 'lladdr': fake_ext_port.mac_address},
{'ifname': 'ge2', 'lladdr': fake_int_port.mac_address},
]
self.fake_driver.ports = ports
self.mock_update_state = self.update_state_p.start()
self.instance_mgr = instance_manager.InstanceManager(
self.fake_driver,
self.ctx
)
self.instances_patch = mock.patch.object(
instance_manager, 'InstanceGroupManager', autospec=True)
self.instance_mgr.instances = self.instances_patch.start()
self.next_state = None
def next_state(*args, **kwargs):
if self.next_state:
self.instance_mgr.state = self.next_state
return self.instance_mgr.state
self.mock_update_state.side_effect = next_state
def set_instances_container_mocks(self, instances=None, mocks=None):
# set up a mock InstanceGroupManager based on dict
# with specified mocks
self.instances_patch.stop()
mocks = mocks or []
instances = instances or []
class FakeInstancesContainer(dict):
@property
def instance_count(self):
return len(self.values())
@property
def cluster_degraded(self):
return len(self.values()) < self.count
def remove(self, worker_context, instance):
self.pop(instance.id_)
def refresh(self, worker_context):
pass
self.instance_mgr.instances = FakeInstancesContainer()
for attr, _mock in mocks:
if attr not in dir(instance_manager.InstanceGroupManager):
raise AttributeError(
'Attempting to mock non-existent method: %s' % attr)
setattr(self.instance_mgr.instances, attr, _mock)
self.instance_mgr.instances.update({
i.id_: i for i in instances
})
self.instance_mgr.instances.count = len(instances)
def test_update_state_gone(self):
self.update_state_p.stop()
self.fake_driver.get_state.return_value = states.GONE
self.assertEqual(
states.GONE,
self.instance_mgr.update_state(self.ctx)
)
def test_update_state_down_no_backing_instances(self):
self.update_state_p.stop()
self.fake_driver.get_state.return_value = states.UP
self.instance_mgr.instances.__nonzero__.return_value = False
self.assertEqual(
states.DOWN,
self.instance_mgr.update_state(self.ctx)
)
self.assertEqual(
states.DOWN,
self.instance_mgr.state
)
def test_update_state_degraded(self):
self.update_state_p.stop()
self.fake_driver.get_state.return_value = states.UP
self.instance_mgr.instances.cluster_degraded = True
self.assertEqual(
states.DEGRADED,
self.instance_mgr.update_state(self.ctx)
)
self.assertEqual(
states.DEGRADED,
self.instance_mgr.state
)
def test_update_state_booting(self):
self.update_state_p.stop()
self.fake_driver.get_state.return_value = states.UP
self.instance_mgr.instances.validate_ports.return_value = \
([], [mock.Mock()]) # (has_ports, no_ports)
self.assertEqual(
states.BOOTING,
self.instance_mgr.update_state(self.ctx)
)
def test_update_state_down_all_instances_dead(self):
self.update_state_p.stop()
self.instance_mgr.state = states.CONFIGURED
self.instance_mgr.instances.validate_ports.return_value = \
([mock.Mock()], []) # (has_ports, no_ports)
self.instance_mgr.instances.are_alive.return_value = \
([], [mock.Mock()]) # (alive, dead)
self.assertEqual(
states.DOWN,
self.instance_mgr.update_state(self.ctx)
)
def test_update_state_degraded_some_instances_dead(self):
self.update_state_p.stop()
self.instance_mgr.state = states.CONFIGURED
self.instance_mgr.instances.validate_ports.return_value = \
([mock.Mock()], []) # (has_ports, no_ports)
self.instance_mgr.instances.are_alive.return_value = \
([mock.Mock()], [mock.Mock()]) # (alive, dead)
self.assertEqual(
states.DEGRADED,
self.instance_mgr.update_state(self.ctx)
)
def test_update_state_up(self):
self.update_state_p.stop()
self.instance_mgr.state = states.BOOTING
self.instance_mgr.instances.validate_ports.return_value = \
([mock.Mock()], []) # (has_ports, no_ports)
self.instance_mgr.instances.are_alive.return_value = \
([mock.Mock()], []) # (alive, dead)
self.assertEqual(
states.UP,
self.instance_mgr.update_state(self.ctx)
)
def test_update_state_configured(self):
self.update_state_p.stop()
self.instance_mgr.log = mock.Mock(
info=mock.Mock())
self.instance_mgr.state = states.CONFIGURED
self.instance_mgr.instances.validate_ports.return_value = \
([mock.Mock()], []) # (has_ports, no_ports)
self.instance_mgr.instances.are_alive.return_value = \
([mock.Mock(booting=False)], []) # (alive, dead)
self.assertEqual(
states.CONFIGURED,
self.instance_mgr.update_state(self.ctx)
)
self.instance_mgr.update_state(self.ctx),
self.instance_mgr.update_state(self.ctx),
self.instance_mgr.update_state(self.ctx),
# ensure the boot was logged only once
self.assertEqual(1, len(self.instance_mgr.log.info.call_args_list))
@mock.patch('time.sleep')
def test_boot_success(self, sleep):
self.next_state = states.UP
self.instance_mgr.boot(self.ctx)
self.assertEqual(states.BOOTING, self.instance_mgr.state)
self.instance_mgr.instances.create.assert_called_with(
self.ctx)
self.assertEqual(1, self.instance_mgr.attempts)
@mock.patch('time.sleep')
def test_boot_instance_deleted(self, sleep):
self.instance_mgr.instances.__nonzero__.return_value = False
self.instance_mgr.boot(self.ctx)
# a deleted VM should reset the vm mgr state and not as a failed
# attempt
self.assertEqual(0, self.instance_mgr.attempts)
@mock.patch('time.sleep')
def test_boot_exception(self, sleep):
self.instance_mgr.instances.create.side_effect = RuntimeError
self.instance_mgr.boot(self.ctx)
self.assertEqual(states.DOWN, self.instance_mgr.state)
self.instance_mgr.instances.create.assert_called_with(
self.ctx)
self.assertEqual(1, self.instance_mgr.attempts)
def test_stop_success(self):
self.instance_mgr.state = states.UP
instance = instance_info()
self.set_instances_container_mocks(
instances=[instance],
mocks=[
('destroy', mock.Mock()),
('update_ports', mock.Mock())])
self.instance_mgr.stop(self.ctx)
self.instance_mgr.instances.destroy.assert_called_with(self.ctx)
self.instance_mgr.resource.delete_ports.assert_called_once_with(
self.ctx)
self.assertEqual(states.DOWN, self.instance_mgr.state)
def test_stop_fail(self):
self.instance_mgr.state = states.UP
self.set_instances_container_mocks(
instances=[instance_info()],
mocks=[
('destroy', mock.Mock()),
('update_ports', mock.Mock())])
self.instance_mgr.instances.destroy.side_effect = Exception
self.instance_mgr.stop(self.ctx)
self.assertEqual(states.UP, self.instance_mgr.state)
self.fake_driver.delete_ports.assert_called_with(self.ctx)
def test_stop_router_already_deleted_from_neutron(self):
self.instance_mgr.state = states.GONE
instance = instance_info()
self.set_instances_container_mocks(
instances=[instance],
mocks=[
('destroy', mock.Mock()),
('update_ports', mock.Mock())])
self.instance_mgr.stop(self.ctx)
self.instance_mgr.instances.destroy.assert_called_with(self.ctx)
self.instance_mgr.resource.delete_ports.assert_called_once_with(
self.ctx)
self.assertEqual(states.GONE, self.instance_mgr.state)
def test_stop_no_inst_router_already_deleted_from_neutron(self):
self.instance_mgr.state = states.GONE
self.set_instances_container_mocks(
instances=[],
mocks=[
('destroy', mock.Mock()),
('update_ports', mock.Mock())])
self.instance_mgr.stop(self.ctx)
self.fake_driver.delete_ports.assert_called_with(self.ctx)
self.assertEqual(states.GONE, self.instance_mgr.state)
def test_stop_instance_already_deleted_from_nova(self):
self.instance_mgr.state = states.RESTART
self.set_instances_container_mocks(
instances=[],
mocks=[
('destroy', mock.Mock()),
('update_ports', mock.Mock())])
self.instance_mgr.stop(self.ctx)
self.fake_driver.delete_ports.assert_called_with(self.ctx)
self.assertEqual(states.DOWN, self.instance_mgr.state)
def test_configure_mismatched_interfaces(self):
self.instance_mgr.instances.verify_interfaces.return_value = False
self.assertEqual(
states.REPLUG,
self.instance_mgr.configure(self.ctx)
)
def test_configure_gone(self):
self.fake_driver.get_state.return_value = states.GONE
self.assertEqual(
states.GONE, self.instance_mgr.configure(self.ctx))
def test_configure(self):
self.instance_mgr.instances.verify_interfaces.return_value = True
self.instance_mgr.instances.configure.return_value = states.RESTART
self.assertEqual(
states.RESTART,
self.instance_mgr.configure(self.ctx)
)
self.instance_mgr.instances.verify_interfaces.assert_called_with(
self.fake_driver.ports
)
self.instance_mgr.instances.configure.assert_called_with(self.ctx)
@mock.patch.object(instance_manager.InstanceManager,
'_wait_for_interface_hotplug')
def test_replug_add_new_port_success(self, wait_for_hotplug):
self.instance_mgr.state = states.REPLUG
instance = instance_info()
get_interfaces = mock.Mock(
return_value={
instance: [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address}]
}
)
self.set_instances_container_mocks(
instances=[instance], mocks=[('get_interfaces', get_interfaces)])
fake_instance = mock.MagicMock()
self.ctx.nova_client.get_instance_by_id = mock.Mock(
return_value=fake_instance)
fake_new_port = fake_add_port
self.fake_driver.ports.append(fake_new_port)
self.ctx.neutron.create_vrrp_port.return_value = fake_new_port
self.fake_driver.get_interfaces.return_value = [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address},
{'lladdr': fake_new_port.mac_address},
]
wait_for_hotplug.return_value = True
self.instance_mgr.replug(self.ctx)
self.ctx.neutron.create_vrrp_port.assert_called_with(
self.fake_driver.id, 'additional-net'
)
self.assertEqual(states.REPLUG, self.instance_mgr.state)
fake_instance.interface_attach.assert_called_once_with(
fake_new_port.id, None, None
)
self.assertIn(fake_new_port, instance.ports)
def test_replug_add_new_port_failure(self):
self.instance_mgr.state = states.REPLUG
instance = instance_info()
get_interfaces = mock.Mock(
return_value={
instance: [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address}]
}
)
self.set_instances_container_mocks(
instances=[instance],
mocks=[('get_interfaces', get_interfaces)]
)
self.fake_driver.get_interfaces.return_value = [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address}
]
fake_instance = mock.MagicMock()
fake_instance.interface_attach = mock.Mock(
side_effect=Exception,
)
self.ctx.nova_client.get_instance_by_id = mock.Mock(
return_value=fake_instance)
fake_new_port = fake_add_port
self.fake_driver.ports.append(fake_new_port)
self.ctx.neutron.create_vrrp_port.return_value = fake_new_port
self.instance_mgr.replug(self.ctx)
self.assertEqual(states.RESTART, self.instance_mgr.state)
fake_instance.interface_attach.assert_called_once_with(
fake_new_port.id, None, None)
@mock.patch.object(instance_manager.InstanceManager,
'_wait_for_interface_hotplug')
def test_replug_add_new_port_failed_degraded(self, wait_for_hotplug):
self.conf.hotplug_timeout = 2
self.instance_mgr.state = states.REPLUG
instance_1 = instance_info()
instance_2 = instance_info()
get_interfaces = mock.Mock(
return_value={
instance_1: [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address}],
instance_2: [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address}]
}
)
self.set_instances_container_mocks(
instances=[instance_1, instance_2],
mocks=[('get_interfaces', get_interfaces)])
self.instance_mgr.instances.update({
i.id_: i for i in [instance_1, instance_2]
})
instances = []
for i in range(2):
fake_instance = mock.MagicMock()
fake_instance.interface_attach = mock.Mock()
instances.append(fake_instance)
instances[1].interface_attach.side_effect = Exception
self.ctx.nova_client.get_instance_by_id.side_effect = instances
fake_new_port = fake_add_port
self.fake_driver.ports.append(fake_new_port)
self.ctx.neutron.create_vrrp_port.return_value = fake_new_port
wait_for_hotplug.return_value = True
self.instance_mgr.replug(self.ctx)
self.assertEqual(states.DEGRADED, self.instance_mgr.state)
for instance in instances:
instance.interface_attach.assert_called_with(
fake_new_port.id, None, None)
self.assertNotIn(instances[1], self.instance_mgr.instances.values())
@mock.patch.object(instance_manager.InstanceManager,
'_wait_for_interface_hotplug')
def test_replug_add_new_port_hotplug_failed_degraded(self,
wait_for_hotplug):
self.instance_mgr.state = states.REPLUG
instance_1 = instance_info()
instance_2 = instance_info()
get_interfaces = mock.Mock(
return_value={
instance_1: [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address}],
instance_2: [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address}]
}
)
self.set_instances_container_mocks(
instances=[instance_1, instance_2],
mocks=[('get_interfaces', get_interfaces)])
fake_new_port = fake_add_port
instances = []
for i in range(2):
fake_instance = mock.MagicMock()
fake_instance.interface_attach = mock.Mock()
instances.append(fake_instance)
self.ctx.nova_client.get_instance_by_id.side_effect = instances
fake_new_port = fake_add_port
self.fake_driver.ports.append(fake_new_port)
self.ctx.neutron.create_vrrp_port.return_value = fake_new_port
# the second instance fails to hotplug
wait_for_hotplug.side_effect = [True, False]
self.instance_mgr.replug(self.ctx)
self.assertEqual(states.DEGRADED, self.instance_mgr.state)
for instance in instances:
instance.interface_attach.assert_called_with(
fake_new_port.id, None, None)
self.assertNotIn(instances[1], self.instance_mgr.instances.values())
@mock.patch.object(instance_manager.InstanceManager,
'_wait_for_interface_hotplug')
def test_replug_remove_port_success(self, wait_for_hotplug):
self.instance_mgr.state = states.REPLUG
self.fake_driver.ports = [fake_ext_port, fake_int_port]
instance_1 = instance_info()
instance_1.ports.append(fake_add_port)
get_interfaces = mock.Mock(
return_value={
# Instance contains an extra port, it will be removed
instance_1: [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address},
{'lladdr': fake_add_port.mac_address},
],
}
)
self.set_instances_container_mocks(
instances=[instance_1],
mocks=[('get_interfaces', get_interfaces)])
fake_instance = mock.MagicMock()
self.ctx.nova_client.get_instance_by_id = mock.Mock(
return_value=fake_instance)
wait_for_hotplug.return_value = True
self.instance_mgr.replug(self.ctx)
self.assertEqual(states.REPLUG, self.instance_mgr.state)
fake_instance.interface_detach.assert_called_once_with(
fake_add_port.id)
self.assertNotIn(fake_add_port, instance_1.ports)
def test_replug_remove_port_failure(self):
self.instance_mgr.state = states.REPLUG
self.fake_driver.ports = [fake_ext_port, fake_int_port]
instance_1 = instance_info()
instance_1.ports.append(fake_add_port)
get_interfaces = mock.Mock(
return_value={
# Instance contains an extra port, it will be removed
instance_1: [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address},
{'lladdr': fake_add_port.mac_address}],
}
)
self.set_instances_container_mocks(
instances=[instance_1],
mocks=[('get_interfaces', get_interfaces)])
fake_instance = mock.MagicMock()
self.ctx.nova_client.get_instance_by_id = mock.Mock(
return_value=fake_instance)
fake_instance.interface_detach.side_effect = Exception
self.instance_mgr.replug(self.ctx)
self.assertEqual(states.RESTART,
self.instance_mgr.state)
fake_instance.interface_detach.assert_called_once_with(
fake_add_port.id
)
@mock.patch.object(instance_manager.InstanceManager,
'_wait_for_interface_hotplug')
def test_replug_remove_port_hotplug_failed(self, wait_for_hotplug):
self.instance_mgr.state = states.REPLUG
self.fake_driver.ports = [fake_ext_port, fake_int_port]
instance_1 = instance_info()
instance_1.ports.append(fake_add_port)
get_interfaces = mock.Mock(
return_value={
# Instance contains an extra port, it will be removed
instance_1: [
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address},
{'lladdr': fake_add_port.mac_address}
],
}
)
self.set_instances_container_mocks(
instances=[instance_1],
mocks=[('get_interfaces', get_interfaces)])
fake_instance = mock.MagicMock()
self.ctx.nova_client.get_instance_by_id = mock.Mock(
return_value=fake_instance)
wait_for_hotplug.return_value = False
self.instance_mgr.replug(self.ctx)
self.assertEqual(states.RESTART,
self.instance_mgr.state)
fake_instance.interface_detach.assert_called_once_with(
fake_add_port.id
)
def test_wait_for_interface_hotplug_true(self):
instance = instance_info()
self.fake_driver.get_interfaces.side_effect = [
[
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
],
[
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
],
[
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
{'lladdr': fake_int_port.mac_address},
],
]
self.assertEqual(
True, self.instance_mgr._wait_for_interface_hotplug(instance))
self.assertEqual(
3, len(self.fake_driver.get_interfaces.call_args_list))
def test_wait_for_interface_hotplug_false(self):
self.conf.hotplug_timeout = 5
instance = instance_info()
self.fake_driver.get_interfaces.side_effect = [
[
{'lladdr': fake_mgt_port.mac_address},
{'lladdr': fake_ext_port.mac_address},
]
for i in six.moves.range(5)]
self.assertEqual(
False, self.instance_mgr._wait_for_interface_hotplug(instance))
self.assertEqual(
4, len(self.fake_driver.get_interfaces.call_args_list))
def test_set_error_when_booting(self):
self.instance_mgr.state = states.BOOTING
self.instance_mgr.set_error(self.ctx)
self.fake_driver.synchronize_state.assert_called_once_with(
self.ctx, state='error')
self.assertEqual(states.ERROR, self.instance_mgr.state)
def test_clear_error_when_gone(self):
self.instance_mgr.state = states.GONE
self.instance_mgr.clear_error(self.ctx)
self.fake_driver.synchronize_state(self.ctx, 'error')
self.assertEqual(states.DOWN, self.instance_mgr.state)
@mock.patch('time.sleep')
def test_boot_success_after_error(self, sleep):
self.next_state = states.UP
rtr = mock.sentinel.router
self.ctx.neutron.get_router_detail.return_value = rtr
rtr.id = 'ROUTER1'
rtr.management_port = None
rtr.external_port = None
rtr.ports = mock.MagicMock()
rtr.ports.__iter__.return_value = []
self.instance_mgr.set_error(self.ctx)
self.instance_mgr.boot(self.ctx)
self.assertEqual(states.BOOTING, self.instance_mgr.state)
self.instance_mgr.instances.create.assert_called_with(self.ctx)
def test_error_cooldown(self):
self.config(error_state_cooldown=30)
self.assertIsNone(self.instance_mgr.last_error)
self.assertFalse(self.instance_mgr.error_cooldown)
self.instance_mgr.state = states.ERROR
self.instance_mgr.last_error = datetime.utcnow() - timedelta(seconds=1)
self.assertTrue(self.instance_mgr.error_cooldown)
self.instance_mgr.last_error = datetime.utcnow() - timedelta(minutes=5)
self.assertFalse(self.instance_mgr.error_cooldown)
def test_ensure_cache(self):
self.set_instances_container_mocks(mocks=[
('update_ports', mock.Mock())
])
self.instance_mgr.instances['fake_instance_id1'] = 'stale_instance1'
self.instance_mgr.instances['fake_instance_id2'] = 'stale_instance2'
fake_inst_1 = mock.Mock(id_='fake_instance_id1')
fake_inst_2 = mock.Mock(id_='fake_instance_id2')
self.ctx.nova_client.get_instances_for_obj.return_value = [
fake_inst_1, fake_inst_2]
def ensured_cache(self, ctx):
pass
wrapped = instance_manager.ensure_cache(ensured_cache)
wrapped(self.instance_mgr, self.ctx)
exp_updated_instances = {
'fake_instance_id1': fake_inst_1,
'fake_instance_id2': fake_inst_2,
}
self.assertEqual(
exp_updated_instances, self.instance_mgr.instances)
self.instance_mgr.instances.update_ports.assert_called_with(self.ctx)
class TestBootAttemptCounter(base.RugTestBase):
def setUp(self):
super(TestBootAttemptCounter, self).setUp()
self.c = instance_manager.BootAttemptCounter()
def test_start(self):
self.c.start()
self.assertEqual(1, self.c._attempts)
self.c.start()
self.assertEqual(2, self.c._attempts)
def test_reset(self):
self.c._attempts = 2
self.c.reset()
self.assertEqual(0, self.c._attempts)
class TestInstanceGroupManager(base.RugTestBase):
def setUp(self):
super(TestInstanceGroupManager, self).setUp()
self.ctx = fakes.fake_worker_context()
self.fake_driver = fakes.fake_driver()
self.group_mgr = instance_manager.InstanceGroupManager(
log=mock.Mock(), resource=self.fake_driver)
name = 'ak-resource-' + str(uuid.uuid4())
self.instance_1 = instance_info(mgt_port=fake_mgt_port,
name=name + '_0')
self.instance_2 = instance_info(mgt_port=fake_add_port,
name=name + '_1')
self.instances = [self.instance_1, self.instance_2]
[self.group_mgr.add_instance(i) for i in self.instances]
def test_validate_ports(self):
self.instance_2.management_port = None
has_ports, no_ports = self.group_mgr.validate_ports()
self.assertIn(self.instance_1, has_ports)
self.assertIn(self.instance_2, no_ports)
def test_are_alive_all_alive(self):
self.fake_driver.is_alive.side_effect = [
False, False, True, False, True]
alive, dead = self.group_mgr.are_alive()
self.assertEqual(sorted(self.instances), sorted(alive))
def test_are_alive_all_dead(self):
self.fake_driver.is_alive.return_value = False
alive, dead = self.group_mgr.are_alive()
self.assertEqual(sorted(self.instances), sorted(dead))
self.assertEqual([], alive)
def test_are_alive_some_dead(self):
self.group_mgr = instance_manager.InstanceGroupManager(
log=mock.Mock(), resource=self.fake_driver)
self.instance_1 = instance_info(mgt_port=fake_mgt_port)
self.instance_2 = instance_info(mgt_port=fake_add_port)
instances = [self.instance_1, self.instance_2]
[self.group_mgr.add_instance(i) for i in instances]
def fake_is_alive(mgt_addr, i1=self.instance_1, i2=self.instance_2):
# tag instance 2 as dead
if mgt_addr == fake_add_port.fixed_ips[0].ip_address:
return False
else:
return True
[self.group_mgr.add_instance(i) for i in instances]
self.fake_driver.is_alive = fake_is_alive
alive, dead = self.group_mgr.are_alive()
self.assertEqual([self.instance_2], dead)
self.assertEqual([self.instance_1], alive)
def test_update_ports(self):
self.ctx.neutron.get_ports_for_instance.side_effect = [
('instance1_mgt_port', ['instance1_inst_port']),
('instance2_mgt_port', ['instance2_inst_port']),
]
self.group_mgr.update_ports(self.ctx)
self.assertEqual('instance1_mgt_port', self.instance_1.management_port)
self.assertEqual(['instance1_inst_port'], self.instance_1.ports)
self.assertEqual('instance2_mgt_port', self.instance_2.management_port)
self.assertEqual(['instance2_inst_port'], self.instance_2.ports)
def test_get_interfaces(self):
self.fake_driver.get_interfaces.side_effect = [
['instance1_interfaces'],
['instance2_interfaces'],
]
self.group_mgr._alive = [i.id_ for i in self.instances]
interfaces_dict = self.group_mgr.get_interfaces()
self.assertIn(
(self.instance_1, ['instance1_interfaces']),
interfaces_dict.items())
self.assertIn(
(self.instance_2, ['instance2_interfaces']),
interfaces_dict.items())
def test_get_interfaces_skip_dead(self):
self.fake_driver.get_interfaces.side_effect = [
['instance1_interfaces'],
['instance2_interfaces'],
]
self.group_mgr._alive = [self.instance_1.id_]
interfaces_dict = self.group_mgr.get_interfaces()
self.assertIn(
(self.instance_1, ['instance1_interfaces']),
interfaces_dict.items())
self.assertNotIn(
(self.instance_2, ['instance2_interfaces']),
interfaces_dict.items())
@mock.patch('astara.instance_manager.InstanceGroupManager.get_interfaces')
def test_verify_interfaces_true(self, fake_get_interfaces):
fake_get_interfaces.return_value = {
self.instance_1: [
{'lladdr': p.mac_address}
for p in self.instance_1.ports +
[self.instance_1.management_port]
],
self.instance_2: [
{'lladdr': p.mac_address}
for p in self.instance_2.ports +
[self.instance_2.management_port]
]
}
ports = [fake_ext_port, fake_int_port]
self.assertTrue(self.group_mgr.verify_interfaces(ports))
@mock.patch('astara.instance_manager.InstanceGroupManager.get_interfaces')
def test_verify_interfaces_false_missing_inst_port(self,
fake_get_interfaces):
fake_get_interfaces.return_value = {
self.instance_1: [
{'lladdr': p.mac_address}
for p in self.instance_1.ports +
[self.instance_1.management_port]
],
self.instance_2: [
{'lladdr': p.mac_address}
for p in self.instance_2.ports +
[self.instance_2.management_port]
]
}
ports = [fake_ext_port, fake_int_port, fake_add_port]
self.assertFalse(self.group_mgr.verify_interfaces(ports))
@mock.patch('astara.instance_manager.InstanceGroupManager.get_interfaces')
def test_verify_interfaces_false_missing_macs(self, fake_get_interfaces):
fake_get_interfaces.return_value = {
self.instance_1: [
{'lladdr': p.mac_address}
for p in self.instance_1.ports
],
self.instance_2: [
{'lladdr': p.mac_address}
for p in self.instance_2.ports]
}
ports = [fake_ext_port, fake_int_port]
self.assertFalse(self.group_mgr.verify_interfaces(ports))
def test__update_config_success(self):
self.fake_driver.update_config.side_effect = [
Exception, Exception, True]
self.assertTrue(self.group_mgr._update_config(self.instance_1, {}))
self.fake_driver.update_config.assert_called_with(
self.instance_1.management_address, {})
def test__update_config_fail(self):
self.fake_driver.update_config.side_effect = Exception
self.assertFalse(self.group_mgr._update_config(self.instance_1, {}))
self.fake_driver.update_config.assert_called_with(
self.instance_1.management_address, {})
def test__ha_config(self):
instance_1_ha_config = self.group_mgr._ha_config(self.instance_1)
instance_2_ha_config = self.group_mgr._ha_config(self.instance_2)
self.assertEqual(
{
'priority': 100,
'peers': [self.instance_2.management_address],
},
instance_1_ha_config)
self.assertEqual(
{
'priority': 50,
'peers': [self.instance_1.management_address],
},
instance_2_ha_config)
@mock.patch('astara.instance_manager.InstanceGroupManager._update_config')
@mock.patch('astara.instance_manager.InstanceGroupManager._ha_config')
@mock.patch('astara.instance_manager._generate_interface_map')
@mock.patch('astara.instance_manager.InstanceGroupManager.get_interfaces')
def test_configure_success(self, fake_get_interfaces, fake_gen_iface_map,
fake_ha_config, fake_update_config):
fake_ha_config.return_value = {'fake_ha_config': 'peers'}
self.fake_driver.is_ha = True
self.fake_driver.build_config.side_effect = [
{'instance_1_config': 'config'},
{'instance_2_config': 'config'},
]
fake_get_interfaces.return_value = collections.OrderedDict([
(self.instance_1, [
{'lladdr': p.mac_address} for p in self.instance_1.ports +
[self.instance_1.management_port]]),
(self.instance_2, [
{'lladdr': p.mac_address} for p in self.instance_2.ports +
[self.instance_2.management_port]])
])
fake_update_config.return_value = True
self.assertEqual(states.CONFIGURED, self.group_mgr.configure(self.ctx))
self.assertIn(
mock.call(
self.instance_1,
{
'instance_1_config': 'config',
'ha_config': {'fake_ha_config': 'peers'}
}),
fake_update_config.call_args_list)
self.assertIn(
mock.call(
self.instance_2,
{
'instance_2_config': 'config',
'ha_config': {'fake_ha_config': 'peers'}
}),
fake_update_config.call_args_list)
@mock.patch('astara.instance_manager.InstanceGroupManager._update_config')
@mock.patch('astara.instance_manager.InstanceGroupManager._ha_config')
@mock.patch('astara.instance_manager._generate_interface_map')
@mock.patch('astara.instance_manager.InstanceGroupManager.get_interfaces')
def test_configure_failed_all(self, fake_get_interfaces,
fake_gen_iface_map, fake_ha_config,
fake_update_config):
fake_ha_config.return_value = {'fake_ha_config': 'peers'}
self.fake_driver.is_ha = True
self.fake_driver.build_config.side_effect = [
{'instance_1_config': 'config'},
{'instance_2_config': 'config'},
]
fake_get_interfaces.return_value = collections.OrderedDict([
(self.instance_1, [
{'lladdr': p.mac_address} for p in self.instance_1.ports +
[self.instance_1.management_port]]),
(self.instance_2, [
{'lladdr': p.mac_address} for p in self.instance_2.ports +
[self.instance_2.management_port]])
])
fake_update_config.return_value = False
self.assertEqual(states.RESTART, self.group_mgr.configure(self.ctx))
@mock.patch('astara.instance_manager.InstanceGroupManager._update_config')
@mock.patch('astara.instance_manager.InstanceGroupManager._ha_config')
@mock.patch('astara.instance_manager._generate_interface_map')
@mock.patch('astara.instance_manager.InstanceGroupManager.get_interfaces')
def test_configure_failed_some(self, fake_get_interfaces,
fake_gen_iface_map, fake_ha_config,
fake_update_config):
fake_ha_config.return_value = {'fake_ha_config': 'peers'}
self.fake_driver.is_ha = True
self.fake_driver.build_config.side_effect = [
{'instance_1_config': 'config'},
{'instance_2_config': 'config'},
]
fake_get_interfaces.return_value = collections.OrderedDict([
(self.instance_1, [
{'lladdr': p.mac_address} for p in self.instance_1.ports +
[self.instance_1.management_port]]),
(self.instance_2, [
{'lladdr': p.mac_address} for p in self.instance_2.ports +
[self.instance_2.management_port]])])
fake_update_config.side_effect = [False, True]
self.assertEqual(states.DEGRADED, self.group_mgr.configure(self.ctx))
@mock.patch('astara.instance_manager.InstanceGroupManager._update_config')
@mock.patch('astara.instance_manager.InstanceGroupManager._ha_config')
@mock.patch('astara.instance_manager._generate_interface_map')
@mock.patch('astara.instance_manager.InstanceGroupManager.get_interfaces')
def test_configure_degraded_waiting(self, fake_get_interfaces,
fake_gen_iface_map, fake_ha_config,
fake_update_config):
fake_ha_config.return_value = {'fake_ha_config': 'peers'}
self.fake_driver.is_ha = True
self.fake_driver.build_config.side_effect = [
{'instance_1_config': 'config'},
{'instance_2_config': 'config'},
]
fake_get_interfaces.return_value = collections.OrderedDict([
(self.instance_1, [
{'lladdr': p.mac_address} for p in self.instance_1.ports +
[self.instance_1.management_port]])
])
fake_update_config.return_value = True
self.assertEqual(states.DEGRADED, self.group_mgr.configure(self.ctx))
def test_delete(self):
self.group_mgr.delete(self.instance_2)
self.assertNotIn(
self.instance_2, self.group_mgr.instances)
def test_refresh(self):
self.ctx.nova_client.update_instance_info.return_value = True
self.group_mgr.refresh(self.ctx)
[self.assertIn(mock.call(i),
self.ctx.nova_client.update_instance_info.call_args_list)
for i in self.instances]
[self.assertIn(i, self.group_mgr.instances) for i in self.instances]
def test_refresh_instance_gone(self):
self.ctx.nova_client.update_instance_info.side_effect = [True, None]
self.group_mgr.refresh(self.ctx)
[self.assertIn(mock.call(i),
self.ctx.nova_client.update_instance_info.call_args_list)
for i in self.instances]
self.assertIn(self.instance_1, self.group_mgr.instances)
self.assertNotIn(self.instance_2, self.group_mgr.instances)
def test_destroy(self):
self.group_mgr.destroy(self.ctx)
self.ctx.nova_client.delete_instances_and_wait.assert_called_with(
self.group_mgr.instances)
def test_remove(self):
self.group_mgr.remove(self.ctx, self.instance_1)
self.ctx.nova_client.destroy_instance.assert_called_with(
self.instance_1)
self.assertNotIn(self.instance_1, self.group_mgr.instances)
def test_next_instance_index(self):
self.assertEqual(
2, self.group_mgr.next_instance_index)
def test_next_instance_index_empty(self):
group_mgr = instance_manager.InstanceGroupManager(
log=mock.Mock(), resource=self.fake_driver)
self.assertEqual(
0, group_mgr.next_instance_index)
def test_create_all(self):
[self.group_mgr.delete(i) for i in self.instances]
self.ctx.nova_client.boot_instance.side_effect = [
instance_info(name='new-instance_0'),
instance_info(name='new-instance_1'),
]
self.group_mgr.create(self.ctx)
self.assertEqual(
2, len(self.ctx.nova_client.boot_instance.call_args_list))
def test_create_some(self):
self.group_mgr.delete(self.instance_1)
self.ctx.nova_client.boot_instance.side_effect = [
instance_info(name='new-instance_0'),
]
self.group_mgr.create(self.ctx)
self.assertEqual(
1, len(self.ctx.nova_client.boot_instance.call_args_list))
self.ctx.nova_client.boot_instance.assert_called_with(
resource_type=self.fake_driver.RESOURCE_NAME,
prev_instance_info=None,
name='ak-FakeDriver-fake_resource_id_2',
image_uuid=self.fake_driver.image_uuid,
flavor=self.fake_driver.flavor,
make_ports_callback=self.fake_driver.make_ports(self.ctx),
)
def test_required_instance_count(self):
self.fake_driver.is_ha = True
self.assertEqual(2, self.group_mgr.required_instance_count)
self.fake_driver.is_ha = False
self.assertEqual(1, self.group_mgr.required_instance_count)
def test_instance_count(self):
self.assertEqual(2, self.group_mgr.instance_count)
def test_cluster_degraded_false(self):
self.assertFalse(self.group_mgr.cluster_degraded)
def test_cluster_degraded_true(self):
self.group_mgr.delete(self.instance_1)
self.assertTrue(self.group_mgr.cluster_degraded)
def test_add_instance(self):
instance_3 = instance_info()
self.group_mgr.add_instance(instance_3)
self.assertIn(instance_3, self.group_mgr.instances)