Check that astute.yaml password is valid before backup
This commit adds astute.yaml password validation in backup procedure. If we have a valid password we don't need ask a user about it and could use astute.yaml "as is" without fixing password there. Change-Id: I0dd79ec624159dd1132ca06a86b35d80941c8c39 Partial-Bug: #1606613
This commit is contained in:
parent
f9d9919da5
commit
d678e14535
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -162,3 +162,5 @@ COMPUTE_PREUPGRADE_PACKAGES = {
|
|||
"python-concurrent.futures",
|
||||
]
|
||||
}
|
||||
|
||||
ASTUTE_YAML = "/etc/fuel/astute.yaml"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue