Inline object creation.
Allows the creation of related objects during a workflow. For example, this patch implements importing keypairs during the launch instance workflow and allocating floating IP addresses during the floating IP associate workflow. This required several significant changes: * SelfHandlingForm should no long return a redirect. Instead, it should return either the object it created/acted on, or else a boolean such as True. * The ModalFormView now differentiates between GET and POST. * Due to the previous two items, SelfHandlingForm was mostly gutted (no more maybe_handle, etc.). * Modals now operate via a "stack" where only the top modal is visible at any given time and closing one causes the next one to become visible. In the process of these large changes there was a large amount of general code cleanup, especially in the javascript code and the existing SelfHandlingForm subclasses/ModalFormView subclasses. Many small bugs were fixed along with the cleanup. Implements blueprint inline-object-creation. Fixes bug 994677. Fixes bug 1025977. Fixes bug 1027342. Fixes bug 1025919. Change-Id: I1808b34cbf6f813eaedf767a6364e815c0c5e969
This commit is contained in:
parent
76246c6b18
commit
df5a13c5ec
@ -364,7 +364,12 @@ def server_reboot(request, instance_id, hardness=REBOOT_HARD):
|
|||||||
|
|
||||||
|
|
||||||
def server_update(request, instance_id, name):
|
def server_update(request, instance_id, name):
|
||||||
return novaclient(request).servers.update(instance_id, name=name)
|
response = novaclient(request).servers.update(instance_id, name=name)
|
||||||
|
# TODO(gabriel): servers.update method doesn't return anything. :-(
|
||||||
|
if response is None:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def server_add_floating_ip(request, server, floating_ip):
|
def server_add_floating_ip(request, server, floating_ip):
|
||||||
|
@ -19,22 +19,15 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from django.contrib import messages
|
|
||||||
from django import shortcuts
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class FloatingIpAllocate(forms.SelfHandlingForm):
|
class FloatingIpAllocate(forms.SelfHandlingForm):
|
||||||
tenant_name = forms.CharField(widget=forms.HiddenInput())
|
|
||||||
pool = forms.ChoiceField(label=_("Pool"))
|
pool = forms.ChoiceField(label=_("Pool"))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -44,16 +37,10 @@ class FloatingIpAllocate(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
fip = api.tenant_floating_ip_allocate(request,
|
fip = api.tenant_floating_ip_allocate(request, pool=data['pool'])
|
||||||
pool=data.get('pool', None))
|
|
||||||
LOG.info('Allocating Floating IP "%s" to project "%s"'
|
|
||||||
% (fip.ip, data['tenant_name']))
|
|
||||||
|
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('Successfully allocated Floating IP "%(ip)s" '
|
_('Allocated Floating IP %(ip)s.')
|
||||||
'to project "%(project)s"')
|
% {"ip": fip.ip})
|
||||||
% {"ip": fip.ip, "project": data['tenant_name']})
|
return fip
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to allocate Floating IP.'))
|
exceptions.handle(request, _('Unable to allocate Floating IP.'))
|
||||||
return shortcuts.redirect(
|
|
||||||
'horizon:nova:access_and_security:index')
|
|
||||||
|
@ -18,13 +18,13 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
from django.contrib import messages
|
|
||||||
from django.core import urlresolvers
|
from django.core import urlresolvers
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
Views for managing Nova floating IPs.
|
Views for managing Nova floating IPs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
@ -41,7 +42,10 @@ class AssociateView(workflows.WorkflowView):
|
|||||||
class AllocateView(forms.ModalFormView):
|
class AllocateView(forms.ModalFormView):
|
||||||
form_class = FloatingIpAllocate
|
form_class = FloatingIpAllocate
|
||||||
template_name = 'nova/access_and_security/floating_ips/allocate.html'
|
template_name = 'nova/access_and_security/floating_ips/allocate.html'
|
||||||
context_object_name = 'floating_ip'
|
success_url = reverse_lazy('horizon:nova:access_and_security:index')
|
||||||
|
|
||||||
|
def get_object_display(self, obj):
|
||||||
|
return obj.ip
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(AllocateView, self).get_context_data(**kwargs)
|
context = super(AllocateView, self).get_context_data(**kwargs)
|
||||||
@ -52,11 +56,13 @@ class AllocateView(forms.ModalFormView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
pools = api.floating_ip_pools_list(self.request)
|
try:
|
||||||
if pools:
|
pools = api.floating_ip_pools_list(self.request)
|
||||||
pool_list = [(pool.name, pool.name)
|
except:
|
||||||
for pool in api.floating_ip_pools_list(self.request)]
|
pools = []
|
||||||
else:
|
exceptions.handle(self.request,
|
||||||
|
_("Unable to retrieve floating IP pools."))
|
||||||
|
pool_list = [(pool.name, pool.name) for pool in pools]
|
||||||
|
if not pool_list:
|
||||||
pool_list = [(None, _("No floating IP pools available."))]
|
pool_list = [(None, _("No floating IP pools available."))]
|
||||||
return {'tenant_name': self.request.user.tenant_name,
|
return {'pool_list': pool_list}
|
||||||
'pool_list': pool_list}
|
|
||||||
|
@ -15,19 +15,23 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import workflows
|
from horizon import workflows
|
||||||
|
from horizon import forms
|
||||||
|
|
||||||
|
|
||||||
|
ALLOCATE_URL = "horizon:nova:access_and_security:floating_ips:allocate"
|
||||||
|
|
||||||
|
|
||||||
class AssociateIPAction(workflows.Action):
|
class AssociateIPAction(workflows.Action):
|
||||||
ip_id = forms.TypedChoiceField(label=_("IP Address"),
|
ip_id = forms.DynamicTypedChoiceField(label=_("IP Address"),
|
||||||
coerce=int,
|
coerce=int,
|
||||||
empty_value=None)
|
empty_value=None,
|
||||||
|
add_item_link=ALLOCATE_URL)
|
||||||
instance_id = forms.ChoiceField(label=_("Instance"))
|
instance_id = forms.ChoiceField(label=_("Instance"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -18,20 +18,18 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import logging
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
from django.contrib import messages
|
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
NEW_LINES = re.compile(r"\r|\n")
|
NEW_LINES = re.compile(r"\r|\n")
|
||||||
|
|
||||||
|
|
||||||
@ -44,14 +42,7 @@ class CreateKeypair(forms.SelfHandlingForm):
|
|||||||
'and hyphens.')})
|
'and hyphens.')})
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
return True # We just redirect to the download view.
|
||||||
return shortcuts.redirect(
|
|
||||||
'horizon:nova:access_and_security:keypairs:download',
|
|
||||||
keypair_name=data['name'])
|
|
||||||
except:
|
|
||||||
exceptions.handle(request,
|
|
||||||
_('Unable to create keypair.'))
|
|
||||||
return shortcuts.redirect(request.build_absolute_uri())
|
|
||||||
|
|
||||||
|
|
||||||
class ImportKeypair(forms.SelfHandlingForm):
|
class ImportKeypair(forms.SelfHandlingForm):
|
||||||
@ -61,14 +52,14 @@ class ImportKeypair(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
LOG.info('Importing keypair "%s"' % data['name'])
|
|
||||||
# Remove any new lines in the public key
|
# Remove any new lines in the public key
|
||||||
data['public_key'] = NEW_LINES.sub("", data['public_key'])
|
data['public_key'] = NEW_LINES.sub("", data['public_key'])
|
||||||
api.keypair_import(request, data['name'], data['public_key'])
|
keypair = api.keypair_import(request,
|
||||||
|
data['name'],
|
||||||
|
data['public_key'])
|
||||||
messages.success(request, _('Successfully imported public key: %s')
|
messages.success(request, _('Successfully imported public key: %s')
|
||||||
% data['name'])
|
% data['name'])
|
||||||
return shortcuts.redirect(
|
return keypair
|
||||||
'horizon:nova:access_and_security:index')
|
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_('Unable to import keypair.'))
|
_('Unable to import keypair.'))
|
||||||
|
@ -124,8 +124,8 @@ class KeyPairViewTests(test.TestCase):
|
|||||||
'name': key1_name,
|
'name': key1_name,
|
||||||
'public_key': public_key}
|
'public_key': public_key}
|
||||||
url = reverse('horizon:nova:access_and_security:keypairs:import')
|
url = reverse('horizon:nova:access_and_security:keypairs:import')
|
||||||
self.client.post(url, formData)
|
res = self.client.post(url, formData)
|
||||||
self.assertMessageCount(success=1)
|
self.assertMessageCount(res, success=1)
|
||||||
|
|
||||||
def test_generate_keypair_exception(self):
|
def test_generate_keypair_exception(self):
|
||||||
keypair = self.keypairs.first()
|
keypair = self.keypairs.first()
|
||||||
|
@ -24,7 +24,7 @@ Views for managing Nova keypairs.
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
from django.views.generic import View, TemplateView
|
from django.views.generic import View, TemplateView
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
@ -41,11 +41,20 @@ LOG = logging.getLogger(__name__)
|
|||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = CreateKeypair
|
form_class = CreateKeypair
|
||||||
template_name = 'nova/access_and_security/keypairs/create.html'
|
template_name = 'nova/access_and_security/keypairs/create.html'
|
||||||
|
success_url = 'horizon:nova:access_and_security:keypairs:download'
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse(self.success_url,
|
||||||
|
kwargs={"keypair_name": self.request.POST['name']})
|
||||||
|
|
||||||
|
|
||||||
class ImportView(forms.ModalFormView):
|
class ImportView(forms.ModalFormView):
|
||||||
form_class = ImportKeypair
|
form_class = ImportKeypair
|
||||||
template_name = 'nova/access_and_security/keypairs/import.html'
|
template_name = 'nova/access_and_security/keypairs/import.html'
|
||||||
|
success_url = reverse_lazy('horizon:nova:access_and_security:index')
|
||||||
|
|
||||||
|
def get_object_id(self, keypair):
|
||||||
|
return keypair.name
|
||||||
|
|
||||||
|
|
||||||
class DownloadView(TemplateView):
|
class DownloadView(TemplateView):
|
||||||
|
@ -18,24 +18,19 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from django import shortcuts
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
from horizon.utils.validators import validate_port_range
|
from horizon.utils.validators import validate_port_range
|
||||||
from horizon.utils import fields
|
from horizon.utils import fields
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateGroup(forms.SelfHandlingForm):
|
class CreateGroup(forms.SelfHandlingForm):
|
||||||
name = forms.CharField(label=_("Name"),
|
name = forms.CharField(label=_("Name"),
|
||||||
validators=[validators.validate_slug])
|
validators=[validators.validate_slug])
|
||||||
@ -43,15 +38,18 @@ class CreateGroup(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
api.security_group_create(request,
|
sg = api.security_group_create(request,
|
||||||
data['name'],
|
data['name'],
|
||||||
data['description'])
|
data['description'])
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('Successfully created security group: %s')
|
_('Successfully created security group: %s')
|
||||||
% data['name'])
|
% data['name'])
|
||||||
|
return sg
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to create security group.'))
|
redirect = reverse("horizon:nova:access_and_security:index")
|
||||||
return shortcuts.redirect('horizon:nova:access_and_security:index')
|
exceptions.handle(request,
|
||||||
|
_('Unable to create security group.'),
|
||||||
|
redirect=redirect)
|
||||||
|
|
||||||
|
|
||||||
class AddRule(forms.SelfHandlingForm):
|
class AddRule(forms.SelfHandlingForm):
|
||||||
@ -59,6 +57,8 @@ class AddRule(forms.SelfHandlingForm):
|
|||||||
choices=[('tcp', 'TCP'),
|
choices=[('tcp', 'TCP'),
|
||||||
('udp', 'UDP'),
|
('udp', 'UDP'),
|
||||||
('icmp', 'ICMP')],
|
('icmp', 'ICMP')],
|
||||||
|
help_text=_("The protocol which this "
|
||||||
|
"rule should be applied to."),
|
||||||
widget=forms.Select(attrs={'class':
|
widget=forms.Select(attrs={'class':
|
||||||
'switchable'}))
|
'switchable'}))
|
||||||
from_port = forms.IntegerField(label=_("From Port"),
|
from_port = forms.IntegerField(label=_("From Port"),
|
||||||
@ -80,7 +80,13 @@ class AddRule(forms.SelfHandlingForm):
|
|||||||
'data-icmp': _('Code')}),
|
'data-icmp': _('Code')}),
|
||||||
validators=[validate_port_range])
|
validators=[validate_port_range])
|
||||||
|
|
||||||
source_group = forms.ChoiceField(label=_('Source Group'), required=False)
|
source_group = forms.ChoiceField(label=_('Source Group'),
|
||||||
|
required=False,
|
||||||
|
help_text=_("To specify an allowed IP "
|
||||||
|
"range, select CIDR. To "
|
||||||
|
"allow access from all "
|
||||||
|
"members of another security "
|
||||||
|
"group select Source Group."))
|
||||||
cidr = fields.IPField(label=_("CIDR"),
|
cidr = fields.IPField(label=_("CIDR"),
|
||||||
required=False,
|
required=False,
|
||||||
initial="0.0.0.0/0",
|
initial="0.0.0.0/0",
|
||||||
@ -92,14 +98,13 @@ class AddRule(forms.SelfHandlingForm):
|
|||||||
security_group_id = forms.IntegerField(widget=forms.HiddenInput())
|
security_group_id = forms.IntegerField(widget=forms.HiddenInput())
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
sg_list = kwargs.pop('sg_list', [])
|
||||||
super(AddRule, self).__init__(*args, **kwargs)
|
super(AddRule, self).__init__(*args, **kwargs)
|
||||||
initials = kwargs.get("initial", {})
|
# Determine if there are security groups available for the
|
||||||
current_group_id = initials.get('security_group_id', 0)
|
# source group option; add the choices and enable the option if so.
|
||||||
security_groups = initials.get('security_group_list', [])
|
security_groups_choices = [("", "CIDR")]
|
||||||
security_groups_choices = [("", "CIDR")] # default choice is CIDR
|
if sg_list:
|
||||||
group_choices = [s for s in security_groups]
|
security_groups_choices.append(('Security Group', sg_list))
|
||||||
if len(group_choices): # add group choice if available
|
|
||||||
security_groups_choices.append(('Security Group', group_choices))
|
|
||||||
self.fields['source_group'].choices = security_groups_choices
|
self.fields['source_group'].choices = security_groups_choices
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
@ -160,7 +165,9 @@ class AddRule(forms.SelfHandlingForm):
|
|||||||
data['source_group'])
|
data['source_group'])
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('Successfully added rule: %s') % unicode(rule))
|
_('Successfully added rule: %s') % unicode(rule))
|
||||||
|
return rule
|
||||||
except:
|
except:
|
||||||
|
redirect = reverse("horizon:nova:access_and_security:index")
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_('Unable to add rule to security group.'))
|
_('Unable to add rule to security group.'),
|
||||||
return shortcuts.redirect("horizon:nova:access_and_security:index")
|
redirect=redirect)
|
||||||
|
@ -100,30 +100,16 @@ class SecurityGroupsViewTests(test.TestCase):
|
|||||||
|
|
||||||
def test_edit_rules_get_exception(self):
|
def test_edit_rules_get_exception(self):
|
||||||
sec_group = self.security_groups.first()
|
sec_group = self.security_groups.first()
|
||||||
sec_group_list = self.security_groups.list()
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'security_group_get')
|
self.mox.StubOutWithMock(api, 'security_group_get')
|
||||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||||
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'server_list')
|
|
||||||
|
|
||||||
api.nova.server_list(IsA(http.HttpRequest),
|
|
||||||
all_tenants=True).AndReturn(self.servers.list())
|
|
||||||
api.nova.keypair_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.keypairs.list())
|
|
||||||
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
|
||||||
.AndReturn(self.floating_ips.list())
|
|
||||||
api.security_group_get(IsA(http.HttpRequest),
|
api.security_group_get(IsA(http.HttpRequest),
|
||||||
sec_group.id).AndRaise(self.exceptions.nova)
|
sec_group.id).AndRaise(self.exceptions.nova)
|
||||||
api.security_group_list(
|
|
||||||
IsA(http.HttpRequest)).AndReturn(sec_group_list)
|
|
||||||
api.security_group_list(
|
|
||||||
IsA(http.HttpRequest)).AndReturn(sec_group_list)
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(self.edit_url)
|
res = self.client.get(self.edit_url)
|
||||||
self.assertRedirects(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_edit_rules_add_rule_cidr(self):
|
def test_edit_rules_add_rule_cidr(self):
|
||||||
sec_group = self.security_groups.first()
|
sec_group = self.security_groups.first()
|
||||||
|
@ -24,6 +24,7 @@ Views for managing Nova instances.
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
@ -37,9 +38,11 @@ from .tables import RulesTable
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class EditRulesView(tables.DataTableView):
|
class EditRulesView(tables.DataTableView, forms.ModalFormView):
|
||||||
table_class = RulesTable
|
table_class = RulesTable
|
||||||
|
form_class = AddRule
|
||||||
template_name = 'nova/access_and_security/security_groups/edit_rules.html'
|
template_name = 'nova/access_and_security/security_groups/edit_rules.html'
|
||||||
|
success_url = reverse_lazy("horizon:nova:access_and_security:index")
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
security_group_id = int(self.kwargs['security_group_id'])
|
security_group_id = int(self.kwargs['security_group_id'])
|
||||||
@ -55,7 +58,12 @@ class EditRulesView(tables.DataTableView):
|
|||||||
_('Unable to retrieve security group.'))
|
_('Unable to retrieve security group.'))
|
||||||
return rules
|
return rules
|
||||||
|
|
||||||
def handle_form(self):
|
def get_initial(self):
|
||||||
|
return {'security_group_id': self.kwargs['security_group_id']}
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
kwargs = super(EditRulesView, self).get_form_kwargs()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
groups = api.security_group_list(self.request)
|
groups = api.security_group_list(self.request)
|
||||||
except:
|
except:
|
||||||
@ -63,36 +71,49 @@ class EditRulesView(tables.DataTableView):
|
|||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_("Unable to retrieve security groups."))
|
_("Unable to retrieve security groups."))
|
||||||
|
|
||||||
security_groups = [(group.id, group.name) for group in groups]
|
security_groups = []
|
||||||
|
for group in groups:
|
||||||
|
if group.id == int(self.kwargs['security_group_id']):
|
||||||
|
security_groups.append((group.id,
|
||||||
|
_("%s (current)") % group.name))
|
||||||
|
else:
|
||||||
|
security_groups.append((group.id, group.name))
|
||||||
|
kwargs['sg_list'] = security_groups
|
||||||
|
return kwargs
|
||||||
|
|
||||||
initial = {'security_group_id': self.kwargs['security_group_id'],
|
def get_form(self):
|
||||||
'security_group_list': security_groups}
|
if not hasattr(self, "_form"):
|
||||||
return AddRule.maybe_handle(self.request, initial=initial)
|
form_class = self.get_form_class()
|
||||||
|
self._form = super(EditRulesView, self).get_form(form_class)
|
||||||
|
return self._form
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(EditRulesView, self).get_context_data(**kwargs)
|
||||||
|
context['form'] = self.get_form()
|
||||||
|
if self.request.is_ajax():
|
||||||
|
context['hide'] = True
|
||||||
|
return context
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
# Form handling
|
|
||||||
form, handled = self.handle_form()
|
|
||||||
if handled:
|
|
||||||
return handled
|
|
||||||
# Table action handling
|
# Table action handling
|
||||||
handled = self.construct_tables()
|
handled = self.construct_tables()
|
||||||
if handled:
|
if handled:
|
||||||
return handled
|
return handled
|
||||||
if not self.object:
|
if not self.object: # Set during table construction.
|
||||||
return shortcuts.redirect("horizon:nova:access_and_security:index")
|
return shortcuts.redirect(self.success_url)
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
context['form'] = form
|
|
||||||
context['security_group'] = self.object
|
context['security_group'] = self.object
|
||||||
if request.is_ajax():
|
|
||||||
context['hide'] = True
|
|
||||||
self.template_name = ('nova/access_and_security/security_groups'
|
|
||||||
'/_edit_rules.html')
|
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
form = self.get_form()
|
||||||
|
if form.is_valid():
|
||||||
|
return self.form_valid(form)
|
||||||
|
else:
|
||||||
|
return self.get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = CreateGroup
|
form_class = CreateGroup
|
||||||
template_name = 'nova/access_and_security/security_groups/create.html'
|
template_name = 'nova/access_and_security/security_groups/create.html'
|
||||||
|
success_url = reverse_lazy('horizon:nova:access_and_security:index')
|
||||||
def get_initial(self):
|
|
||||||
return {"tenant_id": self.request.user.tenant_id}
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Allocate Floating IP" %}{% endblock %}
|
{% block title %}{% trans "Allocate Floating IP" %}{% endblock %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/access_and_security/floating_ips/_allocate.html' %}
|
{% include 'nova/access_and_security/floating_ips/_allocate.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Associate Floating IP" %}{% endblock %}
|
{% block title %}{% trans "Associate Floating IP" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Associate Floating IP") %}
|
{% include "horizon/common/_page_header.html" with title=_("Associate Floating IP") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'horizon/common/_workflow.html' %}
|
{% include 'horizon/common/_workflow.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Access & Security{% endblock %}
|
{% block title %}Access & Security{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Access & Security") %}
|
{% include "horizon/common/_page_header.html" with title=_("Access & Security") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
<div id="floating_ips">
|
<div id="floating_ips">
|
||||||
{{ floating_ips_table.render }}
|
{{ floating_ips_table.render }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Create Keypair{% endblock %}
|
{% block title %}Create Keypair{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create Keypair") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create Keypair") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/access_and_security/keypairs/_create.html' %}
|
{% include 'nova/access_and_security/keypairs/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% blocktrans %}Download Keypair{% endblocktrans %}{% endblock %}
|
{% block title %}{% blocktrans %}Download Keypair{% endblocktrans %}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Download Keypair") %}
|
{% include "horizon/common/_page_header.html" with title=_("Download Keypair") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3>{% blocktrans %}The keypair "{{ keypair_name }}" should download automatically. If not use the link below.{% endblocktrans %}</h3>
|
<h3>{% blocktrans %}The keypair "{{ keypair_name }}" should download automatically. If not use the link below.{% endblocktrans %}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Import Keypair{% endblock %}
|
{% block title %}Import Keypair{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Import Keypair") %}
|
{% include "horizon/common/_page_header.html" with title=_("Import Keypair") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/access_and_security/keypairs/_import.html' %}
|
{% include 'nova/access_and_security/keypairs/_import.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Create Security Group{% endblock %}
|
{% block title %}Create Security Group{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create Security Group") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create Security Group") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/access_and_security/security_groups/_create.html' %}
|
{% include 'nova/access_and_security/security_groups/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Edit Security Group Rules{% endblock %}
|
{% block title %}Edit Security Group Rules{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Edit Security Group Rules") %}
|
{% include "horizon/common/_page_header.html" with title=_("Edit Security Group Rules") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include "nova/access_and_security/security_groups/_edit_rules.html" %}
|
{% include "nova/access_and_security/security_groups/_edit_rules.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -24,7 +24,6 @@ Views for Instances and Volumes.
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
@ -29,6 +27,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -65,17 +64,10 @@ class CreateContainer(forms.SelfHandlingForm):
|
|||||||
container,
|
container,
|
||||||
subfolder_name)
|
subfolder_name)
|
||||||
messages.success(request, _("Folder created successfully."))
|
messages.success(request, _("Folder created successfully."))
|
||||||
url = "horizon:nova:containers:object_index"
|
return True
|
||||||
if remainder:
|
|
||||||
remainder = remainder.rstrip("/")
|
|
||||||
remainder += "/"
|
|
||||||
return shortcuts.redirect(url, container, remainder)
|
|
||||||
|
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to create container.'))
|
exceptions.handle(request, _('Unable to create container.'))
|
||||||
|
|
||||||
return shortcuts.redirect("horizon:nova:containers:index")
|
|
||||||
|
|
||||||
|
|
||||||
class UploadObject(forms.SelfHandlingForm):
|
class UploadObject(forms.SelfHandlingForm):
|
||||||
path = forms.CharField(max_length=255,
|
path = forms.CharField(max_length=255,
|
||||||
@ -101,10 +93,9 @@ class UploadObject(forms.SelfHandlingForm):
|
|||||||
obj.metadata['orig-filename'] = object_file.name
|
obj.metadata['orig-filename'] = object_file.name
|
||||||
obj.sync_metadata()
|
obj.sync_metadata()
|
||||||
messages.success(request, _("Object was successfully uploaded."))
|
messages.success(request, _("Object was successfully uploaded."))
|
||||||
|
return obj
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _("Unable to upload object."))
|
exceptions.handle(request, _("Unable to upload object."))
|
||||||
return shortcuts.redirect("horizon:nova:containers:object_index",
|
|
||||||
data['container_name'], data['path'])
|
|
||||||
|
|
||||||
|
|
||||||
class CopyObject(forms.SelfHandlingForm):
|
class CopyObject(forms.SelfHandlingForm):
|
||||||
@ -160,12 +151,13 @@ class CopyObject(forms.SelfHandlingForm):
|
|||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('Copied "%(orig)s" to "%(dest)s" as "%(new)s".')
|
_('Copied "%(orig)s" to "%(dest)s" as "%(new)s".')
|
||||||
% vals)
|
% vals)
|
||||||
|
return True
|
||||||
except exceptions.HorizonException, exc:
|
except exceptions.HorizonException, exc:
|
||||||
messages.error(request, exc)
|
messages.error(request, exc)
|
||||||
return shortcuts.redirect(object_index, orig_container)
|
raise exceptions.Http302(reverse(object_index,
|
||||||
|
args=[orig_container]))
|
||||||
except:
|
except:
|
||||||
redirect = reverse(object_index, args=(orig_container,))
|
redirect = reverse(object_index, args=(orig_container,))
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_("Unable to copy object."),
|
_("Unable to copy object."),
|
||||||
redirect=redirect)
|
redirect=redirect)
|
||||||
return shortcuts.redirect(object_index, new_container, data['path'])
|
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from cloudfiles.errors import ContainerNotEmpty
|
from cloudfiles.errors import ContainerNotEmpty
|
||||||
from django.contrib import messages
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.template.defaultfilters import filesizeformat
|
from django.template.defaultfilters import filesizeformat
|
||||||
from django.utils import http
|
from django.utils import http
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Copy Object" %}{% endblock %}
|
{% block title %}{% trans "Copy Object" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Copy Object") %}
|
{% include "horizon/common/_page_header.html" with title=_("Copy Object") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/containers/_copy.html' %}
|
{% include 'nova/containers/_copy.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Create Container{% endblock %}
|
{% block title %}Create Container{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create Container") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create Container") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include "nova/containers/_create.html" %}
|
{% include "nova/containers/_create.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Objects" %}{% endblock %}
|
{% block title %}{% trans "Objects" %}{% endblock %}
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
<div id="subfolders">
|
<div id="subfolders">
|
||||||
{{ subfolders_table.render }}
|
{{ subfolders_table.render }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Containers{% endblock %}
|
{% block title %}Containers{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Containers") %}
|
{% include "horizon/common/_page_header.html" with title=_("Containers") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Upload Object" %}{% endblock %}
|
{% block title %}{% trans "Upload Object" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Upload Objects") %}
|
{% include "horizon/common/_page_header.html" with title=_("Upload Objects") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/containers/_upload.html' %}
|
{% include 'nova/containers/_upload.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -93,7 +93,9 @@ class ContainerViewTests(test.TestCase):
|
|||||||
'method': forms.CreateContainer.__name__}
|
'method': forms.CreateContainer.__name__}
|
||||||
res = self.client.post(reverse('horizon:nova:containers:create'),
|
res = self.client.post(reverse('horizon:nova:containers:create'),
|
||||||
formData)
|
formData)
|
||||||
self.assertRedirectsNoFollow(res, CONTAINER_INDEX_URL)
|
url = reverse('horizon:nova:containers:object_index',
|
||||||
|
args=[self.containers.first().name])
|
||||||
|
self.assertRedirectsNoFollow(res, url)
|
||||||
|
|
||||||
|
|
||||||
class ObjectViewTests(test.TestCase):
|
class ObjectViewTests(test.TestCase):
|
||||||
|
@ -63,6 +63,17 @@ class IndexView(tables.DataTableView):
|
|||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = CreateContainer
|
form_class = CreateContainer
|
||||||
template_name = 'nova/containers/create.html'
|
template_name = 'nova/containers/create.html'
|
||||||
|
success_url = "horizon:nova:containers:object_index"
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
parent = self.request.POST.get('parent', None)
|
||||||
|
if parent:
|
||||||
|
container, slash, remainder = parent.partition("/")
|
||||||
|
if remainder and not remainder.endswith("/"):
|
||||||
|
remainder = "".join([remainder, "/"])
|
||||||
|
return reverse(self.success_url, args=(container, remainder))
|
||||||
|
else:
|
||||||
|
return reverse(self.success_url, args=[self.request.POST['name']])
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
initial = super(CreateView, self).get_initial()
|
initial = super(CreateView, self).get_initial()
|
||||||
@ -132,6 +143,12 @@ class ObjectIndexView(tables.MultiTableView):
|
|||||||
class UploadView(forms.ModalFormView):
|
class UploadView(forms.ModalFormView):
|
||||||
form_class = UploadObject
|
form_class = UploadObject
|
||||||
template_name = 'nova/containers/upload.html'
|
template_name = 'nova/containers/upload.html'
|
||||||
|
success_url = "horizon:nova:containers:object_index"
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse(self.success_url,
|
||||||
|
args=(self.request.POST['container_name'],
|
||||||
|
self.request.POST.get('path', '')))
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {"container_name": self.kwargs["container_name"],
|
return {"container_name": self.kwargs["container_name"],
|
||||||
@ -172,6 +189,12 @@ def object_download(request, container_name, object_path):
|
|||||||
class CopyView(forms.ModalFormView):
|
class CopyView(forms.ModalFormView):
|
||||||
form_class = CopyObject
|
form_class = CopyObject
|
||||||
template_name = 'nova/containers/copy.html'
|
template_name = 'nova/containers/copy.html'
|
||||||
|
success_url = "horizon:nova:containers:object_index"
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse(self.success_url,
|
||||||
|
args=(self.request.POST['new_container_name'],
|
||||||
|
self.request.POST.get('path', '')))
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super(CopyView, self).get_form_kwargs()
|
kwargs = super(CopyView, self).get_form_kwargs()
|
||||||
|
@ -24,26 +24,23 @@ Views for managing Nova images.
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CreateImageForm(forms.SelfHandlingForm):
|
class CreateImageForm(forms.SelfHandlingForm):
|
||||||
completion_view = 'horizon:nova:images_and_snapshots:index'
|
|
||||||
|
|
||||||
name = forms.CharField(max_length="255", label=_("Name"), required=True)
|
name = forms.CharField(max_length="255", label=_("Name"), required=True)
|
||||||
copy_from = forms.CharField(max_length="255",
|
copy_from = forms.CharField(max_length="255",
|
||||||
label=_("Image Location"),
|
label=_("Image Location"),
|
||||||
help_text=_("An external (HTTP) URL where"
|
help_text=_("An external (HTTP) URL to load "
|
||||||
" the image should be loaded from."),
|
"the image from."),
|
||||||
required=True)
|
required=True)
|
||||||
disk_format = forms.ChoiceField(label=_('Format'),
|
disk_format = forms.ChoiceField(label=_('Format'),
|
||||||
required=True,
|
required=True,
|
||||||
@ -103,19 +100,16 @@ class CreateImageForm(forms.SelfHandlingForm):
|
|||||||
'name': data['name']}
|
'name': data['name']}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
api.glance.image_create(request, **meta)
|
image = api.glance.image_create(request, **meta)
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('Your image %s has been queued for creation.' %
|
_('Your image %s has been queued for creation.' %
|
||||||
data['name']))
|
data['name']))
|
||||||
|
return image
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to create new image.'))
|
exceptions.handle(request, _('Unable to create new image.'))
|
||||||
|
|
||||||
return shortcuts.redirect(self.get_success_url())
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateImageForm(forms.SelfHandlingForm):
|
class UpdateImageForm(forms.SelfHandlingForm):
|
||||||
completion_view = 'horizon:nova:images_and_snapshots:index'
|
|
||||||
|
|
||||||
image_id = forms.CharField(widget=forms.HiddenInput())
|
image_id = forms.CharField(widget=forms.HiddenInput())
|
||||||
name = forms.CharField(max_length="255", label=_("Name"))
|
name = forms.CharField(max_length="255", label=_("Name"))
|
||||||
kernel = forms.CharField(max_length="36", label=_("Kernel ID"),
|
kernel = forms.CharField(max_length="36", label=_("Kernel ID"),
|
||||||
@ -139,13 +133,17 @@ class UpdateImageForm(forms.SelfHandlingForm):
|
|||||||
public = forms.BooleanField(label=_("Public"), required=False)
|
public = forms.BooleanField(label=_("Public"), required=False)
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
# TODO add public flag to image meta properties
|
|
||||||
image_id = data['image_id']
|
image_id = data['image_id']
|
||||||
error_updating = _('Unable to update image "%s".')
|
error_updating = _('Unable to update image "%s".')
|
||||||
|
|
||||||
|
if data['disk_format'] in ['aki', 'ari', 'ami']:
|
||||||
|
container_format = data['disk_format']
|
||||||
|
else:
|
||||||
|
container_format = 'bare'
|
||||||
|
|
||||||
meta = {'is_public': data['public'],
|
meta = {'is_public': data['public'],
|
||||||
'disk_format': data['disk_format'],
|
'disk_format': data['disk_format'],
|
||||||
'container_format': 'bare',
|
'container_format': container_format,
|
||||||
'name': data['name'],
|
'name': data['name'],
|
||||||
'properties': {}}
|
'properties': {}}
|
||||||
if data['kernel']:
|
if data['kernel']:
|
||||||
@ -154,13 +152,13 @@ class UpdateImageForm(forms.SelfHandlingForm):
|
|||||||
meta['properties']['ramdisk_id'] = data['ramdisk']
|
meta['properties']['ramdisk_id'] = data['ramdisk']
|
||||||
if data['architecture']:
|
if data['architecture']:
|
||||||
meta['properties']['architecture'] = data['architecture']
|
meta['properties']['architecture'] = data['architecture']
|
||||||
|
# Ensure we do not delete properties that have already been
|
||||||
|
# set on an image.
|
||||||
|
meta['purge_props'] = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Ensure we do not delete properties that have already been
|
image = api.image_update(request, image_id, **meta)
|
||||||
# set on an image.
|
|
||||||
meta['features'] = {'X-Glance-Registry-Purge-Props': False}
|
|
||||||
api.image_update(request, image_id, **meta)
|
|
||||||
messages.success(request, _('Image was successfully updated.'))
|
messages.success(request, _('Image was successfully updated.'))
|
||||||
|
return image
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, error_updating % image_id)
|
exceptions.handle(request, error_updating % image_id)
|
||||||
return shortcuts.redirect(self.get_success_url())
|
|
||||||
|
@ -24,7 +24,7 @@ Views for managing Nova images.
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
@ -43,35 +43,39 @@ class CreateView(forms.ModalFormView):
|
|||||||
form_class = CreateImageForm
|
form_class = CreateImageForm
|
||||||
template_name = 'nova/images_and_snapshots/images/create.html'
|
template_name = 'nova/images_and_snapshots/images/create.html'
|
||||||
context_object_name = 'image'
|
context_object_name = 'image'
|
||||||
|
success_url = reverse_lazy("horizon:nova:images_and_snapshots:index")
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(forms.ModalFormView):
|
||||||
form_class = UpdateImageForm
|
form_class = UpdateImageForm
|
||||||
template_name = 'nova/images_and_snapshots/images/update.html'
|
template_name = 'nova/images_and_snapshots/images/update.html'
|
||||||
context_object_name = 'image'
|
success_url = reverse_lazy("horizon:nova:images_and_snapshots:index")
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self):
|
||||||
try:
|
if not hasattr(self, "_object"):
|
||||||
self.object = api.image_get(self.request, kwargs['image_id'])
|
try:
|
||||||
except:
|
self._object = api.image_get(self.request,
|
||||||
msg = _('Unable to retrieve image.')
|
self.kwargs['image_id'])
|
||||||
redirect = reverse('horizon:nova:images_and_snapshots:index')
|
except:
|
||||||
exceptions.handle(self.request, msg, redirect=redirect)
|
msg = _('Unable to retrieve image.')
|
||||||
return self.object
|
redirect = reverse('horizon:nova:images_and_snapshots:index')
|
||||||
|
exceptions.handle(self.request, msg, redirect=redirect)
|
||||||
|
return self._object
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(UpdateView, self).get_context_data(**kwargs)
|
||||||
|
context['image'] = self.get_object()
|
||||||
|
return context
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
properties = self.object.properties
|
image = self.get_object()
|
||||||
# NOTE(gabriel): glanceclient currently treats "is_public" as a string
|
|
||||||
# rather than a boolean. This should be fixed in the client.
|
|
||||||
public = self.object.is_public == "True"
|
|
||||||
return {'image_id': self.kwargs['image_id'],
|
return {'image_id': self.kwargs['image_id'],
|
||||||
'name': self.object.name,
|
'name': image.name,
|
||||||
'kernel': properties.get('kernel_id', ''),
|
'kernel': image.properties.get('kernel_id', ''),
|
||||||
'ramdisk': properties.get('ramdisk_id', ''),
|
'ramdisk': image.properties.get('ramdisk_id', ''),
|
||||||
'architecture': properties.get('architecture', ''),
|
'architecture': image.properties.get('architecture', ''),
|
||||||
'container_format': self.object.container_format,
|
'disk_format': image.disk_format,
|
||||||
'disk_format': self.object.disk_format,
|
'public': image.is_public == "True"}
|
||||||
'public': public}
|
|
||||||
|
|
||||||
|
|
||||||
class DetailView(tabs.TabView):
|
class DetailView(tabs.TabView):
|
||||||
|
@ -20,21 +20,19 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CreateSnapshot(forms.SelfHandlingForm):
|
class CreateSnapshot(forms.SelfHandlingForm):
|
||||||
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
|
||||||
instance_id = forms.CharField(label=_("Instance ID"),
|
instance_id = forms.CharField(label=_("Instance ID"),
|
||||||
widget=forms.TextInput(
|
widget=forms.TextInput(
|
||||||
attrs={'readonly': 'readonly'}))
|
attrs={'readonly': 'readonly'}))
|
||||||
@ -42,14 +40,15 @@ class CreateSnapshot(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
api.snapshot_create(request, data['instance_id'], data['name'])
|
snapshot = api.snapshot_create(request,
|
||||||
|
data['instance_id'],
|
||||||
|
data['name'])
|
||||||
# NOTE(gabriel): This API call is only to display a pretty name.
|
# NOTE(gabriel): This API call is only to display a pretty name.
|
||||||
instance = api.server_get(request, data['instance_id'])
|
instance = api.server_get(request, data['instance_id'])
|
||||||
vals = {"name": data['name'], "inst": instance.name}
|
vals = {"name": data['name'], "inst": instance.name}
|
||||||
messages.success(request, _('Snapshot "%(name)s" created for '
|
messages.success(request, _('Snapshot "%(name)s" created for '
|
||||||
'instance "%(inst)s"') % vals)
|
'instance "%(inst)s"') % vals)
|
||||||
return shortcuts.redirect('horizon:nova:images_and_snapshots:'
|
return snapshot
|
||||||
'index')
|
|
||||||
except:
|
except:
|
||||||
redirect = reverse("horizon:nova:instances:index")
|
redirect = reverse("horizon:nova:instances:index")
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
|
@ -42,18 +42,6 @@ class SnapshotsViewTests(test.TestCase):
|
|||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/images_and_snapshots/snapshots/create.html')
|
'nova/images_and_snapshots/snapshots/create.html')
|
||||||
|
|
||||||
def test_create_snapshot_get_with_invalid_status(self):
|
|
||||||
server = self.servers.get(status='BUILD')
|
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
|
||||||
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
|
||||||
args=[server.id])
|
|
||||||
res = self.client.get(url)
|
|
||||||
redirect = reverse("horizon:nova:instances:index")
|
|
||||||
self.assertRedirectsNoFollow(res, redirect)
|
|
||||||
|
|
||||||
def test_create_get_server_exception(self):
|
def test_create_get_server_exception(self):
|
||||||
server = self.servers.first()
|
server = self.servers.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
@ -76,7 +64,6 @@ class SnapshotsViewTests(test.TestCase):
|
|||||||
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
api.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \
|
api.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \
|
||||||
.AndReturn(snapshot)
|
.AndReturn(snapshot)
|
||||||
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'method': 'CreateSnapshot',
|
formData = {'method': 'CreateSnapshot',
|
||||||
@ -95,7 +82,6 @@ class SnapshotsViewTests(test.TestCase):
|
|||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
self.mox.StubOutWithMock(api, 'snapshot_create')
|
self.mox.StubOutWithMock(api, 'snapshot_create')
|
||||||
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
|
||||||
api.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \
|
api.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \
|
||||||
.AndRaise(self.exceptions.nova)
|
.AndRaise(self.exceptions.nova)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
@ -24,7 +24,7 @@ Views for managing Nova instance snapshots.
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
@ -39,24 +39,24 @@ LOG = logging.getLogger(__name__)
|
|||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = CreateSnapshot
|
form_class = CreateSnapshot
|
||||||
template_name = 'nova/images_and_snapshots/snapshots/create.html'
|
template_name = 'nova/images_and_snapshots/snapshots/create.html'
|
||||||
|
success_url = reverse_lazy("horizon:nova:images_and_snapshots:index")
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
if not hasattr(self, "_object"):
|
||||||
|
try:
|
||||||
|
self._object = api.server_get(self.request,
|
||||||
|
self.kwargs["instance_id"])
|
||||||
|
except:
|
||||||
|
redirect = reverse('horizon:nova:instances:index')
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_("Unable to retrieve instance."),
|
||||||
|
redirect=redirect)
|
||||||
|
return self._object
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
redirect = reverse('horizon:nova:instances:index')
|
return {"instance_id": self.kwargs["instance_id"]}
|
||||||
instance_id = self.kwargs["instance_id"]
|
|
||||||
try:
|
|
||||||
self.instance = api.server_get(self.request, instance_id)
|
|
||||||
except:
|
|
||||||
self.instance = None
|
|
||||||
msg = _("Unable to retrieve instance.")
|
|
||||||
exceptions.handle(self.request, msg, redirect)
|
|
||||||
if self.instance.status != api.nova.INSTANCE_ACTIVE_STATE:
|
|
||||||
msg = _('To create a snapshot, the instance must be in '
|
|
||||||
'the "%s" state.') % api.nova.INSTANCE_ACTIVE_STATE
|
|
||||||
raise exceptions.Http302(redirect, message=msg)
|
|
||||||
return {"instance_id": instance_id,
|
|
||||||
"tenant_id": self.request.user.tenant_id}
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(CreateView, self).get_context_data(**kwargs)
|
context = super(CreateView, self).get_context_data(**kwargs)
|
||||||
context['instance'] = self.instance
|
context['instance'] = self.get_object()
|
||||||
return context
|
return context
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Create An Image" %}{% endblock %}
|
{% block title %}{% trans "Create An Image" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create An Image") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create An Image") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/images_and_snapshots/images/_create.html' %}
|
{% include 'nova/images_and_snapshots/images/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}{% trans "Image Detail "%}{% endblock %}
|
{% block title %}{% trans "Image Detail "%}{% endblock %}
|
||||||
@ -7,7 +7,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title="Image Detail" %}
|
{% include "horizon/common/_page_header.html" with title="Image Detail" %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
{{ tab_group.render }}
|
{{ tab_group.render }}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Update Image" %}{% endblock %}
|
{% block title %}{% trans "Update Image" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Update Image") %}
|
{% include "horizon/common/_page_header.html" with title=_("Update Image") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/images_and_snapshots/images/_update.html' %}
|
{% include 'nova/images_and_snapshots/images/_update.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Images & Snapshots" %}{% endblock %}
|
{% block title %}{% trans "Images & Snapshots" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Images & Snapshots") %}
|
{% include "horizon/common/_page_header.html" with title=_("Images & Snapshots") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
<div class="images">
|
<div class="images">
|
||||||
{{ images_table.render }}
|
{{ images_table.render }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Create Snapshot" %}{% endblock %}
|
{% block title %}{% trans "Create Snapshot" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create a Snapshot") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create a Snapshot") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/images_and_snapshots/snapshots/_create.html' %}
|
{% include 'nova/images_and_snapshots/snapshots/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -20,13 +20,13 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -39,10 +39,12 @@ class UpdateInstance(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
api.server_update(request, data['instance'], data['name'])
|
server = api.server_update(request, data['instance'], data['name'])
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('Instance "%s" updated.') % data['name'])
|
_('Instance "%s" updated.') % data['name'])
|
||||||
|
return server
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to update instance.'))
|
redirect = reverse("horizon:nova:instances:index")
|
||||||
|
exceptions.handle(request,
|
||||||
return shortcuts.redirect('horizon:nova:instances:index')
|
_('Unable to update instance.'),
|
||||||
|
redirect=redirect)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block form_id %}update_instance_form{% endblock %}
|
{% block form_id %}update_instance_form{% endblock %}
|
||||||
{% block form_action %}{% url horizon:nova:instances:update instance.id %}{% endblock %}
|
{% block form_action %}{% url horizon:nova:instances:update instance_id %}{% endblock %}
|
||||||
|
|
||||||
{% block modal-header %}{% trans "Edit Instance" %}{% endblock %}
|
{% block modal-header %}{% trans "Edit Instance" %}{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n sizeformat %}
|
{% load i18n sizeformat %}
|
||||||
{% block title %}{% trans "Instance Detail" %}{% endblock %}
|
{% block title %}{% trans "Instance Detail" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title="Instance Detail: "|add:instance.name %}
|
{% include "horizon/common/_page_header.html" with title="Instance Detail: "|add:instance.name %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
{{ tab_group.render }}
|
{{ tab_group.render }}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Instances" %}{% endblock %}
|
{% block title %}{% trans "Instances" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Instances") %}
|
{% include "horizon/common/_page_header.html" with title=_("Instances") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Launch Instance" %}{% endblock %}
|
{% block title %}{% trans "Launch Instance" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Launch Instance") %}
|
{% include "horizon/common/_page_header.html" with title=_("Launch Instance") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'horizon/common/_workflow.html' %}
|
{% include 'horizon/common/_workflow.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Update Instance" %}{% endblock %}
|
{% block title %}{% trans "Update Instance" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Update Instance") %}
|
{% include "horizon/common/_page_header.html" with title=_("Update Instance") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/instances/_update.html' %}
|
{% include 'nova/instances/_update.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -491,28 +491,22 @@ class InstanceTests(test.TestCase):
|
|||||||
'server_delete',)})
|
'server_delete',)})
|
||||||
def test_create_instance_snapshot(self):
|
def test_create_instance_snapshot(self):
|
||||||
server = self.servers.first()
|
server = self.servers.first()
|
||||||
snapshot_server = deepcopy(server)
|
|
||||||
setattr(snapshot_server, 'OS-EXT-STS:task_state',
|
|
||||||
"IMAGE_SNAPSHOT")
|
|
||||||
|
|
||||||
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
api.snapshot_create(IsA(http.HttpRequest),
|
api.snapshot_create(IsA(http.HttpRequest),
|
||||||
server.id,
|
server.id,
|
||||||
"snapshot1")
|
"snapshot1").AndReturn(self.snapshots.first())
|
||||||
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
|
||||||
api.snapshot_list_detailed(IsA(http.HttpRequest),
|
api.snapshot_list_detailed(IsA(http.HttpRequest),
|
||||||
marker=None).AndReturn([[], False])
|
marker=None).AndReturn([[], False])
|
||||||
api.image_list_detailed(IsA(http.HttpRequest),
|
api.image_list_detailed(IsA(http.HttpRequest),
|
||||||
marker=None).AndReturn([[], False])
|
marker=None).AndReturn([[], False])
|
||||||
api.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
api.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
||||||
api.server_list(IsA(http.HttpRequest)).AndReturn([snapshot_server])
|
|
||||||
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'instance_id': server.id,
|
formData = {'instance_id': server.id,
|
||||||
'method': 'CreateSnapshot',
|
'method': 'CreateSnapshot',
|
||||||
'tenant_id': server.tenant_id,
|
|
||||||
'name': 'snapshot1'}
|
'name': 'snapshot1'}
|
||||||
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
||||||
args=[server.id])
|
args=[server.id])
|
||||||
@ -520,10 +514,6 @@ class InstanceTests(test.TestCase):
|
|||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, formData)
|
||||||
self.assertRedirects(res, redir_url)
|
self.assertRedirects(res, redir_url)
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
|
||||||
self.assertContains(res, '<td class="status_unknown sortable">'
|
|
||||||
'Snapshotting</td>', 1)
|
|
||||||
|
|
||||||
@test.create_stubs({api: ('server_get',)})
|
@test.create_stubs({api: ('server_get',)})
|
||||||
def test_instance_update_get(self):
|
def test_instance_update_get(self):
|
||||||
server = self.servers.first()
|
server = self.servers.first()
|
||||||
@ -532,12 +522,10 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:nova:instances:update',
|
url = reverse('horizon:nova:instances:update', args=[server.id])
|
||||||
args=[server.id])
|
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res, 'nova/instances/update.html')
|
||||||
'nova/instances/update.html')
|
|
||||||
|
|
||||||
@test.create_stubs({api: ('server_get',)})
|
@test.create_stubs({api: ('server_get',)})
|
||||||
def test_instance_update_get_server_get_exception(self):
|
def test_instance_update_get_server_get_exception(self):
|
||||||
@ -559,7 +547,9 @@ class InstanceTests(test.TestCase):
|
|||||||
server = self.servers.first()
|
server = self.servers.first()
|
||||||
|
|
||||||
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
api.server_update(IsA(http.HttpRequest), server.id, server.name)
|
api.server_update(IsA(http.HttpRequest),
|
||||||
|
server.id,
|
||||||
|
server.name).AndReturn(server)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import logging
|
|||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
@ -126,22 +126,28 @@ class UpdateView(forms.ModalFormView):
|
|||||||
form_class = UpdateInstance
|
form_class = UpdateInstance
|
||||||
template_name = 'nova/instances/update.html'
|
template_name = 'nova/instances/update.html'
|
||||||
context_object_name = 'instance'
|
context_object_name = 'instance'
|
||||||
|
success_url = reverse_lazy("horizon:nova:instances:index")
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(UpdateView, self).get_context_data(**kwargs)
|
||||||
|
context["instance_id"] = self.kwargs['instance_id']
|
||||||
|
return context
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
if not hasattr(self, "object"):
|
if not hasattr(self, "_object"):
|
||||||
instance_id = self.kwargs['instance_id']
|
instance_id = self.kwargs['instance_id']
|
||||||
try:
|
try:
|
||||||
self.object = api.server_get(self.request, instance_id)
|
self._object = api.server_get(self.request, instance_id)
|
||||||
except:
|
except:
|
||||||
redirect = reverse("horizon:nova:instances:index")
|
redirect = reverse("horizon:nova:instances:index")
|
||||||
msg = _('Unable to retrieve instance details.')
|
msg = _('Unable to retrieve instance details.')
|
||||||
exceptions.handle(self.request, msg, redirect=redirect)
|
exceptions.handle(self.request, msg, redirect=redirect)
|
||||||
return self.object
|
return self._object
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {'instance': self.kwargs['instance_id'],
|
return {'instance': self.kwargs['instance_id'],
|
||||||
'tenant_id': self.request.user.tenant_id,
|
'tenant_id': self.request.user.tenant_id,
|
||||||
'name': getattr(self.object, 'name', '')}
|
'name': getattr(self.get_object(), 'name', '')}
|
||||||
|
|
||||||
|
|
||||||
class DetailView(tabs.TabView):
|
class DetailView(tabs.TabView):
|
||||||
|
@ -18,14 +18,14 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.utils.text import normalize_newlines
|
from django.utils.text import normalize_newlines
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon.openstack.common import jsonutils
|
from horizon import forms
|
||||||
from horizon import workflows
|
from horizon import workflows
|
||||||
|
from horizon.openstack.common import jsonutils
|
||||||
|
|
||||||
|
|
||||||
class SelectProjectUserAction(workflows.Action):
|
class SelectProjectUserAction(workflows.Action):
|
||||||
@ -320,11 +320,15 @@ class SetInstanceDetails(workflows.Step):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
KEYPAIR_IMPORT_URL = "horizon:nova:access_and_security:keypairs:import"
|
||||||
|
|
||||||
|
|
||||||
class SetAccessControlsAction(workflows.Action):
|
class SetAccessControlsAction(workflows.Action):
|
||||||
keypair = forms.ChoiceField(label=_("Keypair"),
|
keypair = forms.DynamicChoiceField(label=_("Keypair"),
|
||||||
required=False,
|
required=False,
|
||||||
help_text=_("Which keypair to use for "
|
help_text=_("Which keypair to use for "
|
||||||
"authentication."))
|
"authentication."),
|
||||||
|
add_item_link=KEYPAIR_IMPORT_URL)
|
||||||
groups = forms.MultipleChoiceField(label=_("Security Groups"),
|
groups = forms.MultipleChoiceField(label=_("Security Groups"),
|
||||||
required=True,
|
required=True,
|
||||||
initial=["default"],
|
initial=["default"],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Instance Overview{% endblock %}
|
{% block title %}Instance Overview{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Overview") %}
|
{% include "horizon/common/_page_header.html" with title=_("Overview") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include "horizon/common/_usage_summary.html" %}
|
{% include "horizon/common/_usage_summary.html" %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -15,12 +15,3 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block sidebar %}
|
|
||||||
{% include 'horizon/common/_sidebar.html' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
{% include "horizon/_messages.html" %}
|
|
||||||
{% block dash_main %}{% endblock %}
|
|
||||||
{% endblock %}
|
|
||||||
|
@ -7,14 +7,14 @@
|
|||||||
Views for managing Nova volumes.
|
Views for managing Nova volumes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django import shortcuts
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib import messages
|
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
from ..instances.tables import ACTIVE_STATES
|
from ..instances.tables import ACTIVE_STATES
|
||||||
|
|
||||||
@ -48,19 +48,19 @@ class CreateForm(forms.SelfHandlingForm):
|
|||||||
' volumes.')
|
' volumes.')
|
||||||
raise ValidationError(error_message)
|
raise ValidationError(error_message)
|
||||||
|
|
||||||
api.volume_create(request, data['size'], data['name'],
|
volume = api.volume_create(request,
|
||||||
data['description'])
|
data['size'],
|
||||||
|
data['name'],
|
||||||
|
data['description'])
|
||||||
message = 'Creating volume "%s"' % data['name']
|
message = 'Creating volume "%s"' % data['name']
|
||||||
|
|
||||||
messages.info(request, message)
|
messages.info(request, message)
|
||||||
|
return volume
|
||||||
except ValidationError, e:
|
except ValidationError, e:
|
||||||
return self.api_error(e.messages[0])
|
return self.api_error(e.messages[0])
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, ignore=True)
|
exceptions.handle(request, ignore=True)
|
||||||
return self.api_error(_("Unable to create volume."))
|
return self.api_error(_("Unable to create volume."))
|
||||||
|
|
||||||
return shortcuts.redirect("horizon:nova:volumes:index")
|
|
||||||
|
|
||||||
|
|
||||||
class AttachForm(forms.SelfHandlingForm):
|
class AttachForm(forms.SelfHandlingForm):
|
||||||
instance = forms.ChoiceField(label="Attach to Instance",
|
instance = forms.ChoiceField(label="Attach to Instance",
|
||||||
@ -113,10 +113,12 @@ class AttachForm(forms.SelfHandlingForm):
|
|||||||
"inst": instance_name,
|
"inst": instance_name,
|
||||||
"dev": data['device']}
|
"dev": data['device']}
|
||||||
messages.info(request, message)
|
messages.info(request, message)
|
||||||
|
return True
|
||||||
except:
|
except:
|
||||||
|
redirect = reverse("horizon:nova:volumes:index")
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_('Unable to attach volume.'))
|
_('Unable to attach volume.'),
|
||||||
return shortcuts.redirect("horizon:nova:volumes:index")
|
redirect=redirect)
|
||||||
|
|
||||||
|
|
||||||
class CreateSnapshotForm(forms.SelfHandlingForm):
|
class CreateSnapshotForm(forms.SelfHandlingForm):
|
||||||
@ -134,15 +136,16 @@ class CreateSnapshotForm(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
api.volume_snapshot_create(request,
|
snapshot = api.volume_snapshot_create(request,
|
||||||
data['volume_id'],
|
data['volume_id'],
|
||||||
data['name'],
|
data['name'],
|
||||||
data['description'])
|
data['description'])
|
||||||
|
|
||||||
message = _('Creating volume snapshot "%s"') % data['name']
|
message = _('Creating volume snapshot "%s"') % data['name']
|
||||||
messages.info(request, message)
|
messages.info(request, message)
|
||||||
|
return snapshot
|
||||||
except:
|
except:
|
||||||
|
redirect = reverse("horizon:nova:images_and_snapshots:index")
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_('Unable to create volume snapshot.'))
|
_('Unable to create volume snapshot.'),
|
||||||
|
redirect=redirect)
|
||||||
return shortcuts.redirect("horizon:nova:images_and_snapshots:index")
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Manage Volume Attachments{% endblock %}
|
{% block title %}Manage Volume Attachments{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Manage Volume Attachments") %}
|
{% include "horizon/common/_page_header.html" with title=_("Manage Volume Attachments") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/volumes/_attach.html' %}
|
{% include 'nova/volumes/_attach.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Create Volume{% endblock %}
|
{% block title %}Create Volume{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create a Volume") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create a Volume") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/volumes/_create.html' %}
|
{% include 'nova/volumes/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Create Volume Snapshot" %}{% endblock %}
|
{% block title %}{% trans "Create Volume Snapshot" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create a Volume Snapshot") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create a Volume Snapshot") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{% include 'nova/volumes/_create_snapshot.html' %}
|
{% include 'nova/volumes/_create_snapshot.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Volume Details" %}{% endblock %}
|
{% block title %}{% trans "Volume Details" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Volume Detail") %}
|
{% include "horizon/common/_page_header.html" with title=_("Volume Detail") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
{{ tab_group.render }}
|
{{ tab_group.render }}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'nova/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Volumes" %}{% endblock %}
|
{% block title %}{% trans "Volumes" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Volumes") %}
|
{% include "horizon/common/_page_header.html" with title=_("Volumes") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block dash_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -29,6 +29,7 @@ from horizon import test
|
|||||||
class VolumeViewTests(test.TestCase):
|
class VolumeViewTests(test.TestCase):
|
||||||
@test.create_stubs({api: ('tenant_quota_usages', 'volume_create',)})
|
@test.create_stubs({api: ('tenant_quota_usages', 'volume_create',)})
|
||||||
def test_create_volume(self):
|
def test_create_volume(self):
|
||||||
|
volume = self.volumes.first()
|
||||||
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
|
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
|
||||||
formData = {'name': u'A Volume I Am Making',
|
formData = {'name': u'A Volume I Am Making',
|
||||||
'description': u'This is a volume I am making for a test.',
|
'description': u'This is a volume I am making for a test.',
|
||||||
@ -39,7 +40,7 @@ class VolumeViewTests(test.TestCase):
|
|||||||
api.volume_create(IsA(http.HttpRequest),
|
api.volume_create(IsA(http.HttpRequest),
|
||||||
formData['size'],
|
formData['size'],
|
||||||
formData['name'],
|
formData['name'],
|
||||||
formData['description'])
|
formData['description']).AndReturn(volume)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ Views for managing Nova volumes.
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from django import shortcuts
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
|
|
||||||
@ -77,6 +79,7 @@ class DetailView(tabs.TabView):
|
|||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = CreateForm
|
form_class = CreateForm
|
||||||
template_name = 'nova/volumes/create.html'
|
template_name = 'nova/volumes/create.html'
|
||||||
|
success_url = reverse_lazy("horizon:nova:volumes:index")
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(CreateView, self).get_context_data(**kwargs)
|
context = super(CreateView, self).get_context_data(**kwargs)
|
||||||
@ -84,24 +87,26 @@ class CreateView(forms.ModalFormView):
|
|||||||
context['usages'] = api.tenant_quota_usages(self.request)
|
context['usages'] = api.tenant_quota_usages(self.request)
|
||||||
except:
|
except:
|
||||||
exceptions.handle(self.request)
|
exceptions.handle(self.request)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class CreateSnapshotView(forms.ModalFormView):
|
class CreateSnapshotView(forms.ModalFormView):
|
||||||
form_class = CreateSnapshotForm
|
form_class = CreateSnapshotForm
|
||||||
template_name = 'nova/volumes/create_snapshot.html'
|
template_name = 'nova/volumes/create_snapshot.html'
|
||||||
|
success_url = reverse_lazy("horizon:nova:images_and_snapshots:index")
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
return {'volume_id': kwargs['volume_id']}
|
return {'volume_id': self.kwargs['volume_id']}
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {'volume_id': self.kwargs["volume_id"]}
|
return {'volume_id': self.kwargs["volume_id"]}
|
||||||
|
|
||||||
|
|
||||||
class EditAttachmentsView(tables.DataTableView):
|
class EditAttachmentsView(tables.DataTableView, forms.ModalFormView):
|
||||||
table_class = AttachmentsTable
|
table_class = AttachmentsTable
|
||||||
|
form_class = AttachForm
|
||||||
template_name = 'nova/volumes/attach.html'
|
template_name = 'nova/volumes/attach.html'
|
||||||
|
success_url = reverse_lazy("horizon:nova:volumes:index")
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
if not hasattr(self, "_object"):
|
if not hasattr(self, "_object"):
|
||||||
@ -124,35 +129,40 @@ class EditAttachmentsView(tables.DataTableView):
|
|||||||
_('Unable to retrieve volume information.'))
|
_('Unable to retrieve volume information.'))
|
||||||
return attachments
|
return attachments
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
try:
|
||||||
|
instances = api.nova.server_list(self.request)
|
||||||
|
except:
|
||||||
|
instances = []
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_("Unable to retrieve attachment information."))
|
||||||
|
return {'volume': self.get_object(),
|
||||||
|
'instances': instances}
|
||||||
|
|
||||||
|
def get_form(self):
|
||||||
|
if not hasattr(self, "_form"):
|
||||||
|
form_class = self.get_form_class()
|
||||||
|
self._form = super(EditAttachmentsView, self).get_form(form_class)
|
||||||
|
return self._form
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(EditAttachmentsView, self).get_context_data(**kwargs)
|
context = super(EditAttachmentsView, self).get_context_data(**kwargs)
|
||||||
context['form'] = self.form
|
context['form'] = self.get_form()
|
||||||
context['volume'] = self.get_object()
|
context['volume'] = self.get_object()
|
||||||
|
if self.request.is_ajax():
|
||||||
|
context['hide'] = True
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def handle_form(self):
|
|
||||||
instances = api.nova.server_list(self.request)
|
|
||||||
initial = {'volume': self.get_object(),
|
|
||||||
'instances': instances}
|
|
||||||
return AttachForm.maybe_handle(self.request, initial=initial)
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.form, handled = self.handle_form()
|
# Table action handling
|
||||||
if handled:
|
|
||||||
return handled
|
|
||||||
handled = self.construct_tables()
|
handled = self.construct_tables()
|
||||||
if handled:
|
if handled:
|
||||||
return handled
|
return handled
|
||||||
context = self.get_context_data(**kwargs)
|
return self.render_to_response(self.get_context_data(**kwargs))
|
||||||
context['form'] = self.form
|
|
||||||
if request.is_ajax():
|
|
||||||
context['hide'] = True
|
|
||||||
self.template_name = ('nova/volumes'
|
|
||||||
'/_attach.html')
|
|
||||||
return self.render_to_response(context)
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
form, handled = self.handle_form()
|
form = self.get_form()
|
||||||
if handled:
|
if form.is_valid():
|
||||||
return handled
|
return self.form_valid(form)
|
||||||
return super(EditAttachmentsView, self).post(request, *args, **kwargs)
|
else:
|
||||||
|
return self.get(request, *args, **kwargs)
|
||||||
|
@ -34,14 +34,8 @@ LOG = logging.getLogger(__name__)
|
|||||||
class DownloadX509Credentials(forms.SelfHandlingForm):
|
class DownloadX509Credentials(forms.SelfHandlingForm):
|
||||||
tenant = forms.ChoiceField(label=_("Select a Project"))
|
tenant = forms.ChoiceField(label=_("Select a Project"))
|
||||||
|
|
||||||
# forms.SelfHandlingForm doesn't pass request object as the first argument
|
|
||||||
# to the class __init__ method, which causes form to explode.
|
|
||||||
@classmethod
|
|
||||||
def _instantiate(cls, request, *args, **kwargs):
|
|
||||||
return cls(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def __init__(self, request, *args, **kwargs):
|
def __init__(self, request, *args, **kwargs):
|
||||||
super(DownloadX509Credentials, self).__init__(*args, **kwargs)
|
super(DownloadX509Credentials, self).__init__(request, *args, **kwargs)
|
||||||
# Populate tenant choices
|
# Populate tenant choices
|
||||||
tenant_choices = []
|
tenant_choices = []
|
||||||
try:
|
try:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'settings/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Download EC2 Credentials" %}{% endblock %}
|
{% block title %}{% trans "Download EC2 Credentials" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Download EC2 Credentials") %}
|
{% include "horizon/common/_page_header.html" with title=_("Download EC2 Credentials") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block settings_main %}
|
{% block main %}
|
||||||
{% include "settings/ec2/download_form.html" %}
|
{% include "settings/ec2/download_form.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -26,3 +26,6 @@ LOG = logging.getLogger(__name__)
|
|||||||
class IndexView(forms.ModalFormView):
|
class IndexView(forms.ModalFormView):
|
||||||
form_class = DownloadX509Credentials
|
form_class = DownloadX509Credentials
|
||||||
template_name = 'settings/ec2/index.html'
|
template_name = 'settings/ec2/index.html'
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
return form.handle(self.request, form.cleaned_data)
|
||||||
|
@ -21,11 +21,11 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -34,14 +34,8 @@ LOG = logging.getLogger(__name__)
|
|||||||
class DownloadOpenRCForm(forms.SelfHandlingForm):
|
class DownloadOpenRCForm(forms.SelfHandlingForm):
|
||||||
tenant = forms.ChoiceField(label=_("Select a Project"))
|
tenant = forms.ChoiceField(label=_("Select a Project"))
|
||||||
|
|
||||||
# forms.SelfHandlingForm doesn't pass request object as the first argument
|
|
||||||
# to the class __init__ method, which causes form to explode.
|
|
||||||
@classmethod
|
|
||||||
def _instantiate(cls, request, *args, **kwargs):
|
|
||||||
return cls(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def __init__(self, request, *args, **kwargs):
|
def __init__(self, request, *args, **kwargs):
|
||||||
super(DownloadOpenRCForm, self).__init__(*args, **kwargs)
|
super(DownloadOpenRCForm, self).__init__(request, *args, **kwargs)
|
||||||
# Populate tenant choices
|
# Populate tenant choices
|
||||||
tenant_choices = []
|
tenant_choices = []
|
||||||
for tenant in api.tenant_list(request):
|
for tenant in api.tenant_list(request):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'settings/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "OpenStack API" %}{% endblock %}
|
{% block title %}{% trans "OpenStack API" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("OpenStack API") %}
|
{% include "horizon/common/_page_header.html" with title=_("OpenStack API") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block settings_main %}
|
{% block main %}
|
||||||
{% include "settings/project/_openrc.html" %}
|
{% include "settings/project/_openrc.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -39,3 +39,6 @@ class OpenRCView(ModalFormView):
|
|||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {'tenant': self.request.user.tenant_id}
|
return {'tenant': self.request.user.tenant_id}
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
return form.handle(self.request, form.cleaned_data)
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block sidebar %}
|
|
||||||
{% include 'horizon/common/_sidebar.html' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
{% include "horizon/_messages.html" %}
|
|
||||||
{% block settings_main %}{% endblock %}
|
|
||||||
{% endblock %}
|
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'settings/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "User Settings" %}{% endblock %}
|
{% block title %}{% trans "User Settings" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("User Settings") %}
|
{% include "horizon/common/_page_header.html" with title=_("User Settings") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block settings_main %}
|
{% block main %}
|
||||||
{% include "settings/user/_settings.html" %}
|
{% include "settings/user/_settings.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -26,3 +26,6 @@ class UserSettingsView(forms.ModalFormView):
|
|||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {'language': self.request.LANGUAGE_CODE,
|
return {'language': self.request.LANGUAGE_CODE,
|
||||||
'timezone': self.request.session.get('django_timezone', 'UTC')}
|
'timezone': self.request.session.get('django_timezone', 'UTC')}
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
return form.handle(self.request, form.cleaned_data)
|
||||||
|
@ -20,12 +20,12 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -41,14 +41,16 @@ class CreateFlavor(forms.SelfHandlingForm):
|
|||||||
eph_gb = forms.IntegerField(label=_("Ephemeral Disk GB"))
|
eph_gb = forms.IntegerField(label=_("Ephemeral Disk GB"))
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
api.flavor_create(request,
|
try:
|
||||||
data['name'],
|
flavor = api.flavor_create(request,
|
||||||
data['memory_mb'],
|
data['name'],
|
||||||
data['vcpus'],
|
data['memory_mb'],
|
||||||
data['disk_gb'],
|
data['vcpus'],
|
||||||
data['flavor_id'],
|
data['disk_gb'],
|
||||||
ephemeral=data['eph_gb'])
|
data['flavor_id'],
|
||||||
msg = _('%s was successfully added to flavors.') % data['name']
|
ephemeral=data['eph_gb'])
|
||||||
LOG.info(msg)
|
msg = _('%s was successfully added to flavors.') % data['name']
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
return shortcuts.redirect('horizon:syspanel:flavors:index')
|
return flavor
|
||||||
|
except:
|
||||||
|
exceptions.handle(request, _("Unable to create flavor"))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Create Flavors{% endblock %}
|
{% block title %}Create Flavors{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create Flavor") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create Flavor") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% include "syspanel/flavors/_create.html" %}
|
{% include "syspanel/flavors/_create.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Flavors{% endblock %}
|
{% block title %}Flavors{% endblock %}
|
||||||
|
|
||||||
@ -8,6 +8,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Flavors") refresh_link=refresh_link searchable="true" %}
|
{% include "horizon/common/_page_header.html" with title=_("Flavors") refresh_link=refresh_link searchable="true" %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -20,11 +20,11 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from novaclient import exceptions as api_exceptions
|
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from .forms import CreateFlavor
|
from .forms import CreateFlavor
|
||||||
@ -43,15 +43,9 @@ class IndexView(tables.DataTableView):
|
|||||||
flavors = []
|
flavors = []
|
||||||
try:
|
try:
|
||||||
flavors = api.flavor_list(request)
|
flavors = api.flavor_list(request)
|
||||||
except api_exceptions.Unauthorized, e:
|
except:
|
||||||
LOG.exception('Unauthorized attempt to access flavor list.')
|
exceptions.handle(request,
|
||||||
messages.error(request, _('Unauthorized.'))
|
_('Unable to retrieve flavor list.'))
|
||||||
except Exception, e:
|
|
||||||
LOG.exception('Exception while fetching usage info')
|
|
||||||
if not hasattr(e, 'message'):
|
|
||||||
e.message = str(e)
|
|
||||||
messages.error(request, _('Unable to get flavor list: %s') %
|
|
||||||
e.message)
|
|
||||||
flavors.sort(key=lambda x: x.id, reverse=True)
|
flavors.sort(key=lambda x: x.id, reverse=True)
|
||||||
return flavors
|
return flavors
|
||||||
|
|
||||||
@ -59,11 +53,15 @@ class IndexView(tables.DataTableView):
|
|||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = CreateFlavor
|
form_class = CreateFlavor
|
||||||
template_name = 'syspanel/flavors/create.html'
|
template_name = 'syspanel/flavors/create.html'
|
||||||
|
success_url = reverse_lazy('horizon:syspanel:flavors:index')
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
# TODO(tres): Get rid of this hacky bit of nonsense after flavors get
|
# TODO(tres): Get rid of this hacky bit of nonsense after flavors
|
||||||
# converted to nova client.
|
# id handling gets fixed.
|
||||||
flavors = api.flavor_list(self.request)
|
try:
|
||||||
|
flavors = api.flavor_list(self.request)
|
||||||
|
except:
|
||||||
|
exceptions.handle(self.request, ignore=True)
|
||||||
if flavors:
|
if flavors:
|
||||||
largest_id = max(flavors, key=lambda f: f.id).id
|
largest_id = max(flavors, key=lambda f: f.id).id
|
||||||
return {'flavor_id': int(largest_id) + 1}
|
return {'flavor_id': int(largest_id) + 1}
|
||||||
|
@ -18,13 +18,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from horizon.dashboards.nova.images_and_snapshots.images import forms
|
from horizon.dashboards.nova.images_and_snapshots.images import forms
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class AdminUpdateImageForm(forms.UpdateImageForm):
|
class AdminUpdateImageForm(forms.UpdateImageForm):
|
||||||
completion_view = 'horizon:syspanel:images:index'
|
pass
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Images" %}{% endblock %}
|
{% block title %}{% trans "Images" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Images") %}
|
{% include "horizon/common/_page_header.html" with title=_("Images") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}{% trans "Update Image" %}{% endblock %}
|
{% block title %}{% trans "Update Image" %}{% endblock %}
|
||||||
@ -7,6 +7,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Update Image") %}
|
{% include "horizon/common/_page_header.html" with title=_("Update Image") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% include 'syspanel/images/_update.html' %}
|
{% include 'syspanel/images/_update.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
@ -57,6 +58,7 @@ class IndexView(tables.DataTableView):
|
|||||||
class UpdateView(views.UpdateView):
|
class UpdateView(views.UpdateView):
|
||||||
template_name = 'syspanel/images/update.html'
|
template_name = 'syspanel/images/update.html'
|
||||||
form_class = AdminUpdateImageForm
|
form_class = AdminUpdateImageForm
|
||||||
|
success_url = reverse_lazy('horizon:syspanel:images:index')
|
||||||
|
|
||||||
|
|
||||||
class DetailView(views.DetailView):
|
class DetailView(views.DetailView):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Instances" %}{% endblock %}
|
{% block title %}{% trans "Instances" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("All Instances") %}
|
{% include "horizon/common/_page_header.html" with title=_("All Instances") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n sizeformat %}
|
{% load i18n sizeformat %}
|
||||||
{% block title %}{% trans "Usage Overview" %}{% endblock %}
|
{% block title %}{% trans "Usage Overview" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Overview") %}
|
{% include "horizon/common/_page_header.html" with title=_("Overview") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% if monitoring %}
|
{% if monitoring %}
|
||||||
<div id="monitoring">
|
<div id="monitoring">
|
||||||
<h3>{% trans "Monitoring" %}: </h3>
|
<h3>{% trans "Monitoring" %}: </h3>
|
||||||
|
@ -20,13 +20,12 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -50,10 +49,9 @@ class AddUser(forms.SelfHandlingForm):
|
|||||||
data['user_id'],
|
data['user_id'],
|
||||||
data['role_id'])
|
data['role_id'])
|
||||||
messages.success(request, _('Successfully added user to project.'))
|
messages.success(request, _('Successfully added user to project.'))
|
||||||
|
return True
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to add user to project.'))
|
exceptions.handle(request, _('Unable to add user to project.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:projects:users',
|
|
||||||
tenant_id=data['tenant_id'])
|
|
||||||
|
|
||||||
|
|
||||||
class CreateTenant(forms.SelfHandlingForm):
|
class CreateTenant(forms.SelfHandlingForm):
|
||||||
@ -68,16 +66,16 @@ class CreateTenant(forms.SelfHandlingForm):
|
|||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
LOG.info('Creating project with name "%s"' % data['name'])
|
LOG.info('Creating project with name "%s"' % data['name'])
|
||||||
api.tenant_create(request,
|
project = api.tenant_create(request,
|
||||||
data['name'],
|
data['name'],
|
||||||
data['description'],
|
data['description'],
|
||||||
data['enabled'])
|
data['enabled'])
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('%s was successfully created.')
|
_('%s was successfully created.')
|
||||||
% data['name'])
|
% data['name'])
|
||||||
|
return project
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to create project.'))
|
exceptions.handle(request, _('Unable to create project.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:projects:index')
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateTenant(forms.SelfHandlingForm):
|
class UpdateTenant(forms.SelfHandlingForm):
|
||||||
@ -92,17 +90,17 @@ class UpdateTenant(forms.SelfHandlingForm):
|
|||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
LOG.info('Updating project with id "%s"' % data['id'])
|
LOG.info('Updating project with id "%s"' % data['id'])
|
||||||
api.tenant_update(request,
|
project = api.tenant_update(request,
|
||||||
data['id'],
|
data['id'],
|
||||||
data['name'],
|
data['name'],
|
||||||
data['description'],
|
data['description'],
|
||||||
data['enabled'])
|
data['enabled'])
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('%s was successfully updated.')
|
_('%s was successfully updated.')
|
||||||
% data['name'])
|
% data['name'])
|
||||||
|
return project
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to update project.'))
|
exceptions.handle(request, _('Unable to update project.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:projects:index')
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateQuotas(forms.SelfHandlingForm):
|
class UpdateQuotas(forms.SelfHandlingForm):
|
||||||
@ -136,6 +134,6 @@ class UpdateQuotas(forms.SelfHandlingForm):
|
|||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('Quotas for %s were successfully updated.')
|
_('Quotas for %s were successfully updated.')
|
||||||
% data['tenant_id'])
|
% data['tenant_id'])
|
||||||
|
return True
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to update quotas.'))
|
exceptions.handle(request, _('Unable to update quotas.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:projects:index')
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Add User To Project" %}{% endblock %}
|
{% block title %}{% trans "Add User To Project" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Add User To Project") %}
|
{% include "horizon/common/_page_header.html" with title=_("Add User To Project") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% include 'syspanel/projects/_add_user.html' %}
|
{% include 'syspanel/projects/_add_user.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Create Project{% endblock %}
|
{% block title %}Create Project{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create Project") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create Project") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% include 'syspanel/projects/_create.html' %}
|
{% include 'syspanel/projects/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Projects{% endblock %}
|
{% block title %}Projects{% endblock %}
|
||||||
|
|
||||||
@ -8,6 +8,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Projects") refresh_link=refresh_link searchable="true" %}
|
{% include "horizon/common/_page_header.html" with title=_("Projects") refresh_link=refresh_link searchable="true" %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Modify Project Quotas{% endblock %}
|
{% block title %}Modify Project Quotas{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Update Project") %}
|
{% include "horizon/common/_page_header.html" with title=_("Update Project") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% include 'syspanel/projects/_quotas.html' with form=form %}
|
{% include 'syspanel/projects/_quotas.html' with form=form %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Update Project{% endblock %}
|
{% block title %}Update Project{% endblock %}
|
||||||
|
|
||||||
@ -6,6 +6,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Update Project") %}
|
{% include "horizon/common/_page_header.html" with title=_("Update Project") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% include 'syspanel/projects/_update.html' %}
|
{% include 'syspanel/projects/_update.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n sizeformat %}
|
{% load i18n sizeformat %}
|
||||||
{% block title %}{% trans "Project Usage Overview" %}{% endblock %}
|
{% block title %}{% trans "Project Usage Overview" %}{% endblock %}
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% include "horizon/common/_usage_summary.html" %}
|
{% include "horizon/common/_usage_summary.html" %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Project Users{% endblock %}
|
{% block title %}Project Users{% endblock %}
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
<div id="tenant_users_table">
|
<div id="tenant_users_table">
|
||||||
{{ tenant_users_table.render }}
|
{{ tenant_users_table.render }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,8 +51,6 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
|||||||
self.mox.StubOutWithMock(api.keystone, 'tenant_get')
|
self.mox.StubOutWithMock(api.keystone, 'tenant_get')
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_quota_get')
|
self.mox.StubOutWithMock(api.nova, 'tenant_quota_get')
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_quota_update')
|
self.mox.StubOutWithMock(api.nova, 'tenant_quota_update')
|
||||||
api.keystone.tenant_get(IgnoreArg(), tenant.id, admin=True) \
|
|
||||||
.AndReturn(tenant)
|
|
||||||
api.nova.tenant_quota_get(IgnoreArg(), tenant.id).AndReturn(quota)
|
api.nova.tenant_quota_get(IgnoreArg(), tenant.id).AndReturn(quota)
|
||||||
api.nova.tenant_quota_update(IgnoreArg(), tenant.id, **quota_data)
|
api.nova.tenant_quota_update(IgnoreArg(), tenant.id, **quota_data)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
@ -36,6 +36,27 @@ from .tables import TenantsTable, TenantUsersTable, AddUsersTable
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TenantContextMixin(object):
|
||||||
|
def get_object(self):
|
||||||
|
if not hasattr(self, "_object"):
|
||||||
|
tenant_id = self.kwargs['tenant_id']
|
||||||
|
try:
|
||||||
|
self._object = api.keystone.tenant_get(self.request,
|
||||||
|
tenant_id,
|
||||||
|
admin=True)
|
||||||
|
except:
|
||||||
|
redirect = reverse("horizon:syspanel:projects:index")
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_('Unable to retrieve project information.'),
|
||||||
|
redirect=redirect)
|
||||||
|
return self._object
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(TenantContextMixin, self).get_context_data(**kwargs)
|
||||||
|
context['tenant'] = self.get_object()
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class IndexView(tables.DataTableView):
|
class IndexView(tables.DataTableView):
|
||||||
table_class = TenantsTable
|
table_class = TenantsTable
|
||||||
template_name = 'syspanel/projects/index.html'
|
template_name = 'syspanel/projects/index.html'
|
||||||
@ -54,28 +75,20 @@ class IndexView(tables.DataTableView):
|
|||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = CreateTenant
|
form_class = CreateTenant
|
||||||
template_name = 'syspanel/projects/create.html'
|
template_name = 'syspanel/projects/create.html'
|
||||||
|
success_url = reverse_lazy('horizon:syspanel:projects:index')
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(TenantContextMixin, forms.ModalFormView):
|
||||||
form_class = UpdateTenant
|
form_class = UpdateTenant
|
||||||
template_name = 'syspanel/projects/update.html'
|
template_name = 'syspanel/projects/update.html'
|
||||||
context_object_name = 'tenant'
|
success_url = reverse_lazy('horizon:syspanel:projects:index')
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
|
||||||
tenant_id = kwargs['tenant_id']
|
|
||||||
try:
|
|
||||||
return api.keystone.tenant_get(self.request, tenant_id, admin=True)
|
|
||||||
except:
|
|
||||||
redirect = reverse("horizon:syspanel:projects:index")
|
|
||||||
exceptions.handle(self.request,
|
|
||||||
_('Unable to retrieve project.'),
|
|
||||||
redirect=redirect)
|
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {'id': self.object.id,
|
project = self.get_object()
|
||||||
'name': self.object.name,
|
return {'id': project.id,
|
||||||
'description': getattr(self.object, "description", ""),
|
'name': project.name,
|
||||||
'enabled': self.object.enabled}
|
'description': project.description,
|
||||||
|
'enabled': project.enabled}
|
||||||
|
|
||||||
|
|
||||||
class UsersView(tables.MultiTableView):
|
class UsersView(tables.MultiTableView):
|
||||||
@ -116,15 +129,14 @@ class UsersView(tables.MultiTableView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class AddUserView(forms.ModalFormView):
|
class AddUserView(TenantContextMixin, forms.ModalFormView):
|
||||||
form_class = AddUser
|
form_class = AddUser
|
||||||
template_name = 'syspanel/projects/add_user.html'
|
template_name = 'syspanel/projects/add_user.html'
|
||||||
context_object_name = 'tenant'
|
success_url = 'horizon:syspanel:projects:users'
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
def get_success_url(self):
|
||||||
return api.keystone.tenant_get(self.request,
|
return reverse(self.success_url,
|
||||||
kwargs["tenant_id"],
|
args=(self.request.POST['tenant_id'],))
|
||||||
admin=True)
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(AddUserView, self).get_context_data(**kwargs)
|
context = super(AddUserView, self).get_context_data(**kwargs)
|
||||||
@ -153,19 +165,19 @@ class AddUserView(forms.ModalFormView):
|
|||||||
'role_id': getattr(default_role, "id", None)}
|
'role_id': getattr(default_role, "id", None)}
|
||||||
|
|
||||||
|
|
||||||
class QuotasView(forms.ModalFormView):
|
class QuotasView(TenantContextMixin, forms.ModalFormView):
|
||||||
form_class = UpdateQuotas
|
form_class = UpdateQuotas
|
||||||
template_name = 'syspanel/projects/quotas.html'
|
template_name = 'syspanel/projects/quotas.html'
|
||||||
context_object_name = 'tenant'
|
success_url = reverse_lazy('horizon:syspanel:projects:index')
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
|
||||||
return api.keystone.tenant_get(self.request,
|
|
||||||
kwargs["tenant_id"],
|
|
||||||
admin=True)
|
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
quotas = api.nova.tenant_quota_get(self.request,
|
try:
|
||||||
self.kwargs['tenant_id'])
|
quotas = api.nova.tenant_quota_get(self.request,
|
||||||
|
self.kwargs['tenant_id'])
|
||||||
|
except:
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_("Unable to retrieve quota information."),
|
||||||
|
redirect=reverse(self.get_sucess_url))
|
||||||
return {
|
return {
|
||||||
'tenant_id': self.kwargs['tenant_id'],
|
'tenant_id': self.kwargs['tenant_id'],
|
||||||
'metadata_items': quotas.metadata_items,
|
'metadata_items': quotas.metadata_items,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Quotas{% endblock %}
|
{% block title %}Quotas{% endblock %}
|
||||||
|
|
||||||
@ -8,6 +8,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Default Quotas") refresh_link=refresh_link searchable="true" %}
|
{% include "horizon/common/_page_header.html" with title=_("Default Quotas") refresh_link=refresh_link searchable="true" %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Services{% endblock %}
|
{% block title %}Services{% endblock %}
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Services") refresh_link=refresh_link searchable="true" %}
|
{% include "horizon/common/_page_header.html" with title=_("Services") refresh_link=refresh_link searchable="true" %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block sidebar %}
|
|
||||||
{% include 'horizon/common/_sidebar.html' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
{% include "horizon/_messages.html" %}
|
|
||||||
{% block syspanel_main %}{% endblock %}
|
|
||||||
{% endblock %}
|
|
@ -20,8 +20,6 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import shortcuts
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.utils.translation import force_unicode, ugettext_lazy as _
|
from django.utils.translation import force_unicode, ugettext_lazy as _
|
||||||
from django.views.decorators.debug import sensitive_variables
|
from django.views.decorators.debug import sensitive_variables
|
||||||
@ -29,6 +27,7 @@ from django.views.decorators.debug import sensitive_variables
|
|||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
from horizon import messages
|
||||||
from horizon.utils import validators
|
from horizon.utils import validators
|
||||||
|
|
||||||
|
|
||||||
@ -37,7 +36,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class BaseUserForm(forms.SelfHandlingForm):
|
class BaseUserForm(forms.SelfHandlingForm):
|
||||||
def __init__(self, request, *args, **kwargs):
|
def __init__(self, request, *args, **kwargs):
|
||||||
super(BaseUserForm, self).__init__(*args, **kwargs)
|
super(BaseUserForm, self).__init__(request, *args, **kwargs)
|
||||||
# Populate tenant choices
|
# Populate tenant choices
|
||||||
tenant_choices = [('', _("Select a project"))]
|
tenant_choices = [('', _("Select a project"))]
|
||||||
|
|
||||||
@ -46,10 +45,6 @@ class BaseUserForm(forms.SelfHandlingForm):
|
|||||||
tenant_choices.append((tenant.id, tenant.name))
|
tenant_choices.append((tenant.id, tenant.name))
|
||||||
self.fields['tenant_id'].choices = tenant_choices
|
self.fields['tenant_id'].choices = tenant_choices
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _instantiate(cls, request, *args, **kwargs):
|
|
||||||
return cls(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
'''Check to make sure password fields match.'''
|
'''Check to make sure password fields match.'''
|
||||||
data = super(forms.Form, self).clean()
|
data = super(forms.Form, self).clean()
|
||||||
@ -103,10 +98,9 @@ class CreateUserForm(BaseUserForm):
|
|||||||
except:
|
except:
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_('Unable to add user to primary project.'))
|
_('Unable to add user to primary project.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:users:index')
|
return new_user
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to create user.'))
|
exceptions.handle(request, _('Unable to create user.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:users:index')
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateUserForm(BaseUserForm):
|
class UpdateUserForm(BaseUserForm):
|
||||||
@ -140,7 +134,6 @@ class UpdateUserForm(BaseUserForm):
|
|||||||
user_is_editable = api.keystone_can_edit_user()
|
user_is_editable = api.keystone_can_edit_user()
|
||||||
user = data.pop('id')
|
user = data.pop('id')
|
||||||
tenant = data.pop('tenant_id')
|
tenant = data.pop('tenant_id')
|
||||||
data.pop('method')
|
|
||||||
|
|
||||||
if user_is_editable:
|
if user_is_editable:
|
||||||
password = data.pop('password')
|
password = data.pop('password')
|
||||||
@ -184,4 +177,4 @@ class UpdateUserForm(BaseUserForm):
|
|||||||
messages.error(request,
|
messages.error(request,
|
||||||
_('Unable to update %(attributes)s for the user.')
|
_('Unable to update %(attributes)s for the user.')
|
||||||
% {"attributes": ", ".join(failed)})
|
% {"attributes": ", ".join(failed)})
|
||||||
return shortcuts.redirect('horizon:syspanel:users:index')
|
return True
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Create User{% endblock %}
|
{% block title %}Create User{% endblock %}
|
||||||
|
|
||||||
@ -7,6 +7,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Create User") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create User") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% include 'syspanel/users/_create.html' %}
|
{% include 'syspanel/users/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Users{% endblock %}
|
{% block title %}Users{% endblock %}
|
||||||
|
|
||||||
@ -8,6 +8,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Users") refresh_link=refresh_link searchable="true" %}
|
{% include "horizon/common/_page_header.html" with title=_("Users") refresh_link=refresh_link searchable="true" %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Update User{% endblock %}
|
{% block title %}Update User{% endblock %}
|
||||||
|
|
||||||
@ -7,6 +7,6 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Update User") %}
|
{% include "horizon/common/_page_header.html" with title=_("Update User") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{% include 'syspanel/users/_update.html' %}
|
{% include 'syspanel/users/_update.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views.decorators.debug import sensitive_post_parameters
|
from django.views.decorators.debug import sensitive_post_parameters
|
||||||
@ -50,33 +50,43 @@ class IndexView(tables.DataTableView):
|
|||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(forms.ModalFormView):
|
||||||
form_class = UpdateUserForm
|
form_class = UpdateUserForm
|
||||||
template_name = 'syspanel/users/update.html'
|
template_name = 'syspanel/users/update.html'
|
||||||
context_object_name = 'user'
|
success_url = reverse_lazy('horizon:syspanel:users:index')
|
||||||
|
|
||||||
@method_decorator(sensitive_post_parameters('password',
|
@method_decorator(sensitive_post_parameters('password',
|
||||||
'confirm_password'))
|
'confirm_password'))
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
return super(UpdateView, self).dispatch(*args, **kwargs)
|
return super(UpdateView, self).dispatch(*args, **kwargs)
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self):
|
||||||
user_id = kwargs['user_id']
|
if not hasattr(self, "_object"):
|
||||||
try:
|
try:
|
||||||
return api.user_get(self.request, user_id, admin=True)
|
self._object = api.user_get(self.request,
|
||||||
except:
|
self.kwargs['user_id'],
|
||||||
redirect = reverse("horizon:syspanel:users:index")
|
admin=True)
|
||||||
exceptions.handle(self.request,
|
except:
|
||||||
_('Unable to update user.'),
|
redirect = reverse("horizon:syspanel:users:index")
|
||||||
redirect=redirect)
|
exceptions.handle(self.request,
|
||||||
|
_('Unable to update user.'),
|
||||||
|
redirect=redirect)
|
||||||
|
return self._object
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(UpdateView, self).get_context_data(**kwargs)
|
||||||
|
context['user'] = self.get_object()
|
||||||
|
return context
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {'id': self.object.id,
|
user = self.get_object()
|
||||||
'name': getattr(self.object, 'name', None),
|
return {'id': user.id,
|
||||||
'tenant_id': getattr(self.object, 'tenantId', None),
|
'name': user.name,
|
||||||
'email': getattr(self.object, 'email', '')}
|
'tenant_id': getattr(user, 'tenantId', None),
|
||||||
|
'email': user.email}
|
||||||
|
|
||||||
|
|
||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = CreateUserForm
|
form_class = CreateUserForm
|
||||||
template_name = 'syspanel/users/create.html'
|
template_name = 'syspanel/users/create.html'
|
||||||
|
success_url = reverse_lazy('horizon:syspanel:users:index')
|
||||||
|
|
||||||
@method_decorator(sensitive_post_parameters('password',
|
@method_decorator(sensitive_post_parameters('password',
|
||||||
'confirm_password'))
|
'confirm_password'))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Volume Details" %}{% endblock %}
|
{% block title %}{% trans "Volume Details" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Volume Detail") %}
|
{% include "horizon/common/_page_header.html" with title=_("Volume Detail") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
{{ tab_group.render }}
|
{{ tab_group.render }}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Volumes" %}{% endblock %}
|
{% block title %}{% trans "Volumes" %}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
{% include "horizon/common/_page_header.html" with title=_("Volumes") %}
|
{% include "horizon/common/_page_header.html" with title=_("Volumes") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -22,3 +22,4 @@ from django.forms import widgets
|
|||||||
# Convenience imports for public API components.
|
# Convenience imports for public API components.
|
||||||
from .base import SelfHandlingForm, DateForm
|
from .base import SelfHandlingForm, DateForm
|
||||||
from .views import ModalFormView
|
from .views import ModalFormView
|
||||||
|
from .fields import DynamicTypedChoiceField, DynamicChoiceField
|
||||||
|
@ -18,41 +18,22 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms.forms import NON_FIELD_ERRORS
|
from django.forms.forms import NON_FIELD_ERRORS
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.utils import dates, timezone
|
from django.utils import dates, timezone
|
||||||
|
|
||||||
from horizon import exceptions
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SelfHandlingForm(forms.Form):
|
class SelfHandlingForm(forms.Form):
|
||||||
"""
|
"""
|
||||||
A base :class:`Form <django:django.forms.Form>` class which includes
|
A base :class:`Form <django:django.forms.Form>` class which includes
|
||||||
processing logic in its subclasses and handling errors raised during
|
processing logic in its subclasses.
|
||||||
form processing.
|
|
||||||
|
|
||||||
.. attribute:: method
|
|
||||||
|
|
||||||
A :class:`CharField <django:django.forms.CharField>` instance
|
|
||||||
rendered with a
|
|
||||||
:class:`CharField <django:django.forms.widgets.HiddenInput>`
|
|
||||||
widget which is automatically set to the value of the class name.
|
|
||||||
|
|
||||||
This is used to determine whether this form should handle the
|
|
||||||
input it is given or not.
|
|
||||||
"""
|
"""
|
||||||
method = forms.CharField(required=True, widget=forms.HiddenInput)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, request, *args, **kwargs):
|
||||||
initial = kwargs.pop('initial', {})
|
self.request = request
|
||||||
initial['method'] = self.__class__.__name__
|
if not hasattr(self, "handle"):
|
||||||
kwargs['initial'] = initial
|
raise NotImplementedError("%s does not define a handle method."
|
||||||
|
% self.__class__.__name__)
|
||||||
super(SelfHandlingForm, self).__init__(*args, **kwargs)
|
super(SelfHandlingForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def api_error(self, message):
|
def api_error(self, message):
|
||||||
@ -64,52 +45,6 @@ class SelfHandlingForm(forms.Form):
|
|||||||
"""
|
"""
|
||||||
self._errors[NON_FIELD_ERRORS] = self.error_class([message])
|
self._errors[NON_FIELD_ERRORS] = self.error_class([message])
|
||||||
|
|
||||||
def get_success_url(self, request=None):
|
|
||||||
"""
|
|
||||||
Returns the URL to redirect to after a successful handling.
|
|
||||||
"""
|
|
||||||
if self.completion_view:
|
|
||||||
return reverse(self.completion_view)
|
|
||||||
if self.completion_url:
|
|
||||||
return self.completion_url
|
|
||||||
return request.get_full_path()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _instantiate(cls, request, *args, **kwargs):
|
|
||||||
""" Instantiates the form. Allows customization in subclasses. """
|
|
||||||
return cls(*args, **kwargs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def maybe_handle(cls, request, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
If the form is valid,
|
|
||||||
:meth:`~horizon.forms.SelfHandlingForm.maybe_handle` calls a
|
|
||||||
``handle(request, data)`` method on its subclass to
|
|
||||||
determine what action to take.
|
|
||||||
|
|
||||||
Any exceptions raised during processing are captured and
|
|
||||||
converted to messages.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if request.method != 'POST' or \
|
|
||||||
cls.__name__ != request.POST.get('method'):
|
|
||||||
return cls._instantiate(request, *args, **kwargs), None
|
|
||||||
|
|
||||||
if request.FILES:
|
|
||||||
form = cls._instantiate(request, request.POST, request.FILES,
|
|
||||||
*args, **kwargs)
|
|
||||||
else:
|
|
||||||
form = cls._instantiate(request, request.POST, *args, **kwargs)
|
|
||||||
|
|
||||||
if not form.is_valid():
|
|
||||||
return form, None
|
|
||||||
|
|
||||||
try:
|
|
||||||
return form, form.handle(request, form.cleaned_data)
|
|
||||||
except:
|
|
||||||
exceptions.handle(request)
|
|
||||||
return form, None
|
|
||||||
|
|
||||||
|
|
||||||
class DateForm(forms.Form):
|
class DateForm(forms.Form):
|
||||||
""" A simple form for selecting a start date. """
|
""" A simple form for selecting a start date. """
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user