diff --git a/octane/handlers/backup_restore/astute.py b/octane/handlers/backup_restore/astute.py index 53eb8d81..1ac38301 100644 --- a/octane/handlers/backup_restore/astute.py +++ b/octane/handlers/backup_restore/astute.py @@ -14,11 +14,13 @@ import shutil import yaml from octane.handlers.backup_restore import base +from octane import magic_consts as consts +from octane.util import auth from octane.util import puppet class AstuteArchivator(base.PathArchivator): - path = "/etc/fuel/astute.yaml" + path = consts.ASTUTE_YAML name = "astute/astute.yaml" keys_to_restore = [ @@ -59,6 +61,15 @@ class AstuteArchivator(base.PathArchivator): ("FUEL_ACCESS", ["user", "password"]), ] + def backup(self): + super(AstuteArchivator, self).backup() + + creds = self.get_backup_dict()['FUEL_ACCESS'] + if not auth.is_creds_valid(creds['user'], creds['password']): + raise Exception( + "astute.yaml file contains invalid Fuel username or password" + ) + def get_backup_dict(self): return yaml.load(self.archive.extractfile(self.name)) @@ -67,13 +78,24 @@ class AstuteArchivator(base.PathArchivator): return yaml.load(current) def pre_restore_check(self): - backup_ip = self.get_backup_dict()["ADMIN_NETWORK"]["ipaddress"] - current_ip = self.get_current_dict()["ADMIN_NETWORK"]["ipaddress"] + backup = self.get_backup_dict() + current = self.get_current_dict() + + backup_ip = backup["ADMIN_NETWORK"]["ipaddress"] + current_ip = current["ADMIN_NETWORK"]["ipaddress"] + if backup_ip != current_ip: raise Exception( "Restore allowed on machine with same ipaddress. " "Use fuel-menu to set up ipaddress to {0}".format(backup_ip)) + creds = backup.get('FUEL_ACCESS') + if creds and not auth.is_creds_valid(creds['user'], creds['password']): + raise Exception( + "Backup's astute.yaml file contains invalid" + " Fuel username or password" + ) + def restore(self): backup_yaml = self.get_backup_dict() current_yaml = self.get_current_dict() diff --git a/octane/handlers/backup_restore/postgres.py b/octane/handlers/backup_restore/postgres.py index 4c865b0b..e41a4a48 100644 --- a/octane/handlers/backup_restore/postgres.py +++ b/octane/handlers/backup_restore/postgres.py @@ -15,7 +15,6 @@ import six from octane.handlers.backup_restore import base from octane import magic_consts -from octane.util import auth from octane.util import keystone from octane.util import patch from octane.util import puppet @@ -44,8 +43,8 @@ class PostgresArchivator(base.CmdArchivator): with subprocess.popen(["sudo", "-u", "postgres", "psql"], stdin=subprocess.PIPE) as process: shutil.copyfileobj(dump, process.stdin) - with auth.set_astute_password(self.context): - puppet.apply_task(self.db) + + puppet.apply_task(self.db) class NailgunArchivator(PostgresArchivator): diff --git a/octane/handlers/backup_restore/puppet.py b/octane/handlers/backup_restore/puppet.py index a99fa308..fdd19e91 100644 --- a/octane/handlers/backup_restore/puppet.py +++ b/octane/handlers/backup_restore/puppet.py @@ -12,7 +12,6 @@ from octane.handlers.backup_restore import base from octane import magic_consts -from octane.util import auth from octane.util import keystone from octane.util import puppet from octane.util import subprocess @@ -33,7 +32,6 @@ class PuppetApplyTasks(base.Base): def restore(self): subprocess.call(["systemctl", "stop"] + self.services) - with auth.set_astute_password(self.context), \ - keystone.admin_token_auth(magic_consts.KEYSTONE_PASTE, - magic_consts.KEYSTONE_PIPELINES): + with keystone.admin_token_auth(magic_consts.KEYSTONE_PASTE, + magic_consts.KEYSTONE_PIPELINES): puppet.apply_all_tasks() diff --git a/octane/magic_consts.py b/octane/magic_consts.py index f388da03..ec34a044 100644 --- a/octane/magic_consts.py +++ b/octane/magic_consts.py @@ -162,3 +162,5 @@ COMPUTE_PREUPGRADE_PACKAGES = { "python-concurrent.futures", ] } + +ASTUTE_YAML = "/etc/fuel/astute.yaml" diff --git a/octane/tests/test_archivators.py b/octane/tests/test_archivators.py index 2751102b..ebe954b4 100644 --- a/octane/tests/test_archivators.py +++ b/octane/tests/test_archivators.py @@ -39,6 +39,11 @@ from octane.handlers.backup_restore import version (ssh.SshArchivator, "/root/.ssh/", "ssh"), ]) def test_path_backup(mocker, cls, path, name): + mocker.patch.object( + astute.AstuteArchivator, 'get_backup_dict', + return_value={'FUEL_ACCESS': {'user': '1', 'password': '2'}} + ) + mocker.patch('octane.util.auth.is_creds_valid', return_value=True) test_archive = mocker.Mock() cls(test_archive).backup() test_archive.add.assert_called_once_with(path, name) diff --git a/octane/tests/test_archivators_restore.py b/octane/tests/test_archivators_restore.py index 41f94194..7ac31390 100644 --- a/octane/tests/test_archivators_restore.py +++ b/octane/tests/test_archivators_restore.py @@ -283,8 +283,6 @@ def test_postgres_restore(mocker, cls, db, services): mock_patch = mocker.patch("octane.util.patch.applied_patch") mock_copyfileobj = mocker.patch("shutil.copyfileobj") - mock_set_astute_password = mocker.patch( - "octane.util.auth.set_astute_password") mock_apply_task = mocker.patch("octane.util.puppet.apply_task") mock_context = mock.Mock() @@ -329,7 +327,6 @@ def test_postgres_restore(mocker, cls, db, services): mock.call.admin_token().__enter__(), mock.call.admin_token().__exit__(None, None, None), ] - mock_set_astute_password.assert_called_once_with(mock_context) @pytest.mark.parametrize("keys_in_dump_file,restored", [ @@ -567,8 +564,6 @@ def test_release_restore(mocker, mock_open, content, existing_releases, calls): def test_post_restore_puppet_apply_tasks(mocker, mock_subprocess): context = backup_restore.NailgunCredentialsContext( user="admin", password="user_pswd") - mock_set_astute_password = mocker.patch( - "octane.util.auth.set_astute_password") mock_apply = mocker.patch("octane.util.puppet.apply_all_tasks") mock_admin_token = mocker.patch("octane.util.keystone.admin_token_auth") @@ -576,7 +571,6 @@ def test_post_restore_puppet_apply_tasks(mocker, mock_subprocess): archivator.restore() assert mock_apply.called - mock_set_astute_password.assert_called_once_with(context) expected_pipelines = [ "pipeline:public_api", "pipeline:admin_api", @@ -600,7 +594,10 @@ def test_post_restore_puppet_apply_tasks(mocker, mock_subprocess): def test_logs_restore( mocker, mock_open, mock_subprocess, nodes, is_dir, exception): domain_name = "test_domain" - mocker.patch("yaml.load", return_value={"DNS_DOMAIN": domain_name}) + mocker.patch("yaml.load", return_value={ + "DNS_DOMAIN": domain_name, + 'OS_USERNAME': 'a', 'OS_PASSWORD': 'b', + }) domain_names = [] fuel_client_values = [] is_link_exists = [] diff --git a/octane/tests/test_util_auth.py b/octane/tests/test_util_auth.py index bc833971..8483961d 100644 --- a/octane/tests/test_util_auth.py +++ b/octane/tests/test_util_auth.py @@ -12,49 +12,28 @@ import mock import pytest +import requests -from octane.handlers import backup_restore from octane.util import auth -class TestException(Exception): - pass +@pytest.mark.parametrize("status,valid", [ + (401, False), + (500, requests.HTTPError), + (200, True), +]) +def test_is_creds_valid(mocker, status, valid): + mock_resp = mock.Mock(spec=["status_code", "raise_for_status"]) + mock_resp.status_code = status + mocker.patch("fuelclient.client.APIClient.get_request_raw", + return_value=mock_resp) -@pytest.mark.parametrize("exc_on_apply", [True, False]) -def test_set_astute_password(mocker, mock_open, exc_on_apply): - fd_mock = mock.Mock() - close_mock = mocker.patch("os.close") - mkstemp_mock = mocker.patch( - "tempfile.mkstemp", - return_value=(fd_mock, "/etc/fuel/.astute.yaml.bac")) - mock_copy = mocker.patch("shutil.copy2") - mock_move = mocker.patch("shutil.move") - yaml_load = mocker.patch( - "yaml.load", return_value={"FUEL_ACCESS": {"password": "dump_pswd"}}) - yaml_dump = mocker.patch("yaml.safe_dump") - context = backup_restore.NailgunCredentialsContext( - user="admin", password="user_pswd") - if exc_on_apply: - with pytest.raises(TestException): - with auth.set_astute_password(context): - raise TestException("text exception") + if not isinstance(valid, bool): + mock_resp.raise_for_status.side_effect = valid() + with pytest.raises(valid): + auth.is_creds_valid('a', 'b') else: - with auth.set_astute_password(context): - pass - assert mock_open.call_args_list == [ - mock.call("/etc/fuel/astute.yaml", "r"), - mock.call("/etc/fuel/astute.yaml", "w"), - ] - yaml_load.assert_called_once_with(mock_open.return_value) - yaml_dump.assert_called_once_with( - {'FUEL_ACCESS': {'password': 'user_pswd'}}, - mock_open.return_value, - default_flow_style=False) - mock_copy.assert_called_once_with("/etc/fuel/astute.yaml", - "/etc/fuel/.astute.yaml.bac") - mock_move.assert_called_once_with("/etc/fuel/.astute.yaml.bac", - "/etc/fuel/astute.yaml") - mkstemp_mock.assert_called_once_with( - dir="/etc/fuel", prefix=".astute.yaml.octane") - close_mock.assert_called_once_with(fd_mock) + assert auth.is_creds_valid('a', 'b') == valid + if not valid: + assert not mock_resp.raise_for_status.called diff --git a/octane/util/auth.py b/octane/util/auth.py index 409de19a..4fdbe938 100644 --- a/octane/util/auth.py +++ b/octane/util/auth.py @@ -10,25 +10,19 @@ # License for the specific language governing permissions and limitations # under the License. -import shutil -import yaml +import collections +from fuelclient import client -import contextlib - -from octane.util import helpers -from octane.util import tempfile +from octane.util import fuel_client -@contextlib.contextmanager -def set_astute_password(auth_context): - tmp_file_name = tempfile.get_tempname( - dir="/etc/fuel", prefix=".astute.yaml.octane") - shutil.copy2("/etc/fuel/astute.yaml", tmp_file_name) - try: - data = helpers.get_astute_dict() - data["FUEL_ACCESS"]["password"] = auth_context.password - with open("/etc/fuel/astute.yaml", "w") as current: - yaml.safe_dump(data, current, default_flow_style=False) - yield - finally: - shutil.move(tmp_file_name, "/etc/fuel/astute.yaml") +Context = collections.namedtuple('Context', ('user', 'password')) + + +def is_creds_valid(user, password): + with fuel_client.set_auth_context(Context(user, password)): + resp = client.APIClient.get_request_raw('/clusters') + if resp.status_code != 401: + resp.raise_for_status() + return True + return False diff --git a/octane/util/helpers.py b/octane/util/helpers.py index e6dfbaf4..b422a81e 100644 --- a/octane/util/helpers.py +++ b/octane/util/helpers.py @@ -14,6 +14,8 @@ import re import yaml +from octane import magic_consts + def merge_dicts(base_dict, update_dict): result = base_dict.copy() @@ -26,7 +28,7 @@ def merge_dicts(base_dict, update_dict): def get_astute_dict(): - return load_yaml("/etc/fuel/astute.yaml") + return load_yaml(magic_consts.ASTUTE_YAML) def load_yaml(filename):