diff --git a/congress_dashboard/api/congress.py b/congress_dashboard/api/congress.py index 4bbf526..54e4a7f 100644 --- a/congress_dashboard/api/congress.py +++ b/congress_dashboard/api/congress.py @@ -308,3 +308,29 @@ def datasource_status_list(request, datasource_name): LOG.exception("Failed to retrieve status for datasource %s", datasource_name) raise + + +def supported_driver_list(request): + client = congressclient(request) + try: + data = client.list_drivers()['results'] + drivers = [(d['id'], d['id']) for d in data] + return drivers + except Exception: + LOG.exception("Unable to get driver list") + raise + + +def create_datasource(request, data): + client = congressclient(request) + datasource = client.create_datasource(data) + return datasource + + +def delete_datasource(request, datasource_name): + client = congressclient(request) + try: + client.delete_datasource(datasource_name) + except Exception: + LOG.exception("deleting datasource %s failed", datasource_name) + raise diff --git a/congress_dashboard/datasources/forms.py b/congress_dashboard/datasources/forms.py new file mode 100644 index 0000000..32454b9 --- /dev/null +++ b/congress_dashboard/datasources/forms.py @@ -0,0 +1,100 @@ +# Copyright 2015 VMware. +# +# 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.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions +from horizon import forms +from horizon import messages + +from congress_dashboard.api import congress + + +LOG = logging.getLogger(__name__) + + +class CreateDatasource(forms.SelfHandlingForm): + name = forms.CharField(max_length=255, + label=_("Data Source Name"), + help_text='Name of the data source') + + driver = forms.ThemableChoiceField(label=_("Driver"), + help_text='Data Source driver') + + description = forms.CharField(label=_("Description"), + required=False, + help_text='Data Source Description') + username = forms.CharField( + max_length=255, + label=_("UserName"), + help_text='username to connect to the driver service') + + password = forms.CharField(widget=forms.PasswordInput(render_value=False), + label=_('Password')) + + tenant_name = forms.CharField(max_length=255, label=_("Project Name")) + + # TODO(ramineni): support adding lazy tables + # lazy_tables = forms.CharField(max_length=255, label=_("Lazy Tables")) + auth_url = forms.URLField(max_length=255, label=_("Keystone Auth URL")) + poll_time = forms.IntegerField( + label=_("Poll Interval (in seconds)"), + help_text='periodic interval congress needs to poll data') + + failure_url = 'horizon:admin:datasources:index' + + @classmethod + def _instantiate(cls, request, *args, **kwargs): + return cls(request, *args, **kwargs) + + def __init__(self, request, *args, **kwargs): + super(CreateDatasource, self).__init__(request, *args, **kwargs) + driver_choices = [('', _("Select a Driver"))] + drivers = congress.supported_driver_list(request) + driver_choices.extend(drivers) + self.fields['driver'].choices = driver_choices + + def handle(self, request, data): + datasource_name = data['name'] + datasource_description = data.get('description') + datasource_driver = data.pop('driver') + config = { + 'username': data['username'], + 'password': data['password'], + 'tenant_name': data['tenant_name'], + 'auth_url': data['auth_url'], + 'poll_time': data['poll_time'] + } + try: + params = { + 'name': datasource_name, + 'driver': datasource_driver, + 'description': datasource_description, + 'config': config + } + datasource = congress.create_datasource(request, params) + msg = _('Created Data Source "%s"') % datasource_name + LOG.info(msg) + messages.success(request, msg) + except Exception as e: + msg_args = {'datasource_name': datasource_name, 'error': str(e)} + msg = _('Failed to create data source "%(datasource_name)s": ' + '%(error)s') % msg_args + LOG.error(msg) + messages.error(self.request, msg) + redirect = reverse(self.failure_url) + raise exceptions.Http302(redirect) + return datasource diff --git a/congress_dashboard/datasources/tables.py b/congress_dashboard/datasources/tables.py index 05a1b95..69d827e 100644 --- a/congress_dashboard/datasources/tables.py +++ b/congress_dashboard/datasources/tables.py @@ -15,8 +15,11 @@ from django.core.urlresolvers import reverse from django.template.defaultfilters import unordered_list from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ungettext_lazy from horizon import tables +from congress_dashboard.api import congress + def get_resource_url(obj): return reverse('horizon:admin:datasources:datasource_table_detail', @@ -40,7 +43,37 @@ class DataSourceRowsTable(tables.DataTable): hidden_title = False -# TODO(ramineni): support create/delete datasource +class CreateDatasource(tables.LinkAction): + name = 'create_datasource' + verbose_name = _('Create Data Source') + url = 'horizon:admin:datasources:create' + classes = ('ajax-modal',) + icon = 'plus' + + +class DeleteDatasource(tables.DeleteAction): + @staticmethod + def action_present(count): + return ungettext_lazy( + u"Delete Data Source", + u'Deleted Data Sources', + count + ) + + @staticmethod + def action_past(count): + return ungettext_lazy( + u'Deleted Data Source', + u'Deleted Data Sources', + count + ) + + redirect_url = 'horizon:admin:datasources:index' + + def delete(self, request, name): + congress.delete_datasource(request, name) + + class DataSourcesTable(tables.DataTable): name = tables.Column("name", verbose_name=_("Data Source Name"), link='horizon:admin:datasources:datasource_detail') @@ -52,8 +85,8 @@ class DataSourcesTable(tables.DataTable): name = "datasources_list" verbose_name = _("Data Sources") hidden_title = False - # table_actions = (CreateDatasource,) - # row_actions = (Disable,) + table_actions = (CreateDatasource, DeleteDatasource) + row_actions = (DeleteDatasource, ) class DataSourceStatusesTable(tables.DataTable): diff --git a/congress_dashboard/datasources/templates/datasources/_create.html b/congress_dashboard/datasources/templates/datasources/_create.html new file mode 100644 index 0000000..5acd279 --- /dev/null +++ b/congress_dashboard/datasources/templates/datasources/_create.html @@ -0,0 +1,26 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} +{% load url from future %} + +{% block form_id %}create_datasource_form{% endblock %} +{% block form_action %}{% url 'horizon:admin:datasources:create' %}{% endblock %} + +{% block modal_id %}create_datasource_modal{% endblock %} +{% block modal-header %}{% trans "Create Data Source" %}{% endblock %} + +{% block modal-body %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+

{% trans "Description:" %}

+

{% trans "Create a new data source with the any of the supported drivers listed."%}

+

{% trans "Congress uses a driver to connect each service to the policy engine. Connection details such as username, password, auth_url, project_name needed for congress to connect to respective service to be able poll data."%}

+
+{% endblock %} +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} diff --git a/congress_dashboard/datasources/templates/datasources/create.html b/congress_dashboard/datasources/templates/datasources/create.html new file mode 100644 index 0000000..db0261f --- /dev/null +++ b/congress_dashboard/datasources/templates/datasources/create.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Create Data Source" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Create Data Source") %} +{% endblock page_header %} + +{% block main %} + {% include "admin/datasources/_create.html" %} +{% endblock %} diff --git a/congress_dashboard/datasources/urls.py b/congress_dashboard/datasources/urls.py index 8ab303d..430afb6 100644 --- a/congress_dashboard/datasources/urls.py +++ b/congress_dashboard/datasources/urls.py @@ -26,6 +26,7 @@ DATASOURCE = r'^(?P[^/]+)/%s$' urlpatterns = patterns( '', url(r'^$', views.IndexView.as_view(), name='index'), + url(r'^create/$', views.CreateView.as_view(), name='create'), url(SERVICES % 'detail', views.DetailView.as_view(), name='datasource_table_detail'), url(DATASOURCE % 'detail', views.DatasourceView.as_view(), diff --git a/congress_dashboard/datasources/views.py b/congress_dashboard/datasources/views.py index 4efb674..dd58cca 100644 --- a/congress_dashboard/datasources/views.py +++ b/congress_dashboard/datasources/views.py @@ -16,13 +16,16 @@ import copy import logging from django.core.urlresolvers import reverse +from django.core.urlresolvers import reverse_lazy from django.template.defaultfilters import slugify from django.utils.translation import ugettext_lazy as _ from horizon import exceptions +from horizon import forms from horizon import messages from horizon import tables from congress_dashboard.api import congress +from congress_dashboard.datasources import forms as datasource_forms from congress_dashboard.datasources import tables as datasources_tables @@ -107,6 +110,12 @@ class DatasourceView(tables.DataTableView): return [] +class CreateView(forms.ModalFormView): + form_class = datasource_forms.CreateDatasource + template_name = 'admin/datasources/create.html' + success_url = reverse_lazy('horizon:admin:datasources:index') + + class DetailView(tables.DataTableView): """List details about and rows from a data source (service or policy).""" table_class = datasources_tables.DataSourceRowsTable