efa5f521c3
Configmap is the default helmv2 storage backend to store release information but its 1MB resource limit prevents scaling up stx openstack worker nodes, so we want to use SQL as helm storage backend. To configure SQL backend, generate helm database hieradata that will be used in puppet to create helm database. The helm database password is stored in keyring which can be retrieved in ansible playbook to configure database connection address. System upgrade support: The helm DB is new in the release stx5.0, so a password is generated for helm user. Helm user and password are written into the hieradata of release stx5.0. For AIO-SX upgrade, helm DB is created when applying bootstrap puppet manifest during ansible upgrade playbook. For two controllers upgrade, helm DB is created when applying upgrade puppet manifest during controller-1 upgrade. A migration script is created to migrate helm releases from configmap to postgresql. Partial-Bug: 1887677 Depends-On: https://review.opendev.org/#/c/761642/ Change-Id: I2f4f414068af297b5f4a3792c061443b7d3bdb32 Signed-off-by: Angie Wang <angie.wang@windriver.com>
279 lines
8.3 KiB
Python
279 lines
8.3 KiB
Python
#
|
|
# Copyright (c) 2017-2018 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
import abc
|
|
import keyring
|
|
import six
|
|
|
|
from sqlalchemy.orm.exc import NoResultFound
|
|
from sysinv.common import constants
|
|
from sysinv.common import utils
|
|
from sysinv.common import exception
|
|
from sysinv.helm import common as helm_common
|
|
|
|
from sysinv.puppet import quoted_str
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class BasePuppet(object):
|
|
"""Base class to encapsulate puppet operations for hiera configuration"""
|
|
|
|
CONFIG_WORKDIR = '/tmp/config'
|
|
DEFAULT_REGION_NAME = 'RegionOne'
|
|
DEFAULT_SERVICE_PROJECT_NAME = 'services'
|
|
DEFAULT_KERNEL_OPTIONS = constants.SYSTEM_SECURITY_FEATURE_SPECTRE_MELTDOWN_DEFAULT_OPTS
|
|
|
|
SYSTEM_CONTROLLER_SERVICES = [
|
|
'keystone',
|
|
'dcorch'
|
|
]
|
|
|
|
def __init__(self, operator):
|
|
self._operator = operator
|
|
|
|
@property
|
|
def dbapi(self):
|
|
return self._operator.dbapi
|
|
|
|
@property
|
|
def config_uuid(self):
|
|
return self._operator.config_uuid
|
|
|
|
@property
|
|
def context(self):
|
|
return self._operator.context
|
|
|
|
@property
|
|
def config(self):
|
|
return self._operator.config
|
|
|
|
def get_static_config(self):
|
|
return {}
|
|
|
|
def get_secure_static_config(self):
|
|
return {}
|
|
|
|
def get_system_config(self):
|
|
return {}
|
|
|
|
def get_secure_system_config(self):
|
|
return {}
|
|
|
|
def get_host_config(self, host):
|
|
return {}
|
|
|
|
def get_host_config_upgrade(self, host):
|
|
return {}
|
|
|
|
@staticmethod
|
|
def quoted_str(value):
|
|
return quoted_str(value)
|
|
|
|
@staticmethod
|
|
def _generate_random_password(length=16):
|
|
return utils.generate_random_password(length=length)
|
|
|
|
def _get_database_password(self, service):
|
|
passwords = self.context.setdefault('_database_passwords', {})
|
|
if service not in passwords:
|
|
passwords[service] = self._get_keyring_password(service,
|
|
'database')
|
|
return passwords[service]
|
|
|
|
def _get_database_username(self, service):
|
|
return 'admin-%s' % service
|
|
|
|
def _get_keyring_password(self, service, user):
|
|
password = keyring.get_password(service, user)
|
|
if not password:
|
|
password = self._generate_random_password()
|
|
keyring.set_password(service, user, password)
|
|
return password
|
|
|
|
def _get_system(self):
|
|
system = self.context.get('_system', None)
|
|
if system is None:
|
|
system = self.dbapi.isystem_get_one()
|
|
self.context['_system'] = system
|
|
return system
|
|
|
|
def _sdn_enabled(self):
|
|
if self.dbapi is None:
|
|
return False
|
|
|
|
system = self._get_system()
|
|
return system.capabilities.get('sdn_enabled', False)
|
|
|
|
def _https_enabled(self):
|
|
if self.dbapi is None:
|
|
return False
|
|
|
|
system = self._get_system()
|
|
return system.capabilities.get('https_enabled', False)
|
|
|
|
def _region_config(self):
|
|
if self.dbapi is None:
|
|
return False
|
|
|
|
system = self._get_system()
|
|
return system.capabilities.get('region_config', False)
|
|
|
|
def _vswitch_type(self):
|
|
if self.dbapi is None:
|
|
return False
|
|
|
|
system = self._get_system()
|
|
return system.capabilities.get('vswitch_type', None)
|
|
|
|
def _distributed_cloud_role(self):
|
|
if self.dbapi is None:
|
|
return None
|
|
|
|
system = self._get_system()
|
|
return system.distributed_cloud_role
|
|
|
|
def _region_name(self):
|
|
"""Returns the local region name of the system"""
|
|
if self.dbapi is None:
|
|
return self.DEFAULT_REGION_NAME
|
|
|
|
system = self._get_system()
|
|
return system.region_name
|
|
|
|
def _get_service_project_name(self):
|
|
if self.dbapi is None:
|
|
return self.DEFAULT_SERVICE_PROJECT_NAME
|
|
|
|
system = self._get_system()
|
|
return system.service_project_name
|
|
|
|
def _get_service(self, service_name):
|
|
if self.dbapi is None:
|
|
return None
|
|
|
|
try:
|
|
service = self.dbapi.service_get(service_name)
|
|
except exception.ServiceNotFound:
|
|
# service not configured
|
|
return None
|
|
return service
|
|
|
|
def _get_shared_services(self):
|
|
if self.dbapi is None:
|
|
return []
|
|
|
|
system = self._get_system()
|
|
return system.capabilities.get('shared_services', [])
|
|
|
|
def _get_address_by_name(self, name, networktype):
|
|
"""
|
|
Retrieve an address entry by name and scoped by network type
|
|
"""
|
|
addresses = self.context.setdefault('_address_names', {})
|
|
address_name = utils.format_address_name(name, networktype)
|
|
address = addresses.get(address_name)
|
|
if address is None:
|
|
address = self.dbapi.address_get_by_name(address_name)
|
|
addresses[address_name] = address
|
|
|
|
return address
|
|
|
|
def _get_management_address(self):
|
|
address = self._get_address_by_name(
|
|
constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_MGMT)
|
|
return address.address
|
|
|
|
def _get_pxeboot_address(self):
|
|
address = self._get_address_by_name(
|
|
constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_PXEBOOT)
|
|
return address.address
|
|
|
|
def _get_oam_address(self):
|
|
address = self._get_address_by_name(
|
|
constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_OAM)
|
|
return address.address
|
|
|
|
def _get_cluster_host_address(self):
|
|
address = self._get_address_by_name(
|
|
constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_CLUSTER_HOST)
|
|
return address.address
|
|
|
|
def _get_cluster_pod_subnet(self):
|
|
address = self._get_address_by_name(
|
|
constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_CLUSTER_POD)
|
|
subnet = address.address + '/' + address.prefix
|
|
return subnet
|
|
|
|
def _get_host_cpu_list(self, host, function=None, threads=False):
|
|
"""
|
|
Retreive a list of CPUs for the host, filtered by function and thread
|
|
siblings (if supplied)
|
|
"""
|
|
cpus = []
|
|
for c in self.dbapi.icpu_get_by_ihost(host.id):
|
|
if c.thread != 0 and not threads:
|
|
continue
|
|
if c.allocated_function == function or not function:
|
|
cpus.append(c)
|
|
return cpus
|
|
|
|
def _get_vswitch_cpu_list(self, host):
|
|
cpus = self._get_host_cpu_list(host, constants.VSWITCH_FUNCTION)
|
|
return sorted(cpus, key=lambda c: c.cpu)
|
|
|
|
def _get_platform_cpu_list(self, host):
|
|
cpus = self._get_host_cpu_list(host, constants.PLATFORM_FUNCTION)
|
|
return sorted(cpus, key=lambda c: c.cpu)
|
|
|
|
def _get_service_parameters(self, service=None):
|
|
service_parameters = []
|
|
if self.dbapi is None:
|
|
return service_parameters
|
|
try:
|
|
service_parameters = self.dbapi.service_parameter_get_all(
|
|
service=service)
|
|
# the service parameter has not been added
|
|
except NoResultFound:
|
|
pass
|
|
return service_parameters
|
|
|
|
def _get_security_feature(self):
|
|
if self.dbapi is None:
|
|
return self.DEFAULT_KERNEL_OPTIONS
|
|
|
|
system = self._get_system()
|
|
return system.security_feature
|
|
|
|
@staticmethod
|
|
def _service_parameter_lookup_one(service_parameters, section, name,
|
|
default):
|
|
for param in service_parameters:
|
|
if param['section'] == section and param['name'] == name:
|
|
return param['value']
|
|
return default
|
|
|
|
def _format_service_parameter(self, service_parameters, section, group, name):
|
|
parameter = {}
|
|
key = group + name
|
|
value = self._service_parameter_lookup_one(service_parameters, section,
|
|
name, 'undef')
|
|
if value != 'undef':
|
|
parameter[key] = value
|
|
return parameter
|
|
|
|
@staticmethod
|
|
def _format_url_address(address):
|
|
return utils.format_url_address(address)
|
|
|
|
# TODO (jgauld): Refactor to use utility has_openstack_compute(labels)
|
|
def is_openstack_compute(self, host):
|
|
if self.dbapi is None:
|
|
return False
|
|
for obj in self.dbapi.label_get_by_host(host.id):
|
|
if helm_common.LABEL_COMPUTE_LABEL == obj.label_key:
|
|
return True
|
|
return False
|