Cherry-pick the following commits from master:
* Made improvements for rpmbuild I1dd7a54f7ade5414ad07cb8e12d0b018ecbc63c2 * Return status code instead of detailes Id0fe5dcae7ae19dee455a7ae8928e4a0d55fca26 * Add ability to delete file from service I6490ac2a391149a8660d5800b62ce8196e47bf00 * Add author field to the Compose Service form Increment service version after modification Forbid to edit version Closes bugs: #1262237, #1262239, #1262242 I5061704c7a3ca96c26d79edfb9f551df3e2f80e2 * Update instance link formation due to changes in Havana Fixes-bug: #1262713 I56e174f35ef4a60cffe31b0e619b53845a701ad6 * Add link with stack details in service details Ic3e34c9d341309ffd005a5479b2a6ef12b3bfe05 Implements: blueprint add-stack-link * Rename dynamic_ui package and normalize it. * Move muranodashboard.environments.services package to muranodashboard.dynamic_ui. * Move all code from dynamic_ui/__init__.py to dynamic_ui/services.py thus having normal empty __init__.py for dynamic_ui package (as in other packages). I395ee621578cb417314fe4960d74108deb848023 * Minor refactoring of dynamic_ui package. Remove one inner import in dynamic_ui.services and rewrite a bit iterate_over_services() function. If14022435a5ef269a448d5f568fe0f33421c9f23 * Do not log exception, if it is Http302 (redirect). I3488fc8523ec2e4628a26006c0211d2d468f189a * Show more sensible error messages during Service Creation. If Metadata Repository is not available, show more concise error messages. Also refactor wrapper for handling Metadata Repository exceptions, thus allowing to throw-away some boilerplate code. I6acd94bf2ca397941f45bc4c8d9e9fe74678b5ee Closes-bug: #1263463 Change-Id: Ia814eccf6841f7dcc153cf0c3c7c793f0f358023
This commit is contained in:
parent
1206c4119a
commit
c70a008456
|
@ -1,6 +1,5 @@
|
|||
recursive-include muranodashboard/static *
|
||||
recursive-include muranodashboard/templates *
|
||||
include muranodashboard/services/*.yaml
|
||||
include tools/pip-requires
|
||||
include run_tests.sh
|
||||
include ChangeLog
|
||||
|
@ -9,6 +8,6 @@ include MANIFEST.in
|
|||
include AUTHORS
|
||||
include LICENSE
|
||||
include ChangeLog
|
||||
include babel.cfg
|
||||
include tox.ini
|
||||
recursive-include build_addons/rpm *.sh
|
||||
global-exclude *.pyc
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
Name: murano-dashboard
|
||||
Version: 0.4
|
||||
Release: 5%{?dist}
|
||||
Summary: OpenStack Murano Dashboard
|
||||
Group: Applications/Communications
|
||||
License: Apache License, Version 2.0
|
||||
URL: https://launchpad.net/murano
|
||||
Source0: murano-dashboard-0.4.tar.gz
|
||||
BuildArch: noarch
|
||||
Requires: openstack-dashboard
|
||||
Requires: python-eventlet
|
||||
BuildRequires: python2-devel
|
||||
BuildRequires: python-setuptools
|
||||
BuildRequires: python-pbr
|
||||
BuildRequires: python-d2to1
|
||||
Requires: python-pbr >= 0.5.21, python-pbr < 1.0
|
||||
Requires: python-anyjson >= 0.3.3
|
||||
Requires: python-bunch >= 1.0.1
|
||||
Requires: python-iso8601 >= 0.1.8
|
||||
Requires: python-six >= 1.4.1
|
||||
Requires: PyYAML >= 3.10
|
||||
Requires: python-django-floppyforms >= 1.1
|
||||
Requires: python-ordereddict >= 1.1
|
||||
Requires: python-yaql >= 0.2
|
||||
Requires: python-muranoclient >= 0.4
|
||||
Requires: murano-metadataclient >= 0.4
|
||||
|
||||
|
||||
%description
|
||||
Murano Dashboard
|
||||
Sytem package - murano-dashboard
|
||||
Python package - murano-dashboard
|
||||
|
||||
%prep
|
||||
%setup -q muranodashboard-%{version}
|
||||
|
||||
%build
|
||||
%{__python} setup.py build
|
||||
|
||||
%install
|
||||
|
||||
%{__python} setup.py install -O1 --skip-build --root %{buildroot}
|
||||
mkdir -p %{buildroot}/usr/bin
|
||||
cp %{_builddir}/murano-dashboard-%{version}/build_addons/rpm/modify-horizon-config.sh %{buildroot}/usr/bin/
|
||||
|
||||
%post
|
||||
/usr/bin/modify-horizon-config.sh install
|
||||
if [ ! -d "/var/log/murano" ]; then
|
||||
mkdir -p /var/log/murano
|
||||
fi
|
||||
touch /var/log/murano/murano-dashboard.log
|
||||
mkdir -p /usr/share/openstack-dashboard/static/floppyforms
|
||||
mkdir -p /usr/share/openstack-dashboard/static/muranodashboard
|
||||
chown -R apache:root /usr/share/openstack-dashboard/static/muranodashboard
|
||||
chown -R apache:root /usr/share/openstack-dashboard/static/floppyforms
|
||||
chown apache:root /var/log/murano/murano-dashboard.log
|
||||
su -c "python /usr/share/openstack-dashboard/manage.py collectstatic --noinput | /usr/bin/logger -t murano-dashboard-install " -s /bin/bash apache
|
||||
service httpd restart
|
||||
|
||||
%files
|
||||
%{python_sitelib}/*
|
||||
/usr/bin/*
|
||||
|
||||
%changelog
|
||||
* Thu Dec 12 2013 built by Igor Yozhikov <iyozhikov@mirantis.com>
|
||||
- build number - 5
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
Name: #SYS_PKG_NAME#
|
||||
Version: 0.4
|
||||
Release: #RELEASE#
|
||||
Summary: OpenStack Murano Dashboard
|
||||
Group: Applications/Communications
|
||||
License: #LICENSE#
|
||||
URL: #URL#
|
||||
Source0: #SOURCE0#
|
||||
BuildArch: noarch
|
||||
Requires: openstack-dashboard
|
||||
Requires: python-eventlet
|
||||
BuildRequires: python2-devel
|
||||
BuildRequires: python-setuptools
|
||||
BuildRequires: python-pbr
|
||||
BuildRequires: python-d2to1
|
||||
#REQUIRES#
|
||||
|
||||
%description
|
||||
Murano Dashboard
|
||||
#DESCRIPTION#
|
||||
|
||||
%prep
|
||||
%setup -q muranodashboard-%{version}
|
||||
|
||||
%build
|
||||
%{__python} setup.py build
|
||||
|
||||
%install
|
||||
|
||||
%{__python} setup.py install -O1 --skip-build --root %{buildroot}
|
||||
mkdir -p %{buildroot}/usr/bin
|
||||
cp %{_builddir}/murano-dashboard-%{version}/build_addons/rpm/modify-horizon-config.sh %{buildroot}/usr/bin/
|
||||
|
||||
%post
|
||||
/usr/bin/modify-horizon-config.sh install
|
||||
if [ ! -d "/var/log/murano" ]; then
|
||||
mkdir -p /var/log/murano
|
||||
fi
|
||||
touch /var/log/murano/murano-dashboard.log
|
||||
mkdir -p /usr/share/openstack-dashboard/static/floppyforms
|
||||
mkdir -p /usr/share/openstack-dashboard/static/muranodashboard
|
||||
chown -R apache:root /usr/share/openstack-dashboard/static/muranodashboard
|
||||
chown -R apache:root /usr/share/openstack-dashboard/static/floppyforms
|
||||
chown apache:root /var/log/murano/murano-dashboard.log
|
||||
su -c "python /usr/share/openstack-dashboard/manage.py collectstatic --noinput | /usr/bin/logger -t murano-dashboard-install " -s /bin/bash apache
|
||||
service httpd restart
|
||||
|
||||
%files
|
||||
%{python_sitelib}/*
|
||||
/usr/bin/*
|
||||
|
||||
%changelog
|
||||
#CHANGELOG#
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# 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.
|
|
@ -14,13 +14,15 @@
|
|||
|
||||
import re
|
||||
import logging
|
||||
import types
|
||||
|
||||
from django import forms
|
||||
from django.core.validators import RegexValidator
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import muranodashboard.environments.services.fields as fields
|
||||
import muranodashboard.environments.services.helpers as helpers
|
||||
import muranodashboard.dynamic_ui.fields as fields
|
||||
import muranodashboard.dynamic_ui.helpers as helpers
|
||||
import yaql
|
||||
import types
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
@ -19,7 +19,8 @@ import tarfile
|
|||
import logging
|
||||
import shutil
|
||||
import hashlib
|
||||
from ..consts import CHUNK_SIZE, CACHE_DIR, ARCHIVE_PKG_PATH
|
||||
from muranodashboard.environments.consts import CHUNK_SIZE, CACHE_DIR, \
|
||||
ARCHIVE_PKG_PATH
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -27,6 +28,11 @@ log = logging.getLogger(__name__)
|
|||
from horizon.exceptions import ServiceCatalogException
|
||||
from openstack_dashboard.api.base import url_for
|
||||
from metadataclient.v1.client import Client
|
||||
from metadataclient.common.exceptions import CommunicationError, Unauthorized
|
||||
from metadataclient.common.exceptions import HTTPInternalServerError
|
||||
from horizon import exceptions
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
if not os.path.exists(CACHE_DIR):
|
||||
|
@ -106,6 +112,26 @@ def unpack_ui_package(archive_path):
|
|||
return dst_dir
|
||||
|
||||
|
||||
@contextmanager
|
||||
def metadata_exceptions(request):
|
||||
"""Handles all metadata-repository specific exceptions."""
|
||||
try:
|
||||
yield
|
||||
except CommunicationError:
|
||||
msg = _('Unable to communicate to Murano Metadata Repository. Add '
|
||||
'MURANO_METADATA_URL to local_settings')
|
||||
log.exception(msg)
|
||||
exceptions.handle(request, msg)
|
||||
except Unauthorized:
|
||||
msg = _('Configure Keystone in Murano Repository Service')
|
||||
log.exception(msg)
|
||||
exceptions.handle(request, msg)
|
||||
except HTTPInternalServerError:
|
||||
msg = _('There is a problem with Murano Repository Service')
|
||||
log.exception(msg)
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
def get_ui_metadata(request):
|
||||
"""Returns directory with unpacked definitions provided by Metadata
|
||||
Repository at `endpoint' URL or, if it was not found or some error has
|
||||
|
@ -116,9 +142,17 @@ def get_ui_metadata(request):
|
|||
if hash:
|
||||
metadata_dir = os.path.join(CACHE_DIR, hash)
|
||||
|
||||
#ToDO: Do we need to catch exception here?
|
||||
response, body_iter = metadataclient(request).metadata_client.get_ui_data(
|
||||
hash)
|
||||
data = None
|
||||
with metadata_exceptions(request):
|
||||
data = metadataclient(request).metadata_client.get_ui_data(hash)
|
||||
|
||||
# mimic normal return value in case metadata repository exception has just
|
||||
# been handled.
|
||||
# TODO: it would be better to use redirect here
|
||||
if data is None:
|
||||
return None, False
|
||||
response, body_iter = data
|
||||
|
||||
code = response.status
|
||||
if code == 200:
|
||||
with tempfile.NamedTemporaryFile(delete=False) as out:
|
||||
|
@ -135,9 +169,14 @@ def get_ui_metadata(request):
|
|||
log.info("Metadata package hash-sum hasn't changed, doing nothing")
|
||||
return metadata_dir, False
|
||||
else:
|
||||
msq = 'Unexpected response received: {0}'.format(code)
|
||||
msg = 'Unexpected response received: {0}'.format(code)
|
||||
if hash:
|
||||
log.error('Using existing version of metadata '
|
||||
'which may be outdated due to: {0}'.format(msq))
|
||||
'which may be outdated due to: {0}'.format(msg))
|
||||
return metadata_dir, False
|
||||
raise RuntimeError(msq)
|
||||
else:
|
||||
log.error('Unable to load any metadata due to: {0}'.format(msg))
|
||||
exceptions.handle(
|
||||
request,
|
||||
_('There is a problem with Murano Repository Service'))
|
||||
return None, False
|
|
@ -12,10 +12,14 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import logging
|
||||
from muranodashboard.dynamic_ui import metadata
|
||||
from muranodashboard.dynamic_ui.helpers import decamelize
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError: # python2.6
|
||||
|
@ -24,8 +28,7 @@ import yaml
|
|||
from yaml.scanner import ScannerError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import copy
|
||||
from ..consts import CACHE_REFRESH_SECONDS_INTERVAL
|
||||
import metadata
|
||||
from muranodashboard.environments.consts import CACHE_REFRESH_SECONDS_INTERVAL
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
_all_services = OrderedDict()
|
||||
|
@ -35,7 +38,7 @@ _current_cache_hash = None
|
|||
|
||||
class Service(object):
|
||||
def __init__(self, **kwargs):
|
||||
import muranodashboard.environments.services.forms as services
|
||||
import muranodashboard.dynamic_ui.forms as services
|
||||
for key, value in kwargs.iteritems():
|
||||
if key == 'forms':
|
||||
self.forms = []
|
||||
|
@ -66,7 +69,6 @@ class Service(object):
|
|||
|
||||
|
||||
def import_service(full_service_name, service_file):
|
||||
from muranodashboard.environments.services.helpers import decamelize
|
||||
try:
|
||||
with open(service_file) as stream:
|
||||
yaml_desc = yaml.load(stream)
|
||||
|
@ -109,7 +111,10 @@ def import_all_services(request):
|
|||
if time.time() - _last_check_time > CACHE_REFRESH_SECONDS_INTERVAL:
|
||||
_last_check_time = time.time()
|
||||
directory, modified = metadata.get_ui_metadata(request)
|
||||
if modified or not are_caches_in_sync():
|
||||
# check directory here in case metadata service is not available
|
||||
# and None is returned as directory value.
|
||||
# TODO: it is better to use redirect for that purpose (if possible)
|
||||
if modified or (directory and not are_caches_in_sync()):
|
||||
_all_services = {}
|
||||
for full_service_name in os.listdir(directory):
|
||||
final_dir = os.path.join(directory, full_service_name)
|
||||
|
@ -124,14 +129,14 @@ def import_all_services(request):
|
|||
def iterate_over_services(request):
|
||||
import_all_services(request)
|
||||
for service in sorted(_all_services.values(), key=lambda v: v.name):
|
||||
yield service.type, service, service.forms
|
||||
yield service.type, service
|
||||
|
||||
|
||||
def make_forms_getter(initial_forms=lambda request: copy.copy([])):
|
||||
def _get_forms(request):
|
||||
_forms = initial_forms(request)
|
||||
for srv_type, service, forms in iterate_over_services(request):
|
||||
for step, form in enumerate(forms):
|
||||
for srv_type, service in iterate_over_services(request):
|
||||
for step, form in enumerate(service.forms):
|
||||
_forms.append(('{0}-{1}'.format(srv_type, step), form))
|
||||
return _forms
|
||||
return _get_forms
|
||||
|
@ -147,7 +152,7 @@ def service_type_from_id(service_id):
|
|||
|
||||
def with_service(request, service_id, getter, default):
|
||||
service_type = service_type_from_id(service_id)
|
||||
for srv_type, service, forms in iterate_over_services(request):
|
||||
for srv_type, service in iterate_over_services(request):
|
||||
if srv_type == service_type:
|
||||
return getter(service)
|
||||
return default
|
||||
|
@ -178,7 +183,7 @@ def get_service_type(wizard):
|
|||
def get_service_choices(request, filter_func=None):
|
||||
filter_func = filter_func or (lambda srv: True, None)
|
||||
filtered, not_filtered = [], []
|
||||
for srv_type, service, forms in iterate_over_services(request):
|
||||
for srv_type, service in iterate_over_services(request):
|
||||
has_filtered, message = filter_func(service, request)
|
||||
if has_filtered:
|
||||
filtered.append((srv_type, service.name))
|
||||
|
@ -201,7 +206,7 @@ def get_service_checkers(request):
|
|||
|
||||
def get_service_descriptions(request):
|
||||
descriptions = []
|
||||
for srv_type, service, forms in iterate_over_services(request):
|
||||
for srv_type, service in iterate_over_services(request):
|
||||
description = getattr(service, 'description', _("<b>Default service \
|
||||
description</b>. If you want to see here something meaningful, please \
|
||||
provide `description' field in service markup."))
|
|
@ -18,7 +18,7 @@ from django.conf import settings
|
|||
from horizon.exceptions import ServiceCatalogException
|
||||
from openstack_dashboard.api.base import url_for
|
||||
from muranoclient.v1.client import Client
|
||||
from muranodashboard.environments.services import get_service_name
|
||||
from muranodashboard.dynamic_ui.services import get_service_name
|
||||
from muranoclient.common.exceptions import HTTPForbidden, HTTPNotFound
|
||||
from consts import STATUS_ID_READY, STATUS_ID_NEW
|
||||
from .network import get_network_params
|
||||
|
|
|
@ -16,8 +16,8 @@ import logging
|
|||
import json
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from .services import get_service_choices
|
||||
from .services.fields import get_murano_images
|
||||
from muranodashboard.dynamic_ui.services import get_service_choices
|
||||
from muranodashboard.dynamic_ui.fields import get_murano_images
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ from horizon import tabs
|
|||
from muranodashboard.environments.consts import LOG_LEVEL_TO_COLOR
|
||||
from muranodashboard.environments.consts import LOG_LEVEL_TO_TEXT
|
||||
from openstack_dashboard.api import nova as nova_api
|
||||
from openstack_dashboard.api import heat as heat_api
|
||||
|
||||
from muranodashboard.environments import api
|
||||
from muranodashboard.environments.tables import STATUS_DISPLAY_CHOICES
|
||||
|
@ -87,13 +88,24 @@ class OverviewTab(tabs.Tab):
|
|||
instances = nova_api.server_list(request)[0]
|
||||
|
||||
# HEAT always adds e before instance name
|
||||
instance_name = 'e' + environment_id + '.' + instance_hostname
|
||||
instance_name = 'e' + environment_id + '-' + instance_hostname
|
||||
|
||||
for instance in instances:
|
||||
if instance.name == instance_name:
|
||||
if instance_name in instance.name:
|
||||
unit_detail['instance'] = {
|
||||
'id': instance.id,
|
||||
'name': instance_name
|
||||
'name': instance.name
|
||||
}
|
||||
break
|
||||
|
||||
# add stack info
|
||||
stack_name = 'e' + environment_id
|
||||
existing_stacks = heat_api.stacks_list(request)
|
||||
for stack in existing_stacks:
|
||||
if stack.stack_name == stack_name:
|
||||
unit_detail['stack'] = {
|
||||
'id': stack.id,
|
||||
'name': stack.stack_name
|
||||
}
|
||||
break
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ from views import DetailServiceView
|
|||
from views import DeploymentsView
|
||||
from views import Wizard, EditEnvironmentView
|
||||
from forms import ChoiceServiceFormFactory
|
||||
from services import get_service_checkers
|
||||
from services import make_forms_getter
|
||||
from muranodashboard.dynamic_ui.services import get_service_checkers
|
||||
from muranodashboard.dynamic_ui.services import make_forms_getter
|
||||
from openstack_dashboard.dashboards.project.instances.views import DetailView
|
||||
|
||||
VIEW_MOD = 'muranodashboard.environments.views'
|
||||
|
|
|
@ -16,12 +16,12 @@ import logging
|
|||
import re
|
||||
import copy
|
||||
import json
|
||||
from functools import update_wrapper
|
||||
|
||||
from django.core.urlresolvers import reverse, reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib.formtools.wizard.views import SessionWizardView
|
||||
from django.http import HttpResponseRedirect
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
from horizon import tables
|
||||
|
@ -32,18 +32,16 @@ from tables import EnvironmentsTable
|
|||
from tables import ServicesTable
|
||||
from tables import DeploymentsTable
|
||||
from tables import EnvConfigTable
|
||||
|
||||
from workflows import CreateEnvironment, UpdateEnvironment
|
||||
from tabs import ServicesTabs, DeploymentTabs
|
||||
|
||||
from . import api
|
||||
from muranoclient.common.exceptions import HTTPUnauthorized, \
|
||||
CommunicationError, HTTPInternalServerError, HTTPForbidden, HTTPNotFound
|
||||
from functools import update_wrapper
|
||||
from django.utils.decorators import classonlymethod
|
||||
from services import get_service_descriptions
|
||||
from services import get_service_name
|
||||
from services import get_service_field_descriptions
|
||||
from muranodashboard.dynamic_ui.services import get_service_descriptions
|
||||
from muranodashboard.dynamic_ui.services import get_service_name
|
||||
from muranodashboard.dynamic_ui.services import get_service_field_descriptions
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from horizon.middleware import HorizonMiddleware
|
||||
from horizon.exceptions import Http302
|
||||
import traceback
|
||||
import logging
|
||||
|
||||
|
@ -21,6 +22,7 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
class ExceptionMiddleware(HorizonMiddleware):
|
||||
def process_exception(self, request, exception):
|
||||
logger.error(traceback.format_exc())
|
||||
if not isinstance(exception, Http302):
|
||||
logger.error(traceback.format_exc())
|
||||
return super(ExceptionMiddleware, self).process_exception(
|
||||
request, exception)
|
||||
|
|
|
@ -19,7 +19,7 @@ from horizon.forms import SelfHandlingForm
|
|||
from horizon import exceptions
|
||||
from horizon import messages
|
||||
from metadataclient.common.exceptions import HTTPException
|
||||
from muranodashboard.environments.services.metadata import metadataclient
|
||||
from muranodashboard.dynamic_ui.metadata import metadataclient
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -38,16 +38,13 @@ class UploadServiceForm(SelfHandlingForm):
|
|||
except HTTPException as e:
|
||||
log.exception(e)
|
||||
redirect = reverse('horizon:murano:service_catalog:index')
|
||||
if e.code == 400:
|
||||
msg = 'File already exists'
|
||||
else:
|
||||
msg = e.details
|
||||
exceptions.handle(request, _('Unable to upload service: '
|
||||
'{0}'.format(msg)),
|
||||
exceptions.handle(request,
|
||||
_('Unable to upload service. '
|
||||
'Error code: {0}'.format(e.code)),
|
||||
redirect=redirect)
|
||||
|
||||
|
||||
class UploadFileKnownTypeForm(SelfHandlingForm):
|
||||
class UploadFileToService(SelfHandlingForm):
|
||||
file = forms.FileField(label=_('Murano Repository File'),
|
||||
required=True,
|
||||
error_messages=
|
||||
|
@ -59,7 +56,7 @@ class UploadFileKnownTypeForm(SelfHandlingForm):
|
|||
*args, **kwargs):
|
||||
self.data_type = data_type
|
||||
self.service_id = full_service_name
|
||||
super(UploadFileKnownTypeForm, self).__init__(request, *args, **kwargs)
|
||||
super(UploadFileToService, self).__init__(request, *args, **kwargs)
|
||||
|
||||
def handle(self, request, data):
|
||||
filename = data['file'].name
|
||||
|
@ -94,7 +91,7 @@ class UploadFileKnownTypeForm(SelfHandlingForm):
|
|||
return file
|
||||
|
||||
|
||||
class UploadFileForm(UploadFileKnownTypeForm):
|
||||
class UploadFileForm(UploadFileToService):
|
||||
supported_data_types = {
|
||||
'ui': 'UI Definition (*.yaml)',
|
||||
'workflows': 'Murano Conductor Workflow (*xml)',
|
||||
|
|
|
@ -21,7 +21,7 @@ from horizon import exceptions
|
|||
from horizon import tables
|
||||
from horizon import messages
|
||||
from metadataclient.common.exceptions import HTTPException
|
||||
from muranodashboard.environments.services.metadata import metadataclient
|
||||
from muranodashboard.dynamic_ui.metadata import metadataclient
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -137,6 +137,8 @@ class ServiceCatalogTable(tables.DataTable):
|
|||
|
||||
service_valid = tables.Column('valid', verbose_name=_('Valid'))
|
||||
service_author = tables.Column('author', verbose_name=_('Author'))
|
||||
service_version = tables.Column('service_version',
|
||||
verbose_name=_('Version'))
|
||||
|
||||
def get_object_display(self, datum):
|
||||
return datum.service_display_name
|
||||
|
@ -197,6 +199,26 @@ class DeleteFile(tables.DeleteAction):
|
|||
redirect=redirect)
|
||||
|
||||
|
||||
class DeleteFileFromService(tables.DeleteAction):
|
||||
name = 'delete_file_from_service'
|
||||
data_type_singular = _('File')
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
service_id = self.table.kwargs.get('full_service_name')
|
||||
try:
|
||||
data_type, filename = obj_id.split('##')
|
||||
metadataclient(request).metadata_admin.delete_from_service(
|
||||
data_type, filename, service_id)
|
||||
except HTTPException as e:
|
||||
LOG.exception(e)
|
||||
redirect = reverse('horizon:murano:service_catalog:manage_service',
|
||||
args=(service_id,))
|
||||
exceptions.handle(
|
||||
request,
|
||||
_('Unable to remove file: {0}'.format(filename)),
|
||||
redirect=redirect)
|
||||
|
||||
|
||||
class UploadFile(tables.LinkAction):
|
||||
name = 'upload_file'
|
||||
verbose_name = _('Upload File')
|
||||
|
|
|
@ -13,14 +13,12 @@
|
|||
# under the License.
|
||||
import logging
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.urlresolvers import reverse, reverse_lazy
|
||||
from .tables import DeleteFile, DownloadFile
|
||||
from .forms import UploadFileKnownTypeForm
|
||||
from .tables import DeleteFileFromService, DownloadFile
|
||||
from horizon import tables, workflows, forms
|
||||
from muranodashboard.environments.services.forms import UpdatableFieldsForm
|
||||
from muranodashboard.environments.services.fields import TableField
|
||||
from muranodashboard.environments.services.fields import Column, CheckColumn
|
||||
from muranodashboard.environments.services.fields import RadioColumn
|
||||
from muranodashboard.dynamic_ui.forms import UpdatableFieldsForm
|
||||
from muranodashboard.dynamic_ui.fields import TableField
|
||||
from muranodashboard.dynamic_ui.fields import Column, CheckColumn
|
||||
from muranodashboard.dynamic_ui.fields import RadioColumn
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -58,11 +56,11 @@ def define_tables(table_name, step_verbose_name):
|
|||
name = table_name
|
||||
verbose_name = step_verbose_name
|
||||
table_actions = (UploadFileDataType,
|
||||
DeleteFile,
|
||||
DeleteFileFromService,
|
||||
)
|
||||
|
||||
row_actions = (DownloadFile,
|
||||
DeleteFile,
|
||||
DeleteFileFromService,
|
||||
)
|
||||
|
||||
return ObjectsTable
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
# under the License.
|
||||
|
||||
import logging
|
||||
from functools import wraps
|
||||
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
@ -26,52 +25,24 @@ from horizon.forms.views import ModalFormView
|
|||
from .tables import ServiceCatalogTable, MetadataObjectsTable
|
||||
from .utils import define_tables
|
||||
from .utils import STEP_NAMES
|
||||
from .forms import UploadServiceForm, UploadFileForm, UploadFileKnownTypeForm
|
||||
from .forms import UploadServiceForm, UploadFileForm, UploadFileToService
|
||||
from .workflows import ComposeService
|
||||
from muranodashboard.environments.services.metadata import metadataclient
|
||||
from metadataclient.common.exceptions import CommunicationError, Unauthorized
|
||||
from muranodashboard.dynamic_ui.metadata import metadataclient
|
||||
from muranodashboard.dynamic_ui.metadata import metadata_exceptions
|
||||
from metadataclient.common.exceptions import HTTPInternalServerError, NotFound
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def with_exception_handlers(default):
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(self):
|
||||
value = default
|
||||
try:
|
||||
value = func(self)
|
||||
except CommunicationError:
|
||||
LOG.exception(CommunicationError)
|
||||
exceptions.handle(
|
||||
self.request,
|
||||
_('Unable to communicate to Murano Metadata '
|
||||
'Repository. Add MURANO_METADATA_URL to local_'
|
||||
'settings'))
|
||||
except Unauthorized:
|
||||
LOG.exception(CommunicationError)
|
||||
exceptions.handle(
|
||||
self.request, _('Configure Keystone in Murano '
|
||||
'Repository Service'))
|
||||
except HTTPInternalServerError:
|
||||
LOG.exception(CommunicationError)
|
||||
exceptions.handle(
|
||||
self.request, _('There is a problem with Murano '
|
||||
'Repository Service'))
|
||||
return value
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class ServiceCatalogView(tables.DataTableView):
|
||||
table_class = ServiceCatalogTable
|
||||
template_name = 'service_catalog/index.html'
|
||||
|
||||
@with_exception_handlers([])
|
||||
def get_data(self):
|
||||
return metadataclient(self.request).metadata_admin.list_services()
|
||||
services, request = [], self.request
|
||||
with metadata_exceptions(request):
|
||||
services = metadataclient(request).metadata_admin.list_services()
|
||||
|
||||
return services
|
||||
|
||||
|
||||
class UploadServiceView(ModalFormView):
|
||||
|
@ -85,9 +56,12 @@ class ManageFilesView(tables.DataTableView):
|
|||
table_class = MetadataObjectsTable
|
||||
template_name = 'service_catalog/files.html'
|
||||
|
||||
@with_exception_handlers([])
|
||||
def get_data(self):
|
||||
return metadataclient(self.request).metadata_admin.get_service_files()
|
||||
files, request = [], self.request
|
||||
with metadata_exceptions(request):
|
||||
files = metadataclient(request).metadata_admin.get_service_files()
|
||||
|
||||
return files
|
||||
|
||||
|
||||
class ComposeServiceView(WorkflowView):
|
||||
|
@ -121,7 +95,8 @@ class ComposeServiceView(WorkflowView):
|
|||
'service_display_name':
|
||||
service.service_display_name,
|
||||
'author': getattr(service, 'author', ''),
|
||||
'version': getattr(service, 'version', 0.1),
|
||||
'service_version': getattr(service,
|
||||
'service_version', 0),
|
||||
'description': getattr(service, 'description', ''),
|
||||
'enabled': getattr(service, 'enabled', False)
|
||||
})
|
||||
|
@ -138,7 +113,7 @@ class ComposeServiceView(WorkflowView):
|
|||
|
||||
class UploadFileView2(ModalFormView):
|
||||
template_name = 'service_catalog/upload_file2.html'
|
||||
form_class = UploadFileKnownTypeForm
|
||||
form_class = UploadFileToService
|
||||
success_url = 'horizon:murano:service_catalog:manage_service'
|
||||
|
||||
def get_success_url(self):
|
||||
|
@ -189,28 +164,12 @@ class ManageServiceView(tables.MultiTableView):
|
|||
super(ManageServiceView, self).__init__(*args, **kwargs)
|
||||
|
||||
def _get_data(self, full_service_name):
|
||||
result = []
|
||||
try:
|
||||
result = metadataclient(self.request).metadata_admin.\
|
||||
get_service_info(full_service_name)
|
||||
info, request = {}, self.request
|
||||
with metadata_exceptions(request):
|
||||
info = metadataclient(request).metadata_admin.get_service_info(
|
||||
full_service_name)
|
||||
|
||||
except CommunicationError as e:
|
||||
LOG.exception(e)
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to communicate to Murano Metadata '
|
||||
'Repository. Add MURANO_METADATA_URL to local_'
|
||||
'settings'))
|
||||
except Unauthorized as e:
|
||||
LOG.exception(e)
|
||||
exceptions.handle(self.request, _('Configure Keystone in Murano '
|
||||
'Repository Service'))
|
||||
except HTTPInternalServerError as e:
|
||||
LOG.exception(e)
|
||||
exceptions.handle(self.request, _('There is a problem with Murano '
|
||||
'Repository Service'))
|
||||
else:
|
||||
return result
|
||||
return result
|
||||
return info
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ManageServiceView,
|
||||
|
@ -232,25 +191,13 @@ class ManageServiceView(tables.MultiTableView):
|
|||
|
||||
def get_file_list(self, data_type):
|
||||
full_service_name = self.kwargs['full_service_name']
|
||||
ui_files = []
|
||||
try:
|
||||
ui_files = metadataclient(self.request).metadata_admin. \
|
||||
get_service_files(data_type, full_service_name)
|
||||
except CommunicationError as e:
|
||||
LOG.exception(e)
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to communicate to Murano Metadata '
|
||||
'Repository. Add MURANO_METADATA_URL to local_'
|
||||
'settings'))
|
||||
except Unauthorized as e:
|
||||
LOG.exception(e)
|
||||
exceptions.handle(self.request, _('Configure Keystone in Murano '
|
||||
'Repository Service'))
|
||||
except HTTPInternalServerError as e:
|
||||
LOG.exception(e)
|
||||
exceptions.handle(self.request, _('There is a problem with Murano '
|
||||
'Repository Service'))
|
||||
return [file for file in ui_files if file.selected]
|
||||
|
||||
files, request = [], self.request
|
||||
with metadata_exceptions(request):
|
||||
files = metadataclient(request).metadata_admin.get_service_files(
|
||||
data_type, full_service_name)
|
||||
|
||||
return [file for file in files if file.selected]
|
||||
|
||||
def get_ui_data(self):
|
||||
return self.get_file_list('ui')
|
||||
|
|
|
@ -21,7 +21,7 @@ from horizon import exceptions
|
|||
from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
from muranodashboard.environments.services.metadata import metadataclient
|
||||
from muranodashboard.dynamic_ui.metadata import metadataclient
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -32,18 +32,21 @@ class EditManifest(Action):
|
|||
full_service_name = forms.RegexField(
|
||||
r'^[a-zA-Z0-9.]+$',
|
||||
label=_('Fully Qualified Service Name'), required=True)
|
||||
version = forms.CharField(label=_('Version'), initial='1')
|
||||
author = forms.CharField(label=_('Author'))
|
||||
enabled = forms.BooleanField(label=_('Active'), initial=True,
|
||||
widget=CheckboxInput)
|
||||
description = forms.CharField(label=_('Description'),
|
||||
widget=forms.Textarea)
|
||||
service_version = forms.IntegerField(label=_('Version'), initial=0)
|
||||
|
||||
def __init__(self, request, context, *args, **kwargs):
|
||||
super(EditManifest, self).__init__(request, context, *args, **kwargs)
|
||||
# disable field with service_id for service being modified
|
||||
self.fields['service_version'].widget.attrs.update(
|
||||
{'disabled': True, })
|
||||
if context and context.get('full_service_name'):
|
||||
self.fields['full_service_name'].widget.attrs.update({
|
||||
'disabled': True
|
||||
'disabled': True,
|
||||
})
|
||||
|
||||
class Meta:
|
||||
|
@ -56,7 +59,7 @@ class EditManifestStep(workflows.Step):
|
|||
action_class = EditManifest
|
||||
template_name = 'service_catalog/_workflow_step.html'
|
||||
contributes = ('service_display_name', 'full_service_name',
|
||||
'version', 'enabled', 'description')
|
||||
'author', 'service_version', 'enabled', 'description')
|
||||
|
||||
# Workflow doesn't handle Media inner class of widgets for us, so we need
|
||||
# to inject media directly to the step
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{% load i18n %}
|
||||
<p>{% blocktrans %}<strong>Service Name:</strong> is a human-readable service name.
|
||||
<p>{% blocktrans %}<strong>Service Name:</strong> is a human-readable service name
|
||||
{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}<strong>Fully Qualified Service Name:</strong> is an internal service name. It should be unique and can contain only alphanumeric symbols and dots.{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}<strong>Version:</strong> is a service version.{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}<strong>Fully Qualified Service Name:</strong> is an internal service name. It should be unique and can contain only alphanumeric symbols and dots{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}<strong>Author:</strong> is a company or person name{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}<strong>Active:</strong> whether this service should be visible for users or not.{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}<strong>Description:</strong> add text information about service. It will appear in service creation form in the Environments panel.{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}<strong>Version:</strong> is a service version. Will increment automatically{% endblocktrans %}</p>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
{{ value.name }}
|
||||
</a>
|
||||
</dd><br>
|
||||
{% elif key == 'stack'%}
|
||||
<dt>Heat stack name</dt>
|
||||
<dd>
|
||||
<a href=" {% url 'horizon:project:stacks:detail' value.id %}">
|
||||
{{ value.name }}
|
||||
</a>
|
||||
</dd><br>
|
||||
{% else %}
|
||||
<dt>{% blocktrans %} {{ key }} {% endblocktrans %}</dt>
|
||||
<dd>{% blocktrans %} {{ value }} {% endblocktrans %}</dd>
|
||||
|
@ -35,6 +42,13 @@
|
|||
{{ value.name }}
|
||||
</a>
|
||||
</dd><br>
|
||||
{% elif key == 'stack'%}
|
||||
<dt>Heat stack name</dt>
|
||||
<dd>
|
||||
<a href=" {% url 'horizon:project:stacks:detail' value.id %}">
|
||||
{{ value.name }}
|
||||
</a>
|
||||
</dd><br>
|
||||
{% else %}
|
||||
<dt>{% blocktrans %} {{ key }} {% endblocktrans %}</dt>
|
||||
<dd>{% blocktrans %} {{ value }} {% endblocktrans %}</dd>
|
||||
|
|
Loading…
Reference in New Issue