Merge "Add MigrationSshKey to generated passwords"

This commit is contained in:
Jenkins 2017-04-05 17:21:08 +00:00 committed by Gerrit Code Review
commit 6a17d629f0
10 changed files with 58 additions and 51 deletions

View File

@ -0,0 +1,4 @@
---
features:
- Add MigrationSshKey to generated passwords. This ssh key-pair is used by
nova cold-migration and libvirt live-migration unless TLS is enabled.

View File

@ -18,3 +18,4 @@ Jinja2!=2.9.0,!=2.9.1,!=2.9.2,!=2.9.3,!=2.9.4,>=2.8 # BSD License (3 clause)
python-novaclient>=7.1.0 # Apache-2.0
passlib>=1.7.0 # BSD
netifaces>=0.10.4 # MIT
paramiko>=2.0 # LGPLv2.1+

View File

@ -12,16 +12,13 @@
# 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 os
import shutil
import tempfile
from mistral.workflow import utils as mistral_workflow_utils
from mistralclient.api import base as mistralclient_api
from oslo_concurrency.processutils import ProcessExecutionError
from tripleo_common.actions import base
from tripleo_common import constants
from tripleo_common.utils import passwords as password_utils
from tripleo_common.utils import validations as utils
@ -33,25 +30,13 @@ class GetPubkeyAction(base.TripleOAction):
env = mc.environments.get('ssh_keys')
public_key = env.variables['public_key']
except Exception:
tmp_dir = tempfile.mkdtemp()
private_key_path = os.path.join(tmp_dir, 'id_rsa')
public_key_path = private_key_path + '.pub'
utils.create_ssh_keypair(private_key_path)
with open(private_key_path, 'r') as f:
private_key = f.read().strip()
with open(public_key_path, 'r') as f:
public_key = f.read().strip()
shutil.rmtree(tmp_dir)
ssh_key = password_utils.create_ssh_keypair()
public_key = ssh_key['public_key']
workflow_env = {
'name': 'ssh_keys',
'description': 'SSH keys for TripleO validations',
'variables': {
'public_key': public_key,
'private_key': private_key,
}
'variables': ssh_key
}
mc.environments.create(**workflow_env)

View File

@ -87,6 +87,7 @@ PASSWORD_PARAMETER_NAMES = (
'NeutronMetadataProxySharedSecret',
'NeutronPassword',
'NovaPassword',
'MigrationSshKey',
'OctaviaHeartbeatKey',
'OctaviaPassword',
'PacemakerRemoteAuthkey',

View File

@ -128,6 +128,10 @@ _EXISTING_PASSWORDS = {
'QttkuxyeQTgHupKNaZF6y7rDyf7mbNR9DaPXpBQuZ7un6KDj2Dfh7yvfhPk8cHG7n9pb'
'KEKD3sgbbKnQ8d9MsGhUtCQVed7dtjpYKsmGJmbYMvZjpGpqsfsHQfFRdCgJHnW3FdQ6'
'sGhUtCQVed7dtj12',
'MigrationSshKey': {
'private_key': 'private_key',
'public_key': 'public_key'
},
}
@ -323,6 +327,8 @@ class GeneratePasswordsActionTest(base.TestCase):
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
@ -330,9 +336,12 @@ class GeneratePasswordsActionTest(base.TestCase):
@mock.patch('mistral.context.ctx')
def test_run_passwords_exist(self, mock_ctx, mock_get_workflow_client,
mock_get_snmpd_readonly_user_password,
mock_create_ssh_keypair,
mock_get_orchestration_client):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'private_key': 'Bar'}
mock_ctx.return_value = mock.MagicMock()
mock_mistral = mock.MagicMock()
@ -361,6 +370,8 @@ class GeneratePasswordsActionTest(base.TestCase):
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
@ -368,9 +379,12 @@ class GeneratePasswordsActionTest(base.TestCase):
@mock.patch('mistral.context.ctx')
def test_passwords_exist_in_heat(self, mock_ctx, mock_get_workflow_client,
mock_get_snmpd_readonly_user_password,
mock_create_ssh_keypair,
mock_get_orchestration_client):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'private_key': 'Bar'}
existing_passwords = _EXISTING_PASSWORDS.copy()
existing_passwords.pop("AdminPassword")

View File

@ -40,26 +40,19 @@ class GetPubkeyActionTest(base.TestCase):
@mock.patch(
'tripleo_common.actions.base.TripleOAction.get_workflow_client')
@mock.patch('tripleo_common.utils.validations.create_ssh_keypair')
@mock.patch('tempfile.mkdtemp')
@mock.patch('shutil.rmtree')
def test_run_no_pubkey(self, mock_rmtree, mock_mkdtemp,
mock_create_keypair, get_workflow_client_mock):
@mock.patch('tripleo_common.utils.passwords.create_ssh_keypair')
def test_run_no_pubkey(self, mock_create_keypair,
get_workflow_client_mock):
mistral = mock.MagicMock()
get_workflow_client_mock.return_value = mistral
mistral.environments.get.side_effect = 'nope, sorry'
mock_mkdtemp.return_value = '/tmp_path'
mock_create_keypair.return_value = {
'public_key': 'public_key',
'private_key': 'private_key',
}
mock_open_context = mock.mock_open()
mock_open_context().read.side_effect = ['private_key', 'public_key']
with mock.patch('six.moves.builtins.open', mock_open_context):
action = validations.GetPubkeyAction()
self.assertEqual('public_key', action.run())
mock_mkdtemp.assert_called_once()
mock_create_keypair.assert_called_once_with('/tmp_path/id_rsa')
mock_rmtree.asser_called_once_with('/tmp_path')
action = validations.GetPubkeyAction()
self.assertEqual('public_key', action.run())
class Enabled(base.TestCase):

View File

@ -69,3 +69,9 @@ class TestPasswords(base.TestCase):
self.assertNotEqual(value['KeystoneCredential0'],
value['KeystoneCredential1'])
def test_create_ssh_keypair(self):
value = password_utils.create_ssh_keypair(comment="Foo")
self.assertEqual('ssh-rsa', value['public_key'][:7])
self.assertEqual('Foo', value['public_key'][-3:])

View File

@ -89,13 +89,6 @@ VALIDATION_GROUPS_1_2_PARSED = {
class ValidationsKeyTest(base.TestCase):
@mock.patch("oslo_concurrency.processutils.execute")
def test_create_ssh_keypair(self, mock_execute):
validations.create_ssh_keypair('/path/to/key')
mock_execute.assert_called_once_with(
'/usr/bin/ssh-keygen', '-t', 'rsa', '-N', '',
'-f', '/path/to/key', '-C', 'tripleo-validations')
@mock.patch("oslo_concurrency.processutils.execute")
@mock.patch('tempfile.mkstemp')
def test_write_identity_file(self, mock_mkstemp, mock_execute):

View File

@ -15,6 +15,7 @@
import base64
import logging
import os
import paramiko
import struct
import time
import uuid
@ -42,7 +43,6 @@ def generate_passwords(mistralclient=None, stack_env=None):
passwords = {}
for name in constants.PASSWORD_PARAMETER_NAMES:
# Support users upgrading from Mitaka or otherwise creating a plan for
# a Heat stack that already exists.
if stack_env and name in stack_env.get('parameter_defaults', {}):
@ -68,6 +68,8 @@ def generate_passwords(mistralclient=None, stack_env=None):
elif name in ('KeystoneCredential0', 'KeystoneCredential1',
'KeystoneFernetKey0', 'KeystoneFernetKey1'):
passwords[name] = create_keystone_credential()
elif name == 'MigrationSshKey':
passwords[name] = create_ssh_keypair()
else:
passwords[name] = passutils.generate_password(
size=_MIN_PASSWORD_SIZE)
@ -93,3 +95,18 @@ def get_snmpd_readonly_user_password(mistralclient):
def create_keystone_credential():
return base64.urlsafe_b64encode(os.urandom(32))
def create_ssh_keypair(comment=None, bits=2048):
"""Generate an ssh keypair for use on the overcloud"""
if comment is None:
comment = "Generated by TripleO"
key = paramiko.RSAKey.generate(bits)
keyout = six.StringIO()
key.write_private_key(keyout)
private_key = keyout.getvalue()
public_key = '{} {} {}'.format(key.get_name(), key.get_base64(), comment)
return {
'private_key': private_key,
'public_key': public_key,
}

View File

@ -93,13 +93,6 @@ def run_validation(validation, identity_file, plan):
)
def create_ssh_keypair(key_path):
"""Create SSH keypair"""
LOG.debug('Creating SSH keypair at %s', key_path)
processutils.execute('/usr/bin/ssh-keygen', '-t', 'rsa', '-N', '',
'-f', key_path, '-C', 'tripleo-validations')
def write_identity_file(key):
"""Write the SSH private key to disk"""
fd, path = tempfile.mkstemp(prefix='validations_identity_')
@ -112,7 +105,7 @@ def write_identity_file(key):
def cleanup_identity_file(path):
"""Write the SSH private key to disk"""
"""Remove the SSH private key from disk"""
LOG.debug('Cleaning up identity file at %s', path)
processutils.execute('/usr/bin/sudo', '/usr/bin/rm', '-f', path)