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:
@@ -16,10 +16,12 @@ from __future__ import absolute_import
|
|||||||
from tobiko.openstack.nova import _client
|
from tobiko.openstack.nova import _client
|
||||||
from tobiko.openstack.nova import _cloud_init
|
from tobiko.openstack.nova import _cloud_init
|
||||||
from tobiko.openstack.nova import _hypervisor
|
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 _quota_set
|
||||||
from tobiko.openstack.nova import _server
|
from tobiko.openstack.nova import _server
|
||||||
from tobiko.openstack.nova import _service
|
from tobiko.openstack.nova import _service
|
||||||
|
|
||||||
|
|
||||||
CLIENT_CLASSES = _client.CLIENT_CLASSES
|
CLIENT_CLASSES = _client.CLIENT_CLASSES
|
||||||
NovaClient = _client.NovaClient
|
NovaClient = _client.NovaClient
|
||||||
NovaClientType = _client.NovaClientType
|
NovaClientType = _client.NovaClientType
|
||||||
@@ -70,6 +72,9 @@ skip_if_missing_hypervisors = _hypervisor.skip_if_missing_hypervisors
|
|||||||
get_server_hypervisor = _hypervisor.get_server_hypervisor
|
get_server_hypervisor = _hypervisor.get_server_hypervisor
|
||||||
list_servers_hypervisors = _hypervisor.list_servers_hypervisors
|
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
|
get_nova_quota_set = _quota_set.get_nova_quota_set
|
||||||
ensure_nova_quota_limits = _quota_set.ensure_nova_quota_limits
|
ensure_nova_quota_limits = _quota_set.ensure_nova_quota_limits
|
||||||
set_nova_quota_set = _quota_set.set_nova_quota_set
|
set_nova_quota_set = _quota_set.set_nova_quota_set
|
||||||
|
|||||||
74
tobiko/openstack/nova/_key_file.py
Normal file
74
tobiko/openstack/nova/_key_file.py
Normal 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
|
||||||
@@ -17,10 +17,17 @@ import itertools
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
DEFAULT_KEY_TYPE = 'ecdsa'
|
||||||
|
DEFAULT_KEY_FILE = f'~/.ssh/id_{DEFAULT_KEY_TYPE}'
|
||||||
|
|
||||||
GROUP_NAME = "nova"
|
GROUP_NAME = "nova"
|
||||||
OPTIONS = [
|
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"),
|
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"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
import os
|
|
||||||
import typing
|
import typing
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
|
|
||||||
@@ -43,34 +42,25 @@ LOG = log.getLogger(__name__)
|
|||||||
|
|
||||||
class KeyPairStackFixture(heat.HeatStackFixture):
|
class KeyPairStackFixture(heat.HeatStackFixture):
|
||||||
template = _hot.heat_template_file('nova/key_pair.yaml')
|
template = _hot.heat_template_file('nova/key_pair.yaml')
|
||||||
key_file = tobiko.tobiko_config_path(CONF.tobiko.nova.key_file)
|
|
||||||
public_key = None
|
public_key: str
|
||||||
private_key = None
|
private_key: str
|
||||||
|
|
||||||
def setup_fixture(self):
|
def setup_fixture(self):
|
||||||
self.create_key_file()
|
|
||||||
self.read_keys()
|
self.read_keys()
|
||||||
super(KeyPairStackFixture, self).setup_fixture()
|
super(KeyPairStackFixture, self).setup_fixture()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key_file(self) -> str:
|
||||||
|
return nova.get_key_file()
|
||||||
|
|
||||||
def read_keys(self):
|
def read_keys(self):
|
||||||
|
LOG.info(f'Reading file {self.key_file} for key pairs...')
|
||||||
with open(self.key_file, 'r') as fd:
|
with open(self.key_file, 'r') as fd:
|
||||||
self.private_key = as_str(fd.read())
|
self.private_key = as_str(fd.read())
|
||||||
with open(self.key_file + '.pub', 'r') as fd:
|
with open(self.key_file + '.pub', 'r') as fd:
|
||||||
self.public_key = as_str(fd.read())
|
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):
|
class FlavorStackFixture(heat.HeatStackFixture):
|
||||||
template = _hot.heat_template_file('nova/flavor.yaml')
|
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
|
#: stack with the key pair for the server instance
|
||||||
key_pair_stack = tobiko.required_fixture(KeyPairStackFixture)
|
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
|
#: stack with the internal where the server port is created
|
||||||
network_stack = tobiko.required_fixture(_neutron.NetworkStackFixture)
|
network_stack = tobiko.required_fixture(_neutron.NetworkStackFixture)
|
||||||
|
|
||||||
@@ -173,6 +167,7 @@ class ServerStackFixture(heat.HeatStackFixture, abc.ABC):
|
|||||||
return dict(host=self.ip_address,
|
return dict(host=self.ip_address,
|
||||||
username=self.username,
|
username=self.username,
|
||||||
password=self.password,
|
password=self.password,
|
||||||
|
key_filename=self.key_file,
|
||||||
connection_timeout=self.connection_timeout,
|
connection_timeout=self.connection_timeout,
|
||||||
disabled_algorithms=self.disabled_algorithms)
|
disabled_algorithms=self.disabled_algorithms)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ description: |
|
|||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
|
|
||||||
|
private_key:
|
||||||
|
type: string
|
||||||
|
description: SSH private key
|
||||||
|
|
||||||
public_key:
|
public_key:
|
||||||
type: string
|
type: string
|
||||||
description: SSH public key
|
description: SSH public key
|
||||||
@@ -13,22 +17,37 @@ parameters:
|
|||||||
|
|
||||||
resources:
|
resources:
|
||||||
|
|
||||||
key_name:
|
_key_name:
|
||||||
type: OS::Heat::RandomString
|
type: OS::Heat::RandomString
|
||||||
description: Random unique key pair name
|
description: Random unique key pair name
|
||||||
properties:
|
properties:
|
||||||
length: 32
|
length: 32
|
||||||
|
|
||||||
key_pair:
|
_key_pair:
|
||||||
type: OS::Nova::KeyPair
|
type: OS::Nova::KeyPair
|
||||||
description: SSH key pair
|
description: SSH key pair
|
||||||
properties:
|
properties:
|
||||||
name: {get_attr: [key_name, value]}
|
name: {get_attr: [_key_name, value]}
|
||||||
public_key: {get_param: public_key}
|
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:
|
outputs:
|
||||||
|
|
||||||
key_name:
|
key_name:
|
||||||
description: unique Nova key pair 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]}
|
||||||
|
|||||||
Reference in New Issue
Block a user