From 116621a0a57c1fcded4556bb38e5ed60a7dc15e9 Mon Sep 17 00:00:00 2001 From: efedorova Date: Fri, 4 Oct 2013 15:55:09 +0400 Subject: [PATCH] Support Murano Images Cherry-picked a393a331718ac28e15e19f4c60f205d04470546c from master. Change-Id: Iedfa5e97d7d47a2c3a49218736017642da0e01aa --- muranodashboard/panel/forms.py | 42 ++++++++++++++++++ muranodashboard/panel/tables.py | 48 ++++++++++++++++++++- muranodashboard/panel/urls.py | 15 ++++++- muranodashboard/panel/views.py | 48 ++++++++++++++++++++- muranodashboard/templates/images/_add.html | 32 ++++++++++++++ muranodashboard/templates/images/add.html | 11 +++++ muranodashboard/templates/images/index.html | 11 +++++ 7 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 muranodashboard/templates/images/_add.html create mode 100644 muranodashboard/templates/images/add.html create mode 100644 muranodashboard/templates/images/index.html diff --git a/muranodashboard/panel/forms.py b/muranodashboard/panel/forms.py index 315edbc59..ee3d9aacd 100644 --- a/muranodashboard/panel/forms.py +++ b/muranodashboard/panel/forms.py @@ -15,6 +15,11 @@ import logging from django import forms from django.utils.translation import ugettext_lazy as _ +from horizon.forms import SelfHandlingForm +from horizon import messages, exceptions +from openstack_dashboard.api import glance +import json + from muranodashboard.panel.services import iterate_over_service_forms, \ get_service_choices @@ -28,3 +33,40 @@ class WizardFormServiceType(forms.Form): FORMS = [('service_choice', WizardFormServiceType)] FORMS.extend(iterate_over_service_forms()) + + +class AddImageForm(SelfHandlingForm): + title = forms.CharField(max_length="255", + label=_("Image Title")) + + image_choices = forms.ChoiceField(label='Images') + + windows_image = ('ws-2012-std', ' Windows Server 2012 Desktop') + demo_image = ('murano_demo', 'Murano Demo Image') + + image_type = forms.ChoiceField(label="Murano Type", + choices=[windows_image, demo_image]) + + def __init__(self, request, *args, **kwargs): + super(AddImageForm, self).__init__(request, *args, **kwargs) + try: + images, _more = glance.image_list_detailed(request) + except Exception: + log.error("Failed to request image list from glance ") + images = [] + exceptions.handle(request, _("Unable to retrieve public images.")) + self.fields['image_choices'].choices = [(image.id, image.name) + for image in images] + + def handle(self, request, data): + log.debug('Adding new murano using data {0}'.format(data)) + murano_properties = {'murano_image_info': json.dumps( + {'title': data['title'], 'type': data['image_type']})} + try: + image = glance.image_update(request, data['image_choices'], + properties=murano_properties) + + messages.success(request, _('Image added to Murano')) + return image + except Exception: + exceptions.handle(request, _('Unable to update image.')) diff --git a/muranodashboard/panel/tables.py b/muranodashboard/panel/tables.py index 2ef20e444..b6cce6afa 100644 --- a/muranodashboard/panel/tables.py +++ b/muranodashboard/panel/tables.py @@ -19,6 +19,7 @@ from django import shortcuts from horizon import exceptions from horizon import tables from horizon import messages +from openstack_dashboard.api import glance from muranodashboard.panel import api from muranodashboard.openstack.common import timeutils @@ -56,6 +57,15 @@ class CreateEnvironment(tables.LinkAction): api.environment_create(request, environment) +class MuranoImages(tables.LinkAction): + name = 'show_images' + verbose_name = _('Murano Images') + url = 'horizon:project:murano:murano_images' + + def allowed(self, request, environment): + return True + + class DeleteEnvironment(tables.DeleteAction): data_type_singular = _('Environment') data_type_plural = _('Environments') @@ -217,7 +227,7 @@ class EnvironmentsTable(tables.DataTable): verbose_name = _('Environments') row_class = UpdateEnvironmentRow status_columns = ['status'] - table_actions = (CreateEnvironment,) + table_actions = (CreateEnvironment, MuranoImages) row_actions = (ShowEnvironmentServices, DeployEnvironment, EditEnvironment, DeleteEnvironment, ShowDeployments) @@ -302,3 +312,39 @@ class EnvConfigTable(tables.DataTable): class Meta: name = 'environment_configuration' verbose_name = _('Deployed Services') + + +class AddMuranoImage(tables.LinkAction): + name = "add_image" + verbose_name = _("Add Image") + url = "horizon:project:murano:add_image" + classes = ("ajax-modal", "btn-create") + + def allowed(self, request, image): + return True + + +class RemoveImageMetadata(tables.DeleteAction): + data_type_singular = _('Murano Metadata') + data_type_plural = _('Murano Metadatum') + + def delete(self, request, obj_id): + try: + glance.image_update(request, obj_id, properties={}) + messages.success(request, _('Image removed from Murano.')) + except Exception: + exceptions.handle(request, _('Unable to update image.')) + + +class ImagesTable(tables.DataTable): + image_title = tables.Column('title', verbose_name=_('Murano title')) + image_id = tables.Column('id', verbose_name=_('Image id')) + + image_name = tables.Column('name', verbose_name=_('Name in Glance')) + image_type = tables.Column('name', verbose_name=_('Murano Type')) + + class Meta: + name = 'images' + verbose_name = _('Murano Images') + table_actions = (AddMuranoImage, RemoveImageMetadata) + row_actions = (RemoveImageMetadata,) diff --git a/muranodashboard/panel/urls.py b/muranodashboard/panel/urls.py index 86cc3d888..eb370f202 100644 --- a/muranodashboard/panel/urls.py +++ b/muranodashboard/panel/urls.py @@ -19,6 +19,7 @@ from views import Services from views import CreateEnvironmentView from views import DetailServiceView from views import DeploymentsView +from views import MuranoImageView, AddMuranoImageView from views import Wizard, EditEnvironmentView from forms import FORMS from muranodashboard.panel.services import get_service_checkers @@ -36,9 +37,18 @@ urlpatterns = patterns( condition_dict=dict(get_service_checkers())), name='create'), - url(r'^create_environment/$', CreateEnvironmentView.as_view(), + url(r'^create_environment$', CreateEnvironmentView.as_view(), name='create_environment'), + url(r'^murano_images$', MuranoImageView.as_view(), + name='murano_images'), + + url(r'^add_image$', AddMuranoImageView.as_view(), + name='add_image'), + + url(r'^remove_image$', MuranoImageView.as_view(), + name='remove_image'), + url(ENVIRONMENT_ID + r'/update_environment$', EditEnvironmentView.as_view(), name='update_environment'), @@ -56,5 +66,6 @@ urlpatterns = patterns( DeploymentsView.as_view(), name='deployments'), url(ENVIRONMENT_ID + r'/deployments/(?P[^/]+)$', - DeploymentDetailsView.as_view(), name='deployment_details') + DeploymentDetailsView.as_view(), name='deployment_details'), + ) diff --git a/muranodashboard/panel/views.py b/muranodashboard/panel/views.py index d97e1a427..1320ad351 100644 --- a/muranodashboard/panel/views.py +++ b/muranodashboard/panel/views.py @@ -18,21 +18,28 @@ import json from django.core.urlresolvers import reverse, reverse_lazy from django.utils.translation import ugettext_lazy as _ +from django.utils.encoding import smart_text from django.contrib.formtools.wizard.views import SessionWizardView from django.http import HttpResponseRedirect + +from openstack_dashboard.api import glance + from horizon import exceptions from horizon import tabs from horizon import tables from horizon import workflows from horizon import messages -from horizon.forms.views import ModalFormMixin +from horizon.forms.views import ModalFormMixin, ModalFormView from tables import EnvironmentsTable from tables import ServicesTable from tables import DeploymentsTable from tables import EnvConfigTable +from tables import ImagesTable + from workflows import CreateEnvironment, UpdateEnvironment from tabs import ServicesTabs, DeploymentTabs +from forms import AddImageForm from muranodashboard.panel import api from muranoclient.common.exceptions import HTTPUnauthorized, \ @@ -330,3 +337,42 @@ class DeploymentDetailsView(tabs.TabbedTableView): return self.tab_group_class(request, deployment=deployment, logs=logs, **kwargs) + + +class MuranoImageView(tables.DataTableView): + table_class = ImagesTable + template_name = 'images/index.html' + + def get_data(self): + images = [] + try: + images, _more = glance.image_list_detailed(self.request) + + except HTTPForbidden: + msg = _('Unable to retrieve list of images') + exceptions.handle(self.request, msg, + redirect=reverse("horizon:project:murano:index")) + murano_images = [] + for image in images: + murano_property = image.properties.get('murano_image_info') + if murano_property: + try: + murano_json = json.loads(murano_property) + except ValueError: + LOG.warning("JSON in image metadata is not valid. " + "Check it in glance.") + messages.error(self.request, + _("Invalid murano image metadata")) + else: + image.title = murano_json.get('title', 'No title') + image.type = murano_json.get('type', 'No title') + + murano_images.append(image) + return murano_images + + +class AddMuranoImageView(ModalFormView): + form_class = AddImageForm + template_name = 'images/add.html' + context_object_name = 'image' + success_url = reverse_lazy("horizon:project:murano:murano_images") diff --git a/muranodashboard/templates/images/_add.html b/muranodashboard/templates/images/_add.html new file mode 100644 index 000000000..600992ca2 --- /dev/null +++ b/muranodashboard/templates/images/_add.html @@ -0,0 +1,32 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_id %}add_murano_image_form{% endblock %} +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} +{% block form_action %}add_image{% endblock %} +{% block modal-header %}{% trans "Add An Image" %}{% endblock %} + +{% block modal-body %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+

{% trans "Description:" %}

+

{% trans "Add image to Murano. Valid image metadata will be added so this image can be picked during service creation." %}

+

{% trans "Image Title" %} + {% blocktrans %}Enter an image desciption which will help you to recognize the service during service creation {% endblocktrans %} +

+

{% trans "Images" %} + {% blocktrans %}Select image from Glance{% endblocktrans %} +

+

{% trans "Murano Type" %} + {% blocktrans %}Pick image type from supported by Murano{% endblocktrans %} +

+
+{% endblock %} + +{% block modal-footer %} + +{% endblock %} diff --git a/muranodashboard/templates/images/add.html b/muranodashboard/templates/images/add.html new file mode 100644 index 000000000..5886e19a3 --- /dev/null +++ b/muranodashboard/templates/images/add.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Add An Image" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Add Image To Murano") %} +{% endblock page_header %} + +{% block main %} + {% include 'horizon/project/murano/images/_add.html' %} +{% endblock %} diff --git a/muranodashboard/templates/images/index.html b/muranodashboard/templates/images/index.html new file mode 100644 index 000000000..739778c62 --- /dev/null +++ b/muranodashboard/templates/images/index.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Murano Images" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Murano Images") %} +{% endblock page_header %} + +{% block main %} + {{ table.render }} +{% endblock %}