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:
mounikasreeram 2018-03-26 19:03:47 +05:30
parent 2932de7186
commit 299a30ca30
10 changed files with 445 additions and 1 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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,)

View File

@ -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 %}

View File

@ -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 %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create SyncJob" %}{% endblock %}
{% block main %}
{% include 'kingbird/resource_management/_create.html' %}
{% endblock %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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'),
]

View File

@ -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})