Bring up openstack services in stages during restore

During restore we need to bring up openstack services in stages.
First we bring up MariaDB to populate the database. Then we
bring up Glance and Cinder to restore images and volumes. Finally
Nova and other services will be brought up. This is achieved by
adding a "mode" option to sysinv application-apply API.

For the stx-openstack application the possible values are "restore_db",
"restore_storage" and "normal". The chartgroup list in the armada
manifest will be modified by the generated meta-overrides based on
the "mode".

Story: 2004761
Task: 30522

Change-Id: I2bdb8b3a9e1fe34ee6f5f6e376279a5dc14f7cc4
Signed-off-by: Wei Zhou <wei.zhou@windriver.com>
This commit is contained in:
Wei Zhou 2019-04-30 14:49:44 -04:00
parent 7f17d2672c
commit cbc5151cf2
12 changed files with 148 additions and 22 deletions

View File

@ -43,13 +43,14 @@ class AppManager(base.Manager):
"""
return self._create(self._path(), data)
def apply(self, app_name):
def apply(self, app_name, data):
"""Install/upgrade the specified application.
:param app_name: name of the application
:param data: extra arguments
"""
return self._update(self._path(app_name) + '?directive=apply',
{'values': {}})
{'values': data})
def remove(self, app_name):
"""Uninstall the specified application

View File

@ -98,10 +98,20 @@ def do_application_upload(cc, args):
@utils.arg('name', metavar='<app name>',
help='Name of the application')
@utils.arg('-m', '--mode',
metavar='<mode>',
default=None,
help=('The mode is application specific. It controls how applicaton'
' manifest is applied.'))
def do_application_apply(cc, args):
"""Apply/reapply the application manifest"""
try:
response = cc.app.apply(args.name)
fields = ['mode']
data = dict((k, v) for (k, v) in vars(args).items()
if k in fields and not (v is None))
response = cc.app.apply(args.name, data)
_print_application_show(response)
_print_reminder_msg(args.name)
except exc.HTTPNotFound:

View File

@ -253,6 +253,22 @@ class KubeAppController(rest.RestController):
"Application-{} rejected: application not found.".format(directive)))
if directive == 'apply':
if not values:
mode = None
elif name not in constants.HELM_APP_APPLY_MODES.keys():
raise wsme.exc.ClientSideError(_(
"Application-apply rejected: Mode is not supported "
"for app {}.".format(name)))
elif (values['mode'] and
values['mode'] not in constants.HELM_APP_APPLY_MODES[name]):
raise wsme.exc.ClientSideError(_(
"Application-apply rejected: Mode {} for app {} is not "
"valid. Valid modes are {}.".format(
values['mode'], name,
constants.HELM_APP_APPLY_MODES[name])))
else:
mode = values['mode']
if db_app.status == constants.APP_APPLY_IN_PROGRESS:
raise wsme.exc.ClientSideError(_(
"Application-apply rejected: install/update is already "
@ -268,7 +284,8 @@ class KubeAppController(rest.RestController):
db_app.progress = None
db_app.save()
pecan.request.rpcapi.perform_app_apply(pecan.request.context,
db_app, app_not_already_applied)
db_app, app_not_already_applied,
mode=mode)
return KubeApp.convert_with_links(db_app)
else:
if db_app.status not in [constants.APP_APPLY_SUCCESS,

View File

@ -1464,6 +1464,22 @@ SUPPORTED_HELM_APP_NAMES = [
HELM_APP_OPENSTACK
]
# Apply mode for openstack app
OPENSTACK_RESTORE_DB = 'restore_db'
OPENSTACK_RESTORE_STORAGE = 'restore_storage'
OPENSTACK_NORMAL = 'normal'
OPENSTACK_APP_APPLY_MODES = [
OPENSTACK_RESTORE_DB,
OPENSTACK_RESTORE_STORAGE,
OPENSTACK_NORMAL
]
# Appliction Apply Modes
HELM_APP_APPLY_MODES = {
HELM_APP_OPENSTACK: OPENSTACK_APP_APPLY_MODES
}
# RBD Provisioner Ceph backend capabilities fields
K8S_RBD_PROV_STORAGECLASS_NAME = 'rbd_storageclass_name' # Customer
K8S_RBD_PROV_NAMESPACES = 'rbd_provisioner_namespaces' # Customer

View File

@ -440,7 +440,7 @@ class AppOperator(object):
if app.system_app:
LOG.info("Generating application overrides...")
self._helm.generate_helm_application_overrides(
app.name, cnamespace=None, armada_format=True, combined=True)
app.name, mode=None, cnamespace=None, armada_format=True, combined=True)
app.charts = self._get_list_of_charts(app.armada_mfile_abs)
self._save_images_list_by_charts(app)
# Get the list of images from the updated images overrides
@ -753,7 +753,7 @@ class AppOperator(object):
pass
return charts
def _get_overrides_files(self, charts):
def _get_overrides_files(self, charts, app_name, mode):
"""Returns list of override files or None, used in
application-install and application-delete."""
@ -773,7 +773,7 @@ class AppOperator(object):
# 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):
chart.name, chart.namespace, app_name, mode):
overrides = chart.namespace + '-' + chart.name + \
'-meta' + '.yaml'
overrides_file = os.path.join(common.HELM_OVERRIDES_PATH,
@ -952,7 +952,7 @@ class AppOperator(object):
LOG.exception(e)
self._abort_operation(app, constants.APP_UPLOAD_OP)
def perform_app_apply(self, rpc_app):
def perform_app_apply(self, rpc_app, mode):
"""Process application install request
This method processes node labels per configuration and invokes
@ -969,6 +969,7 @@ class AppOperator(object):
correct/update a previous manifest apply.
:param rpc_app: application object in the RPC request
:param mode: mode to control how to apply application manifest
:return boolean: whether application apply was successful
"""
@ -985,9 +986,9 @@ class AppOperator(object):
app, new_progress=constants.APP_PROGRESS_GENERATE_OVERRIDES)
LOG.info("Generating application overrides...")
self._helm.generate_helm_application_overrides(
app.name, cnamespace=None, armada_format=True,
app.name, mode, cnamespace=None, armada_format=True,
combined=True)
overrides_files = self._get_overrides_files(app.charts)
overrides_files = self._get_overrides_files(app.charts, app.name, mode)
if overrides_files:
LOG.info("Application overrides generated.")
# Ensure all chart overrides are readable by Armada

View File

@ -10664,15 +10664,16 @@ class ConductorManager(service.PeriodicService):
"""
self._app.perform_app_upload(rpc_app, tarfile)
def perform_app_apply(self, context, rpc_app, app_not_already_applied):
def perform_app_apply(self, context, rpc_app, app_not_already_applied,
mode):
"""Handling of application install request (via AppOperator)
:param context: request context.
:param rpc_app: data object provided in the rpc request
:param app_not_already_applied: app not yet successfully applied
:param mode: mode to control how to apply application manifest
"""
app_installed = self._app.perform_app_apply(rpc_app)
app_installed = self._app.perform_app_apply(rpc_app, mode)
if app_installed and app_not_already_applied:
# Update the VIM configuration as it may need to manage the newly
# installed application. Only do this if the application

View File

@ -1745,18 +1745,21 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy):
rpc_app=rpc_app,
tarfile=tarfile))
def perform_app_apply(self, context, rpc_app, app_not_already_applied):
def perform_app_apply(self, context, rpc_app, app_not_already_applied,
mode):
"""Handle application apply request
:param context: request context.
:param rpc_app: data object provided in the rpc request
:param app_not_already_applied: app not already succesfully applied
:param mode: mode to control how to apply application manifest
"""
return self.cast(context,
self.make_msg(
'perform_app_apply',
rpc_app=rpc_app,
app_not_already_applied=app_not_already_applied))
app_not_already_applied=app_not_already_applied,
mode=mode))
def perform_app_remove(self, context, rpc_app):
"""Handle application remove request

View File

@ -214,7 +214,7 @@ class BaseHelm(object):
"""
return {}
def get_meta_overrides(self, namespace):
def get_meta_overrides(self, namespace, app_name=None, mode=None):
"""
Return Armada-formatted chart-specific meta-overrides

View File

@ -26,7 +26,7 @@ class GarbdHelm(base.BaseHelm):
SUPPORTED_NAMESPACES = \
base.BaseHelm.SUPPORTED_NAMESPACES + [common.HELM_NS_OPENSTACK]
def get_meta_overrides(self, namespace):
def get_meta_overrides(self, namespace, app_name=None, mode=None):
def _meta_overrides():
if (self._num_controllers() < 2 or

View File

@ -365,19 +365,21 @@ class HelmOperator(object):
LOG.exception("chart name is required")
@helm_context
def generate_meta_overrides(self, chart_name, chart_namespace):
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))
chart_namespace, app_name, mode))
except exception.InvalidHelmNamespace:
raise
return overrides
@helm_context
def generate_helm_application_overrides(self, app_name, cnamespace=None,
def generate_helm_application_overrides(self, app_name, mode=None,
cnamespace=None,
armada_format=False,
combined=False):
"""Create the system overrides files for a supported application
@ -389,6 +391,7 @@ class HelmOperator(object):
:param app_name: name of the bundle of charts required to support an
application
:param mode: mode to control how to apply application manifest
:param cnamespace: (optional) namespace
:param armada_format: (optional) whether to emit in armada format
instead of helm format (with extra header)
@ -444,7 +447,9 @@ class HelmOperator(object):
# armada format already.
if armada_format:
overrides = self.generate_meta_overrides(chart_name,
cnamespace)
cnamespace,
app_name,
mode)
if overrides:
chart_meta_name = chart_name + '-meta'
self._write_chart_overrides(

View File

@ -21,6 +21,78 @@ 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

@ -21,7 +21,7 @@ class KeystoneApiProxyHelm(openstack.OpenstackBaseHelm):
SERVICE_NAME = constants.HELM_CHART_KEYSTONE_API_PROXY
DCORCH_SERVICE_NAME = 'dcorch'
def get_meta_overrides(self, namespace):
def get_meta_overrides(self, namespace, app_name=None, mode=None):
def _meta_overrides():
if (self._distributed_cloud_role() ==