Merge remote branch '4p/master' into floating-ips-mergeready
Conflicts: openstack-dashboard/dashboard/static/dashboard/css/style.css openstack-dashboard/dashboard/templates/_dash_sidebar.html openstack-dashboard/tools/pip-requires
This commit is contained in:
commit
3aff55d436
@ -200,7 +200,8 @@ class ServerAttributes(APIDictWrapper):
|
||||
_attrs = ['description', 'disk_gb', 'host', 'image_ref', 'kernel_id',
|
||||
'key_name', 'launched_at', 'mac_address', 'memory_mb', 'name',
|
||||
'os_type', 'tenant_id', 'ramdisk_id', 'scheduled_at',
|
||||
'terminated_at', 'user_data', 'user_id', 'vcpus', 'hostname']
|
||||
'terminated_at', 'user_data', 'user_id', 'vcpus', 'hostname',
|
||||
'security_groups']
|
||||
|
||||
|
||||
class Services(APIResourceWrapper):
|
||||
@ -235,6 +236,22 @@ class User(APIResourceWrapper):
|
||||
_attrs = ['email', 'enabled', 'id', 'tenantId']
|
||||
|
||||
|
||||
class SecurityGroup(APIResourceWrapper):
|
||||
"""Simple wrapper around openstackx.extras.security_groups.SecurityGroup"""
|
||||
_attrs = ['id', 'name', 'description', 'tenant_id', 'rules']
|
||||
|
||||
|
||||
class SecurityGroupRule(APIResourceWrapper):
|
||||
"""Simple wrapper around openstackx.extras.security_groups.SecurityGroupRule"""
|
||||
_attrs = ['id', 'parent_group_id', 'group_id', 'ip_protocol',
|
||||
'from_port', 'to_port', 'groups', 'ip_ranges']
|
||||
|
||||
|
||||
class SecurityGroupRule(APIResourceWrapper):
|
||||
"""Simple wrapper around openstackx.extras.users.User"""
|
||||
_attrs = ['id', 'name', 'description', 'tenant_id', 'security_group_rules']
|
||||
|
||||
|
||||
class SwiftAuthentication(object):
|
||||
"""Auth container to pass CloudFiles storage URL and token from
|
||||
session.
|
||||
@ -340,6 +357,16 @@ def extras_api(request):
|
||||
return openstackx.extras.Extras(auth_token=request.user.token,
|
||||
management_url=url_for(request, 'nova'))
|
||||
|
||||
def novaclient(request):
|
||||
LOG.debug('novaclient connection created using token "%s"'
|
||||
' and url "%s"' % (request.user.token, url_for(request, 'nova')))
|
||||
c = client.Client(username=request.user.username,
|
||||
api_key=request.user.token,
|
||||
project_id=request.user.tenant,
|
||||
auth_url=url_for(request, 'nova'))
|
||||
c.client.auth_token = request.user.token
|
||||
c.client.management_url=url_for(request, 'nova')
|
||||
return c
|
||||
|
||||
def novaclient(request):
|
||||
LOG.debug('novaclient connection created using token "%s"'
|
||||
@ -477,10 +504,11 @@ def keypair_list(request):
|
||||
return [KeyPair(key) for key in extras_api(request).keypairs.list()]
|
||||
|
||||
|
||||
def server_create(request, name, image, flavor, key_name, user_data):
|
||||
def server_create(request, name, image, flavor,
|
||||
key_name, user_data, security_groups):
|
||||
return Server(extras_api(request).servers.create(
|
||||
name, image, flavor, key_name=key_name, user_data=user_data),
|
||||
request)
|
||||
name, image, flavor, key_name=key_name, user_data=user_data,
|
||||
security_groups=security_groups), request)
|
||||
|
||||
|
||||
def server_delete(request, instance):
|
||||
@ -637,6 +665,39 @@ def user_get(request, user_id):
|
||||
return User(account_api(request).users.get(user_id))
|
||||
|
||||
|
||||
def security_group_list(request):
|
||||
return [SecurityGroup(g) for g in novaclient(request).\
|
||||
security_groups.list()]
|
||||
|
||||
def security_group_get(request, security_group_id):
|
||||
return SecurityGroup(novaclient(request).\
|
||||
security_groups.get(security_group_id))
|
||||
|
||||
def security_group_create(request, name, description):
|
||||
return SecurityGroup(novaclient(request).\
|
||||
security_groups.create(name, description))
|
||||
|
||||
|
||||
def security_group_delete(request, security_group_id):
|
||||
novaclient(request).security_groups.delete(security_group_id)
|
||||
|
||||
|
||||
def security_group_rule_create(request, parent_group_id, ip_protocol=None,
|
||||
from_port=None, to_port=None, cidr=None,
|
||||
group_id=None):
|
||||
return SecurityGroup(novaclient(request).\
|
||||
security_group_rules.create(parent_group_id,
|
||||
ip_protocol,
|
||||
from_port,
|
||||
to_port,
|
||||
cidr,
|
||||
group_id))
|
||||
|
||||
|
||||
def security_group_rule_delete(request, security_group_rule_id):
|
||||
novaclient(request).security_group_rules.delete(security_group_rule_id)
|
||||
|
||||
|
||||
@check_openstackx
|
||||
def user_list(request):
|
||||
return [User(u) for u in account_api(request).users.list()]
|
||||
@ -846,28 +907,38 @@ class GlobalSummary(object):
|
||||
|
||||
for service in self.service_list:
|
||||
if service.type == 'nova-compute':
|
||||
self.summary['total_vcpus'] += min(service.stats['max_vcpus'], service.stats.get('vcpus', 0))
|
||||
self.summary['total_disk_size'] += min(service.stats['max_gigabytes'], service.stats.get('local_gb', 0))
|
||||
self.summary['total_ram_size'] += min(service.stats['max_ram'], service.stats['memory_mb']) if 'max_ram' in service.stats else service.stats.get('memory_mb', 0)
|
||||
self.summary['total_vcpus'] += min(service.stats['max_vcpus'],
|
||||
service.stats.get('vcpus', 0))
|
||||
self.summary['total_disk_size'] += min(
|
||||
service.stats['max_gigabytes'],
|
||||
service.stats.get('local_gb', 0))
|
||||
self.summary['total_ram_size'] += min(
|
||||
service.stats['max_ram'],
|
||||
service.stats['memory_mb']) if 'max_ram' \
|
||||
in service.stats \
|
||||
else service.stats.get('memory_mb', 0)
|
||||
|
||||
def usage(self, datetime_start, datetime_end):
|
||||
try:
|
||||
self.usage_list = usage_list(self.request, datetime_start, datetime_end)
|
||||
self.usage_list = usage_list(self.request, datetime_start,
|
||||
datetime_end)
|
||||
except api_exceptions.ApiException, e:
|
||||
self.usage_list = []
|
||||
LOG.error('ApiException fetching usage list in instance usage'
|
||||
' on date range "%s to %s"' % (datetime_start,
|
||||
datetime_end),
|
||||
exc_info=True)
|
||||
messages.error(self.request, 'Unable to get usage info: %s' % e.message)
|
||||
messages.error(self.request,
|
||||
'Unable to get usage info: %s' % e.message)
|
||||
return
|
||||
|
||||
for usage in self.usage_list:
|
||||
# FIXME: api needs a simpler dict interface (with iteration) - anthony
|
||||
# NOTE(mgius): Changed this on the api end. Not too much neater, but
|
||||
# at least its not going into private member data of an external
|
||||
# class anymore
|
||||
#usage = usage._info
|
||||
# FIXME: api needs a simpler dict interface (with iteration)
|
||||
# - anthony
|
||||
# NOTE(mgius): Changed this on the api end. Not too much
|
||||
# neater, but at least its not going into private member
|
||||
# data of an external class anymore
|
||||
# usage = usage._info
|
||||
for k in usage._attrs:
|
||||
v = usage.__getattr__(k)
|
||||
if type(v) in [float, int]:
|
||||
@ -884,8 +955,11 @@ class GlobalSummary(object):
|
||||
mult = 1.0
|
||||
|
||||
for kind in GlobalSummary.node_resource_info:
|
||||
self.summary['total_' + kind + rsrc + '_hr'] = self.summary['total_' + kind + rsrc] / mult
|
||||
self.summary['total_' + kind + rsrc + '_hr'] = \
|
||||
self.summary['total_' + kind + rsrc] / mult
|
||||
|
||||
def avail(self):
|
||||
for rsrc in GlobalSummary.node_resources:
|
||||
self.summary['total_avail_' + rsrc] = self.summary['total_' + rsrc] - self.summary['total_active_' + rsrc]
|
||||
self.summary['total_avail_' + rsrc] = \
|
||||
self.summary['total_' + rsrc] - \
|
||||
self.summary['total_active_' + rsrc]
|
||||
|
@ -25,5 +25,6 @@ from django.conf import settings
|
||||
urlpatterns = patterns('django_openstack.auth.views',
|
||||
url(r'login/$', 'login', name='auth_login'),
|
||||
url(r'logout/$', 'logout', name='auth_logout'),
|
||||
url(r'switch/(?P<tenant_id>[^/]+)/$', 'switch_tenants', name='auth_switch'),
|
||||
url(r'switch/(?P<tenant_id>[^/]+)/$', 'switch_tenants',
|
||||
name='auth_switch'),
|
||||
)
|
||||
|
@ -66,7 +66,7 @@ class Login(forms.SelfHandlingForm):
|
||||
|
||||
class LoginWithTenant(Login):
|
||||
username = forms.CharField(max_length="20",
|
||||
widget=forms.TextInput(attrs={'readonly':'readonly'}))
|
||||
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
||||
tenant = forms.CharField(widget=forms.HiddenInput())
|
||||
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
from django.conf.urls.defaults import *
|
||||
|
||||
SECURITY_GROUPS = r'^(?P<tenant_id>[^/]+)/security_groups/(?P<security_group_id>[^/]+)/%s$'
|
||||
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$'
|
||||
@ -33,12 +34,19 @@ PORTS = r'^(?P<tenant_id>[^/]+)/networks/(?P<network_id>[^/]+)/ports/%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(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'),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('django_openstack.dash.views.security_groups',
|
||||
url(r'^(?P<tenant_id>[^/]+)/security_groups/$', 'index', name='dash_security_groups'),
|
||||
url(r'^(?P<tenant_id>[^/]+)/security_groups/create$', 'create', name='dash_security_groups_create'),
|
||||
url(SECURITY_GROUPS % 'edit_rules', 'edit_rules', name='dash_security_groups_edit_rules'),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('django_openstack.dash.views.images',
|
||||
url(r'^(?P<tenant_id>[^/]+)/images/$', 'index', name='dash_images'),
|
||||
url(IMAGES % 'launch', 'launch', name='dash_images_launch'),
|
||||
@ -78,11 +86,14 @@ urlpatterns += patterns('django_openstack.dash.views.objects',
|
||||
urlpatterns += patterns('django_openstack.dash.views.networks',
|
||||
url(r'^(?P<tenant_id>[^/]+)/networks/$', 'index', name='dash_networks'),
|
||||
url(NETWORKS % 'create', 'create', name='dash_network_create'),
|
||||
url(NETWORKS % '(?P<network_id>[^/]+)/detail', 'detail', name='dash_networks_detail'),
|
||||
url(NETWORKS % '(?P<network_id>[^/]+)/rename', 'rename', name='dash_network_rename'),
|
||||
url(NETWORKS % '(?P<network_id>[^/]+)/detail', 'detail',
|
||||
name='dash_networks_detail'),
|
||||
url(NETWORKS % '(?P<network_id>[^/]+)/rename', 'rename',
|
||||
name='dash_network_rename'),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('django_openstack.dash.views.ports',
|
||||
url(PORTS % 'create', 'create', name='dash_ports_create'),
|
||||
url(PORTS % '(?P<port_id>[^/]+)/attach', 'attach', name='dash_ports_attach'),
|
||||
url(PORTS % '(?P<port_id>[^/]+)/attach', 'attach',
|
||||
name='dash_ports_attach'),
|
||||
)
|
||||
|
@ -39,6 +39,7 @@ from django_openstack import api
|
||||
from django_openstack import forms
|
||||
from openstackx.api import exceptions as api_exceptions
|
||||
from glance.common import exception as glance_exception
|
||||
from novaclient import exceptions as novaclient_exceptions
|
||||
|
||||
|
||||
LOG = logging.getLogger('django_openstack.dash.views.images')
|
||||
@ -68,6 +69,14 @@ class LaunchForm(forms.SelfHandlingForm):
|
||||
required=False,
|
||||
help_text="Which keypair to use for authentication")
|
||||
|
||||
securitygrouplist = kwargs.get('initial', {}).get('securitygrouplist', [])
|
||||
self.fields['security_groups'] = forms.MultipleChoiceField(choices=securitygrouplist,
|
||||
label='Security Groups',
|
||||
required=True,
|
||||
initial=['default'],
|
||||
widget=forms.SelectMultiple(attrs={'class': 'chzn-select',
|
||||
'style': "min-width: 200px"}),
|
||||
help_text="Launch instance in these Security Groups")
|
||||
# setting self.fields.keyOrder seems to break validation,
|
||||
# so ordering fields manually
|
||||
field_list = (
|
||||
@ -78,7 +87,6 @@ class LaunchForm(forms.SelfHandlingForm):
|
||||
for field in field_list[::-1]:
|
||||
self.fields.insert(0, field, self.fields.pop(field))
|
||||
|
||||
|
||||
def handle(self, request, data):
|
||||
image_id = data['image_id']
|
||||
tenant_id = data['tenant_id']
|
||||
@ -90,7 +98,8 @@ class LaunchForm(forms.SelfHandlingForm):
|
||||
image,
|
||||
flavor,
|
||||
data.get('key_name'),
|
||||
data.get('user_data'))
|
||||
data.get('user_data'),
|
||||
data.get('security_groups'))
|
||||
|
||||
msg = 'Instance was successfully launched'
|
||||
LOG.info(msg)
|
||||
@ -163,13 +172,22 @@ def launch(request, tenant_id, image_id):
|
||||
LOG.error('Unable to retrieve list of keypairs', exc_info=True)
|
||||
return []
|
||||
|
||||
def securitygrouplist():
|
||||
try:
|
||||
fl = api.security_group_list(request)
|
||||
sel = [(f.name, f.name) for f in fl]
|
||||
return sel
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
LOG.error('Unable to retrieve list of security groups', exc_info=True)
|
||||
return []
|
||||
|
||||
# TODO(mgius): Any reason why these can't be after the launchform logic?
|
||||
# If The form is valid, we've just wasted these two api calls
|
||||
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)
|
||||
try:
|
||||
quotas.ram = int(quotas.ram)/100
|
||||
quotas.ram = int(quotas.ram) / 100
|
||||
except Exception, e:
|
||||
messages.error(request, 'Error parsing quota for %s: %s' %
|
||||
(image_id, e.message))
|
||||
@ -178,6 +196,7 @@ def launch(request, tenant_id, image_id):
|
||||
form, handled = LaunchForm.maybe_handle(
|
||||
request, initial={'flavorlist': flavorlist(),
|
||||
'keynamelist': keynamelist(),
|
||||
'securitygrouplist': securitygrouplist(),
|
||||
'image_id': image_id,
|
||||
'tenant_id': tenant_id})
|
||||
if handled:
|
||||
|
@ -90,7 +90,7 @@ class RebootInstance(forms.SelfHandlingForm):
|
||||
class UpdateInstance(forms.SelfHandlingForm):
|
||||
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
||||
instance = forms.CharField(widget=forms.TextInput(
|
||||
attrs={'readonly':'readonly'}))
|
||||
attrs={'readonly': 'readonly'}))
|
||||
name = forms.CharField(required=True)
|
||||
description = forms.CharField(required=False)
|
||||
|
||||
@ -98,7 +98,10 @@ class UpdateInstance(forms.SelfHandlingForm):
|
||||
tenant_id = data['tenant_id']
|
||||
description = data.get('description', '')
|
||||
try:
|
||||
api.server_update(request, data['instance'], data['name'], description)
|
||||
api.server_update(request,
|
||||
data['instance'],
|
||||
data['name'],
|
||||
description)
|
||||
messages.success(request, "Instance '%s' updated" % data['name'])
|
||||
except api_exceptions.ApiException, e:
|
||||
messages.error(request,
|
||||
@ -131,6 +134,7 @@ def index(request, tenant_id):
|
||||
'reboot_form': reboot_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def refresh(request, tenant_id):
|
||||
instances = []
|
||||
@ -150,6 +154,7 @@ def refresh(request, tenant_id):
|
||||
'reboot_form': reboot_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def usage(request, tenant_id=None):
|
||||
today = utils.today()
|
||||
@ -212,7 +217,7 @@ def usage(request, tenant_id=None):
|
||||
'datetime_start': datetime_start,
|
||||
'datetime_end': datetime_end,
|
||||
'instances': instances
|
||||
}, context_instance = template.RequestContext(request), mimetype=mimetype)
|
||||
}, context_instance=template.RequestContext(request), mimetype=mimetype)
|
||||
|
||||
|
||||
@login_required
|
||||
@ -238,7 +243,8 @@ def vnc(request, tenant_id, instance_id):
|
||||
try:
|
||||
console = api.console_create(request, instance_id, 'vnc')
|
||||
instance = api.server_get(request, instance_id)
|
||||
return shortcuts.redirect(console.output + ("&title=%s(%s)" % (instance.name, instance_id)))
|
||||
return shortcuts.redirect(console.output +
|
||||
("&title=%s(%s)" % (instance.name, instance_id)))
|
||||
except api_exceptions.ApiException, e:
|
||||
LOG.error('ApiException while fetching instance vnc connection',
|
||||
exc_info=True)
|
||||
|
200
django-openstack/django_openstack/dash/views/security_groups.py
Normal file
200
django-openstack/django_openstack/dash/views/security_groups.py
Normal file
@ -0,0 +1,200 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2011 Fourth Paradigm Development, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Views for managing Nova instances.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from django import http
|
||||
from django import template
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core import validators
|
||||
from django import shortcuts
|
||||
from django.shortcuts import redirect, render_to_response
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from django_openstack import api
|
||||
from django_openstack import forms
|
||||
from novaclient import exceptions as novaclient_exceptions
|
||||
|
||||
|
||||
LOG = logging.getLogger('django_openstack.dash.views.security_groups')
|
||||
|
||||
|
||||
class CreateGroup(forms.SelfHandlingForm):
|
||||
name = forms.CharField(validators=[validators.validate_slug])
|
||||
description = forms.CharField()
|
||||
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
LOG.info('Add security_group: "%s"' % data)
|
||||
|
||||
security_group = api.security_group_create(request,
|
||||
data['name'],
|
||||
data['description'])
|
||||
messages.info(request, 'Successfully created security_group: %s' \
|
||||
% data['name'])
|
||||
return shortcuts.redirect('dash_security_groups',
|
||||
data['tenant_id'])
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
LOG.error("ClientException in CreateGroup", exc_info=True)
|
||||
messages.error(request, 'Error creating security group: %s' %
|
||||
e.message)
|
||||
|
||||
|
||||
class DeleteGroup(forms.SelfHandlingForm):
|
||||
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
||||
security_group_id = forms.CharField(widget=forms.HiddenInput())
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
LOG.info('Delete security_group: "%s"' % data)
|
||||
|
||||
security_group = api.security_group_delete(request,
|
||||
data['security_group_id'])
|
||||
messages.info(request, 'Successfully deleted security_group: %s' \
|
||||
% data['security_group_id'])
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
LOG.error("ClientException in DeleteGroup", exc_info=True)
|
||||
messages.error(request, 'Error deleting security group: %s'
|
||||
% e.message)
|
||||
return shortcuts.redirect('dash_security_groups', data['tenant_id'])
|
||||
|
||||
|
||||
class AddRule(forms.SelfHandlingForm):
|
||||
ip_protocol = forms.ChoiceField(choices=[('tcp', 'tcp'),
|
||||
('udp', 'udp'),
|
||||
('icmp', 'icmp')])
|
||||
from_port = forms.CharField()
|
||||
to_port = forms.CharField()
|
||||
cidr = forms.CharField()
|
||||
# TODO (anthony) source group support
|
||||
# group_id = forms.CharField()
|
||||
|
||||
security_group_id = forms.CharField(widget=forms.HiddenInput())
|
||||
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
||||
|
||||
def handle(self, request, data):
|
||||
tenant_id = data['tenant_id']
|
||||
try:
|
||||
LOG.info('Add security_group_rule: "%s"' % data)
|
||||
|
||||
rule = api.security_group_rule_create(request,
|
||||
data['security_group_id'],
|
||||
data['ip_protocol'],
|
||||
data['from_port'],
|
||||
data['to_port'],
|
||||
data['cidr'])
|
||||
messages.info(request, 'Successfully added rule: %s' \
|
||||
% rule.id)
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
LOG.error("ClientException in AddRule", exc_info=True)
|
||||
messages.error(request, 'Error adding rule security group: %s'
|
||||
% e.message)
|
||||
return shortcuts.redirect(request.build_absolute_uri())
|
||||
|
||||
|
||||
class DeleteRule(forms.SelfHandlingForm):
|
||||
security_group_rule_id = forms.CharField(widget=forms.HiddenInput())
|
||||
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
||||
|
||||
def handle(self, request, data):
|
||||
security_group_rule_id = data['security_group_rule_id']
|
||||
tenant_id = data['tenant_id']
|
||||
try:
|
||||
LOG.info('Delete security_group_rule: "%s"' % data)
|
||||
|
||||
security_group = api.security_group_rule_delete(
|
||||
request,
|
||||
security_group_rule_id)
|
||||
messages.info(request, 'Successfully deleted rule: %s' \
|
||||
% security_group_rule_id)
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
LOG.error("ClientException in DeleteRule", exc_info=True)
|
||||
messages.error(request, 'Error authorizing security group: %s'
|
||||
% e.message)
|
||||
return shortcuts.redirect(request.build_absolute_uri())
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request, tenant_id):
|
||||
delete_form, handled = DeleteGroup.maybe_handle(request,
|
||||
initial={'tenant_id': tenant_id})
|
||||
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
try:
|
||||
security_groups = api.security_group_list(request)
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
security_groups = []
|
||||
LOG.error("ClientException in security_groups index", exc_info=True)
|
||||
messages.error(request, 'Error fetching security_groups: %s'
|
||||
% e.message)
|
||||
|
||||
return shortcuts.render_to_response('dash_security_groups.html', {
|
||||
'security_groups': security_groups,
|
||||
'delete_form': delete_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def edit_rules(request, tenant_id, security_group_id):
|
||||
add_form, handled = AddRule.maybe_handle(request,
|
||||
initial={'tenant_id': tenant_id,
|
||||
'security_group_id': security_group_id})
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
delete_form, handled = DeleteRule.maybe_handle(request,
|
||||
initial={'tenant_id': tenant_id,
|
||||
'security_group_id': security_group_id})
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
try:
|
||||
security_group = api.security_group_get(request, security_group_id)
|
||||
except novaclient_exceptions.ClientException, e:
|
||||
LOG.error("ClientException in security_groups rules edit", exc_info=True)
|
||||
messages.error(request, 'Error getting security_group: %s' % e.message)
|
||||
return shortcuts.redirect('dash_security_groups', tenant_id)
|
||||
|
||||
return shortcuts.render_to_response(
|
||||
'dash_security_groups_edit_rules.html', {
|
||||
'security_group': security_group,
|
||||
'delete_form': delete_form,
|
||||
'form': add_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def create(request, tenant_id):
|
||||
form, handled = CreateGroup.maybe_handle(request,
|
||||
initial={'tenant_id': tenant_id})
|
||||
if handled:
|
||||
return handled
|
||||
|
||||
return shortcuts.render_to_response('dash_security_groups_create.html', {
|
||||
'form': form,
|
||||
}, context_instance=template.RequestContext(request))
|
@ -46,13 +46,16 @@ LOG = logging.getLogger('django_openstack.dash.views.snapshots')
|
||||
|
||||
class CreateSnapshot(forms.SelfHandlingForm):
|
||||
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
||||
instance_id = forms.CharField(widget=forms.TextInput(attrs={'readonly':'readonly'}))
|
||||
instance_id = forms.CharField(widget=forms.TextInput(
|
||||
attrs={'readonly': 'readonly'}))
|
||||
name = forms.CharField(max_length="20", label="Snapshot Name")
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
LOG.info('Creating snapshot "%s"' % data['name'])
|
||||
snapshot = api.snapshot_create(request, data['instance_id'], data['name'])
|
||||
snapshot = api.snapshot_create(request,
|
||||
data['instance_id'],
|
||||
data['name'])
|
||||
instance = api.server_get(request, data['instance_id'])
|
||||
|
||||
messages.info(request, 'Snapshot "%s" created for instance "%s"' %\
|
||||
|
@ -18,7 +18,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
"""
|
||||
Simple decorator container for general purpose
|
||||
"""
|
||||
|
||||
@ -30,13 +30,13 @@ LOG = logging.getLogger('django_openstack.syspanel')
|
||||
|
||||
|
||||
def enforce_admin_access(fn):
|
||||
""" Preserve unauthorized bypass typing directly the URL and redirects to
|
||||
""" Preserve unauthorized bypass typing directly the URL and redirects to
|
||||
the overview dash page """
|
||||
def dec(*args,**kwargs):
|
||||
def dec(*args, **kwargs):
|
||||
if args[0].user.is_admin():
|
||||
return fn(*args,**kwargs)
|
||||
return fn(*args, **kwargs)
|
||||
else:
|
||||
LOG.warn('Redirecting user "%s" from syspanel to dash ( %s )' %
|
||||
( args[0].user.username, fn.__name__) , exc_info=True)
|
||||
(args[0].user.username, fn.__name__), exc_info=True)
|
||||
return redirect('dash_overview')
|
||||
return dec
|
||||
|
@ -48,8 +48,10 @@ class SelectDateWidget(widgets.Widget):
|
||||
day_field = '%s_day'
|
||||
year_field = '%s_year'
|
||||
|
||||
def __init__(self, attrs=None, years=None, required=True, skip_day_field=False):
|
||||
# years is an optional list/tuple of years to use in the "year" select box.
|
||||
def __init__(self, attrs=None, years=None, required=True,
|
||||
skip_day_field=False):
|
||||
# years is an optional list/tuple of years to use in
|
||||
# the "year" select box.
|
||||
self.attrs = attrs or {}
|
||||
self.required = required
|
||||
self.skip_day_field = skip_day_field
|
||||
@ -57,7 +59,7 @@ class SelectDateWidget(widgets.Widget):
|
||||
self.years = years
|
||||
else:
|
||||
this_year = datetime.date.today().year
|
||||
self.years = range(this_year, this_year+10)
|
||||
self.years = range(this_year, this_year + 10)
|
||||
|
||||
def render(self, name, value, attrs=None, skip_day_field=True):
|
||||
print "Render %s %s" % (name, value)
|
||||
@ -68,25 +70,32 @@ class SelectDateWidget(widgets.Widget):
|
||||
if isinstance(value, basestring):
|
||||
if settings.USE_L10N:
|
||||
try:
|
||||
input_format = formats.get_format('DATE_INPUT_FORMATS')[0]
|
||||
input_format = formats.get_format(
|
||||
'DATE_INPUT_FORMATS')[0]
|
||||
# Python 2.4 compatibility:
|
||||
# v = datetime.datetime.strptime(value, input_format)
|
||||
# v = datetime.datetime.strptime(value,
|
||||
# input_format)
|
||||
# would be clearer, but datetime.strptime was added in
|
||||
# Python 2.5
|
||||
v = datetime.datetime(*(time.strptime(value, input_format)[0:6]))
|
||||
v = datetime.datetime(*(time.strptime(value,
|
||||
input_format)[0:6]))
|
||||
year_val, month_val, day_val = v.year, v.month, v.day
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
match = RE_DATE.match(value)
|
||||
if match:
|
||||
year_val, month_val, day_val = [int(v) for v in match.groups()]
|
||||
year_val, month_val, day_val = \
|
||||
[int(v) for v in match.groups()]
|
||||
choices = [(i, i) for i in self.years]
|
||||
year_html = self.create_select(name, self.year_field, value, year_val, choices)
|
||||
year_html = self.create_select(name,
|
||||
self.year_field, value, year_val, choices)
|
||||
choices = dates.MONTHS.items()
|
||||
month_html = self.create_select(name, self.month_field, value, month_val, choices)
|
||||
month_html = self.create_select(name,
|
||||
self.month_field, value, month_val, choices)
|
||||
choices = [(i, i) for i in range(1, 32)]
|
||||
day_html = self.create_select(name, self.day_field, value, day_val, choices)
|
||||
day_html = self.create_select(name,
|
||||
self.day_field, value, day_val, choices)
|
||||
|
||||
format = formats.get_format('DATE_FORMAT')
|
||||
escaped = False
|
||||
@ -174,4 +183,6 @@ class SelfHandlingForm(Form):
|
||||
|
||||
|
||||
class DateForm(Form):
|
||||
date = DateField(widget=SelectDateWidget(years=range(datetime.date.today().year, 2009, -1), skip_day_field=True))
|
||||
date = DateField(widget=SelectDateWidget(
|
||||
years=range(datetime.date.today().year, 2009, -1),
|
||||
skip_day_field=True))
|
||||
|
@ -42,7 +42,7 @@ class User(object):
|
||||
|
||||
def get_user_from_request(request):
|
||||
if 'user' not in request.session:
|
||||
return User(None,None,None,None,None)
|
||||
return User(None, None, None, None, None)
|
||||
return User(request.session['token'],
|
||||
request.session['user'],
|
||||
request.session['tenant'],
|
||||
|
@ -9,14 +9,14 @@ def dash_modules_detect():
|
||||
"""
|
||||
Sends a pinging signal to the app, all listening modules will reply with
|
||||
items for the sidebar.
|
||||
|
||||
|
||||
The response is a tuple of the Signal object instance, and a dictionary.
|
||||
The values within the dictionary containing links and a title which should
|
||||
be added to the sidebar navigation.
|
||||
|
||||
|
||||
Example: (<dash_apps_ping>,
|
||||
{'title': 'Nixon',
|
||||
'links': [{'url':'/syspanel/nixon/google',
|
||||
{'title': 'Nixon',
|
||||
'links': [{'url':'/syspanel/nixon/google',
|
||||
'text':'Google', 'active_text': 'google'}],
|
||||
'type': syspanel})
|
||||
"""
|
||||
@ -28,5 +28,3 @@ def dash_app_setup_urls():
|
||||
Adds urls from modules
|
||||
"""
|
||||
return dash_modules_urls.send(sender=dash_modules_urls)
|
||||
|
||||
|
||||
|
@ -75,6 +75,7 @@ class DeleteFlavor(forms.SelfHandlingForm):
|
||||
e.message)
|
||||
return redirect(request.build_absolute_uri())
|
||||
|
||||
|
||||
@login_required
|
||||
@enforce_admin_access
|
||||
def index(request):
|
||||
@ -93,10 +94,10 @@ def index(request):
|
||||
messages.error(request, 'Unable to get usage info: %s' % e.message)
|
||||
|
||||
flavors.sort(key=lambda x: x.id, reverse=True)
|
||||
return render_to_response('syspanel_flavors.html',{
|
||||
return render_to_response('syspanel_flavors.html', {
|
||||
'delete_form': delete_form,
|
||||
'flavors': flavors,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
@ -112,7 +113,7 @@ def create(request):
|
||||
global_summary.human_readable('disk_size')
|
||||
global_summary.human_readable('ram_size')
|
||||
|
||||
return render_to_response('syspanel_create_flavor.html',{
|
||||
return render_to_response('syspanel_create_flavor.html', {
|
||||
'global_summary': global_summary.summary,
|
||||
'form': form,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
@ -59,7 +59,8 @@ class ToggleImage(forms.SelfHandlingForm):
|
||||
def handle(self, request, data):
|
||||
image_id = data['image_id']
|
||||
try:
|
||||
api.image_update(request, image_id, image_meta={'is_public': False})
|
||||
api.image_update(request, image_id,
|
||||
image_meta={'is_public': False})
|
||||
except glance_exception.ClientConnectionError, e:
|
||||
LOG.error("Error connecting to glance", exc_info=True)
|
||||
messages.error(request,
|
||||
@ -70,16 +71,21 @@ class ToggleImage(forms.SelfHandlingForm):
|
||||
messages.error(request, "Error updating image: %s" % e.message)
|
||||
return redirect(request.build_absolute_uri())
|
||||
|
||||
|
||||
class UpdateImageForm(forms.Form):
|
||||
name = forms.CharField(max_length="25", label="Name")
|
||||
kernel = forms.CharField(max_length="25", label="Kernel ID", required=False)
|
||||
ramdisk = forms.CharField(max_length="25", label="Ramdisk ID", required=False)
|
||||
kernel = forms.CharField(max_length="25", label="Kernel ID",
|
||||
required=False)
|
||||
ramdisk = forms.CharField(max_length="25", label="Ramdisk ID",
|
||||
required=False)
|
||||
architecture = forms.CharField(label="Architecture", required=False)
|
||||
#project_id = forms.CharField(label="Project ID")
|
||||
container_format = forms.CharField(label="Container Format", required=False)
|
||||
container_format = forms.CharField(label="Container Format",
|
||||
required=False)
|
||||
disk_format = forms.CharField(label="Disk Format")
|
||||
#is_public = forms.BooleanField(label="Publicly Available", required=False)
|
||||
|
||||
|
||||
@login_required
|
||||
@enforce_admin_access
|
||||
def index(request):
|
||||
@ -109,7 +115,7 @@ def index(request):
|
||||
'delete_form': delete_form,
|
||||
'toggle_form': toggle_form,
|
||||
'images': images,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
@ -142,9 +148,11 @@ def update(request, image_id):
|
||||
if image_form['kernel']:
|
||||
metadata['properties']['kernel_id'] = image_form['kernel']
|
||||
if image_form['ramdisk']:
|
||||
metadata['properties']['ramdisk_id'] = image_form['ramdisk']
|
||||
metadata['properties']['ramdisk_id'] = \
|
||||
image_form['ramdisk']
|
||||
if image_form['architecture']:
|
||||
metadata['properties']['architecture'] = image_form['architecture']
|
||||
metadata['properties']['architecture'] = \
|
||||
image_form['architecture']
|
||||
api.image_update(request, image_id, metadata)
|
||||
messages.success(request, "Image was successfully updated.")
|
||||
except glance_exception.ClientConnectionError, e:
|
||||
@ -167,10 +175,10 @@ def update(request, image_id):
|
||||
messages.error(request,
|
||||
"Image could not be uploaded, please try agian.")
|
||||
form = UpdateImageForm(request.POST)
|
||||
return render_to_response('syspanel_image_update.html',{
|
||||
return render_to_response('syspanel_image_update.html', {
|
||||
'image': image,
|
||||
'form': form,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
else:
|
||||
form = UpdateImageForm(initial={
|
||||
'name': image.get('name', ''),
|
||||
@ -185,10 +193,10 @@ def update(request, image_id):
|
||||
'disk_format': image.get('disk_format', ''),
|
||||
})
|
||||
|
||||
return render_to_response('syspanel_image_update.html',{
|
||||
return render_to_response('syspanel_image_update.html', {
|
||||
'image': image,
|
||||
'form': form,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
@ -206,7 +214,8 @@ def upload(request):
|
||||
messages.success(request, "Image was successfully uploaded.")
|
||||
except:
|
||||
# TODO add better error management
|
||||
messages.error(request, "Image could not be uploaded, please try again.")
|
||||
messages.error(request, "Image could not be uploaded, "
|
||||
"please try again.")
|
||||
|
||||
try:
|
||||
api.image_create(request, metadata, image['image_file'])
|
||||
@ -225,13 +234,15 @@ def upload(request):
|
||||
messages.error(request,
|
||||
"Image could not be uploaded, please try agian.")
|
||||
form = UploadImageForm(request.POST)
|
||||
return render_to_response('django_nova_syspanel/images/image_upload.html',{
|
||||
return render_to_response('django_nova_syspanel/images/'
|
||||
'image_upload.html', {
|
||||
'form': form,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
return redirect('syspanel_images')
|
||||
else:
|
||||
form = UploadImageForm()
|
||||
return render_to_response('django_nova_syspanel/images/image_upload.html',{
|
||||
return render_to_response('django_nova_syspanel/images/'
|
||||
'image_upload.html', {
|
||||
'form': form,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
@ -45,8 +45,8 @@ LOG = logging.getLogger('django_openstack.syspanel.views.instances')
|
||||
|
||||
|
||||
def _next_month(date_start):
|
||||
y = date_start.year + (date_start.month + 1)/13
|
||||
m = ((date_start.month + 1)%13)
|
||||
y = date_start.year + (date_start.month + 1) / 13
|
||||
m = ((date_start.month + 1) % 13)
|
||||
if m == 0:
|
||||
m = 1
|
||||
return datetime.date(y, m, 1)
|
||||
@ -54,15 +54,18 @@ def _next_month(date_start):
|
||||
|
||||
def _current_month():
|
||||
today = datetime.date.today()
|
||||
return datetime.date(today.year, today.month,1)
|
||||
return datetime.date(today.year, today.month, 1)
|
||||
|
||||
|
||||
def _get_start_and_end_date(request):
|
||||
try:
|
||||
date_start = datetime.date(int(request.GET['date_year']), int(request.GET['date_month']), 1)
|
||||
date_start = datetime.date(
|
||||
int(request.GET['date_year']),
|
||||
int(request.GET['date_month']),
|
||||
1)
|
||||
except:
|
||||
today = datetime.date.today()
|
||||
date_start = datetime.date(today.year, today.month,1)
|
||||
date_start = datetime.date(today.year, today.month, 1)
|
||||
|
||||
date_end = _next_month(date_start)
|
||||
datetime_start = datetime.datetime.combine(date_start, datetime.time())
|
||||
@ -74,13 +77,15 @@ def _get_start_and_end_date(request):
|
||||
|
||||
|
||||
def _csv_usage_link(date_start):
|
||||
return "?date_month=%s&date_year=%s&format=csv" % (date_start.month, date_start.year)
|
||||
return "?date_month=%s&date_year=%s&format=csv" % (date_start.month,
|
||||
date_start.year)
|
||||
|
||||
|
||||
@login_required
|
||||
@enforce_admin_access
|
||||
def usage(request):
|
||||
(date_start, date_end, datetime_start, datetime_end) = _get_start_and_end_date(request)
|
||||
(date_start, date_end, datetime_start, datetime_end) = \
|
||||
_get_start_and_end_date(request)
|
||||
|
||||
global_summary = api.GlobalSummary(request)
|
||||
if date_start > _current_month():
|
||||
@ -104,7 +109,7 @@ def usage(request):
|
||||
else:
|
||||
template_name = 'syspanel_usage.html'
|
||||
mimetype = "text/html"
|
||||
|
||||
|
||||
return render_to_response(
|
||||
template_name, {
|
||||
'dateform': dateform,
|
||||
@ -114,13 +119,14 @@ def usage(request):
|
||||
'csv_link': _csv_usage_link(date_start),
|
||||
'global_summary': global_summary.summary,
|
||||
'external_links': settings.EXTERNAL_MONITORING,
|
||||
}, context_instance = template.RequestContext(request), mimetype=mimetype)
|
||||
}, context_instance=template.RequestContext(request), mimetype=mimetype)
|
||||
|
||||
|
||||
@login_required
|
||||
@enforce_admin_access
|
||||
def tenant_usage(request, tenant_id):
|
||||
(date_start, date_end, datetime_start, datetime_end) = _get_start_and_end_date(request)
|
||||
(date_start, date_end, datetime_start, datetime_end) = \
|
||||
_get_start_and_end_date(request)
|
||||
if date_start > _current_month():
|
||||
messages.error(request, 'No data for the selected period')
|
||||
date_end = date_start
|
||||
@ -167,7 +173,7 @@ def tenant_usage(request, tenant_id):
|
||||
'csv_link': _csv_usage_link(date_start),
|
||||
'instances': running_instances + terminated_instances,
|
||||
'tenant_id': tenant_id,
|
||||
}, context_instance = template.RequestContext(request), mimetype=mimetype)
|
||||
}, context_instance=template.RequestContext(request), mimetype=mimetype)
|
||||
|
||||
|
||||
@login_required
|
||||
@ -196,6 +202,7 @@ def index(request):
|
||||
'reboot_form': reboot_form,
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
@enforce_admin_access
|
||||
def refresh(request):
|
||||
|
@ -16,6 +16,7 @@ from django_openstack import api
|
||||
from django_openstack import forms
|
||||
from django_openstack.decorators import enforce_admin_access
|
||||
|
||||
|
||||
@login_required
|
||||
@enforce_admin_access
|
||||
def index(request):
|
||||
@ -23,7 +24,6 @@ def index(request):
|
||||
quotas['ram'] = int(quotas['ram']) / 100
|
||||
quotas.pop('id')
|
||||
|
||||
return render_to_response('syspanel_quotas.html',{
|
||||
return render_to_response('syspanel_quotas.html', {
|
||||
'quotas': quotas,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
@ -100,7 +100,7 @@ def index(request):
|
||||
up = False
|
||||
hostname = urlparse.urlparse(v['internalURL']).hostname
|
||||
row = {'type': k, 'internalURL': v['internalURL'], 'host': hostname,
|
||||
'region': v['region'], 'up': up }
|
||||
'region': v['region'], 'up': up}
|
||||
other_services.append(row)
|
||||
|
||||
services = sorted(services, key=lambda svc: (svc.type +
|
||||
@ -112,4 +112,4 @@ def index(request):
|
||||
'services': services,
|
||||
'service_toggle_enabled_form': ToggleService,
|
||||
'other_services': other_services,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
@ -48,8 +48,10 @@ class AddUser(forms.SelfHandlingForm):
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
api.account_api(request).role_refs.add_for_tenant_user(data['tenant'],
|
||||
data['user'], settings.OPENSTACK_KEYSTONE_DEFAULT_ROLE)
|
||||
api.account_api(request).role_refs.add_for_tenant_user(
|
||||
data['tenant'],
|
||||
data['user'],
|
||||
settings.OPENSTACK_KEYSTONE_DEFAULT_ROLE)
|
||||
messages.success(request,
|
||||
'%s was successfully added to %s.'
|
||||
% (data['user'], data['tenant']))
|
||||
@ -65,8 +67,10 @@ class RemoveUser(forms.SelfHandlingForm):
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
api.account_api(request).role_refs.delete_for_tenant_user(data['tenant'],
|
||||
data['user'], 'Member')
|
||||
api.account_api(request).role_refs.delete_for_tenant_user(
|
||||
data['tenant'],
|
||||
data['user'],
|
||||
'Member')
|
||||
messages.success(request,
|
||||
'%s was successfully removed from %s.'
|
||||
% (data['user'], data['tenant']))
|
||||
@ -78,8 +82,10 @@ class RemoveUser(forms.SelfHandlingForm):
|
||||
|
||||
class CreateTenant(forms.SelfHandlingForm):
|
||||
id = forms.CharField(label="ID (name)")
|
||||
description = forms.CharField(widget=forms.widgets.Textarea(), label="Description")
|
||||
enabled = forms.BooleanField(label="Enabled", required=False, initial=True)
|
||||
description = forms.CharField(widget=forms.widgets.Textarea(),
|
||||
label="Description")
|
||||
enabled = forms.BooleanField(label="Enabled", required=False,
|
||||
initial=True)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
@ -102,8 +108,10 @@ class CreateTenant(forms.SelfHandlingForm):
|
||||
|
||||
|
||||
class UpdateTenant(forms.SelfHandlingForm):
|
||||
id = forms.CharField(label="ID (name)", widget=forms.TextInput(attrs={'readonly':'readonly'}))
|
||||
description = forms.CharField(widget=forms.widgets.Textarea(), label="Description")
|
||||
id = forms.CharField(label="ID (name)",
|
||||
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
|
||||
description = forms.CharField(widget=forms.widgets.Textarea(),
|
||||
label="Description")
|
||||
enabled = forms.BooleanField(label="Enabled", required=False)
|
||||
|
||||
def handle(self, request, data):
|
||||
@ -126,10 +134,12 @@ class UpdateTenant(forms.SelfHandlingForm):
|
||||
|
||||
|
||||
class UpdateQuotas(forms.SelfHandlingForm):
|
||||
tenant_id = forms.CharField(label="ID (name)", widget=forms.TextInput(attrs={'readonly':'readonly'}))
|
||||
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")
|
||||
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")
|
||||
@ -140,16 +150,15 @@ class UpdateQuotas(forms.SelfHandlingForm):
|
||||
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'],
|
||||
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.'
|
||||
@ -169,9 +178,9 @@ def index(request):
|
||||
LOG.error('ApiException while getting tenant list', exc_info=True)
|
||||
messages.error(request, 'Unable to get tenant info: %s' % e.message)
|
||||
tenants.sort(key=lambda x: x.id, reverse=True)
|
||||
return render_to_response('syspanel_tenants.html',{
|
||||
return render_to_response('syspanel_tenants.html', {
|
||||
'tenants': tenants,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
@ -182,9 +191,9 @@ def create(request):
|
||||
return handled
|
||||
|
||||
return render_to_response(
|
||||
'syspanel_tenant_create.html',{
|
||||
'syspanel_tenant_create.html', {
|
||||
'form': form,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
@ -207,9 +216,9 @@ def update(request, tenant_id):
|
||||
return redirect('syspanel_tenants')
|
||||
|
||||
return render_to_response(
|
||||
'syspanel_tenant_update.html',{
|
||||
'syspanel_tenant_update.html', {
|
||||
'form': form,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
@ -237,13 +246,13 @@ def users(request, tenant_id):
|
||||
if i in new_user_ids:
|
||||
new_user_ids.remove(i)
|
||||
return render_to_response(
|
||||
'syspanel_tenant_users.html',{
|
||||
'syspanel_tenant_users.html', {
|
||||
'add_user_form': add_user_form,
|
||||
'remove_user_form': remove_user_form,
|
||||
'tenant_id': tenant_id,
|
||||
'users': users,
|
||||
'new_users': new_user_ids,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
@ -270,8 +279,8 @@ def quotas(request, tenant_id):
|
||||
form = UpdateQuotas(initial=quota_set)
|
||||
|
||||
return render_to_response(
|
||||
'syspanel_tenant_quotas.html',{
|
||||
'syspanel_tenant_quotas.html', {
|
||||
'form': form,
|
||||
'tenant_id': tenant_id,
|
||||
'quotas': quotas,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
@ -47,11 +47,14 @@ class UserForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
tenant_list = kwargs.pop('tenant_list', None)
|
||||
super(UserForm, self).__init__(*args, **kwargs)
|
||||
self.fields['tenant_id'].choices = [[tenant.id,tenant.id] for tenant in tenant_list]
|
||||
self.fields['tenant_id'].choices = [[tenant.id, tenant.id]
|
||||
for tenant in tenant_list]
|
||||
|
||||
id = forms.CharField(label="ID (username)")
|
||||
email = forms.CharField(label="Email")
|
||||
password = forms.CharField(label="Password", widget=forms.PasswordInput(render_value=False), required=False)
|
||||
password = forms.CharField(label="Password",
|
||||
widget=forms.PasswordInput(render_value=False),
|
||||
required=False)
|
||||
tenant_id = forms.ChoiceField(label="Primary Tenant")
|
||||
|
||||
|
||||
@ -64,7 +67,6 @@ class UserDeleteForm(forms.SelfHandlingForm):
|
||||
api.user_delete(request, user_id)
|
||||
messages.info(request, '%s was successfully deleted.'
|
||||
% user_id)
|
||||
|
||||
return redirect(request.build_absolute_uri())
|
||||
|
||||
|
||||
@ -108,7 +110,7 @@ def index(request):
|
||||
|
||||
user_delete_form = UserDeleteForm()
|
||||
user_enable_disable_form = UserEnableDisableForm()
|
||||
|
||||
|
||||
return shortcuts.render_to_response('syspanel_users.html', {
|
||||
'users': users,
|
||||
'user_delete_form': user_delete_form,
|
||||
@ -144,10 +146,10 @@ def update(request, user_id):
|
||||
please try again.')
|
||||
|
||||
return render_to_response(
|
||||
'syspanel_user_update.html',{
|
||||
'syspanel_user_update.html', {
|
||||
'form': form,
|
||||
'user_id': user_id,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
else:
|
||||
u = api.user_get(request, user_id)
|
||||
@ -167,10 +169,10 @@ def update(request, user_id):
|
||||
'email': email},
|
||||
tenant_list=tenants)
|
||||
return render_to_response(
|
||||
'syspanel_user_update.html',{
|
||||
'syspanel_user_update.html', {
|
||||
'form': form,
|
||||
'user_id': user_id,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
@ -216,13 +218,13 @@ def create(request):
|
||||
return redirect('syspanel_users')
|
||||
else:
|
||||
return render_to_response(
|
||||
'syspanel_user_create.html',{
|
||||
'syspanel_user_create.html', {
|
||||
'form': form,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
||||
else:
|
||||
form = UserForm(tenant_list=tenants)
|
||||
return render_to_response(
|
||||
'syspanel_user_create.html',{
|
||||
'syspanel_user_create.html', {
|
||||
'form': form,
|
||||
}, context_instance = template.RequestContext(request))
|
||||
}, context_instance=template.RequestContext(request))
|
||||
|
@ -38,6 +38,7 @@ class SiteBrandingNode(template.Node):
|
||||
def site_branding(parser, token):
|
||||
return SiteBrandingNode()
|
||||
|
||||
|
||||
@register.tag
|
||||
def site_title(parser, token):
|
||||
return settings.SITE_BRANDING
|
||||
|
@ -26,8 +26,10 @@ import datetime
|
||||
from django import template
|
||||
from dateutil import tz
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
def _parse_datetime(dtstr):
|
||||
fmts = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%d %H:%M:%S.%f",
|
||||
"%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"]
|
||||
|
@ -10,16 +10,17 @@ def dash_sidebar_modules(request):
|
||||
if signals_call:
|
||||
if signals_call[0][1]['type'] == "dash":
|
||||
return {'modules': [module[1] for module in signals_call],
|
||||
'request': request }
|
||||
'request': request}
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
@register.inclusion_tag('_sidebar_module.html')
|
||||
def syspanel_sidebar_modules(request):
|
||||
signals_call = signals.dash_modules_detect()
|
||||
if signals_call:
|
||||
if signals_call[0][1]['type'] == "syspanel":
|
||||
return {'modules': [module[1] for module in signals_call],
|
||||
'request': request }
|
||||
'request': request}
|
||||
else:
|
||||
return {}
|
||||
|
@ -10,6 +10,7 @@ from django.utils import formats
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
def int_format(value):
|
||||
return int(value)
|
||||
|
||||
@ -21,20 +22,27 @@ def float_format(value):
|
||||
def filesizeformat(bytes, filesize_number_format):
|
||||
try:
|
||||
bytes = float(bytes)
|
||||
except (TypeError,ValueError,UnicodeDecodeError):
|
||||
return translation.ungettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
|
||||
except (TypeError, ValueError, UnicodeDecodeError):
|
||||
return translation.ungettext("%(size)d byte",
|
||||
"%(size)d bytes", 0) % {'size': 0}
|
||||
|
||||
if bytes < 1024:
|
||||
return translation.ungettext("%(size)d", "%(size)d", bytes) % {'size': bytes}
|
||||
return translation.ungettext("%(size)d",
|
||||
"%(size)d", bytes) % {'size': bytes}
|
||||
if bytes < 1024 * 1024:
|
||||
return translation.ugettext("%s KB") % filesize_number_format(bytes / 1024)
|
||||
return translation.ugettext("%s KB") % \
|
||||
filesize_number_format(bytes / 1024)
|
||||
if bytes < 1024 * 1024 * 1024:
|
||||
return translation.ugettext("%s MB") % filesize_number_format(bytes / (1024 * 1024))
|
||||
return translation.ugettext("%s MB") % \
|
||||
filesize_number_format(bytes / (1024 * 1024))
|
||||
if bytes < 1024 * 1024 * 1024 * 1024:
|
||||
return translation.ugettext("%s GB") % filesize_number_format(bytes / (1024 * 1024 * 1024))
|
||||
return translation.ugettext("%s GB") % \
|
||||
filesize_number_format(bytes / (1024 * 1024 * 1024))
|
||||
if bytes < 1024 * 1024 * 1024 * 1024 * 1024:
|
||||
return translation.ugettext("%s TB") % filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024))
|
||||
return translation.ugettext("%s PB") % filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024 * 1024))
|
||||
return translation.ugettext("%s TB") % \
|
||||
filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024))
|
||||
return translation.ugettext("%s PB") % \
|
||||
filesize_number_format(bytes / (1024 * 1024 * 1024 * 1024 * 1024))
|
||||
|
||||
|
||||
@register.filter(name='mbformat')
|
||||
@ -44,4 +52,5 @@ def mbformat(mb):
|
||||
|
||||
@register.filter(name='diskgbformat')
|
||||
def diskgbformat(gb):
|
||||
return filesizeformat(gb * 1024 * 1024 * 1024, float_format).replace(' ', '')
|
||||
return filesizeformat(gb * 1024 * 1024 * 1024,
|
||||
float_format).replace(' ', '')
|
||||
|
@ -58,7 +58,7 @@ class Server(object):
|
||||
""" More or less fakes what the api is looking for """
|
||||
def __init__(self, id, image, attrs=None):
|
||||
self.id = id
|
||||
|
||||
|
||||
self.image = image
|
||||
if attrs is not None:
|
||||
self.attrs = attrs
|
||||
@ -230,7 +230,7 @@ class ServerWrapperTests(test.TestCase):
|
||||
HOST = 'hostname'
|
||||
ID = '1'
|
||||
IMAGE_NAME = 'imageName'
|
||||
IMAGE_OBJ = { 'id': '3', 'links': [{'href': '3', u'rel': u'bookmark'}] }
|
||||
IMAGE_OBJ = {'id': '3', 'links': [{'href': '3', u'rel': u'bookmark'}]}
|
||||
|
||||
def setUp(self):
|
||||
super(ServerWrapperTests, self).setUp()
|
||||
@ -1051,16 +1051,19 @@ class ExtrasApiTests(test.TestCase):
|
||||
FLAVOR = 'cherry'
|
||||
USER_DATA = {'nuts': 'berries'}
|
||||
KEY = 'user'
|
||||
SECGROUP = self.mox.CreateMock(api.SecurityGroup)
|
||||
|
||||
extras_api = self.stub_extras_api()
|
||||
extras_api.servers = self.mox.CreateMockAnything()
|
||||
extras_api.servers.create(NAME, IMAGE, FLAVOR, user_data=USER_DATA,
|
||||
key_name=KEY).AndReturn(TEST_RETURN)
|
||||
key_name=KEY,
|
||||
security_groups=[SECGROUP])\
|
||||
.AndReturn(TEST_RETURN)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
ret_val = api.server_create(self.request, NAME, IMAGE, FLAVOR,
|
||||
KEY, USER_DATA)
|
||||
KEY, USER_DATA, [SECGROUP])
|
||||
|
||||
self.assertIsInstance(ret_val, api.Server)
|
||||
self.assertEqual(ret_val._apiresource, TEST_RETURN)
|
||||
|
@ -63,7 +63,7 @@ OPENSTACK_ADMIN_TOKEN = 'test'
|
||||
QUANTUM_URL = '127.0.0.1'
|
||||
QUANTUM_PORT = '9696'
|
||||
QUANTUM_TENANT = '1234'
|
||||
QUANTUM_CLIENT_VERSION='0.1'
|
||||
QUANTUM_CLIENT_VERSION = '0.1'
|
||||
|
||||
CREDENTIAL_AUTHORIZATION_DAYS = 2
|
||||
CREDENTIAL_DOWNLOAD_URL = TESTSERVER + '/credentials/'
|
||||
|
@ -29,8 +29,10 @@ from django_openstack import urls as django_openstack_urls
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', 'django_openstack.tests.views.fakeView', name='splash'),
|
||||
url(r'^dash/$', 'django_openstack.dash.views.instances.usage', name='dash_overview'),
|
||||
url(r'^syspanel/$', 'django_openstack.syspanel.views.instances.usage', name='syspanel_overview')
|
||||
url(r'^dash/$', 'django_openstack.dash.views.instances.usage',
|
||||
name='dash_overview'),
|
||||
url(r'^syspanel/$', 'django_openstack.syspanel.views.instances.usage',
|
||||
name='syspanel_overview')
|
||||
)
|
||||
|
||||
|
||||
|
@ -38,6 +38,10 @@ class ImageViewTests(base.BaseViewTests):
|
||||
keypair.key_name = 'keyName'
|
||||
self.keypairs = (keypair,)
|
||||
|
||||
security_group = self.mox.CreateMock(api.SecurityGroup)
|
||||
security_group.name = 'default'
|
||||
self.security_groups = (security_group,)
|
||||
|
||||
def test_index(self):
|
||||
self.mox.StubOutWithMock(api, 'token_get_tenant')
|
||||
api.token_get_tenant(IsA(http.HttpRequest), self.TEST_TENANT)
|
||||
@ -135,6 +139,10 @@ class ImageViewTests(base.BaseViewTests):
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
|
||||
self.security_groups)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('dash_images_launch',
|
||||
@ -172,6 +180,7 @@ class ImageViewTests(base.BaseViewTests):
|
||||
'name': SERVER_NAME,
|
||||
'user_data': USER_DATA,
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'security_groups': 'default',
|
||||
}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'image_get')
|
||||
@ -192,6 +201,10 @@ class ImageViewTests(base.BaseViewTests):
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
|
||||
self.security_groups)
|
||||
|
||||
# called again by the form
|
||||
api.image_get(IsA(http.HttpRequest),
|
||||
IMAGE_ID).AndReturn(self.visibleImage)
|
||||
@ -204,7 +217,7 @@ class ImageViewTests(base.BaseViewTests):
|
||||
|
||||
api.server_create(IsA(http.HttpRequest), SERVER_NAME,
|
||||
self.visibleImage, self.flavors[0],
|
||||
KEY_NAME, USER_DATA)
|
||||
KEY_NAME, USER_DATA, [self.security_groups[0].name])
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'success')
|
||||
messages.success(IsA(http.HttpRequest), IsA(str))
|
||||
@ -242,6 +255,10 @@ class ImageViewTests(base.BaseViewTests):
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
|
||||
self.security_groups)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('dash_images_launch',
|
||||
@ -278,6 +295,10 @@ class ImageViewTests(base.BaseViewTests):
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
api.keypair_list(IsA(http.HttpRequest)).AndRaise(exception)
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
|
||||
self.security_groups)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('dash_images_launch',
|
||||
@ -306,6 +327,7 @@ class ImageViewTests(base.BaseViewTests):
|
||||
'name': SERVER_NAME,
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'user_data': USER_DATA,
|
||||
'security_groups': 'default',
|
||||
}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'image_get')
|
||||
@ -326,6 +348,10 @@ class ImageViewTests(base.BaseViewTests):
|
||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||
api.keypair_list(IgnoreArg()).AndReturn(self.keypairs)
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
|
||||
self.security_groups)
|
||||
|
||||
# called again by the form
|
||||
api.image_get(IgnoreArg(),
|
||||
IMAGE_ID).AndReturn(self.visibleImage)
|
||||
@ -339,8 +365,8 @@ class ImageViewTests(base.BaseViewTests):
|
||||
exception = api_exceptions.ApiException('apiException')
|
||||
api.server_create(IsA(http.HttpRequest), SERVER_NAME,
|
||||
self.visibleImage, self.flavors[0],
|
||||
KEY_NAME,
|
||||
USER_DATA).AndRaise(exception)
|
||||
KEY_NAME, USER_DATA,
|
||||
self.security_groups).AndRaise(exception)
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'error')
|
||||
messages.error(IsA(http.HttpRequest), IsA(str))
|
||||
|
@ -321,7 +321,8 @@ class InstanceViewTests(base.BaseViewTests):
|
||||
res = self.client.get(reverse('dash_instances_vnc',
|
||||
args=[self.TEST_TENANT, INSTANCE_ID]))
|
||||
|
||||
self.assertRedirectsNoFollow(res, CONSOLE_OUTPUT + '&title=serverName(1)')
|
||||
self.assertRedirectsNoFollow(res,
|
||||
CONSOLE_OUTPUT + '&title=serverName(1)')
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
|
@ -7,6 +7,7 @@ from mox import IsA
|
||||
|
||||
import openstackx.api.exceptions as api_exceptions
|
||||
|
||||
|
||||
class KeyPairViewTests(base.BaseViewTests):
|
||||
def setUp(self):
|
||||
super(KeyPairViewTests, self).setUp()
|
||||
@ -20,7 +21,8 @@ class KeyPairViewTests(base.BaseViewTests):
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('dash_keypairs', args=[self.TEST_TENANT]))
|
||||
res = self.client.get(reverse('dash_keypairs',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
self.assertTemplateUsed(res, 'dash_keypairs.html')
|
||||
self.assertItemsEqual(res.context['keypairs'], self.keypairs)
|
||||
@ -38,7 +40,8 @@ class KeyPairViewTests(base.BaseViewTests):
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('dash_keypairs', args=[self.TEST_TENANT]))
|
||||
res = self.client.get(reverse('dash_keypairs',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
self.assertTemplateUsed(res, 'dash_keypairs.html')
|
||||
self.assertEqual(len(res.context['keypairs']), 0)
|
||||
@ -91,7 +94,7 @@ class KeyPairViewTests(base.BaseViewTests):
|
||||
def test_create_keypair_get(self):
|
||||
res = self.client.get(reverse('dash_keypairs_create',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
|
||||
self.assertTemplateUsed(res, 'dash_keypairs_create.html')
|
||||
|
||||
def test_create_keypair_post(self):
|
||||
|
@ -6,6 +6,7 @@ from django_openstack import api
|
||||
from django_openstack.tests.view_tests import base
|
||||
from mox import IsA
|
||||
|
||||
|
||||
class ObjectViewTests(base.BaseViewTests):
|
||||
CONTAINER_NAME = 'containerName'
|
||||
|
||||
|
@ -0,0 +1,347 @@
|
||||
from django import http
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
from django_openstack import api
|
||||
from django_openstack.tests.view_tests import base
|
||||
from glance.common import exception as glance_exception
|
||||
from openstackx.api import exceptions as api_exceptions
|
||||
from novaclient import exceptions as novaclient_exceptions
|
||||
from mox import IgnoreArg, IsA
|
||||
|
||||
|
||||
class SecurityGroupsViewTests(base.BaseViewTests):
|
||||
def setUp(self):
|
||||
super(SecurityGroupsViewTests, self).setUp()
|
||||
|
||||
security_group = self.mox.CreateMock(api.SecurityGroup)
|
||||
security_group.name = 'default'
|
||||
self.security_groups = (security_group,)
|
||||
|
||||
def test_index(self):
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).\
|
||||
AndReturn(self.security_groups)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('dash_security_groups',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
self.assertTemplateUsed(res, 'dash_security_groups.html')
|
||||
self.assertItemsEqual(res.context['security_groups'],
|
||||
self.security_groups)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_index_exception(self):
|
||||
exception = novaclient_exceptions.ClientException('ClientException',
|
||||
message='ClientException')
|
||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||
api.security_group_list(IsA(http.HttpRequest)).AndRaise(exception)
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'error')
|
||||
messages.error(IsA(http.HttpRequest), IsA(str))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('dash_security_groups',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
self.assertTemplateUsed(res, 'dash_security_groups.html')
|
||||
self.assertEqual(len(res.context['security_groups']), 0)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_create_security_groups_get(self):
|
||||
res = self.client.get(reverse('dash_security_groups_create',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
self.assertTemplateUsed(res, 'dash_security_groups_create.html')
|
||||
|
||||
def test_create_security_groups_post(self):
|
||||
SECGROUP_NAME = 'fakegroup'
|
||||
SECGROUP_DESC = 'fakegroup_desc'
|
||||
|
||||
new_group = self.mox.CreateMock(api.SecurityGroup)
|
||||
new_group.name = SECGROUP_NAME
|
||||
|
||||
formData = {'method': 'CreateGroup',
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'name': SECGROUP_NAME,
|
||||
'description': SECGROUP_DESC,
|
||||
}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_create')
|
||||
api.security_group_create(IsA(http.HttpRequest),
|
||||
SECGROUP_NAME, SECGROUP_DESC).AndReturn(new_group)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(reverse('dash_security_groups_create',
|
||||
args=[self.TEST_TENANT]),
|
||||
formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res, reverse('dash_security_groups',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_create_security_groups_post_exception(self):
|
||||
SECGROUP_NAME = 'fakegroup'
|
||||
SECGROUP_DESC = 'fakegroup_desc'
|
||||
|
||||
exception = novaclient_exceptions.ClientException('ClientException',
|
||||
message='ClientException')
|
||||
|
||||
formData = {'method': 'CreateGroup',
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'name': SECGROUP_NAME,
|
||||
'description': SECGROUP_DESC,
|
||||
}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_create')
|
||||
api.security_group_create(IsA(http.HttpRequest),
|
||||
SECGROUP_NAME, SECGROUP_DESC).AndRaise(exception)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(reverse('dash_security_groups_create',
|
||||
args=[self.TEST_TENANT]),
|
||||
formData)
|
||||
|
||||
self.assertTemplateUsed(res, 'dash_security_groups_create.html')
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_edit_rules_get(self):
|
||||
SECGROUP_ID = '1'
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_get')
|
||||
api.security_group_get(IsA(http.HttpRequest), SECGROUP_ID).AndReturn(
|
||||
self.security_groups[0])
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]))
|
||||
|
||||
self.assertTemplateUsed(res, 'dash_security_groups_edit_rules.html')
|
||||
self.assertItemsEqual(res.context['security_group'].name,
|
||||
self.security_groups[0].name)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_edit_rules_get_exception(self):
|
||||
SECGROUP_ID = '1'
|
||||
|
||||
exception = novaclient_exceptions.ClientException('ClientException',
|
||||
message='ClientException')
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_get')
|
||||
api.security_group_get(IsA(http.HttpRequest), SECGROUP_ID).AndRaise(
|
||||
exception)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]))
|
||||
|
||||
self.assertRedirectsNoFollow(res, reverse('dash_security_groups',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_edit_rules_add_rule(self):
|
||||
SECGROUP_ID = '1'
|
||||
RULE_ID = '1'
|
||||
FROM_PORT = '-1'
|
||||
TO_PORT = '-1'
|
||||
IP_PROTOCOL = 'icmp'
|
||||
CIDR = '0.0.0.0/0'
|
||||
|
||||
new_rule = self.mox.CreateMock(api.SecurityGroup)
|
||||
new_rule.from_port = FROM_PORT
|
||||
new_rule.to_port = TO_PORT
|
||||
new_rule.ip_protocol = IP_PROTOCOL
|
||||
new_rule.cidr = CIDR
|
||||
new_rule.security_group_id = SECGROUP_ID
|
||||
new_rule.id = RULE_ID
|
||||
|
||||
formData = {'method': 'AddRule',
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'security_group_id': SECGROUP_ID,
|
||||
'from_port': FROM_PORT,
|
||||
'to_port': TO_PORT,
|
||||
'ip_protocol': IP_PROTOCOL,
|
||||
'cidr': CIDR}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_rule_create')
|
||||
api.security_group_rule_create(IsA(http.HttpRequest),
|
||||
SECGROUP_ID, IP_PROTOCOL, FROM_PORT, TO_PORT, CIDR)\
|
||||
.AndReturn(new_rule)
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'info')
|
||||
messages.info(IsA(http.HttpRequest), IsA(str))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]),
|
||||
formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res,
|
||||
reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]))
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_edit_rules_add_rule_exception(self):
|
||||
exception = novaclient_exceptions.ClientException('ClientException',
|
||||
message='ClientException')
|
||||
|
||||
SECGROUP_ID = '1'
|
||||
RULE_ID = '1'
|
||||
FROM_PORT = '-1'
|
||||
TO_PORT = '-1'
|
||||
IP_PROTOCOL = 'icmp'
|
||||
CIDR = '0.0.0.0/0'
|
||||
|
||||
formData = {'method': 'AddRule',
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'security_group_id': SECGROUP_ID,
|
||||
'from_port': FROM_PORT,
|
||||
'to_port': TO_PORT,
|
||||
'ip_protocol': IP_PROTOCOL,
|
||||
'cidr': CIDR}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_rule_create')
|
||||
api.security_group_rule_create(IsA(http.HttpRequest),
|
||||
SECGROUP_ID, IP_PROTOCOL, FROM_PORT,
|
||||
TO_PORT, CIDR).AndRaise(exception)
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'error')
|
||||
messages.error(IsA(http.HttpRequest), IsA(str))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]),
|
||||
formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res,
|
||||
reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]))
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_edit_rules_delete_rule(self):
|
||||
SECGROUP_ID = '1'
|
||||
RULE_ID = '1'
|
||||
|
||||
formData = {'method': 'DeleteRule',
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'security_group_rule_id': RULE_ID,
|
||||
}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_rule_delete')
|
||||
api.security_group_rule_delete(IsA(http.HttpRequest), RULE_ID)
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'info')
|
||||
messages.info(IsA(http.HttpRequest), IsA(unicode))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]),
|
||||
formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res,
|
||||
reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]))
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_edit_rules_delete_rule_exception(self):
|
||||
exception = novaclient_exceptions.ClientException('ClientException',
|
||||
message='ClientException')
|
||||
|
||||
SECGROUP_ID = '1'
|
||||
RULE_ID = '1'
|
||||
|
||||
formData = {'method': 'DeleteRule',
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'security_group_rule_id': RULE_ID,
|
||||
}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_rule_delete')
|
||||
api.security_group_rule_delete(IsA(http.HttpRequest), RULE_ID).\
|
||||
AndRaise(exception)
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'error')
|
||||
messages.error(IsA(http.HttpRequest), IsA(str))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]),
|
||||
formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res,
|
||||
reverse('dash_security_groups_edit_rules',
|
||||
args=[self.TEST_TENANT, SECGROUP_ID]))
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_delete_group(self):
|
||||
SECGROUP_ID = '1'
|
||||
|
||||
formData = {'method': 'DeleteGroup',
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'security_group_id': SECGROUP_ID,
|
||||
}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_delete')
|
||||
api.security_group_delete(IsA(http.HttpRequest), SECGROUP_ID)
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'info')
|
||||
messages.info(IsA(http.HttpRequest), IsA(unicode))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(reverse('dash_security_groups',
|
||||
args=[self.TEST_TENANT]),
|
||||
formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res, reverse('dash_security_groups',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_delete_group_exception(self):
|
||||
exception = novaclient_exceptions.ClientException('ClientException',
|
||||
message='ClientException')
|
||||
|
||||
SECGROUP_ID = '1'
|
||||
|
||||
formData = {'method': 'DeleteGroup',
|
||||
'tenant_id': self.TEST_TENANT,
|
||||
'security_group_id': SECGROUP_ID,
|
||||
}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'security_group_delete')
|
||||
api.security_group_delete(IsA(http.HttpRequest), SECGROUP_ID).\
|
||||
AndRaise(exception)
|
||||
|
||||
self.mox.StubOutWithMock(messages, 'error')
|
||||
messages.error(IsA(http.HttpRequest), IsA(str))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.post(reverse('dash_security_groups',
|
||||
args=[self.TEST_TENANT]),
|
||||
formData)
|
||||
|
||||
self.assertRedirectsNoFollow(res, reverse('dash_security_groups',
|
||||
args=[self.TEST_TENANT]))
|
||||
|
||||
self.mox.VerifyAll()
|
@ -4,6 +4,7 @@ from django_openstack.tests.view_tests import base
|
||||
from mox import IgnoreArg
|
||||
from openstackx.api import exceptions as api_exceptions
|
||||
|
||||
|
||||
class UsersViewTests(base.BaseViewTests):
|
||||
def setUp(self):
|
||||
super(UsersViewTests, self).setUp()
|
||||
@ -71,8 +72,10 @@ class UsersViewTests(base.BaseViewTests):
|
||||
'enabled': 'enable'}
|
||||
|
||||
self.mox.StubOutWithMock(api, 'user_update_enabled')
|
||||
api_exception = api_exceptions.ApiException('apiException', message='apiException')
|
||||
api.user_update_enabled(IgnoreArg(), OTHER_USER, True).AndRaise(api_exception)
|
||||
api_exception = api_exceptions.ApiException('apiException',
|
||||
message='apiException')
|
||||
api.user_update_enabled(IgnoreArg(),
|
||||
OTHER_USER, True).AndRaise(api_exception)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
|
@ -21,10 +21,14 @@
|
||||
|
||||
from django.core.management import execute_manager
|
||||
try:
|
||||
import settings # Assumed to be in the same directory.
|
||||
import settings # Assumed to be in the same directory.
|
||||
except ImportError:
|
||||
import sys
|
||||
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
|
||||
sys.stderr.write("Error: Can't find the file 'settings.py' in the "
|
||||
"directory containing %r. It appears you've customized things.\nYou'll "
|
||||
"have to run django-admin.py, passing it your settings module.\n(If "
|
||||
"the file settings.py does indeed exist, it's causing an ImportError "
|
||||
"somehow.)\n" % __file__)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -131,8 +131,9 @@ if DEBUG:
|
||||
import debug_toolbar
|
||||
|
||||
INSTALLED_APPS += ('debug_toolbar',)
|
||||
MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
|
||||
MIDDLEWARE_CLASSES += (
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',)
|
||||
except ImportError:
|
||||
logging.info('Running in debug mode without debug_toolbar.')
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE='Member'
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = 'Member'
|
||||
|
@ -948,8 +948,34 @@ td ul span {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.dash_block .left h3 {
|
||||
font-size: 24px;
|
||||
margin: 0 0 25px 0;
|
||||
}
|
||||
|
||||
/* Security Groups */
|
||||
|
||||
.security_group_rule_form_inner {
|
||||
margin: 30px 30px;
|
||||
}
|
||||
|
||||
.dash_block .security_group_rule_form_wrapper {
|
||||
border: 1px solid #eee;
|
||||
width: 46%;
|
||||
}
|
||||
|
||||
.dash_block .security_group_rule_form_wrapper input[type="submit"]{
|
||||
background: #93bc8d;
|
||||
color: #fff;
|
||||
margin: 0 17px 0 0;
|
||||
border: 1px solid #8eb08a;
|
||||
-webkit-transition-property: background;
|
||||
-webkit-transition-duration: 0.2s;
|
||||
text-shadow: #62935c 0 -1px 0;
|
||||
}
|
||||
|
||||
/* Network details page */
|
||||
|
||||
td.ACTIVE {
|
||||
color:#6EAF6E;
|
||||
}
|
||||
|
@ -45,6 +45,9 @@ $(function(){
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
// Fancy multi-selects
|
||||
$(".chzn-select").chosen()
|
||||
|
||||
$(".detach").click(function(e){
|
||||
var response = confirm('Are you sure you want to detach the '+$(this).attr('title')+" ?");
|
||||
|
@ -9,6 +9,7 @@
|
||||
<li><a {% if current_sidebar == "snapshots" %} class="active" {% endif %} href="{% url dash_snapshots request.user.tenant %}">Snapshots</a></li>
|
||||
<li><a {% if current_sidebar == "keypairs" %} class="active" {% endif %} href="{% url dash_keypairs request.user.tenant %}">Keypairs</a></li>
|
||||
<li><a {% if current_sidebar == "floatingips" %} class="active" {% endif %} href="{% url dash_floating_ips request.user.tenant %}">Floating IPs</a></li>
|
||||
<li><a {% if current_sidebar == "security_groups" %} class="active" {% endif %} href="{% url dash_security_groups request.user.tenant %}">Security Groups</a></li>
|
||||
{% if quantum_configured %}
|
||||
<li><a {% if current_sidebar == "networks" %} class="active" {% endif %} href="{% url dash_networks request.user.tenant %}">Networks</a></li>
|
||||
{% endif %}
|
||||
|
@ -0,0 +1,8 @@
|
||||
<form id="form_delete_{{security_group.id}}" class="form-delete" method="post">
|
||||
{% csrf_token %}
|
||||
{% for hidden in form.hidden_fields %}
|
||||
{{hidden}}
|
||||
{% endfor %}
|
||||
<input name="security_group_id" type="hidden" value="{{security_group.id}}" />
|
||||
<input id="delete_{{security_group.id}}" class="delete" title="Security Group: {{security_group.id}}" type="submit" value="Delete" />
|
||||
</form>
|
@ -0,0 +1,8 @@
|
||||
<form id="form_delete_{{rule.id}}" class="form-delete" method="post">
|
||||
{% csrf_token %}
|
||||
{% for hidden in form.hidden_fields %}
|
||||
{{hidden}}
|
||||
{% endfor %}
|
||||
<input name="security_group_rule_id" type="hidden" value="{{rule.id}}" />
|
||||
<input id="delete_{{rule.id}}" class="delete" title="Security Group: {{rule.id}}" type="submit" value="Delete" />
|
||||
</form>
|
@ -4,6 +4,7 @@
|
||||
<tr id='headings'>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Groups</th>
|
||||
<th>Image</th>
|
||||
<th>Size</th>
|
||||
<th>IPs</th>
|
||||
@ -20,6 +21,13 @@
|
||||
<small> ({{instance.attrs.key_name}}) </small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
{% for group in instance.attrs.security_groups %}
|
||||
<li>{{group}}</li>
|
||||
{% endfor %}
|
||||
<ul>
|
||||
</td>
|
||||
<td>{{instance.image_name}}</td>
|
||||
<td>
|
||||
<ul>
|
||||
|
@ -0,0 +1,12 @@
|
||||
<form id="security_group_form" method="post">
|
||||
<fieldset>
|
||||
{% 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 Security Group" class="large-rounded" />
|
||||
</fieldset>
|
||||
</form>
|
@ -0,0 +1,23 @@
|
||||
<table id="security_groups" class="wide">
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
{% for security_group in security_groups %}
|
||||
<tr class="{% cycle 'odd' 'even' %}">
|
||||
<td>{{ security_group.id }}</td>
|
||||
<td>{{ security_group.name }}</td>
|
||||
<td>{{ security_group.description }}</td>
|
||||
<td id="actions">
|
||||
<ul>
|
||||
<li><a href="{% url dash_security_groups_edit_rules request.user.tenant security_group.id %}">Edit Rules</a></li>
|
||||
{% if security_group.name != 'default' %}
|
||||
<li class="form">{% include "_delete_security_group.html" with form=delete_form %}</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
@ -44,8 +44,8 @@
|
||||
<ul>
|
||||
<li class="form">{% include "_terminate.html" with form=terminate_form %}</li>
|
||||
<li class="form">{% include "_reboot.html" with form=reboot_form %}</li>
|
||||
<li><a target="_blank" href="{% url dash_instances_console instance.attrs.tenant_id instance.id %}">Console Log</a></li>
|
||||
<li><a target="_blank" href="{% url dash_instances_vnc instance.attrs.tenant_id instance.id %}">VNC Console</a></li>
|
||||
<li><a target="_blank" href="{% url dash_instances_console request.user.tenant instance.id %}">Console Log</a></li>
|
||||
<li><a target="_blank" href="{% url dash_instances_vnc request.user.tenant instance.id %}">VNC Console</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -11,6 +11,7 @@
|
||||
<script charset='utf-8' src='{{ STATIC_URL }}dashboard/js/form_examples.js' type='text/javascript'></script>
|
||||
<script charset='utf-8' src='{{ STATIC_URL }}dashboard/js/application.js' type='text/javascript'></script>
|
||||
<link href='{{ STATIC_URL }}dashboard/css/style.css' media='screen' rel='stylesheet' />
|
||||
<link href='/media/dashboard/css/chosen.css' media='screen' rel='stylesheet' />
|
||||
{% block headerjs %}{% endblock %}
|
||||
{% block headercss %}{% endblock %}
|
||||
</head>
|
||||
|
@ -0,0 +1,25 @@
|
||||
{% extends 'dash_base.html' %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% with current_sidebar="security_groups" %}
|
||||
{{block.super}}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% url dash_security_groups request.user.tenant as refresh_link %}
|
||||
{# to make searchable false, just remove it from the include statement #}
|
||||
{% include "_page_header.html" with title="Security Groups" refresh_link=refresh_link searchable="true" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block dash_main %}
|
||||
{% if security_groups %}
|
||||
{% include '_security_group_list.html' %}
|
||||
<a id="security_groups_create_link" class="action_link large-rounded" href="{% url dash_security_groups_create request.user.tenant %}">Create Security Group</a>
|
||||
{% else %}
|
||||
<div class="message_box info">
|
||||
<h2>Info</h2>
|
||||
<p>There are currently no security groups. <a href='{% url dash_security_groups_create request.user.tenant %}'>Create A Security Group >></a></p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -0,0 +1,25 @@
|
||||
{% extends 'dash_base.html' %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% with current_sidebar="security_groups" %}
|
||||
{{block.super}}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "_page_header.html" with title="Create Security Group" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block dash_main %}
|
||||
<div class="dash_block">
|
||||
<div class="left">
|
||||
{% include '_security_group_form.html' %}
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<h3>Description:</h3>
|
||||
<p>From here you can create a new security group</p>
|
||||
</div>
|
||||
<div class="clear"> </div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,68 @@
|
||||
{% extends 'dash_base.html' %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% with current_sidebar="security_groups" %}
|
||||
{{block.super}}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "_page_header.html" with title="Edit Security Group Rules" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block dash_main %}
|
||||
<div class="dash_block">
|
||||
<div class="left">
|
||||
<h3> Rules for Security Group '{{security_group.name}}'</h3>
|
||||
<table id="security_groups" class="wide">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>IP Protocol</th>
|
||||
<th>From Port</th>
|
||||
<th>To Port</th>
|
||||
<th>CIDR</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
{% for rule in security_group.rules %}
|
||||
<tr class="{% cycle 'odd' 'even' %}">
|
||||
<td>{{ rule.id }}</td>
|
||||
<td>{{ rule.ip_protocol }}</td>
|
||||
<td>{{ rule.from_port }}</td>
|
||||
<td>{{ rule.to_port }}</td>
|
||||
<td>{{rule.ip_range.cidr}}</td>
|
||||
<td id="actions">
|
||||
<ul>
|
||||
<li class="form">{% include "_delete_security_group_rule.html" with form=delete_form %}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="100%">
|
||||
No rules for this security group
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
<div class="right security_group_rule_form_wrapper">
|
||||
<div class="security_group_rule_form_inner">
|
||||
<h3>Add a rule</h3>
|
||||
<form id="security_group_rule_form" method="post">
|
||||
<fieldset>
|
||||
{% csrf_token %}
|
||||
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
|
||||
{% for field in form.visible_fields %}
|
||||
{{ field.label_tag }}
|
||||
{{ field.errors }}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
<input name="security_group_id" type="hidden" value="{{security_group.id}}" />
|
||||
<input type="submit" value="Add Rule" class="large-rounded action_link" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,7 +1,7 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
''' Test for django mailer.
|
||||
|
||||
This test is pretty much worthless, and should be removed once real testing of
|
||||
This test is pretty much worthless, and should be removed once real testing of
|
||||
views that send emails is implemented
|
||||
'''
|
||||
|
||||
|
@ -33,8 +33,10 @@ from django_openstack import urls as django_openstack_urls
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', 'dashboard.views.splash', name='splash'),
|
||||
url(r'^dash/$', 'django_openstack.dash.views.instances.usage', name='dash_overview'),
|
||||
url(r'^syspanel/$', 'django_openstack.syspanel.views.instances.usage', name='syspanel_overview'),
|
||||
url(r'^dash/$', 'django_openstack.dash.views.instances.usage',
|
||||
name='dash_overview'),
|
||||
url(r'^syspanel/$', 'django_openstack.syspanel.views.instances.usage',
|
||||
name='syspanel_overview'),
|
||||
)
|
||||
|
||||
# Development static app and project media serving using the staticfiles app.
|
||||
|
340
openstack-dashboard/media/dashboard/css/chosen.css
Normal file
340
openstack-dashboard/media/dashboard/css/chosen.css
Normal file
@ -0,0 +1,340 @@
|
||||
/* @group Base */
|
||||
select.chzn-select {
|
||||
visibility: hidden;
|
||||
height: 28px !important;
|
||||
min-height: 28px !important;
|
||||
}
|
||||
.chzn-container {
|
||||
font-size: 13px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.chzn-container .chzn-drop {
|
||||
background: #fff;
|
||||
border: 1px solid #aaa;
|
||||
border-top: 0;
|
||||
position: absolute;
|
||||
top: 29px;
|
||||
left: 0;
|
||||
-webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15);
|
||||
-moz-box-shadow : 0 4px 5px rgba(0,0,0,.15);
|
||||
-o-box-shadow : 0 4px 5px rgba(0,0,0,.15);
|
||||
box-shadow : 0 4px 5px rgba(0,0,0,.15);
|
||||
z-index: 999;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Single Chosen */
|
||||
.chzn-container-single .chzn-single {
|
||||
background-color: #fff;
|
||||
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
|
||||
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
|
||||
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
|
||||
background-image: -o-linear-gradient(top, #eeeeee 0%,#ffffff 50%);
|
||||
background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 50%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
|
||||
background-image: linear-gradient(top, #eeeeee 0%,#ffffff 50%);
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius : 4px;
|
||||
border-radius : 4px;
|
||||
-moz-background-clip : padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip : padding-box;
|
||||
border: 1px solid #aaa;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
padding: 0 0 0 8px;
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
}
|
||||
.chzn-container-single .chzn-single span {
|
||||
margin-right: 26px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
-o-text-overflow: ellipsis;
|
||||
-ms-text-overflow: ellipsis;
|
||||
-moz-binding: url('/xml/ellipsis.xml#ellipsis');
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.chzn-container-single .chzn-single div {
|
||||
-webkit-border-radius: 0 4px 4px 0;
|
||||
-moz-border-radius : 0 4px 4px 0;
|
||||
border-radius : 0 4px 4px 0;
|
||||
-moz-background-clip : padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip : padding-box;
|
||||
background: #ccc;
|
||||
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
|
||||
background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
|
||||
background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
|
||||
background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
|
||||
background-image: -ms-linear-gradient(top, #cccccc 0%,#eeeeee 60%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#cccccc', endColorstr='#eeeeee',GradientType=0 );
|
||||
background-image: linear-gradient(top, #cccccc 0%,#eeeeee 60%);
|
||||
border-left: 1px solid #aaa;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 18px;
|
||||
}
|
||||
.chzn-container-single .chzn-single div b {
|
||||
background: url('chosen-sprite.png') no-repeat 0 1px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.chzn-container-single .chzn-search {
|
||||
padding: 3px 4px;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chzn-container-single .chzn-search input {
|
||||
background: #fff url('chosen-sprite.png') no-repeat 100% -20px;
|
||||
background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
|
||||
background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat 100% -20px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat 100% -20px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat 100% -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat 100% -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat 100% -20px, linear-gradient(top, #ffffff 85%,#eeeeee 99%);
|
||||
margin: 1px 0;
|
||||
padding: 4px 20px 4px 5px;
|
||||
outline: 0;
|
||||
border: 1px solid #aaa;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
.chzn-container-single .chzn-drop {
|
||||
-webkit-border-radius: 0 0 4px 4px;
|
||||
-moz-border-radius : 0 0 4px 4px;
|
||||
border-radius : 0 0 4px 4px;
|
||||
-moz-background-clip : padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip : padding-box;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Multi Chosen */
|
||||
.chzn-container-multi .chzn-choices {
|
||||
background-color: #fff;
|
||||
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
|
||||
background-image: -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
||||
background-image: -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
||||
background-image: -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
|
||||
background-image: -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
|
||||
background-image: linear-gradient(top, #ffffff 85%,#eeeeee 99%);
|
||||
border: 1px solid #aaa;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
cursor: text;
|
||||
overflow: hidden;
|
||||
height: auto !important;
|
||||
height: 1%;
|
||||
position: relative;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices li {
|
||||
float: left;
|
||||
list-style: none;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-field {
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-field input {
|
||||
color: #666;
|
||||
background: transparent !important;
|
||||
border: 0 !important;
|
||||
padding: 5px;
|
||||
margin: 1px 0;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow : none;
|
||||
-o-box-shadow : none;
|
||||
box-shadow : none;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-field .default {
|
||||
color: #999;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice {
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius : 3px;
|
||||
border-radius : 3px;
|
||||
-moz-background-clip : padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip : padding-box;
|
||||
background-color: #e4e4e4;
|
||||
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #e4e4e4), color-stop(0.7, #eeeeee));
|
||||
background-image: -webkit-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%);
|
||||
background-image: -moz-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%);
|
||||
background-image: -o-linear-gradient(bottom, #e4e4e4 0%, #eeeeee 70%);
|
||||
background-image: -ms-linear-gradient(top, #e4e4e4 0%,#eeeeee 70%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e4e4e4', endColorstr='#eeeeee',GradientType=0 );
|
||||
background-image: linear-gradient(top, #e4e4e4 0%,#eeeeee 70%);
|
||||
color: #333;
|
||||
border: 1px solid #b4b4b4;
|
||||
line-height: 13px;
|
||||
padding: 3px 19px 3px 6px;
|
||||
margin: 3px 0 3px 5px;
|
||||
position: relative;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice span {
|
||||
cursor: default;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice-focus {
|
||||
background: #d4d4d4;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice .search-choice-close {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 6px;
|
||||
width: 8px;
|
||||
height: 9px;
|
||||
font-size: 1px;
|
||||
background: url(chosen-sprite.png) right top no-repeat;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover {
|
||||
background-position: right -9px;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close {
|
||||
background-position: right -9px;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Results */
|
||||
.chzn-container .chzn-results {
|
||||
margin: 0 4px 4px 0;
|
||||
max-height: 190px;
|
||||
padding: 0 0 0 4px;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.chzn-container-multi .chzn-results {
|
||||
margin: -1px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
.chzn-container .chzn-results li {
|
||||
line-height: 80%;
|
||||
padding: 7px 7px 8px;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.chzn-container .chzn-results .active-result {
|
||||
cursor: pointer;
|
||||
}
|
||||
.chzn-container .chzn-results .highlighted {
|
||||
background: #3875d7;
|
||||
color: #fff;
|
||||
}
|
||||
.chzn-container .chzn-results li em {
|
||||
background: #feffde;
|
||||
font-style: normal;
|
||||
}
|
||||
.chzn-container .chzn-results .highlighted em {
|
||||
background: transparent;
|
||||
}
|
||||
.chzn-container .chzn-results .no-results {
|
||||
background: #f4f4f4;
|
||||
}
|
||||
.chzn-container .chzn-results .group-result {
|
||||
cursor: default;
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
}
|
||||
.chzn-container .chzn-results .group-option {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.chzn-container-multi .chzn-drop .result-selected {
|
||||
display: none;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Active */
|
||||
.chzn-container-active .chzn-single {
|
||||
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
|
||||
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
-o-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
border: 1px solid #5897fb;
|
||||
}
|
||||
.chzn-container-active .chzn-single-with-drop {
|
||||
border: 1px solid #aaa;
|
||||
-webkit-box-shadow: 0 1px 0 #fff inset;
|
||||
-moz-box-shadow : 0 1px 0 #fff inset;
|
||||
-o-box-shadow : 0 1px 0 #fff inset;
|
||||
box-shadow : 0 1px 0 #fff inset;
|
||||
background-color: #eee;
|
||||
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
|
||||
background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
|
||||
background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
|
||||
background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
|
||||
background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
|
||||
background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
|
||||
-webkit-border-bottom-left-radius : 0;
|
||||
-webkit-border-bottom-right-radius: 0;
|
||||
-moz-border-radius-bottomleft : 0;
|
||||
-moz-border-radius-bottomright: 0;
|
||||
border-bottom-left-radius : 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.chzn-container-active .chzn-single-with-drop div {
|
||||
background: transparent;
|
||||
border-left: none;
|
||||
}
|
||||
.chzn-container-active .chzn-single-with-drop div b {
|
||||
background-position: -18px 1px;
|
||||
}
|
||||
.chzn-container-active .chzn-choices {
|
||||
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
|
||||
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
-o-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
border: 1px solid #5897fb;
|
||||
}
|
||||
.chzn-container-active .chzn-choices .search-field input {
|
||||
color: #111 !important;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Right to Left */
|
||||
.chzn-rtl { direction:rtl;text-align: right; }
|
||||
.chzn-rtl .chzn-single { padding-left: 0; padding-right: 8px; }
|
||||
.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; }
|
||||
.chzn-rtl .chzn-single div {
|
||||
left: 0; right: auto;
|
||||
border-left: none; border-right: 1px solid #aaaaaa;
|
||||
-webkit-border-radius: 4px 0 0 4px;
|
||||
-moz-border-radius : 4px 0 0 4px;
|
||||
border-radius : 4px 0 0 4px;
|
||||
}
|
||||
.chzn-rtl .chzn-choices li { float: right; }
|
||||
.chzn-rtl .chzn-choices .search-choice { padding: 3px 6px 3px 19px; margin: 3px 5px 3px 0; }
|
||||
.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 5px; right: auto; background-position: right top;}
|
||||
.chzn-rtl.chzn-container-single .chzn-results { margin-left: 4px; margin-right: 0; padding-left: 0; padding-right: 4px; }
|
||||
.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 20px; }
|
||||
.chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; }
|
||||
.chzn-rtl .chzn-search input {
|
||||
background: url('chosen-sprite.png') no-repeat -38px -20px, #ffffff;
|
||||
background: url('chosen-sprite.png') no-repeat -38px -20px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
|
||||
background: url('chosen-sprite.png') no-repeat -38px -20px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat -38px -20px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat -38px -20px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat -38px -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat -38px -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
|
||||
background: url('chosen-sprite.png') no-repeat -38px -20px, linear-gradient(top, #ffffff 85%,#eeeeee 99%);
|
||||
padding: 4px 5px 4px 20px;
|
||||
}
|
||||
/* @end */
|
786
openstack-dashboard/media/dashboard/js/chosen.jquery.js
Normal file
786
openstack-dashboard/media/dashboard/js/chosen.jquery.js
Normal file
@ -0,0 +1,786 @@
|
||||
// Chosen, a Select Box Enhancer for jQuery and Protoype
|
||||
// by Patrick Filler for Harvest, http://getharvest.com
|
||||
//
|
||||
// Version 0.9
|
||||
// Full source at https://github.com/harvesthq/chosen
|
||||
// Copyright (c) 2011 Harvest http://getharvest.com
|
||||
|
||||
// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
|
||||
// This file is generated by `cake build`, do not edit it by hand.
|
||||
(function() {
|
||||
/*
|
||||
Chosen source: generate output using 'cake build'
|
||||
Copyright (c) 2011 by Harvest
|
||||
*/ var $, Chosen, get_side_border_padding, root;
|
||||
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
root = this;
|
||||
$ = jQuery;
|
||||
$.fn.extend({
|
||||
chosen: function(data, options) {
|
||||
return $(this).each(function(input_field) {
|
||||
if (!($(this)).hasClass("chzn-done")) {
|
||||
return new Chosen(this, data, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
Chosen = (function() {
|
||||
function Chosen(elmn) {
|
||||
this.set_default_values();
|
||||
this.form_field = elmn;
|
||||
this.form_field_jq = $(this.form_field);
|
||||
this.is_multiple = this.form_field.multiple;
|
||||
this.is_rtl = this.form_field_jq.hasClass("chzn-rtl");
|
||||
this.default_text_default = this.form_field.multiple ? "Select Some Options" : "Select an Option";
|
||||
this.set_up_html();
|
||||
this.register_observers();
|
||||
this.form_field_jq.addClass("chzn-done");
|
||||
}
|
||||
Chosen.prototype.set_default_values = function() {
|
||||
this.click_test_action = __bind(function(evt) {
|
||||
return this.test_active_click(evt);
|
||||
}, this);
|
||||
this.active_field = false;
|
||||
this.mouse_on_container = false;
|
||||
this.results_showing = false;
|
||||
this.result_highlighted = null;
|
||||
this.result_single_selected = null;
|
||||
return this.choices = 0;
|
||||
};
|
||||
Chosen.prototype.set_up_html = function() {
|
||||
var container_div, dd_top, dd_width, sf_width;
|
||||
this.container_id = this.form_field.id.length ? this.form_field.id.replace(/(:|\.)/g, '_') : this.generate_field_id();
|
||||
this.container_id += "_chzn";
|
||||
this.f_width = this.form_field_jq.width();
|
||||
this.default_text = this.form_field_jq.data('placeholder') ? this.form_field_jq.data('placeholder') : this.default_text_default;
|
||||
container_div = $("<div />", {
|
||||
id: this.container_id,
|
||||
"class": "chzn-container " + (this.is_rtl ? ' chzn-rtl' : void 0),
|
||||
style: 'width: ' + this.f_width + 'px;'
|
||||
});
|
||||
if (this.is_multiple) {
|
||||
container_div.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>');
|
||||
} else {
|
||||
container_div.html('<a href="javascript:void(0)" class="chzn-single"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" /></div><ul class="chzn-results"></ul></div>');
|
||||
}
|
||||
this.form_field_jq.hide().after(container_div);
|
||||
this.container = $('#' + this.container_id);
|
||||
this.container.addClass("chzn-container-" + (this.is_multiple ? "multi" : "single"));
|
||||
this.dropdown = this.container.find('div.chzn-drop').first();
|
||||
dd_top = this.container.height();
|
||||
dd_width = this.f_width - get_side_border_padding(this.dropdown);
|
||||
this.dropdown.css({
|
||||
"width": dd_width + "px",
|
||||
"top": dd_top + "px"
|
||||
});
|
||||
this.search_field = this.container.find('input').first();
|
||||
this.search_results = this.container.find('ul.chzn-results').first();
|
||||
this.search_field_scale();
|
||||
this.search_no_results = this.container.find('li.no-results').first();
|
||||
if (this.is_multiple) {
|
||||
this.search_choices = this.container.find('ul.chzn-choices').first();
|
||||
this.search_container = this.container.find('li.search-field').first();
|
||||
} else {
|
||||
this.search_container = this.container.find('div.chzn-search').first();
|
||||
this.selected_item = this.container.find('.chzn-single').first();
|
||||
sf_width = dd_width - get_side_border_padding(this.search_container) - get_side_border_padding(this.search_field);
|
||||
this.search_field.css({
|
||||
"width": sf_width + "px"
|
||||
});
|
||||
}
|
||||
this.results_build();
|
||||
return this.set_tab_index();
|
||||
};
|
||||
Chosen.prototype.register_observers = function() {
|
||||
this.container.click(__bind(function(evt) {
|
||||
return this.container_click(evt);
|
||||
}, this));
|
||||
this.container.mouseenter(__bind(function(evt) {
|
||||
return this.mouse_enter(evt);
|
||||
}, this));
|
||||
this.container.mouseleave(__bind(function(evt) {
|
||||
return this.mouse_leave(evt);
|
||||
}, this));
|
||||
this.search_results.click(__bind(function(evt) {
|
||||
return this.search_results_click(evt);
|
||||
}, this));
|
||||
this.search_results.mouseover(__bind(function(evt) {
|
||||
return this.search_results_mouseover(evt);
|
||||
}, this));
|
||||
this.search_results.mouseout(__bind(function(evt) {
|
||||
return this.search_results_mouseout(evt);
|
||||
}, this));
|
||||
this.form_field_jq.bind("liszt:updated", __bind(function(evt) {
|
||||
return this.results_update_field(evt);
|
||||
}, this));
|
||||
this.search_field.blur(__bind(function(evt) {
|
||||
return this.input_blur(evt);
|
||||
}, this));
|
||||
this.search_field.keyup(__bind(function(evt) {
|
||||
return this.keyup_checker(evt);
|
||||
}, this));
|
||||
this.search_field.keydown(__bind(function(evt) {
|
||||
return this.keydown_checker(evt);
|
||||
}, this));
|
||||
if (this.is_multiple) {
|
||||
this.search_choices.click(__bind(function(evt) {
|
||||
return this.choices_click(evt);
|
||||
}, this));
|
||||
return this.search_field.focus(__bind(function(evt) {
|
||||
return this.input_focus(evt);
|
||||
}, this));
|
||||
} else {
|
||||
return this.selected_item.focus(__bind(function(evt) {
|
||||
return this.activate_field(evt);
|
||||
}, this));
|
||||
}
|
||||
};
|
||||
Chosen.prototype.container_click = function(evt) {
|
||||
if (evt && evt.type === "click") {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
if (!this.pending_destroy_click) {
|
||||
if (!this.active_field) {
|
||||
if (this.is_multiple) {
|
||||
this.search_field.val("");
|
||||
}
|
||||
$(document).click(this.click_test_action);
|
||||
this.results_show();
|
||||
} else if (!this.is_multiple && evt && ($(evt.target) === this.selected_item || $(evt.target).parents("a.chzn-single").length)) {
|
||||
evt.preventDefault();
|
||||
this.results_toggle();
|
||||
}
|
||||
return this.activate_field();
|
||||
} else {
|
||||
return this.pending_destroy_click = false;
|
||||
}
|
||||
};
|
||||
Chosen.prototype.mouse_enter = function() {
|
||||
return this.mouse_on_container = true;
|
||||
};
|
||||
Chosen.prototype.mouse_leave = function() {
|
||||
return this.mouse_on_container = false;
|
||||
};
|
||||
Chosen.prototype.input_focus = function(evt) {
|
||||
if (!this.active_field) {
|
||||
return setTimeout((__bind(function() {
|
||||
return this.container_click();
|
||||
}, this)), 50);
|
||||
}
|
||||
};
|
||||
Chosen.prototype.input_blur = function(evt) {
|
||||
if (!this.mouse_on_container) {
|
||||
this.active_field = false;
|
||||
return setTimeout((__bind(function() {
|
||||
return this.blur_test();
|
||||
}, this)), 100);
|
||||
}
|
||||
};
|
||||
Chosen.prototype.blur_test = function(evt) {
|
||||
if (!this.active_field && this.container.hasClass("chzn-container-active")) {
|
||||
return this.close_field();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.close_field = function() {
|
||||
$(document).unbind("click", this.click_test_action);
|
||||
if (!this.is_multiple) {
|
||||
this.selected_item.attr("tabindex", this.search_field.attr("tabindex"));
|
||||
this.search_field.attr("tabindex", -1);
|
||||
}
|
||||
this.active_field = false;
|
||||
this.results_hide();
|
||||
this.container.removeClass("chzn-container-active");
|
||||
this.winnow_results_clear();
|
||||
this.clear_backstroke();
|
||||
this.show_search_field_default();
|
||||
return this.search_field_scale();
|
||||
};
|
||||
Chosen.prototype.activate_field = function() {
|
||||
if (!this.is_multiple && !this.active_field) {
|
||||
this.search_field.attr("tabindex", this.selected_item.attr("tabindex"));
|
||||
this.selected_item.attr("tabindex", -1);
|
||||
}
|
||||
this.container.addClass("chzn-container-active");
|
||||
this.active_field = true;
|
||||
this.search_field.val(this.search_field.val());
|
||||
return this.search_field.focus();
|
||||
};
|
||||
Chosen.prototype.test_active_click = function(evt) {
|
||||
if ($(evt.target).parents('#' + this.container_id).length) {
|
||||
return this.active_field = true;
|
||||
} else {
|
||||
return this.close_field();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.results_build = function() {
|
||||
var content, data, startTime, _i, _len, _ref;
|
||||
startTime = new Date();
|
||||
this.parsing = true;
|
||||
this.results_data = root.SelectParser.select_to_array(this.form_field);
|
||||
if (this.is_multiple && this.choices > 0) {
|
||||
this.search_choices.find("li.search-choice").remove();
|
||||
this.choices = 0;
|
||||
} else if (!this.is_multiple) {
|
||||
this.selected_item.find("span").text(this.default_text);
|
||||
}
|
||||
content = '';
|
||||
_ref = this.results_data;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
data = _ref[_i];
|
||||
if (data.group) {
|
||||
content += this.result_add_group(data);
|
||||
} else if (!data.empty) {
|
||||
content += this.result_add_option(data);
|
||||
if (data.selected && this.is_multiple) {
|
||||
this.choice_build(data);
|
||||
} else if (data.selected && !this.is_multiple) {
|
||||
this.selected_item.find("span").text(data.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.show_search_field_default();
|
||||
this.search_field_scale();
|
||||
this.search_results.html(content);
|
||||
return this.parsing = false;
|
||||
};
|
||||
Chosen.prototype.result_add_group = function(group) {
|
||||
if (!group.disabled) {
|
||||
group.dom_id = this.container_id + "_g_" + group.array_index;
|
||||
return '<li id="' + group.dom_id + '" class="group-result">' + $("<div />").text(group.label).html() + '</li>';
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
Chosen.prototype.result_add_option = function(option) {
|
||||
var classes;
|
||||
if (!option.disabled) {
|
||||
option.dom_id = this.container_id + "_o_" + option.array_index;
|
||||
classes = option.selected && this.is_multiple ? [] : ["active-result"];
|
||||
if (option.selected) {
|
||||
classes.push("result-selected");
|
||||
}
|
||||
if (option.group_array_index != null) {
|
||||
classes.push("group-option");
|
||||
}
|
||||
return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '">' + option.html + '</li>';
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
Chosen.prototype.results_update_field = function() {
|
||||
this.result_clear_highlight();
|
||||
this.result_single_selected = null;
|
||||
return this.results_build();
|
||||
};
|
||||
Chosen.prototype.result_do_highlight = function(el) {
|
||||
var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
|
||||
if (el.length) {
|
||||
this.result_clear_highlight();
|
||||
this.result_highlight = el;
|
||||
this.result_highlight.addClass("highlighted");
|
||||
maxHeight = parseInt(this.search_results.css("maxHeight"), 10);
|
||||
visible_top = this.search_results.scrollTop();
|
||||
visible_bottom = maxHeight + visible_top;
|
||||
high_top = this.result_highlight.position().top + this.search_results.scrollTop();
|
||||
high_bottom = high_top + this.result_highlight.outerHeight();
|
||||
if (high_bottom >= visible_bottom) {
|
||||
return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
|
||||
} else if (high_top < visible_top) {
|
||||
return this.search_results.scrollTop(high_top);
|
||||
}
|
||||
}
|
||||
};
|
||||
Chosen.prototype.result_clear_highlight = function() {
|
||||
if (this.result_highlight) {
|
||||
this.result_highlight.removeClass("highlighted");
|
||||
}
|
||||
return this.result_highlight = null;
|
||||
};
|
||||
Chosen.prototype.results_toggle = function() {
|
||||
if (this.results_showing) {
|
||||
return this.results_hide();
|
||||
} else {
|
||||
return this.results_show();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.results_show = function() {
|
||||
var dd_top;
|
||||
if (!this.is_multiple) {
|
||||
this.selected_item.addClass("chzn-single-with-drop");
|
||||
if (this.result_single_selected) {
|
||||
this.result_do_highlight(this.result_single_selected);
|
||||
}
|
||||
}
|
||||
dd_top = this.is_multiple ? this.container.height() : this.container.height() - 1;
|
||||
this.dropdown.css({
|
||||
"top": dd_top + "px",
|
||||
"left": 0
|
||||
});
|
||||
this.results_showing = true;
|
||||
this.search_field.focus();
|
||||
this.search_field.val(this.search_field.val());
|
||||
return this.winnow_results();
|
||||
};
|
||||
Chosen.prototype.results_hide = function() {
|
||||
if (!this.is_multiple) {
|
||||
this.selected_item.removeClass("chzn-single-with-drop");
|
||||
}
|
||||
this.result_clear_highlight();
|
||||
this.dropdown.css({
|
||||
"left": "-9000px"
|
||||
});
|
||||
return this.results_showing = false;
|
||||
};
|
||||
Chosen.prototype.set_tab_index = function(el) {
|
||||
var ti;
|
||||
if (this.form_field_jq.attr("tabindex")) {
|
||||
ti = this.form_field_jq.attr("tabindex");
|
||||
this.form_field_jq.attr("tabindex", -1);
|
||||
if (this.is_multiple) {
|
||||
return this.search_field.attr("tabindex", ti);
|
||||
} else {
|
||||
this.selected_item.attr("tabindex", ti);
|
||||
return this.search_field.attr("tabindex", -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
Chosen.prototype.show_search_field_default = function() {
|
||||
if (this.is_multiple && this.choices < 1 && !this.active_field) {
|
||||
this.search_field.val(this.default_text);
|
||||
return this.search_field.addClass("default");
|
||||
} else {
|
||||
this.search_field.val("");
|
||||
return this.search_field.removeClass("default");
|
||||
}
|
||||
};
|
||||
Chosen.prototype.search_results_click = function(evt) {
|
||||
var target;
|
||||
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
|
||||
if (target.length) {
|
||||
this.result_highlight = target;
|
||||
return this.result_select();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.search_results_mouseover = function(evt) {
|
||||
var target;
|
||||
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
|
||||
if (target) {
|
||||
return this.result_do_highlight(target);
|
||||
}
|
||||
};
|
||||
Chosen.prototype.search_results_mouseout = function(evt) {
|
||||
if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) {
|
||||
return this.result_clear_highlight();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.choices_click = function(evt) {
|
||||
evt.preventDefault();
|
||||
if (this.active_field && !($(evt.target).hasClass("search-choice" || $(evt.target).parents('.search-choice').first)) && !this.results_showing) {
|
||||
return this.results_show();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.choice_build = function(item) {
|
||||
var choice_id, link;
|
||||
choice_id = this.container_id + "_c_" + item.array_index;
|
||||
this.choices += 1;
|
||||
this.search_container.before('<li class="search-choice" id="' + choice_id + '"><span>' + item.html + '</span><a href="javascript:void(0)" class="search-choice-close" rel="' + item.array_index + '"></a></li>');
|
||||
link = $('#' + choice_id).find("a").first();
|
||||
return link.click(__bind(function(evt) {
|
||||
return this.choice_destroy_link_click(evt);
|
||||
}, this));
|
||||
};
|
||||
Chosen.prototype.choice_destroy_link_click = function(evt) {
|
||||
evt.preventDefault();
|
||||
this.pending_destroy_click = true;
|
||||
return this.choice_destroy($(evt.target));
|
||||
};
|
||||
Chosen.prototype.choice_destroy = function(link) {
|
||||
this.choices -= 1;
|
||||
this.show_search_field_default();
|
||||
if (this.is_multiple && this.choices > 0 && this.search_field.val().length < 1) {
|
||||
this.results_hide();
|
||||
}
|
||||
this.result_deselect(link.attr("rel"));
|
||||
return link.parents('li').first().remove();
|
||||
};
|
||||
Chosen.prototype.result_select = function() {
|
||||
var high, high_id, item, position;
|
||||
if (this.result_highlight) {
|
||||
high = this.result_highlight;
|
||||
high_id = high.attr("id");
|
||||
this.result_clear_highlight();
|
||||
high.addClass("result-selected");
|
||||
if (this.is_multiple) {
|
||||
this.result_deactivate(high);
|
||||
} else {
|
||||
this.result_single_selected = high;
|
||||
}
|
||||
position = high_id.substr(high_id.lastIndexOf("_") + 1);
|
||||
item = this.results_data[position];
|
||||
item.selected = true;
|
||||
this.form_field.options[item.options_index].selected = true;
|
||||
if (this.is_multiple) {
|
||||
this.choice_build(item);
|
||||
} else {
|
||||
this.selected_item.find("span").first().text(item.text);
|
||||
}
|
||||
this.results_hide();
|
||||
this.search_field.val("");
|
||||
this.form_field_jq.trigger("change");
|
||||
return this.search_field_scale();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.result_activate = function(el) {
|
||||
return el.addClass("active-result").show();
|
||||
};
|
||||
Chosen.prototype.result_deactivate = function(el) {
|
||||
return el.removeClass("active-result").hide();
|
||||
};
|
||||
Chosen.prototype.result_deselect = function(pos) {
|
||||
var result, result_data;
|
||||
result_data = this.results_data[pos];
|
||||
result_data.selected = false;
|
||||
this.form_field.options[result_data.options_index].selected = false;
|
||||
result = $("#" + this.container_id + "_o_" + pos);
|
||||
result.removeClass("result-selected").addClass("active-result").show();
|
||||
this.result_clear_highlight();
|
||||
this.winnow_results();
|
||||
this.form_field_jq.trigger("change");
|
||||
return this.search_field_scale();
|
||||
};
|
||||
Chosen.prototype.results_search = function(evt) {
|
||||
if (this.results_showing) {
|
||||
return this.winnow_results();
|
||||
} else {
|
||||
return this.results_show();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.winnow_results = function() {
|
||||
var found, option, part, parts, regex, result_id, results, searchText, startTime, startpos, text, zregex, _i, _j, _len, _len2, _ref;
|
||||
startTime = new Date();
|
||||
this.no_results_clear();
|
||||
results = 0;
|
||||
searchText = this.search_field.val() === this.default_text ? "" : $('<div/>').text($.trim(this.search_field.val())).html();
|
||||
regex = new RegExp('^' + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
|
||||
zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
|
||||
_ref = this.results_data;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
option = _ref[_i];
|
||||
if (!option.disabled && !option.empty) {
|
||||
if (option.group) {
|
||||
$('#' + option.dom_id).hide();
|
||||
} else if (!(this.is_multiple && option.selected)) {
|
||||
found = false;
|
||||
result_id = option.dom_id;
|
||||
if (regex.test(option.html)) {
|
||||
found = true;
|
||||
results += 1;
|
||||
} else if (option.html.indexOf(" ") >= 0 || option.html.indexOf("[") === 0) {
|
||||
parts = option.html.replace(/\[|\]/g, "").split(" ");
|
||||
if (parts.length) {
|
||||
for (_j = 0, _len2 = parts.length; _j < _len2; _j++) {
|
||||
part = parts[_j];
|
||||
if (regex.test(part)) {
|
||||
found = true;
|
||||
results += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (searchText.length) {
|
||||
startpos = option.html.search(zregex);
|
||||
text = option.html.substr(0, startpos + searchText.length) + '</em>' + option.html.substr(startpos + searchText.length);
|
||||
text = text.substr(0, startpos) + '<em>' + text.substr(startpos);
|
||||
} else {
|
||||
text = option.html;
|
||||
}
|
||||
if ($("#" + result_id).html !== text) {
|
||||
$("#" + result_id).html(text);
|
||||
}
|
||||
this.result_activate($("#" + result_id));
|
||||
if (option.group_array_index != null) {
|
||||
$("#" + this.results_data[option.group_array_index].dom_id).show();
|
||||
}
|
||||
} else {
|
||||
if (this.result_highlight && result_id === this.result_highlight.attr('id')) {
|
||||
this.result_clear_highlight();
|
||||
}
|
||||
this.result_deactivate($("#" + result_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (results < 1 && searchText.length) {
|
||||
return this.no_results(searchText);
|
||||
} else {
|
||||
return this.winnow_results_set_highlight();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.winnow_results_clear = function() {
|
||||
var li, lis, _i, _len, _results;
|
||||
this.search_field.val("");
|
||||
lis = this.search_results.find("li");
|
||||
_results = [];
|
||||
for (_i = 0, _len = lis.length; _i < _len; _i++) {
|
||||
li = lis[_i];
|
||||
li = $(li);
|
||||
_results.push(li.hasClass("group-result") ? li.show() : !this.is_multiple || !li.hasClass("result-selected") ? this.result_activate(li) : void 0);
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
Chosen.prototype.winnow_results_set_highlight = function() {
|
||||
var do_high;
|
||||
if (!this.result_highlight) {
|
||||
do_high = this.search_results.find(".active-result").first();
|
||||
if (do_high) {
|
||||
return this.result_do_highlight(do_high);
|
||||
}
|
||||
}
|
||||
};
|
||||
Chosen.prototype.no_results = function(terms) {
|
||||
var no_results_html;
|
||||
no_results_html = $('<li class="no-results">No results match "<span></span>"</li>');
|
||||
no_results_html.find("span").first().html(terms);
|
||||
return this.search_results.append(no_results_html);
|
||||
};
|
||||
Chosen.prototype.no_results_clear = function() {
|
||||
return this.search_results.find(".no-results").remove();
|
||||
};
|
||||
Chosen.prototype.keydown_arrow = function() {
|
||||
var first_active, next_sib;
|
||||
if (!this.result_highlight) {
|
||||
first_active = this.search_results.find("li.active-result").first();
|
||||
if (first_active) {
|
||||
this.result_do_highlight($(first_active));
|
||||
}
|
||||
} else if (this.results_showing) {
|
||||
next_sib = this.result_highlight.nextAll("li.active-result").first();
|
||||
if (next_sib) {
|
||||
this.result_do_highlight(next_sib);
|
||||
}
|
||||
}
|
||||
if (!this.results_showing) {
|
||||
return this.results_show();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.keyup_arrow = function() {
|
||||
var prev_sibs;
|
||||
if (!this.results_showing && !this.is_multiple) {
|
||||
return this.results_show();
|
||||
} else if (this.result_highlight) {
|
||||
prev_sibs = this.result_highlight.prevAll("li.active-result");
|
||||
if (prev_sibs.length) {
|
||||
return this.result_do_highlight(prev_sibs.first());
|
||||
} else {
|
||||
if (this.choices > 0) {
|
||||
this.results_hide();
|
||||
}
|
||||
return this.result_clear_highlight();
|
||||
}
|
||||
}
|
||||
};
|
||||
Chosen.prototype.keydown_backstroke = function() {
|
||||
if (this.pending_backstroke) {
|
||||
this.choice_destroy(this.pending_backstroke.find("a").first());
|
||||
return this.clear_backstroke();
|
||||
} else {
|
||||
this.pending_backstroke = this.search_container.siblings("li.search-choice").last();
|
||||
return this.pending_backstroke.addClass("search-choice-focus");
|
||||
}
|
||||
};
|
||||
Chosen.prototype.clear_backstroke = function() {
|
||||
if (this.pending_backstroke) {
|
||||
this.pending_backstroke.removeClass("search-choice-focus");
|
||||
}
|
||||
return this.pending_backstroke = null;
|
||||
};
|
||||
Chosen.prototype.keyup_checker = function(evt) {
|
||||
var stroke, _ref;
|
||||
stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
|
||||
this.search_field_scale();
|
||||
switch (stroke) {
|
||||
case 8:
|
||||
if (this.is_multiple && this.backstroke_length < 1 && this.choices > 0) {
|
||||
return this.keydown_backstroke();
|
||||
} else if (!this.pending_backstroke) {
|
||||
this.result_clear_highlight();
|
||||
return this.results_search();
|
||||
}
|
||||
break;
|
||||
case 13:
|
||||
evt.preventDefault();
|
||||
if (this.results_showing) {
|
||||
return this.result_select();
|
||||
}
|
||||
break;
|
||||
case 27:
|
||||
if (this.results_showing) {
|
||||
return this.results_hide();
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
case 38:
|
||||
case 40:
|
||||
case 16:
|
||||
break;
|
||||
default:
|
||||
return this.results_search();
|
||||
}
|
||||
};
|
||||
Chosen.prototype.keydown_checker = function(evt) {
|
||||
var stroke, _ref;
|
||||
stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
|
||||
this.search_field_scale();
|
||||
if (stroke !== 8 && this.pending_backstroke) {
|
||||
this.clear_backstroke();
|
||||
}
|
||||
switch (stroke) {
|
||||
case 8:
|
||||
this.backstroke_length = this.search_field.val().length;
|
||||
break;
|
||||
case 9:
|
||||
this.mouse_on_container = false;
|
||||
break;
|
||||
case 13:
|
||||
evt.preventDefault();
|
||||
break;
|
||||
case 38:
|
||||
evt.preventDefault();
|
||||
this.keyup_arrow();
|
||||
break;
|
||||
case 40:
|
||||
this.keydown_arrow();
|
||||
break;
|
||||
}
|
||||
};
|
||||
Chosen.prototype.search_field_scale = function() {
|
||||
var dd_top, div, h, style, style_block, styles, w, _i, _len;
|
||||
if (this.is_multiple) {
|
||||
h = 0;
|
||||
w = 0;
|
||||
style_block = "position:absolute; left: -1000px; top: -1000px; display:none;";
|
||||
styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'];
|
||||
for (_i = 0, _len = styles.length; _i < _len; _i++) {
|
||||
style = styles[_i];
|
||||
style_block += style + ":" + this.search_field.css(style) + ";";
|
||||
}
|
||||
div = $('<div />', {
|
||||
'style': style_block
|
||||
});
|
||||
div.text(this.search_field.val());
|
||||
$('body').append(div);
|
||||
w = div.width() + 25;
|
||||
div.remove();
|
||||
if (w > this.f_width - 10) {
|
||||
w = this.f_width - 10;
|
||||
}
|
||||
this.search_field.css({
|
||||
'width': w + 'px'
|
||||
});
|
||||
dd_top = this.container.height();
|
||||
return this.dropdown.css({
|
||||
"top": dd_top + "px"
|
||||
});
|
||||
}
|
||||
};
|
||||
Chosen.prototype.generate_field_id = function() {
|
||||
var new_id;
|
||||
new_id = this.generate_random_id();
|
||||
this.form_field.id = new_id;
|
||||
return new_id;
|
||||
};
|
||||
Chosen.prototype.generate_random_id = function() {
|
||||
var string;
|
||||
string = "sel" + this.generate_random_char() + this.generate_random_char() + this.generate_random_char();
|
||||
while ($("#" + string).length > 0) {
|
||||
string += this.generate_random_char();
|
||||
}
|
||||
return string;
|
||||
};
|
||||
Chosen.prototype.generate_random_char = function() {
|
||||
var chars, newchar, rand;
|
||||
chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ";
|
||||
rand = Math.floor(Math.random() * chars.length);
|
||||
return newchar = chars.substring(rand, rand + 1);
|
||||
};
|
||||
return Chosen;
|
||||
})();
|
||||
get_side_border_padding = function(elmt) {
|
||||
var side_border_padding;
|
||||
return side_border_padding = elmt.outerWidth() - elmt.width();
|
||||
};
|
||||
root.get_side_border_padding = get_side_border_padding;
|
||||
}).call(this);
|
||||
(function() {
|
||||
var SelectParser;
|
||||
SelectParser = (function() {
|
||||
function SelectParser() {
|
||||
this.options_index = 0;
|
||||
this.parsed = [];
|
||||
}
|
||||
SelectParser.prototype.add_node = function(child) {
|
||||
if (child.nodeName === "OPTGROUP") {
|
||||
return this.add_group(child);
|
||||
} else {
|
||||
return this.add_option(child);
|
||||
}
|
||||
};
|
||||
SelectParser.prototype.add_group = function(group) {
|
||||
var group_position, option, _i, _len, _ref, _results;
|
||||
group_position = this.parsed.length;
|
||||
this.parsed.push({
|
||||
array_index: group_position,
|
||||
group: true,
|
||||
label: group.label,
|
||||
children: 0,
|
||||
disabled: group.disabled
|
||||
});
|
||||
_ref = group.childNodes;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
option = _ref[_i];
|
||||
_results.push(this.add_option(option, group_position, group.disabled));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
|
||||
if (option.nodeName === "OPTION") {
|
||||
if (option.text !== "") {
|
||||
if (group_position != null) {
|
||||
this.parsed[group_position].children += 1;
|
||||
}
|
||||
this.parsed.push({
|
||||
array_index: this.parsed.length,
|
||||
options_index: this.options_index,
|
||||
value: option.value,
|
||||
text: option.text,
|
||||
html: option.innerHTML,
|
||||
selected: option.selected,
|
||||
disabled: group_disabled === true ? group_disabled : option.disabled,
|
||||
group_array_index: group_position
|
||||
});
|
||||
} else {
|
||||
this.parsed.push({
|
||||
array_index: this.parsed.length,
|
||||
options_index: this.options_index,
|
||||
empty: true
|
||||
});
|
||||
}
|
||||
return this.options_index += 1;
|
||||
}
|
||||
};
|
||||
return SelectParser;
|
||||
})();
|
||||
SelectParser.select_to_array = function(select) {
|
||||
var child, parser, _i, _len, _ref;
|
||||
parser = new SelectParser();
|
||||
_ref = select.childNodes;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
child = _ref[_i];
|
||||
parser.add_node(child);
|
||||
}
|
||||
return parser.parsed;
|
||||
};
|
||||
this.SelectParser = SelectParser;
|
||||
}).call(this);
|
@ -4,7 +4,6 @@ django-nose==0.1.2
|
||||
django-mailer
|
||||
django-registration==0.7
|
||||
kombu
|
||||
nova-adminclient
|
||||
python-cloudfiles
|
||||
python-dateutil
|
||||
routes
|
||||
@ -22,6 +21,6 @@ coverage
|
||||
bzr+https://launchpad.net/glance#egg=glance
|
||||
bzr+https://launchpad.net/quantum#egg=quantum
|
||||
|
||||
-e git://github.com/jacobian/openstack.compute.git#egg=openstack
|
||||
-e git://github.com/cloudbuilders/openstackx.git#egg=openstackx
|
||||
-e git+https://github.com/jacobian/openstack.compute.git#egg=openstack
|
||||
-e git+https://github.com/cloudbuilders/openstackx.git#egg=openstackx
|
||||
-e git://github.com/rackspace/python-novaclient.git#egg=python-novaclient
|
||||
|
Loading…
Reference in New Issue
Block a user