Update Nova SSH key file fixture

- by default use ~/.ssh/id_ecdsa file to login to Nova instances
- crete key files if they don't exist
- explicitely use such key file to log in to Nova instances
- allow to configure file name and key type in Nova section

Change-Id: I3c02576bf0d14eaec5e55631bc129df0b5a7e67b
This commit is contained in:
Federico Ressi
2022-06-28 13:26:03 +02:00
parent 21e9f1298f
commit 7bad0aef77
5 changed files with 123 additions and 23 deletions

View File

@@ -16,10 +16,12 @@ from __future__ import absolute_import
from tobiko.openstack.nova import _client
from tobiko.openstack.nova import _cloud_init
from tobiko.openstack.nova import _hypervisor
from tobiko.openstack.nova import _key_file
from tobiko.openstack.nova import _quota_set
from tobiko.openstack.nova import _server
from tobiko.openstack.nova import _service
CLIENT_CLASSES = _client.CLIENT_CLASSES
NovaClient = _client.NovaClient
NovaClientType = _client.NovaClientType
@@ -70,6 +72,9 @@ skip_if_missing_hypervisors = _hypervisor.skip_if_missing_hypervisors
get_server_hypervisor = _hypervisor.get_server_hypervisor
list_servers_hypervisors = _hypervisor.list_servers_hypervisors
KeyFileFixture = _key_file.KeyFileFixture
get_key_file = _key_file.get_key_file
get_nova_quota_set = _quota_set.get_nova_quota_set
ensure_nova_quota_limits = _quota_set.ensure_nova_quota_limits
set_nova_quota_set = _quota_set.set_nova_quota_set

View File

@@ -0,0 +1,74 @@
# Copyright 2022 Red Hat
#
# 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.
from __future__ import absolute_import
import os
from oslo_log import log
import tobiko
from tobiko.shell import sh
LOG = log.getLogger(__name__)
class KeyFileFixture(tobiko.SharedFixture):
def __init__(self,
key_file: str = None,
key_type: str = None):
super(KeyFileFixture, self).__init__()
if key_file is None:
key_file = tobiko.tobiko_config().nova.key_file
self.key_file = tobiko.tobiko_config_path(key_file)
if key_type is None:
key_type = tobiko.tobiko_config().nova.key_type
self.key_type = key_type
def setup_fixture(self):
self.ensure_key_file()
def ensure_key_file(self):
key_file = tobiko.check_valid_type(self.key_file, str)
LOG.debug(f'Ensuring Nova key files exist: {key_file}')
if os.path.isfile(key_file):
if os.path.isfile(f'{key_file}.pub'):
LOG.info(f"Key file found: {key_file}")
return
else:
LOG.info(f"Public key file not found: {key_file}.pub")
else:
LOG.info(f"Key file not found: {key_file}")
LOG.info(f"Creating file for key pairs: '{self.key_file}'...")
key_dir = os.path.dirname(key_file)
tobiko.makedirs(key_dir)
try:
sh.local_execute(['ssh-keygen',
'-f', key_file,
'-P', '',
'-t', self.key_type])
except sh.ShellCommandFailed:
if (not os.path.isfile(key_file) or
not os.path.isfile(key_file + '.pub')):
raise
else:
assert os.path.isfile(key_file)
assert os.path.isfile(key_file + '.pub')
LOG.info(f'Key file created: {self.key_file}')
def get_key_file() -> str:
return tobiko.setup_fixture(KeyFileFixture).key_file

View File

@@ -17,10 +17,17 @@ import itertools
from oslo_config import cfg
DEFAULT_KEY_TYPE = 'ecdsa'
DEFAULT_KEY_FILE = f'~/.ssh/id_{DEFAULT_KEY_TYPE}'
GROUP_NAME = "nova"
OPTIONS = [
cfg.StrOpt('key_file', default='~/.ssh/id_rsa',
cfg.StrOpt('key_file',
default=DEFAULT_KEY_FILE,
help="Default SSH key to login to server instances"),
cfg.StrOpt('key_type',
default=DEFAULT_KEY_TYPE,
help="Default SSH key type to login to server instances"),
]

View File

@@ -16,7 +16,6 @@
from __future__ import absolute_import
import abc
import os
import typing
from abc import ABC
@@ -43,34 +42,25 @@ LOG = log.getLogger(__name__)
class KeyPairStackFixture(heat.HeatStackFixture):
template = _hot.heat_template_file('nova/key_pair.yaml')
key_file = tobiko.tobiko_config_path(CONF.tobiko.nova.key_file)
public_key = None
private_key = None
public_key: str
private_key: str
def setup_fixture(self):
self.create_key_file()
self.read_keys()
super(KeyPairStackFixture, self).setup_fixture()
@property
def key_file(self) -> str:
return nova.get_key_file()
def read_keys(self):
LOG.info(f'Reading file {self.key_file} for key pairs...')
with open(self.key_file, 'r') as fd:
self.private_key = as_str(fd.read())
with open(self.key_file + '.pub', 'r') as fd:
self.public_key = as_str(fd.read())
def create_key_file(self):
key_file = self.key_file
if not os.path.isfile(key_file):
key_dir = os.path.dirname(key_file)
tobiko.makedirs(key_dir)
try:
sh.local_execute(['ssh-keygen', '-f', key_file, '-P', ''])
except sh.ShellCommandFailed:
if not os.path.isfile(key_file):
raise
else:
assert os.path.isfile(key_file)
class FlavorStackFixture(heat.HeatStackFixture):
template = _hot.heat_template_file('nova/flavor.yaml')
@@ -94,6 +84,10 @@ class ServerStackFixture(heat.HeatStackFixture, abc.ABC):
#: stack with the key pair for the server instance
key_pair_stack = tobiko.required_fixture(KeyPairStackFixture)
@property
def key_file(self) -> str:
return self.key_pair_stack.key_file
#: stack with the internal where the server port is created
network_stack = tobiko.required_fixture(_neutron.NetworkStackFixture)
@@ -173,6 +167,7 @@ class ServerStackFixture(heat.HeatStackFixture, abc.ABC):
return dict(host=self.ip_address,
username=self.username,
password=self.password,
key_filename=self.key_file,
connection_timeout=self.connection_timeout,
disabled_algorithms=self.disabled_algorithms)

View File

@@ -6,6 +6,10 @@ description: |
parameters:
private_key:
type: string
description: SSH private key
public_key:
type: string
description: SSH public key
@@ -13,22 +17,37 @@ parameters:
resources:
key_name:
_key_name:
type: OS::Heat::RandomString
description: Random unique key pair name
properties:
length: 32
key_pair:
_key_pair:
type: OS::Nova::KeyPair
description: SSH key pair
properties:
name: {get_attr: [key_name, value]}
name: {get_attr: [_key_name, value]}
public_key: {get_param: public_key}
_private_key:
type: OS::Heat::Value
description: SSH private key
properties:
type: string
value: {get_param: private_key}
outputs:
key_name:
description: unique Nova key pair name
value: {get_attr: [key_name, value]}
value: {get_attr: [_key_name, value]}
private_key_value:
description: private key value
value: {get_attr: [_private_key, value]}
public_key_value:
description: public key value
value: {get_attr: [_key_pair, public_key]}