Merge remote branch 'origin/master'
This commit is contained in:
commit
fb60f932c4
@ -191,6 +191,8 @@ def tenant_update(request, tenant_id, description, enabled):
|
||||
def token_create(request, tenant, username, password):
|
||||
return auth_api().tokens.create(tenant, username, password)
|
||||
|
||||
def tenant_quota_get(request, tenant):
|
||||
return admin_api(request).quota_sets.get(tenant)
|
||||
|
||||
def token_info(request, token):
|
||||
hdrs = {"Content-type": "application/json",
|
||||
|
@ -10,6 +10,7 @@ KEYPAIRS = r'^(?P<tenant_id>[^/]+)/keypairs/%s$'
|
||||
urlpatterns = patterns('django_openstack.dash.views.instances',
|
||||
url(r'^(?P<tenant_id>[^/]+)/$', 'usage', name='dash_usage'),
|
||||
url(r'^(?P<tenant_id>[^/]+)/instances/$', 'index', name='dash_instances'),
|
||||
url(r'^(?P<tenant_id>[^/]+)/instances/refresh$', 'refresh', name='dash_instances_refresh'),
|
||||
url(INSTANCES % 'console', 'console', name='dash_instances_console'),
|
||||
url(INSTANCES % 'vnc', 'vnc', name='dash_instances_vnc'),
|
||||
url(INSTANCES % 'update', 'update', name='dash_instances_update'),
|
||||
|
@ -175,6 +175,10 @@ def launch(request, tenant_id, image_id):
|
||||
|
||||
try:
|
||||
image = api.image_get(request, image_id)
|
||||
tenant = api.token_get_tenant(request, request.user.tenant)
|
||||
quotas = api.tenant_quota_get(request, request.user.tenant)
|
||||
quotas.ram = int(quotas.ram)/100
|
||||
|
||||
except Exception, e:
|
||||
messages.error(request, 'Unable to retrieve image %s: %s' %
|
||||
(image_id, e.message))
|
||||
@ -199,4 +203,5 @@ def launch(request, tenant_id, image_id):
|
||||
'tenant': tenant,
|
||||
'image': image,
|
||||
'form': form,
|
||||
'quotas': quotas,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
@ -109,7 +109,34 @@ def index(request, tenant_id):
|
||||
'reboot_form': reboot_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def refresh(request, tenant_id):
|
||||
for f in (TerminateInstance, RebootInstance):
|
||||
_, handled = f.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
instances = []
|
||||
try:
|
||||
image_dict = api.image_all_metadata(request)
|
||||
instances = api.server_list(request)
|
||||
for instance in instances:
|
||||
# FIXME - ported this over, but it is hacky
|
||||
instance.attrs['image_name'] =\
|
||||
image_dict.get(int(instance.attrs['image_ref']),{}).get('name')
|
||||
except Exception as e:
|
||||
messages.error(request, 'Unable to get instance list: %s' % e.message)
|
||||
|
||||
# We don't have any way of showing errors for these, so don't bother
|
||||
# trying to reuse the forms from above
|
||||
terminate_form = TerminateInstance()
|
||||
reboot_form = RebootInstance()
|
||||
|
||||
return render_to_response('_instance_list.html', {
|
||||
'instances': instances,
|
||||
'terminate_form': terminate_form,
|
||||
'reboot_form': reboot_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def usage(request, tenant_id=None):
|
||||
today = datetime.date.today()
|
||||
|
@ -12,6 +12,7 @@ urlpatterns = patterns('django_openstack.syspanel.views.instances',
|
||||
url(r'^usage/(?P<tenant_id>[^/]+)$', 'tenant_usage',
|
||||
name='syspanel_tenant_usage'),
|
||||
url(r'^instances/$', 'index', name='syspanel_instances'),
|
||||
url(r'^instances/refresh$', 'refresh', name='syspanel_instances_refresh'),
|
||||
# NOTE(termie): currently just using the 'dash' versions
|
||||
#url(INSTANCES % 'console', 'console', name='syspanel_instances_console'),
|
||||
#url(INSTANCES % 'vnc', 'vnc', name='syspanel_instances_vnc'),
|
||||
@ -25,6 +26,10 @@ urlpatterns += patterns('django_openstack.syspanel.views.images',
|
||||
)
|
||||
|
||||
|
||||
urlpatterns += patterns('django_openstack.syspanel.views.quotas',
|
||||
url(r'^quotas/$', 'index', name='syspanel_quotas'),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('django_openstack.syspanel.views.flavors',
|
||||
url(r'^flavors/$', 'index', name='syspanel_flavors'),
|
||||
url(r'^flavors/create/$', 'create', name='syspanel_flavors_create'),
|
||||
@ -45,7 +50,8 @@ urlpatterns += patterns('django_openstack.syspanel.views.services',
|
||||
|
||||
urlpatterns += patterns('django_openstack.syspanel.views.tenants',
|
||||
url(r'^tenants/$', 'index', name='syspanel_tenants'),
|
||||
url(r'^tenants/create$', 'create', name='syspanel_tenants_create'),
|
||||
url(TENANTS % 'update', 'update', name='syspanel_tenant_update'),
|
||||
url(TENANTS % 'users', 'users', name='syspanel_tenant_users'),
|
||||
url(r'^tenants/create$', 'create', name='syspanel_tenants_create'),
|
||||
url(TENANTS % 'quotas', 'quotas', name='syspanel_tenant_quotas'),
|
||||
)
|
||||
|
@ -196,3 +196,32 @@ def index(request):
|
||||
'terminate_form': terminate_form,
|
||||
'reboot_form': reboot_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def refresh(request):
|
||||
for f in (TerminateInstance, RebootInstance):
|
||||
_, handled = f.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
instances = []
|
||||
try:
|
||||
image_dict = api.image_all_metadata(request)
|
||||
instances = api.server_list(request)
|
||||
for instance in instances:
|
||||
# FIXME - ported this over, but it is hacky
|
||||
instance._info['attrs']['image_name'] =\
|
||||
image_dict.get(int(instance.attrs['image_ref']),{}).get('name')
|
||||
except Exception as e:
|
||||
messages.error(request, 'Unable to get instance list: %s' % e.message)
|
||||
|
||||
# We don't have any way of showing errors for these, so don't bother
|
||||
# trying to reuse the forms from above
|
||||
terminate_form = TerminateInstance()
|
||||
reboot_form = RebootInstance()
|
||||
|
||||
return render_to_response('_syspanel_instance_list.html', {
|
||||
'instances': instances,
|
||||
'terminate_form': terminate_form,
|
||||
'reboot_form': reboot_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
28
django-openstack/django_openstack/syspanel/views/quotas.py
Normal file
28
django-openstack/django_openstack/syspanel/views/quotas.py
Normal file
@ -0,0 +1,28 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
from operator import itemgetter
|
||||
|
||||
from django import template
|
||||
from django import http
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import redirect
|
||||
from django.shortcuts import render_to_response
|
||||
from openstackx.api import exceptions as api_exceptions
|
||||
|
||||
|
||||
from django_openstack import api
|
||||
from django_openstack import forms
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
quotas = api.admin_api(request).quota_sets.get(True)._info
|
||||
quotas['ram'] = int(quotas['ram']) / 100
|
||||
quotas.pop('id')
|
||||
|
||||
return render_to_response('syspanel_quotas.html',{
|
||||
'quotas': quotas,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
|
@ -92,6 +92,38 @@ class UpdateTenant(forms.SelfHandlingForm):
|
||||
messages.error(request, 'Unable to update tenant: %s' % e.message)
|
||||
return redirect('syspanel_tenants')
|
||||
|
||||
class UpdateQuotas(forms.SelfHandlingForm):
|
||||
tenant_id = forms.CharField(label="ID (name)", widget=forms.TextInput(attrs={'readonly':'readonly'}))
|
||||
metadata_items = forms.CharField(label="Metadata Items")
|
||||
injected_files = forms.CharField(label="Injected Files")
|
||||
injected_file_content_bytes = forms.CharField(label="Injected File Content Bytes")
|
||||
cores = forms.CharField(label="VCPUs")
|
||||
instances = forms.CharField(label="Instances")
|
||||
volumes = forms.CharField(label="Volumes")
|
||||
gigabytes = forms.CharField(label="Gigabytes")
|
||||
ram = forms.CharField(label="RAM (in MB)")
|
||||
floating_ips = forms.CharField(label="Floating IPs")
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
api.admin_api(request).quota_sets.update(data['tenant_id'],
|
||||
metadata_items=data['metadata_items'],
|
||||
injected_file_content_bytes=
|
||||
data['injected_file_content_bytes'],
|
||||
volumes=data['volumes'],
|
||||
gigabytes=data['gigabytes'],
|
||||
ram=int(data['ram']) * 100,
|
||||
floating_ips=data['floating_ips'],
|
||||
instances=data['instances'],
|
||||
injected_files=data['injected_files'],
|
||||
cores=data['cores'],
|
||||
)
|
||||
messages.success(request,
|
||||
'Quotas for %s were successfully updated.'
|
||||
% data['tenant_id'])
|
||||
except api_exceptions.ApiException, e:
|
||||
messages.error(request, 'Unable to update quotas: %s' % e.message)
|
||||
return redirect('syspanel_tenants')
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
@ -171,3 +203,32 @@ def users(request, tenant_id):
|
||||
'users': users,
|
||||
'new_users': new_user_ids,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def quotas(request, tenant_id):
|
||||
for f in (UpdateQuotas,):
|
||||
_, handled = f.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
quotas = api.admin_api(request).quota_sets.get(tenant_id)
|
||||
quota_set = {
|
||||
'tenant_id': quotas.id,
|
||||
'metadata_items': quotas.metadata_items,
|
||||
'injected_file_content_bytes': quotas.injected_file_content_bytes,
|
||||
'volumes': quotas.volumes,
|
||||
'gigabytes': quotas.gigabytes,
|
||||
'ram': int(quotas.ram) / 100,
|
||||
'floating_ips': quotas.floating_ips,
|
||||
'instances': quotas.instances,
|
||||
'injected_files': quotas.injected_files,
|
||||
'cores': quotas.cores,
|
||||
}
|
||||
form = UpdateQuotas(initial=quota_set)
|
||||
|
||||
return render_to_response(
|
||||
'syspanel_tenant_quotas.html',{
|
||||
'form': form,
|
||||
'tenant_id': tenant_id,
|
||||
'quotas': quotas,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
|
@ -0,0 +1,6 @@
|
||||
{% extends "_quotas_form.html" %}
|
||||
|
||||
{% block submit %}
|
||||
<input type="submit" value="Update Quotas" class="large-rounded" />
|
||||
{% endblock %}
|
||||
|
12
openstack-dashboard/dashboard/templates/_quotas_form.html
Normal file
12
openstack-dashboard/dashboard/templates/_quotas_form.html
Normal file
@ -0,0 +1,12 @@
|
||||
<form id="quotas_form" action="" method="post">
|
||||
{% csrf_token %}
|
||||
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
|
||||
{% for field in form.visible_fields %}
|
||||
{{ field.label_tag }}
|
||||
{{ field.errors }}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
{% block submit %}
|
||||
{% endblock %}
|
||||
</form>
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% load parse_date %}
|
||||
<table class="wide">
|
||||
<table id="instances" class="wide">
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>User</th>
|
||||
|
@ -8,5 +8,6 @@
|
||||
<li><a {% if current_sidebar == "images" %} class="active" {% endif %} href="{% url syspanel_images %}">Images</a></li>
|
||||
<li><a {% if current_sidebar == "tenants" %} class="active" {% endif %} href="{% url syspanel_tenants %}">Tenants</a></li>
|
||||
<li><a {% if current_sidebar == "users" %} class="active" {% endif %} href="{% url syspanel_users %}">Users</a></li>
|
||||
<li><a {% if current_sidebar == "quotas" %} class="active" {% endif %} href="{% url syspanel_quotas %}">Quotas</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -15,6 +15,7 @@
|
||||
<li>{% include "_tenant_delete.html" with form=tenant_delete_form %}</li>
|
||||
<li><a href="{% url syspanel_tenant_update tenant.id %}">Edit</a></li>
|
||||
<li><a href="{% url syspanel_tenant_users tenant.id %}">View Members</a></li>
|
||||
<li><a href="{% url syspanel_tenant_quotas tenant.id %}">Modify Quotas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -12,6 +12,7 @@
|
||||
<div id='page_header'>
|
||||
<h2><span>Compute:</span> Instances</h2>
|
||||
<p class='desc'><span>—</span> Instances are virtual servers launched from images.</p>
|
||||
<div id="spinner" class="dash">Loading...</div>
|
||||
</div>
|
||||
|
||||
{% include "_messages.html" %}
|
||||
@ -41,3 +42,26 @@
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block footer_js %}
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
$(function(){
|
||||
$("#spinner").hide()
|
||||
function loadInstances(){
|
||||
$('#spinner').show();
|
||||
$('#instances').load('{% url dash_instances_refresh request.user.tenant %}', function(){
|
||||
$("#spinner").hide()
|
||||
});
|
||||
}
|
||||
setInterval(function(){
|
||||
loadInstances();
|
||||
}, 15000);
|
||||
|
||||
$("a.refresh").click(function(e){
|
||||
e.preventDefault()
|
||||
loadInstances();
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{% endblock footer_js %}
|
@ -24,11 +24,36 @@
|
||||
</div>
|
||||
|
||||
{% include '_launch.html' %}
|
||||
|
||||
<div class="right">
|
||||
<h3>Description:</h3>
|
||||
<p>Specify the details for launching an instance.</p>
|
||||
</div>
|
||||
<p>Specify the details for launching an instance. Also please make note of the table below; all tenants have quotas which define the limit of resources you are allowed to provision.</p>
|
||||
<table border="0">
|
||||
<tr>
|
||||
<th>Quota Name</th>
|
||||
<th>Limit</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RAM (MB)</td>
|
||||
<td>{{quotas.ram}}MB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Floating IPs</td>
|
||||
<td>{{quotas.floating_ips}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Instances</td>
|
||||
<td>{{quotas.instances}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes</td>
|
||||
<td>{{quotas.volumes}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gigabytes</td>
|
||||
<td>{{quotas.gigabytes}}GB</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -12,6 +12,7 @@
|
||||
<div id='page_header'>
|
||||
<h2><span>System Panel:</span> Instances</h2>
|
||||
<p class='desc'><span>—</span> View all running instances other than VPNs.</p>
|
||||
<div id="spinner" class="syspanel">Loading...</div>
|
||||
</div>
|
||||
|
||||
{% include "_messages.html" %}
|
||||
@ -43,3 +44,25 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer_js %}
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
$(function(){
|
||||
$("#spinner").hide()
|
||||
function loadInstances(){
|
||||
$('#spinner').show();
|
||||
$('#instances').load('{% url syspanel_instances_refresh %}', function(){
|
||||
$("#spinner").hide()
|
||||
});
|
||||
}
|
||||
setInterval(function(){
|
||||
loadInstances();
|
||||
}, 15000);
|
||||
|
||||
$("a.refresh").click(function(e){
|
||||
e.preventDefault()
|
||||
loadInstances();
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{% endblock footer_js %}
|
48
openstack-dashboard/dashboard/templates/syspanel_quotas.html
Normal file
48
openstack-dashboard/dashboard/templates/syspanel_quotas.html
Normal file
@ -0,0 +1,48 @@
|
||||
{% extends 'syspanel_base.html' %}
|
||||
{# list of tenants #}
|
||||
{# standard nav, sidebar, list of instances in main #}
|
||||
|
||||
{% block sidebar %}
|
||||
{% with current_sidebar="quotas" %}
|
||||
{{block.super}}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div id='page_header'>
|
||||
<h2><span>System Panel:</span> Quotas</h2>
|
||||
<p class='desc'><span>—</span>Default Quotas</p>
|
||||
</div>
|
||||
{% include "_messages.html" %}
|
||||
<div class="main_content">
|
||||
<div class='table_title wide'>
|
||||
<h3>Default Quotas</h3>
|
||||
<a class="refresh" title="Refresh" href="{% url syspanel_quotas %}">Refresh List</a>
|
||||
<div class='search'>
|
||||
<form action='' method='post'>
|
||||
<fieldset>
|
||||
<label for='table_search'>Search</label>
|
||||
<input id='table_search' name='search' type='text' value='' />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="wide">
|
||||
<tr>
|
||||
<th>Quota Name</th>
|
||||
<th>Limit</th>
|
||||
</tr>
|
||||
{% for name,value in quotas.items %}
|
||||
<tr>
|
||||
<td>{{name}}</td>
|
||||
<td>{{value}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
|
||||
<a id="tenant_create_link" class="action_link large-rounded" href="{% url syspanel_tenants_create %}">Create New Tenant </a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,33 @@
|
||||
{% extends 'syspanel_base.html' %}
|
||||
{# list of tenants #}
|
||||
{# standard nav, sidebar, list of instances in main #}
|
||||
|
||||
{% block sidebar %}
|
||||
{% with current_sidebar="tenants" %}
|
||||
{{block.super}}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div id='page_header'>
|
||||
<h2><span>System Panel:</span> Tenants</h2>
|
||||
<p class='desc'><span>—</span>Manage Tenants Quotas</p>
|
||||
</div>
|
||||
{% include "_messages.html" %}
|
||||
|
||||
<div class="main_content">
|
||||
<div class="dash_block wide form">
|
||||
<div class='title_block'>
|
||||
<h3>Update Tenant Quotas</h3>
|
||||
</div>
|
||||
{% include 'django_openstack/syspanel/_update_quotas_form.html' with form=form %}
|
||||
|
||||
<div class="right">
|
||||
<h3>Description:</h3>
|
||||
<p>From here you can edit quotas (max limits) for the tenant {{tenant_id}}.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -1692,3 +1692,26 @@ li.title h4{
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
#spinner {
|
||||
background: url(../images/spinner.gif) top left no-repeat;
|
||||
float: right;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
#spinner.dash {
|
||||
position: relative;
|
||||
right: 10px;
|
||||
bottom: 0;
|
||||
margin-bottom: -24px;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
#spinner.syspanel {
|
||||
position: relative;
|
||||
right: 20px;
|
||||
top: -24px;
|
||||
bottom: 0;
|
||||
margin-bottom: -24px;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user