fuel-octane/octane/tests/test_env.py

369 lines
12 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 io
import mock
import pytest
from octane import magic_consts
from octane.util import env as env_util
@pytest.fixture
def mock_os_path(mocker):
res = mocker.patch('os.path')
res.exists.return_value = True
res.dirname.return_value = magic_consts.FUEL_CACHE
return res
def test_find_node_deployment_info():
roles = ['controller', 'primary-controller']
node = mock.Mock()
node.id = 1
res = env_util.find_node_deployment_info(node, roles, DEPLOYMENT_INFO)
assert res == DEPLOYMENT_INFO[0]
def test_find_node_deployment_info_none():
roles = ['controller', 'primary-controller']
node = mock.Mock()
node.id = 2
res = env_util.find_node_deployment_info(node, roles, DEPLOYMENT_INFO)
assert res is None
@pytest.mark.parametrize("facts", [[{'uid': 1}, {'uid': 2}]])
@pytest.mark.parametrize("env_id", [1])
@pytest.mark.parametrize("os_path_exist", [True, False])
def test_write_facts_to_dir(mocker, mock_open, mock_os_path,
os_path_exist, facts, env_id):
mock_os_makedirs = mocker.patch("os.makedirs")
mock_os_path.exists.return_value = os_path_exist
buf = io.BytesIO()
mock_open.return_value.write.side_effect = buf.write
mock_yaml = mocker.patch("yaml.safe_dump")
env_util.write_facts_to_dir(facts, env_id)
join_calls = [mock.call('/tmp', "deployment_{0}.orig".format(env_id))]
join_calls.extend(
[mock.call(mock_os_path.join.return_value,
"{0}.yaml".format(fact['uid'])) for fact in facts])
mock_os_path.join.assert_has_calls(join_calls)
mock_open.assert_called_with(mock_os_path.join.return_value, "w")
mock_yaml.assert_has_calls(
[mock.call(fact, mock_open.return_value, default_flow_style=False)
for fact in facts]
)
if not os_path_exist:
mock_os_makedirs.assert_called_once_with(
mock_os_path.join.return_value
)
else:
mock_os_makedirs.assert_not_called()
@pytest.mark.parametrize("env_id", [1])
def test_get_dir_deployment_info(mock_os_path, env_id):
env_util.get_dir_deployment_info(env_id)
mock_os_path.join.assert_called_once_with(
'/tmp', "deployment_{0}.orig".format(env_id)
)
def test_get_one_node_of(mocker):
get_nodes = mocker.patch('octane.util.env.get_nodes')
get_nodes.return_value = iter(['node1', 'node2'])
node = env_util.get_one_node_of(mock.Mock(), 'controller')
assert node == 'node1'
DEPLOYMENT_INFO = [{
'uid': '1',
'role': 'primary-controller',
'nodes': [{
'uid': '1',
'role': 'primary-controller',
'name': 'test',
}, {
'uid': '1',
'role': 'zabbix',
'name': 'test',
}, {
'uid': '2',
'role': 'compute',
'name': 'test2',
}],
}, {
'uid': '1',
'role': 'zabbix',
'nodes': [{
'uid': '1',
'role': 'primary-controller',
'name': 'test',
}, {
'uid': '1',
'role': 'zabbix',
'name': 'test',
}, {
'uid': '2',
'role': 'compute',
'name': 'test2',
}],
}, {
'uid': '2',
'role': 'compute',
'nodes': [{
'uid': '1',
'role': 'primary-controller',
'name': 'test',
}, {
'uid': '1',
'role': 'zabbix',
'name': 'test',
}, {
'uid': '2',
'role': 'compute',
'name': 'test2',
}],
}]
ENV_SETTINGS = {
'editable': {
'public_ssl': {
'horizon': {
'value': None
},
'services': {
'value': None
}
},
'external_ntp': {
'ntp_list': {
'value': None
}
},
'external_dns': {
'dns_list': {
'value': None
}
},
'provision': {
'method': {
'value': None
}
}
}
}
NORMALIZED_DATA = [
{"name": "test", "id": 2},
{"name": "test"},
]
@pytest.mark.parametrize("normalized_data,is_error",
zip(NORMALIZED_DATA, [False, True]))
def test_clone_env(mocker, normalized_data, is_error):
release = mock.Mock(data={'name': "14.04", 'id': 2})
mock_fuel_call = mocker.patch('octane.util.env.fuel2_env_call')
mock_json_loads = mocker.patch('json.loads')
mock_normalized = mocker.patch(
'octane.util.helpers.normalized_cliff_show_json'
)
mock_normalized.return_value = normalized_data
orig_id = 1
if not is_error:
seed_id = env_util.clone_env(orig_id, release)
assert seed_id == 2
mock_json_loads.assert_called_once_with(mock_fuel_call.return_value)
else:
with pytest.raises(Exception) as exc_info:
assert ("Couldn't find new environment ID in fuel CLI output:"
"\n{0}".format(normalized_data)) == exc_info.value.args[0]
def test_copy_vips(mock_subprocess):
env_id = -1
env = mock.Mock(data={'id': env_id})
env_util.copy_vips(env)
mock_subprocess.assert_called_once_with(
['fuel2', '--debug', 'env', 'copy', 'vips', str(env_id)]
)
@pytest.mark.parametrize("mock_method,version,expected_result",
[("cobbler", "5.1.1", True),
("image", "6.0", False),
("cobbler", "6.0", True),
("image", "6.0", False),
("image", "7.0", False),
("image", "", False),
(None, None, False)])
def test_incompatible_provision_method(mocker,
mock_method,
version,
expected_result):
mock_env = mock.Mock()
mock_env.data = {"fuel_version": version, "id": "test"}
mock_get_method = mocker.patch("octane.util.env.get_env_provision_method")
mock_get_method.return_value = mock_method
if version:
result = env_util.incompatible_provision_method(mock_env)
assert expected_result == result
else:
with pytest.raises(Exception) as exc_info:
env_util.incompatible_provision_method(mock_env)
assert ("Cannot find version of environment {0}:"
" attribute 'fuel_version' missing or has incorrect value"
.format(mock_env.data["id"])) == exc_info.value.args[0]
@pytest.mark.parametrize("provision,compat", [
(True, True),
(False, True),
])
def test_move_nodes(mocker, mock_subprocess, provision, compat):
env = mock.Mock()
env.data = {
'id': 'test-id',
}
nodes = [mock.Mock(), mock.Mock()]
for idx, node in enumerate(nodes, 1):
node.data = {'id': str(idx)}
mock_move_nodes = mocker.patch(
"octane.util.env.fuel2_env_call")
mock_create_configdrive = mocker.patch(
"octane.util.disk.create_configdrive_partition")
mock_wait_for = mocker.patch(
"octane.util.env.wait_for_nodes")
mock_get_provision_method = mocker.patch(
"octane.util.env.incompatible_provision_method")
mock_get_provision_method.return_value = compat
env_util.move_nodes(env, nodes, provision)
if provision:
assert mock_create_configdrive.call_args_list == \
[mock.call(node) for node in nodes]
mock_wait_for.assert_called_once_with(nodes, 'provisioned')
mock_move_nodes.assert_called_once_with(
["move", "node", "test-id", "1", "2"])
else:
mock_move_nodes.assert_called_once_with(
["move", "node", "--no-provision", "test-id", "1", "2"])
assert mock_create_configdrive.call_args_list == []
assert mock_wait_for.call_args_list == []
def test_deploy_nodes_without_tasks(mocker):
env = mock.Mock()
nodes = [mock.Mock(), mock.Mock()]
tasks_to_skip = ['task-0', 'task-1']
mock_wait = mocker.patch("octane.util.env.wait_for_nodes_tasks")
env_util.deploy_nodes_without_tasks(env, nodes, tasks_to_skip)
env.get_tasks.assert_called_once_with(skip=tasks_to_skip)
env.execute_tasks.assert_called_once_with(
nodes, env.get_tasks.return_value, force=False, noop_run=False)
mock_wait.assert_called_once_with(env, nodes)
@pytest.mark.parametrize("env_id,expected_url", [
(42, 'clusters/42/generated'),
])
def test_get_generated(mocker, env_id, expected_url):
m = mocker.patch("fuelclient.objects.environment.Environment.connection")
res = env_util.get_generated(env_id)
assert res is m.get_request.return_value
assert m.mock_calls == [mock.call.get_request(expected_url)]
def test_wait_for_node_stopped(mocker):
sleep = mocker.patch("time.sleep", autospec=True)
time = mocker.patch(
"time.time",
austospec=True,
side_effect=list(range(10))
)
node = mock.Mock(name='Node object')
get_fresh_data = mock.Mock(
name='get_fresh_data',
side_effect=[
{'status': 'running', 'online': True},
{'status': 'stopped', 'online': True},
]
)
node.attach_mock(get_fresh_data, 'get_fresh_data')
node.configure_mock(data={'status': 'running', 'online': True, 'id': 0})
with pytest.raises(Exception) as excinfo:
env_util.wait_for_node(node, 'success')
assert (
"Node {node_id} fell into stopped status, "
"while expected {status}".format(
node_id=0, status='success'
) in excinfo.args)
sleep.assert_called_once_with(60)
time.assert_has_calls((mock.call(), mock.call()))
@pytest.mark.parametrize("api,modified", [
([], []),
(
[
{'uid': 'common', 'common_key': 'common_value'},
{'uid': 'master'},
{'uid': 'node-1', 'common_key': 'uncommon_value'},
],
[
{'uid': 'master', 'common_key': 'common_value'},
{'uid': 'node-1', 'common_key': 'uncommon_value'},
]
),
(
[
{'uid': 'master', 'common_key': 'common_value'},
{'uid': 'node-1', 'common_key': 'uncommon_value'},
],
[
{'uid': 'master', 'common_key': 'common_value'},
{'uid': 'node-1', 'common_key': 'uncommon_value'},
]
),
])
@pytest.mark.parametrize("nodes", [None, [1, 2, 3]])
def test_util_env(mocker, api, modified, nodes):
env = mock.Mock()
env.get_default_facts.return_value = api
assert modified == env_util.get_node_default_facts(env, nodes)
env.get_default_facts.assert_called_once_with('deployment', nodes=nodes)
def test_set_upgrade_levels_for_controllers(mocker):
env = mock.Mock(data={"fuel_version": "9.1"})
node = mock.Mock()
mock_get_levels = mocker.patch("octane.util.nova.get_upgrade_levels")
mock_get_levels.return_value = "liberty"
mock_get_conts = mocker.patch("octane.util.env.get_controllers")
mock_get_conts.return_value = [node]
mock_add = mocker.patch("octane.util.node.add_compute_upgrade_levels")
mock_restart = mocker.patch("octane.util.node.restart_nova_services")
env_util.set_upgrade_levels_for_controllers(env)
mock_get_levels.assert_called_once_with("9.1")
mock_add.assert_called_once_with(node, "liberty")
mock_restart.assert_called_once_with(node)