Integrate with newer client

In overall following change adjustes monasca-ui code to the
shape of the python-monascaclient (integrated with osc-lib,
see Depends-On for details).

Extra:
- added profiling capabilities
- added caching of client instances
- installing master python-monascaclient for test purposes
- supporting installing python-monascaclient if specified via Depends-On

Depends-On: I1712a24739438e2d8331a495f18f357749a633c5
Change-Id: I01d0f3d9814376f9358feb23494e1ca5231aedac
This commit is contained in:
Tomasz Trębski 2017-05-17 14:17:05 +02:00
parent 91f7719951
commit 9dc87decbf
8 changed files with 229 additions and 94 deletions

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
from django.contrib import messages
from django.core.paginator import Paginator, EmptyPage
from django.core.urlresolvers import reverse_lazy, reverse # noqa
@ -24,7 +26,7 @@ from horizon import tables
from horizon.utils import functions as utils
from horizon import workflows
import monascaclient.exc as exc
from monascaclient import exc
from monitoring.alarmdefs import constants
from monitoring.alarmdefs import forms as alarm_forms
from monitoring.alarmdefs import tables as alarm_tables
@ -34,6 +36,7 @@ from monitoring import api
from openstack_dashboard import policy
LOG = logging.getLogger(__name__)
PREV_PAGE_LIMIT = 100
@ -53,7 +56,8 @@ class IndexView(tables.DataTableView):
results = paginator.page(1)
except EmptyPage:
results = paginator.page(paginator.num_pages)
except Exception:
except Exception as ex:
LOG.exception(str(ex))
messages.error(self.request, _("Could not retrieve alarm definitions"))
return results
@ -147,7 +151,7 @@ class AlarmDetailView(TemplateView):
notification['undetermined'] = False
notifications.append(notification)
# except exceptions.NOT_FOUND:
except exc.HTTPException:
except exc.HttpError:
msg = _("Notification %s has already been deleted.") % id
notifications.append({"id": id,
"name": unicode(msg),
@ -214,7 +218,7 @@ class AlarmEditView(forms.ModalFormView):
notification['undetermined'] = False
notifications.append(notification)
# except exceptions.NOT_FOUND:
except exc.HTTPException:
except exc.HttpError:
msg = _("Notification %s has already been deleted.") % id
messages.warning(self.request, msg)

View File

@ -297,9 +297,9 @@ class AlarmHistoryView(tables.DataTableView):
limit = utils.get_page_size(self.request)
try:
results = api.monitor.alarm_history(self.request,
object_id,
page_offset,
limit)
object_id,
page_offset,
limit)
paginator = Paginator(results, limit)
contacts = paginator.page(1)
except EmptyPage:

99
monitoring/api/client.py Normal file
View File

@ -0,0 +1,99 @@
# Copyright 2017 Fujitsu LIMITED
#
# 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 horizon import exceptions
from horizon.utils import memoized
from oslo_log import log as logging
from openstack_dashboard.api import base
from monascaclient import client as mon_client
from monitoring.config import local_settings as settings
LOG = logging.getLogger(__name__)
INSECURE = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
CACERT = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
KEYSTONE_SERVICE = 'identity'
MONITORING_SERVICE = getattr(settings, 'MONITORING_SERVICE_TYPE', 'monitoring')
VERSIONS = base.APIVersionManager(
MONITORING_SERVICE,
preferred_version=getattr(settings,
'OPENSTACK_API_VERSIONS',
{}).get(MONITORING_SERVICE, 2.0)
)
VERSIONS.load_supported_version(2.0, {'client': mon_client, 'version': '2_0'})
def _get_endpoint(request):
try:
endpoint = base.url_for(request,
service_type=settings.MONITORING_SERVICE_TYPE,
endpoint_type=settings.MONITORING_ENDPOINT_TYPE)
except exceptions.ServiceCatalogException:
endpoint = 'http://127.0.0.1:8070/v2.0'
LOG.warning('Monasca API location could not be found in Service '
'Catalog, using default: {0}'.format(endpoint))
return endpoint
def _get_auth_params_from_request(request):
"""Extracts the properties from the request object needed by the monascaclient
call below. These will be used to memoize the calls to monascaclient
"""
LOG.debug('Extracting intel from request')
return (
request.user.user_domain_id,
request.user.token.id,
request.user.tenant_id,
request.user.token.project.get('domain_id'),
base.url_for(request, MONITORING_SERVICE),
base.url_for(request, KEYSTONE_SERVICE)
)
@memoized.memoized_with_request(_get_auth_params_from_request)
def monascaclient(request_auth_params, version=None):
(
user_domain_id,
token_id,
project_id,
project_domain_id,
monasca_url,
auth_url
) = request_auth_params
# NOTE(trebskit) this is bit hacky, we should
# go straight into using numbers as version representation
version = (VERSIONS.get_active_version()['version']
if not version else version)
LOG.debug('Monasca::Client <Url: %s> <Version: %s>'
% (monasca_url, version))
c = mon_client.Client(api_version=version,
token=token_id,
project_id=project_id,
user_domain_id=user_domain_id,
project_domain_id=project_domain_id,
insecure=INSECURE,
cert=CACERT,
auth_url=auth_url,
endpoint=monasca_url)
return c

View File

@ -10,59 +10,25 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
from oslo_log import log
from monascaclient import client as monasca_client
from openstack_dashboard.api import base
from monitoring.config import local_settings as settings
LOG = logging.getLogger(__name__)
def format_parameters(params):
parameters = {}
for count, p in enumerate(params, 1):
parameters['Parameters.member.%d.ParameterKey' % count] = p
parameters['Parameters.member.%d.ParameterValue' % count] = params[p]
return parameters
def monasca_endpoint(request):
endpoint = base.url_for(request, settings.MONITORING_SERVICE_TYPE)
if endpoint.endswith('/'):
endpoint = endpoint[:-1]
return endpoint
def monascaclient(request, password=None):
api_version = "2_0"
endpoint = monasca_endpoint(request)
LOG.debug('monascaclient connection created using token "%s" , url "%s"' %
(request.user.token.id, endpoint))
kwargs = {
'token': request.user.token.id,
'insecure': settings.OPENSTACK_SSL_NO_VERIFY,
'ca_file': settings.OPENSTACK_SSL_CACERT,
'username': request.user.username,
'password': password
# 'timeout': args.timeout,
# 'ca_file': args.ca_file,
# 'cert_file': args.cert_file,
# 'key_file': args.key_file,
}
client = monasca_client.Client(api_version, endpoint, **kwargs)
client.format_parameters = format_parameters
return client
from openstack_dashboard.contrib.developer.profiler import api as profiler
from monitoring.api import client
LOG = log.getLogger(__name__)
@profiler.trace
def alarm_list(request, offset=0, limit=10000, marker=None, paginate=False):
result = monascaclient(request).alarms.list(offset=offset, limit=limit)
result = client.monascaclient(request).alarms.list(offset=offset,
limit=limit)
return result['elements'] if type(result) is dict else result
@profiler.trace
def alarm_list_by_dimension(request, dimensions, offset=0, limit=10000,
marker=None, paginate=False):
marker=None, paginate=False):
dim_dict = {}
metric = None
dimensions = dimensions.split(",")
@ -76,118 +42,152 @@ def alarm_list_by_dimension(request, dimensions, offset=0, limit=10000,
else:
dim_dict[item] = None
if metric:
result = monascaclient(request).alarms.list(offset=offset, limit=limit,
metric_dimensions=dim_dict,
metric_name=metric)
result = client.monascaclient(request).alarms.list(offset=offset,
limit=limit,
metric_dimensions=dim_dict,
metric_name=metric)
else:
result = monascaclient(request).alarms.list(offset=offset, limit=limit,
metric_dimensions=dim_dict)
result = client.monascaclient(request).alarms.list(offset=offset,
limit=limit,
metric_dimensions=dim_dict)
return result['elements'] if type(result) is dict else result
@profiler.trace
def alarm_show(request, alarm_id):
result = monascaclient(request).alarms.get(alarm_id=alarm_id)
result = client.monascaclient(request).alarms.get(alarm_id=alarm_id)
return result
@profiler.trace
def alarm_delete(request, alarm_id):
return monascaclient(request).alarms.delete(alarm_id=alarm_id)
return client.monascaclient(request).alarms.delete(alarm_id=alarm_id)
@profiler.trace
def alarm_history(request, alarm_id, offset=0, limit=10000):
result = monascaclient(request).alarms.history(alarm_id=alarm_id, offset=offset, limit=limit)
result = client.monascaclient(request).alarms.history(alarm_id=alarm_id,
offset=offset,
limit=limit)
return result['elements'] if type(result) is dict else result
@profiler.trace
def alarm_get(request, alarm_id):
return monascaclient(request).alarms.get(alarm_id=alarm_id)
return client.monascaclient(request).alarms.get(alarm_id=alarm_id)
@profiler.trace
def alarm_patch(request, **kwargs):
return monascaclient(request).alarms.patch(**kwargs)
return client.monascaclient(request).alarms.patch(**kwargs)
@profiler.trace
def alarmdef_list(request, offset=0, limit=10000, marker=None, paginate=False):
result = monascaclient(request).alarm_definitions.list(offset=offset, limit=limit)
result = client.monascaclient(request).alarm_definitions.list(offset=offset,
limit=limit)
return result['elements'] if type(result) is dict else result
def alarmdef_list_by_service(request, service_name, marker=None, paginate=False):
@profiler.trace
def alarmdef_list_by_service(request, service_name, marker=None,
paginate=False):
service_dim = {'service': service_name}
result = monascaclient(request).alarm_definitions.list(dimensions=service_dim)
result = client.monascaclient(request).alarm_definitions.list(
dimensions=service_dim)
return result['elements'] if type(result) is dict else result
@profiler.trace
def alarmdef_delete(request, alarm_id):
return monascaclient(request).alarm_definitions.delete(alarm_id=alarm_id)
return client.monascaclient(request).alarm_definitions.delete(
alarm_id=alarm_id)
@profiler.trace
def alarmdef_history(request, alarm_id):
return monascaclient(request).alarm_definitions.history(alarm_id=alarm_id)
return client.monascaclient(request).alarm_definitions.history(
alarm_id=alarm_id)
@profiler.trace
def alarmdef_get(request, alarm_id):
return monascaclient(request).alarm_definitions.get(alarm_id=alarm_id)
return client.monascaclient(request).alarm_definitions.get(alarm_id=alarm_id)
@profiler.trace
def alarmdef_get_by_name(request, name):
return monascaclient(request).alarm_definitions.list(
return client.monascaclient(request).alarm_definitions.list(
name=name,
limit=1
)
def alarmdef_create(request, password=None, **kwargs):
return monascaclient(request, password).alarm_definitions.create(**kwargs)
@profiler.trace
def alarmdef_create(request, **kwargs):
return client.monascaclient(request).alarm_definitions.create(**kwargs)
@profiler.trace
def alarmdef_update(request, **kwargs):
return monascaclient(request).alarm_definitions.update(**kwargs)
return client.monascaclient(request).alarm_definitions.update(**kwargs)
@profiler.trace
def alarmdef_patch(request, **kwargs):
return monascaclient(request).alarm_definitions.patch(**kwargs)
return client.monascaclient(request).alarm_definitions.patch(**kwargs)
def notification_list(request, offset=0, limit=10000, marker=None, paginate=False):
result = monascaclient(request).notifications.list(offset=offset, limit=limit)
@profiler.trace
def notification_list(request, offset=0, limit=10000, marker=None,
paginate=False):
result = client.monascaclient(request).notifications.list(offset=offset,
limit=limit)
return result['elements'] if type(result) is dict else result
@profiler.trace
def notification_delete(request, notification_id):
return monascaclient(request).notifications.delete(
return client.monascaclient(request).notifications.delete(
notification_id=notification_id)
@profiler.trace
def notification_get(request, notification_id):
return monascaclient(request).notifications. \
return client.monascaclient(request).notifications. \
get(notification_id=notification_id)
@profiler.trace
def notification_create(request, **kwargs):
return monascaclient(request).notifications.create(**kwargs)
return client.monascaclient(request).notifications.create(**kwargs)
@profiler.trace
def notification_update(request, notification_id, **kwargs):
return monascaclient(request).notifications. \
return client.monascaclient(request).notifications. \
update(notification_id=notification_id, **kwargs)
@profiler.trace
def notification_type_list(request, **kwargs):
result = monascaclient(request).notificationtypes.list(**kwargs)
result = client.monascaclient(request).notificationtypes.list(**kwargs)
return result['elements'] if type(result) is dict else result
@profiler.trace
def metrics_list(request, **kwargs):
result = monascaclient(request).metrics.list(**kwargs)
result = client.monascaclient(request).metrics.list(**kwargs)
return result['elements'] if type(result) is dict else result
@profiler.trace
def metrics_measurement_list(request, **kwargs):
result = monascaclient(request).metrics.list_measurements(**kwargs)
result = client.monascaclient(request).metrics.list_measurements(**kwargs)
return result['elements'] if type(result) is dict else result
@profiler.trace
def metrics_stat_list(request, **kwargs):
result = monascaclient(request).metrics.list_statistics(**kwargs)
result = client.monascaclient(request).metrics.list_statistics(**kwargs)
return result['elements'] if type(result) is dict else result

View File

@ -41,9 +41,16 @@ MONITORING_SERVICES = getattr(
# {'name': _('Instances'), 'groupBy': 'hostname'}]},
# ]
MONITORING_SERVICE_VERSION = getattr(
settings, 'MONITORING_SERVICE_VERSION', '2_0'
)
MONITORING_SERVICE_TYPE = getattr(
settings, 'MONITORING_SERVICE_TYPE', 'monitoring'
)
MONITORING_ENDPOINT_TYPE = getattr(
# NOTE(trebskit) # will default to OPENSTACK_ENDPOINT_TYPE
settings, 'MONITORING_ENDPOINT_TYPE', None
)
# Grafana button titles/file names (global across all projects):
GRAFANA_LINKS = []

View File

@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from monascaclient import exc
DASHBOARD = "monitoring"
# A list of applications to be added to INSTALLED_APPS.
@ -29,10 +31,15 @@ ADD_JS_FILES = ['monitoring/js/app.js',
ADD_SCSS_FILES = [
'monitoring/css/alarm-create.scss']
from monascaclient import exc
# A dictionary of exception classes to be added to HORIZON['exceptions'].
_RECOVERABLE_ERRORS = (exc.UnprocessableEntity, exc.Conflict,
exc.BadRequest, exc.ConnectionError,
exc.Forbidden, exc.InternalServerError)
_NOT_FOUND_ERRORS = (exc.NotFound,)
_UNAUTHORIZED_ERRORS = (exc.Unauthorized,)
ADD_EXCEPTIONS = {
'recoverable': (exc.HTTPUnProcessable, exc.HTTPConflict, exc.HTTPException),
'not_found': (exc.HTTPNotFound,),
'unauthorized': (exc.HTTPUnauthorized,),
'recoverable': _RECOVERABLE_ERRORS,
'not_found': _NOT_FOUND_ERRORS,
'unauthorized': _UNAUTHORIZED_ERRORS,
}

View File

@ -12,17 +12,14 @@
# under the License.
# NOTE(dmllr): Remove me when we require monascaclient >= 1.3.0
try:
from monascaclient.apiclient import exceptions as monascacli
except ImportError:
from monascaclient.openstack.common.apiclient import exceptions as monascacli
from monascaclient import exc
from openstack_dashboard.test.test_data import exceptions
def data(TEST):
TEST.exceptions = exceptions.data
monitoring_exception = monascacli.ClientException
monitoring_exception = exc.ClientException
TEST.exceptions.monitoring = exceptions.create_stubbed_exception(
monitoring_exception)

View File

@ -9,6 +9,27 @@ BRANCH_NAME=master
PACKAGE_NAME=monasca-ui
requirements_installed=$(echo "import openstack_requirements" | python 2>/dev/null ; echo $?)
function install_client_depends_on() {
local client_location
if [ -x "$ZUUL_CLONER" ]; then
# install in gate environment
pushd $mydir
$ZUUL_CLONER --cache-dir \
/opt/git \
git://git.openstack.org \
openstack/python-monascaclient
cd openstack/python-monascaclient
echo "Using python-monascaclient $(git log -n 1 --oneline)"
client_location="file://$PWD#egg=python_monascaclient"
popd
else
echo "Using python-monascaclient@master"
client_location="git+https://git.openstack.org/openstack/python-monascaclient@master#egg=python_monascaclient"
fi
edit-constraints $localfile -- "python-monascaclient" "$client_location"
$install_cmd -U "$client_location"
}
set -e
git config --global url.https://git.openstack.org/.insteadOf git://git.openstack.org/
@ -37,8 +58,7 @@ elif [ -x "$ZUUL_CLONER" ]; then
--branch $BRANCH_NAME \
git://git.openstack.org \
openstack/requirements
cd openstack/requirements
$install_cmd -e .
cd openstack/requirements ; $install_cmd -e . ; cd -
popd
else
echo "PIP HARDCODE" > /tmp/tox_install.txt
@ -52,6 +72,7 @@ fi
# the current repo. It is listed in constraints file and thus any
# install will be constrained and we need to unconstrain it.
edit-constraints $localfile -- $PACKAGE_NAME "-e file://$PWD#egg=$PACKAGE_NAME"
install_client_depends_on
$install_cmd -U $*
exit $?