# Copyright 2019 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 io import os from oslo_log import log import six import tobiko from tobiko import config from tobiko.openstack import keystone from tobiko.openstack import nova from tobiko.shell import sh from tobiko.shell import ssh from tobiko.tripleo import _undercloud CONF = config.CONF LOG = log.getLogger(__name__) def has_overcloud(): # rewrite this function return _undercloud.has_undercloud() def load_overcloud_rcfile(): return _undercloud.fetch_os_env(*CONF.tobiko.tripleo.overcloud_rcfile) skip_if_missing_overcloud = tobiko.skip_unless( 'TripleO overcloud not configured', has_overcloud) class OvercloudKeystoneCredentialsFixture( keystone.EnvironKeystoneCredentialsFixture): def get_environ(self): if has_overcloud(): return load_overcloud_rcfile() else: return {} def list_overcloud_nodes(**params): session = _undercloud.undercloud_keystone_session() client = nova.get_nova_client(session=session) return nova.list_servers(client=client, **params) def find_overcloud_node(**params): session = _undercloud.undercloud_keystone_session() client = nova.get_nova_client(session=session) return nova.find_server(client=client, **params) def overcloud_ssh_client(hostname, ip_version=None, network_name=None): host_config = overcloud_host_config(hostname=hostname, ip_version=ip_version, network_name=network_name) return ssh.ssh_client(host=hostname, host_config=host_config) def overcloud_host_config(hostname, ip_version=None, network_name=None): host_config = OvercloudHostConfig(host=hostname, ip_version=ip_version, network_name=network_name) return tobiko.setup_fixture(host_config) def overcloud_node_ip_address(ip_version=None, network_name=None, server=None, **params): server = server or find_overcloud_node(**params) ip_version = ip_version or CONF.tobiko.tripleo.overcloud_ip_version network_name = network_name or CONF.tobiko.tripleo.overcloud_network_name address = nova.find_server_ip_address(server=server, ip_version=ip_version, network_name=network_name) LOG.debug(f"Got Overcloud node address '{address}' from Undercloud " f"(ip_version={ip_version}, network_name={network_name}, " f"server={server})") return address class OvercloudSshKeyFileFixture(tobiko.SharedFixture): @property def key_filename(self): return tobiko.tobiko_config_path( CONF.tobiko.tripleo.overcloud_ssh_key_filename) def setup_fixture(self): self.setup_key_file() def setup_key_file(self): key_filename = self.key_filename key_dirname = os.path.dirname(key_filename) tobiko.makedirs(key_dirname, mode=0o700) ssh_client = _undercloud.undercloud_ssh_client() _get_undercloud_file(ssh_client=ssh_client, source='~/.ssh/id_rsa', destination=key_filename, mode=0o600) _get_undercloud_file(ssh_client=ssh_client, source='~/.ssh/id_rsa.pub', destination=key_filename + '.pub', mode=0o600) def _get_undercloud_file(ssh_client, source, destination, mode): content = sh.execute(['cat', source], ssh_client=ssh_client).stdout with io.open(destination, 'wb') as fd: fd.write(content.encode()) os.chmod(destination, mode) class OvercloudHostConfig(tobiko.SharedFixture): hostname = None port = None username = None key_file = tobiko.required_setup_fixture(OvercloudSshKeyFileFixture) ip_version = None network_name = None key_filename = None def __init__(self, host, ip_version=None, network_name=None, **kwargs): super(OvercloudHostConfig, self).__init__() tobiko.check_valid_type(host, six.string_types) self.host = host if ip_version: self.ip_version = ip_version if network_name: self.network_name = network_name self._connect_parameters = ssh.gather_ssh_connect_parameters(**kwargs) def setup_fixture(self): self.hostname = str(overcloud_node_ip_address( name=self.host, ip_version=self.ip_version, network_name=self.network_name)) self.port = self.port or CONF.tobiko.tripleo.overcloud_ssh_port self.username = (self.username or CONF.tobiko.tripleo.overcloud_ssh_username) self.key_filename = self.key_filename or self.key_file.key_filename @property def connect_parameters(self): parameters = ssh.ssh_host_config( host=str(self.hostname)).connect_parameters parameters.update(ssh.gather_ssh_connect_parameters(self)) parameters.update(self._connect_parameters) return parameters def setup_overcloud_keystone_crederntials(): keystone.DEFAULT_KEYSTONE_CREDENTIALS_FIXTURES.append( OvercloudKeystoneCredentialsFixture) def get_overcloud_nodes_dataframe(oc_node_df_function): """ :param oc_node_df_function : a function that queries a oc node using a cli command and returns a datraframe with an added hostname field. This function concats oc nodes dataframes into a unified overcloud dataframe, seperated by hostname field :return: dataframe of all overcloud nodes processes """ import pandas oc_nodes_selection = list_overcloud_nodes() oc_nodes_names = [node.name for node in oc_nodes_selection] oc_nodes_dfs = [oc_node_df_function(node_name) for node_name in oc_nodes_names] oc_procs_df = pandas.concat(oc_nodes_dfs, ignore_index=True) return oc_procs_df