# Copyright (c) 2015 Ericsson AB. # Copyright (c) 2017-2023 Wind River Systems, Inc. # All Rights Reserved. # # 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. # """ Implementation of SQLAlchemy backend. """ import datetime import sqlalchemy import sys import threading from oslo_db import exception as db_exc from oslo_db.exception import DBDuplicateEntry from oslo_db.sqlalchemy import enginefacade from oslo_log import log as logging from oslo_utils import strutils from oslo_utils import uuidutils from sqlalchemy import desc from sqlalchemy import or_ from sqlalchemy.orm.exc import MultipleResultsFound from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm import joinedload_all from sqlalchemy.orm import load_only from sqlalchemy.sql.expression import true from dccommon import consts as dccommon_consts from dcmanager.common import consts from dcmanager.common import exceptions as exception from dcmanager.common.i18n import _ from dcmanager.db.sqlalchemy import migration from dcmanager.db.sqlalchemy import models LOG = logging.getLogger(__name__) _facade = None _main_context_manager = None _CONTEXT = threading.local() def _get_main_context_manager(): global _main_context_manager if not _main_context_manager: _main_context_manager = enginefacade.transaction_context() return _main_context_manager def get_engine(): return _get_main_context_manager().get_legacy_facade().get_engine() def get_session(): return _get_main_context_manager().get_legacy_facade().get_session() def read_session(): return _get_main_context_manager().reader.using(_CONTEXT) def write_session(): return _get_main_context_manager().writer.using(_CONTEXT) def get_backend(): """The backend is this module itself.""" return sys.modules[__name__] def model_query(context, *args): with read_session() as session: query = session.query(*args).options(joinedload_all('*')) return query def _session(context): return get_session() def is_admin_context(context): """Indicate if the request context is an administrator.""" if not context: LOG.warning(_('Use of empty request context is deprecated'), DeprecationWarning) raise Exception('die') return context.is_admin def is_user_context(context): """Indicate if the request context is a normal user.""" if not context: return False if context.is_admin: return False if not context.user or not context.project: return False return True def require_admin_context(f): """Decorator to require admin request context. The first argument to the wrapped function must be the context. """ def wrapper(*args, **kwargs): if not is_admin_context(args[0]): raise exception.AdminRequired() return f(*args, **kwargs) return wrapper def require_context(f): """Decorator to require *any* user or admin context. This does no authorization for user or project access matching, see :py:func:`authorize_project_context` and :py:func:`authorize_user_context`. The first argument to the wrapped function must be the context. """ def wrapper(*args, **kwargs): if not is_admin_context(args[0]) and not is_user_context(args[0]): raise exception.NotAuthorized() return f(*args, **kwargs) return wrapper ################### @require_context def subcloud_audits_get(context, subcloud_id): result = model_query(context, models.SubcloudAudits). \ filter_by(deleted=0). \ filter_by(subcloud_id=subcloud_id). \ first() if not result: raise exception.SubcloudNotFound(subcloud_id=subcloud_id) return result @require_context def subcloud_audits_get_all(context): return model_query(context, models.SubcloudAudits). \ filter_by(deleted=0). \ all() @require_context def subcloud_audits_update_all(context, values): with write_session() as session: result = session.query(models.SubcloudAudits).\ filter_by(deleted=0).\ update(values) return result @require_admin_context def subcloud_audits_create(context, subcloud_id): with write_session() as session: subcloud_audits_ref = models.SubcloudAudits() subcloud_audits_ref.subcloud_id = subcloud_id session.add(subcloud_audits_ref) return subcloud_audits_ref @require_admin_context def subcloud_audits_update(context, subcloud_id, values): with write_session() as session: subcloud_audits_ref = subcloud_audits_get(context, subcloud_id) subcloud_audits_ref.update(values) subcloud_audits_ref.save(session) return subcloud_audits_ref @require_context def subcloud_audits_get_all_need_audit(context, last_audit_threshold): with read_session() as session: result = session.query(models.SubcloudAudits).\ filter_by(deleted=0).\ filter(models.SubcloudAudits.audit_started_at <= models.SubcloudAudits.audit_finished_at).\ filter((models.SubcloudAudits.audit_finished_at < last_audit_threshold) | (models.SubcloudAudits.patch_audit_requested == true()) | (models.SubcloudAudits.firmware_audit_requested == true()) | (models.SubcloudAudits.load_audit_requested == true()) | (models.SubcloudAudits.kube_rootca_update_audit_requested == true()) | (models.SubcloudAudits.kubernetes_audit_requested == true())).\ all() return result # In the functions below it would be cleaner if the timestamp were calculated # by the DB server. If server time is in UTC func.now() might work. @require_context def subcloud_audits_get_and_start_audit(context, subcloud_id): with write_session() as session: subcloud_audits_ref = subcloud_audits_get(context, subcloud_id) subcloud_audits_ref.audit_started_at = datetime.datetime.utcnow() subcloud_audits_ref.save(session) return subcloud_audits_ref @require_context def subcloud_audits_end_audit(context, subcloud_id, audits_done): with write_session() as session: subcloud_audits_ref = subcloud_audits_get(context, subcloud_id) subcloud_audits_ref.audit_finished_at = datetime.datetime.utcnow() subcloud_audits_ref.state_update_requested = False # todo(abailey): define new constants for these audit strings # and update subcloud_audit_worker_manager to use them as well if 'patch' in audits_done: subcloud_audits_ref.patch_audit_requested = False if 'firmware' in audits_done: subcloud_audits_ref.firmware_audit_requested = False if 'load' in audits_done: subcloud_audits_ref.load_audit_requested = False if 'kube-rootca-update' in audits_done: subcloud_audits_ref.kube_rootca_update_audit_requested = False if 'kubernetes' in audits_done: subcloud_audits_ref.kubernetes_audit_requested = False subcloud_audits_ref.save(session) return subcloud_audits_ref # Find and fix up subcloud audits where the audit has taken too long. # We want to find subclouds that started an audit but never finished # it and update the "finished at" timestamp to be the same as # the "started at" timestamp. Returns the number of rows updated. @require_context def subcloud_audits_fix_expired_audits(context, last_audit_threshold, trigger_audits=False): values = { "audit_finished_at": models.SubcloudAudits.audit_started_at } if trigger_audits: # request all the special audits values['patch_audit_requested'] = True values['firmware_audit_requested'] = True values['load_audit_requested'] = True values['kubernetes_audit_requested'] = True values['kube_rootca_update_audit_requested'] = True with write_session() as session: result = session.query(models.SubcloudAudits).\ options(load_only("deleted", "audit_started_at", "audit_finished_at")).\ filter_by(deleted=0).\ filter(models.SubcloudAudits.audit_finished_at < last_audit_threshold).\ filter(models.SubcloudAudits.audit_started_at > models.SubcloudAudits.audit_finished_at).\ update(values, synchronize_session=False) return result ################### @require_context def subcloud_get(context, subcloud_id): result = model_query(context, models.Subcloud). \ filter_by(deleted=0). \ filter_by(id=subcloud_id). \ first() if not result: raise exception.SubcloudNotFound(subcloud_id=subcloud_id) return result @require_context def subcloud_get_with_status(context, subcloud_id): result = model_query(context, models.Subcloud, models.SubcloudStatus). \ outerjoin(models.SubcloudStatus, (models.Subcloud.id == models.SubcloudStatus.subcloud_id) | (not models.SubcloudStatus.subcloud_id)). \ filter(models.Subcloud.id == subcloud_id). \ filter(models.Subcloud.deleted == 0). \ order_by(models.SubcloudStatus.endpoint_type). \ all() if not result: raise exception.SubcloudNotFound(subcloud_id=subcloud_id) return result @require_context def subcloud_get_by_name(context, name): result = model_query(context, models.Subcloud). \ filter_by(deleted=0). \ filter_by(name=name). \ first() if not result: raise exception.SubcloudNameNotFound(name=name) return result @require_context def subcloud_get_by_region_name(context, region_name): result = model_query(context, models.Subcloud). \ filter_by(deleted=0). \ filter_by(region_name=region_name). \ first() if not result: raise exception.SubcloudRegionNameNotFound(region_name=region_name) return result @require_context def subcloud_get_by_name_or_region_name(context, name): result = model_query(context, models.Subcloud). \ filter_by(deleted=0). \ filter(or_(models.Subcloud.name == name, models.Subcloud.region_name == name)). \ first() if not result: raise exception.SubcloudNameOrRegionNameNotFound(name=name) return result @require_context def subcloud_get_all(context): return model_query(context, models.Subcloud). \ filter_by(deleted=0). \ all() def subcloud_get_all_ordered_by_id(context): return model_query(context, models.Subcloud). \ filter_by(deleted=0). \ order_by(models.Subcloud.id). \ all() @require_context def subcloud_get_all_with_status(context): result = model_query(context, models.Subcloud, models.SubcloudStatus). \ outerjoin(models.SubcloudStatus, (models.Subcloud.id == models.SubcloudStatus.subcloud_id) | (not models.SubcloudStatus.subcloud_id)). \ filter(models.Subcloud.deleted == 0). \ order_by(models.Subcloud.id). \ all() return result @require_admin_context def subcloud_create(context, name, description, location, software_version, management_subnet, management_gateway_ip, management_start_ip, management_end_ip, systemcontroller_gateway_ip, deploy_status, error_description, region_name, openstack_installed, group_id, data_install=None): with write_session() as session: subcloud_ref = models.Subcloud() subcloud_ref.name = name subcloud_ref.description = description subcloud_ref.location = location subcloud_ref.software_version = software_version subcloud_ref.management_state = dccommon_consts.MANAGEMENT_UNMANAGED subcloud_ref.availability_status = dccommon_consts.AVAILABILITY_OFFLINE subcloud_ref.management_subnet = management_subnet subcloud_ref.management_gateway_ip = management_gateway_ip subcloud_ref.management_start_ip = management_start_ip subcloud_ref.management_end_ip = management_end_ip subcloud_ref.systemcontroller_gateway_ip = systemcontroller_gateway_ip subcloud_ref.deploy_status = deploy_status subcloud_ref.error_description = error_description subcloud_ref.region_name = region_name subcloud_ref.audit_fail_count = 0 subcloud_ref.openstack_installed = openstack_installed subcloud_ref.group_id = group_id if data_install is not None: subcloud_ref.data_install = data_install session.add(subcloud_ref) session.flush() subcloud_status_create_all(context, subcloud_ref.id) subcloud_audits_create(context, subcloud_ref.id) return subcloud_ref @require_admin_context def subcloud_update(context, subcloud_id, management_state=None, availability_status=None, software_version=None, name=None, description=None, management_subnet=None, management_gateway_ip=None, management_start_ip=None, management_end_ip=None, location=None, audit_fail_count=None, deploy_status=None, backup_status=None, backup_datetime=None, error_description=None, openstack_installed=None, group_id=None, data_install=None, data_upgrade=None, first_identity_sync_complete=None, systemcontroller_gateway_ip=None, peer_group_id=None, rehome_data=None): with write_session() as session: subcloud_ref = subcloud_get(context, subcloud_id) if management_state is not None: subcloud_ref.management_state = management_state if availability_status is not None: subcloud_ref.availability_status = availability_status if software_version is not None: subcloud_ref.software_version = software_version if name is not None: subcloud_ref.name = name if description is not None: subcloud_ref.description = description if management_subnet is not None: subcloud_ref.management_subnet = management_subnet if management_gateway_ip is not None: subcloud_ref.management_gateway_ip = management_gateway_ip if management_start_ip is not None: subcloud_ref.management_start_ip = management_start_ip if management_end_ip is not None: subcloud_ref.management_end_ip = management_end_ip if location is not None: subcloud_ref.location = location if audit_fail_count is not None: subcloud_ref.audit_fail_count = audit_fail_count if data_install is not None: subcloud_ref.data_install = data_install if deploy_status is not None: subcloud_ref.deploy_status = deploy_status if backup_status is not None: subcloud_ref.backup_status = backup_status if backup_datetime is not None: subcloud_ref.backup_datetime = backup_datetime if error_description is not None: subcloud_ref.error_description = error_description if data_upgrade is not None: subcloud_ref.data_upgrade = data_upgrade if openstack_installed is not None: subcloud_ref.openstack_installed = openstack_installed if group_id is not None: subcloud_ref.group_id = group_id if first_identity_sync_complete is not None: subcloud_ref.first_identity_sync_complete = first_identity_sync_complete if systemcontroller_gateway_ip is not None: subcloud_ref.systemcontroller_gateway_ip = \ systemcontroller_gateway_ip if peer_group_id is not None: if str(peer_group_id).lower() == 'none': subcloud_ref.peer_group_id = None else: subcloud_ref.peer_group_id = peer_group_id if rehome_data is not None: subcloud_ref.rehome_data = rehome_data subcloud_ref.save(session) return subcloud_ref @require_admin_context def subcloud_bulk_update_by_ids(context, subcloud_ids, update_form): with write_session(): model_query(context, models.Subcloud). \ filter_by(deleted=0). \ filter(models.Subcloud.id.in_(subcloud_ids)). \ update(update_form, synchronize_session="fetch") @require_admin_context def subcloud_destroy(context, subcloud_id): with write_session() as session: subcloud_ref = subcloud_get(context, subcloud_id) session.delete(subcloud_ref) ########################## @require_context def subcloud_status_get(context, subcloud_id, endpoint_type): result = model_query(context, models.SubcloudStatus). \ filter_by(deleted=0). \ filter_by(subcloud_id=subcloud_id). \ filter_by(endpoint_type=endpoint_type). \ first() if not result: raise exception.SubcloudStatusNotFound(subcloud_id=subcloud_id, endpoint_type=endpoint_type) return result @require_context def subcloud_status_get_all(context, subcloud_id): return model_query(context, models.SubcloudStatus). \ filter_by(deleted=0). \ join(models.Subcloud, models.SubcloudStatus.subcloud_id == models.Subcloud.id). \ filter(models.Subcloud.id == subcloud_id).all() @require_context def subcloud_status_get_all_by_name(context, name): return model_query(context, models.SubcloudStatus). \ filter_by(deleted=0). \ join(models.Subcloud, models.SubcloudStatus.subcloud_id == models.Subcloud.id). \ filter(models.Subcloud.name == name).all() @require_admin_context def subcloud_status_create(context, subcloud_id, endpoint_type): with write_session() as session: subcloud_status_ref = models.SubcloudStatus() subcloud_status_ref.subcloud_id = subcloud_id subcloud_status_ref.endpoint_type = endpoint_type subcloud_status_ref.sync_status = dccommon_consts.SYNC_STATUS_UNKNOWN session.add(subcloud_status_ref) return subcloud_status_ref @require_admin_context def subcloud_status_create_all(context, subcloud_id): with write_session() as session: for endpoint_type in dccommon_consts.ENDPOINT_TYPES_LIST: subcloud_status_ref = models.SubcloudStatus() subcloud_status_ref.subcloud_id = subcloud_id subcloud_status_ref.endpoint_type = endpoint_type subcloud_status_ref.sync_status = dccommon_consts.SYNC_STATUS_UNKNOWN session.add(subcloud_status_ref) @require_admin_context def subcloud_status_delete(context, subcloud_id, endpoint_type): with write_session() as session: subcloud_status_ref = subcloud_status_get(context, subcloud_id, endpoint_type) session.delete(subcloud_status_ref) @require_admin_context def subcloud_status_update(context, subcloud_id, endpoint_type, sync_status): with write_session() as session: subcloud_status_ref = subcloud_status_get(context, subcloud_id, endpoint_type) subcloud_status_ref.sync_status = sync_status subcloud_status_ref.save(session) return subcloud_status_ref @require_admin_context def subcloud_status_update_endpoints(context, subcloud_id, endpoint_type_list, sync_status): """Update all statuses of endpoints in endpoint_type_list of a subcloud. Will raise if subcloud status does not exist. """ value = {"sync_status": sync_status} with write_session() as session: result = session.query(models.SubcloudStatus). \ filter_by(subcloud_id=subcloud_id). \ filter(models.SubcloudStatus.endpoint_type.in_(endpoint_type_list)). \ update(value, synchronize_session=False) if not result: raise exception.SubcloudStatusNotFound(subcloud_id=subcloud_id, endpoint_type="any") return result @require_admin_context def subcloud_status_destroy_all(context, subcloud_id): with write_session() as session: subcloud_statuses = subcloud_status_get_all(context, subcloud_id) if subcloud_statuses: for subcloud_status_ref in subcloud_statuses: session.delete(subcloud_status_ref) else: raise exception.SubcloudStatusNotFound(subcloud_id=subcloud_id, endpoint_type="any") ################### @require_context def sw_update_strategy_get(context, update_type=None): query = model_query(context, models.SwUpdateStrategy).filter_by(deleted=0) if update_type is not None: query = query.filter_by(type=update_type) result = query.first() if not result: raise exception.NotFound() return result @require_admin_context def sw_update_strategy_create(context, type, subcloud_apply_type, max_parallel_subclouds, stop_on_failure, state, extra_args=None): with write_session() as session: sw_update_strategy_ref = models.SwUpdateStrategy() sw_update_strategy_ref.type = type sw_update_strategy_ref.subcloud_apply_type = subcloud_apply_type sw_update_strategy_ref.max_parallel_subclouds = max_parallel_subclouds sw_update_strategy_ref.stop_on_failure = stop_on_failure sw_update_strategy_ref.state = state sw_update_strategy_ref.extra_args = extra_args session.add(sw_update_strategy_ref) return sw_update_strategy_ref @require_admin_context def sw_update_strategy_update(context, state=None, update_type=None, additional_args=None): with write_session() as session: sw_update_strategy_ref = \ sw_update_strategy_get(context, update_type=update_type) if state is not None: sw_update_strategy_ref.state = state if additional_args is not None: if sw_update_strategy_ref.extra_args is None: sw_update_strategy_ref.extra_args = additional_args else: # extend the existing dictionary sw_update_strategy_ref.extra_args = dict( sw_update_strategy_ref.extra_args, **additional_args) sw_update_strategy_ref.save(session) return sw_update_strategy_ref @require_admin_context def sw_update_strategy_destroy(context, update_type=None): with write_session() as session: sw_update_strategy_ref = \ sw_update_strategy_get(context, update_type=update_type) session.delete(sw_update_strategy_ref) ########################## @require_context def sw_update_opts_get(context, subcloud_id): result = model_query(context, models.SwUpdateOpts). \ filter_by(deleted=0). \ filter_by(subcloud_id=subcloud_id). \ first() # Note we will return None if not found return result @require_context def sw_update_opts_get_all_plus_subcloud_info(context): result = model_query(context, models.Subcloud, models.SwUpdateOpts). \ outerjoin(models.SwUpdateOpts, (models.Subcloud.id == models.SwUpdateOpts.subcloud_id) | (not models.SubcloudStatus.subcloud_id)). \ filter(models.Subcloud.deleted == 0). \ order_by(models.Subcloud.id). \ all() return result @require_admin_context def sw_update_opts_create(context, subcloud_id, storage_apply_type, worker_apply_type, max_parallel_workers, alarm_restriction_type, default_instance_action): with write_session() as session: sw_update_opts_ref = models.SwUpdateOpts() sw_update_opts_ref.subcloud_id = subcloud_id sw_update_opts_ref.storage_apply_type = storage_apply_type sw_update_opts_ref.worker_apply_type = worker_apply_type sw_update_opts_ref.max_parallel_workers = max_parallel_workers sw_update_opts_ref.alarm_restriction_type = alarm_restriction_type sw_update_opts_ref.default_instance_action = default_instance_action session.add(sw_update_opts_ref) return sw_update_opts_ref @require_admin_context def sw_update_opts_update(context, subcloud_id, storage_apply_type=None, worker_apply_type=None, max_parallel_workers=None, alarm_restriction_type=None, default_instance_action=None): with write_session() as session: sw_update_opts_ref = sw_update_opts_get(context, subcloud_id) if storage_apply_type is not None: sw_update_opts_ref.storage_apply_type = storage_apply_type if worker_apply_type is not None: sw_update_opts_ref.worker_apply_type = worker_apply_type if max_parallel_workers is not None: sw_update_opts_ref.max_parallel_workers = max_parallel_workers if alarm_restriction_type is not None: sw_update_opts_ref.alarm_restriction_type = alarm_restriction_type if default_instance_action is not None: sw_update_opts_ref.default_instance_action = \ default_instance_action sw_update_opts_ref.save(session) return sw_update_opts_ref @require_admin_context def sw_update_opts_destroy(context, subcloud_id): with write_session() as session: sw_update_opts_ref = sw_update_opts_get(context, subcloud_id) session.delete(sw_update_opts_ref) ########################## @require_context def sw_update_opts_default_get(context): result = model_query(context, models.SwUpdateOptsDefault). \ filter_by(deleted=0). \ first() # Note we will return None if not found return result @require_admin_context def sw_update_opts_default_create(context, storage_apply_type, worker_apply_type, max_parallel_workers, alarm_restriction_type, default_instance_action): with write_session() as session: sw_update_opts_default_ref = models.SwUpdateOptsDefault() sw_update_opts_default_ref.subcloud_id = 0 sw_update_opts_default_ref.storage_apply_type = storage_apply_type sw_update_opts_default_ref.worker_apply_type = worker_apply_type sw_update_opts_default_ref.max_parallel_workers = \ max_parallel_workers sw_update_opts_default_ref.alarm_restriction_type = \ alarm_restriction_type sw_update_opts_default_ref.default_instance_action = \ default_instance_action session.add(sw_update_opts_default_ref) return sw_update_opts_default_ref @require_admin_context def sw_update_opts_default_update(context, storage_apply_type=None, worker_apply_type=None, max_parallel_workers=None, alarm_restriction_type=None, default_instance_action=None): with write_session() as session: sw_update_opts_default_ref = sw_update_opts_default_get(context) if storage_apply_type is not None: sw_update_opts_default_ref.storage_apply_type = storage_apply_type if worker_apply_type is not None: sw_update_opts_default_ref.worker_apply_type = worker_apply_type if max_parallel_workers is not None: sw_update_opts_default_ref.max_parallel_workers = \ max_parallel_workers if alarm_restriction_type is not None: sw_update_opts_default_ref.alarm_restriction_type = \ alarm_restriction_type if default_instance_action is not None: sw_update_opts_default_ref.default_instance_action = \ default_instance_action sw_update_opts_default_ref.save(session) return sw_update_opts_default_ref @require_admin_context def sw_update_opts_default_destroy(context): with write_session() as session: sw_update_opts_default_ref = sw_update_opts_default_get(context) session.delete(sw_update_opts_default_ref) ########################## # system peer ########################## @require_context def system_peer_get(context, peer_id): try: result = model_query(context, models.SystemPeer). \ filter_by(deleted=0). \ filter_by(id=peer_id). \ one() except NoResultFound: raise exception.SystemPeerNotFound(peer_id=peer_id) except MultipleResultsFound: raise exception.InvalidParameterValue( err="Multiple entries found for system peer %s" % peer_id) return result @require_context def system_peer_get_by_name(context, name): try: result = model_query(context, models.SystemPeer). \ filter_by(deleted=0). \ filter_by(peer_name=name). \ one() except NoResultFound: raise exception.SystemPeerNameNotFound(name=name) except MultipleResultsFound: # This exception should never happen due to the UNIQUE setting for name raise exception.InvalidParameterValue( err="Multiple entries found for system peer %s" % name) return result @require_context def system_peer_get_by_uuid(context, uuid): try: result = model_query(context, models.SystemPeer). \ filter_by(deleted=0). \ filter_by(peer_uuid=uuid). \ one() except NoResultFound: raise exception.SystemPeerUUIDNotFound(uuid=uuid) except MultipleResultsFound: # This exception should never happen due to the UNIQUE setting for uuid raise exception.InvalidParameterValue( err="Multiple entries found for system peer %s" % uuid) return result @require_context def system_peer_get_all(context): result = model_query(context, models.SystemPeer). \ filter_by(deleted=0). \ order_by(models.SystemPeer.id). \ all() return result # This method returns all subcloud peer groups for a particular system peer @require_context def peer_group_get_for_system_peer(context, peer_id): return model_query(context, models.SubcloudPeerGroup). \ join(models.PeerGroupAssociation, models.SubcloudPeerGroup.id == models.PeerGroupAssociation.peer_group_id). \ filter(models.SubcloudPeerGroup.deleted == 0). \ filter(models.PeerGroupAssociation.system_peer_id == peer_id). \ order_by(models.SubcloudPeerGroup.id). \ all() @require_admin_context def system_peer_create(context, peer_uuid, peer_name, endpoint, username, password, gateway_ip, administrative_state="enabled", heartbeat_interval=60, heartbeat_failure_threshold=3, heartbeat_failure_policy="alarm", heartbeat_maintenance_timeout=600, heartbeat_status="created"): with write_session() as session: system_peer_ref = models.SystemPeer() system_peer_ref.peer_uuid = peer_uuid system_peer_ref.peer_name = peer_name system_peer_ref.manager_endpoint = endpoint system_peer_ref.manager_username = username system_peer_ref.manager_password = password system_peer_ref.peer_controller_gateway_ip = gateway_ip system_peer_ref.administrative_state = administrative_state system_peer_ref.heartbeat_interval = heartbeat_interval system_peer_ref.heartbeat_failure_threshold = \ heartbeat_failure_threshold system_peer_ref.heartbeat_failure_policy = heartbeat_failure_policy system_peer_ref.heartbeat_maintenance_timeout = \ heartbeat_maintenance_timeout system_peer_ref.heartbeat_status = heartbeat_status session.add(system_peer_ref) return system_peer_ref @require_admin_context def system_peer_update(context, peer_id, peer_uuid=None, peer_name=None, endpoint=None, username=None, password=None, gateway_ip=None, administrative_state=None, heartbeat_interval=None, heartbeat_failure_threshold=None, heartbeat_failure_policy=None, heartbeat_maintenance_timeout=None, heartbeat_status=None): with write_session() as session: system_peer_ref = system_peer_get(context, peer_id) if peer_uuid is not None: system_peer_ref.peer_uuid = peer_uuid if peer_name is not None: system_peer_ref.peer_name = peer_name if endpoint is not None: system_peer_ref.manager_endpoint = endpoint if username is not None: system_peer_ref.manager_username = username if password is not None: system_peer_ref.manager_password = password if gateway_ip is not None: system_peer_ref.peer_controller_gateway_ip = gateway_ip if administrative_state is not None: system_peer_ref.administrative_state = administrative_state if heartbeat_interval is not None: system_peer_ref.heartbeat_interval = heartbeat_interval if heartbeat_failure_threshold is not None: system_peer_ref.heartbeat_failure_threshold = \ heartbeat_failure_threshold if heartbeat_failure_policy is not None: system_peer_ref.heartbeat_failure_policy = heartbeat_failure_policy if heartbeat_maintenance_timeout is not None: system_peer_ref.heartbeat_maintenance_timeout = \ heartbeat_maintenance_timeout if heartbeat_status is not None: system_peer_ref.heartbeat_status = heartbeat_status system_peer_ref.save(session) return system_peer_ref @require_admin_context def system_peer_destroy(context, peer_id): with write_session() as session: system_peer_ref = system_peer_get(context, peer_id) session.delete(system_peer_ref) ########################## # subcloud group ########################## @require_context def subcloud_group_get(context, group_id): try: result = model_query(context, models.SubcloudGroup). \ filter_by(deleted=0). \ filter_by(id=group_id). \ one() except NoResultFound: raise exception.SubcloudGroupNotFound(group_id=group_id) except MultipleResultsFound: raise exception.InvalidParameterValue( err="Multiple entries found for subcloud group %s" % group_id) return result @require_context def subcloud_group_get_by_name(context, name): try: result = model_query(context, models.SubcloudGroup). \ filter_by(deleted=0). \ filter_by(name=name). \ one() except NoResultFound: raise exception.SubcloudGroupNameNotFound(name=name) except MultipleResultsFound: # This exception should never happen due to the UNIQUE setting for name raise exception.InvalidParameterValue( err="Multiple entries found for subcloud group %s" % name) return result # This method returns all subclouds for a particular subcloud group @require_context def subcloud_get_for_group(context, group_id): return model_query(context, models.Subcloud). \ filter_by(deleted=0). \ filter_by(group_id=group_id). \ order_by(models.Subcloud.id). \ all() @require_context def subcloud_group_get_all(context): result = model_query(context, models.SubcloudGroup). \ filter_by(deleted=0). \ order_by(models.SubcloudGroup.id). \ all() return result @require_admin_context def subcloud_group_create(context, name, description, update_apply_type, max_parallel_subclouds): with write_session() as session: subcloud_group_ref = models.SubcloudGroup() subcloud_group_ref.name = name subcloud_group_ref.description = description subcloud_group_ref.update_apply_type = update_apply_type subcloud_group_ref.max_parallel_subclouds = max_parallel_subclouds session.add(subcloud_group_ref) return subcloud_group_ref @require_admin_context def subcloud_group_update(context, group_id, name=None, description=None, update_apply_type=None, max_parallel_subclouds=None): with write_session() as session: subcloud_group_ref = subcloud_group_get(context, group_id) if name is not None: # Do not allow the name of the default group to be edited if subcloud_group_ref.id == consts.DEFAULT_SUBCLOUD_GROUP_ID: raise exception.SubcloudGroupNameViolation() # do not allow another group to use the default group name if name == consts.DEFAULT_SUBCLOUD_GROUP_NAME: raise exception.SubcloudGroupNameViolation() subcloud_group_ref.name = name if description is not None: subcloud_group_ref.description = description if update_apply_type is not None: subcloud_group_ref.update_apply_type = update_apply_type if max_parallel_subclouds is not None: subcloud_group_ref.max_parallel_subclouds = max_parallel_subclouds subcloud_group_ref.save(session) return subcloud_group_ref @require_admin_context def subcloud_group_destroy(context, group_id): with write_session() as session: subcloud_group_ref = subcloud_group_get(context, group_id) if subcloud_group_ref.id == consts.DEFAULT_SUBCLOUD_GROUP_ID: raise exception.SubcloudGroupDefaultNotDeletable(group_id=group_id) session.delete(subcloud_group_ref) def initialize_subcloud_group_default(engine): try: default_group = { "id": consts.DEFAULT_SUBCLOUD_GROUP_ID, "name": consts.DEFAULT_SUBCLOUD_GROUP_NAME, "description": consts.DEFAULT_SUBCLOUD_GROUP_DESCRIPTION, "update_apply_type": consts.DEFAULT_SUBCLOUD_GROUP_UPDATE_APPLY_TYPE, "max_parallel_subclouds": consts.DEFAULT_SUBCLOUD_GROUP_MAX_PARALLEL_SUBCLOUDS, "deleted": 0 } meta = sqlalchemy.MetaData(bind=engine) subcloud_group = sqlalchemy.Table('subcloud_group', meta, autoload=True) try: with engine.begin() as conn: conn.execute( subcloud_group.insert(), # pylint: disable=E1120 default_group) LOG.info("Default Subcloud Group created") except DBDuplicateEntry: # The default already exists. pass except Exception as ex: LOG.error("Exception occurred setting up default subcloud group", ex) ########################## ########################## # subcloud peer group ########################## @require_context def subcloud_peer_group_get(context, group_id): try: result = model_query(context, models.SubcloudPeerGroup). \ filter_by(deleted=0). \ filter_by(id=group_id). \ one() except NoResultFound: raise exception.SubcloudPeerGroupNotFound(group_id=group_id) except MultipleResultsFound: raise exception.InvalidParameterValue( err="Multiple entries found for subcloud peer group %s" % group_id) return result @require_context def subcloud_get_for_peer_group(context, peer_group_id): """Get all subclouds for a subcloud peer group. :param context: request context object :param peer_group_id: ID of the subcloud peer group """ return model_query(context, models.Subcloud). \ filter_by(deleted=0). \ filter_by(peer_group_id=peer_group_id). \ order_by(models.Subcloud.id). \ all() @require_context def subcloud_peer_group_get_all(context): result = model_query(context, models.SubcloudPeerGroup). \ filter_by(deleted=0). \ order_by(models.SubcloudPeerGroup.id). \ all() return result @require_context def subcloud_peer_group_get_by_name(context, name): try: result = model_query(context, models.SubcloudPeerGroup). \ filter_by(deleted=0). \ filter_by(peer_group_name=name). \ one() except NoResultFound: raise exception.SubcloudPeerGroupNameNotFound(name=name) except MultipleResultsFound: # This exception should never happen due to the UNIQUE setting for name raise exception.InvalidParameterValue( err="Multiple entries found for subcloud peer group %s" % name) return result @require_context def subcloud_peer_group_get_by_leader_id(context, system_leader_id): result = model_query(context, models.SubcloudPeerGroup). \ filter_by(deleted=0). \ filter_by(system_leader_id=system_leader_id). \ order_by(models.SubcloudPeerGroup.id). \ all() return result @require_admin_context def subcloud_peer_group_create(context, peer_group_name, group_priority, group_state, max_subcloud_rehoming, system_leader_id, system_leader_name, migration_status): with write_session() as session: subcloud_peer_group_ref = models.SubcloudPeerGroup() subcloud_peer_group_ref.peer_group_name = peer_group_name subcloud_peer_group_ref.group_priority = group_priority subcloud_peer_group_ref.group_state = group_state subcloud_peer_group_ref.max_subcloud_rehoming = max_subcloud_rehoming subcloud_peer_group_ref.system_leader_id = system_leader_id subcloud_peer_group_ref.system_leader_name = system_leader_name subcloud_peer_group_ref.migration_status = migration_status session.add(subcloud_peer_group_ref) return subcloud_peer_group_ref @require_admin_context def subcloud_peer_group_destroy(context, group_id): with write_session() as session: subcloud_peer_group_ref = subcloud_peer_group_get(context, group_id) session.delete(subcloud_peer_group_ref) @require_admin_context def subcloud_peer_group_update(context, group_id, peer_group_name=None, group_priority=None, group_state=None, max_subcloud_rehoming=None, system_leader_id=None, system_leader_name=None, migration_status=None): with write_session() as session: subcloud_peer_group_ref = subcloud_peer_group_get(context, group_id) if peer_group_name is not None: subcloud_peer_group_ref.peer_group_name = peer_group_name if group_priority is not None: subcloud_peer_group_ref.group_priority = group_priority if group_state is not None: subcloud_peer_group_ref.group_state = group_state if max_subcloud_rehoming is not None: subcloud_peer_group_ref.max_subcloud_rehoming = max_subcloud_rehoming if system_leader_id is not None: subcloud_peer_group_ref.system_leader_id = system_leader_id if system_leader_name is not None: subcloud_peer_group_ref.system_leader_name = system_leader_name if migration_status is not None: if str(migration_status).lower() == 'none': subcloud_peer_group_ref.migration_status = None else: subcloud_peer_group_ref.migration_status = migration_status subcloud_peer_group_ref.save(session) return subcloud_peer_group_ref ########################## ########################## # peer group association ########################## @require_admin_context def peer_group_association_create(context, peer_group_id, system_peer_id, peer_group_priority, sync_status, sync_message): with write_session() as session: peer_group_association_ref = models.PeerGroupAssociation() peer_group_association_ref.peer_group_id = peer_group_id peer_group_association_ref.system_peer_id = system_peer_id peer_group_association_ref.peer_group_priority = peer_group_priority peer_group_association_ref.sync_status = sync_status peer_group_association_ref.sync_message = sync_message session.add(peer_group_association_ref) return peer_group_association_ref @require_admin_context def peer_group_association_update(context, associate_id, peer_group_priority=None, sync_status=None, sync_message=None): with write_session() as session: association_ref = peer_group_association_get(context, associate_id) if peer_group_priority is not None: association_ref.peer_group_priority = peer_group_priority if sync_status is not None: association_ref.sync_status = sync_status if sync_message is not None: association_ref.sync_message = sync_message association_ref.save(session) return association_ref @require_admin_context def peer_group_association_destroy(context, association_id): with write_session() as session: association_ref = peer_group_association_get(context, association_id) session.delete(association_ref) @require_context def peer_group_association_get(context, association_id): try: result = model_query(context, models.PeerGroupAssociation). \ filter_by(deleted=0). \ filter_by(id=association_id). \ one() except NoResultFound: raise exception.PeerGroupAssociationNotFound( association_id=association_id) except MultipleResultsFound: raise exception.InvalidParameterValue( err="Multiple entries found for peer group association %s" % association_id) return result @require_context def peer_group_association_get_all(context): result = model_query(context, models.PeerGroupAssociation). \ filter_by(deleted=0). \ order_by(models.PeerGroupAssociation.id). \ all() return result # Each combination of 'peer_group_id' and 'system_peer_id' is unique # and appears only once in the entries. @require_context def peer_group_association_get_by_peer_group_and_system_peer_id(context, peer_group_id, system_peer_id): try: result = model_query(context, models.PeerGroupAssociation). \ filter_by(deleted=0). \ filter_by(peer_group_id=peer_group_id). \ filter_by(system_peer_id=system_peer_id). \ one() except NoResultFound: raise exception.PeerGroupAssociationCombinationNotFound( peer_group_id=peer_group_id, system_peer_id=system_peer_id) except MultipleResultsFound: # This exception should never happen due to the UNIQUE setting for name raise exception.InvalidParameterValue( err="Multiple entries found for peer group association %s,%s" % (peer_group_id, system_peer_id)) return result @require_context def peer_group_association_get_by_peer_group_id(context, peer_group_id): result = model_query(context, models.PeerGroupAssociation). \ filter_by(deleted=0). \ filter_by(peer_group_id=peer_group_id). \ order_by(models.PeerGroupAssociation.id). \ all() return result @require_context def peer_group_association_get_by_system_peer_id(context, system_peer_id): result = model_query(context, models.PeerGroupAssociation). \ filter_by(deleted=0). \ filter_by(system_peer_id=system_peer_id). \ order_by(models.PeerGroupAssociation.id). \ all() return result ########################## @require_context def strategy_step_get(context, subcloud_id): result = model_query(context, models.StrategyStep). \ filter_by(deleted=0). \ filter_by(subcloud_id=subcloud_id). \ first() if not result: raise exception.StrategyStepNotFound(subcloud_id=subcloud_id) return result @require_context def strategy_step_get_by_name(context, name): result = model_query(context, models.StrategyStep). \ filter_by(deleted=0). \ join(models.Subcloud, models.StrategyStep.subcloud_id == models.Subcloud.id). \ filter(models.Subcloud.name == name).first() if not result: raise exception.StrategyStepNameNotFound(name=name) return result @require_context def strategy_step_get_all(context): result = model_query(context, models.StrategyStep). \ filter_by(deleted=0). \ order_by(models.StrategyStep.id). \ all() return result @require_admin_context def strategy_step_create(context, subcloud_id, stage, state, details): with write_session() as session: strategy_step_ref = models.StrategyStep() strategy_step_ref.subcloud_id = subcloud_id strategy_step_ref.stage = stage strategy_step_ref.state = state strategy_step_ref.details = details session.add(strategy_step_ref) return strategy_step_ref @require_admin_context def strategy_step_update(context, subcloud_id, stage=None, state=None, details=None, started_at=None, finished_at=None): with write_session() as session: strategy_step_ref = strategy_step_get(context, subcloud_id) if stage is not None: strategy_step_ref.stage = stage if state is not None: strategy_step_ref.state = state if details is not None: strategy_step_ref.details = details if started_at is not None: strategy_step_ref.started_at = started_at if finished_at is not None: strategy_step_ref.finished_at = finished_at strategy_step_ref.save(session) return strategy_step_ref @require_admin_context def strategy_step_destroy_all(context): with write_session() as session: strategy_step_stages = strategy_step_get_all(context) if strategy_step_stages: for strategy_step_ref in strategy_step_stages: session.delete(strategy_step_ref) ########################## def initialize_db_defaults(engine): # a default value may already exist. If it does not, create it initialize_subcloud_group_default(engine) def db_sync(engine, version=None): """Migrate the database to `version` or the most recent version.""" retVal = migration.db_sync(engine, version=version) # returns None if migration has completed if retVal is None: initialize_db_defaults(engine) return retVal def db_version(engine): """Display the current database version.""" return migration.db_version(engine) ########################## def add_identity_filter(query, value, use_name=None): """Adds an identity filter to a query. Filters results by 'id', if supplied value is a valid integer. then attempts to filter results by 'uuid'; otherwise filters by name :param query: Initial query to add filter to. :param value: Value for filtering results by. :param use_name: Use name in filter :return: Modified query. """ if strutils.is_int_like(value): return query.filter_by(id=value) elif uuidutils.is_uuid_like(value): return query.filter_by(uuid=value) elif use_name: return query.filter_by(name=value) else: return query.filter_by(name=value) @require_context def _subcloud_alarms_get(context, name): query = model_query(context, models.SubcloudAlarmSummary). \ filter_by(deleted=0) query = add_identity_filter(query, name, use_name=True) try: return query.one() except NoResultFound: raise exception.SubcloudNameNotFound(name=name) except MultipleResultsFound: raise exception.InvalidParameterValue( err="Multiple entries found for subcloud %s" % name) @require_context def subcloud_alarms_get(context, name): return _subcloud_alarms_get(context, name) @require_context def subcloud_alarms_get_all(context, name=None): query = model_query(context, models.SubcloudAlarmSummary). \ filter_by(deleted=0) if name: query = add_identity_filter(query, name, use_name=True) return query.order_by(desc(models.SubcloudAlarmSummary.id)).all() @require_admin_context def subcloud_alarms_create(context, name, values): with write_session() as session: result = models.SubcloudAlarmSummary() result.name = name if not values.get('uuid'): values['uuid'] = uuidutils.generate_uuid() result.update(values) try: session.add(result) except db_exc.DBDuplicateEntry: raise exception.SubcloudAlreadyExists(region_name=name) return result @require_admin_context def subcloud_alarms_update(context, name, values): with write_session() as session: result = _subcloud_alarms_get(context, name) result.update(values) result.save(session) return result @require_admin_context def subcloud_alarms_delete(context, name): with write_session() as session: session.query(models.SubcloudAlarmSummary).\ filter_by(name=name).delete() @require_admin_context def subcloud_rename_alarms(context, subcloud_name, new_name): with write_session() as session: result = _subcloud_alarms_get(context, subcloud_name) result.name = new_name result.save(session) return result