pax_global_header00006660000000000000000000000064146170516700014521gustar00rootroot0000000000000052 comment=e7bf46d8a47041be6bc7daa50ba42efab7936db8 cloudkitty-dashboard-19.0.0/000077500000000000000000000000001461705167000157305ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/.coveragerc000066400000000000000000000001201461705167000200420ustar00rootroot00000000000000[run] branch = True source = cloudkittydashboard [report] ignore_errors = True cloudkitty-dashboard-19.0.0/.gitignore000066400000000000000000000002211461705167000177130ustar00rootroot00000000000000*.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/.gitreview000066400000000000000000000001271461705167000177360ustar00rootroot00000000000000[gerrit] host=review.opendev.org port=29418 project=openstack/cloudkitty-dashboard.git cloudkitty-dashboard-19.0.0/.stestr.conf000066400000000000000000000000731461705167000202010ustar00rootroot00000000000000[DEFAULT] test_path=./cloudkittydashboard/tests top_dir=./ cloudkitty-dashboard-19.0.0/.zuul.yaml000066400000000000000000000003021461705167000176640ustar00rootroot00000000000000- 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.rst000066400000000000000000000012221461705167000203660ustar00rootroot00000000000000The 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.rst000066400000000000000000000002421461705167000175240ustar00rootroot00000000000000cloudkitty-dashboard Style Commandments ======================================= Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ cloudkitty-dashboard-19.0.0/LICENSE000066400000000000000000000236371461705167000167500ustar00rootroot00000000000000 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.rst000066400000000000000000000024711461705167000174230ustar00rootroot00000000000000==================== 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.cfg000066400000000000000000000001151461705167000207130ustar00rootroot00000000000000[python: **.py] [django: **/templates/**.html] [django: **/templates/**.csv] cloudkitty-dashboard-19.0.0/babel-djangojs.cfg000066400000000000000000000000611461705167000212500ustar00rootroot00000000000000[javascript: **.js] [angular: **/static/**.html] cloudkitty-dashboard-19.0.0/cloudkittydashboard/000077500000000000000000000000001461705167000217735ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/__init__.py000066400000000000000000000012501461705167000241020ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000225445ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/api/__init__.py000066400000000000000000000000001461705167000246430ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/api/cloudkitty.py000066400000000000000000000042601461705167000253130ustar00rootroot00000000000000# -*- 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/000077500000000000000000000000001461705167000241055ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/__init__.py000066400000000000000000000000001461705167000262040ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/000077500000000000000000000000001461705167000251755ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/__init__.py000066400000000000000000000000001461705167000272740ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/000077500000000000000000000000001461705167000266165ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/__init__.py000066400000000000000000000000001461705167000307150ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/forms.py000066400000000000000000000274531461705167000303310ustar00rootroot00000000000000# 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.py000066400000000000000000000013631461705167000302720ustar00rootroot00000000000000# 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.py000066400000000000000000000412751461705167000304530ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000306145ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap/000077500000000000000000000000001461705167000322355ustar00rootroot00000000000000_field_create.html000066400000000000000000000003471461705167000356150ustar00rootroot00000000000000cloudkitty-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. " %}

{% endblock %} _group_create.html000066400000000000000000000003421461705167000356610ustar00rootroot00000000000000cloudkitty-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 group is a way to group calculations of mappings." %}

{% endblock %} _mapping_create.html000066400000000000000000000003571461705167000361660ustar00rootroot00000000000000cloudkitty-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 mapping is the final object, it’s what triggers calculation." %}

{% endblock %} _service_create.html000066400000000000000000000003601461705167000361650ustar00rootroot00000000000000cloudkitty-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 service is a way to map the rule to the type of data collected." %}

{% endblock %} _threshold_create.html000066400000000000000000000005061461705167000365230ustar00rootroot00000000000000cloudkitty-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 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 %} field_create.html000066400000000000000000000002001461705167000354420ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap{% extends 'base.html' %} {% load i18n %} {% block main %} {% include 'admin/hashmap/_field_create.html' %} {% endblock %} field_details.html000066400000000000000000000003301461705167000356300ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Hashmap" %}{% endblock %} {% block main %}
{{ tab_group.render }}
{% endblock %} group_create.html000066400000000000000000000002001461705167000355130ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap{% extends 'base.html' %} {% load i18n %} {% block main %} {% include 'admin/hashmap/_group_create.html' %} {% endblock %} group_details.html000066400000000000000000000066051461705167000357140ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap{% extends 'base.html' %} {% load i18n sizeformat %} {% block title %}{% trans "Group Details" %}{% endblock %} {% block main %}
{% trans "Id" %}
{{ group.group_id }}
{% trans "Name" %}
{{ group.name }}

{% if mappings %}

{% trans "Mappings" %}

{% if mappings.services %}

{% trans "Services" %}

{% for service in mappings.services %}
{% trans "Mapping ID" %}
{{ service.mapping_id }}
{% trans "Service ID" %}
{{ service.service_id }}
{% trans "Cost" %}
{{ service.cost }}
{% trans "Type" %}
{{ service.type }}
{% endfor %} {% endif %} {% if mappings.fields %}

{% trans "Fields" %}

{% for field in mappings.fields %}
{% trans "Mapping ID" %}
{{ field.mapping_id }}
{% trans "Field ID" %}
{{ field.field_id }}
{% trans "Cost" %}
{{ field.cost }}
{% trans "Value" %}
{{ field.value }}
{% trans "Type" %}
{{ field.type }}
{% endfor %} {% endif %} {% endif %} {% if thresholds %}

{% trans "Thresholds" %}

{% if thresholds.services %}

{% trans "Services" %}

{% for service in thresholds.services %}
{% trans "Service ID" %}
{{ service.service_id }}
{% trans "Level" %}
{{ service.level }}
{% trans "Cost" %}
{{ service.cost }}
{% trans "Type" %}
{{ service.type }}
{% endfor %} {% endif %} {% if thresholds.fields %}

{% trans "Fields" %}

{% for field in thresholds.fields %}
{% trans "Field ID" %}
{{ field.field_id }}
{% trans "Level" %}
{{ field.level }}
{% trans "Cost" %}
{{ field.cost }}
{% trans "Value" %}
{{ field.value }}
{% trans "Type" %}
{{ field.type }}
{% endfor %} {% endif %} {% endif %}
{% endblock %} mapping_create.html000066400000000000000000000002021461705167000360140ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap{% extends 'base.html' %} {% load i18n %} {% block main %} {% include 'admin/hashmap/_mapping_create.html' %} {% endblock %} service_create.html000066400000000000000000000002021461705167000360210ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap{% extends 'base.html' %} {% load i18n %} {% block main %} {% include 'admin/hashmap/_service_create.html' %} {% endblock %} service_details.html000066400000000000000000000006041461705167000362110ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Hashmap" %}{% endblock %} {% block main %}
{% if service_period %} {% blocktrans with service_period as period %}

Usage data are collected every {{ period }} seconds.

{% endblocktrans %} {% endif %} {{ tab_group.render }}
{% endblock %} services_list.html000066400000000000000000000004461461705167000357260ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Services" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("Services") %} {% endblock page_header %} {% block main %} {{ table.render }} {{ modules }} {% endblock %} threshold_create.html000066400000000000000000000002041461705167000363570ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/templates/hashmap{% extends 'base.html' %} {% load i18n %} {% block main %} {% include 'admin/hashmap/_threshold_create.html' %} {% endblock %} cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/tests.py000066400000000000000000000014121461705167000303300ustar00rootroot00000000000000# 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/hashmap/urls.py000066400000000000000000000055741461705167000301700ustar00rootroot00000000000000# 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.hashmap import views urlpatterns = [ re_path(r'^$', views.IndexView.as_view(), name='index'), re_path(r'^service/(?P[^/]+)/?$', views.ServiceView.as_view(), name='service'), re_path(r'^create_service/?$', views.ServiceCreateView.as_view(), name='service_create'), re_path(r'^field/(?P[^/]+)/?$', views.FieldView.as_view(), name='field'), re_path(r'^group/(?P[^/]+)/?$', views.GroupView.as_view(), name='group'), re_path(r'^group/(?P[^/]+)/details/?$', views.GroupDetailsView.as_view(), name='group_details'), re_path(r'^create_field/service/(?P[^/]+)/?$', views.FieldCreateView.as_view(), name='field_create'), re_path(r'^create_group/(?:(?P[^/]+)/)?$', views.GroupCreateView.as_view(), name='group_create'), re_path(r'^create_threshold/service/(?P[^/]+)/?$', views.ServiceThresholdCreateView.as_view(), name='service_threshold_create'), re_path(r'^create_threshold/field/(?P[^/]+)/?$', views.FieldThresholdCreateView.as_view(), name='field_threshold_create'), re_path(r'^create_mapping/service/(?P[^/]+)/?$', views.ServiceMappingCreateView.as_view(), name='service_mapping_create'), re_path(r'^create_mapping/field/(?P[^/]+)/?$', views.FieldMappingCreateView.as_view(), name='field_mapping_create'), re_path(r'^edit_mapping/service/(?P[^/]+)/?$', views.ServiceMappingEditView.as_view(), name='service_mapping_edit'), re_path(r'^edit_mapping/field/(?P[^/]+)/?$', views.FieldMappingEditView.as_view(), name='field_mapping_edit'), re_path(r'^edit_threshold/service/(?P[^/]+)/?$', views.ServiceThresholdEditView.as_view(), name='service_threshold_edit'), re_path(r'^edit_threshold/field/(?P[^/]+)/?$', views.FieldThresholdEditView.as_view(), name='field_threshold_edit'), ] cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/hashmap/views.py000066400000000000000000000377411461705167000303410ustar00rootroot00000000000000# 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 tabs from horizon import views from keystoneauth1 import exceptions from cloudkittydashboard.api import cloudkitty as api from cloudkittydashboard.dashboards.admin.hashmap import forms as hashmap_forms from cloudkittydashboard.dashboards.admin.hashmap \ import tables as hashmap_tables class IndexView(tables.DataTableView): table_class = hashmap_tables.ServicesTable template_name = "admin/hashmap/services_list.html" def get_data(self): manager = api.cloudkittyclient(self.request) services = manager.rating.hashmap.get_service().get('services', []) services = sorted(services, key=lambda service: service['name']) list_services = [] for s in services: try: service = manager.info.get_metric(metric_name=s['name']) unit = service['unit'] except exceptions.NotFound: unit = "-" list_services.append({ "id": s['service_id'], "name": s['name'], "unit": unit }) return list_services class ServiceView(tabs.TabbedTableView): tab_group_class = hashmap_tables.ServiceTabs template_name = 'admin/hashmap/service_details.html' def get(self, *args, **kwargs): service = api.cloudkittyclient( self.request).rating.hashmap.get_service( service_id=kwargs['service_id']) self.request.service_id = service['service_id'] self.page_title = "Hashmap Service : %s" % service['name'] return super(ServiceView, self).get(*args, **kwargs) def get_context_data(self, **kwargs): context = super(ServiceView, self).get_context_data(**kwargs) manager = api.cloudkittyclient(self.request) service = manager.rating.hashmap.get_service( service_id=kwargs['service_id']) config = manager.info.get_config() period = None if service['name'] in config['metrics'].keys(): period = config.get('period', 3600) context["service_period"] = period return context class ServiceCreateView(forms.ModalFormView): form_class = hashmap_forms.CreateServiceForm form_id = "create_service" modal_header = _("Create Service") page_title = _("Create Service") success_url = reverse_lazy('horizon:admin:hashmap:index') submit_url = reverse_lazy('horizon:admin:hashmap:service_create') template_name = 'admin/hashmap/service_create.html' def get_object_id(self, obj): return obj.service_id class FieldView(tabs.TabbedTableView): tab_group_class = hashmap_tables.FieldTabs template_name = 'admin/hashmap/field_details.html' def get(self, *args, **kwargs): field = api.cloudkittyclient(self.request).rating.hashmap.get_field( field_id=kwargs['field_id']) self.request.field_id = field['field_id'] self.page_title = "Hashmap Field : %s" % field['name'] return super(FieldView, self).get(*args, **kwargs) class FieldCreateView(forms.ModalFormView): form_class = hashmap_forms.CreateFieldForm form_id = "create_field" modal_header = _("Create Field") page_title = _("Create Field") template_name = 'admin/hashmap/field_create.html' success_url = 'horizon:admin:hashmap:service' submit_url = 'horizon:admin:hashmap:field_create' def get_object_id(self, obj): return obj.field_id def get_context_data(self, **kwargs): context = super(FieldCreateView, self).get_context_data(**kwargs) context["service_id"] = self.kwargs['service_id'] args = (self.kwargs['service_id'],) context['submit_url'] = reverse_lazy(self.submit_url, args=args) return context def get_initial(self): return {"service_id": self.kwargs["service_id"]} def get_success_url(self, **kwargs): args = (self.kwargs['service_id'],) return reverse_lazy(self.success_url, args=args) class ServiceMappingCreateView(forms.ModalFormView): form_class = hashmap_forms.CreateServiceMappingForm form_id = "create_mapping" modal_header = _("Create Mapping") page_title = _("Create Mapping") template_name = 'admin/hashmap/mapping_create.html' success_url = 'horizon:admin:hashmap:service' submit_url = 'horizon:admin:hashmap:service_mapping_create' def get_object_id(self, obj): return obj.mapping_id def get_context_data(self, **kwargs): context = super(ServiceMappingCreateView, self).get_context_data(**kwargs) context["service_id"] = self.kwargs.get('service_id') context['submit_url'] = reverse_lazy(self.submit_url, args=(context['service_id'], )) return context def get_initial(self): return {"service_id": self.kwargs.get("service_id")} def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:service', args=(self.kwargs['service_id'],)) class ServiceMappingEditView(ServiceMappingCreateView): form_class = hashmap_forms.EditServiceMappingForm form_id = "update_mapping" modal_header = _("Update Mapping") page_title = _("Update Mapping") submit_url = 'horizon:admin:hashmap:service_mapping_edit' success_url = 'horizon:admin:hashmap:service_mapping_edit' def get_initial(self): out = api.cloudkittyclient(self.request).rating.hashmap.get_mapping( mapping_id=self.kwargs['mapping_id']) self.initial = out return self.initial def get_context_data(self, **kwargs): context = super(ServiceMappingEditView, self).get_context_data(**kwargs) context["mapping_id"] = self.kwargs.get('mapping_id') context['submit_url'] = reverse_lazy(self.submit_url, args=(context['mapping_id'], )) return context def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:service', args=(self.initial['service_id'], )) class FieldMappingCreateView(forms.ModalFormView): form_class = hashmap_forms.CreateFieldMappingForm form_id = "create_field_mapping" modal_header = _("Create Field Mapping") page_title = _("Create field Mapping") template_name = 'admin/hashmap/mapping_create.html' submit_url = 'horizon:admin:hashmap:field_mapping_create' success_url = 'horizon:admin:hashmap:field_mapping_create' def get_object_id(self, obj): return obj.mapping_id def get_context_data(self, **kwargs): context = super(FieldMappingCreateView, self).get_context_data(**kwargs) context["field_id"] = self.kwargs.get('field_id') context['submit_url'] = reverse_lazy(self.submit_url, args=(context['field_id'], )) return context def get_initial(self): return {"field_id": self.kwargs.get("field_id")} def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:field', args=(self.kwargs['field_id'], )) class FieldMappingEditView(FieldMappingCreateView): form_class = hashmap_forms.EditFieldMappingForm form_id = "update_field_mapping" modal_header = _("Update Field Mapping") page_title = _("Update Field Mapping") submit_url = 'horizon:admin:hashmap:field_mapping_edit' def get_initial(self): out = api.cloudkittyclient(self.request).rating.hashmap.get_mapping( mapping_id=self.kwargs['mapping_id']) self.initial = out return self.initial def get_context_data(self, **kwargs): context = super(FieldMappingEditView, self).get_context_data(**kwargs) context["mapping_id"] = self.kwargs.get('mapping_id') context['submit_url'] = reverse_lazy(self.submit_url, args=(context['mapping_id'], )) return context def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:field', args=(self.initial['field_id'], )) class GroupCreateView(forms.ModalFormView): form_class = hashmap_forms.CreateGroupForm form_id = "create_group" modal_header = _("Create Group") page_title = _("Create Group") template_name = 'admin/hashmap/group_create.html' submit_url = 'horizon:admin:hashmap:group_create' success_url = 'horizon:admin:hashmap:group_create' def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:service', args=(self.kwargs['service_id'],)) def get_object_id(self, obj): return obj.group_id def get_context_data(self, **kwargs): context = super(GroupCreateView, self).get_context_data(**kwargs) context["service_id"] = self.kwargs.get('service_id') context['submit_url'] = reverse_lazy(self.submit_url, args=(context['service_id'], )) return context ''' def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:group', args=(kwargs['group_id'], )) ''' class ServiceThresholdCreateView(forms.ModalFormView): form_class = hashmap_forms.CreateServiceThresholdForm form_id = "create_service_threshold" modal_header = _("Create Service Threshold") page_title = _("Create Service Threshold") template_name = 'admin/hashmap/threshold_create.html' success_url = 'horizon:admin:hashmap:service' submit_url = 'horizon:admin:hashmap:service_threshold_create' def get_object_id(self, obj): return obj.field_id def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:service', args=(self.kwargs['service_id'],)) def get_context_data(self, **kwargs): context = super(ServiceThresholdCreateView, self).get_context_data(**kwargs) context["service_id"] = self.kwargs.get('service_id') args = (context['service_id'],) context['submit_url'] = reverse_lazy(self.submit_url, args=args) return context def get_initial(self): return {"service_id": self.kwargs["service_id"]} class ServiceThresholdEditView(ServiceThresholdCreateView): form_class = hashmap_forms.EditServiceThresholdForm form_id = "update_service_threshold" modal_header = _("Update Service Threshold") page_title = _("Update Service Threshold") submit_url = 'horizon:admin:hashmap:service_threshold_edit' def get_initial(self): out = api.cloudkittyclient(self.request).rating.hashmap.get_threshold( threshold_id=self.kwargs['threshold_id']) self.initial = out return self.initial def get_context_data(self, **kwargs): context = super(ServiceThresholdEditView, self).get_context_data(**kwargs) context["threshold_id"] = self.kwargs.get('threshold_id') context['submit_url'] = reverse_lazy(self.submit_url, args=(context['threshold_id'], )) return context def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:service', args=(self.initial['service_id'], )) class FieldThresholdCreateView(forms.ModalFormView): form_class = hashmap_forms.CreateFieldThresholdForm form_id = "create_field_threshold" modal_header = _("Create Field Threshold") page_title = _("Create Field Threshold") template_name = 'admin/hashmap/threshold_create.html' success_url = 'horizon:admin:hashmap:field' submit_url = 'horizon:admin:hashmap:field_threshold_create' def get_object_id(self, obj): return obj.field_id def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:field', args=(self.kwargs['field_id'],)) def get_context_data(self, **kwargs): context = super(FieldThresholdCreateView, self).get_context_data(**kwargs) context["field_id"] = self.kwargs.get('field_id') args = (context['field_id'],) context['submit_url'] = reverse_lazy(self.submit_url, args=args) return context def get_initial(self): return {"field_id": self.kwargs["field_id"]} class FieldThresholdEditView(FieldThresholdCreateView): form_class = hashmap_forms.EditFieldThresholdForm form_id = "update_field_threshold" modal_header = _("Update Field Threshold") page_title = _("Update Field Threshold") submit_url = 'horizon:admin:hashmap:field_threshold_edit' def get_initial(self): out = api.cloudkittyclient(self.request).rating.hashmap.get_threshold( threshold_id=self.kwargs['threshold_id']) self.initial = out return self.initial def get_context_data(self, **kwargs): context = super(FieldThresholdEditView, self).get_context_data(**kwargs) context["threshold_id"] = self.kwargs.get('threshold_id') context['submit_url'] = reverse_lazy(self.submit_url, args=(context['threshold_id'], )) return context def get_success_url(self, **kwargs): return reverse('horizon:admin:hashmap:field', args=(self.initial['field_id'], )) class GroupView(tabs.TabbedTableView): tab_group_class = hashmap_tables.GroupsTab template_name = 'admin/hashmap/group_details.html' def get(self, *args, **kwargs): group = api.cloudkittyclient(self.request).rating.hashmap.get_group( group_id=kwargs['group_id'] ) self.request.group_id = group.group_id self.page_title = "Hashmap Group : %s" % group.name return super(GroupView, self).get(*args, **kwargs) def get_data(self): out = api.cloudkittyclient(self.request).rating.hashmap.get_group() return api.identify(out) class GroupDetailsView(views.APIView): template_name = 'admin/hashmap/group_details.html' page_title = _("Group Details") def get_data(self, request, context, *args, **kwargs): group_id = kwargs.get("group_id") ck_client = api.cloudkittyclient(self.request) try: group = ck_client.rating.hashmap.get_group(group_id=group_id) except Exception: group = None try: mappings = ck_client.rating.hashmap.get_mapping( group_id=group_id)['mappings'] except Exception: mappings = [] try: thresholds = ck_client.rating.hashmap.get_threshold( group_id=group_id)['thresholds'] except Exception: thresholds = [] values = { "mappings": {"fields": [], "services": []}, "thresholds": {"fields": [], "services": []} } for key, value in dict( mappings=mappings, thresholds=thresholds).items(): for entry in value: if entry.get('service_id'): values[key]['services'].append(entry) else: values[key]['fields'].append(entry) context.update(values) context['group'] = group return context cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/000077500000000000000000000000001461705167000266455ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/__init__.py000066400000000000000000000000001461705167000307440ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/forms.py000066400000000000000000000025771461705167000303600ustar00rootroot00000000000000# Copyright 2017 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 exceptions from horizon import forms from horizon import messages from cloudkittydashboard.api import cloudkitty as api class EditPriorityForm(forms.SelfHandlingForm): priority = forms.IntegerField(label=_("Priority")) def handle(self, request, data): ck_client = api.cloudkittyclient(request) try: priority = ck_client.rating.update_module( module_id=self.initial["module_id"], priority=data["priority"]) messages.success( request, _('Successfully updated priority')) return priority except Exception: exceptions.handle(request, _("Unable to update priority.")) cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/panel.py000066400000000000000000000014011461705167000303120ustar00rootroot00000000000000# 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 Modules(horizon.Panel): name = _("Rating Modules") slug = "rating_modules" cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/tables.py000066400000000000000000000066371461705167000305050ustar00rootroot00000000000000# 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 ENABLE = 0 DISABLE = 1 class EditModulePriority(tables.LinkAction): name = "editmodulepriority" verbose_name = _("Edit module priority") icon = "edit" ajax = True classes = ("ajax-modal",) def get_link_url(self, datum): if datum.module_id: url = "horizon:admin:rating_modules:edit_priority" return reverse(url, kwargs={'module_id': datum.module_id}) class ToggleEnabledModule(tables.BatchAction): name = "toggle_module" data_type_singular = _("Module") data_type_plural = _("Modules") classes = ("btn-toggle",) @staticmethod def action_present(count): return ( ngettext_lazy( u"Enable Module", u"Enable Modules", count ), ngettext_lazy( u"Disable Module", u"Disable Modules", count ), ) @staticmethod def action_past(count): return ( ngettext_lazy( u"Enabled Module", u"Enabled Modules", count ), ngettext_lazy( u"Disabled Module", u"Disabled Modules", count ), ) def allowed(self, request, module=None): self.enabled = module.get('enabled') if self.enabled: self.current_present_action = DISABLE else: self.current_present_action = ENABLE return True def action(self, request, obj_id): client = api.cloudkittyclient(request) module = client.rating.get_module(module_id=obj_id) self.enabled = module.get('enabled', False) self.current_past_action = DISABLE if self.enabled else ENABLE client.rating.update_module(module_id=obj_id, enabled=(not self.enabled)) def get_details_link(datum): if datum.module_id: url = "horizon:admin:rating_modules:module_details" return reverse(url, kwargs={'module_id': datum.module_id}) class ModulesTable(tables.DataTable): name = tables.Column('name', verbose_name=_("Name"), link=get_details_link) description = tables.Column('description', verbose_name=_("Description")) hot_config = tables.Column('hot-config', verbose_name=_("Configurable")) priority = tables.Column('priority', verbose_name=_("Priority")) enabled = tables.Column('enabled', verbose_name=_("Enabled")) class Meta(object): name = "modules" verbose_name = _("Modules") row_actions = (ToggleEnabledModule, EditModulePriority) cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/templates/000077500000000000000000000000001461705167000306435ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/templates/rating_modules/000077500000000000000000000000001461705167000336575ustar00rootroot00000000000000_priority_edit.html000066400000000000000000000004611461705167000375140ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/templates/rating_modules{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% blocktrans with module_id=module_id %} Edit the priority for the {{ module_id }} module. {% endblocktrans %}

{% endblock %} details.html000066400000000000000000000013371461705167000361170ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/modules/templates/rating_modules{% extends 'base.html' %} {% load i18n sizeformat %} {% block title %}{% trans "Rating Module Details" %}{% endblock %} {% block main %}
{% trans "Id" %}
{{ module.module_id }}
{% trans "Description" %}
{{ module.description }}
{% trans "Priority" %}
{{ module.priority }}
{% trans "Enabled" %}
{{ module.enabled }}
{% trans "Hot Config" %}
{{ hotconfig }}
{% endblock %} index.html000066400000000000000000000004441461705167000355770ustar00rootroot00000000000000cloudkitty-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.html000066400000000000000000000002101461705167000373450ustar00rootroot00000000000000cloudkitty-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.py000066400000000000000000000002531461705167000303610ustar00rootroot00000000000000from 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.py000066400000000000000000000020151461705167000302020ustar00rootroot00000000000000# 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.py000066400000000000000000000061061461705167000303570ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000272355ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/__init__.py000066400000000000000000000000001461705167000313340ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/forms.py000066400000000000000000000114121461705167000307340ustar00rootroot00000000000000# 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.py000066400000000000000000000013711461705167000307100ustar00rootroot00000000000000# 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.py000066400000000000000000000051501461705167000310620ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000312335ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/pyscripts/templates/pyscripts/000077500000000000000000000000001461705167000332735ustar00rootroot00000000000000_form.html000066400000000000000000000002021461705167000351760ustar00rootroot00000000000000cloudkitty-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.html000066400000000000000000000010261461705167000355260ustar00rootroot00000000000000cloudkitty-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.html000066400000000000000000000002311461705167000350410ustar00rootroot00000000000000cloudkitty-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.html000066400000000000000000000004521461705167000371760ustar00rootroot00000000000000cloudkitty-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.py000066400000000000000000000014121461705167000307470ustar00rootroot00000000000000# 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.py000066400000000000000000000021461461705167000305770ustar00rootroot00000000000000# 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.py000066400000000000000000000065641461705167000307570ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000266725ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/__init__.py000066400000000000000000000000001461705167000307710ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/panel.py000066400000000000000000000014011461705167000303370ustar00rootroot00000000000000# 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.py000066400000000000000000000030561461705167000305220ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000306705ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/admin/summary/templates/rating_summary/000077500000000000000000000000001461705167000337315ustar00rootroot00000000000000details.html000066400000000000000000000005341461705167000361670ustar00rootroot00000000000000cloudkitty-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.html000066400000000000000000000004441461705167000356510ustar00rootroot00000000000000cloudkitty-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.py000066400000000000000000000016011461705167000302270ustar00rootroot00000000000000# 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.py000066400000000000000000000062221461705167000304030ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000255535ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/__init__.py000066400000000000000000000000001461705167000276520ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/000077500000000000000000000000001461705167000270375ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/__init__.py000066400000000000000000000000001461705167000311360ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/panel.py000066400000000000000000000015421461705167000305120ustar00rootroot00000000000000# 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.py000066400000000000000000000020601461705167000306610ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000310355ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/rating/templates/rating/000077500000000000000000000000001461705167000323215ustar00rootroot00000000000000_launch_details_price.html000066400000000000000000000007651461705167000374400ustar00rootroot00000000000000cloudkitty-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.html000066400000000000000000000004601461705167000342370ustar00rootroot00000000000000cloudkitty-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.py000066400000000000000000000002531461705167000305530ustar00rootroot00000000000000from 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.py000066400000000000000000000015031461705167000303750ustar00rootroot00000000000000# 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.py000066400000000000000000000054651461705167000305600ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000275645ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/__init__.py000066400000000000000000000000001461705167000316630ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/panel.py000066400000000000000000000015561461705167000312440ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000315625ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/templates/reporting/000077500000000000000000000000001461705167000335735ustar00rootroot00000000000000index.html000066400000000000000000000005431461705167000355130ustar00rootroot00000000000000cloudkitty-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 %}
{{ tab_group.render }}
{% endblock %} this_month.html000066400000000000000000000107401461705167000365600ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/templates/reporting{% load i18n %} {% load l10n %} {% load static %}

{% trans "Legend" %}

{% trans "Cumulative Cost Repartition" %}

{% trans "Cost Per Service Per Hour" %}

cloudkitty-dashboard-19.0.0/cloudkittydashboard/dashboards/project/reporting/urls.py000066400000000000000000000002641461705167000311250ustar00rootroot00000000000000from 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.py000066400000000000000000000065051461705167000313010ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000233655ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_10_admin_group.py000066400000000000000000000012621461705167000267030ustar00rootroot00000000000000# 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.py000066400000000000000000000012641461705167000272630ustar00rootroot00000000000000# 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.py000066400000000000000000000015621461705167000303530ustar00rootroot00000000000000# 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.py000066400000000000000000000014501461705167000302120ustar00rootroot00000000000000# 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.py000066400000000000000000000014501461705167000304230ustar00rootroot00000000000000# 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.py000066400000000000000000000014521461705167000305720ustar00rootroot00000000000000# 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.py000066400000000000000000000015271461705167000313230ustar00rootroot00000000000000# 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.py000066400000000000000000000002231461705167000307650ustar00rootroot00000000000000PANEL_GROUP = 'rating' PANEL_DASHBOARD = 'admin' PANEL = 'pyscripts' ADD_PANEL = 'cloudkittydashboard.dashboards.admin.pyscripts.panel.PyScripts' cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/_31000_cloudkitty.py000066400000000000000000000001701461705167000270120ustar00rootroot00000000000000FEATURE = 'cloudkitty' ADD_ANGULAR_MODULES = [ 'horizon.dashboard.cloudkitty', ] AUTO_DISCOVER_STATIC_FILES = True cloudkitty-dashboard-19.0.0/cloudkittydashboard/enabled/__init__.py000066400000000000000000000000001461705167000254640ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/000077500000000000000000000000001461705167000232325ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/en_GB/000077500000000000000000000000001461705167000242045ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/en_GB/LC_MESSAGES/000077500000000000000000000000001461705167000257715ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/en_GB/LC_MESSAGES/django.po000066400000000000000000000243611461705167000276010ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000236415ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/fr/LC_MESSAGES/000077500000000000000000000000001461705167000254265ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/locale/fr/LC_MESSAGES/django.po000066400000000000000000000210261461705167000272310ustar00rootroot00000000000000# 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.\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/000077500000000000000000000000001461705167000232625ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/000077500000000000000000000000001461705167000254555ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/js/000077500000000000000000000000001461705167000260715ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/js/cloudkitty.controller.js000066400000000000000000000030261461705167000330050ustar00rootroot00000000000000(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.js000066400000000000000000000013741461705167000321130ustar00rootroot00000000000000(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.js000066400000000000000000000003441461705167000315540ustar00rootroot00000000000000(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.js000066400000000000000000000120211461705167000300560ustar00rootroot00000000000000/* 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/000077500000000000000000000000001461705167000274535ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/templates/cloudkitty-help.html000066400000000000000000000000561461705167000334630ustar00rootroot00000000000000

Price for instance(s)

cloudkitty-dashboard-19.0.0/cloudkittydashboard/static/cloudkitty/templates/cloudkitty-step.html000066400000000000000000000002401461705167000335010ustar00rootroot00000000000000

Price

{$ price | currency $}

cloudkitty-dashboard-19.0.0/cloudkittydashboard/tests/000077500000000000000000000000001461705167000231355ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/tests/__init__.py000066400000000000000000000000001461705167000252340ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/cloudkittydashboard/tests/base.py000066400000000000000000000014321461705167000244210ustar00rootroot00000000000000# -*- 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.py000066400000000000000000000025171461705167000306160ustar00rootroot00000000000000# -*- 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.py000066400000000000000000000056761461705167000304350ustar00rootroot00000000000000# 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.py000066400000000000000000000016771461705167000257210ustar00rootroot00000000000000# 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.py000066400000000000000000000021351461705167000235060ustar00rootroot00000000000000# -*- 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/000077500000000000000000000000001461705167000164755ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/doc/requirements.txt000066400000000000000000000005341461705167000217630ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000177755ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/doc/source/conf.py000066400000000000000000000205151461705167000212770ustar00rootroot00000000000000# -*- 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/000077500000000000000000000000001461705167000223475ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/doc/source/contributor/contributing.rst000066400000000000000000000036461461705167000256210ustar00rootroot00000000000000============================ 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.rst000066400000000000000000000012701461705167000216360ustar00rootroot00000000000000.. 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.rst000066400000000000000000000037631461705167000232410ustar00rootroot00000000000000============ 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.rst000066400000000000000000000000361461705167000217630ustar00rootroot00000000000000.. include:: ../../README.rst cloudkitty-dashboard-19.0.0/releasenotes/000077500000000000000000000000001461705167000204215ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/notes/000077500000000000000000000000001461705167000215515ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/notes/.placeholder000066400000000000000000000000001461705167000240220ustar00rootroot00000000000000add-options-to-attach-pre-post-fix-to-rate-value-2d78f5cb7c289445.yaml000066400000000000000000000007321461705167000356550ustar00rootroot00000000000000cloudkitty-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.yaml000066400000000000000000000002301461705167000315260ustar00rootroot00000000000000--- 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.yaml000066400000000000000000000003411461705167000266770ustar00rootroot00000000000000--- 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.yaml000066400000000000000000000010411461705167000303050ustar00rootroot00000000000000--- 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.yaml000066400000000000000000000002641461705167000311620ustar00rootroot00000000000000--- 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.yaml000066400000000000000000000001521461705167000300450ustar00rootroot00000000000000--- 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.yaml000066400000000000000000000003131461705167000306160ustar00rootroot00000000000000--- 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.yaml000066400000000000000000000001441461705167000331210ustar00rootroot00000000000000cloudkitty-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.yaml000066400000000000000000000003561461705167000317370ustar00rootroot00000000000000--- 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/000077500000000000000000000000001461705167000217215ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/2023.1.rst000066400000000000000000000002021461705167000231720ustar00rootroot00000000000000=========================== 2023.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.1 cloudkitty-dashboard-19.0.0/releasenotes/source/2023.2.rst000066400000000000000000000002021461705167000231730ustar00rootroot00000000000000=========================== 2023.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.2 cloudkitty-dashboard-19.0.0/releasenotes/source/2024.1.rst000066400000000000000000000002021461705167000231730ustar00rootroot00000000000000=========================== 2024.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2024.1 cloudkitty-dashboard-19.0.0/releasenotes/source/_static/000077500000000000000000000000001461705167000233475ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/_static/.placeholder000066400000000000000000000000001461705167000256200ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/_templates/000077500000000000000000000000001461705167000240565ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/_templates/.placeholder000066400000000000000000000000001461705167000263270ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/conf.py000066400000000000000000000202411461705167000232170ustar00rootroot00000000000000# -*- 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.rst000066400000000000000000000005341461705167000235640ustar00rootroot00000000000000CloudKitty 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/000077500000000000000000000000001461705167000231605ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/locale/de/000077500000000000000000000000001461705167000235505ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/locale/de/LC_MESSAGES/000077500000000000000000000000001461705167000253355ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/locale/de/LC_MESSAGES/releasenotes.po000066400000000000000000000035761461705167000304010ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000241325ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/000077500000000000000000000000001461705167000257175ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po000066400000000000000000000154471461705167000307630ustar00rootroot00000000000000# 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/000077500000000000000000000000001461705167000235675ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/locale/fr/LC_MESSAGES/000077500000000000000000000000001461705167000253545ustar00rootroot00000000000000cloudkitty-dashboard-19.0.0/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po000066400000000000000000000021751461705167000304120ustar00rootroot00000000000000# 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.rst000066400000000000000000000002051461705167000235370ustar00rootroot00000000000000========================== Ocata Series Release Notes ========================== .. release-notes:: :branch: origin/stable/ocata cloudkitty-dashboard-19.0.0/releasenotes/source/pike.rst000066400000000000000000000002011461705167000233740ustar00rootroot00000000000000========================= Pike Series Release Notes ========================= .. release-notes:: :branch: origin/stable/pike cloudkitty-dashboard-19.0.0/releasenotes/source/queens.rst000066400000000000000000000002021461705167000237450ustar00rootroot00000000000000=========================== Queens Series Release Notes =========================== .. release-notes:: :branch: stable/queens cloudkitty-dashboard-19.0.0/releasenotes/source/rocky.rst000066400000000000000000000002211461705167000235750ustar00rootroot00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky cloudkitty-dashboard-19.0.0/releasenotes/source/stein.rst000066400000000000000000000001761461705167000236010ustar00rootroot00000000000000========================== Stein Series Release Notes ========================== .. release-notes:: :branch: stable/stein cloudkitty-dashboard-19.0.0/releasenotes/source/train.rst000066400000000000000000000001761461705167000235740ustar00rootroot00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train cloudkitty-dashboard-19.0.0/releasenotes/source/unreleased.rst000066400000000000000000000001531461705167000246010ustar00rootroot00000000000000============================ Current Series Release Notes ============================ .. release-notes:: cloudkitty-dashboard-19.0.0/releasenotes/source/ussuri.rst000066400000000000000000000002021461705167000237770ustar00rootroot00000000000000=========================== Ussuri Series Release Notes =========================== .. release-notes:: :branch: stable/ussuri cloudkitty-dashboard-19.0.0/releasenotes/source/victoria.rst000066400000000000000000000002201461705167000242650ustar00rootroot00000000000000============================= Victoria Series Release Notes ============================= .. release-notes:: :branch: unmaintained/victoria cloudkitty-dashboard-19.0.0/releasenotes/source/wallaby.rst000066400000000000000000000002141461705167000241030ustar00rootroot00000000000000============================ Wallaby Series Release Notes ============================ .. release-notes:: :branch: unmaintained/wallaby cloudkitty-dashboard-19.0.0/releasenotes/source/xena.rst000066400000000000000000000002001461705167000233760ustar00rootroot00000000000000========================= Xena Series Release Notes ========================= .. release-notes:: :branch: unmaintained/xena cloudkitty-dashboard-19.0.0/releasenotes/source/yoga.rst000066400000000000000000000002001461705167000234020ustar00rootroot00000000000000========================= Yoga Series Release Notes ========================= .. release-notes:: :branch: unmaintained/yoga cloudkitty-dashboard-19.0.0/releasenotes/source/zed.rst000066400000000000000000000001741461705167000232370ustar00rootroot00000000000000======================== Zed Series Release Notes ======================== .. release-notes:: :branch: unmaintained/zed cloudkitty-dashboard-19.0.0/requirements.txt000066400000000000000000000005311461705167000212130ustar00rootroot00000000000000# 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.cfg000066400000000000000000000015111461705167000175470ustar00rootroot00000000000000[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.py000066400000000000000000000012671461705167000174500ustar00rootroot00000000000000# 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.txt000066400000000000000000000010261461705167000221700ustar00rootroot00000000000000# 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.ini000066400000000000000000000030331461705167000172420ustar00rootroot00000000000000[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