Add "Sync create","Sync template" to KB-dashboard
This commit makes the users to utilize the “Sync create” and “Sync template” features in an easier and interactive way from "ResourceManagement" panel in kingbird-dashboard. Depends-On: <I86f2d73bc416798e5c249ddb9b996c4df71534d1> Change-Id: Iab7db07610fa8125883609d03a547a70a4e8e59e
This commit is contained in:
parent
2932de7186
commit
299a30ca30
|
@ -14,12 +14,22 @@
|
|||
|
||||
from django.conf import settings
|
||||
|
||||
from glanceclient import client as gl_client
|
||||
|
||||
from horizon.utils import memoized
|
||||
|
||||
from keystoneauth1 import loading
|
||||
|
||||
from keystoneauth1 import session
|
||||
|
||||
from keystoneclient.v3 import client as ks_client
|
||||
|
||||
import kingbirdclient
|
||||
|
||||
from kingbirdclient.api import client as kb_client
|
||||
|
||||
from novaclient import client as nv_client
|
||||
|
||||
SERVICE_TYPE = 'synchronization'
|
||||
|
||||
NOVA_API_VERSION = "2.37"
|
||||
|
@ -38,6 +48,80 @@ def kingbird_dashboardclient(request):
|
|||
)
|
||||
|
||||
|
||||
def keystone_session(request):
|
||||
"""Keystone session for establishment of Nova and Glance clients."""
|
||||
auth_url = getattr(settings, 'OPENSTACK_KEYSTONE_URL')
|
||||
loader = loading.get_plugin_loader('token')
|
||||
auth = loader.load_from_options(auth_url=auth_url,
|
||||
token=request.user.token.id,
|
||||
project_id=request.user.tenant_id)
|
||||
sess = session.Session(auth=auth)
|
||||
return ks_client.Client(session=sess)
|
||||
|
||||
|
||||
def _get_endpoint_from_region(keystone_admin, region):
|
||||
services_list = keystone_admin.services.list()
|
||||
endpoints_list = keystone_admin.endpoints.list()
|
||||
service_id = [service.id for service in
|
||||
services_list if service.type == 'image'][0]
|
||||
|
||||
glance_endpoint = [endpoint.url for endpoint in
|
||||
endpoints_list
|
||||
if endpoint.service_id == service_id
|
||||
and endpoint.region == region and
|
||||
endpoint.interface == 'public'][0]
|
||||
return glance_endpoint
|
||||
|
||||
|
||||
def nova_client(request, region):
|
||||
"""Nova Client for API calls."""
|
||||
return nv_client.Client(
|
||||
NOVA_API_VERSION, session=keystone_session(request).session,
|
||||
region_name=region)
|
||||
|
||||
|
||||
def glance_client(request, region):
|
||||
"""Glance Client for API calls."""
|
||||
keystone_admin = keystone_session(request)
|
||||
region_endpoint = _get_endpoint_from_region(keystone_admin,
|
||||
region)
|
||||
return gl_client.Client(
|
||||
GLANCE_API_VERSION, session=keystone_session(request).session,
|
||||
endpoint=region_endpoint)
|
||||
|
||||
|
||||
def regions_list(request):
|
||||
"""List of available regions."""
|
||||
return keystone_session(request).regions.list()
|
||||
|
||||
|
||||
def images_list(request, region):
|
||||
"""List of images."""
|
||||
images_choices = list()
|
||||
images_list = glance_client(request, region).images.list()
|
||||
for image in images_list:
|
||||
images_choices.append(image.id)
|
||||
return images_choices
|
||||
|
||||
|
||||
def flavors_list(request, region):
|
||||
"""List of flavors."""
|
||||
flavors_choices = list()
|
||||
flavors_list = nova_client(request, region).flavors.list()
|
||||
for flavor in flavors_list:
|
||||
flavors_choices.append(flavor.name)
|
||||
return flavors_choices
|
||||
|
||||
|
||||
def keypairs_list(request, region):
|
||||
"""List of keypairs."""
|
||||
keypairs_choices = list()
|
||||
keypairs_list = nova_client(request, region).keypairs.list()
|
||||
for keypair in keypairs_list:
|
||||
keypairs_choices.append(keypair.name)
|
||||
return keypairs_choices
|
||||
|
||||
|
||||
def list_defaults(request):
|
||||
"""Default Quota Limits."""
|
||||
return kingbird_dashboardclient(request).quota_manager.\
|
||||
|
@ -97,3 +181,9 @@ def sync_delete(request, job_id):
|
|||
"""Delete sync jobs from database."""
|
||||
return kingbird_dashboardclient(request).sync_manager.\
|
||||
delete_sync_job(job_id)
|
||||
|
||||
|
||||
def sync_job_create(request, data):
|
||||
"""Sync a job from source to target region."""
|
||||
return kingbird_dashboardclient(request).sync_manager.\
|
||||
sync_resources(**data)
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
# Copyright 2017 Ericsson AB.
|
||||
#
|
||||
# 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 yaml
|
||||
|
||||
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 kingbird_dashboard.api import client as kb_client
|
||||
|
||||
|
||||
class SyncJobForm(forms.SelfHandlingForm):
|
||||
|
||||
source = forms.ChoiceField(
|
||||
label=_('Source Region'),
|
||||
help_text=_('Select Source Region.'),
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable',
|
||||
'data-slug': 'source_region_selection'}
|
||||
)
|
||||
)
|
||||
|
||||
target = forms.MultipleChoiceField(
|
||||
label=_('Target Region'),
|
||||
help_text=_('Select Target Region.'),
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={'class': 'switchable',
|
||||
'data-slug': 'target_region_selection'}
|
||||
)
|
||||
)
|
||||
|
||||
resource_type = forms.ChoiceField(
|
||||
label=_('Resource Type'),
|
||||
help_text=_('Select Resource Type'),
|
||||
choices=[('flavor', _('Flavor')),
|
||||
('keypair', _('Keypair')),
|
||||
('image', _('Image'))],
|
||||
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable',
|
||||
'data-slug': 'resource_type'})
|
||||
)
|
||||
|
||||
flavors = forms.MultipleChoiceField(
|
||||
label=_('Flavor resources'),
|
||||
help_text=_('Select flavors'),
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={'class': 'switched',
|
||||
'data-switch-on': 'resource_type',
|
||||
'data-resource_type-flavor': _('Flavors'),
|
||||
'data-slug': 'flavors'}
|
||||
)
|
||||
)
|
||||
|
||||
keypairs = forms.MultipleChoiceField(
|
||||
label=_('Keypair resources'),
|
||||
help_text=_('Select keypairs'),
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={'class': 'switched',
|
||||
'data-switch-on': 'resource_type',
|
||||
'data-resource_type-keypair': _('Keypairs'),
|
||||
'data-slug': 'keypairs'}
|
||||
)
|
||||
)
|
||||
|
||||
images = forms.MultipleChoiceField(
|
||||
label=_('Image resources'),
|
||||
help_text=_('Select Images'),
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={'class': 'switched',
|
||||
'data-switch-on': 'resource_type',
|
||||
'data-resource_type-image': _('Images'),
|
||||
'data-slug': 'images'}
|
||||
)
|
||||
)
|
||||
|
||||
force = forms.BooleanField(
|
||||
label=_('Force'),
|
||||
required=False,
|
||||
help_text=_('Select force to re-create the resources'),
|
||||
widget=forms.CheckboxInput(
|
||||
attrs={'class': 'switchable',
|
||||
'data-slug': 'force'}
|
||||
)
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(SyncJobForm, self).clean()
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(SyncJobForm, self).__init__(request, *args, **kwargs)
|
||||
region_choices = list()
|
||||
keypairs_choices = list()
|
||||
flavors_choices = list()
|
||||
images_choices = list()
|
||||
# Choices for Source and Target regions.
|
||||
regions_list = kb_client.regions_list(request)
|
||||
for region in regions_list:
|
||||
region_choices.append((region.id, (region.id)))
|
||||
self.fields['source'].choices = region_choices
|
||||
self.fields['target'].choices = region_choices
|
||||
if 'flavors' in self.data:
|
||||
flavors_list = kb_client.flavors_list(request,
|
||||
self.data.get('source'))
|
||||
for flavor in flavors_list:
|
||||
flavors_choices.append((flavor, (flavor)))
|
||||
self.fields['flavors'].choices = flavors_choices
|
||||
self.fields['flavors'].initial = flavors_choices[0]
|
||||
if 'keypairs' in self.data:
|
||||
keypairs_list = kb_client.keypairs_list(request,
|
||||
self.data.get('source'))
|
||||
for keypair in keypairs_list:
|
||||
keypairs_choices.append((keypair, (keypair)))
|
||||
self.fields['keypairs'].choices = keypairs_choices
|
||||
self.fields['keypairs'].initial = keypairs_choices[0]
|
||||
if 'images' in self.data:
|
||||
images_list = kb_client.images_list(request,
|
||||
self.data.get('source'))
|
||||
for image in images_list:
|
||||
images_choices.append((image, (image)))
|
||||
self.fields['images'].choices = images_choices
|
||||
self.fields['images'].initial = images_choices[0]
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
payload = dict()
|
||||
payload["source"] = data.get("source")
|
||||
payload["target"] = data.get("target")
|
||||
payload["resource_type"] = data.get("resource_type")
|
||||
if payload["resource_type"] == "flavor":
|
||||
payload["resources"] = data.get("flavors")
|
||||
if payload["resource_type"] == "keypair":
|
||||
payload["resources"] = data.get("keypairs")
|
||||
if payload["resource_type"] == "image":
|
||||
payload["resources"] = data.get("images")
|
||||
payload["force"] = str(data.get("force"))
|
||||
kb_client.sync_job_create(request, payload)
|
||||
msg = _('Successfully created Sync Job.')
|
||||
messages.success(request, msg)
|
||||
return True
|
||||
except Exception:
|
||||
msg = _('Failed to create sync job.')
|
||||
redirect = reverse('horizon:kingbird:resource_management:index')
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
|
||||
class SyncTemplateForm(forms.SelfHandlingForm):
|
||||
|
||||
input_upload = forms.FileField(
|
||||
label=_('Input File'),
|
||||
help_text=_('Upload .yaml/.yml/.json template to sync resources.'),
|
||||
widget=forms.FileInput(
|
||||
attrs={'class': 'switched',
|
||||
'data-switch-on': 'inputsource',
|
||||
'data-inputsource-file': _('Input File')}
|
||||
),
|
||||
required=True
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(SyncTemplateForm, self).clean()
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
if data['input_upload'].name.endswith('.json'):
|
||||
resource_set = json.load(data['input_upload'])
|
||||
if data['input_upload'].name.endswith('.yaml') or \
|
||||
data['input_upload'].name.endswith('.yml'):
|
||||
resource_set = yaml.load(data['input_upload'])
|
||||
del data['input_upload']
|
||||
data.update(resource_set)
|
||||
kb_client.sync_job_create(request, data)
|
||||
msg = _('Successfully created Sync Template.')
|
||||
messages.success(request, msg)
|
||||
return True
|
||||
except Exception:
|
||||
msg = _('Failed to create sync template.')
|
||||
redirect = reverse('horizon:kingbird:resource_management:index')
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
|
@ -18,6 +18,22 @@ from horizon import tables
|
|||
from kingbird_dashboard.api import client as kb_client
|
||||
|
||||
|
||||
class SyncJobCreate(tables.LinkAction):
|
||||
name = "sync_job"
|
||||
verbose_name = _("Create Sync Job")
|
||||
url = "horizon:kingbird:resource_management:create_sync_job"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
|
||||
class SyncTemplateCreate(tables.LinkAction):
|
||||
name = "sync_template"
|
||||
verbose_name = _("Create Sync Template")
|
||||
url = "horizon:kingbird:resource_management:create_sync_template"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
|
||||
class DeleteSyncJob(tables.DeleteAction):
|
||||
|
||||
@staticmethod
|
||||
|
@ -110,6 +126,8 @@ class ResourceSyncTable(tables.DataTable):
|
|||
name = "job_set"
|
||||
verbose_name = _("Resource Management")
|
||||
table_actions = (
|
||||
DeleteSyncJob,
|
||||
SyncJobCreate,
|
||||
SyncTemplateCreate,
|
||||
DeleteSyncJob
|
||||
)
|
||||
row_actions = (DeleteSyncJob,)
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
{% block modal-body-right %}
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$("#create_sync_job #id_source").change(function() {
|
||||
var source = $(this).val();
|
||||
$.ajax({
|
||||
url: "load_flavors",
|
||||
data: {
|
||||
'source': source
|
||||
},
|
||||
success: function (data) {
|
||||
$("#id_flavors").html(data);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: "load_keypairs",
|
||||
data: {
|
||||
'source': source
|
||||
},
|
||||
success: function (data) {
|
||||
$("#id_keypairs").html(data);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: "load_images",
|
||||
data: {
|
||||
'source': source
|
||||
},
|
||||
success: function (data) {
|
||||
$("#id_images").html(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Select the resources for a resource-type to sync the job from source region to multiple target regions." %}</p>
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Upload a template containing resource-set with extension .yaml/.yml/.json to sync more than one resource-type to multiple target regions." %}</p>
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create SyncJob" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'kingbird/resource_management/_create.html' %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Sync Template" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'kingbird/resource_management/_create_template.html' %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% for resource in resources %}
|
||||
{% if forloop.first %}
|
||||
<option value="{{ resource }}" selected>{{ resource }}</option>
|
||||
{% else %}
|
||||
<option value="{{ resource }}">{{ resource }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
|
@ -25,4 +25,11 @@ SYNCJOB = r'^(?P<job_id>[^/]+)/%s$'
|
|||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(SYNCJOB % 'detail', views.DetailView.as_view(), name='detail'),
|
||||
url(r'^create_sync_job$', views.CreateSyncJobView.as_view(),
|
||||
name='create_sync_job'),
|
||||
url(r'^create_sync_template$', views.CreateSyncTemplateView.as_view(),
|
||||
name='create_sync_template'),
|
||||
url(r'^load_flavors$', views.load_flavors, name='load_flavors'),
|
||||
url(r'^load_keypairs$', views.load_keypairs, name='load_keypairs'),
|
||||
url(r'^load_images$', views.load_images, name='load_images'),
|
||||
]
|
||||
|
|
|
@ -13,15 +13,51 @@
|
|||
# limitations under the License.
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.shortcuts import render
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
|
||||
|
||||
from kingbird_dashboard.api import client as kb_client
|
||||
from kingbird_dashboard.resource_management import forms as kb_forms
|
||||
from kingbird_dashboard.resource_management import tables as kb_tables
|
||||
|
||||
|
||||
class CreateSyncJobView(forms.ModalFormView):
|
||||
template_name = 'kingbird/resource_management/create.html'
|
||||
form_id = "create_sync_job"
|
||||
form_class = kb_forms.SyncJobForm
|
||||
submit_label = _("Create Sync Job")
|
||||
submit_url = reverse_lazy(
|
||||
"horizon:kingbird:resource_management:create_sync_job")
|
||||
success_url = reverse_lazy('horizon:kingbird:resource_management:index')
|
||||
page_title = _("Create Sync Job")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(CreateSyncJobView, self).get_form_kwargs()
|
||||
return kwargs
|
||||
|
||||
|
||||
class CreateSyncTemplateView(forms.ModalFormView):
|
||||
template_name = 'kingbird/resource_management/create_template.html'
|
||||
form_id = "create_sync_template"
|
||||
form_class = kb_forms.SyncTemplateForm
|
||||
submit_label = _("Create Sync Template")
|
||||
submit_url = reverse_lazy(
|
||||
"horizon:kingbird:resource_management:create_sync_template")
|
||||
success_url = reverse_lazy('horizon:kingbird:resource_management:index')
|
||||
page_title = _("Create Sync Template")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(CreateSyncTemplateView, self).get_form_kwargs()
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_id = "sync_jobs"
|
||||
table_class = kb_tables.ResourceSyncTable
|
||||
|
@ -49,3 +85,27 @@ class DetailView(tables.DataTableView):
|
|||
msg = _('Unable to get job_details "%s".') % job_id
|
||||
redirect = reverse('horizon:kingbird:resource_management:index')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
|
||||
def load_flavors(request):
|
||||
source = request.GET.get('source')
|
||||
# Choices for flavors.
|
||||
flavors_list = kb_client.flavors_list(request, source)
|
||||
return render(request, 'kingbird/resource_management/resources.html',
|
||||
{'resources': flavors_list})
|
||||
|
||||
|
||||
def load_keypairs(request):
|
||||
source = request.GET.get('source')
|
||||
# Choices for keypairs.
|
||||
keypairs_list = kb_client.keypairs_list(request, source)
|
||||
return render(request, 'kingbird/resource_management/resources.html',
|
||||
{'resources': keypairs_list})
|
||||
|
||||
|
||||
def load_images(request):
|
||||
source = request.GET.get('source')
|
||||
# Choices for images.
|
||||
images_list = kb_client.images_list(request, source)
|
||||
return render(request, 'kingbird/resource_management/resources.html',
|
||||
{'resources': images_list})
|
||||
|
|
Loading…
Reference in New Issue