Introduce ArmadaManifestOperator

Add the ArmadaManifestOperator to provide a single point of manipulation
for an application manifest.

This operator will load the original manifest file and provide a set of
utilily methods to update the list of chart_groups, an individual chart
group, or an individual chart based on platform and plugin conditions.
Based on the methods that are executed, changes are tracked and
overrides are generated for those elements that have changed.

These armada overrides are fed to the Armada command in addition to the
Helm overrides.

This also includes updated changes originally proposed from
I4bf4dcc3900c65f1492a362d31c9b3e7d73b8e31 to allow Barbican and
Telemetry chart groups to be optional services. This is utilizing a k8s
label to enable the services. This mechanism will be re-visited in a
future commit, but for now continue to use the existing mechanism.

Change-Id: I7e0c5bbe7daa0b0b8365ca1ed5f8fdf11a348c62
Partial-Bug: #1833746
Signed-off-by: Robert Church <robert.church@windriver.com>
This commit is contained in:
Robert Church 2019-06-28 03:09:18 -04:00
parent 320af02b02
commit e6b177eb93
13 changed files with 647 additions and 312 deletions

View File

@ -2065,3 +2065,11 @@ def is_aio_duplex_system(dbapi):
return (system.system_type == constants.TIS_AIO_BUILD and
(system.system_mode == constants.SYSTEM_MODE_DUPLEX or
system.system_mode == constants.SYSTEM_MODE_DUPLEX_DIRECT))
def generate_armada_manifest_dir(app_name, app_version):
return os.path.join(constants.APP_SYNCED_DATA_PATH, app_name, app_version)
def generate_armada_manifest_filename_abs(armada_mfile_dir, app_name, manifest_filename):
return os.path.join(armada_mfile_dir, app_name + '-' + manifest_filename)

View File

@ -37,6 +37,7 @@ from sysinv.common import utils as cutils
from sysinv.common.storage_backend_conf import K8RbdProvisioner
from sysinv.helm import common
from sysinv.helm import helm
from sysinv.helm import manifest
from sysinv.helm import utils as helm_utils
@ -83,14 +84,6 @@ def generate_armada_manifest_filename(app_name, app_version, manifest_filename):
app_name + '-' + manifest_filename)
def generate_armada_manifest_dir(app_name, app_version):
return os.path.join(constants.APP_SYNCED_DATA_PATH, app_name, app_version)
def generate_armada_manifest_filename_abs(armada_mfile_dir, app_name, manifest_filename):
return os.path.join(armada_mfile_dir, app_name + '-' + manifest_filename)
def generate_manifest_filename_abs(app_name, app_version, manifest_filename):
return os.path.join(constants.APP_INSTALL_PATH,
app_name, app_version, manifest_filename)
@ -1044,39 +1037,43 @@ class AppOperator(object):
"""Returns list of override files or None, used in
application-install and application-delete."""
missing_overrides = []
available_overrides = []
missing_helm_overrides = []
available_helm_overrides = []
for chart in charts:
overrides = chart.namespace + '-' + chart.name + '.yaml'
overrides_file = os.path.join(overrides_dir, overrides)
if not os.path.exists(overrides_file):
missing_overrides.append(overrides_file)
missing_helm_overrides.append(overrides_file)
else:
available_overrides.append(overrides_file)
available_helm_overrides.append(overrides_file)
# Now handle any meta-overrides files. These can affect
# sections of the chart schema other than "values, and can
# affect the chartgroup or even the manifest.
if self._helm.generate_meta_overrides(
chart.name, chart.namespace, app_name, mode):
overrides = chart.namespace + '-' + chart.name + \
'-meta' + '.yaml'
overrides_file = os.path.join(overrides_dir, overrides)
if not os.path.exists(overrides_file):
missing_overrides.append(overrides_file)
else:
available_overrides.append(overrides_file)
if missing_overrides:
LOG.error("Missing the following overrides: %s" % missing_overrides)
if missing_helm_overrides:
LOG.error("Missing the following overrides: %s" % missing_helm_overrides)
return None
return available_overrides
def _generate_armada_overrides_str(self, app_name, app_version, overrides_files):
return " ".join([' --values /overrides/{0}/{1}/{2}'.format(app_name, app_version,
os.path.basename(i))
for i in overrides_files])
# Get the armada manifest overrides files
manifest_op = manifest.ArmadaManifestOperator()
armada_overrides = manifest_op.load_summary(overrides_dir)
return (available_helm_overrides, armada_overrides)
def _generate_armada_overrides_str(self, app_name, app_version,
helm_files, armada_files):
overrides_str = ""
if helm_files:
overrides_str += " ".join([
' --values /overrides/{0}/{1}/{2}'.format(
app_name, app_version, os.path.basename(i))
for i in helm_files
])
if armada_files:
overrides_str += " ".join([
' --values /manifests/{0}/{1}/{2}'.format(
app_name, app_version, os.path.basename(i))
for i in armada_files
])
return overrides_str
def _remove_chart_overrides(self, overrides_dir, manifest_file):
charts = self._get_list_of_charts(manifest_file)
@ -1335,12 +1332,11 @@ class AppOperator(object):
overrides_str = ''
old_app.charts = self._get_list_of_charts(old_app.armada_mfile_abs)
if old_app.system_app:
overrides_files = self._get_overrides_files(old_app.overrides_dir,
old_app.charts,
old_app.name, mode=None)
overrides_str = \
self._generate_armada_overrides_str(old_app.name, old_app.version,
overrides_files)
(helm_files, armada_files) = self._get_overrides_files(
old_app.overrides_dir, old_app.charts, old_app.name, mode=None)
overrides_str = self._generate_armada_overrides_str(
old_app.name, old_app.version, helm_files, armada_files)
if self._make_armada_request_with_monitor(old_app,
constants.APP_APPLY_OP,
@ -1573,13 +1569,12 @@ class AppOperator(object):
self._helm.generate_helm_application_overrides(
app.overrides_dir, app.name, mode, cnamespace=None,
armada_format=True, armada_chart_info=app.charts, combined=True)
overrides_files = self._get_overrides_files(app.overrides_dir,
app.charts,
app.name, mode)
if overrides_files:
(helm_files, armada_files) = self._get_overrides_files(
app.overrides_dir, app.charts, app.name, mode)
if helm_files or armada_files:
LOG.info("Application overrides generated.")
overrides_str = self._generate_armada_overrides_str(
app.name, app.version, overrides_files)
app.name, app.version, helm_files, armada_files)
self._update_app_status(
app, new_progress=constants.APP_PROGRESS_DOWNLOAD_IMAGES)
self._download_images(app)
@ -1842,14 +1837,14 @@ class AppOperator(object):
self.overrides_dir = generate_overrides_dir(
self._kube_app.get('name'),
self._kube_app.get('app_version'))
self.armada_mfile_dir = generate_armada_manifest_dir(
self.armada_mfile_dir = cutils.generate_armada_manifest_dir(
self._kube_app.get('name'),
self._kube_app.get('app_version'))
self.armada_mfile = generate_armada_manifest_filename(
self._kube_app.get('name'),
self._kube_app.get('app_version'),
self._kube_app.get('manifest_file'))
self.armada_mfile_abs = generate_armada_manifest_filename_abs(
self.armada_mfile_abs = cutils.generate_armada_manifest_filename_abs(
self.armada_mfile_dir,
self._kube_app.get('name'),
self._kube_app.get('manifest_file'))
@ -1903,7 +1898,7 @@ class AppOperator(object):
self._kube_app.manifest_file = new_mfile
self.armada_mfile = generate_armada_manifest_filename(
self.name, self.version, new_mfile)
self.armada_mfile_abs = generate_armada_manifest_filename_abs(
self.armada_mfile_abs = cutils.generate_armada_manifest_filename_abs(
self.armada_mfile_dir, self.name, new_mfile)
self.mfile_abs = generate_manifest_filename_abs(
self.name, self.version, new_mfile)
@ -1914,7 +1909,7 @@ class AppOperator(object):
self.system_app = \
(self.name == constants.HELM_APP_OPENSTACK)
new_armada_dir = generate_armada_manifest_dir(
new_armada_dir = cutils.generate_armada_manifest_dir(
self.name, self.version)
shutil.move(self.armada_mfile_dir, new_armada_dir)
shutil.rmtree(os.path.dirname(self.armada_mfile_dir))

View File

@ -17,6 +17,11 @@ class BarbicanHelm(openstack.OpenstackBaseHelm):
AUTH_USERS = ['barbican']
SERVICE_NAME = constants.HELM_CHART_BARBICAN
def execute_manifest_updates(self, operator, app_name=None):
if not self._is_labeled(common.LABEL_BARBICAN, 'enabled'):
operator.manifest_chart_groups_delete(
'armada-manifest', 'openstack-barbican')
def get_overrides(self, namespace=None):
overrides = {
common.HELM_NS_OPENSTACK: {

View File

@ -241,19 +241,6 @@ class BaseHelm(object):
"""
return {}
def get_meta_overrides(self, namespace, app_name=None, mode=None):
"""
Return Armada-formatted chart-specific meta-overrides
This allows a helm chart class to specify overrides (in Armada format)
for things other than the "values" section of a chart. This includes
other sections of a chart, as well as chart groups or even the
overall manifest itself.
May be left blank to indicate that there are no additional overrides.
"""
return {}
def version_check(self, app_version):
"""
Validate application version
@ -261,3 +248,20 @@ class BaseHelm(object):
Return False if version is not supported by the plugin.
"""
return True
def execute_manifest_updates(self, operator, app_name=None):
"""
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
:parameter app_name: application for which the specific actions are
taken
"""
pass

View File

@ -22,6 +22,11 @@ class CeilometerHelm(openstack.OpenstackBaseHelm):
SERVICE_NAME = 'ceilometer'
AUTH_USERS = ['ceilometer']
def execute_manifest_updates(self, operator, app_name=None):
if not self._is_labeled(common.LABEL_TELEMETRY, 'enabled'):
operator.manifest_chart_groups_delete(
'armada-manifest', 'openstack-telemetry')
def get_overrides(self, namespace=None):
overrides = {
common.HELM_NS_OPENSTACK: {

View File

@ -51,6 +51,8 @@ LABEL_COMPUTE_LABEL = 'openstack-compute-node'
LABEL_OPENVSWITCH = 'openvswitch'
LABEL_REMOTE_STORAGE = 'remote-storage'
LABEL_IRONIC = 'openstack-ironic'
LABEL_BARBICAN = 'openstack-barbican'
LABEL_TELEMETRY = 'openstack-telemetry'
# Label values
LABEL_VALUE_ENABLED = 'enabled'

View File

@ -30,9 +30,8 @@ class GarbdHelm(base.BaseHelm):
base.BaseHelm.SUPPORTED_NAMESPACES + [common.HELM_NS_OPENSTACK]
}
def get_meta_overrides(self, namespace, app_name=None, mode=None):
def _meta_overrides():
def execute_manifest_updates(self, operator, app_name=None):
if app_name == constants.HELM_APP_OPENSTACK:
if (self._num_controllers() < 2 or
utils.is_aio_duplex_system(self.dbapi) or
(self._distributed_cloud_role() ==
@ -42,33 +41,8 @@ class GarbdHelm(base.BaseHelm):
# we'll use a single mariadb server and so we don't want to
# run garbd. This will remove "openstack-garbd" from the
# charts in the openstack-mariadb chartgroup.
return {
'schema': 'armada/ChartGroup/v1',
'metadata': {
'schema': 'metadata/Document/v1',
'name': 'openstack-mariadb',
},
'data': {
'description': 'Mariadb',
'sequenced': True,
'chart_group': [
'openstack-mariadb',
]
}
}
else:
return {}
overrides = {
common.HELM_NS_OPENSTACK: _meta_overrides()
}
if namespace in self.SUPPORTED_NAMESPACES:
return overrides[namespace]
elif namespace:
raise exception.InvalidHelmNamespace(chart=self.CHART,
namespace=namespace)
else:
return overrides
operator.chart_group_chart_delete('openstack-mariadb',
'openstack-garbd')
def get_overrides(self, namespace=None):
overrides = {

View File

@ -22,6 +22,7 @@ from sysinv.common import exception
from sysinv.common import utils
from sysinv.openstack.common import log as logging
from sysinv.helm import common
from sysinv.helm import manifest
LOG = logging.getLogger(__name__)
@ -448,19 +449,6 @@ class HelmOperator(object):
else:
LOG.exception("chart name is required")
@helm_context
def generate_meta_overrides(self, chart_name, chart_namespace,
app_name=None, mode=None):
overrides = {}
if chart_name in self.chart_operators:
try:
overrides.update(
self.chart_operators[chart_name].get_meta_overrides(
chart_namespace, app_name, mode))
except exception.InvalidHelmNamespace:
raise
return overrides
@helm_context
def generate_helm_application_overrides(self, path, app_name,
mode=None,
@ -495,6 +483,16 @@ class HelmOperator(object):
raise
if app_name in self.helm_system_applications:
# Get a manifest operator to provide a single point of
# manipulation for the chart, chart group and manifest schemas
manifest_op = manifest.ArmadaManifestOperator()
# Load the manifest into the operator
armada_manifest = utils.generate_armada_manifest_filename_abs(
utils.generate_armada_manifest_dir(app.name, app.app_version),
app.name, app.manifest_file)
manifest_op.load(armada_manifest)
app_overrides = self._get_helm_application_overrides(app_name,
cnamespace)
for (chart_name, overrides) in iteritems(app_overrides):
@ -543,17 +541,19 @@ class HelmOperator(object):
overrides[key] = new_overrides
self._write_chart_overrides(path, chart_name, cnamespace, overrides)
# Write any meta-overrides for this chart. These will be in
# armada format already.
if armada_format:
overrides = self.generate_meta_overrides(chart_name,
cnamespace,
app_name,
mode)
if overrides:
chart_meta_name = chart_name + '-meta'
self._write_chart_overrides(
path, chart_meta_name, cnamespace, overrides)
# Update manifest docs based on the plugin directives
if chart_name in self.chart_operators:
self.chart_operators[chart_name].execute_manifest_updates(
manifest_op, app_name)
# Update the manifest based on platform conditions
manifest.platform_mode_manifest_updates(
self.dbapi, manifest_op, app_name, mode)
# Write the manifest doc overrides for the chart, chart group and manifest
manifest_op.save()
manifest_op.save_summary(path=path)
else:
# Generic applications
for chart in armada_chart_info:

View File

@ -21,78 +21,6 @@ class HelmToolkitHelm(base.BaseHelm):
common.HELM_NS_HELM_TOOLKIT,
]
# (WZ) This code will be moved to a proper place once meta-overrides
# for the manifest can be generated not thru a specific chart.
# Note: The application-apply mode is associated with the application not
# the namespace. So app_name needs to be passed in.
def get_meta_overrides(self, namespace, app_name=None, mode=None):
def _meta_overrides(app_name, mode):
if not app_name:
# Application is unknown, so ignore mode.
LOG.info("App is None. Ignore mode.")
return {}
elif app_name not in constants.HELM_APP_APPLY_MODES.keys():
LOG.info("App %s is not supported. Ignore mode." % app_name)
return {}
elif mode == constants.OPENSTACK_RESTORE_DB:
# During application restore, first bring up
# MariaDB service.
return {
'schema': 'armada/Manifest/v1',
'metadata': {
'schema': 'metadata/Document/v1',
'name': 'armada-manifest'
},
'data': {
'release_prefix': 'osh',
'chart_groups': [
'kube-system-ingress',
'openstack-ingress',
'provisioner',
'openstack-mariadb',
]
}
}
elif mode == constants.OPENSTACK_RESTORE_STORAGE:
# After MariaDB data is restored, restore Keystone,
# Glance and Cinder.
return {
'schema': 'armada/Manifest/v1',
'metadata': {
'schema': 'metadata/Document/v1',
'name': 'armada-manifest'
},
'data': {
'release_prefix': 'osh',
'chart_groups': [
'kube-system-ingress',
'openstack-ingress',
'provisioner',
'openstack-mariadb',
'openstack-memcached',
'openstack-rabbitmq',
'openstack-keystone',
'openstack-glance',
'openstack-cinder',
]
}
}
else:
# When mode is OPENSTACK_RESTORE_NORMAL or None,
# bring up all the openstack services.
return {}
overrides = {
common.HELM_NS_HELM_TOOLKIT: _meta_overrides(app_name, mode)
}
if namespace in self.SUPPORTED_NAMESPACES:
return overrides[namespace]
elif namespace:
raise exception.InvalidHelmNamespace(chart=self.CHART,
namespace=namespace)
else:
return overrides
def get_namespaces(self):
return self.SUPPORTED_NAMESPACES

View File

@ -20,49 +20,14 @@ class IronicHelm(openstack.OpenstackBaseHelm):
SERVICE_USERS = ['glance']
AUTH_USERS = ['ironic']
def get_meta_overrides(self, namespace, app_name=None, mode=None):
def _meta_overrides():
if (self._num_controllers() >= 2 and
self._is_labeled(common.LABEL_IRONIC, 'enabled')):
# If there are fewer than 2 controllers or openstack-ironic
# label was not set, ironic chart won't be added to
# openstack-compute-kit chartgroup
return {
'schema': 'armada/ChartGroup/v1',
'metadata': {
'schema': 'metadata/Document/v1',
'name': 'openstack-compute-kit',
},
'data': {
'description':
'Deploy nova/neutron/ironic,' +
'as well as supporting services',
'sequenced': False,
'chart_group': [
'openstack-libvirt',
'openstack-openvswitch',
'openstack-nova',
'openstack-nova-api-proxy',
'openstack-neutron',
'openstack-placement',
'openstack-ironic',
]
}
}
else:
return {}
overrides = {
common.HELM_NS_OPENSTACK: _meta_overrides()
}
if namespace in self.SUPPORTED_NAMESPACES:
return overrides[namespace]
elif namespace:
raise exception.InvalidHelmNamespace(chart=self.CHART,
namespace=namespace)
else:
return overrides
def execute_manifest_updates(self, operator, app_name=None):
if (self._num_controllers() >= 2 and
self._is_labeled(common.LABEL_IRONIC, 'enabled')):
# If there are fewer than 2 controllers or openstack-ironic
# label was not set, ironic chart won't be added to
# openstack-compute-kit chartgroup
operator.chart_group_chart_insert('openstack-compute-kit',
'openstack-ironic')
def get_overrides(self, namespace=None):
overrides = {

View File

@ -21,50 +21,11 @@ class KeystoneApiProxyHelm(openstack.OpenstackBaseHelm):
SERVICE_NAME = constants.HELM_CHART_KEYSTONE_API_PROXY
DCORCH_SERVICE_NAME = 'dcorch'
def get_meta_overrides(self, namespace, app_name=None, mode=None):
def _meta_overrides():
if (self._distributed_cloud_role() ==
constants.DISTRIBUTED_CLOUD_ROLE_SYSTEMCONTROLLER):
# If we are on distributed cloud system controller,
# it will only include the required chart groups
# in the armada manifest
return {
'schema': 'armada/Manifest/v1',
'metadata': {
'schema': 'metadata/Document/v1',
'name': 'armada-manifest'
},
'data': {
'release_prefix': 'osh',
'chart_groups': [
'kube-system-ingress',
'openstack-ingress',
'openstack-mariadb',
'openstack-memcached',
'openstack-rabbitmq',
'openstack-keystone',
'openstack-barbican',
'openstack-glance',
'openstack-horizon',
'openstack-cinder',
'openstack-keystone-api-proxy',
]
}
}
else:
return {}
overrides = {
common.HELM_NS_OPENSTACK: _meta_overrides()
}
if namespace in self.SUPPORTED_NAMESPACES:
return overrides[namespace]
elif namespace:
raise exception.InvalidHelmNamespace(chart=self.CHART,
namespace=namespace)
else:
return overrides
def execute_manifest_updates(self, operator, app_name=None):
if (self._distributed_cloud_role() ==
constants.DISTRIBUTED_CLOUD_ROLE_SYSTEMCONTROLLER):
operator.manifest_chart_groups_insert(
'armada-manifest', 'openstack-keystone-api-proxy')
def get_overrides(self, namespace=None):
overrides = {

View File

@ -0,0 +1,520 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# All Rights Reserved.
#
""" System inventory Armada manifest operator."""
import os
import json
import ruamel.yaml as yaml
import tempfile
from glob import glob
from six import iteritems
from sysinv.common import constants
from sysinv.common import exception
from sysinv.openstack.common import log as logging
LOG = logging.getLogger(__name__)
KEY_SCHEMA = 'schema'
VAL_SCHEMA_MANIFEST = 'armada/Manifest/v1'
VAL_SCHEMA_CHART_GROUP = 'armada/ChartGroup/v1'
VAL_SCHEMA_CHART = 'armada/Chart/v1'
KEY_METADATA = 'metadata'
KEY_METADATA_NAME = 'name'
KEY_DATA = 'data'
KEY_DATA_CHART_GROUPS = 'chart_groups' # for manifest doc updates
KEY_DATA_CHART_GROUP = 'chart_group' # for chart group doc updates
KEY_DATA_CHART_NAME = 'chart_name' # for chart doc updates
# Attempt to keep a compact filename
FILE_PREFIX = {
KEY_DATA_CHART_GROUPS: 'm-', # for manifest doc overrides
KEY_DATA_CHART_GROUP: 'cg-', # for chart group doc overrides
KEY_DATA_CHART_NAME: 'c-' # for chart doc overrides
}
FILE_SUFFIX = '-meta.yaml'
SUMMARY_FILE = 'armada-overrides.yaml'
class ArmadaManifestOperator(object):
def __init__(self, manifest_fqpn=None):
self.manifest_path = None # Location to write overrides
self.content = [] # original app manifest content
self.docs = {
KEY_DATA_CHART_GROUPS: {}, # LUT for all manifest docs
KEY_DATA_CHART_GROUP: {}, # LUT for all chart group docs
KEY_DATA_CHART_NAME: {} # LUT for all chart docs
}
self.updated = {
KEY_DATA_CHART_GROUPS: set(), # indicate manifest doc change
KEY_DATA_CHART_GROUP: set(), # indicate chart group update
KEY_DATA_CHART_NAME: set() # indicate chart doc update
}
if manifest_fqpn:
self.load(manifest_fqpn)
def __str__(self):
return json.dumps({
'manifest': self.docs[KEY_DATA_CHART_GROUPS],
'chart_groups': self.docs[KEY_DATA_CHART_GROUP],
'charts': self.docs[KEY_DATA_CHART_NAME],
}, indent=2)
def load_summary(self, path):
""" Load the list of generated overrides files
Generate a list of override files that were written for the manifest.
This is used to generate Armada --values overrides for the manifest.
:param path: location of the overrides summary file
:return: a list of override files written
"""
files_written = []
summary_fqpn = os.path.join(path, SUMMARY_FILE)
if os.path.exists(summary_fqpn):
self.manifest_path = os.path.dirname(summary_fqpn)
with open(summary_fqpn, 'r') as f:
files_written = list(yaml.load_all(
f, Loader=yaml.RoundTripLoader))[0]
return files_written
def load(self, manifest_fqpn):
""" Load the application manifest for processing
:param manifest_fqpn: fully qualified path name of the application manifest
"""
if os.path.exists(manifest_fqpn):
self.manifest_path = os.path.dirname(manifest_fqpn)
with open(manifest_fqpn, 'r') as f:
self.content = list(yaml.load_all(
f, Loader=yaml.RoundTripLoader))
# Generate the lookup tables
# For the individual chart docs
self.docs[KEY_DATA_CHART_NAME] = {
i[KEY_METADATA][KEY_METADATA_NAME]: i
for i in self.content
if i[KEY_SCHEMA] == VAL_SCHEMA_CHART}
# For the chart group docs
self.docs[KEY_DATA_CHART_GROUP] = {
i[KEY_METADATA][KEY_METADATA_NAME]: i
for i in self.content
if i[KEY_SCHEMA] == VAL_SCHEMA_CHART_GROUP}
# For the single manifest doc
self.docs[KEY_DATA_CHART_GROUPS] = {
i[KEY_METADATA][KEY_METADATA_NAME]: i
for i in self.content
if i[KEY_SCHEMA] == VAL_SCHEMA_MANIFEST}
else:
LOG.error("Manifest file %s does not exist" % manifest_fqpn)
def _cleanup_meta_files(self, path):
""" Remove any previously written overrides files
:param path: directory containing manifest overrides files
"""
for k, v in iteritems(FILE_PREFIX):
fileregex = "{}*{}".format(v, FILE_SUFFIX)
filepath = os.path.join(self.manifest_path, fileregex)
for f in glob(filepath):
os.remove(f)
def _write_file(self, path, filename, pathfilename, data):
""" Write a yaml file
:param path: path to write the file
:param filename: name of the file
:param pathfilename: FQPN of the file
:param data: file data
"""
try:
fd, tmppath = tempfile.mkstemp(dir=path, prefix=filename,
text=True)
with open(tmppath, 'w') as f:
yaml.dump(data, f, Dumper=yaml.RoundTripDumper,
default_flow_style=False)
os.close(fd)
os.rename(tmppath, pathfilename)
# Change the permission to be readable to non-root
# users(ie.Armada)
os.chmod(pathfilename, 0o644)
except Exception:
if os.path.exists(tmppath):
os.remove(tmppath)
LOG.exception("Failed to write meta overrides %s" % pathfilename)
raise
def save_summary(self, path=None):
""" Write a yaml file containing the list of override files generated
:param path: optional alternative location to write the file
"""
files_written = []
for k, v in iteritems(self.updated):
for i in v:
filename = '{}{}{}'.format(FILE_PREFIX[k], i, FILE_SUFFIX)
filepath = os.path.join(self.manifest_path, filename)
files_written.append(filepath)
# Write the list of files generated. This can be read to include with
# the Armada overrides
if path and os.path.exists(path):
# if provided, write to an alternate location
self._write_file(path, SUMMARY_FILE,
os.path.join(path, SUMMARY_FILE),
files_written)
else:
# if not provided, write to the armada directory
self._write_file(self.manifest_path, SUMMARY_FILE,
os.path.join(self.manifest_path, SUMMARY_FILE),
files_written)
def save(self):
""" Save the overrides files
Write the elements of the manifest (manifest, chart_group, chart) that
was updated into an overrides file. The files are written to the same
directory as the application manifest.
"""
if os.path.exists(self.manifest_path):
# cleanup any existing meta override files
self._cleanup_meta_files(self.manifest_path)
# Only write the updated docs as meta overrides
for k, v in iteritems(self.updated):
for i in v:
filename = '{}{}{}'.format(FILE_PREFIX[k], i, FILE_SUFFIX)
filepath = os.path.join(self.manifest_path, filename)
self._write_file(self.manifest_path, filename, filepath,
self.docs[k][i])
else:
LOG.error("Manifest directory %s does not exist" % self.manifest_path)
def _validate_manifest(self, manifest):
""" Ensure that the manifest is known
:param manifest: name of the manifest
"""
if manifest not in self.docs[KEY_DATA_CHART_GROUPS]:
LOG.error("%s is not %s" % (manifest, self.docs[KEY_DATA_CHART_GROUPS].keys()))
return False
return True
def _validate_chart_group(self, chart_group):
""" Ensure that the chart_group is known
:param chart_group: name of the chart_group
"""
if chart_group not in self.docs[KEY_DATA_CHART_GROUP]:
LOG.error("%s is an unknown chart_group" % chart_group)
return False
return True
def _validate_chart_groups_from_list(self, chart_group_list):
""" Ensure that all the charts groups in chart group list are known
:param chart_group_list: list of chart groups
"""
for cg in chart_group_list:
if not self._validate_chart_group(cg):
return False
return True
def _validate_chart(self, chart):
""" Ensure that the chart is known
:param chart: name of the chart
"""
if chart not in self.docs[KEY_DATA_CHART_NAME]:
LOG.error("%s is an unknown chart" % chart)
return False
return True
def _validate_chart_from_list(self, chart_list):
""" Ensure that all the charts in chart list are known
:param chart_list: list of charts
"""
for c in chart_list:
if not self._validate_chart(c):
return False
return True
def manifest_chart_groups_delete(self, manifest, chart_group):
""" Delete a chart group from a manifest
This method will delete a chart group from a manifest's list of charts
groups.
:param manifest: manifest containing the list of chart groups
:param chart_group: chart group name to delete
"""
if (not self._validate_manifest(manifest) or
not self._validate_chart_group(chart_group)):
return
if chart_group not in self.docs[KEY_DATA_CHART_GROUPS][manifest][KEY_DATA][
KEY_DATA_CHART_GROUPS]:
LOG.info("%s is not currently enabled. Cannot delete." %
chart_group)
return
self.docs[KEY_DATA_CHART_GROUPS][manifest][KEY_DATA][
KEY_DATA_CHART_GROUPS].remove(chart_group)
self.updated[KEY_DATA_CHART_GROUPS].update([manifest])
def manifest_chart_groups_insert(self, manifest, chart_group, before_group=None):
""" Insert a chart group into a manifest
This method will insert a chart group into a manifest at the end of the
list of chart groups. If the before_group parameter is used the chart
group can be placed at a specific point in the chart group list.
:param manifest: manifest containing the list of chart groups
:param chart_group: chart group name to insert
:param before_group: chart group name to be appear after the inserted
chart group in the list
"""
if (not self._validate_manifest(manifest) or
not self._validate_chart_group(chart_group)):
return
if chart_group in self.docs[KEY_DATA_CHART_GROUPS][manifest][KEY_DATA][KEY_DATA_CHART_GROUPS]:
LOG.error("%s is already enabled. Cannot insert." %
chart_group)
return
if before_group:
if not self._validate_chart_group(before_group):
return
if before_group not in self.docs[KEY_DATA_CHART_GROUPS][manifest][KEY_DATA][
KEY_DATA_CHART_GROUPS]:
LOG.error("%s is not currently enabled. Cannot insert %s" %
(before_group, chart_group))
return
cgs = self.docs[KEY_DATA_CHART_GROUPS][manifest][KEY_DATA][KEY_DATA_CHART_GROUPS]
insert_index = cgs.index(before_group)
cgs.insert(insert_index, chart_group)
self.docs[KEY_DATA_CHART_GROUPS][manifest][KEY_DATA][KEY_DATA_CHART_GROUPS] = cgs
else:
self.docs[KEY_DATA_CHART_GROUPS][manifest][KEY_DATA][
KEY_DATA_CHART_GROUPS].append(chart_group)
self.updated[KEY_DATA_CHART_GROUPS].update([manifest])
def manifest_chart_groups_set(self, manifest, chart_group_list=None):
""" Set the chart groups for a specific manifest
This will replace the current set of charts groups in the manifest as
specified by the armada/Manifest/v1 schema with the provided list of
chart groups.
:param manifest: manifest containing the list of chart groups
:param chart_group_list: list of chart groups to replace the current set
of chart groups
"""
if not self._validate_manifest(manifest):
return
if chart_group_list:
if not self._validate_chart_groups_from_list(chart_group_list):
return
self.docs[KEY_DATA_CHART_GROUPS][manifest][KEY_DATA][KEY_DATA_CHART_GROUPS] = chart_group_list
else:
LOG.error("Cannot set the manifest chart_groups to an empty list")
def chart_group_chart_delete(self, chart_group, chart):
""" Delete a chart from a chart group
This method will delete a chart from a chart group's list of charts.
:param chart_group: chart group name
:param chart: chart name to remove from the chart list
"""
if (not self._validate_chart_group(chart_group) or
not self._validate_chart(chart)):
return
if chart not in self.docs[KEY_DATA_CHART_GROUP][chart_group][KEY_DATA][
KEY_DATA_CHART_GROUP]:
LOG.info("%s is not currently enabled. Cannot delete." %
chart)
return
self.docs[KEY_DATA_CHART_GROUP][chart_group][KEY_DATA][
KEY_DATA_CHART_GROUP].remove(chart)
self.updated[KEY_DATA_CHART_GROUP].update([chart_group])
def chart_group_chart_insert(self, chart_group, chart, before_chart=None):
""" Insert a chart into a chart group
This method will insert a chart into a chart group at the end of the
list of charts. If the before_chart parameter is used the chart can be
placed at a specific point in the chart list.
:param chart_group: chart group name
:param chart: chart name to insert
:param before_chart: chart name to be appear after the inserted chart in
the list
"""
if (not self._validate_chart_group(chart_group) or
not self._validate_chart(chart)):
return
if chart in self.docs[KEY_DATA_CHART_GROUP][chart_group][KEY_DATA][KEY_DATA_CHART_GROUP]:
LOG.error("%s is already enabled. Cannot insert." %
chart)
return
if before_chart:
if not self._validate_chart(before_chart):
return
if before_chart not in self.docs[KEY_DATA_CHART_GROUP][chart_group][KEY_DATA][
KEY_DATA_CHART_GROUP]:
LOG.error("%s is not currently enabled. Cannot insert %s" %
(before_chart, chart))
return
cg = self.docs[KEY_DATA_CHART_GROUP][chart_group][KEY_DATA][KEY_DATA_CHART_GROUP]
insert_index = cg.index(before_chart)
cg.insert(insert_index, chart)
self.docs[KEY_DATA_CHART_GROUP][chart_group][KEY_DATA][KEY_DATA_CHART_GROUP] = cg
else:
self.docs[KEY_DATA_CHART_GROUP][chart_group][KEY_DATA][
KEY_DATA_CHART_GROUP].append(chart)
self.updated[KEY_DATA_CHART_GROUP].update([chart_group])
def chart_group_set(self, chart_group, chart_list=None):
""" Set the charts for a specific chart group
This will replace the current set of charts specified in the chart group
with the provided list.
:param chart_group: chart group name
:param chart_list: list of charts to replace the current set of charts
"""
if not self._validate_chart_group(chart_group):
return
if chart_list:
if not self._validate_chart_from_list(chart_list):
return
self.docs[KEY_DATA_CHART_GROUP][chart_group][KEY_DATA][KEY_DATA_CHART_GROUP] = chart_list
else:
LOG.error("Cannot set the chart_group charts to an empty list")
def chart_group_add(self, chart_group, data):
""" Add a new chart group to the manifest.
To support a self-contained dynamic plugin, this method is called to
introduced a new chart group based on the armada/ChartGroup/v1 schema.
:param chart_group: chart group name
:param data: chart group data
"""
# Not implemented... yet.
pass
def chart_add(self, chart, data):
""" Add a new chart to the manifest.
To support a self-contained dynamic plugin, this method is called to
introduced a new chart based on the armada/Chart/v1 schema.
:param chart: chart name
:param data: chart data
"""
# Not implemented... yet.
pass
def platform_mode_manifest_updates(dbapi, manifest_op, app_name, mode):
""" Update the application manifest based on the platform
This is used for
:param dbapi: DB api object
:param manifest_op: ArmadaManifestOperator for updating the application
manifest
:param app_name: application name
:param mode: mode to control how to apply the application manifest
"""
if not app_name:
LOG.info("App is None. No platform mode based manifest updates taken.")
elif app_name not in constants.HELM_APP_APPLY_MODES.keys():
LOG.info("App %s is not supported. No platform mode based manifest "
"updates taken." % app_name)
elif app_name == constants.HELM_APP_OPENSTACK:
if mode == constants.OPENSTACK_RESTORE_DB:
# During application restore, first bring up
# MariaDB service.
manifest_op.manifest_chart_groups_set(
'armada-manifest',
['kube-system-ingress',
'openstack-ingress',
'openstack-mariadb'])
elif mode == constants.OPENSTACK_RESTORE_STORAGE:
# After MariaDB data is restored, restore Keystone,
# Glance and Cinder.
manifest_op.manifest_chart_groups_set(
'armada-manifest',
['kube-system-ingress',
'openstack-ingress',
'openstack-mariadb',
'openstack-memcached',
'openstack-rabbitmq',
'openstack-keystone',
'openstack-glance',
'openstack-cinder'])
else:
# When mode is OPENSTACK_RESTORE_NORMAL or None,
# bring up all the openstack services.
try:
system = dbapi.isystem_get_one()
except exception.NotFound:
LOG.exception("System %s not found.")
raise
if (system.distributed_cloud_role ==
constants.DISTRIBUTED_CLOUD_ROLE_SYSTEMCONTROLLER):
# remove the chart_groups not needed in this configuration
manifest_op.manifest_chart_groups_delete(
'armada-manifest', 'openstack-ceph-rgw')
manifest_op.manifest_chart_groups_delete(
'armada-manifest', 'openstack-compute-kit')
manifest_op.manifest_chart_groups_delete(
'armada-manifest', 'openstack-heat')
manifest_op.manifest_chart_groups_delete(
'armada-manifest', 'openstack-telemetry')

View File

@ -19,45 +19,13 @@ class OpenvswitchHelm(openstack.OpenstackBaseHelm):
CHART = constants.HELM_CHART_OPENVSWITCH
# There are already two places at where we generate chartgroup overrides.
# If more chartgroup overrides are needed in future, it's better to do it
# at a fixed place. Distributing the overrides in the chart plugins makes
# it hard to manage chartgroup overrides.
def get_meta_overrides(self, namespace, app_name=None, mode=None):
def _meta_overrides():
if utils.get_vswitch_type(self.dbapi) == \
constants.VSWITCH_TYPE_NONE:
# add the openvswitch chart into computekit chart group
return {
'schema': 'armada/ChartGroup/v1',
'metadata': {
'schema': 'metadata/Document/v1',
'name': 'openstack-compute-kit',
},
'data': {
'chart_group': [
'openstack-libvirt',
'openstack-openvswitch',
'openstack-nova',
'openstack-nova-api-proxy',
'openstack-neutron',
'openstack-placement',
]
}
}
else:
return {}
overrides = {
common.HELM_NS_OPENSTACK: _meta_overrides()
}
if namespace in self.SUPPORTED_NAMESPACES:
return overrides[namespace]
elif namespace:
raise exception.InvalidHelmNamespace(chart=self.CHART,
namespace=namespace)
else:
return overrides
def execute_manifest_updates(self, operator, app_name=None):
if utils.get_vswitch_type(self.dbapi) == constants.VSWITCH_TYPE_NONE:
# add the openvswitch chart into computekit chart group
operator.chart_group_chart_insert(
'openstack-compute-kit',
'openstack-openvswitch',
before_chart='openstack-nova')
def get_overrides(self, namespace=None):
overrides = {