Merge remote branch 'origin/master'

This commit is contained in:
Anthony Young 2011-07-13 09:06:22 -07:00
commit fb60f932c4
19 changed files with 360 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -0,0 +1,6 @@
{% extends "_quotas_form.html" %}
{% block submit %}
<input type="submit" value="Update Quotas" class="large-rounded" />
{% endblock %}

View 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>

View File

@ -1,5 +1,5 @@
{% load parse_date %}
<table class="wide">
<table id="instances" class="wide">
<tr>
<th>Id</th>
<th>User</th>

View File

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

View File

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

View File

@ -12,6 +12,7 @@
<div id='page_header'>
<h2><span>Compute:</span> Instances</h2>
<p class='desc'><span>&mdash;</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 %}

View File

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

View File

@ -12,6 +12,7 @@
<div id='page_header'>
<h2><span>System Panel:</span> Instances</h2>
<p class='desc'><span>&mdash;</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 %}

View 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>&mdash;</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 %}

View File

@ -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>&mdash;</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 %}

View File

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