# Copyright (c) 2013 Mirantis, Inc. # # 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. from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from oslo_log import log as logging import six from muranoclient.common import exceptions as exc from muranodashboard import api from muranodashboard.api import packages as packages_api from muranodashboard.common import utils from muranodashboard.environments import consts from muranodashboard.environments import topology KEY_ERROR_TEMPLATE = _( "Error fetching the environment. The page may be rendered incorrectly. " "Reason: %s") LOG = logging.getLogger(__name__) def get_status_messages_for_service(request, service_id, environment_id): client = api.muranoclient(request) deployments = client.deployments.list(environment_id) LOG.debug('Deployment::List {0}'.format(deployments)) result = '\n' # TODO(efedorova): Add updated time to logs if deployments: for deployment in reversed(deployments): reports = client.deployments.reports( environment_id, deployment.id, service_id) for report in reports: result += (utils.adjust_datestr(request, report.created) + ' - ' + report.text + '\n') return result def create_session(request, environment_id): sessions = request.session.get('sessions', {}) id = api.muranoclient(request).sessions.configure(environment_id).id sessions[environment_id] = id request.session['sessions'] = sessions return id class Session(object): @staticmethod def get_or_create(request, environment_id): """Get an open session id Gets id from already opened session for specified environment, otherwise opens new session and returns its id :param request: :param environment_id: :return: Session Id """ # We store opened sessions for each environment in dictionary per user sessions = request.session.get('sessions', {}) if environment_id in sessions: id = sessions[environment_id] else: id = create_session(request, environment_id) return id @staticmethod def get_or_create_or_delete(request, environment_id): """Get an open session id Gets id from session in open state for specified environment. If state is deployed, then the session is deleted and a new one is created. If there are no sessions, then a new one is created. Returns id of chosen or created session. :param request: :param environment_id: :return: Session id """ sessions = request.session.get('sessions', {}) client = api.muranoclient(request) if environment_id in sessions: id = sessions[environment_id] try: session_data = client.sessions.get(environment_id, id) except exc.HTTPForbidden: del sessions[environment_id] LOG.debug("The environment is being deployed by other user. " "Creating a new session " "for the environment {0}".format(environment_id)) return create_session(request, environment_id) else: if session_data.state in [consts.STATUS_ID_DEPLOY_FAILURE, consts.STATUS_ID_READY]: del sessions[environment_id] LOG.debug("The existing session has been already deployed." " Creating a new session " "for the environment {0}".format(environment_id)) return create_session(request, environment_id) else: LOG.debug("Creating a new session") return create_session(request, environment_id) LOG.debug("Found active session for the environment {0}" .format(environment_id)) return id @staticmethod def get_if_available(request, environment_id): """Get an id of open session if it exists and is not in deployed state. Returns None otherwise """ sessions = request.session.get('sessions', {}) client = api.muranoclient(request) if environment_id in sessions: id = sessions[environment_id] try: session_data = client.sessions.get(environment_id, id) except exc.HTTPForbidden: return None else: if session_data.state in [consts.STATUS_ID_DEPLOY_FAILURE, consts.STATUS_ID_READY]: return None return id @staticmethod def get(request, environment_id): """Get an open session id Gets id from already opened session for specified environment, otherwise returns None :param request: :param environment_id: :return: Session Id """ # We store opened sessions for each environment in dictionary per user sessions = request.session.get('sessions', {}) session_id = sessions.get(environment_id, '') if session_id: LOG.debug("Using session_id {0} for the environment {1}".format( session_id, environment_id)) else: LOG.debug("Session for the environment {0} not found".format( environment_id)) return session_id @staticmethod def set(request, environment_id, session_id): """Set an open session id sets id from already opened session for specified environment. :param request: :param environment_id: :param session_id """ # We store opened sessions for each environment in dictionary per user sessions = request.session.get('sessions', {}) sessions[environment_id] = session_id request.session['sessions'] = sessions def _update_env(env, request): # TODO(vakovalchuk): optimize latest deployment when limit is available deployments = deployments_list(request, env.id) if deployments: latest_deployment = deployments[0] try: deployed_services = {service['?']['id'] for service in latest_deployment.description['services']} except KeyError as e: deployed_services = set() exceptions.handle_recoverable( request, KEY_ERROR_TEMPLATE % e.message) else: deployed_services = set() if env.services: try: current_services = {service['?']['id'] for service in env.services} except KeyError as e: current_services = set() exceptions.handle_recoverable( request, KEY_ERROR_TEMPLATE % e.message) else: current_services = set() env.has_new_services = current_services != deployed_services if not env.has_new_services and env.status == consts.STATUS_ID_PENDING: env.status = consts.STATUS_ID_READY if not env.has_new_services and env.version == 0: if env.status == consts.STATUS_ID_READY: env.status = consts.STATUS_ID_NEW return env def environments_list(request): environments = [] client = api.muranoclient(request) with api.handled_exceptions(request): environments = client.environments.list() LOG.debug('Environment::List {0}'.format(environments)) for index, env in enumerate(environments): environments[index] = environment_get(request, env.id) return environments def environment_create(request, parameters): # name is required param body = {'name': parameters['name']} if 'defaultNetworks' in parameters: body['defaultNetworks'] = parameters['defaultNetworks'] env = api.muranoclient(request).environments.create(body) LOG.debug('Environment::Create {0}'.format(env)) return env def environment_delete(request, environment_id, abandon=False): action = 'Abandon' if abandon else 'Delete' LOG.debug('Environment::{0} '.format(action, environment_id)) return api.muranoclient(request).environments.delete( environment_id, abandon) def environment_get(request, environment_id): session_id = Session.get(request, environment_id) LOG.debug('Environment::Get '. format(environment_id, session_id)) client = api.muranoclient(request) env = client.environments.get(environment_id, session_id) acquired = getattr(env, 'acquired_by', None) if acquired and acquired != session_id: env = client.environments.get(environment_id, acquired) Session.set(request, environment_id, acquired) env = _update_env(env, request) LOG.debug('Environment::Get {0}'.format(env)) return env def environment_deploy(request, environment_id): session_id = Session.get_or_create_or_delete(request, environment_id) LOG.debug('Session::Get '.format(session_id)) env = api.muranoclient(request).sessions.deploy(environment_id, session_id) LOG.debug('Environment::Deploy ' ''.format(environment_id, session_id)) return env def environment_update(request, environment_id, name): return api.muranoclient(request).environments.update(environment_id, name) def action_allowed(request, environment_id): env = environment_get(request, environment_id) status = getattr(env, 'status', None) return status not in ('deploying',) def services_list(request, environment_id): """Get environment applications. This function collects data from Murano API and modifies it only for dashboard purposes. Those changes don't impact application deployment parameters. """ def strip(msg, to=100): return u'%s...' % msg[:to] if len(msg) > to else msg services = [] # need to create new session to see services deployed by other user session_id = Session.get(request, environment_id) get_environment = api.muranoclient(request).environments.get environment = get_environment(environment_id, session_id) try: client = api.muranoclient(request) reports = client.environments.last_status(environment_id, session_id) except exc.HTTPNotFound: LOG.exception(_('Could not retrieve latest status for ' 'the {0} environment').format(environment_id)) reports = {} for service_item in environment.services or []: service_data = service_item try: service_id = service_data['?']['id'] except KeyError as e: exceptions.handle_recoverable( request, KEY_ERROR_TEMPLATE % e.message) continue if service_id in reports and reports[service_id]: last_operation = strip(reports[service_id].text) time = reports[service_id].updated else: last_operation = 'Component draft created' \ if environment.version == 0 else '' try: time = service_data['updated'][:-7] except KeyError: time = None service_data['environment_id'] = environment_id service_data['environment_version'] = environment.version service_data['operation'] = last_operation service_data['operation_updated'] = time if service_data['?'].get('name'): service_data['name'] = service_data['?']['name'] if (consts.DASHBOARD_ATTRS_KEY not in service_data['?'] or not service_data['?'][consts.DASHBOARD_ATTRS_KEY].get('name')): try: fqn = service_data['?']['type'] except KeyError as e: exceptions.handle_recoverable( request, KEY_ERROR_TEMPLATE % e.message) continue version = None if '/' in fqn: version, fqn = fqn.split('/')[1].split('@') pkg = packages_api.app_by_fqn(request, fqn, version=version) if pkg: app_name = pkg.name storage = service_data['?'].setdefault( consts.DASHBOARD_ATTRS_KEY, {}) storage['name'] = app_name services.append(service_data) LOG.debug('Service::List') return [utils.Bunch(**service) for service in services] def service_list_by_fqns(request, environment_id, fqns): if environment_id is None: return [] services = services_list(request, environment_id) LOG.debug('Service::Instances::List') try: services = [service for service in services if service['?']['type'].split('/')[0] in fqns] except KeyError as e: services = [] exceptions.handle_recoverable(request, KEY_ERROR_TEMPLATE % e.message) return services def service_create(request, environment_id, parameters): # We should be able to delete session if we want to add new services to # this environment. session_id = Session.get_or_create_or_delete(request, environment_id) LOG.debug('Service::Create {0}'.format(parameters['?']['type'])) return api.muranoclient(request).services.post(environment_id, path='/', data=parameters, session_id=session_id) def service_delete(request, environment_id, service_id): LOG.debug('Service::Delete '.format(service_id)) session_id = Session.get_or_create_or_delete(request, environment_id) return api.muranoclient(request).services.delete(environment_id, '/' + service_id, session_id) def service_get(request, environment_id, service_id): services = services_list(request, environment_id) LOG.debug("Return service detail for a specified id") try: for service in services: if service['?']['id'] == service_id: return service except KeyError as e: exceptions.handle_recoverable(request, KEY_ERROR_TEMPLATE % e.message) return None def extract_actions_list(service): actions_data = service['?'].get('_actions', {}) def make_action_datum(action_id, _action): return dict(_action.items() + [('id', action_id)]) return [make_action_datum(_id, action) for (_id, action) in six.iteritems(actions_data) if action.get('enabled')] def run_action(request, environment_id, action_id): mc = api.muranoclient(request) return mc.actions.call(environment_id, action_id) def deployments_list(request, environment_id): LOG.debug('Deployments::List') deployments = api.muranoclient(request).deployments.list(environment_id) LOG.debug('Environment::List {0}'.format(deployments)) return deployments def deployment_history(request): LOG.debug('Deployment::History') deployment_history = api.muranoclient(request).deployments.list( None, all_environments=True) for deployment in deployment_history: reports = deployment_reports(request, deployment.environment_id, deployment.id) deployment.reports = reports LOG.debug('Deployment::History {0}'.format(deployment_history)) return deployment_history def deployment_reports(request, environment_id, deployment_id): LOG.debug('Deployment::Reports::List') reports = api.muranoclient(request).deployments.reports(environment_id, deployment_id) LOG.debug('Deployment::Reports::List {0}'.format(reports)) return reports def get_deployment_start(request, environment_id, deployment_id): deployments = api.muranoclient(request).deployments.list(environment_id) LOG.debug('Get deployment start time') for deployment in deployments: if deployment.id == deployment_id: return utils.adjust_datestr(request, deployment.started) return None def get_deployment_descr(request, environment_id, deployment_id): deployments = api.muranoclient(request).deployments.list(environment_id) LOG.debug('Get deployment description') for deployment in deployments: if deployment.id == deployment_id: return deployment.description return None def load_environment_data(request, environment_id): environment = environment_get(request, environment_id) return topology.render_d3_data(request, environment)