pax_global_header 0000666 0000000 0000000 00000000064 14617051670 0014521 g ustar 00root root 0000000 0000000 52 comment=e7bf46d8a47041be6bc7daa50ba42efab7936db8
cloudkitty-dashboard-19.0.0/ 0000775 0000000 0000000 00000000000 14617051670 0015730 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/.coveragerc 0000664 0000000 0000000 00000000120 14617051670 0020042 0 ustar 00root root 0000000 0000000 [run]
branch = True
source = cloudkittydashboard
[report]
ignore_errors = True
cloudkitty-dashboard-19.0.0/.gitignore 0000664 0000000 0000000 00000000221 14617051670 0017713 0 ustar 00root root 0000000 0000000 *.pyc
*.egg-info
*.log
build
.coverage
.coverage.*
.tox
cover
htmlcov
.secret_key_store
.stestr/
dist
*.egg
*.sw?
.ropeproject
AUTHORS
ChangeLog
cloudkitty-dashboard-19.0.0/.gitreview 0000664 0000000 0000000 00000000127 14617051670 0017736 0 ustar 00root root 0000000 0000000 [gerrit]
host=review.opendev.org
port=29418
project=openstack/cloudkitty-dashboard.git
cloudkitty-dashboard-19.0.0/.stestr.conf 0000664 0000000 0000000 00000000073 14617051670 0020201 0 ustar 00root root 0000000 0000000 [DEFAULT]
test_path=./cloudkittydashboard/tests
top_dir=./
cloudkitty-dashboard-19.0.0/.zuul.yaml 0000664 0000000 0000000 00000000302 14617051670 0017664 0 ustar 00root root 0000000 0000000 - project:
templates:
- check-requirements
- horizon-non-primary-django-jobs
- openstack-python3-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3
cloudkitty-dashboard-19.0.0/CONTRIBUTING.rst 0000664 0000000 0000000 00000001222 14617051670 0020366 0 ustar 00root root 0000000 0000000 The source repository for this project can be found at:
https://opendev.org/openstack/cloudkitty-dashboard
Pull requests submitted through GitHub are not monitored.
To start contributing to OpenStack, follow the steps in the contribution guide
to set up and use Gerrit:
https://docs.openstack.org/contributors/code-and-documentation/quick-start.html
Bugs should be filed on Storyboard:
https://storyboard.openstack.org/#!/project/891
For more specific information about contributing to this repository, see the
cloudkitty-dashboard contributor guide:
https://docs.openstack.org/cloudkitty-dashboard/latest/contributor/contributing.html
cloudkitty-dashboard-19.0.0/HACKING.rst 0000664 0000000 0000000 00000000242 14617051670 0017524 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard Style Commandments
=======================================
Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/
cloudkitty-dashboard-19.0.0/LICENSE 0000664 0000000 0000000 00000023637 14617051670 0016750 0 ustar 00root root 0000000 0000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
cloudkitty-dashboard-19.0.0/README.rst 0000664 0000000 0000000 00000002471 14617051670 0017423 0 ustar 00root root 0000000 0000000 ====================
CloudKitty dashboard
====================
.. image:: https://governance.openstack.org/badges/cloudkitty-dashboard.svg
:target: https://governance.openstack.org/tc/reference/tags/index.html
.. Change things from this point on
:Wiki: `CloudKitty Wiki`_
:IRC: #cloudkitty @ oftc.net
Horizon dashboard for CloudKitty.
.. _CloudKitty Wiki: https://wiki.openstack.org/wiki/CloudKitty
About
-----
cloudkitty-dashboard is an extension for OpenStack Dashboard providing UI for
CloudKitty.
With CloudKitty dashboard, operators can easily define a rating policy for
their cloud without the use of a CLI.
Users can get information about their usage, and predict costs of an instance.
Contributing
------------
We are welcoming new contributors, if you've got new ideas, suggestions or want
to contribute, contact us.
You can reach us through IRC (#cloudkitty @oftc.net), or on the official
OpenStack mailing list openstack-discuss@lists.openstack.org.
A storyboard_ project is available if you need to report bugs.
Trying it
---------
CloudKitty can be deployed with devstack, more information can be found in the
`devstack section`_ of the documentation.
.. _storyboard: https://storyboard.openstack.org/#!/project/891
.. _devstack section: https://cloudkitty.readthedocs.org/en/latest/devstack.html
cloudkitty-dashboard-19.0.0/babel-django.cfg 0000664 0000000 0000000 00000000115 14617051670 0020713 0 ustar 00root root 0000000 0000000 [python: **.py]
[django: **/templates/**.html]
[django: **/templates/**.csv]
cloudkitty-dashboard-19.0.0/babel-djangojs.cfg 0000664 0000000 0000000 00000000061 14617051670 0021250 0 ustar 00root root 0000000 0000000 [javascript: **.js]
[angular: **/static/**.html]
cloudkitty-dashboard-19.0.0/cloudkittydashboard/ 0000775 0000000 0000000 00000000000 14617051670 0021773 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/__init__.py 0000664 0000000 0000000 00000001250 14617051670 0024102 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.
import pbr.version
__version__ = pbr.version.VersionInfo('cloudkitty-dashboard').version_string()
cloudkitty-dashboard-19.0.0/cloudkittydashboard/api/ 0000775 0000000 0000000 00000000000 14617051670 0022544 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/api/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0024643 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/api/cloudkitty.py 0000664 0000000 0000000 00000004260 14617051670 0025313 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2015 Objectif Libre
#
# 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 collections import abc
from django.conf import settings
from horizon.utils.memoized import memoized # noqa
from keystoneauth1.identity.v3 import Token
from cloudkittyclient import client as ck_client
from cloudkittydashboard import utils
@memoized
def cloudkittyclient(request, version='1'):
"""Initialization of Cloudkitty client."""
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
auth_url = getattr(settings, 'OPENSTACK_KEYSTONE_URL', None)
interface = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL')
auth = Token(
auth_url,
token=request.user.token.id,
project_id=request.user.project_id,
domain_id=request.user.domain_id,
)
adapter_options = {
'region_name': request.user.services_region,
'interface': interface,
}
return ck_client.Client(
version,
auth=auth,
cacert=cacert,
insecure=insecure,
adapter_options=adapter_options,
)
def identify(what, name=False, key=None):
if isinstance(what, abc.Iterable):
for i in what:
i['id'] = i.get(key or "%s_id" % i['key'])
if name and not i.get('name'):
i['name'] = i.get(key or "%s_id" % i['key'])
what = [utils.TemplatizableDict(i) for i in what]
else:
what['id'] = what.get(key or "%s_id" % what['key'])
if name and not i.get('name'):
what['name'] = what.get(key or "%s_id" % what['key'])
what = utils.TemplatizableDict(what)
return what
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/ 0000775 0000000 0000000 00000000000 14617051670 0024105 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0026204 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/ 0000775 0000000 0000000 00000000000 14617051670 0025175 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0027274 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/ 0000775 0000000 0000000 00000000000 14617051670 0026616 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0030715 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/forms.py 0000664 0000000 0000000 00000027453 14617051670 0030331 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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 decimal import Decimal
import logging
from django.utils.translation import gettext_lazy as _
from horizon import exceptions as horizon_exceptions
from horizon import forms
from horizon import messages
from keystoneauth1 import exceptions
from cloudkittydashboard.api import cloudkitty as api
from openstack_dashboard import api as api_keystone
LOG = logging.getLogger(__name__)
class CreateServiceForm(forms.SelfHandlingForm):
services_choices = [("service", _("Service")),
("custom_service", _("Custom service"))]
service_type = forms.ChoiceField(
label=_("Service type"),
choices=services_choices,
widget=forms.Select(attrs={
'class': 'switchable',
'data-slug': 'servicetype'}))
service = forms.DynamicChoiceField(
label=_("Service"),
help_text=_("Services are provided by main collector."),
widget=forms.Select(attrs={
'class': 'switched',
'data-switch-on': 'servicetype',
'data-required-when-shown': 'true',
'data-servicetype-service': _('Service')}),
required=False)
custom_service = forms.CharField(
label=_("Custom service"),
help_text=_("Custom services can be defined for any "
"additional collector."),
widget=forms.widgets.TextInput(attrs={
'class': 'switched',
'data-switch-on': 'servicetype',
'data-required-when-shown': 'true',
'data-servicetype-custom_service': _('Custom service')}),
required=False)
def clean_custom_service(self):
custom_service = self.cleaned_data.get('custom_service').strip()
service_type = self.cleaned_data.get('service_type')
if service_type == "custom_service" and not custom_service:
msg = _('Custom service cannot be empty.')
self._errors['custom_service'] = self.error_class([msg])
return custom_service
def handle(self, request, data):
if data['service_type'] == 'service':
service = data['service']
else:
service = data['custom_service']
services_mgr = api.cloudkittyclient(request).rating.hashmap
LOG.info('Creating service with name %s' % (service))
try:
service = services_mgr.create_service(name=service)
messages.success(
request,
_('Service was successfully created'))
return service
except Exception:
horizon_exceptions.handle(request,
_("Unable to create new service."))
def __init__(self, request, *args, **kwargs):
super(CreateServiceForm, self).__init__(request, *args, **kwargs)
metrics = api.cloudkittyclient(request).info.get_metric()['metrics']
metrics = api.identify(metrics, key='metric_id', name=True)
choices = sorted([(s.metric_id, s.metric_id) for s in metrics])
self.fields['service'].choices = choices
class CreateFieldForm(forms.SelfHandlingForm):
service_id = forms.CharField(label=_("Service ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
service_name = forms.CharField(label=_("Service Name"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
def handle(self, request, data):
service_id = data['service_id']
field = data['field']
LOG.info('Creating field with name %s' % (field))
fields_mgr = api.cloudkittyclient(request).rating.hashmap
try:
field = fields_mgr.create_field(name=field, service_id=service_id)
messages.success(
request,
_('Field was successfully created'))
return field
except Exception:
horizon_exceptions.handle(
request,
_("Unable to create field."))
def __init__(self, request, *args, **kwargs):
super(CreateFieldForm, self).__init__(request, *args, **kwargs)
service_id = kwargs['initial']['service_id']
manager = api.cloudkittyclient(request).rating.hashmap
service = manager.get_service(service_id=service_id)
self.fields['service_name'].initial = service['name']
try:
fields = manager.get_field(service_id=service['name'])['fields']
except exceptions.NotFound:
fields = None
if fields:
fields = api.identify(fields)
choices = sorted([(field, field) for field in fields['metadata']])
self.fields['field'] = forms.DynamicChoiceField(
label=_("Field"))
self.fields['field'].choices = choices
else:
self.fields['field'] = forms.CharField(
label=_("Field"))
class CreateGroupForm(forms.SelfHandlingForm):
name = forms.CharField(label=_("Name"))
def handle(self, request, data):
name = data['name']
LOG.info('Creating group with name %s' % (name))
try:
group = api.cloudkittyclient(request).rating.hashmap.create_group(
name=name)
messages.success(
request,
_('Group was successfully created'))
return group
except Exception:
horizon_exceptions.handle(
request,
_("Unable to create group."))
class BaseForm(forms.SelfHandlingForm):
type = forms.ChoiceField(label=_("Type"),
choices=(("flat", _("Flat")),
("rate", _("Rate"))))
cost = forms.DecimalField(label=_("Cost"))
url = "horizon:admin:hashmap:group_create"
group_id = forms.DynamicChoiceField(label=_("Group"),
required=False,
add_item_link=url)
tenant_id = forms.ChoiceField(label=_("Project"),
required=False)
fields_order = ['type', 'cost', 'group_id']
def __init__(self, request, *args, **kwargs):
super(BaseForm, self).__init__(request, *args, **kwargs)
# self.order_fields(self.fields_order)
groups = api.cloudkittyclient(
request).rating.hashmap.get_group()['groups']
groups = api.identify(groups, key='group_id', name=True)
choices = [(group['id'], group['name']) for group in groups]
choices.insert(0, ('', ' '))
self.fields['group_id'].choices = choices
tenants, __ = api_keystone.keystone.tenant_list(request)
choices_tenants = [(tenant.id, tenant.name) for tenant in tenants]
choices_tenants.insert(0, (None, ' '))
self.fields['tenant_id'].choices = choices_tenants
class BaseThresholdForm(BaseForm):
level = forms.DecimalField(label=_("Level"))
fields_order = ['level', 'type', 'cost', 'group_id', 'tenant_id']
def handle(self, request, data):
thresholds_mgr = api.cloudkittyclient(request).rating.hashmap
threshold = {}
for k, v in data.items():
if v:
threshold[k] = float(v) if isinstance(v, Decimal) else v
return thresholds_mgr.create_threshold(**threshold)
class CreateServiceThresholdForm(BaseThresholdForm):
service_id = forms.CharField(label=_("Service ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
fields_order = ['service_id', 'level', 'type', 'cost', 'group_id',
'tenant_id']
class CreateFieldThresholdForm(BaseThresholdForm):
field_id = forms.CharField(label=_("Field"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
fields_order = ['field_id', 'level', 'type', 'cost', 'group_id',
'tenant_id']
class BaseMappingForm(BaseForm):
def handle(self, request, data):
mapping_mgr = api.cloudkittyclient(request).rating.hashmap
mapping = {}
for k, v in data.items():
if v:
mapping[k] = float(v) if isinstance(v, Decimal) else v
return mapping_mgr.create_mapping(**mapping)
class CreateFieldMappingForm(BaseMappingForm):
value = forms.CharField(label=_("Value"))
field_id = forms.CharField(label=_("Field ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}),
required=False)
fields_order = ['field_id', 'value', 'type', 'cost', 'group_id',
'tenant_id']
class CreateServiceMappingForm(BaseMappingForm):
service_id = forms.CharField(label=_("Service ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}),
required=False)
fields_order = ['service_id', 'type', 'cost', 'group_id',
'tenant_id']
class BaseEditMappingForm(BaseMappingForm):
mapping_id = forms.CharField(label=_("Mapping ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
tenant_id = forms.ChoiceField(label=_("Project"),
required=False)
def handle(self, request, data):
mapping_mgr = api.cloudkittyclient(request).rating.hashmap
mapping = {}
for k, v in data.items():
if v:
mapping[k] = float(v) if isinstance(v, Decimal) else v
mapping['mapping_id'] = self.initial['mapping_id']
return mapping_mgr.update_mapping(**mapping)
class EditServiceMappingForm(BaseEditMappingForm, CreateServiceMappingForm):
fields_order = ['service_id', 'mapping_id', 'type', 'cost', 'group_id',
'tenant_id']
class EditFieldMappingForm(BaseEditMappingForm, CreateFieldMappingForm):
fields_order = [
'field_id',
'mapping_id',
'value',
'type',
'cost',
'group_id',
'tenant_id']
class BaseEditThresholdForm(BaseThresholdForm):
threshold_id = forms.CharField(label=_("Threshold ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
def handle(self, request, data):
threshold_mgr = api.cloudkittyclient(request).rating.hashmap
threshold = {}
for k, v in data.items():
if v:
threshold[k] = float(v) if isinstance(v, Decimal) else v
threshold['threshold_id'] = self.initial['threshold_id']
return threshold_mgr.update_threshold(**threshold)
class EditServiceThresholdForm(BaseEditThresholdForm,
CreateServiceThresholdForm):
fields_order = [
'service_id',
'threshold_id',
'level',
'type',
'cost',
'group_id',
'tenant_id']
class EditFieldThresholdForm(BaseEditThresholdForm,
CreateFieldThresholdForm):
fields_order = [
'field_id',
'threshold_id',
'level',
'type',
'cost',
'group_id',
'tenant_id']
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/panel.py 0000664 0000000 0000000 00000001363 14617051670 0030272 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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 gettext_lazy as _
import horizon
class Hashmap(horizon.Panel):
name = _("Hashmap")
slug = "hashmap"
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/tables.py 0000664 0000000 0000000 00000041275 14617051670 0030453 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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 collections import OrderedDict
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from horizon import tables
from horizon import tabs
from cloudkittydashboard.api import cloudkitty as api
class CreateService(tables.LinkAction):
name = "createservice"
verbose_name = _("Create new Service")
url = 'horizon:admin:hashmap:service_create'
icon = "plus"
ajax = True
classes = ("ajax-modal",)
class DeleteService(tables.DeleteAction):
name = "deleteservice"
verbose_name = _("Delete Service")
data_type_singular = _("Service")
data_type_plural = _("Services")
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete Service",
u"Delete Services",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted Service",
u"Deleted Services",
count
)
def action(self, request, service_id):
api.cloudkittyclient(request).rating.hashmap.delete_service(
service_id=service_id)
class ServicesTable(tables.DataTable):
"""This table list the available services.
Clicking on a service name sends you to a ServiceTabs page.
"""
name = tables.Column('name', verbose_name=_("Name"),
link='horizon:admin:hashmap:service')
unit = tables.Column("unit", verbose_name=_("Unit"))
def get_object_id(self, datum):
return datum['id']
def get_object_display(self, datum):
return datum['name']
class Meta(object):
name = "services"
verbose_name = _("Services")
table_actions = (CreateService, DeleteService)
row_actions = (DeleteService,)
class CreateGroup(tables.LinkAction):
name = "creategroup"
verbose_name = _("Create new Group")
icon = "plus"
ajax = True
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:group_create'
service_id = self.table.request.service_id
return reverse(url, args=[service_id])
class DeleteGroup(tables.DeleteAction):
name = "deletegroup"
verbose_name = _("Delete Group")
data_type_singular = _("Group")
data_type_plural = _("Groups")
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete Group",
u"Delete Groups",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted Group",
u"Deleted Groups",
count
)
def action(self, request, group_id):
api.cloudkittyclient(request).rating.hashmap.delete_group(
group_id=group_id)
def get_detail_link(datum):
if datum.group_id:
url = "horizon:admin:hashmap:group_details"
return reverse(url, kwargs={'group_id': datum.group_id})
class GroupsTable(tables.DataTable):
"""This table list the available groups.
Clicking on a group name sends you to a GroupsTab page.
"""
name = tables.Column('name', verbose_name=_("Name"), link=get_detail_link)
group_id = tables.Column('group_id', verbose_name=_("Group"))
class Meta(object):
name = "groups"
verbose_name = _("Groups")
table_actions = (CreateGroup, DeleteGroup)
row_actions = (DeleteGroup,)
class GroupsTab(tabs.TableTab):
name = _("Groups")
slug = "hashmap_groups"
table_classes = (GroupsTable,)
template_name = "horizon/common/_detail_table.html"
preload = True
def get_groups_data(self):
client = api.cloudkittyclient(self.request)
groups = client.rating.hashmap.get_group().get('groups', [])
return api.identify(groups, key='group_id')
class CreateServiceThreshold(tables.LinkAction):
name = "createservicethreshold"
verbose_name = _("Create new Service Threshold")
icon = "plus"
ajax = False
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:service_threshold_create'
service_id = self.table.request.service_id
return reverse(url, args=[service_id])
class CreateFieldThreshold(tables.LinkAction):
name = "createfieldthreshold"
verbose_name = _("Create new Field Threshold")
icon = "plus"
ajax = False
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:field_threshold_create'
field_id = self.table.request.field_id
return reverse(url, args=[field_id])
class DeleteServiceThreshold(tables.DeleteAction):
name = "deletetservicethreshold"
verbose_name = _("Delete Service Threshold")
data_type_singular = _("Service Threshold")
data_type_plural = _("Service Thresholds")
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete Service Threshold",
u"Delete Service Thresholds",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted Service Threshold",
u"Deleted Service Thresholds",
count
)
def action(self, request, threshold_id):
api.cloudkittyclient(request).rating.hashmap.delete_threshold(
threshold_id=threshold_id)
class DeleteFieldThreshold(tables.DeleteAction):
name = "deletefieldthreshold"
verbose_name = _("Delete Field Threshold")
data_type_singular = _("Field Threshold")
data_type_plural = _("Field Thresholds")
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete Field Threshold",
u"Delete Field Thresholds",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted Field Threshold",
u"Deleted Field Thresholds",
count
)
def action(self, request, threshold_id):
api.cloudkittyclient(request).rating.hashmap.delete_threshold(
threshold_id=threshold_id)
class EditServiceThreshold(tables.LinkAction):
name = "editservicethreshold"
verbose_name = _("Edit Service Threshold")
icon = "edit"
ajax = True
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:service_threshold_edit'
return reverse(url, args=[datum.threshold_id])
def get_groupname(datum):
if hasattr(datum, "group_name"):
groupname = datum.group_name
return groupname
return _("Not available")
def add_groupname(request, datums):
client = api.cloudkittyclient(request)
groups = client.rating.hashmap.get_group().get('groups', [])
full_groups = OrderedDict([(str(group['group_id']), group['name'])
for group in groups])
for datum in datums:
if datum.get('group_id'):
if datum['group_id'] in full_groups:
datum['group_name'] = full_groups[datum['group_id']]
else:
group = client.rating.hashmap.get_group(
group_id=datum['group_id'])
datum['group_name'] = group['name']
class BaseThresholdsTable(tables.DataTable):
level = tables.Column('level', verbose_name=_("Level"))
type = tables.Column('type', verbose_name=_("Type"))
cost = tables.Column('cost', verbose_name=_("Cost"))
group_name = tables.Column(get_groupname,
verbose_name=_("Group Name"),
link=get_detail_link)
tenant_id = tables.Column('tenant_id', verbose_name=_("Project"))
class ServiceThresholdsTable(BaseThresholdsTable):
"""This table list the available service thresholds.
Clicking on a group name sends you to a GroupsTab page.
"""
class Meta(object):
name = "service_thresholds"
verbose_name = _("Service Threshold")
table_actions = (CreateServiceThreshold, DeleteServiceThreshold)
row_actions = (EditServiceThreshold, DeleteServiceThreshold)
class ServiceThresholdsTab(tabs.TableTab):
name = _("Service Thresholds")
slug = "hashmap_service_thresholds"
table_classes = (ServiceThresholdsTable,)
template_name = "horizon/common/_detail_table.html"
preload = True
def get_service_thresholds_data(self):
client = api.cloudkittyclient(self.request)
thresholds = client.rating.hashmap.get_threshold(
service_id=self.request.service_id).get('thresholds', [])
add_groupname(self.request, thresholds)
return api.identify(thresholds, key='threshold_id', name=True)
class EditFieldThreshold(tables.LinkAction):
name = "editfieldthreshold"
verbose_name = _("Edit Field Threshold")
icon = "edit"
ajax = True
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:field_threshold_edit'
return reverse(url, args=[datum.threshold_id])
class FieldThresholdsTable(BaseThresholdsTable):
"""This table list the available field thresholds.
Clicking on a group name sends you to a GroupsTab page.
"""
class Meta(object):
name = "field_thresholds"
verbose_name = _("Field Threshold")
table_actions = (CreateFieldThreshold, DeleteFieldThreshold)
row_actions = (EditFieldThreshold, DeleteFieldThreshold)
class FieldThresholdsTab(tabs.TableTab):
name = _("Field Thresholds")
slug = "hashmap_field_thresholds"
table_classes = (FieldThresholdsTable,)
template_name = "horizon/common/_detail_table.html"
preload = True
def get_field_thresholds_data(self):
client = api.cloudkittyclient(self.request)
thresholds = client.rating.hashmap.get_threshold(
field_id=self.request.field_id).get('thresholds', [])
add_groupname(self.request, thresholds)
return api.identify(thresholds, key='threshold_id', name=True)
class DeleteField(tables.DeleteAction):
name = "deletefield"
verbose_name = _("Delete Field")
data_type_singular = _("Field")
data_type_plural = _("Fields")
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete Field",
u"Delete Fields",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted Field",
u"Deleted Fields",
count
)
def action(self, request, field_id):
api.cloudkittyclient(request).rating.hashmap.delete_field(
field_id=field_id)
class CreateField(tables.LinkAction):
name = "createfield"
verbose_name = _("Create new Field")
icon = "plus"
ajax = True
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:field_create'
service_id = self.table.request.service_id
return reverse(url, args=[service_id])
class FieldsTable(tables.DataTable):
"""This table lists the available fields for a given service.
Clicking on a fields sends you to a MappingsTable.
"""
name = tables.Column(
'name',
verbose_name=_("Name"),
link='horizon:admin:hashmap:field')
class Meta(object):
name = "fields"
verbose_name = _("Fields")
multi_select = False
row_actions = (DeleteField,)
table_actions = (CreateField, DeleteField)
class FieldsTab(tabs.TableTab):
name = _("Fields")
slug = "hashmap_fields"
table_classes = (FieldsTable,)
template_name = "horizon/common/_detail_table.html"
preload = True
def get_fields_data(self):
client = api.cloudkittyclient(self.request)
fields = client.rating.hashmap.get_field(
service_id=self.request.service_id)['fields']
return api.identify(fields, key='field_id')
class DeleteMapping(tables.DeleteAction):
name = "deletemapping"
verbose_name = _("Delete Mapping")
data_type_singular = _("Mapping")
data_type_plural = _("Mappings")
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete Mapping",
u"Delete Mappings",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted Mapping",
u"Deleted Mappings",
count
)
def action(self, request, mapping_id):
api.cloudkittyclient(request).rating.hashmap.delete_mapping(
mapping_id=mapping_id)
class CreateServiceMapping(tables.LinkAction):
name = "createiservicemapping"
verbose_name = _("Create new Mapping")
icon = "plus"
ajax = True
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:service_mapping_create'
service_id = self.table.request.service_id
return reverse(url, args=[service_id])
class EditServiceMapping(tables.LinkAction):
name = "editservicemapping"
verbose_name = _("Edit Mapping")
icon = "edit"
ajax = True
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:service_mapping_edit'
return reverse(url, args=[datum.mapping_id])
class BaseMappingsTable(tables.DataTable):
type = tables.Column('type', verbose_name=_("Type"))
cost = tables.Column('cost', verbose_name=_("Cost"))
group_name = tables.Column(get_groupname,
verbose_name=_("Group Name"),
link=get_detail_link)
tenant_id = tables.Column('tenant_id', verbose_name=_("Project"))
class ServiceMappingsTable(BaseMappingsTable):
class Meta(object):
name = "mappings"
verbose_name = _("Mappings")
row_actions = (EditServiceMapping, DeleteMapping)
table_actions = (CreateServiceMapping, DeleteMapping)
class CreateFieldMapping(tables.LinkAction):
name = "createfieldmapping"
verbose_name = _("Create new Mapping")
icon = "plus"
ajax = True
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:field_mapping_create'
field_id = self.table.request.field_id
return reverse(url, args=[field_id])
class EditFieldMapping(tables.LinkAction):
name = "editfieldmapping"
verbose_name = _("Edit Mapping")
icon = "edit"
ajax = True
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
url = 'horizon:admin:hashmap:field_mapping_edit'
return reverse(url, args=[datum.mapping_id])
class FieldMappingsTable(BaseMappingsTable):
value = tables.Column('value', verbose_name=_("Value"))
class Meta(object):
name = "mappings"
verbose_name = _("Mappings")
row_actions = (EditFieldMapping, DeleteMapping)
table_actions = (CreateFieldMapping, DeleteMapping)
class FieldMappingsTab(tabs.TableTab):
name = _("Field Mappings")
slug = "hashmap_field_mappings"
table_classes = (FieldMappingsTable,)
template_name = "horizon/common/_detail_table.html"
preload = True
def get_mappings_data(self):
client = api.cloudkittyclient(self.request)
mappings = client.rating.hashmap.get_mapping(
field_id=self.request.field_id).get('mappings', [])
add_groupname(self.request, mappings)
return api.identify(mappings, key='mapping_id', name=True)
class MappingsTab(tabs.TableTab):
name = _("Service Mappings")
slug = "hashmap_mappings"
table_classes = (ServiceMappingsTable,)
template_name = "horizon/common/_detail_table.html"
preload = True
def get_mappings_data(self):
client = api.cloudkittyclient(self.request)
mappings = client.rating.hashmap.get_mapping(
service_id=self.request.service_id).get('mappings', [])
add_groupname(self.request, mappings)
return api.identify(mappings, key='mapping_id', name=True)
class FieldTabs(tabs.TabGroup):
slug = "field_tabs"
tabs = (FieldMappingsTab, FieldThresholdsTab)
sticky = True
class ServiceTabs(tabs.TabGroup):
slug = "services_tabs"
tabs = (FieldsTab, MappingsTab, ServiceThresholdsTab, GroupsTab)
sticky = True
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/ 0000775 0000000 0000000 00000000000 14617051670 0030614 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap/ 0000775 0000000 0000000 00000000000 14617051670 0032235 5 ustar 00root root 0000000 0000000 _field_create.html 0000664 0000000 0000000 00000000347 14617051670 0035615 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap {% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
{% trans "Description:" %}
{% trans "A field is referring to a metadata field of a resource. " %}
{% trans "A threshold entry is used to apply rating rules base on level. Its behaviour is similar to a mapping except that it applies the cost base on the level." %}
{% endblock %}
index.html 0000664 0000000 0000000 00000000444 14617051670 0035577 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/templates/rating_modules {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Modules" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Modules") %}
{% endblock page_header %}
{% block main %}
{{ table.render }}
{{ modules }}
{% endblock %}
priority_edit.html 0000664 0000000 0000000 00000000210 14617051670 0037345 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/templates/rating_modules {% extends 'base.html' %}
{% load i18n %}
{% block main %}
{% include 'admin/rating_modules/_priority_edit.html' %}
{% endblock %}
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/tests.py 0000664 0000000 0000000 00000000253 14617051670 0030361 0 ustar 00root root 0000000 0000000 from horizon.test import helpers as test
class CkprojectTests(test.TestCase):
# Unit tests for ckproject.
def test_me(self):
self.assertTrue(1 + 1 == 2)
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/urls.py 0000664 0000000 0000000 00000002015 14617051670 0030202 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.urls import re_path
from cloudkittydashboard.dashboards.admin.modules import views
urlpatterns = [
re_path(r'^$', views.IndexView.as_view(), name='index'),
re_path(r'^(?P[^/]+)/?$', views.ModuleDetailsView.as_view(),
name="module_details"),
re_path(r'^edit_priority/(?P[^/]+)/?$',
views.PriorityModuleEditView.as_view(),
name="edit_priority"),
]
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/views.py 0000664 0000000 0000000 00000006106 14617051670 0030357 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from horizon import forms
from horizon import tables
from horizon import views
from cloudkittydashboard.api import cloudkitty as api
from cloudkittydashboard.dashboards.admin.modules import forms as module_forms
from cloudkittydashboard.dashboards.admin.modules import tables as admin_tables
class IndexView(tables.DataTableView):
# A very simple class-based view...
template_name = 'admin/rating_modules/index.html'
table_class = admin_tables.ModulesTable
def get_data(self):
modules = api.cloudkittyclient(
self.request).rating.get_module()['modules']
modules = api.identify(modules, key='module_id', name=True)
return modules
class ModuleDetailsView(views.APIView):
template_name = 'admin/rating_modules/details.html'
page_title = _("Rating Module Details")
def get_data(self, request, context, *args, **kwargs):
module_id = kwargs.get("module_id")
try:
module = api.cloudkittyclient(self.request).rating.get_module(
module_id=module_id)
context['hotconfig'] = module['hot-config']
context['module'] = module
except Exception:
context['hotconfig'] = False
context['module'] = {}
return context
class PriorityModuleEditView(forms.ModalFormView):
form_class = module_forms.EditPriorityForm
form_id = "edit_priority"
modal_header = _("Edit Module Priority")
page_title = _("Edit module priority")
submit_url = "horizon:admin:rating_modules:edit_priority"
success_url = "horizon:admin:rating_modules:edit_priority"
template_name = "admin/rating_modules/priority_edit.html"
def get_initial(self):
module = api.cloudkittyclient(self.request).rating.get_module(
module_id=self.kwargs['module_id'])
self.initial = module
return self.initial
def get_object_id(self, obj):
return obj.module_id
def get_context_data(self, **kwargs):
context = super(
PriorityModuleEditView, self).get_context_data(**kwargs)
context['module_id'] = self.kwargs.get('module_id')
context['submit_url'] = reverse_lazy(self.submit_url,
args=(context['module_id'], ))
return context
def get_success_url(self, **kwargs):
return reverse('horizon:admin:rating_modules:index')
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/ 0000775 0000000 0000000 00000000000 14617051670 0027235 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0031334 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/forms.py 0000664 0000000 0000000 00000011412 14617051670 0030734 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.
import logging
from django.utils.text import normalize_newlines
from django.utils.translation import gettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import messages
from cloudkittydashboard.api import cloudkitty as api
LOG = logging.getLogger(__name__)
class CreateScriptForm(forms.SelfHandlingForm):
help_text = _('Create a new rating script.')
name = forms.CharField(label=_("Name"))
source_choices = [('raw', _('Direct Input')),
('file', _('File'))]
script_source = forms.ChoiceField(
label=_('Rating Script Source'),
choices=source_choices,
widget=forms.Select(attrs={
'class': 'switchable',
'data-slug': 'scriptsource'}))
script_help = _("A script or set of python commands to modify rating "
"calculations.")
script_upload = forms.FileField(
label=_('Script File'),
help_text=script_help,
widget=forms.FileInput(attrs={
'class': 'switched',
'data-required-when-shown': 'true',
'data-switch-on': 'scriptsource',
'data-scriptsource-file': _('Script File')}),
required=False)
script_data = forms.CharField(
label=_('Script Data'),
help_text=script_help,
widget=forms.widgets.Textarea(attrs={
'class': 'switched',
'data-required-when-shown': 'true',
'data-switch-on': 'scriptsource',
'data-scriptsource-raw': _('Script Data')}),
required=False)
class Meta(object):
name = _('Create Script')
def clean(self):
cleaned = super(CreateScriptForm, self).clean()
files = self.request.FILES
script = self.clean_uploaded_files('script', files)
if script is not None:
cleaned['script_data'] = script
return cleaned
def clean_uploaded_files(self, prefix, files):
upload_str = prefix + "_upload"
has_upload = upload_str in files
if has_upload:
upload_file = files[upload_str]
log_script_name = upload_file.name
LOG.info('got upload %s' % log_script_name)
script = upload_file.read()
if script != "":
try:
normalize_newlines(script)
except Exception as e:
msg = _('There was a problem parsing the'
' %(prefix)s: %(error)s')
msg = msg % {'prefix': prefix, 'error': e}
raise forms.ValidationError(msg)
return script
else:
return None
def handle(self, request, data):
name = data['name']
LOG.info('Creating script with name %s' % (name))
ck_client = api.cloudkittyclient(request)
try:
script = ck_client.rating.pyscripts.create_script(
name=name,
data=data['script_data'])
messages.success(
request,
_('Successfully created script'))
return script
except Exception:
exceptions.handle(request,
_("Unable to create script."))
class EditScriptForm(CreateScriptForm):
script_id = forms.CharField(label=_("Script ID"),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
fields_order = ['script_id', 'name', 'script_source', 'script_upload',
'script_data']
class Meta(object):
name = _("Update Script")
def handle(self, request, data):
script_id = self.initial['script_id']
LOG.info('Updating script with id %s' % (script_id))
ck_client = api.cloudkittyclient(request)
try:
script = ck_client.rating.pyscripts.update_script(
script_id=script_id, name=data['name'],
data=data['script_data'])
messages.success(
request,
_('Successfully updated script'))
return script
except Exception:
exceptions.handle(request,
_("Unable to update script."))
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/panel.py 0000664 0000000 0000000 00000001371 14617051670 0030710 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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 gettext_lazy as _
import horizon
class PyScripts(horizon.Panel):
name = _("PyScripts")
slug = "pyscripts"
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/tables.py 0000664 0000000 0000000 00000005150 14617051670 0031062 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from horizon import tables
from cloudkittydashboard.api import cloudkitty as api
def get_detail_link(datum):
if datum.script_id:
url = "horizon:admin:pyscripts:script_details"
return reverse(url, kwargs={'script_id': datum.script_id})
class CreatePyScript(tables.LinkAction):
name = "createpyscript"
verbose_name = _("Create Script")
url = "horizon:admin:pyscripts:script_create"
icon = "plus"
ajax = True
classes = ("ajax-modal",)
class UpdateScript(tables.LinkAction):
name = "updatepyscript"
verbose_name = _("Edit Script")
classes = ("ajax-modal",)
icon = "pencil"
def get_link_url(self, datum=None):
url = "horizon:admin:pyscripts:script_update"
return reverse(url, kwargs={'script_id': datum.script_id})
class DeletePyScript(tables.DeleteAction):
name = "deletepyscript"
verbose_name = _("Delete Script")
data_type_singular = _("PyScript")
data_type_plural = _("PyScripts")
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete PyScript",
u"Delete PyScripts",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted PyScript",
u"Deleted PyScripts",
count
)
def action(self, request, script_id):
api.cloudkittyclient(request).rating.pyscripts.delete_script(
script_id=script_id)
class PyScriptsTable(tables.DataTable):
id = tables.Column("id", verbose_name=_("id"), link=get_detail_link)
name = tables.Column("name", verbose_name=_("Name"))
checksum = tables.Column("checksum", verbose_name=_("Checksum"))
class Meta(object):
name = "pyscripts"
verbose_name = _("pyscripts")
table_actions = (CreatePyScript, DeletePyScript)
row_actions = (UpdateScript, DeletePyScript)
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/templates/ 0000775 0000000 0000000 00000000000 14617051670 0031233 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/templates/pyscripts/ 0000775 0000000 0000000 00000000000 14617051670 0033273 5 ustar 00root root 0000000 0000000 _form.html 0000664 0000000 0000000 00000000202 14617051670 0035176 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/templates/pyscripts {% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
details.html 0000664 0000000 0000000 00000001026 14617051670 0035526 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/templates/pyscripts {% extends 'base.html' %}
{% load i18n sizeformat %}
{% block title %}{% trans "Script Details" %}{% endblock %}
{% block main %}
{{ _("Id") }}
{{ script.script_id }}
{{ _("Name") }}
{{ script.name }}
{{ _("Data") }}
{{ script.data|linebreaksbr }}
{% endblock %}
form.html 0000664 0000000 0000000 00000000231 14617051670 0035041 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/templates/pyscripts {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% endblock %}
{% block main %}
{% include 'admin/pyscripts/_form.html' %}
{% endblock %}
pyscripts_list.html 0000664 0000000 0000000 00000000452 14617051670 0037176 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/templates/pyscripts {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "PyScripts" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("PyScripts") %}
{% endblock page_header %}
{% block main %}
{{ table.render }}
{{ modules }}
{% endblock %}
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/tests.py 0000664 0000000 0000000 00000001412 14617051670 0030747 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.test import helpers as test
class CkprojectTests(test.TestCase):
# Unit tests for ckproject.
def test_me(self):
self.assertTrue(1 + 1 == 2)
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/urls.py 0000664 0000000 0000000 00000002146 14617051670 0030577 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.urls import re_path
from cloudkittydashboard.dashboards.admin.pyscripts import views
urlpatterns = [
re_path(r'^$', views.IndexView.as_view(), name='index'),
re_path(r'^create/?$', views.ScriptCreateView.as_view(),
name="script_create"),
re_path(r'^update/(?P[^/]+)/?$',
views.ScriptUpdateView.as_view(),
name="script_update"),
re_path(r'^(?P[^/]+)/?$', views.ScriptDetailsView.as_view(),
name="script_details"),
]
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/views.py 0000664 0000000 0000000 00000006564 14617051670 0030757 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from horizon import forms
from horizon import tables
from horizon import views
from cloudkittydashboard.api import cloudkitty as api
from cloudkittydashboard.dashboards.admin.pyscripts import forms \
as pyscripts_forms
from cloudkittydashboard.dashboards.admin.pyscripts import tables \
as pyscripts_tables
class IndexView(tables.DataTableView):
table_class = pyscripts_tables.PyScriptsTable
template_name = 'admin/pyscripts/pyscripts_list.html'
def get_data(self):
data = api.cloudkittyclient(
self.request).rating.pyscripts.list_scripts()['scripts']
data = api.identify(data, key='script_id')
return data
class ScriptCreateView(forms.ModalFormView):
form_class = pyscripts_forms.CreateScriptForm
form_id = "create_script"
modal_header = _("Create Script")
page_title = _("Create Script")
submit_url = reverse_lazy('horizon:admin:pyscripts:script_create')
success_url = reverse_lazy('horizon:admin:pyscripts:index')
template_name = 'admin/pyscripts/form.html'
def get_object_id(self, obj):
return obj
class ScriptUpdateView(forms.ModalFormView):
form_class = pyscripts_forms.EditScriptForm
form_id = "update_script"
modal_header = _("Update Script")
page_title = _("Update Script")
submit_url = 'horizon:admin:pyscripts:script_update'
success_url = 'horizon:admin:pyscripts:script_update'
template_name = 'admin/pyscripts/form.html'
def get_initial(self):
client = api.cloudkittyclient(self.request)
script = client.rating.pyscripts.get_script(
script_id=self.kwargs['script_id'])
self.initial = script
self.initial['script_data'] = self.initial['data']
return self.initial
def get_context_data(self, **kwargs):
context = super(ScriptUpdateView, self).get_context_data(**kwargs)
context['script_id'] = self.kwargs.get('script_id')
context['submit_url'] = reverse_lazy(self.submit_url,
args=(context['script_id'], ))
return context
def get_success_url(self, **kwargs):
return reverse('horizon:admin:pyscripts:index')
class ScriptDetailsView(views.APIView):
template_name = 'admin/pyscripts/details.html'
page_title = _("Script Details : {{ script.name }}")
def get_data(self, request, context, *args, **kwargs):
script_id = kwargs.get("script_id")
try:
client = api.cloudkittyclient(self.request)
script = client.rating.pyscripts.get_script(script_id=script_id)
except Exception:
script = None
context['script'] = script
return context
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/ 0000775 0000000 0000000 00000000000 14617051670 0026672 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0030771 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/panel.py 0000664 0000000 0000000 00000001401 14617051670 0030337 0 ustar 00root root 0000000 0000000 # Copyright 2018 Objectif Libre
#
# 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 gettext_lazy as _
import horizon
class Summary(horizon.Panel):
name = _("Rating Summary")
slug = "rating_summary"
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/tables.py 0000664 0000000 0000000 00000003056 14617051670 0030522 0 ustar 00root root 0000000 0000000 # Copyright 2018 Objectif Libre
#
# 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.urls import reverse
from django.utils.translation import gettext_lazy as _
from horizon import tables
def get_details_link(datum):
if datum.tenant_id:
url = "horizon:admin:rating_summary:project_details"
return reverse(url, kwargs={'project_id': datum.tenant_id})
class SummaryTable(tables.DataTable):
project_id = tables.Column(
'tenant_id', verbose_name=_("Project ID"), link=get_details_link)
project_name = tables.Column(
'name', verbose_name=_("Project Name"), link=get_details_link)
total = tables.Column('rate', verbose_name=_("Project Total"))
class Meta(object):
name = "summary"
verbose_name = _("Summary")
class TenantSummaryTable(tables.DataTable):
res_type = tables.Column('res_type', verbose_name=_("Res Type"))
rate = tables.Column('rate', verbose_name=_("Rate"))
class Meta(object):
name = "tenant_summary"
verbose_name = _("Tenant Summary")
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/templates/ 0000775 0000000 0000000 00000000000 14617051670 0030670 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/templates/rating_summary/ 0000775 0000000 0000000 00000000000 14617051670 0033731 5 ustar 00root root 0000000 0000000 details.html 0000664 0000000 0000000 00000000534 14617051670 0036167 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/templates/rating_summary {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Tenant Details" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Tenant Details") %}
{% endblock page_header %}
{% block main %}
{% trans "Project ID:" %} {{ project_id }}
{{ table.render }}
{{ modules }}
{% endblock %}
index.html 0000664 0000000 0000000 00000000444 14617051670 0035651 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/templates/rating_summary {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Summary" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Summary") %}
{% endblock page_header %}
{% block main %}
{{ table.render }}
{{ modules }}
{% endblock %}
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/urls.py 0000664 0000000 0000000 00000001601 14617051670 0030227 0 ustar 00root root 0000000 0000000 # Copyright 2018 Objectif Libre
#
# 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.urls import re_path
from cloudkittydashboard.dashboards.admin.summary import views
urlpatterns = [
re_path(r'^$', views.IndexView.as_view(), name='index'),
re_path(r'^(?P[^/]+)/?$', views.TenantDetailsView.as_view(),
name="project_details"),
]
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/views.py 0000664 0000000 0000000 00000006222 14617051670 0030403 0 ustar 00root root 0000000 0000000 # Copyright 2018 Objectif Libre
#
# 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.conf import settings
from django.utils.translation import gettext_lazy as _
from horizon import tables
from openstack_dashboard.api import keystone as api_keystone
from cloudkittydashboard.api import cloudkitty as api
from cloudkittydashboard.dashboards.admin.summary import tables as sum_tables
from cloudkittydashboard import utils
rate_prefix = getattr(settings,
'OPENSTACK_CLOUDKITTY_RATE_PREFIX', None)
rate_postfix = getattr(settings,
'OPENSTACK_CLOUDKITTY_RATE_POSTFIX', None)
class IndexView(tables.DataTableView):
template_name = 'admin/rating_summary/index.html'
table_class = sum_tables.SummaryTable
def get_data(self):
summary = api.cloudkittyclient(self.request).report.get_summary(
groupby=['tenant_id'], all_tenants=True)['summary']
tenants, _ = api_keystone.tenant_list(self.request)
tenants = {tenant.id: tenant.name for tenant in tenants}
summary.append({
'tenant_id': 'ALL',
'rate': sum([float(item['rate']) for item in summary]),
})
summary = api.identify(summary, key='tenant_id')
for tenant in summary:
tenant['name'] = tenants.get(tenant.id, '-')
summary[-1]['name'] = 'Cloud Total'
for tenant in summary:
tenant['rate'] = utils.formatRate(tenant['rate'],
rate_prefix, rate_postfix)
return summary
class TenantDetailsView(tables.DataTableView):
template_name = 'admin/rating_summary/details.html'
table_class = sum_tables.TenantSummaryTable
page_title = _("Script Details : {{ table.project_id }}")
def _get_cloud_total_summary(self):
return api.cloudkittyclient(self.request).report.get_summary(
groupby=['res_type'], all_tenants=True)['summary']
def get_data(self):
tenant_id = self.kwargs['project_id']
if tenant_id == 'ALL':
summary = self._get_cloud_total_summary()
else:
summary = api.cloudkittyclient(self.request).report.get_summary(
groupby=['res_type'], tenant_id=tenant_id)['summary']
summary.append({
'tenant_id': tenant_id,
'res_type': 'TOTAL',
'rate': sum([float(item['rate']) for item in summary]),
})
summary = api.identify(summary, key='res_type', name=True)
for item in summary:
item['rate'] = utils.formatRate(item['rate'],
rate_prefix, rate_postfix)
return summary
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/ 0000775 0000000 0000000 00000000000 14617051670 0025553 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0027652 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/ 0000775 0000000 0000000 00000000000 14617051670 0027037 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0031136 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/panel.py 0000664 0000000 0000000 00000001542 14617051670 0030512 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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 gettext_lazy as _
import horizon
from openstack_dashboard.dashboards.project import dashboard
class Project_rating(horizon.Panel):
name = _("Rating")
slug = "rating"
dashboard.Project.register(Project_rating)
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/tables.py 0000664 0000000 0000000 00000002060 14617051670 0030661 0 ustar 00root root 0000000 0000000 # Copyright 2018 Objectif Libre
#
# 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 gettext_lazy as _
from horizon import tables
class SummaryTable(tables.DataTable):
"""This table formats a summary for the given tenant."""
res_type = tables.Column('type', verbose_name=_('Metric Type'))
rate = tables.Column('rate', verbose_name=_('Rate'))
class Meta(object):
name = "summary"
verbose_name = _("Summary")
def get_object_id(self, datum):
return datum.get('type')
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/templates/ 0000775 0000000 0000000 00000000000 14617051670 0031035 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/templates/rating/ 0000775 0000000 0000000 00000000000 14617051670 0032321 5 ustar 00root root 0000000 0000000 _launch_details_price.html 0000664 0000000 0000000 00000000765 14617051670 0037440 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/templates/rating {% include 'project/instances/_launch_details_help.html' %}
{% load i18n %}
{% load static %}
{% block price %}
{% trans "Rating Information" %}
{% trans "Price" %}
{% trans "$" %}
{% endblock %}
index.html 0000664 0000000 0000000 00000000460 14617051670 0034237 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/templates/rating {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Rating Summary" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Rating Summary") %}
{% endblock page_header %}
{% block main %}
{{ table.render }}
{{ modules }}
{% endblock %}
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/tests.py 0000664 0000000 0000000 00000000253 14617051670 0030553 0 ustar 00root root 0000000 0000000 from horizon.test import helpers as test
class CkprojectTests(test.TestCase):
# Unit tests for ckproject.
def test_me(self):
self.assertTrue(1 + 1 == 2)
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/urls.py 0000664 0000000 0000000 00000001503 14617051670 0030375 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.urls import re_path
from cloudkittydashboard.dashboards.project.rating import views
urlpatterns = [
re_path(r'^$', views.IndexView.as_view(), name='index'),
re_path(r'^quote$', views.quote, name='quote')
]
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/views.py 0000664 0000000 0000000 00000005465 14617051670 0030560 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.
import json
from django.conf import settings
from django import http
from django.utils.translation import gettext_lazy as _
from horizon import exceptions
from horizon import tables
from cloudkittydashboard.api import cloudkitty as api
from cloudkittydashboard.dashboards.project.rating \
import tables as rating_tables
from cloudkittydashboard import utils
rate_prefix = getattr(settings,
'OPENSTACK_CLOUDKITTY_RATE_PREFIX', None)
rate_postfix = getattr(settings,
'OPENSTACK_CLOUDKITTY_RATE_POSTFIX', None)
class IndexView(tables.DataTableView):
table_class = rating_tables.SummaryTable
template_name = 'project/rating/index.html'
def get_data(self):
summary = api.cloudkittyclient(
self.request, version='2').summary.get_summary(
tenant_id=self.request.user.tenant_id,
groupby=['type'], response_format='object')
data = summary.get('results')
total = sum([r.get('rate') for r in data])
data.append({'type': 'TOTAL', 'rate': total})
for item in data:
item['rate'] = utils.formatRate(item['rate'],
rate_prefix, rate_postfix)
return data
def quote(request):
pricing = 0.0
if request.is_ajax():
if request.method == 'POST':
json_data = json.loads(request.body)
def __update_quotation_data(element, service):
if isinstance(element, dict):
element['service'] = service
else:
for elem in element:
__update_quotation_data(elem, service)
try:
service = getattr(
settings, 'CLOUDKITTY_QUOTATION_SERVICE', 'instance')
__update_quotation_data(json_data, service)
pricing = float(api.cloudkittyclient(request)
.rating.get_quotation(res_data=json_data))
except Exception:
exceptions.handle(request,
_('Unable to retrieve price.'))
return http.HttpResponse(json.dumps(pricing),
content_type='application/json')
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/ 0000775 0000000 0000000 00000000000 14617051670 0027564 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0031663 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/panel.py 0000664 0000000 0000000 00000001556 14617051670 0031244 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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 gettext_lazy as _
import horizon
from openstack_dashboard.dashboards.project import dashboard
class Project_reporting(horizon.Panel):
name = _("Reporting")
slug = "reporting"
dashboard.Project.register(Project_reporting)
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/templates/ 0000775 0000000 0000000 00000000000 14617051670 0031562 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/templates/reporting/ 0000775 0000000 0000000 00000000000 14617051670 0033573 5 ustar 00root root 0000000 0000000 index.html 0000664 0000000 0000000 00000000543 14617051670 0035513 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/templates/reporting {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Reporting" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Reporting") %}
{% endblock page_header %}
{% block main %}
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/urls.py 0000664 0000000 0000000 00000000264 14617051670 0031125 0 ustar 00root root 0000000 0000000 from django.urls import re_path
from cloudkittydashboard.dashboards.project.reporting import views
urlpatterns = [
re_path(r'^$', views.IndexView.as_view(), name='index'),
]
cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/views.py 0000664 0000000 0000000 00000006505 14617051670 0031301 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.
import calendar
import collections
import datetime
import decimal
import time
from horizon import tabs
from cloudkittydashboard.api import cloudkitty as api
def _do_this_month(data):
services = {}
# these variables will keep track of the time span to fill the dicts with
# empty values after the parsing. This is needed by rickshaw to display
# stacked graphs
start_timestamp = None
end_timestamp = None
for dataframe in data.get('dataframes', []):
begin = dataframe['begin']
timestamp = int(time.mktime(
datetime.datetime.strptime(begin[:16],
"%Y-%m-%dT%H:%M").timetuple()))
if start_timestamp is None or timestamp < start_timestamp:
start_timestamp = timestamp
if end_timestamp is None or timestamp > end_timestamp:
end_timestamp = timestamp
for resource in dataframe['resources']:
service_id = resource['service']
service_data = services.setdefault(
service_id, {'cumulated': 0, 'hourly': {}})
service_data['cumulated'] += decimal.Decimal(resource['rating'])
hourly_data = service_data['hourly']
hourly_data.setdefault(timestamp, 0)
hourly_data[timestamp] += float(resource['rating'])
service_names = services.keys()
t = start_timestamp
if end_timestamp:
while t <= end_timestamp:
for service in service_names:
hourly_d = services[service]['hourly']
hourly_d.setdefault(t, 0)
t += 3600
# now sort the dicts
for service in service_names:
d = services[service]['hourly']
services[service]['hourly'] = collections.OrderedDict(
sorted(d.items(), key=lambda t: t[0]))
return services
class CostRepartitionTab(tabs.Tab):
name = "This month"
slug = "this_month"
template_name = 'project/reporting/this_month.html'
def get_context_data(self, request, **kwargs):
today = datetime.datetime.today()
day_start, day_end = calendar.monthrange(today.year, today.month)
begin = "%4d-%02d-01T00:00:00" % (today.year, today.month)
end = "%4d-%02d-%02dT23:59:59" % (today.year, today.month, day_end)
client = api.cloudkittyclient(request)
data = client.storage.get_dataframes(
begin=begin, end=end, tenant_id=request.user.tenant_id)
parsed_data = _do_this_month(data)
return {'repartition_data': parsed_data}
class ReportingTabs(tabs.TabGroup):
slug = "reporting_tabs"
tabs = (CostRepartitionTab, )
sticky = True
class IndexView(tabs.TabbedTableView):
tab_group_class = ReportingTabs
template_name = 'project/reporting/index.html'
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/ 0000775 0000000 0000000 00000000000 14617051670 0023365 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_10_admin_group.py 0000664 0000000 0000000 00000001262 14617051670 0026703 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.
PANEL_GROUP = 'rating'
PANEL_GROUP_NAME = 'Rating'
PANEL_GROUP_DASHBOARD = 'admin'
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_10_project_group.py 0000664 0000000 0000000 00000001264 14617051670 0027263 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.
PANEL_GROUP = 'rating'
PANEL_GROUP_NAME = 'Rating'
PANEL_GROUP_DASHBOARD = 'project'
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_11_admin_hashmap_panel.py 0000664 0000000 0000000 00000001562 14617051670 0030353 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.
ADD_INSTALLED_APPS = ['cloudkittydashboard']
AUTO_DISCOVER_STATIC_FILES = True
PANEL_GROUP = 'rating'
PANEL_DASHBOARD = 'admin'
PANEL = 'hashmap'
# Python panel class of the PANEL to be added.
ADD_PANEL = \
'cloudkittydashboard.dashboards.admin.hashmap.panel.Hashmap'
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_11_admin_rating_panel.py 0000664 0000000 0000000 00000001450 14617051670 0030212 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.
PANEL_GROUP = 'rating'
PANEL_DASHBOARD = 'admin'
PANEL = 'rating_modules'
# Python panel class of the PANEL to be added.
ADD_PANEL = \
'cloudkittydashboard.dashboards.admin.modules.panel.Modules'
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_11_admin_summary_panel.py 0000664 0000000 0000000 00000001450 14617051670 0030423 0 ustar 00root root 0000000 0000000 # Copyright 2018 Objectif Libre
#
# 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.
PANEL_GROUP = 'rating'
PANEL_DASHBOARD = 'admin'
PANEL = 'rating_summary'
# Python panel class of the PANEL to be added.
ADD_PANEL = \
'cloudkittydashboard.dashboards.admin.summary.panel.Summary'
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_11_project_rating_panel.py 0000664 0000000 0000000 00000001452 14617051670 0030572 0 ustar 00root root 0000000 0000000 # Copyright 2015 Objectif Libre
#
# 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.
PANEL_GROUP = 'rating'
PANEL_DASHBOARD = 'project'
PANEL = 'rating'
# Python panel class of the PANEL to be added.
ADD_PANEL = \
'cloudkittydashboard.dashboards.project.rating.panel.Project_rating'
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_12_project_reporting_panel.py 0000664 0000000 0000000 00000001527 14617051670 0031323 0 ustar 00root root 0000000 0000000 # Copyright 2019 Objectif Libre
#
# 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.
#
PANEL_GROUP = 'rating'
PANEL_DASHBOARD = 'project'
PANEL = 'reporting'
ADD_XSTATIC_MODULES = [
('xstatic.pkg.d3', ['d3.js']),
('xstatic.pkg.rickshaw', ['rickshaw.js'])
]
ADD_PANEL = \
'cloudkittydashboard.dashboards.project.reporting.panel.Project_reporting'
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_13_admin_pyscripts_panel.py 0000664 0000000 0000000 00000000223 14617051670 0030765 0 ustar 00root root 0000000 0000000 PANEL_GROUP = 'rating'
PANEL_DASHBOARD = 'admin'
PANEL = 'pyscripts'
ADD_PANEL = 'cloudkittydashboard.dashboards.admin.pyscripts.panel.PyScripts'
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_31000_cloudkitty.py 0000664 0000000 0000000 00000000170 14617051670 0027012 0 ustar 00root root 0000000 0000000 FEATURE = 'cloudkitty'
ADD_ANGULAR_MODULES = [
'horizon.dashboard.cloudkitty',
]
AUTO_DISCOVER_STATIC_FILES = True
cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0025464 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/ 0000775 0000000 0000000 00000000000 14617051670 0023232 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/en_GB/ 0000775 0000000 0000000 00000000000 14617051670 0024204 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/en_GB/LC_MESSAGES/ 0000775 0000000 0000000 00000000000 14617051670 0025771 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/en_GB/LC_MESSAGES/django.po 0000664 0000000 0000000 00000024361 14617051670 0027601 0 ustar 00root root 0000000 0000000 # Andi Chandler , 2018. #zanata
# Andi Chandler , 2019. #zanata
# Andi Chandler , 2020. #zanata
msgid ""
msgstr ""
"Project-Id-Version: cloudkitty-dashboard VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
"POT-Creation-Date: 2022-08-08 15:59+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2020-10-03 10:01+0000\n"
"Last-Translator: Andi Chandler \n"
"Language-Team: English (United Kingdom)\n"
"Language: en_GB\n"
"X-Generator: Zanata 4.3.3\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#, python-format
msgid ""
"\n"
" Edit the priority for the %(module_id)s module.\n"
" "
msgstr ""
"\n"
" Edit the priority for the %(module_id)s module.\n"
" "
#, python-format
msgid ""
"\n"
"
Usage data are collected every %(period)s seconds.
\n"
" "
msgstr ""
"\n"
"
Usage data are collected every %(period)s seconds.
\n"
" "
msgid "$"
msgstr "$"
msgid "A field is referring to a metadata field of a resource. "
msgstr "A field is referring to a metadata field of a resource. "
msgid "A group is a way to group calculations of mappings."
msgstr "A group is a way to group calculations of mappings."
msgid "A mapping is the final object, it’s what triggers calculation."
msgstr "A mapping is the final object, it’s what triggers calculation."
msgid "A script or set of python commands to modify rating calculations."
msgstr "A script or set of Python commands to modify rating calculations."
msgid "A service is a way to map the rule to the type of data collected."
msgstr "A service is a way to map the rule to the type of data collected."
msgid ""
"A threshold entry is used to apply rating rules base on level. Its behaviour "
"is similar to a mapping except that it applies the cost base on the level."
msgstr ""
"A threshold entry is used to apply rating rules base on level. Its behaviour "
"is similar to a mapping except that it applies the cost base on the level."
msgid "Checksum"
msgstr "Checksum"
msgid "Configurable"
msgstr "Configurable"
msgid "Cost"
msgstr "Cost"
msgid "Cost Per Service Per Hour"
msgstr "Cost Per Service Per Hour"
msgid "Create Field"
msgstr "Create Field"
msgid "Create Field Mapping"
msgstr "Create Field Mapping"
msgid "Create Field Threshold"
msgstr "Create Field Threshold"
msgid "Create Group"
msgstr "Create Group"
msgid "Create Mapping"
msgstr "Create Mapping"
msgid "Create Script"
msgstr "Create Script"
msgid "Create Service"
msgstr "Create Service"
msgid "Create Service Threshold"
msgstr "Create Service Threshold"
msgid "Create a new rating script."
msgstr "Create a new rating script."
msgid "Create field Mapping"
msgstr "Create field Mapping"
msgid "Create new Field"
msgstr "Create new Field"
msgid "Create new Field Threshold"
msgstr "Create new Field Threshold"
msgid "Create new Group"
msgstr "Create new Group"
msgid "Create new Mapping"
msgstr "Create new Mapping"
msgid "Create new Service"
msgstr "Create new Service"
msgid "Create new Service Threshold"
msgstr "Create new Service Threshold"
msgid "Cumulative Cost Repartition"
msgstr "Cumulative Cost Repartition"
msgid "Custom service"
msgstr "Custom service"
msgid "Custom service cannot be empty."
msgstr "Custom service cannot be empty."
msgid "Custom services can be defined for any additional collector."
msgstr "Custom services can be defined for any additional collector."
msgid "Data"
msgstr "Data"
msgid "Delete Field"
msgid_plural "Delete Fields"
msgstr[0] "Delete Field"
msgstr[1] "Delete Fields"
msgid "Delete Field Threshold"
msgid_plural "Delete Field Thresholds"
msgstr[0] "Delete Field Threshold"
msgstr[1] "Delete Field Thresholds"
msgid "Delete Group"
msgid_plural "Delete Groups"
msgstr[0] "Delete Group"
msgstr[1] "Delete Groups"
msgid "Delete Mapping"
msgid_plural "Delete Mappings"
msgstr[0] "Delete Mapping"
msgstr[1] "Delete Mappings"
msgid "Delete PyScript"
msgid_plural "Delete PyScripts"
msgstr[0] "Delete PyScript"
msgstr[1] "Delete PyScripts"
msgid "Delete Script"
msgstr "Delete Script"
msgid "Delete Service"
msgid_plural "Delete Services"
msgstr[0] "Delete Service"
msgstr[1] "Delete Services"
msgid "Delete Service Threshold"
msgid_plural "Delete Service Thresholds"
msgstr[0] "Delete Service Threshold"
msgstr[1] "Delete Service Thresholds"
msgid "Deleted Field"
msgid_plural "Deleted Fields"
msgstr[0] "Deleted Field"
msgstr[1] "Deleted Fields"
msgid "Deleted Field Threshold"
msgid_plural "Deleted Field Thresholds"
msgstr[0] "Deleted Field Threshold"
msgstr[1] "Deleted Field Thresholds"
msgid "Deleted Group"
msgid_plural "Deleted Groups"
msgstr[0] "Deleted Group"
msgstr[1] "Deleted Groups"
msgid "Deleted Mapping"
msgid_plural "Deleted Mappings"
msgstr[0] "Deleted Mapping"
msgstr[1] "Deleted Mappings"
msgid "Deleted PyScript"
msgid_plural "Deleted PyScripts"
msgstr[0] "Deleted PyScript"
msgstr[1] "Deleted PyScripts"
msgid "Deleted Service"
msgid_plural "Deleted Services"
msgstr[0] "Deleted Service"
msgstr[1] "Deleted Services"
msgid "Deleted Service Threshold"
msgid_plural "Deleted Service Thresholds"
msgstr[0] "Deleted Service Threshold"
msgstr[1] "Deleted Service Thresholds"
msgid "Description"
msgstr "Description"
msgid "Description:"
msgstr "Description:"
msgid "Direct Input"
msgstr "Direct Input"
msgid "Disable Module"
msgid_plural "Disable Modules"
msgstr[0] "Disable Module"
msgstr[1] "Disable Modules"
msgid "Disabled Module"
msgid_plural "Disabled Modules"
msgstr[0] "Disabled Module"
msgstr[1] "Disabled Modules"
msgid "Edit Field Threshold"
msgstr "Edit Field Threshold"
msgid "Edit Mapping"
msgstr "Edit Mapping"
msgid "Edit Module Priority"
msgstr "Edit Module Priority"
msgid "Edit Script"
msgstr "Edit Script"
msgid "Edit Service Threshold"
msgstr "Edit Service Threshold"
msgid "Edit module priority"
msgstr "Edit module priority"
msgid "Enable Module"
msgid_plural "Enable Modules"
msgstr[0] "Enable Module"
msgstr[1] "Enable Modules"
msgid "Enabled"
msgstr "Enabled"
msgid "Enabled Module"
msgid_plural "Enabled Modules"
msgstr[0] "Enabled Module"
msgstr[1] "Enabled Modules"
msgid "Field"
msgstr "Field"
msgid "Field ID"
msgstr "Field ID"
msgid "Field Mappings"
msgstr "Field Mappings"
msgid "Field Threshold"
msgstr "Field Threshold"
msgid "Field Thresholds"
msgstr "Field Thresholds"
msgid "Field was successfully created"
msgstr "Field was successfully created"
msgid "Fields"
msgstr "Fields"
msgid "File"
msgstr "File"
msgid "Flat"
msgstr "Flat"
msgid "Group"
msgstr "Group"
msgid "Group Details"
msgstr "Group Details"
msgid "Group Name"
msgstr "Group Name"
msgid "Group was successfully created"
msgstr "Group was successfully created"
msgid "Groups"
msgstr "Groups"
msgid "Hashmap"
msgstr "Hashmap"
msgid "Hot Config"
msgstr "Hot Config"
msgid "Id"
msgstr "Id"
msgid "Legend"
msgstr "Legend"
msgid "Level"
msgstr "Level"
msgid "Mapping"
msgstr "Mapping"
msgid "Mapping ID"
msgstr "Mapping ID"
msgid "Mappings"
msgstr "Mappings"
msgid "Metric Type"
msgstr "Metric Type"
msgid "Module"
msgstr "Module"
msgid "Modules"
msgstr "Modules"
msgid "Name"
msgstr "Name"
msgid "Not available"
msgstr "Not available"
msgid "Price"
msgstr "Price"
msgid "Priority"
msgstr "Priority"
msgid "Project"
msgstr "Project"
msgid "Project ID"
msgstr "Project ID"
msgid "Project ID:"
msgstr "Project ID:"
msgid "Project Name"
msgstr "Project Name"
msgid "Project Total"
msgstr "Project Total"
msgid "PyScript"
msgstr "PyScript"
msgid "PyScripts"
msgstr "PyScripts"
msgid "Rate"
msgstr "Rate"
msgid "Rating"
msgstr "Rating"
msgid "Rating Information"
msgstr "Rating Information"
msgid "Rating Module Details"
msgstr "Rating Module Details"
msgid "Rating Modules"
msgstr "Rating Modules"
msgid "Rating Script Source"
msgstr "Rating Script Source"
msgid "Rating Summary"
msgstr "Rating Summary"
msgid "Reporting"
msgstr "Reporting"
msgid "Res Type"
msgstr "Res Type"
msgid "Script Data"
msgstr "Script Data"
msgid "Script Details"
msgstr "Script Details"
msgid "Script Details : {{ script.name }}"
msgstr "Script Details : {{ script.name }}"
msgid "Script Details : {{ table.project_id }}"
msgstr "Script Details : {{ table.project_id }}"
msgid "Script File"
msgstr "Script File"
msgid "Script ID"
msgstr "Script ID"
msgid "Service"
msgstr "Service"
msgid "Service ID"
msgstr "Service ID"
msgid "Service Mappings"
msgstr "Service Mappings"
msgid "Service Name"
msgstr "Service Name"
msgid "Service Threshold"
msgstr "Service Threshold"
msgid "Service Thresholds"
msgstr "Service Thresholds"
msgid "Service type"
msgstr "Service type"
msgid "Service was successfully created"
msgstr "Service was successfully created"
msgid "Services"
msgstr "Services"
msgid "Services are provided by main collector."
msgstr "Services are provided by main collector."
msgid "Successfully created script"
msgstr "Successfully created script"
msgid "Successfully updated priority"
msgstr "Successfully updated priority"
msgid "Successfully updated script"
msgstr "Successfully updated script"
msgid "Summary"
msgstr "Summary"
msgid "Tenant Details"
msgstr "Tenant Details"
msgid "Tenant Summary"
msgstr "Tenant Summary"
#, python-format
msgid "There was a problem parsing the %(prefix)s: %(error)s"
msgstr "There was a problem parsing the %(prefix)s: %(error)s"
msgid "Threshold ID"
msgstr "Threshold ID"
msgid "Thresholds"
msgstr "Thresholds"
msgid "Type"
msgstr "Type"
msgid "Unable to create field."
msgstr "Unable to create field."
msgid "Unable to create group."
msgstr "Unable to create group."
msgid "Unable to create new service."
msgstr "Unable to create new service."
msgid "Unable to create script."
msgstr "Unable to create script."
msgid "Unable to retrieve price."
msgstr "Unable to retrieve price."
msgid "Unable to update priority."
msgstr "Unable to update priority."
msgid "Unable to update script."
msgstr "Unable to update script."
msgid "Unit"
msgstr "Unit"
msgid "Update Field Mapping"
msgstr "Update Field Mapping"
msgid "Update Field Threshold"
msgstr "Update Field Threshold"
msgid "Update Mapping"
msgstr "Update Mapping"
msgid "Update Script"
msgstr "Update Script"
msgid "Update Service Threshold"
msgstr "Update Service Threshold"
msgid "Value"
msgstr "Value"
msgid "id"
msgstr "id"
msgid "pyscripts"
msgstr "pyscripts"
cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/fr/ 0000775 0000000 0000000 00000000000 14617051670 0023641 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/fr/LC_MESSAGES/ 0000775 0000000 0000000 00000000000 14617051670 0025426 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/fr/LC_MESSAGES/django.po 0000664 0000000 0000000 00000021026 14617051670 0027231 0 ustar 00root root 0000000 0000000 # François Magimel , 2018. #zanata
msgid ""
msgstr ""
"Project-Id-Version: cloudkitty-dashboard VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
"POT-Creation-Date: 2022-08-08 15:59+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2018-12-26 11:55+0000\n"
"Last-Translator: François Magimel \n"
"Language-Team: French\n"
"Language: fr\n"
"X-Generator: Zanata 4.3.3\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
#, python-format
msgid ""
"\n"
" Edit the priority for the %(module_id)s module.\n"
" "
msgstr ""
"\n"
" Éditer la priorité pour le module %(module_id)s.\n"
" "
#, python-format
msgid ""
"\n"
"
Usage data are collected every %(period)s seconds.
\n"
" "
msgstr ""
"\n"
"
Les données d'utilisation sont collectées toutes %(period)s secondes."
"p>\n"
" "
#, fuzzy
msgid "$"
msgstr "€"
msgid "A field is referring to a metadata field of a resource. "
msgstr "Un champ fait référence à un champ de métadonnées d'une ressource."
msgid "A group is a way to group calculations of mappings."
msgstr "Un groupe permet de regrouper les calculs propres à des mappings."
msgid "A mapping is the final object, it’s what triggers calculation."
msgstr "Un mapping est l'objet final : c'est ce qui déclenche le calcul."
msgid "A service is a way to map the rule to the type of data collected."
msgstr ""
"Un service est un moyen de faire correspondre la règle au type de données "
"recueillies."
msgid "Checksum"
msgstr "Somme de contrôle"
msgid "Cost"
msgstr "Coût"
msgid "Cost Per Service Per Hour"
msgstr "Coût par service par heure"
msgid "Create Field"
msgstr "Créer un champ"
msgid "Create Field Mapping"
msgstr "Créer un mapping du champ"
msgid "Create Field Threshold"
msgstr "Créer un seuil du champ"
msgid "Create Group"
msgstr "Créer un groupe"
msgid "Create Mapping"
msgstr "Créer un mapping"
msgid "Create Script"
msgstr "Créer un script"
msgid "Create Service"
msgstr "Créer un service"
msgid "Create Service Threshold"
msgstr "Créer un seuil du service"
msgid "Create field Mapping"
msgstr "Créer un mapping du champ"
msgid "Create new Field"
msgstr "Créer un nouveau champ"
msgid "Create new Field Threshold"
msgstr "Créer un nouveau seuil du champ"
msgid "Create new Group"
msgstr "Créer un nouveau groupe"
msgid "Create new Mapping"
msgstr "Créer un nouveau mapping"
msgid "Create new Service"
msgstr "Créer un nouveau service"
msgid "Create new Service Threshold"
msgstr "Créer un nouveau seuil du service"
msgid "Cumulative Cost Repartition"
msgstr "Répartition des coûts cumulés"
msgid "Custom service"
msgstr "Service personnalisé"
msgid "Data"
msgstr "Données"
msgid "Delete Field"
msgid_plural "Delete Fields"
msgstr[0] "Supprimer le champ"
msgstr[1] "Supprimer les champs"
msgid "Delete Field Threshold"
msgid_plural "Delete Field Thresholds"
msgstr[0] "Supprimer le seuil du champ"
msgstr[1] "Supprimer les seuils du champ"
msgid "Delete Group"
msgid_plural "Delete Groups"
msgstr[0] "Supprimer le groupe"
msgstr[1] "Supprimer les groupes"
msgid "Delete Mapping"
msgid_plural "Delete Mappings"
msgstr[0] "Supprimer le mapping"
msgstr[1] "Supprimer les mappings"
msgid "Delete PyScript"
msgid_plural "Delete PyScripts"
msgstr[0] "Supprimer le PyScript"
msgstr[1] "Supprimer les PyScripts"
msgid "Delete Script"
msgstr "Supprimer le script"
msgid "Delete Service"
msgid_plural "Delete Services"
msgstr[0] "Supprimer le service"
msgstr[1] "Supprimer les services"
msgid "Delete Service Threshold"
msgid_plural "Delete Service Thresholds"
msgstr[0] "Supprimer le seuil du service"
msgstr[1] "Supprimer les seuils du service"
msgid "Deleted Field"
msgid_plural "Deleted Fields"
msgstr[0] "Champ supprimé"
msgstr[1] "Champs supprimés"
msgid "Deleted Field Threshold"
msgid_plural "Deleted Field Thresholds"
msgstr[0] "Seuil du champ supprimé"
msgstr[1] "Seuils du champ supprimés"
msgid "Deleted Group"
msgid_plural "Deleted Groups"
msgstr[0] "Groupe supprimé"
msgstr[1] "Groupes supprimés"
msgid "Deleted Mapping"
msgid_plural "Deleted Mappings"
msgstr[0] "Mapping supprimé"
msgstr[1] "Mappings supprimés"
msgid "Deleted PyScript"
msgid_plural "Deleted PyScripts"
msgstr[0] "PyScript supprimé"
msgstr[1] "PyScripts supprimés"
msgid "Deleted Service"
msgid_plural "Deleted Services"
msgstr[0] "Service supprimé"
msgstr[1] "Services supprimés"
msgid "Deleted Service Threshold"
msgid_plural "Deleted Service Thresholds"
msgstr[0] "Seuil du service supprimé"
msgstr[1] "Seuils du service supprimés"
msgid "Description"
msgstr "Description"
msgid "Description:"
msgstr "Description :"
msgid "Direct Input"
msgstr "Entrée directe"
msgid "Disable Module"
msgid_plural "Disable Modules"
msgstr[0] "Désactiver le module"
msgstr[1] "Désactiver les modules"
msgid "Disabled Module"
msgid_plural "Disabled Modules"
msgstr[0] "Module désactivé"
msgstr[1] "Modules désactivés"
msgid "Edit Field Threshold"
msgstr "Éditer le seuil du champ"
msgid "Edit Mapping"
msgstr "Éditer le mapping"
msgid "Edit Module Priority"
msgstr "Édité la priorité du module"
msgid "Edit Script"
msgstr "Éditer le script"
msgid "Edit Service Threshold"
msgstr "Éditer le seuil du service"
msgid "Edit module priority"
msgstr "Édité la priorité du module"
msgid "Enable Module"
msgid_plural "Enable Modules"
msgstr[0] "Activer le module"
msgstr[1] "Activer les modules"
msgid "Enabled"
msgstr "Activé"
msgid "Enabled Module"
msgid_plural "Enabled Modules"
msgstr[0] "Module activé"
msgstr[1] "Modules activés"
msgid "Field"
msgstr "Champ"
msgid "Field ID"
msgstr "ID du champ"
msgid "Field Mappings"
msgstr "Mappings du champ"
msgid "Field Threshold"
msgstr "Seuil du champ"
msgid "Field Thresholds"
msgstr "Seuils du champ"
msgid "Fields"
msgstr "Champs"
msgid "File"
msgstr "Fichier"
msgid "Group"
msgstr "Groupe"
msgid "Group Details"
msgstr "Détails du groupe"
msgid "Group Name"
msgstr "Nom du groupe"
msgid "Groups"
msgstr "Groupes"
msgid "Hashmap"
msgstr "Table de hachage"
msgid "Id"
msgstr "Id"
msgid "Level"
msgstr "Niveau"
msgid "Mapping"
msgstr "Mapping"
msgid "Mapping ID"
msgstr "ID du mapping"
msgid "Mappings"
msgstr "Mappings"
msgid "Metric Type"
msgstr "Type de mesure"
msgid "Module"
msgstr "Module"
msgid "Modules"
msgstr "Modules"
msgid "Name"
msgstr "Nom"
msgid "Not available"
msgstr "Non disponible"
msgid "Price"
msgstr "Prix"
msgid "Priority"
msgstr "Priorité"
msgid "Project"
msgstr "Projet"
msgid "Project ID"
msgstr "ID du projet"
msgid "Project ID:"
msgstr "ID du projet :"
msgid "Project Name"
msgstr "Nom du projet"
msgid "Project Total"
msgstr "Total du projet"
msgid "PyScript"
msgstr "PyScript"
msgid "PyScripts"
msgstr "PyScripts"
msgid "Rating"
msgstr "Tarification"
msgid "Reporting"
msgstr "Rapport"
msgid "Script Data"
msgstr "Données du script"
msgid "Script Details"
msgstr "Détails du script"
msgid "Script Details : {{ script.name }}"
msgstr "Détails du script : {{ script.name }}"
msgid "Script Details : {{ table.project_id }}"
msgstr "Détails du script : {{ table.project_id }}"
#, fuzzy
msgid "Script File"
msgstr "Fichier (script)"
msgid "Script ID"
msgstr "ID du script"
msgid "Service"
msgstr "Service"
msgid "Service ID"
msgstr "ID du service"
msgid "Service Mappings"
msgstr "Mappings du service"
msgid "Service Name"
msgstr "Nom du service"
msgid "Service Threshold"
msgstr "Seuil du service"
msgid "Service Thresholds"
msgstr "Seuils du service"
msgid "Service type"
msgstr "Type de service"
msgid "Services"
msgstr "Services"
msgid "Services are provided by main collector."
msgstr "Les services sont fournis par le collecteur principal."
msgid "Summary"
msgstr "Résumé"
#, python-format
msgid "There was a problem parsing the %(prefix)s: %(error)s"
msgstr "Il y a eu un problème pour analyser le %(prefix)s : %(error)s"
msgid "Threshold ID"
msgstr "Id du seuil"
msgid "Thresholds"
msgstr "Seuils"
msgid "Type"
msgstr "Type"
msgid "Unable to retrieve price."
msgstr "Impossible de récupérer le prix."
msgid "Unit"
msgstr "Unité"
msgid "Update Field Mapping"
msgstr "Mettre à jour le mapping du champ"
msgid "Update Field Threshold"
msgstr "Mettre à jour le seuil du champ"
msgid "Update Mapping"
msgstr "Mettre à jour le mapping"
msgid "Update Script"
msgstr "Mettre à jour le script"
msgid "Update Service Threshold"
msgstr "Mettre à jour le seuil du service"
msgid "Value"
msgstr "Valeur"
msgid "id"
msgstr "id"
msgid "pyscripts"
msgstr "pyscripts"
cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/ 0000775 0000000 0000000 00000000000 14617051670 0023262 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/ 0000775 0000000 0000000 00000000000 14617051670 0025455 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/js/ 0000775 0000000 0000000 00000000000 14617051670 0026071 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/js/cloudkitty.controller.js 0000664 0000000 0000000 00000003026 14617051670 0033005 0 ustar 00root root 0000000 0000000 (function () {
'use strict';
angular
.module('horizon.dashboard.cloudkitty')
.controller('CloudkittyStepController', CloudkittyStepController);
CloudkittyStepController.$inject = [
'$scope',
'horizon.framework.widgets.wizard.events',
'$http',
'$window'
];
function CloudkittyStepController($scope, wizardEvents, $http, $window) {
var onSwitch = $scope.$on(wizardEvents.ON_SWITCH, function(evt, args) {
if(!$scope.model.newInstanceSpec.flavor) return false;
var disk_total = $scope.model.newInstanceSpec.flavor.ephemeral + $scope.model.newInstanceSpec.flavor.disk;
var desc_form = {
'flavor_name': $scope.model.newInstanceSpec.flavor.name,
'flavor_id': $scope.model.newInstanceSpec.flavor.id,
'vcpus': $scope.model.newInstanceSpec.flavor.vcpus,
'disk': $scope.model.newInstanceSpec.flavor.disk,
'ephemeral': $scope.model.newInstanceSpec.flavor.ephemeral,
'disk_total': disk_total,
'disk_total_display': disk_total,
'ram': $scope.model.newInstanceSpec.flavor.ram,
'source_type': $scope.model.newInstanceSpec.source_type.type,
'source_val': $scope.model.newInstanceSpec.source[0].id,
'image_id': $scope.model.newInstanceSpec.source[0].id,
}
var form_data = [{"desc": desc_form, "volume": $scope.model.newInstanceSpec.instance_count}];
$http.post($window.WEBROOT + 'project/rating/quote', form_data).then(function(res, status) {
$scope.price = res.data;
});
});
}
})();
cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/js/cloudkitty.module.js 0000664 0000000 0000000 00000001374 14617051670 0032113 0 ustar 00root root 0000000 0000000 (function () {
'use strict';
var ck = angular.module('horizon.dashboard.cloudkitty', ['horizon.dashboard.project.workflow']).config(config)
config.$inject = [
'$provide',
'$windowProvider'
];
function config($provide, $windowProvider) {
$provide.decorator("horizon.dashboard.project.workflow.launch-instance.workflow", ['$delegate', function ($delegate) {
var workflow = $delegate;
var static_path = $windowProvider.$get().STATIC_URL;
workflow.append({
formName: 'CloudkittyForm',
templateUrl: static_path + 'cloudkitty/templates/cloudkitty-step.html',
helpUrl: static_path + 'cloudkitty/templates/cloudkitty-help.html',
title: 'Price'
});
return workflow;
}]);
}
})();
cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/js/cloudkitty.spec.js 0000664 0000000 0000000 00000000344 14617051670 0031554 0 ustar 00root root 0000000 0000000 (function () {
'use strict';
describe('horizon.dashboard.cloudkitty', function () {
it('should be defined', function () {
expect(angular.module('horizon.dashboard.cloudkitty')).toBeDefined();
});
});
})();
cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/js/pricing.js 0000664 0000000 0000000 00000012021 14617051670 0030056 0 ustar 00root root 0000000 0000000 /*
Copyright 2015 Objectif Libre
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.
*/
pricing = {
is_price: false, // Is this a price display ?
init: function() {
this._attachInputHandlers(); // handler
},
init_work: function() {
this.is_price = true;
//$('#price').text('0');
this.init();
this.work();
},
/*
Attaches event handlers for the form elements associated with the price
(see horizon.Quota._attachInputHandlers).
*/
_attachInputHandlers: function() {
var scope = this;
if (this.is_price) {
var eventCallback = function(evt) {
scope.work();
};
$('#id_flavor').on('change', eventCallback);
$('#id_count').on('change', eventCallback);
$('#id_image_id').on('change', eventCallback);
}
},
work: function() {
// Quota available → what about the selected flavor
if (horizon.Quota.selected_flavor) {
// get data of the flavor (form)
_image = horizon.Quota.getSelectedImageOrSnapshot();
var flavor = horizon.Quota.selected_flavor.name;
var vcpus = horizon.Quota.selected_flavor.vcpus;
var disk = horizon.Quota.selected_flavor.disk;
var ephemeral = horizon.Quota.selected_flavor["OS-FLV-EXT-DATA:ephemeral"];
var disk_total = horizon.Quota.selected_flavor.disk + horizon.Quota.selected_flavor["OS-FLV-EXT-DATA:ephemeral"];
var disk_total_display = disk_total;
var ram = horizon.Quota.selected_flavor.ram;
var source_type = $('#id_source_type option:selected').val();
var source_val = $('#id_' + source_type + ' option:selected').val();
var instance_count = parseInt($("#id_count").val());
// make the json data form
desc_form = {
'flavor': flavor,
'source_type': source_type,
'source_val': source_val, // images : horizon.Quota.findImageById(source_val);
'vcpus': vcpus,
'disk': disk,
'ephemeral': ephemeral,
'disk_total': disk_total,
'disk_total_display': disk_total_display,
'ram': ram
}
if (_image != undefined) {
desc_form['image_id'] = _image.id
}
var form_data = [{"desc": desc_form, "volume": instance_count}];
// send the JSON by a POST request
var url_data = [
'/dashboard/project/rating/quote',
'/project/rating/quote']
this.sendPost(form_data, url_data);
}
},
sendPost: function(form_data, url_data) {
var url = url_data.shift();
$.ajax({
type: "post", // send POST data
url: url,
dataType: 'json',
data: JSON.stringify(form_data), // data sent
contentType: 'application/json; charset=utf-8',
success: function (data) {
$("#price").text(data);
},
error: function () {
if (url_data)
pricing.sendPost(form_data, url_data);
},
beforeSend: function(xhr, settings){
$.ajaxSettings.beforeSend(xhr, settings);
}
});
}
};
// https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
// http://stackoverflow.com/a/6199592
// http://coreymaynard.com/blog/performing-ajax-post-requests-in-django/
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
if(typeof horizon.Quota !== 'undefined') {
pricing.init_work();
} else {
addHorizonLoadEvent(function() {
pricing.init_work();
});
}
cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/templates/ 0000775 0000000 0000000 00000000000 14617051670 0027453 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/templates/cloudkitty-help.html 0000664 0000000 0000000 00000000056 14617051670 0033463 0 ustar 00root root 0000000 0000000
cloudkitty-dashboard-19.0.0/cloudkittydashboard/tests/ 0000775 0000000 0000000 00000000000 14617051670 0023135 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/tests/__init__.py 0000664 0000000 0000000 00000000000 14617051670 0025234 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/cloudkittydashboard/tests/base.py 0000664 0000000 0000000 00000001432 14617051670 0024421 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2010-2011 OpenStack Foundation
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# 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 oslotest import base
class TestCase(base.BaseTestCase):
"""Test case base class for all unit tests."""
cloudkitty-dashboard-19.0.0/cloudkittydashboard/tests/test_cloudkittydashboard.py 0000664 0000000 0000000 00000002517 14617051670 0030616 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2015 Objectif Libre
# 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.
"""
test_cloudkittydashboard
----------------------------------
Tests for `cloudkittydashboard` module.
"""
from cloudkittydashboard.dashboards.project.rating import \
panel as rating_panel
from cloudkittydashboard.dashboards.project.reporting import \
panel as reporting_panel
from cloudkittydashboard.tests import base
import horizon
class TestCloudkittydashboard(base.TestCase):
def test_registered(self):
project_dashboard = horizon.get_dashboard('project')
panel_1 = project_dashboard.get_panel('rating')
self.assertEqual(rating_panel.Project_rating, panel_1.__class__)
panel_2 = project_dashboard.get_panel('reporting')
self.assertEqual(reporting_panel.Project_reporting, panel_2.__class__)
cloudkitty-dashboard-19.0.0/cloudkittydashboard/tests/test_predictive_pricing.py 0000664 0000000 0000000 00000005676 14617051670 0030435 0 ustar 00root root 0000000 0000000 # Copyright 2019 Objectif Libre
#
# 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.
import json
import os
from unittest import mock
from django import http
from cloudkittydashboard.tests import base
class PredictivePricingTest(base.TestCase):
def setUp(self):
super(PredictivePricingTest, self).setUp()
os.environ['DJANGO_SETTINGS_MODULE'] = 'openstack_dashboard.settings'
import horizon.tables # noqa
with mock.patch('horizon.tables'):
from cloudkittydashboard.dashboards.project.rating.views \
import quote
os.environ.pop('DJANGO_SETTINGS_MODULE')
self.quote = quote
def _test_quote_request_not_ajax_post(self, arg):
request = mock.MagicMock()
if arg == 'ajax':
request.is_ajax.return_value = False
elif arg == 'method':
request.method == 'POST'
resp = self.quote(request)
self.assertIsInstance(resp, http.HttpResponse)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.content.decode(), '0.0')
def test_quote_request_not_ajax(self):
self._test_quote_request_not_ajax_post('ajax')
def test_quote_request_wrong_method(self):
self._test_quote_request_not_ajax_post('method')
@mock.patch('cloudkittydashboard.dashboards.project.rating.views.api')
def test_quote_does_update_request_dict(self, api_mock):
body = [{'service': 'nope'}, {'other_key': None}]
expected_body = [{'service': 'test_service'},
{'other_key': None, 'service': 'test_service'}]
request = mock.MagicMock()
request.is_ajax.return_value = True
request.method = 'POST'
request.body = json.dumps(body)
client_mock = mock.MagicMock()
client_mock.rating.get_quotation.return_value = 42.0
api_mock.cloudkittyclient.return_value = client_mock
settings = mock.MagicMock()
settings.CLOUDKITTY_QUOTATION_SERVICE = 'test_service'
with mock.patch(
'cloudkittydashboard.dashboards.project.rating.views.settings',
settings):
resp = self.quote(request)
api_mock.cloudkittyclient.assert_called_with(request)
client_mock.rating.get_quotation.assert_called_with(
res_data=expected_body)
self.assertIsInstance(resp, http.HttpResponse)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.content.decode(), '42.0')
cloudkitty-dashboard-19.0.0/cloudkittydashboard/tests/test_utils.py 0000664 0000000 0000000 00000001677 14617051670 0025721 0 ustar 00root root 0000000 0000000 # Copyright 2019 Objectif Libre
#
# 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.
#
import unittest
from cloudkittydashboard import utils
class TemplatizableDictTest(unittest.TestCase):
def test_hasattr_attr_exists(self):
obj = utils.TemplatizableDict(a=1, b=2)
self.assertTrue(hasattr(obj, 'a'))
def test_hasattr_attr_does_not_exist(self):
obj = utils.TemplatizableDict(a=1, b=2)
self.assertFalse(hasattr(obj, 'c'))
cloudkitty-dashboard-19.0.0/cloudkittydashboard/utils.py 0000664 0000000 0000000 00000002135 14617051670 0023506 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2018 Objectif Libre
#
# 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.
#
class TemplatizableDict(dict):
"""Class allowing to pass a dict to horizon templates"""
def __getattr__(self, key):
if key in self.keys():
return self[key]
raise AttributeError("Object has no {} attribute".format(key))
def __setattr__(self, key, val):
self[key] = val
def formatRate(rate: float, prefix: str, postfix: str) -> str:
rate = str(rate)
if prefix:
rate = prefix + rate
if postfix:
rate = rate + postfix
return rate
cloudkitty-dashboard-19.0.0/doc/ 0000775 0000000 0000000 00000000000 14617051670 0016475 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/doc/requirements.txt 0000664 0000000 0000000 00000000534 14617051670 0021763 0 ustar 00root root 0000000 0000000 # The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
openstackdocstheme>=2.2.1 # Apache-2.0
sphinx>=2.0.0,!=2.1.0 # BSD
sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD
reno>=3.1.0 # Apache-2.0
cloudkitty-dashboard-19.0.0/doc/source/ 0000775 0000000 0000000 00000000000 14617051670 0017775 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/doc/source/conf.py 0000664 0000000 0000000 00000020515 14617051670 0021277 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# 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.
import os
import sys
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'openstackdocstheme',
'sphinxcontrib.rsvgconverter',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'cloudkitty-dashboard'
copyright = '2014, Objectif Libre'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'native'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#html_theme = 'default'
html_theme = 'openstackdocs'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
# -- Options for LaTeX output ---------------------------------------------
# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664
latex_use_xindy = False
latex_domain_indices = False
latex_elements = {
'makeindex': '',
'printindex': '',
'preamble': r'\setcounter{tocdepth}{3}',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
# NOTE: Specify toctree_only=True for a better document structure of
# the generated PDF file.
latex_documents = [
(
'index',
'doc-%s.tex' % project,
'Cloudkitty-Dashboard Documentation',
'OpenStack Foundation', 'howto', True
),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(
'index',
project,
'%s Documentation' % project,
['Objectif Libre'],
1
),
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
'index',
project,
'%s Documentation' % project,
'Objectif Libre',
project,
'CloudKitty Horizon Plugin',
'Miscellaneous'
),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# -- Options for openstackdocstheme -------------------------------------------
openstackdocs_repo_name = 'openstack/cloudkitty-dashboard'
openstackdocs_pdf_link = True
openstackdocs_auto_name = False
openstackdocs_use_storyboard = True
cloudkitty-dashboard-19.0.0/doc/source/contributor/ 0000775 0000000 0000000 00000000000 14617051670 0022347 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/doc/source/contributor/contributing.rst 0000664 0000000 0000000 00000003646 14617051670 0025621 0 ustar 00root root 0000000 0000000 ============================
So You Want to Contribute...
============================
For general information on contributing to OpenStack, please check out the
`contributor guide `_ to get started.
It covers all the basics that are common to all OpenStack projects: the accounts
you need, the basics of interacting with our Gerrit review system, how we
communicate as a community, etc.
Below will cover the more project specific information you need to get started
with cloudkitty-dashboard.
Communication
~~~~~~~~~~~~~
* IRC channel #cloudkitty at `OFTC `_
* Mailing list (prefix subjects with ``[cloudkitty]`` for faster responses)
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss
Contacting the Core Team
~~~~~~~~~~~~~~~~~~~~~~~~
Please refer the `cloudkitty-dashboard Core Team
`_ contacts.
New Feature Planning
~~~~~~~~~~~~~~~~~~~~
Cloudkitty features are tracked on `Storyboard `_.
Task Tracking
~~~~~~~~~~~~~
We track our tasks in `Storyboard `_.
If you're looking for some smaller, easier work item to pick up and get started
on, search for the 'low-hanging-fruit' tag.
Reporting a Bug
~~~~~~~~~~~~~~~
You found an issue and want to make sure we are aware of it? You can do so on
`StoryBoard `_.
Getting Your Patch Merged
~~~~~~~~~~~~~~~~~~~~~~~~~
All changes proposed to the cloudkitty-dashboard project require one or two +2 votes
from cloudkitty-dashboard core reviewers before one of the core reviewers can approve
patch by giving ``Workflow +1`` vote.
Project Team Lead Duties
~~~~~~~~~~~~~~~~~~~~~~~~
All common PTL duties are enumerated in the `PTL guide
`_.
cloudkitty-dashboard-19.0.0/doc/source/index.rst 0000664 0000000 0000000 00000001270 14617051670 0021636 0 ustar 00root root 0000000 0000000 .. cloudkitty-dashboard documentation master file, created by
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to the cloudkitty-dashboard's documentation!
====================================================
Contents:
.. toctree::
:maxdepth: 2
readme
installation
For Contributors
================
* If you are a new contributor to cloudkitty-dashboard please refer: :doc:`contributor/contributing`
.. toctree::
:hidden:
contributor/contributing
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
cloudkitty-dashboard-19.0.0/doc/source/installation.rst 0000664 0000000 0000000 00000003763 14617051670 0023241 0 ustar 00root root 0000000 0000000 ============
Installation
============
Retrieve and install CloudKitty dashboard:
::
git clone https://opendev.org/openstack/cloudkitty-dashboard
cd cloudkitty-dashboard
python setup.py install
Find where the python packages are installed:
::
PY_PACKAGES_PATH=`pip --version | cut -d' ' -f4`
Then add the additional settings file to the horizon settings or installation.
Depending on your setup, you might need to add it to ``/usr/share`` or directly
in the horizon python package:
::
# If horizon is installed by packages:
ln -s $PY_PACKAGES_PATH/cloudkittydashboard/enabled/_[0-9]*.py \
/usr/share/openstack-dashboard/openstack_dashboard/enabled/
# Directly from sources:
ln -s $PY_PACKAGES_PATH/cloudkittydashboard/enabled/_[0-9]*.py \
$PY_PACKAGES_PATH/openstack_dashboard/enabled/
Restart the web server hosting Horizon.
For more detailed information about CloudKitty installation check out the
`installation section`_ of the documentation.
.. _installation section: https://cloudkitty.readthedocs.org/en/latest/installation.html
Configuration
=============
To configure CloudKitty dashboard, add variables to your Horizon settings
file.
For more details about how to add variables to Horizon settings checkout the
`Horizon Settings Reference documentation`_.
.. _Horizon Settings Reference documentation: https://docs.openstack.org/horizon/latest/configuration/settings.html
Rate Pre/Postfix
----------------
You can configure pre/postfix to rate vaules shown at the dashboard.
Here's example of setting rate currency to US Dollar.
.. code-block:: python
# You can choose to have prefix or postfix or both.
# Prefix and postfix are not mutally exclusive.
OPENSTACK_CLOUDKITTY_RATE_PREFIX = '$'
OPENSTACK_CLOUDKITTY_RATE_POSTFIX = 'USD'
Some symbols (Such as Non-ASCII) might require to use unicode value directly.
.. code-block:: python
# British Pound
OPENSTACK_CLOUDKITTY_RATE_PREFIX = u'\xA3'
OPENSTACK_CLOUDKITTY_RATE_POSTFIX = 'GBP'
cloudkitty-dashboard-19.0.0/doc/source/readme.rst 0000664 0000000 0000000 00000000036 14617051670 0021763 0 ustar 00root root 0000000 0000000 .. include:: ../../README.rst
cloudkitty-dashboard-19.0.0/releasenotes/ 0000775 0000000 0000000 00000000000 14617051670 0020421 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/notes/ 0000775 0000000 0000000 00000000000 14617051670 0021551 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/notes/.placeholder 0000664 0000000 0000000 00000000000 14617051670 0024022 0 ustar 00root root 0000000 0000000 add-options-to-attach-pre-post-fix-to-rate-value-2d78f5cb7c289445.yaml 0000664 0000000 0000000 00000000732 14617051670 0035655 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/notes ---
features:
- |
Adds optional Horizon settings variable OPENSTACK_CLOUDKITTY_RATE_PREFIX
and OPENSTACK_CLOUDKITTY_RATE_POSTFIX. These allow users to attach
pre/postfix to their rate vaules shown at the dashboard such as currency.
These values can be set in ``.py`` settings snippets under
``openstack_dashboard/local/local_settings.d`` directory. Follow
https://docs.openstack.org/horizon/latest/configuration/settings.html
for more details.
cloudkitty-dashboard-19.0.0/releasenotes/notes/create-instance-override-8a2cfc152365d5d5.yaml 0000664 0000000 0000000 00000000230 14617051670 0031526 0 ustar 00root root 0000000 0000000 ---
fixes:
- |
Fixes compatibility with Horizon 21.0.0 and newer following the removal of
the Django-based implementation of launch instance.
cloudkitty-dashboard-19.0.0/releasenotes/notes/drop-py-2-7-c62eb5a7f3eb4d30.yaml 0000664 0000000 0000000 00000000341 14617051670 0026677 0 ustar 00root root 0000000 0000000 ---
upgrade:
- |
Python 2.7 support has been dropped. Last release of cloudkitty-dashboard
to support py2.7 is OpenStack Train. The minimum version of Python now
supported by cloudkitty-dashboard is Python 3.6.
cloudkitty-dashboard-19.0.0/releasenotes/notes/improve-total-tab-96df42d1e562fc4f.yaml 0000664 0000000 0000000 00000001041 14617051670 0030305 0 ustar 00root root 0000000 0000000 ---
features:
- |
An "Admin/Rating Summary" tab has been added. An admin user can now have
the cost of every rated tenant at once. By clicking on a tenant, a
per-resource total for the given tenant can be obtained (this view is
similar to the "Project/Rating" tab). A per-resource total for the whole
cloud is also available.
upgrade:
- |
The "Project/Rating" tab has been improved: it does now provide a total
by metric type. This make use of the /summary endpoint instead of /total
(/total is deprecated).
cloudkitty-dashboard-19.0.0/releasenotes/notes/keystone-endpoint-type-b5646c052e65c848.yaml 0000664 0000000 0000000 00000000264 14617051670 0031162 0 ustar 00root root 0000000 0000000 ---
upgrade:
- |
The CloudKitty dashboard now inherits the interface type from Horizon.
This allows for easier testing, like in an all-in-one to use the
internalURL.
cloudkitty-dashboard-19.0.0/releasenotes/notes/project-rating-v2-a96436c0f59da133.yaml 0000664 0000000 0000000 00000000152 14617051670 0030045 0 ustar 00root root 0000000 0000000 ---
other:
- |
The ratings panel in the project dashboard has been converted
to use the v2 API.
cloudkitty-dashboard-19.0.0/releasenotes/notes/rework-reporting-tab-99cd8a8574911e09.yaml 0000664 0000000 0000000 00000000313 14617051670 0030616 0 ustar 00root root 0000000 0000000 ---
upgrade:
- |
The "reporting" tab has been reworked and the dashboard does not require
D3pie anymore. The colors between the charts are now consistent and a
color legend has been added.
unstack-cost-per-service-per-hour-eff50255d66029c8.yaml 0000664 0000000 0000000 00000000144 14617051670 0033121 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/notes ---
upgrade:
- |
The "Cost Per Service Per Hour" graph no longer stacks series on the Y axis.
cloudkitty-dashboard-19.0.0/releasenotes/notes/update-predictive-pricing-dc97030d8898af0d.yaml 0000664 0000000 0000000 00000000356 14617051670 0031737 0 ustar 00root root 0000000 0000000 ---
features:
- |
The predictive pricing has been updated. It is now possible to specify the
HashMap service to use for predictive pricing in Horizon's configuration
file through the ``CLOUDKITTY_QUOTATION_SERVICE`` option.
cloudkitty-dashboard-19.0.0/releasenotes/source/ 0000775 0000000 0000000 00000000000 14617051670 0021721 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/2023.1.rst 0000664 0000000 0000000 00000000202 14617051670 0023172 0 ustar 00root root 0000000 0000000 ===========================
2023.1 Series Release Notes
===========================
.. release-notes::
:branch: stable/2023.1
cloudkitty-dashboard-19.0.0/releasenotes/source/2023.2.rst 0000664 0000000 0000000 00000000202 14617051670 0023173 0 ustar 00root root 0000000 0000000 ===========================
2023.2 Series Release Notes
===========================
.. release-notes::
:branch: stable/2023.2
cloudkitty-dashboard-19.0.0/releasenotes/source/2024.1.rst 0000664 0000000 0000000 00000000202 14617051670 0023173 0 ustar 00root root 0000000 0000000 ===========================
2024.1 Series Release Notes
===========================
.. release-notes::
:branch: stable/2024.1
cloudkitty-dashboard-19.0.0/releasenotes/source/_static/ 0000775 0000000 0000000 00000000000 14617051670 0023347 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/_static/.placeholder 0000664 0000000 0000000 00000000000 14617051670 0025620 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/_templates/ 0000775 0000000 0000000 00000000000 14617051670 0024056 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/_templates/.placeholder 0000664 0000000 0000000 00000000000 14617051670 0026327 0 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/conf.py 0000664 0000000 0000000 00000020241 14617051670 0023217 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
#
# Cloudkitty Release Notes documentation build configuration file.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'openstackdocstheme',
'reno.sphinxext',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'Cloudkitty Dashboard Release Notes'
copyright = '2016, Cloudkitty developers'
# Release notes are version independent
# The short X.Y version.
version = ''
# The full version, including alpha/beta/rc tags.
release = ''
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'native'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'openstackdocs'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'CloudkittyDashboardReleaseNotestdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'PythonCloudkitty.tex', 'Cloudkitty Release Notes Documentation',
'Cloudkitty developers', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'cloudkittydashboard',
'Cloudkitty Dashboard Release Notes Documentation',
['Cloudkitty developers'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'CloudkittyDashboard',
'Cloudkitty Dashboard Release Notes Documentation',
'Cloudkitty Dashboard developers', 'CloudkittyDashboard',
'One line description of project.', 'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# -- Options for openstackdocstheme -------------------------------------------
openstackdocs_repo_name = 'openstack/cloudkitty-dashboard'
openstackdocs_auto_name = False
openstackdocs_use_storyboard = True
# -- Options for Internationalization output ------------------------------
locale_dirs = ['locale/']
cloudkitty-dashboard-19.0.0/releasenotes/source/index.rst 0000664 0000000 0000000 00000000534 14617051670 0023564 0 ustar 00root root 0000000 0000000 CloudKitty Dashboard Release Notes
==================================
Contents
========
.. toctree::
:maxdepth: 2
unreleased
2024.1
2023.2
2023.1
zed
yoga
xena
wallaby
victoria
ussuri
train
stein
rocky
queens
pike
ocata
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`
cloudkitty-dashboard-19.0.0/releasenotes/source/locale/ 0000775 0000000 0000000 00000000000 14617051670 0023160 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/locale/de/ 0000775 0000000 0000000 00000000000 14617051670 0023550 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/locale/de/LC_MESSAGES/ 0000775 0000000 0000000 00000000000 14617051670 0025335 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/locale/de/LC_MESSAGES/releasenotes.po 0000664 0000000 0000000 00000003576 14617051670 0030401 0 ustar 00root root 0000000 0000000 # Andreas Jaeger , 2018. #zanata
# Andreas Jaeger , 2019. #zanata
# Andreas Jaeger , 2020. #zanata
msgid ""
msgstr ""
"Project-Id-Version: Cloudkitty Dashboard Release Notes\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-05-11 10:25+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2020-04-25 09:14+0000\n"
"Last-Translator: Andreas Jaeger \n"
"Language-Team: German\n"
"Language: de\n"
"X-Generator: Zanata 4.3.3\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
msgid "8.1.0"
msgstr "8.1.0"
msgid ":ref:`genindex`"
msgstr ":ref:`genindex`"
msgid ":ref:`search`"
msgstr ":ref:`search`"
msgid "Contents"
msgstr "Inhalt"
msgid "Current Series Release Notes"
msgstr "Aktuelle Serie Releasenotes"
msgid "Indices and tables"
msgstr "Indizes und Tabellen"
msgid "New Features"
msgstr "Neue Funktionen"
msgid "Ocata Series Release Notes"
msgstr "Ocata Serie Releasenotes"
msgid "Pike Series Release Notes"
msgstr "Pike Serie Releasenotes"
msgid ""
"Python 2.7 support has been dropped. Last release of cloudkitty-dashboard to "
"support py2.7 is OpenStack Train. The minimum version of Python now "
"supported by cloudkitty-dashboard is Python 3.6."
msgstr ""
"Python 2.7 Unterstützung wurde beendet. Der letzte Release von cloudkitty-"
"dashboard welcher Python 2.7 unterstützt ist OpenStack Train. Die minimal "
"Python Version welche von cloudkitty-dashboard unterstützt wird ist Python "
"3.6."
msgid "Queens Series Release Notes"
msgstr "Queens Serie Releasenotes"
msgid "Rocky Series Release Notes"
msgstr "Rocky Serie Releasenotes"
msgid "Stein Series Release Notes"
msgstr "Stein Serie Releasenotes"
msgid "Train Series Release Notes"
msgstr "Train Serie Releasenotes"
msgid "Upgrade Notes"
msgstr "Aktualisierungsnotizen"
cloudkitty-dashboard-19.0.0/releasenotes/source/locale/en_GB/ 0000775 0000000 0000000 00000000000 14617051670 0024132 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/ 0000775 0000000 0000000 00000000000 14617051670 0025717 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po 0000664 0000000 0000000 00000015447 14617051670 0030763 0 ustar 00root root 0000000 0000000 # Andi Chandler , 2018. #zanata
# Andi Chandler , 2019. #zanata
# Andi Chandler , 2020. #zanata
# Andi Chandler , 2021. #zanata
# Andi Chandler , 2022. #zanata
# Andi Chandler , 2023. #zanata
# Andi Chandler , 2024. #zanata
msgid ""
msgstr ""
"Project-Id-Version: Cloudkitty Dashboard Release Notes\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-29 10:44+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2024-05-08 01:30+0000\n"
"Last-Translator: Andi Chandler \n"
"Language-Team: English (United Kingdom)\n"
"Language: en_GB\n"
"X-Generator: Zanata 4.3.3\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
msgid "10.0.0"
msgstr "10.0.0"
msgid "11.0.1"
msgstr "11.0.1"
msgid "12.0.0"
msgstr "12.0.0"
msgid "12.0.0-3"
msgstr "12.0.0-3"
msgid "13.0.0"
msgstr "13.0.0"
msgid "14.0.1"
msgstr "14.0.1"
msgid "15.0.0"
msgstr "15.0.0"
msgid "18.0.0-7"
msgstr "18.0.0-7"
msgid "2023.1 Series Release Notes"
msgstr "2023.1 Series Release Notes"
msgid "2023.2 Series Release Notes"
msgstr "2023.2 Series Release Notes"
msgid "2024.1 Series Release Notes"
msgstr "2024.1 Series Release Notes"
msgid "8.1.0"
msgstr "8.1.0"
msgid ":ref:`genindex`"
msgstr ":ref:`genindex`"
msgid ":ref:`search`"
msgstr ":ref:`search`"
msgid ""
"Adds optional Horizon settings variable OPENSTACK_CLOUDKITTY_RATE_PREFIX and "
"OPENSTACK_CLOUDKITTY_RATE_POSTFIX. These allow users to attach pre/postfix "
"to their rate vaules shown at the dashboard such as currency. These values "
"can be set in ``.py`` settings snippets under ``openstack_dashboard/local/"
"local_settings.d`` directory. Follow https://docs.openstack.org/horizon/"
"latest/configuration/settings.html for more details."
msgstr ""
"Adds optional Horizon settings variable OPENSTACK_CLOUDKITTY_RATE_PREFIX and "
"OPENSTACK_CLOUDKITTY_RATE_POSTFIX. These allow users to attach pre/postfix "
"to their rate vaules shown at the dashboard such as currency. These values "
"can be set in ``.py`` settings snippets under ``openstack_dashboard/local/"
"local_settings.d`` directory. Follow https://docs.openstack.org/horizon/"
"latest/configuration/settings.html for more details."
msgid ""
"An \"Admin/Rating Summary\" tab has been added. An admin user can now have "
"the cost of every rated tenant at once. By clicking on a tenant, a per-"
"resource total for the given tenant can be obtained (this view is similar to "
"the \"Project/Rating\" tab). A per-resource total for the whole cloud is "
"also available."
msgstr ""
"An \"Admin/Rating Summary\" tab has been added. An admin user can now have "
"the cost of every rated tenant at once. By clicking on a tenant, a per-"
"resource total for the given tenant can be obtained (this view is similar to "
"the \"Project/Rating\" tab). A per-resource total for the whole cloud is "
"also available."
msgid "Bug Fixes"
msgstr "Bug Fixes"
msgid "CloudKitty Dashboard Release Notes"
msgstr "CloudKitty Dashboard Release Notes"
msgid "Contents"
msgstr "Contents"
msgid "Current Series Release Notes"
msgstr "Current Series Release Notes"
msgid ""
"Fixes compatibility with Horizon 21.0.0 and newer following the removal of "
"the Django-based implementation of launch instance."
msgstr ""
"Fixes compatibility with Horizon 21.0.0 and newer following the removal of "
"the Django-based implementation of launch instance."
msgid "Indices and tables"
msgstr "Indices and tables"
msgid "New Features"
msgstr "New Features"
msgid "Ocata Series Release Notes"
msgstr "Ocata Series Release Notes"
msgid "Other Notes"
msgstr "Other Notes"
msgid "Pike Series Release Notes"
msgstr "Pike Series Release Notes"
msgid ""
"Python 2.7 support has been dropped. Last release of cloudkitty-dashboard to "
"support py2.7 is OpenStack Train. The minimum version of Python now "
"supported by cloudkitty-dashboard is Python 3.6."
msgstr ""
"Python 2.7 support has been dropped. Last release of cloudkitty-dashboard to "
"support py2.7 is OpenStack Train. The minimum version of Python now "
"supported by cloudkitty-dashboard is Python 3.6."
msgid "Queens Series Release Notes"
msgstr "Queens Series Release Notes"
msgid "Rocky Series Release Notes"
msgstr "Rocky Series Release Notes"
msgid "Stein Series Release Notes"
msgstr "Stein Series Release Notes"
msgid ""
"The \"Cost Per Service Per Hour\" graph no longer stacks series on the Y "
"axis."
msgstr ""
"The \"Cost Per Service Per Hour\" graph no longer stacks series on the Y "
"axis."
msgid ""
"The \"Project/Rating\" tab has been improved: it does now provide a total by "
"metric type. This make use of the /summary endpoint instead of /total (/"
"total is deprecated)."
msgstr ""
"The \"Project/Rating\" tab has been improved: it does now provide a total by "
"metric type. This make use of the /summary endpoint instead of /total (/"
"total is deprecated)."
msgid ""
"The \"reporting\" tab has been reworked and the dashboard does not require "
"D3pie anymore. The colors between the charts are now consistent and a color "
"legend has been added."
msgstr ""
"The \"reporting\" tab has been reworked and the dashboard does not require "
"D3pie anymore. The colors between the charts are now consistent and a colour "
"legend has been added."
msgid ""
"The CloudKitty dashboard now inherits the interface type from Horizon. This "
"allows for easier testing, like in an all-in-one to use the internalURL."
msgstr ""
"The CloudKitty dashboard now inherits the interface type from Horizon. This "
"allows for easier testing, like in an all-in-one to use the internalURL."
msgid ""
"The predictive pricing has been updated. It is now possible to specify the "
"HashMap service to use for predictive pricing in Horizon's configuration "
"file through the ``CLOUDKITTY_QUOTATION_SERVICE`` option."
msgstr ""
"The predictive pricing has been updated. It is now possible to specify the "
"HashMap service to use for predictive pricing in Horizon's configuration "
"file through the ``CLOUDKITTY_QUOTATION_SERVICE`` option."
msgid ""
"The ratings panel in the project dashboard has been converted to use the v2 "
"API."
msgstr ""
"The ratings panel in the project dashboard has been converted to use the v2 "
"API."
msgid "Train Series Release Notes"
msgstr "Train Series Release Notes"
msgid "Upgrade Notes"
msgstr "Upgrade Notes"
msgid "Ussuri Series Release Notes"
msgstr "Ussuri Series Release Notes"
msgid "Victoria Series Release Notes"
msgstr "Victoria Series Release Notes"
msgid "Wallaby Series Release Notes"
msgstr "Wallaby Series Release Notes"
msgid "Xena Series Release Notes"
msgstr "Xena Series Release Notes"
msgid "Yoga Series Release Notes"
msgstr "Yoga Series Release Notes"
msgid "Zed Series Release Notes"
msgstr "Zed Series Release Notes"
cloudkitty-dashboard-19.0.0/releasenotes/source/locale/fr/ 0000775 0000000 0000000 00000000000 14617051670 0023567 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/locale/fr/LC_MESSAGES/ 0000775 0000000 0000000 00000000000 14617051670 0025354 5 ustar 00root root 0000000 0000000 cloudkitty-dashboard-19.0.0/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po 0000664 0000000 0000000 00000002175 14617051670 0030412 0 ustar 00root root 0000000 0000000 # François Magimel , 2018. #zanata
msgid ""
msgstr ""
"Project-Id-Version: Cloudkitty Dashboard Release Notes\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-05-11 10:25+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2018-12-26 11:44+0000\n"
"Last-Translator: François Magimel \n"
"Language-Team: French\n"
"Language: fr\n"
"X-Generator: Zanata 4.3.3\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
msgid ":ref:`genindex`"
msgstr ":ref:`genindex`"
msgid ":ref:`search`"
msgstr ":ref:`search`"
msgid "Contents"
msgstr "Contenu"
msgid "Indices and tables"
msgstr "Index et table des matières"
msgid "New Features"
msgstr "Nouvelles fonctionnalités"
msgid "Ocata Series Release Notes"
msgstr "Notes de version pour Ocata "
msgid "Pike Series Release Notes"
msgstr "Notes de version pour Pike"
msgid "Queens Series Release Notes"
msgstr "Notes de version pour Queens"
msgid "Rocky Series Release Notes"
msgstr "Notes de version pour Rocky"
msgid "Upgrade Notes"
msgstr "Notes de mises à jour"
cloudkitty-dashboard-19.0.0/releasenotes/source/ocata.rst 0000664 0000000 0000000 00000000205 14617051670 0023537 0 ustar 00root root 0000000 0000000 ==========================
Ocata Series Release Notes
==========================
.. release-notes::
:branch: origin/stable/ocata
cloudkitty-dashboard-19.0.0/releasenotes/source/pike.rst 0000664 0000000 0000000 00000000201 14617051670 0023374 0 ustar 00root root 0000000 0000000 =========================
Pike Series Release Notes
=========================
.. release-notes::
:branch: origin/stable/pike
cloudkitty-dashboard-19.0.0/releasenotes/source/queens.rst 0000664 0000000 0000000 00000000202 14617051670 0023745 0 ustar 00root root 0000000 0000000 ===========================
Queens Series Release Notes
===========================
.. release-notes::
:branch: stable/queens
cloudkitty-dashboard-19.0.0/releasenotes/source/rocky.rst 0000664 0000000 0000000 00000000221 14617051670 0023575 0 ustar 00root root 0000000 0000000 ===================================
Rocky Series Release Notes
===================================
.. release-notes::
:branch: stable/rocky
cloudkitty-dashboard-19.0.0/releasenotes/source/stein.rst 0000664 0000000 0000000 00000000176 14617051670 0023601 0 ustar 00root root 0000000 0000000 ==========================
Stein Series Release Notes
==========================
.. release-notes::
:branch: stable/stein
cloudkitty-dashboard-19.0.0/releasenotes/source/train.rst 0000664 0000000 0000000 00000000176 14617051670 0023574 0 ustar 00root root 0000000 0000000 ==========================
Train Series Release Notes
==========================
.. release-notes::
:branch: stable/train
cloudkitty-dashboard-19.0.0/releasenotes/source/unreleased.rst 0000664 0000000 0000000 00000000153 14617051670 0024601 0 ustar 00root root 0000000 0000000 ============================
Current Series Release Notes
============================
.. release-notes::
cloudkitty-dashboard-19.0.0/releasenotes/source/ussuri.rst 0000664 0000000 0000000 00000000202 14617051670 0023777 0 ustar 00root root 0000000 0000000 ===========================
Ussuri Series Release Notes
===========================
.. release-notes::
:branch: stable/ussuri
cloudkitty-dashboard-19.0.0/releasenotes/source/victoria.rst 0000664 0000000 0000000 00000000220 14617051670 0024265 0 ustar 00root root 0000000 0000000 =============================
Victoria Series Release Notes
=============================
.. release-notes::
:branch: unmaintained/victoria
cloudkitty-dashboard-19.0.0/releasenotes/source/wallaby.rst 0000664 0000000 0000000 00000000214 14617051670 0024103 0 ustar 00root root 0000000 0000000 ============================
Wallaby Series Release Notes
============================
.. release-notes::
:branch: unmaintained/wallaby
cloudkitty-dashboard-19.0.0/releasenotes/source/xena.rst 0000664 0000000 0000000 00000000200 14617051670 0023376 0 ustar 00root root 0000000 0000000 =========================
Xena Series Release Notes
=========================
.. release-notes::
:branch: unmaintained/xena
cloudkitty-dashboard-19.0.0/releasenotes/source/yoga.rst 0000664 0000000 0000000 00000000200 14617051670 0023402 0 ustar 00root root 0000000 0000000 =========================
Yoga Series Release Notes
=========================
.. release-notes::
:branch: unmaintained/yoga
cloudkitty-dashboard-19.0.0/releasenotes/source/zed.rst 0000664 0000000 0000000 00000000174 14617051670 0023237 0 ustar 00root root 0000000 0000000 ========================
Zed Series Release Notes
========================
.. release-notes::
:branch: unmaintained/zed
cloudkitty-dashboard-19.0.0/requirements.txt 0000664 0000000 0000000 00000000531 14617051670 0021213 0 ustar 00root root 0000000 0000000 # The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr!= 2.1.0,>=2.0.0
Babel>=1.3
python-cloudkittyclient>=0.5.0
horizon>=17.1.0 # Apache-2.0
XStatic-D3>=3.5.17.0
XStatic-Rickshaw>=1.5
cloudkitty-dashboard-19.0.0/setup.cfg 0000664 0000000 0000000 00000001511 14617051670 0017547 0 ustar 00root root 0000000 0000000 [metadata]
name = cloudkitty-dashboard
summary = CloudKitty Horizon dashboard
description_file =
README.rst
author = OpenStack
author_email = openstack-discuss@lists.openstack.org
home_page = https://docs.openstack.org/cloudkitty-dashboard/latest/
python_requires = >=3.8
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
[files]
packages =
cloudkittydashboard
[upload_sphinx]
upload-dir = doc/build/html
cloudkitty-dashboard-19.0.0/setup.py 0000664 0000000 0000000 00000001267 14617051670 0017450 0 ustar 00root root 0000000 0000000 # Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# 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.
import setuptools
setuptools.setup(
setup_requires=['pbr>=1.8'],
pbr=True)
cloudkitty-dashboard-19.0.0/test-requirements.txt 0000664 0000000 0000000 00000001026 14617051670 0022170 0 ustar 00root root 0000000 0000000 # The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
hacking>=3.0.1,<3.1.0 # Apache-2.0
# remove this pyflakes from here once you bump the
# hacking to 3.2.0 or above. hacking 3.2.0 takes
# care of pyflakes version compatibilty.
pyflakes>=2.1.1
coverage>=3.6
python-subunit>=0.0.18
oslotest>=1.10.0 # Apache-2.0
stestr>=2.0.0 # Apache-2.0
testscenarios>=0.4
testtools>=1.4.0
cloudkitty-dashboard-19.0.0/tox.ini 0000664 0000000 0000000 00000003033 14617051670 0017242 0 ustar 00root root 0000000 0000000 [tox]
minversion = 3.18.0
envlist = py3,pep8
skipsdist = True
ignore_basepython_conflict = True
[testenv]
basepython = python3
usedevelop = True
install_command = pip install -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -U {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
stestr run --slowest {posargs}
[testenv:pep8]
commands = flake8
[testenv:venv]
commands = {posargs}
[testenv:cover]
setenv =
{[testenv]setenv}
PYTHON=coverage run --source cloudkittydashboard --parallel-mode
commands =
stestr run {posargs}
coverage combine
coverage html -d cover
coverage xml -o cover/coverage.xml
[testenv:docs]
deps = -r{toxinidir}/doc/requirements.txt
commands = sphinx-build -W --keep-going -b html doc/source doc/build/html
[testenv:pdf-docs]
envdir = {toxworkdir}/docs
deps = {[testenv:docs]deps}
allowlist_externals =
make
commands =
sphinx-build -W --keep-going -b latex doc/source doc/build/pdf
make -C doc/build/pdf
[testenv:debug]
commands = oslo_debug_helper -t cloudkittydashboard/tests {posargs}
[flake8]
# E123, E125 skipped as they are invalid PEP-8.
show-source = True
ignore = E123,E125
builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,.ropeproject,releasenotes
[testenv:releasenotes]
deps = -r{toxinidir}/doc/requirements.txt
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html