Support for VERY basic Swift container and object management.
This commit is contained in:
parent
23e928c1e2
commit
dfdcc259f4
@ -2,17 +2,16 @@
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
import logging
|
||||
|
||||
import cloudfiles
|
||||
import glance.client
|
||||
import httplib
|
||||
import json
|
||||
import logging
|
||||
import openstack.compute
|
||||
import openstackx.admin
|
||||
import openstackx.extras
|
||||
import openstackx.auth
|
||||
from urlparse import urlparse
|
||||
import json
|
||||
|
||||
|
||||
LOG = logging.getLogger('django_openstack.api')
|
||||
@ -76,7 +75,15 @@ def extras_api(request):
|
||||
def auth_api():
|
||||
LOG.debug('auth_api connection created using url "%s"' %
|
||||
settings.OPENSTACK_KEYSTONE_URL)
|
||||
return openstackx.auth.Auth(management_url=settings.OPENSTACK_KEYSTONE_URL)
|
||||
return openstackx.auth.Auth(
|
||||
management_url=settings.OPENSTACK_KEYSTONE_URL)
|
||||
|
||||
|
||||
def swift_api():
|
||||
return cloudfiles.get_connection(
|
||||
settings.SWIFT_ACCOUNT + ":" + settings.SWIFT_USER,
|
||||
settings.SWIFT_PASS,
|
||||
authurl=settings.SWIFT_AUTHURL)
|
||||
|
||||
|
||||
def console_create(request, instance_id, kind=None):
|
||||
@ -270,3 +277,36 @@ def user_update_password(request, user_id, password):
|
||||
|
||||
def user_update_tenant(request, user_id, tenant_id):
|
||||
return account_api(request).users.update_tenant(user_id, tenant_id)
|
||||
|
||||
|
||||
def swift_get_containers():
|
||||
return [{"name":c.name} for c in swift_api().get_all_containers()]
|
||||
|
||||
|
||||
def swift_create_container(name):
|
||||
return swift_api().create_container(name)
|
||||
|
||||
|
||||
def swift_delete_container(name):
|
||||
return swift_api().delete_container(name)
|
||||
|
||||
|
||||
def swift_get_objects(container_name):
|
||||
container = swift_api().get_container(container_name)
|
||||
return [{"name":obj.name} for obj in container.get_objects()]
|
||||
|
||||
|
||||
def swift_upload_object(container_name, object_name, object_data):
|
||||
container = swift_api().get_container(container_name)
|
||||
obj = container.create_object(object_name)
|
||||
return obj.write(object_data)
|
||||
|
||||
|
||||
def swift_delete_object(container_name, object_name):
|
||||
container = swift_api().get_container(container_name)
|
||||
return container.delete_object(object_name)
|
||||
|
||||
|
||||
def swift_get_object_data(container_name, object_name):
|
||||
container = swift_api().get_container(container_name)
|
||||
return container.get_object(object_name).stream()
|
||||
|
@ -1,4 +1,4 @@
|
||||
|
||||
from django.conf import settings
|
||||
from django_openstack import api
|
||||
|
||||
|
||||
@ -6,3 +6,7 @@ def tenants(request):
|
||||
if not request.user or not request.user.is_authenticated():
|
||||
return {}
|
||||
return {'tenants': api.token_list_tenants(request, request.user.token)}
|
||||
|
||||
|
||||
def swift(request):
|
||||
return {'swift_configured': hasattr(settings, "SWIFT_AUTHURL")}
|
||||
|
@ -1,11 +1,12 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
from django.conf.urls.defaults import *
|
||||
from django.conf import settings
|
||||
|
||||
INSTANCES = r'^(?P<tenant_id>[^/]+)/instances/(?P<instance_id>[^/]+)/%s$'
|
||||
IMAGES = r'^(?P<tenant_id>[^/]+)/images/(?P<image_id>[^/]+)/%s$'
|
||||
KEYPAIRS = r'^(?P<tenant_id>[^/]+)/keypairs/%s$'
|
||||
CONTAINERS = r'^(?P<tenant_id>[^/]+)/containers/%s$'
|
||||
OBJECTS = r'^(?P<tenant_id>[^/]+)/containers/(?P<container_name>[^/]+)/%s$'
|
||||
|
||||
urlpatterns = patterns('django_openstack.dash.views.instances',
|
||||
url(r'^(?P<tenant_id>[^/]+)/$', 'usage', name='dash_usage'),
|
||||
@ -23,3 +24,16 @@ urlpatterns += patterns('django_openstack.dash.views.keypairs',
|
||||
url(r'^(?P<tenant_id>[^/]+)/keypairs/$', 'index', name='dash_keypairs'),
|
||||
url(KEYPAIRS % 'create', 'create', name='dash_keypairs_create'),
|
||||
)
|
||||
|
||||
# Swift containers and objects.
|
||||
urlpatterns += patterns('django_openstack.dash.views.containers',
|
||||
url(CONTAINERS % '', 'index', name='dash_containers'),
|
||||
url(CONTAINERS % 'create', 'create', name='dash_containers_create'),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('django_openstack.dash.views.objects',
|
||||
url(OBJECTS % '', 'index', name='dash_objects'),
|
||||
url(OBJECTS % 'upload', 'upload', name='dash_objects_upload'),
|
||||
url(OBJECTS % '(?P<object_name>[^/]+)/download',
|
||||
'download', name='dash_objects_download'),
|
||||
)
|
||||
|
73
django-openstack/django_openstack/dash/views/containers.py
Normal file
73
django-openstack/django_openstack/dash/views/containers.py
Normal file
@ -0,0 +1,73 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
"""
|
||||
Views for managing Swift containers.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from django import template
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django import shortcuts
|
||||
from django.shortcuts import render_to_response
|
||||
|
||||
from django_openstack import api
|
||||
from django_openstack import forms
|
||||
|
||||
from cloudfiles.errors import ContainerNotEmpty
|
||||
|
||||
|
||||
LOG = logging.getLogger('django_openstack.dash')
|
||||
|
||||
|
||||
class DeleteContainer(forms.SelfHandlingForm):
|
||||
container_name = forms.CharField(widget=forms.HiddenInput())
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
api.swift_delete_container(data['container_name'])
|
||||
except ContainerNotEmpty, e:
|
||||
messages.error(request,
|
||||
'Unable to delete non-empty container: %s' % \
|
||||
data['container_name'])
|
||||
LOG.error('Unable to delete container "%s". Exception: "%s"' %
|
||||
(data['container_name'], str(e)))
|
||||
else:
|
||||
messages.info(request,
|
||||
'Successfully deleted container: %s' % \
|
||||
data['container_name'])
|
||||
return shortcuts.redirect(request.build_absolute_uri())
|
||||
|
||||
|
||||
class CreateContainer(forms.SelfHandlingForm):
|
||||
name = forms.CharField(max_length="255", label="Container Name")
|
||||
|
||||
def handle(self, request, data):
|
||||
api.swift_create_container(data['name'])
|
||||
messages.success(request, "Container was successfully created.")
|
||||
return shortcuts.redirect(request.build_absolute_uri())
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request, tenant_id):
|
||||
delete_form, handled = DeleteContainer.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
containers = api.swift_get_containers()
|
||||
|
||||
return render_to_response('dash_containers.html', {
|
||||
'containers': containers,
|
||||
'delete_form': delete_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def create(request, tenant_id):
|
||||
form, handled = CreateContainer.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
return render_to_response('dash_containers_create.html', {
|
||||
'create_form': form,
|
||||
}, context_instance=template.RequestContext(request))
|
@ -85,7 +85,7 @@ def index(request, tenant_id):
|
||||
except api_exceptions.ApiException, e:
|
||||
keypairs = []
|
||||
LOG.error("ApiException in keypair index", exc_info=True)
|
||||
messages.error(request, 'Error featching keypairs: %s' % e.message)
|
||||
messages.error(request, 'Error fetching keypairs: %s' % e.message)
|
||||
|
||||
return render_to_response('dash_keypairs.html', {
|
||||
'keypairs': keypairs,
|
||||
|
85
django-openstack/django_openstack/dash/views/objects.py
Normal file
85
django-openstack/django_openstack/dash/views/objects.py
Normal file
@ -0,0 +1,85 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
"""
|
||||
Views for managing Swift containers.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from django import http
|
||||
from django import template
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django import shortcuts
|
||||
from django.shortcuts import render_to_response
|
||||
|
||||
from django_openstack import api
|
||||
from django_openstack import forms
|
||||
|
||||
|
||||
LOG = logging.getLogger('django_openstack.dash')
|
||||
|
||||
|
||||
class DeleteObject(forms.SelfHandlingForm):
|
||||
object_name = forms.CharField(widget=forms.HiddenInput())
|
||||
|
||||
def handle(self, request, data):
|
||||
api.swift_delete_object(
|
||||
request.POST['container_name'],
|
||||
data['object_name'])
|
||||
messages.info(request,
|
||||
'Successfully deleted object: %s' % \
|
||||
data['object_name'])
|
||||
return shortcuts.redirect(request.build_absolute_uri())
|
||||
|
||||
|
||||
class UploadObject(forms.SelfHandlingForm):
|
||||
name = forms.CharField(max_length="255", label="Object Name")
|
||||
object_file = forms.FileField(label="File")
|
||||
|
||||
def handle(self, request, data):
|
||||
api.swift_upload_object(
|
||||
request.POST['container_name'],
|
||||
data['name'],
|
||||
request.FILES['object_file'].read())
|
||||
messages.success(request, "Object was successfully uploaded.")
|
||||
return shortcuts.redirect(request.build_absolute_uri())
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request, tenant_id, container_name):
|
||||
delete_form, handled = DeleteObject.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
objects = api.swift_get_objects(container_name)
|
||||
|
||||
return render_to_response('dash_objects.html', {
|
||||
'container_name': container_name,
|
||||
'objects': objects,
|
||||
'delete_form': delete_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def upload(request, tenant_id, container_name):
|
||||
form, handled = UploadObject.maybe_handle(request)
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
return render_to_response('dash_objects_upload.html', {
|
||||
'container_name': container_name,
|
||||
'upload_form': form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def download(request, tenant_id, container_name, object_name):
|
||||
object_data = api.swift_get_object_data(
|
||||
container_name, object_name)
|
||||
|
||||
response = http.HttpResponse()
|
||||
response['Content-Disposition'] = 'attachment; filename=%s' % \
|
||||
object_name
|
||||
for data in object_data:
|
||||
response.write(data)
|
||||
return response
|
@ -138,7 +138,10 @@ class SelfHandlingForm(Form):
|
||||
return cls(*args, **kwargs), None
|
||||
|
||||
try:
|
||||
form = cls(request.POST, *args, **kwargs)
|
||||
if request.FILES:
|
||||
form = cls(request.POST, request.FILES, *args, **kwargs)
|
||||
else:
|
||||
form = cls(request.POST, *args, **kwargs)
|
||||
|
||||
if not form.is_valid():
|
||||
return form, None
|
||||
|
@ -44,6 +44,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
'django.core.context_processors.media',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'django_openstack.context_processors.tenants',
|
||||
'django_openstack.context_processors.swift',
|
||||
)
|
||||
|
||||
TEMPLATE_LOADERS = (
|
||||
|
10
openstack-dashboard/dashboard/templates/_container_form.html
Normal file
10
openstack-dashboard/dashboard/templates/_container_form.html
Normal file
@ -0,0 +1,10 @@
|
||||
<form id="container_form" 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 %}
|
||||
<input type="submit" value="Create Container" class="large-rounded" />
|
||||
</form>
|
17
openstack-dashboard/dashboard/templates/_container_list.html
Normal file
17
openstack-dashboard/dashboard/templates/_container_list.html
Normal file
@ -0,0 +1,17 @@
|
||||
<table id="containers" style="width: 100%">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th style="width: 100px;">Actions</th>
|
||||
</tr>
|
||||
{% for container in containers %}
|
||||
<tr class="{% cycle 'odd' 'even' %}">
|
||||
<td>{{ container.name }}</td>
|
||||
<td id="actions">
|
||||
<ul>
|
||||
<li><a href="{% url dash_objects request.user.tenant container.name %}">List Objects</a></li>
|
||||
<li>{% include "_delete_container.html" with form=delete_form %}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
@ -6,4 +6,10 @@
|
||||
<li><a {% if current_sidebar == "images" %} class="active" {% endif %} href="{% url dash_images request.user.tenant %}">Images</a></li>
|
||||
<li><a {% if current_sidebar == "keypairs" %} class="active" {% endif %} href="{% url dash_keypairs request.user.tenant %}">Keypairs</a></li>
|
||||
</ul>
|
||||
{% if swift_configured %}
|
||||
<h3>Manage Object Store</h3>
|
||||
<ul class='sub_nav'>
|
||||
<li><a {% if current_sidebar == "containers" %} class="active" {% endif %} href="{% url dash_containers request.user.tenant %}">Containers</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -0,0 +1,8 @@
|
||||
<form id="form_delete_{{container.name}}" class="form-delete" method="post">
|
||||
{% csrf_token %}
|
||||
{% for hidden in form.hidden_fields %}
|
||||
{{hidden}}
|
||||
{% endfor %}
|
||||
<input name="container_name" type="hidden" value="{{container.name}}" />
|
||||
<input id="delete_{{container.name}}" class="delete" title="Container: {{container.name}}" type="submit" value="Delete" />
|
||||
</form>
|
@ -0,0 +1,9 @@
|
||||
<form id="form_delete_{{ object.name }}" class="form-delete" method="post">
|
||||
{% csrf_token %}
|
||||
{% for hidden in form.hidden_fields %}
|
||||
{{hidden}}
|
||||
{% endfor %}
|
||||
<input type="hidden" name="container_name" value="{{ container_name }}" />
|
||||
<input name="object_name" type="hidden" value="{{ object.name }}" />
|
||||
<input id="delete_{{ object.name }}" class="delete" title="Object: {{ object.name }}" type="submit" value="Delete" />
|
||||
</form>
|
11
openstack-dashboard/dashboard/templates/_object_form.html
Normal file
11
openstack-dashboard/dashboard/templates/_object_form.html
Normal file
@ -0,0 +1,11 @@
|
||||
<form id="object_form" enctype="multipart/form-data" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="container_name" value="{{ container_name }}" />
|
||||
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
|
||||
{% for field in form.visible_fields %}
|
||||
{{ field.label_tag }}
|
||||
{{ field.errors }}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
<input type="submit" value="Upload Object" class="large-rounded" />
|
||||
</form>
|
17
openstack-dashboard/dashboard/templates/_object_list.html
Normal file
17
openstack-dashboard/dashboard/templates/_object_list.html
Normal file
@ -0,0 +1,17 @@
|
||||
<table id="objects" style="width: 100%">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th style="width: 100px;">Actions</th>
|
||||
</tr>
|
||||
{% for object in objects %}
|
||||
<tr class="{% cycle 'odd' 'even' %}">
|
||||
<td>{{ object.name }}</td>
|
||||
<td id="actions">
|
||||
<ul>
|
||||
<li>{% include "_delete_object.html" with form=delete_form %}</li>
|
||||
<li><a href="{% url dash_objects_download request.user.tenant container_name object.name %}">Download</a>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
37
openstack-dashboard/dashboard/templates/dash_containers.html
Normal file
37
openstack-dashboard/dashboard/templates/dash_containers.html
Normal file
@ -0,0 +1,37 @@
|
||||
{% extends 'dash_base.html' %}
|
||||
{# list of user's containers #}
|
||||
|
||||
{% block sidebar %}
|
||||
{% with current_sidebar="containers" %}
|
||||
{{block.super}}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div id='page_header'>
|
||||
<h2><span>Object Store:</span> Containers</h2>
|
||||
<p class='desc'><span>—</span> Manage Containers.</p>
|
||||
</div>
|
||||
{% include "_messages.html" %}
|
||||
|
||||
<div class='main_content'>
|
||||
{% if containers %}
|
||||
<div class='table_title wide'>
|
||||
<h3>Containers</h3>
|
||||
<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>
|
||||
|
||||
{% include '_container_list.html' %}
|
||||
|
||||
{% endif %}
|
||||
<a href="{% url dash_containers_create request.user.tenant %}">Create New Container >></a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,33 @@
|
||||
{% extends 'dash_base.html' %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% with current_sidebar="containers" %}
|
||||
{{block.super}}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div id='page_header'>
|
||||
<h2><span>Object Store:</span> Containers</h2>
|
||||
<p class='desc'><span>—</span> Create a container.</p>
|
||||
</div>
|
||||
|
||||
{% include "_messages.html" %}
|
||||
|
||||
<div class="main_content">
|
||||
<div class="dash_block wide form">
|
||||
<div class='title_block'>
|
||||
<h3>Create Container</h3>
|
||||
</div>
|
||||
<div class="left">
|
||||
{% include '_container_form.html' with form=create_form %}
|
||||
<h3><a href="{% url dash_containers request.user.tenant %}"><< Return to containers list</a></h3>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<h3>Description:</h3>
|
||||
<p>A container is a storage compartment for your data and provides a way for you to organize your data. You can think of a container as a folder in Windows® or a directory in UNIX®. The primary difference between a container and these other file system concepts is that containers cannot be nested. You can, however, create an unlimited number of containers within your account. Data must be stored in a container so you must have at least one container defined in your account prior to uploading data.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
38
openstack-dashboard/dashboard/templates/dash_objects.html
Normal file
38
openstack-dashboard/dashboard/templates/dash_objects.html
Normal file
@ -0,0 +1,38 @@
|
||||
{% extends 'dash_base.html' %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% with current_sidebar="containers" %}
|
||||
{{block.super}}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div id='page_header'>
|
||||
<h2><span>Object Store:</span> Objects</h2>
|
||||
<p class='desc'><span>—</span> Manage Objects.</p>
|
||||
</div>
|
||||
{% include "_messages.html" %}
|
||||
<div class="container-label">Container:</div>
|
||||
<div class="container-name">{{ container_name }}</div>
|
||||
<div style="clear: both;"></div>
|
||||
<div class='main_content'>
|
||||
{% if objects %}
|
||||
<div class='table_title wide'>
|
||||
<h3>Objects</h3>
|
||||
<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>
|
||||
|
||||
{% include '_object_list.html' %}
|
||||
|
||||
{% endif %}
|
||||
<a href="{% url dash_objects_upload request.user.tenant container_name %}">Upload New Object >></a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,36 @@
|
||||
{% extends 'dash_base.html' %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% with current_sidebar="containers" %}
|
||||
{{block.super}}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div id='page_header'>
|
||||
<h2><span>Object Store:</span> Objects</h2>
|
||||
<p class='desc'><span>—</span> Upload an object.</p>
|
||||
</div>
|
||||
|
||||
{% include "_messages.html" %}
|
||||
|
||||
<div class="container-label">Container:</div>
|
||||
<div class="container-name">{{ container_name }}</div>
|
||||
<div style="clear: both;"></div>
|
||||
<div class="main_content">
|
||||
<div class="dash_block wide form">
|
||||
<div class='title_block'>
|
||||
<h3>Upload Object</h3>
|
||||
</div>
|
||||
<div class="left">
|
||||
{% include '_object_form.html' with form=upload_form %}
|
||||
<h3><a href="{% url dash_objects request.user.tenant container_name %}"><< Return to objects list</a></h3>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<h3>Description:</h3>
|
||||
<p>An object is the basic storage entity and any optional metadata that represents the files you store in the OpenStack Object Storage system. When you upload data to OpenStack Object Storage, the data is stored as-is (no compression or encryption) and consists of a location (container), the object's name, and any metadata consisting of key/value pairs.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -34,6 +34,13 @@ MAILER_EMAIL_BACKEND = EMAIL_BACKEND
|
||||
OPENSTACK_ADMIN_TOKEN = "999888777666"
|
||||
OPENSTACK_KEYSTONE_URL = "http://localhost:5000/v2.0/"
|
||||
|
||||
# NOTE(tres): Replace all of this nonsense once Swift+Keystone
|
||||
# is stable.
|
||||
# SWIFT_AUTHURL = 'http://localhost:8080/auth/v1.0'
|
||||
# SWIFT_ACCOUNT = 'test'
|
||||
# SWIFT_USER = 'tester'
|
||||
# SWIFT_PASS = 'testing'
|
||||
|
||||
# If you have external monitoring links
|
||||
EXTERNAL_MONITORING = [
|
||||
['Nagios','http://foo.com'],
|
||||
|
@ -1,7 +1,7 @@
|
||||
@import url("reset.css");
|
||||
|
||||
/*==================
|
||||
=== Misc/General
|
||||
=== Misc/General
|
||||
==================*/
|
||||
|
||||
body {
|
||||
@ -60,7 +60,7 @@ p .ui-icon {
|
||||
|
||||
|
||||
/*==================
|
||||
=== Header
|
||||
=== Header
|
||||
==================*/
|
||||
|
||||
#header {
|
||||
@ -96,7 +96,7 @@ p .ui-icon {
|
||||
|
||||
|
||||
/*==================
|
||||
=== Content
|
||||
=== Content
|
||||
==================*/
|
||||
|
||||
#content_wrap {
|
||||
@ -483,7 +483,7 @@ input.delete, input.detach {
|
||||
}
|
||||
|
||||
#right_content td.image_location {
|
||||
text-align: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h3.image_list_heading {
|
||||
@ -505,7 +505,7 @@ h3.image_list_heading {
|
||||
td.detail_wrapper {
|
||||
padding: 0 !important;
|
||||
text-align: left !important;
|
||||
|
||||
|
||||
}
|
||||
div.image_detail, div.instance_detail {
|
||||
background: url(/media/dashboard/img/image_detail.png) top left no-repeat;
|
||||
@ -727,20 +727,19 @@ div.image_detail, div.instance_detail {
|
||||
border-bottom: none;
|
||||
border-top: none;
|
||||
}
|
||||
#projects .project a.manage_link:hover {
|
||||
#projects .project a.manage_link:hover {
|
||||
background-color: #f1f1f1 !important;
|
||||
border-color: #b7b7b7;
|
||||
}
|
||||
#projects .project.active a.manage_link { background-color: #5393c9 !important; border-color: #4c83af;}
|
||||
#projects .project.active a.manage_link:hover {
|
||||
#projects .project.active a.manage_link:hover {
|
||||
background-color: #80afd6 !important;
|
||||
border-color: #b7b7b7;
|
||||
}
|
||||
|
||||
|
||||
/* Footer */
|
||||
|
||||
#footer {
|
||||
#footer {
|
||||
clear: both;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ ul, li {
|
||||
|
||||
h1 {
|
||||
float: left;
|
||||
|
||||
|
||||
}
|
||||
|
||||
h1 a{
|
||||
@ -91,7 +91,7 @@ border: 1px solid #cbe0e8;
|
||||
z-index: 500;
|
||||
color: #8eacb7;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
display: block;
|
||||
float: left;
|
||||
@ -126,7 +126,7 @@ border: 1px solid #cbe0e8;
|
||||
}
|
||||
|
||||
#nav li, #nav li a{
|
||||
float: left;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#nav li a.active {
|
||||
@ -166,7 +166,7 @@ border: 1px solid #cbe0e8;
|
||||
float: right;
|
||||
height: 400px;
|
||||
margin-bottom: -400px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#login_btn {
|
||||
@ -213,7 +213,7 @@ border: 1px solid #cbe0e8;
|
||||
#standalone #login {
|
||||
float:left;
|
||||
margin: 0 !important;
|
||||
-webkit-box-shadow: 0 0 10px rgba(0,0,0,0.2);
|
||||
-webkit-box-shadow: 0 0 10px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.clear {
|
||||
@ -880,7 +880,7 @@ ol {
|
||||
|
||||
ol li {
|
||||
display: block;
|
||||
float: left;
|
||||
float: left;
|
||||
position: relative;
|
||||
margin-right: -28px;
|
||||
}
|
||||
@ -1134,23 +1134,23 @@ ol li.first a {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.status_box.info {
|
||||
.status_box.info {
|
||||
background-color: #e8f8ff;
|
||||
border-color: #9ac7dc;
|
||||
color: #7ab6c5;
|
||||
}
|
||||
.status_box.success {
|
||||
.status_box.success {
|
||||
background-color: #e9ffe8;
|
||||
border-color: #9edd9b;
|
||||
color: #7ec67b;
|
||||
}
|
||||
.status_box.warning {
|
||||
.status_box.warning {
|
||||
background-color: #ffffe8;
|
||||
border-color: #ffe37b;
|
||||
color: #d1b12d;
|
||||
}
|
||||
|
||||
.status_box.error {
|
||||
.status_box.error {
|
||||
background-color: #ffdbdc;
|
||||
border-color: #ff9a99;
|
||||
color: #ff8080;
|
||||
@ -1161,7 +1161,7 @@ ol li.first a {
|
||||
float: left;
|
||||
padding: 20px;
|
||||
min-width: 120px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.status_box.info h2 { color: #2a7380; border-color: #9ac7dc; }
|
||||
@ -1308,18 +1308,18 @@ a.refresh:hover {
|
||||
|
||||
|
||||
|
||||
.stat_box.good {
|
||||
.stat_box.good {
|
||||
background-color: #e9ffe8;
|
||||
border-color: #9edd9b;
|
||||
color: #7ec67b;
|
||||
}
|
||||
.stat_box.medium {
|
||||
.stat_box.medium {
|
||||
background-color: #ffffe8;
|
||||
border-color: #ffe37b;
|
||||
color: #eada83;
|
||||
}
|
||||
|
||||
.stat_box.bad {
|
||||
.stat_box.bad {
|
||||
background-color: #ffdbdc;
|
||||
border-color: #ff9a99;
|
||||
color: #ff8080;
|
||||
@ -1376,7 +1376,7 @@ a.refresh:hover {
|
||||
}
|
||||
|
||||
|
||||
.stat_box p.avail span, .stat_box p.used span {
|
||||
.stat_box p.avail span, .stat_box p.used span {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@ -1609,8 +1609,16 @@ li.title h4{
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.container-label {
|
||||
font-weight: bold;
|
||||
font-size: 1.22em;
|
||||
margin-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.container-name {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ django-nose==0.1.2
|
||||
django-mailer
|
||||
django-registration==0.7
|
||||
nova-adminclient
|
||||
python-cloudfiles
|
||||
|
||||
bzr+https://launchpad.net/glance#egg=glance
|
||||
-e git://github.com/jacobian/openstack.compute.git#egg=openstack
|
||||
|
Loading…
x
Reference in New Issue
Block a user