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 _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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 | 
			
		||||
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"),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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]}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user