Sexy boxes drag and drop
The plan edit page is modified to show the flavors with a visualisation of the nodes. Roles can be dragged and dropped on the flavors. TODO: actually save the flavor for a role in the form TODO: add a popup for adding the roles to flavors Change-Id: I9b97f39e3800b5b6c2e5639fcfd186b4196d0190
This commit is contained in:
parent
f1bf2337ae
commit
05e505c5c6
@ -7,7 +7,7 @@ include AUTHORS
|
||||
include LICENSE
|
||||
include Makefile
|
||||
include manage.py
|
||||
include README.md
|
||||
include README.rst
|
||||
include run_tests.sh
|
||||
include tox.ini
|
||||
include doc/Makefile
|
||||
|
@ -16,7 +16,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from tuskar_ui.infrastructure import dashboard
|
||||
import tuskar_ui.infrastructure.dashboard as tuskar_dashboard
|
||||
from tuskar_ui.infrastructure.overview.panel import Overview as TuskarOverview
|
||||
|
||||
|
||||
class Overview(horizon.Panel):
|
||||
@ -24,4 +25,5 @@ class Overview(horizon.Panel):
|
||||
slug = "overview"
|
||||
|
||||
|
||||
dashboard.Infrastructure.register(Overview)
|
||||
tuskar_dashboard.Infrastructure.unregister(TuskarOverview)
|
||||
tuskar_dashboard.Infrastructure.register(Overview)
|
||||
|
@ -1,26 +0,0 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}provision_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:infrastructure:overview:deploy_confirmation' %}{% endblock %}
|
||||
|
||||
{% block modal_id %}provision_modal{% endblock %}
|
||||
{% block modal-header %}{% trans "Deployment Confirmation" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div>
|
||||
<p>{% trans "You are about deploy your overcloud" %}
|
||||
</p>
|
||||
{% if autogenerated_parameters %}
|
||||
<strong>These parameters will be randomly generated before the deployment:</strong>
|
||||
<p>{{ autogenerated_parameters|join:", " }}</p>
|
||||
{% endif %}
|
||||
<p>{% trans "This operation cannot be undone. Are you sure you want to do that?" %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans "Deploy" %}" />
|
||||
<a href="{% url 'horizon:infrastructure:overview:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -1,27 +0,0 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}post_deploy_init_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:infrastructure:overview:post_deploy_init' %}{% endblock %}
|
||||
|
||||
{% block modal_id %}provision_modal{% endblock %}
|
||||
{% block modal-header %}{% trans "Initialize Overcloud" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div>
|
||||
<fieldset>
|
||||
<div class="left">
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</div>
|
||||
<div class="right">
|
||||
{% trans "Your OpenStack cloud nodes are deployed. They need to be initialized before your cloud will be live."%}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans "Initialize" %}" />
|
||||
<a href="{% url 'horizon:infrastructure:overview:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -1,25 +0,0 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}provision_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:infrastructure:overview:undeploy_confirmation' %}{% endblock %}
|
||||
|
||||
{% block modal_id %}provision_modal{% endblock %}
|
||||
{% block modal-header %}{% trans "Undeployment Confirmation" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div>
|
||||
<p>{% trans "You are about undeploy your overcloud" %}
|
||||
</p>
|
||||
<p>{% trans "This operation cannot be undone. Are you sure you want to do that?" %}</p>
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans "Undeploy" %}" />
|
||||
<a href="{% url 'horizon:infrastructure:overview:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -1,11 +0,0 @@
|
||||
{% extends 'infrastructure/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Deploy overcloud" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Deploy overcloud") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block infrastructure_main %}
|
||||
{% include "infrastructure/overview/_deploy_confirmation.html" %}
|
||||
{% endblock %}
|
@ -1,22 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% load url from future%}
|
||||
|
||||
<div class="deployment-icon">
|
||||
{% block deployment-icon %}
|
||||
<i class="fa fa-cloud text-info"></i>
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="deployment-box clearfix">
|
||||
<h4>{% block deployment-title %}{% endblock %}</h4>
|
||||
{% block deployment-info %}{% endblock %}
|
||||
<div class="deployment-buttons clearfix">
|
||||
{% block deployment-buttons %}
|
||||
<a
|
||||
href="{% url 'horizon:infrastructure:overview:undeploy_confirmation' %}"
|
||||
class="btn btn-danger ajax-modal">
|
||||
<i class="fa fa-trash"></i>
|
||||
{% trans "Undeploy" %}
|
||||
</a>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
@ -1,44 +0,0 @@
|
||||
{% extends "infrastructure/overview/deployment_base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load url from future%}
|
||||
|
||||
{% block deployment-icon %}
|
||||
<i class="fa fa-exclamation-circle text-danger"></i>
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-title %}
|
||||
{% if stack.is_delete_failed %}
|
||||
{% trans "Undeploying failed" %}
|
||||
{% elif stack.is_failed %}
|
||||
{% trans "Deployment failed" %}
|
||||
{% else %}
|
||||
{% trans "Failure" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-info %}
|
||||
{% if last_failed_events %}
|
||||
<strong>{% trans "Last failed events:" %}</strong>
|
||||
{% for event in last_failed_events %}
|
||||
<div>
|
||||
<dl>
|
||||
<dt>{% trans "Timestamp" %}</dt>
|
||||
<dd><time datetime="{{ event.event_time }}">{{ event.event_time }}</time></dd>
|
||||
<dt>{% trans "Resource Name" %}</dt>
|
||||
<dd>{{ event.resource_name }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ event.resource_status }}</dd>
|
||||
<dt>{% trans "Reason" %}</dt>
|
||||
<dd>{{ event.resource_status_reason }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<p><a href="{% url 'horizon:infrastructure:history:index' %}">See full log</a></p>
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-buttons %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
@ -1,23 +0,0 @@
|
||||
{% extends "infrastructure/overview/deployment_base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load url from future%}
|
||||
|
||||
{% block deployment-icon %}
|
||||
<i class="fa fa-exclamation-triangle text-warning"></i>
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-title %}{% trans "Initialization" %}{% endblock %}
|
||||
|
||||
{% block deployment-info %}
|
||||
<p>{% trans "Your OpenStack cloud is deployed but it needs to get initialized in order to get live." %}</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-buttons %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'horizon:infrastructure:overview:post_deploy_init' %}"
|
||||
class="btn btn-primary ajax-modal">
|
||||
<i class="fa fa-flag-checkered"></i>
|
||||
{% trans "Initialize" %}
|
||||
</a>
|
||||
{% endblock %}
|
@ -1,33 +0,0 @@
|
||||
{% extends "infrastructure/overview/deployment_base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load url from future%}
|
||||
|
||||
{% block deployment-icon %}
|
||||
<i class="fa fa-check-circle-o text-success"></i>
|
||||
{% endblock %}
|
||||
{% block deployment-title %}{% trans "Deployment is live" %}{% endblock %}
|
||||
{% block deployment-info %}
|
||||
<strong>{% trans "Access information" %}</strong>
|
||||
<dl>
|
||||
{% for dashboard_url in dashboard_urls %}
|
||||
<dt>{% trans "Horizon URL" %}</dt>
|
||||
<dd><a href="{{ dashboard_url }}">{{ dashboard_url }}</a></dd>
|
||||
{% endfor %}
|
||||
<dt>{% trans "User name" %}</dt>
|
||||
<dd>admin</dd>
|
||||
<dt></dt>
|
||||
<dd>
|
||||
<form>
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label class="control-label required" for="id_password">{% trans "Password" %}</label>
|
||||
<div>
|
||||
<input class="form-control" id="id_password" type="password" value="{{ admin_password }}" disabled="true" readonly="true"/>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</dd>
|
||||
</dl>
|
||||
{% endblock %}
|
@ -1,43 +0,0 @@
|
||||
{% extends "infrastructure/overview/deployment_base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load url from future%}
|
||||
|
||||
{% block deployment-icon %}
|
||||
{% if plan_invalid %}
|
||||
<i class="fa fa-exclamation-circle text-danger"></i>
|
||||
{% else %}
|
||||
<i class="fa fa-check-circle text-success"></i>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-title %}
|
||||
{% if plan_invalid %}
|
||||
{% trans "Design your deployment" %}
|
||||
{% else %}
|
||||
{% trans "Ready to get deployed" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-info %}
|
||||
<ul class="list-unstyled">
|
||||
{% for message in plan_messages %}
|
||||
<li class="{% if message.is_critical %}text-danger{% else %}text-warning{% endif %}"><p>
|
||||
{{ message.text }}
|
||||
{% if message.link_url %}
|
||||
<a href="{{ message.link_url }}">
|
||||
{{ message.link_label|default:message.link_url }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</p></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-buttons %}
|
||||
<a href="{% url 'horizon:infrastructure:overview:deploy_confirmation' %}"
|
||||
class="btn btn-primary ajax-modal btn-default {% if plan_invalid %}disabled{% endif %}">
|
||||
<i class="fa fa-rocket"></i>
|
||||
{% trans "Deploy" %}
|
||||
</a>
|
||||
{% endblock %}
|
@ -1,61 +0,0 @@
|
||||
{% extends "infrastructure/overview/deployment_base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load url from future%}
|
||||
|
||||
{% block deployment-icon %}
|
||||
<i class="fa fa-spinner text-info"></i>
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-title %}
|
||||
{% if stack.is_deleting %}
|
||||
{% trans "Undeploying..." %}
|
||||
{% elif stack.is_deploying %}
|
||||
{% trans "Deploying..." %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-info %}
|
||||
{% if progress %}
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar progress-bar-striped active"
|
||||
role="progressbar"
|
||||
aria-valuenow="{{ progress }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
style="width: {{ progress }}%"
|
||||
><span class="sr-only">{{ progress }}% {% trans "Complete" %}</span></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if last_failed_events %}
|
||||
<strong>{% trans "Last failed events:" %}</strong>
|
||||
{% for event in last_failed_events %}
|
||||
<div>
|
||||
<dl>
|
||||
<dt>{% trans "Timestamp" %}</dt>
|
||||
<dd><time datetime="{{ event.event_time }}">{{ event.event_time }}</time></dd>
|
||||
<dt>{% trans "Resource Name" %}</dt>
|
||||
<dd>{{ event.resource_name }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ event.resource_status }}</dd>
|
||||
<dt>{% trans "Reason" %}</dt>
|
||||
<dd>{{ event.resource_status_reason }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<p><a href="{% url 'horizon:infrastructure:history:index' %}">See full log</a></p>
|
||||
{% endblock %}
|
||||
|
||||
{% block deployment-buttons %}
|
||||
{% if stack.is_deploying %}
|
||||
<a
|
||||
href="{% url 'horizon:infrastructure:overview:undeploy_confirmation' %}"
|
||||
class="btn btn-danger ajax-modal">
|
||||
<i class="fa fa-close"></i>
|
||||
{% trans "Stop" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@ -1,11 +0,0 @@
|
||||
{% extends 'infrastructure/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Initialize" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Initialize Overcloud") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block infrastructure_main %}
|
||||
{% include "infrastructure/overview/_post_deploy_init.html" %}
|
||||
{% endblock %}
|
@ -1,32 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
{% load form_helpers %}
|
||||
|
||||
<h4>{% trans "Deployment Roles" %}</h4>
|
||||
<form method="POST" action="." class="deployment-roles-form">
|
||||
{% csrf_token %}
|
||||
{% include 'horizon/common/_form_errors.html' with form=form %}
|
||||
{% for role in roles %}
|
||||
<div class="form-group well well-sm clearfix{% if field.errors %} error{% endif %} {{ field.css_classes }}">
|
||||
<div class="col-sm-2">
|
||||
{{ role.field|add_bootstrap_class }}
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<a
|
||||
href="{% url "horizon:infrastructure:roles:detail" role_id=role.id %}"
|
||||
class="deployment-roles-label"
|
||||
>{{ role.name }}</a>
|
||||
{% for error in role.field.errors %}
|
||||
<span class="help-block"><span class="text-danger">
|
||||
{{ error }}
|
||||
</span></span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<hr>
|
||||
<button type="submit" class="btn btn-default">
|
||||
<i class="fa fa-save"></i>
|
||||
{% trans "Save changes" %}
|
||||
</button>
|
||||
</form>
|
@ -1,28 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
<h4>{% trans "Deployment Roles" %}</h4>
|
||||
{% for role in roles %}
|
||||
<div class="alert well-sm clearfix
|
||||
{% if role.error_node_count %}
|
||||
alert-danger
|
||||
{% elif role.deployed_node_count == role.planned_node_count %}
|
||||
alert-success
|
||||
{% else %}
|
||||
alert-info
|
||||
{% endif %}
|
||||
">
|
||||
<div class="col-sm-2">
|
||||
{% if role.deployed_node_count < role.planned_node_count %}
|
||||
<strong>{{ role.deployed_node_count }}</strong><small class="text-muted">/{{ role.planned_node_count }}</small>
|
||||
{% else %}
|
||||
<strong>{{ role.planned_node_count }}</strong>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<a
|
||||
href="{% url "horizon:infrastructure:roles:detail" role_id=role.id %}"
|
||||
>{{ role.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
@ -1,11 +0,0 @@
|
||||
{% extends 'infrastructure/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Undeploy overcloud" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Undeploy overcloud") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block infrastructure_main %}
|
||||
{% include "infrastructure/overview/_undeploy_confirmation.html" %}
|
||||
{% endblock %}
|
@ -15,12 +15,14 @@
|
||||
from django.conf import urls
|
||||
|
||||
from tuskar_ui.infrastructure.overview import views
|
||||
import tuskar_boxes.views
|
||||
import tuskar_boxes.overview.views as boxes_views
|
||||
|
||||
|
||||
urlpatterns = urls.patterns(
|
||||
'',
|
||||
urls.url(r'^$', tuskar_boxes.views.IndexView.as_view(), name='index'),
|
||||
urls.url(r'^$',
|
||||
boxes_views.IndexView.as_view(),
|
||||
name='index'),
|
||||
urls.url(r'^deploy-confirmation$',
|
||||
views.DeployConfirmationView.as_view(),
|
||||
name='deploy_confirmation'),
|
||||
|
@ -12,7 +12,40 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from tuskar_ui import api
|
||||
from tuskar_ui.infrastructure.overview import views
|
||||
|
||||
|
||||
def flavor_nodes(request, flavor):
|
||||
"""Lists all nodes that match the given flavor exactly."""
|
||||
for node in api.node.Node.list(request):
|
||||
if all([
|
||||
int(node.cpus) == int(flavor.vcpus),
|
||||
int(node.memory_mb) == int(flavor.ram),
|
||||
int(node.local_gb) == int(flavor.disk),
|
||||
# TODO(rdopieralski) add architecture when available
|
||||
]):
|
||||
yield node
|
||||
|
||||
class IndexView(views.IndexView):
|
||||
pass
|
||||
template_name = "tuskar_boxes/overview/index.html"
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super(IndexView, self).get_context_data(*args, **kwargs)
|
||||
flavors = api.flavor.Flavor.list(self.request)
|
||||
flavors.sort(key=lambda np: (np.vcpus, np.ram, np.disk))
|
||||
context['flavors'] = []
|
||||
for flavor in flavors:
|
||||
nodes = [{
|
||||
'role': '',
|
||||
} for node in flavor_nodes(self.request, flavor)]
|
||||
flavor = {
|
||||
'name': flavor.name,
|
||||
'vcpus': flavor.vcpus,
|
||||
'ram': flavor.ram,
|
||||
'disk': flavor.disk,
|
||||
'nodes': nodes,
|
||||
}
|
||||
if nodes: # Don't list empty flavors
|
||||
context['flavors'].append(flavor)
|
||||
return context
|
||||
|
@ -4,13 +4,6 @@
|
||||
|
||||
{% block title %}{% trans 'My OpenStack Deployment' %}{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{% if stack.is_deploying %}
|
||||
<meta http-equiv="refresh" content="30">
|
||||
{% endif %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include 'horizon/common/_domain_page_header.html' with title=_('My OpenStack Deployment') %}
|
||||
{% endblock page_header %}
|
||||
@ -36,7 +29,7 @@
|
||||
{% if stack %}
|
||||
{% include "infrastructure/overview/role_nodes_status.html" %}
|
||||
{% else %}
|
||||
{% include "infrastructure/overview/role_nodes_edit.html" %}
|
||||
{% include "tuskar_boxes/overview/role_nodes_edit.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,205 @@
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
{% load form_helpers %}
|
||||
|
||||
<h4>{% trans "Available Deployment Roles" %}</h4>
|
||||
<form method="POST" action="." class="boxes-form">
|
||||
{% csrf_token %}
|
||||
{% include 'horizon/common/_form_errors.html' with form=form %}
|
||||
|
||||
<div class="boxes-available-roles">
|
||||
{% for role in roles %}{% spaceless %}
|
||||
<div class="boxes-role-{{ role.name|slugify }} boxes-role" data-name="{{ role.name|slugify }}">
|
||||
{{ role.field|add_bootstrap_class }}
|
||||
{{ role.name|title }}
|
||||
</div>
|
||||
{% endspaceless %}{% endfor %}
|
||||
</div>
|
||||
|
||||
<h4>{% trans "Available Node Profiles" %}</h4>
|
||||
{% for flavor in flavors %}
|
||||
<div class="boxes-flavor">
|
||||
<p>
|
||||
<strong>Node Profile:</strong>
|
||||
<i>{{ flavor.name }}</i>
|
||||
{{ flavor.vcpus }} CPU,
|
||||
{{ flavor.ram }}MB RAM,
|
||||
{{ flavor.disk }}GB Disk
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-xs-5">
|
||||
<div class="boxes-drop-roles">
|
||||
</div>
|
||||
<div class="boxes-drop">
|
||||
<i class="fa fa-plus"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-7 boxes-nodes">
|
||||
{% for node in flavor.nodes %}{% spaceless %}
|
||||
<div class="boxes-node">free</div>
|
||||
{% endspaceless %}{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<hr>
|
||||
<button type="submit" class="btn btn-default">
|
||||
<i class="fa fa-save"></i>
|
||||
{% trans "Save changes" %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
(window.$ || window.addHorizonLoadEvent)(function () {
|
||||
function get_role_counts($flavor) {
|
||||
var roles = {};
|
||||
$flavor.find('div.boxes-drop-roles div.boxes-role').each(function () {
|
||||
var $this = $(this);
|
||||
var name = $this.data('name');
|
||||
var count = +$this.find('input.number-picker').val();
|
||||
roles[name] = count;
|
||||
});
|
||||
return roles;
|
||||
}
|
||||
|
||||
function update_boxes() {
|
||||
$('div.boxes-flavor').each(function () {
|
||||
var $flavor = $(this);
|
||||
var roles = get_role_counts($flavor);
|
||||
var role_names = Object.getOwnPropertyNames(roles);
|
||||
var count = 0;
|
||||
var role = 0;
|
||||
$flavor.find('div.boxes-nodes div.boxes-node').each(function () {
|
||||
var $this = $(this);
|
||||
$this.removeClass('boxes-role-controller boxes-role-compute boxes-role-block-storage boxes-role-object-storage');
|
||||
while (count >= roles[role_names[role]]) {
|
||||
role += 1;
|
||||
count = 0;
|
||||
}
|
||||
if (!role_names[role]) {
|
||||
$(this).html('free');
|
||||
} else {
|
||||
$this.addClass('boxes-role-' + role_names[role]).html(' ');
|
||||
}
|
||||
count += 1;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$('div.boxes-role').draggable({
|
||||
revert: 'invalid',
|
||||
helper: 'clone',
|
||||
stack: '.boxes-roles',
|
||||
opacity: 0.5
|
||||
});
|
||||
$('div.boxes-drop').droppable({
|
||||
accept: 'div.boxes-role',
|
||||
activeClass: 'boxes-drop-active',
|
||||
hoverClass: 'boxes-drop-hover',
|
||||
tolerance: 'touch',
|
||||
drop: function (ev, ui) {
|
||||
ui.draggable.appendTo($(this).prev('.boxes-drop-roles'));
|
||||
ui.draggable.find('input.number-picker').val(1);
|
||||
window.setTimeout(update_boxes, 0);
|
||||
}
|
||||
});
|
||||
$('div.boxes-available-roles').droppable({
|
||||
accept: 'div.boxes-role',
|
||||
activeClass: 'boxes-drop-active',
|
||||
hoverClass: 'boxes-drop-hover',
|
||||
tolerance: 'touch',
|
||||
drop: function (ev, ui) {
|
||||
ui.draggable.appendTo(this);
|
||||
ui.draggable.find('input.number-picker').val(0);
|
||||
window.setTimeout(update_boxes, 0);
|
||||
}
|
||||
});
|
||||
|
||||
update_boxes();
|
||||
$('input.number-picker').change(update_boxes);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
.boxes-node {
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #999;
|
||||
background: #eee;
|
||||
margin: 0 4px 4px 0;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
padding: 20px 4px 0 4px;
|
||||
}
|
||||
.boxes-available-roles {
|
||||
border-radius: 2px;
|
||||
background: #eee;
|
||||
border: 1px dashed #666;
|
||||
min-height: 42px;
|
||||
min-width: 120px;
|
||||
display: inline-block;
|
||||
padding: 4px 0 0 4px;
|
||||
}
|
||||
.boxes-role {
|
||||
display: inline-block;
|
||||
padding: 6px;
|
||||
border: 1px solid;
|
||||
width: 180px;
|
||||
cursor: move;
|
||||
border-radius: 2px;
|
||||
margin: 0 4px 4px 0;
|
||||
background-color: #fce94f;
|
||||
border-color: #edd400;
|
||||
color: #c4a000;
|
||||
}
|
||||
.boxes-available-roles .boxes-role {
|
||||
text-align: center;
|
||||
width: 120px;
|
||||
}
|
||||
.boxes-available-roles .boxes-role .form-control {
|
||||
display: none;
|
||||
}
|
||||
.boxes-role-controller {
|
||||
background-color: #fcaf3e;
|
||||
border-color: #f57900;
|
||||
color: #ce5c00;
|
||||
}
|
||||
.boxes-role-compute {
|
||||
background-color: #8ae234;
|
||||
border-color: #73d216;
|
||||
color: #4e9a06;
|
||||
}
|
||||
.boxes-role-object-storage {
|
||||
background-color: #729fcf;
|
||||
border-color: #3465a4;
|
||||
color: #204a87;
|
||||
}
|
||||
.boxes-role-block-storage {
|
||||
background-color: #ad7fa8;
|
||||
border-color: #75507b;
|
||||
color: #5c3566;
|
||||
}
|
||||
.boxes-drop {
|
||||
display: inline-block;
|
||||
padding: 6px;
|
||||
border: 1px dashed;
|
||||
width: 180px;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
background-color: #eee;
|
||||
border-color: #666;
|
||||
color: #444;
|
||||
}
|
||||
.boxes-drop-active {
|
||||
background-color: #ccc;
|
||||
border-style: solid;
|
||||
}
|
||||
.boxes-drop-hover {
|
||||
background-color: #999;
|
||||
border-style: solid;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user