config/sysinv/sysinv/sysinv/sysinv/helm/base.py

293 lines
9.1 KiB
Python

#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import abc
import binascii
import os
import six
from sysinv.common import constants
from sysinv.common import exception
from sysinv.common import utils
from sysinv.common.storage_backend_conf import StorageBackendConfig
from sysinv.openstack.common import log as logging
from sysinv.helm import common
from sysinv.helm import quoted_str
LOG = logging.getLogger('object')
@six.add_metaclass(abc.ABCMeta)
class BaseHelm(object):
"""Base class to encapsulate helm operations for chart overrides"""
DEFAULT_REGION_NAME = 'RegionOne'
CEPH_MON_SERVICE_PORT = 6789
SUPPORTED_NAMESPACES = []
SUPPORTED_APP_NAMESPACES = {}
SYSTEM_CONTROLLER_SERVICES = [
common.HELM_CHART_KEYSTONE_API_PROXY,
]
def __init__(self, operator):
self._operator = operator
@property
def dbapi(self):
return self._operator.dbapi
@property
def context(self):
return self._operator.context
@staticmethod
def quoted_str(value):
return quoted_str(value)
@staticmethod
def _generate_random_password(length=16):
suffix = "Ti0*"
num = int((length / 2) - len(suffix) / 2)
return binascii.hexlify(os.urandom(num)).decode() + suffix
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 _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 _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.encode('utf8', 'strict')
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_service_parameter(self, service, section, name):
if self.dbapi is None:
return None
try:
parameter = self.dbapi.service_parameter_get_one(service=service, section=section, name=name)
except exception.NotFound:
return None
except exception.MultipleResults:
LOG.error("Multiple service parameters found for %s/%s/%s\n" %
(service, section, name))
return None
return parameter
def _count_hosts_by_label(self, label):
return int(self.dbapi.count_hosts_by_label(label))
def _num_controllers(self, label=None):
return self._count_hosts_by_label(common.LABEL_CONTROLLER)
def _num_computes(self):
return self._count_hosts_by_label(common.LABEL_COMPUTE)
def _num_controllers_by_personality(self):
return int(self.dbapi.count_hosts_by_personality(
constants.CONTROLLER))
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_oam_address(self):
address = self._get_address_by_name(
constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_OAM)
return address.address
def _system_mode(self):
return self.dbapi.isystem_get_one().system_mode
def _get_ceph_monitor_ips(self):
if self._system_mode() == constants.SYSTEM_MODE_SIMPLEX:
monitors = [self._get_controller_0_management_address()]
else:
monitors = StorageBackendConfig.get_ceph_mon_ip_addresses(
self.dbapi).values()
return monitors
def _get_formatted_ceph_monitor_ips(self):
port = self.CEPH_MON_SERVICE_PORT
monitor_ips = self._get_ceph_monitor_ips()
formatted_monitor_ips = [
utils.format_ceph_mon_address(mon, port) for mon in monitor_ips
]
return formatted_monitor_ips
def _get_management_address(self):
address = self._get_address_by_name(
constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_MGMT)
return address.address
def _get_controller_0_management_address(self):
address = self._get_address_by_name(
constants.CONTROLLER_0_HOSTNAME, constants.NETWORK_TYPE_MGMT)
return address.address
@staticmethod
def _format_url_address(address):
return utils.format_url_address(address)
def _get_host_cpu_list(self, host, function=None, threads=False):
"""
Retrieve 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_platform_res_limit(self):
"""
On All-In-One, not all CPUs and Mem are used for platform services.
It needs to limit the CPU and Mem usage of some services which use too
many resources.
"""
limit_enabled = False
limit_cpus = 0
limit_mem_mib = 0
system = self._get_system()
if system.system_type == constants.TIS_AIO_BUILD:
limit_enabled = True
controller_0 = self.dbapi.ihost_get_by_hostname(
constants.CONTROLLER_0_HOSTNAME)
platform_cpus = self._get_host_cpu_list(
controller_0, function=constants.PLATFORM_FUNCTION, threads=True)
limit_cpus = max(len(platform_cpus), 1)
host_memory = self.dbapi.imemory_get_by_ihost(controller_0.id)
for mem in host_memory:
limit_mem_mib += mem.platform_reserved_mib
return limit_enabled, limit_cpus, limit_mem_mib
def get_namespaces(self):
"""
Return list of namespaces supported by this chart
If a chart supports namespaces other than common.HELM_NS_OPENSTACK
then it can override self.SUPPORTED_NAMESPACES as desired.
"""
return self.SUPPORTED_NAMESPACES
def get_namespaces_by_app(self, app_name):
"""
Return list of namespaces supported by an applcation
"""
if app_name in self.SUPPORTED_APP_NAMESPACES:
return self.SUPPORTED_APP_NAMESPACES[app_name]
else:
return []
def get_overrides(self, namespace=None):
"""
Return chart-specific values overrides
This allows a helm chart class to specify overrides (in Helm format)
for the "values" section of a helm chart.
May be left blank to indicate that there are no additional overrides.
"""
return {}
def version_check(self, app_version):
"""
Validate application version
Return False if version is not supported by the plugin.
"""
return True
def execute_manifest_updates(self, operator):
"""
Update the elements of the armada manifest.
This allows a helm chart plugin to use the ArmadaManifestOperator to
make dynamic structural changes to the application manifest based on the
current conditions in the platform
Changes include updates to manifest documents for the following schemas:
armada/Manifest/v1, armada/ChartGroup/v1, armada/Chart/v1.
:param operator: an instance of the ArmadaManifestOperator
"""
pass
def _is_enabled(self, app_name, chart_name, namespace):
"""
Check if the chart is enable at an application level
:param app_name: Application name
:param chart_name: Chart supplied with the application
:param namespace: Namespace where the chart will be executed
Returns true by default if an exception occurs as most charts are
enabled.
"""
return utils.is_chart_enabled(
self.dbapi, app_name, chart_name, namespace)