Allow admin user to create runtime
We should allow the admin user (users with admin role) to create runtime on the dashboard. This is equivalent function to following CLI command openstack runtime create $IMAGE --name python3, The $IMAGE is a docker image like openstackqinling/python3-runtime:0.0.2 which should be provided by the user. According to qinling team, For now, there is no need to do validation for that input but in future the qinling-api should do some sanity checks and reply to the UI accordingly. Change-Id: I279442ac9b20dce3f141beffbfda7e8b5ef9c692 Story: 2004391 Task: 28018
This commit is contained in:
parent
d4aeab942a
commit
c6ead89c12
|
@ -52,6 +52,11 @@ def runtime_get(request, runtime_id):
|
|||
return qinlingclient(request).runtimes.get(runtime_id)
|
||||
|
||||
|
||||
def runtime_create(request, **params):
|
||||
resource = qinlingclient(request).runtimes.create(**params)
|
||||
return resource
|
||||
|
||||
|
||||
def set_code(datum):
|
||||
if isinstance(datum.code, six.string_types):
|
||||
code_dict = json.loads(datum.code)
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
"admin_or_owner": "rule:context_is_admin or rule:owner",
|
||||
"default": "rule:admin_or_owner",
|
||||
|
||||
"runtime:create": "rule:context_is_admin",
|
||||
|
||||
"function:create": "rule:admin_or_owner",
|
||||
"function:update": "rule:admin_or_owner",
|
||||
"function:delete": "rule:admin_or_owner",
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
# Copyright 2012 Nebula, Inc.
|
||||
# All rights reserved.
|
||||
|
||||
# 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 ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from qinling_dashboard import api
|
||||
|
||||
# from qinling_dashboard.utils import calculate_md5
|
||||
|
||||
from qinling_dashboard import validators
|
||||
|
||||
|
||||
class CreateRuntimeForm(forms.SelfHandlingForm):
|
||||
|
||||
image = forms.CharField(max_length=255,
|
||||
label=_("Image"),
|
||||
validators=[validators.validate_one_line_string],
|
||||
required=True)
|
||||
|
||||
name = forms.CharField(max_length=255,
|
||||
label=_("Name"),
|
||||
validators=[validators.validate_one_line_string],
|
||||
required=False)
|
||||
|
||||
description = forms.CharField(
|
||||
max_length=255,
|
||||
widget=forms.Textarea(
|
||||
attrs={'class': 'modal-body-fixed-width',
|
||||
'rows': 3}),
|
||||
label=_("Description"),
|
||||
required=False)
|
||||
|
||||
untrusted = forms.BooleanField(
|
||||
label=_("Create as Untrusted Image"),
|
||||
required=False,
|
||||
help_text=_("Check this item if you would like to "
|
||||
"create untrusted runtime"),
|
||||
initial=False
|
||||
)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(CreateRuntimeForm, self).__init__(request, *args, **kwargs)
|
||||
|
||||
def handle(self, request, context):
|
||||
params = {}
|
||||
|
||||
# basic parameters
|
||||
params.update({'image': context.get('image')})
|
||||
|
||||
if context.get('name'):
|
||||
params.update({'name': context.get('name')})
|
||||
|
||||
if context.get('description'):
|
||||
params.update({'description': context.get('description')})
|
||||
|
||||
if context.get('untrusted'):
|
||||
trusted = not bool(context.get('untrusted'))
|
||||
params.update({'trusted': trusted})
|
||||
|
||||
try:
|
||||
api.qinling.runtime_create(request, **params)
|
||||
message = _('Created runtime "%s"') % params.get('name',
|
||||
'unknown name')
|
||||
messages.success(request, message)
|
||||
return True
|
||||
except Exception:
|
||||
redirect = reverse("horizon:project:runtimes:index")
|
||||
exceptions.handle(request,
|
||||
_("Unable to create runtime."),
|
||||
redirect=redirect)
|
|
@ -19,6 +19,16 @@ from qinling_dashboard import api
|
|||
from qinling_dashboard import utils
|
||||
|
||||
|
||||
class CreateRuntime(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Runtime")
|
||||
url = "horizon:project:runtimes:create"
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
policy_rules = (("function_engine", "runtime:create"),)
|
||||
icon = "plus"
|
||||
ajax = True
|
||||
|
||||
|
||||
class RuntimesFilterAction(tables.FilterAction):
|
||||
|
||||
def filter(self, table, runtimes, filter_string):
|
||||
|
@ -78,4 +88,5 @@ class RuntimesTable(tables.DataTable):
|
|||
status_columns = ["status"]
|
||||
multi_select = True
|
||||
row_class = UpdateRow
|
||||
table_actions = (RuntimesFilterAction,)
|
||||
table_actions = (CreateRuntime,
|
||||
RuntimesFilterAction,)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Create a new runtime." %}</p>
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Runtime" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/runtimes/_create.html' %}
|
||||
{% endblock %}
|
|
@ -18,4 +18,5 @@ urlpatterns = [
|
|||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^(?P<runtime_id>[^/]+)/$',
|
||||
views.DetailView.as_view(), name='detail'),
|
||||
url(r'^create', views.CreateRuntimeView.as_view(), name='create'),
|
||||
]
|
||||
|
|
|
@ -15,15 +15,26 @@ from django.urls import reverse_lazy
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
from horizon import tabs
|
||||
from horizon.utils import memoized
|
||||
|
||||
from qinling_dashboard import api
|
||||
from qinling_dashboard.content.runtimes import forms as project_forms
|
||||
from qinling_dashboard.content.runtimes import tables as project_tables
|
||||
from qinling_dashboard.content.runtimes import tabs as project_tabs
|
||||
|
||||
|
||||
class CreateRuntimeView(forms.ModalFormView):
|
||||
|
||||
form_class = project_forms.CreateRuntimeForm
|
||||
modal_header = submit_label = page_title = _("Create Runtime")
|
||||
template_name = 'project/runtimes/create.html'
|
||||
submit_url = reverse_lazy("horizon:project:runtimes:create")
|
||||
success_url = reverse_lazy("horizon:project:runtimes:index")
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
|
||||
table_class = project_tables.RuntimesTable
|
||||
|
|
|
@ -26,6 +26,55 @@ INDEX_URL = reverse('horizon:project:runtimes:index')
|
|||
|
||||
class RuntimesTests(test.TestCase):
|
||||
|
||||
@test.create_mocks({
|
||||
api.qinling: [
|
||||
'runtime_create',
|
||||
]})
|
||||
def test_execution_create_with_maximum_params(self):
|
||||
data_runtime = self.runtimes.first()
|
||||
|
||||
self.mock_runtime_create.return_value = data_runtime
|
||||
|
||||
image_name = 'dummy/dockerimage'
|
||||
form_data = {'image': image_name,
|
||||
'name': 'test_name',
|
||||
'description': 'description',
|
||||
'untrusted': 'on'}
|
||||
|
||||
url = reverse('horizon:project:runtimes:create')
|
||||
res = self.client.post(url, form_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
self.mock_runtime_create.assert_called_once_with(
|
||||
test.IsHttpRequest(),
|
||||
image=image_name,
|
||||
name='test_name',
|
||||
description='description',
|
||||
trusted=False)
|
||||
|
||||
@test.create_mocks({
|
||||
api.qinling: [
|
||||
'runtime_create',
|
||||
]})
|
||||
def test_execution_create_with_minimum_params(self):
|
||||
data_runtime = self.runtimes.first()
|
||||
|
||||
self.mock_runtime_create.return_value = data_runtime
|
||||
|
||||
image_name = 'dummy/dockerimage'
|
||||
form_data = {'image': image_name}
|
||||
|
||||
url = reverse('horizon:project:runtimes:create')
|
||||
res = self.client.post(url, form_data)
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
self.mock_runtime_create.assert_called_once_with(
|
||||
test.IsHttpRequest(), image=image_name)
|
||||
|
||||
@test.create_mocks({
|
||||
api.qinling: [
|
||||
'runtimes_list',
|
||||
|
|
Loading…
Reference in New Issue