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:
		@@ -364,7 +364,12 @@ def server_reboot(request, instance_id, hardness=REBOOT_HARD):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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):
 | 
			
		||||
 
 | 
			
		||||
@@ -19,22 +19,15 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FloatingIpAllocate(forms.SelfHandlingForm):
 | 
			
		||||
    tenant_name = forms.CharField(widget=forms.HiddenInput())
 | 
			
		||||
    pool = forms.ChoiceField(label=_("Pool"))
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
@@ -44,16 +37,10 @@ class FloatingIpAllocate(forms.SelfHandlingForm):
 | 
			
		||||
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        try:
 | 
			
		||||
            fip = api.tenant_floating_ip_allocate(request,
 | 
			
		||||
                                                  pool=data.get('pool', None))
 | 
			
		||||
            LOG.info('Allocating Floating IP "%s" to project "%s"'
 | 
			
		||||
                     % (fip.ip, data['tenant_name']))
 | 
			
		||||
 | 
			
		||||
            fip = api.tenant_floating_ip_allocate(request, pool=data['pool'])
 | 
			
		||||
            messages.success(request,
 | 
			
		||||
                             _('Successfully allocated Floating IP "%(ip)s" '
 | 
			
		||||
                               'to project "%(project)s"')
 | 
			
		||||
                             % {"ip": fip.ip, "project": data['tenant_name']})
 | 
			
		||||
                             _('Allocated Floating IP %(ip)s.')
 | 
			
		||||
                             % {"ip": fip.ip})
 | 
			
		||||
            return fip
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _('Unable to allocate Floating IP.'))
 | 
			
		||||
        return shortcuts.redirect(
 | 
			
		||||
                        'horizon:nova:access_and_security:index')
 | 
			
		||||
 
 | 
			
		||||
@@ -18,13 +18,13 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.core import urlresolvers
 | 
			
		||||
from django.utils.http import urlencode
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import messages
 | 
			
		||||
from horizon import tables
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
Views for managing Nova floating IPs.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from django.core.urlresolvers import reverse_lazy
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
@@ -41,7 +42,10 @@ class AssociateView(workflows.WorkflowView):
 | 
			
		||||
class AllocateView(forms.ModalFormView):
 | 
			
		||||
    form_class = FloatingIpAllocate
 | 
			
		||||
    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):
 | 
			
		||||
        context = super(AllocateView, self).get_context_data(**kwargs)
 | 
			
		||||
@@ -52,11 +56,13 @@ class AllocateView(forms.ModalFormView):
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        pools = api.floating_ip_pools_list(self.request)
 | 
			
		||||
        if pools:
 | 
			
		||||
            pool_list = [(pool.name, pool.name)
 | 
			
		||||
                         for pool in api.floating_ip_pools_list(self.request)]
 | 
			
		||||
        else:
 | 
			
		||||
        try:
 | 
			
		||||
            pools = api.floating_ip_pools_list(self.request)
 | 
			
		||||
        except:
 | 
			
		||||
            pools = []
 | 
			
		||||
            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."))]
 | 
			
		||||
        return {'tenant_name': self.request.user.tenant_name,
 | 
			
		||||
                'pool_list': pool_list}
 | 
			
		||||
        return {'pool_list': pool_list}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,19 +15,23 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import workflows
 | 
			
		||||
from horizon import forms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ALLOCATE_URL = "horizon:nova:access_and_security:floating_ips:allocate"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssociateIPAction(workflows.Action):
 | 
			
		||||
    ip_id = forms.TypedChoiceField(label=_("IP Address"),
 | 
			
		||||
                                   coerce=int,
 | 
			
		||||
                                   empty_value=None)
 | 
			
		||||
    ip_id = forms.DynamicTypedChoiceField(label=_("IP Address"),
 | 
			
		||||
                                          coerce=int,
 | 
			
		||||
                                          empty_value=None,
 | 
			
		||||
                                          add_item_link=ALLOCATE_URL)
 | 
			
		||||
    instance_id = forms.ChoiceField(label=_("Instance"))
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
 
 | 
			
		||||
@@ -18,20 +18,18 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.core import validators
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
NEW_LINES = re.compile(r"\r|\n")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -44,14 +42,7 @@ class CreateKeypair(forms.SelfHandlingForm):
 | 
			
		||||
                                'and hyphens.')})
 | 
			
		||||
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        try:
 | 
			
		||||
            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())
 | 
			
		||||
        return True  # We just redirect to the download view.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImportKeypair(forms.SelfHandlingForm):
 | 
			
		||||
@@ -61,14 +52,14 @@ class ImportKeypair(forms.SelfHandlingForm):
 | 
			
		||||
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        try:
 | 
			
		||||
            LOG.info('Importing keypair "%s"' % data['name'])
 | 
			
		||||
            # Remove any new lines in the 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')
 | 
			
		||||
                                       % data['name'])
 | 
			
		||||
            return shortcuts.redirect(
 | 
			
		||||
                            'horizon:nova:access_and_security:index')
 | 
			
		||||
            return keypair
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request,
 | 
			
		||||
                              _('Unable to import keypair.'))
 | 
			
		||||
 
 | 
			
		||||
@@ -124,8 +124,8 @@ class KeyPairViewTests(test.TestCase):
 | 
			
		||||
                    'name': key1_name,
 | 
			
		||||
                    'public_key': public_key}
 | 
			
		||||
        url = reverse('horizon:nova:access_and_security:keypairs:import')
 | 
			
		||||
        self.client.post(url, formData)
 | 
			
		||||
        self.assertMessageCount(success=1)
 | 
			
		||||
        res = self.client.post(url, formData)
 | 
			
		||||
        self.assertMessageCount(res, success=1)
 | 
			
		||||
 | 
			
		||||
    def test_generate_keypair_exception(self):
 | 
			
		||||
        keypair = self.keypairs.first()
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ Views for managing Nova keypairs.
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
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.views.generic import View, TemplateView
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
@@ -41,11 +41,20 @@ LOG = logging.getLogger(__name__)
 | 
			
		||||
class CreateView(forms.ModalFormView):
 | 
			
		||||
    form_class = CreateKeypair
 | 
			
		||||
    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):
 | 
			
		||||
    form_class = ImportKeypair
 | 
			
		||||
    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):
 | 
			
		||||
 
 | 
			
		||||
@@ -18,24 +18,19 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.core import validators
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.forms import ValidationError
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
from horizon.utils.validators import validate_port_range
 | 
			
		||||
from horizon.utils import fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CreateGroup(forms.SelfHandlingForm):
 | 
			
		||||
    name = forms.CharField(label=_("Name"),
 | 
			
		||||
                           validators=[validators.validate_slug])
 | 
			
		||||
@@ -43,15 +38,18 @@ class CreateGroup(forms.SelfHandlingForm):
 | 
			
		||||
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        try:
 | 
			
		||||
            api.security_group_create(request,
 | 
			
		||||
                                      data['name'],
 | 
			
		||||
                                      data['description'])
 | 
			
		||||
            sg = api.security_group_create(request,
 | 
			
		||||
                                           data['name'],
 | 
			
		||||
                                           data['description'])
 | 
			
		||||
            messages.success(request,
 | 
			
		||||
                             _('Successfully created security group: %s')
 | 
			
		||||
                                    % data['name'])
 | 
			
		||||
                               % data['name'])
 | 
			
		||||
            return sg
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _('Unable to create security group.'))
 | 
			
		||||
        return shortcuts.redirect('horizon:nova:access_and_security:index')
 | 
			
		||||
            redirect = reverse("horizon:nova:access_and_security:index")
 | 
			
		||||
            exceptions.handle(request,
 | 
			
		||||
                              _('Unable to create security group.'),
 | 
			
		||||
                              redirect=redirect)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AddRule(forms.SelfHandlingForm):
 | 
			
		||||
@@ -59,6 +57,8 @@ class AddRule(forms.SelfHandlingForm):
 | 
			
		||||
                                    choices=[('tcp', 'TCP'),
 | 
			
		||||
                                             ('udp', 'UDP'),
 | 
			
		||||
                                             ('icmp', 'ICMP')],
 | 
			
		||||
                                    help_text=_("The protocol which this "
 | 
			
		||||
                                                "rule should be applied to."),
 | 
			
		||||
                                    widget=forms.Select(attrs={'class':
 | 
			
		||||
                                                               'switchable'}))
 | 
			
		||||
    from_port = forms.IntegerField(label=_("From Port"),
 | 
			
		||||
@@ -80,7 +80,13 @@ class AddRule(forms.SelfHandlingForm):
 | 
			
		||||
                                               'data-icmp': _('Code')}),
 | 
			
		||||
                                 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"),
 | 
			
		||||
                           required=False,
 | 
			
		||||
                           initial="0.0.0.0/0",
 | 
			
		||||
@@ -92,14 +98,13 @@ class AddRule(forms.SelfHandlingForm):
 | 
			
		||||
    security_group_id = forms.IntegerField(widget=forms.HiddenInput())
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        sg_list = kwargs.pop('sg_list', [])
 | 
			
		||||
        super(AddRule, self).__init__(*args, **kwargs)
 | 
			
		||||
        initials = kwargs.get("initial", {})
 | 
			
		||||
        current_group_id = initials.get('security_group_id', 0)
 | 
			
		||||
        security_groups = initials.get('security_group_list', [])
 | 
			
		||||
        security_groups_choices = [("", "CIDR")]  # default choice is CIDR
 | 
			
		||||
        group_choices = [s for s in security_groups]
 | 
			
		||||
        if len(group_choices):  # add group choice if available
 | 
			
		||||
            security_groups_choices.append(('Security Group', group_choices))
 | 
			
		||||
        # Determine if there are security groups available for the
 | 
			
		||||
        # source group option; add the choices and enable the option if so.
 | 
			
		||||
        security_groups_choices = [("", "CIDR")]
 | 
			
		||||
        if sg_list:
 | 
			
		||||
            security_groups_choices.append(('Security Group', sg_list))
 | 
			
		||||
        self.fields['source_group'].choices = security_groups_choices
 | 
			
		||||
 | 
			
		||||
    def clean(self):
 | 
			
		||||
@@ -160,7 +165,9 @@ class AddRule(forms.SelfHandlingForm):
 | 
			
		||||
                                                  data['source_group'])
 | 
			
		||||
            messages.success(request,
 | 
			
		||||
                             _('Successfully added rule: %s') % unicode(rule))
 | 
			
		||||
            return rule
 | 
			
		||||
        except:
 | 
			
		||||
            redirect = reverse("horizon:nova:access_and_security:index")
 | 
			
		||||
            exceptions.handle(request,
 | 
			
		||||
                              _('Unable to add rule to security group.'))
 | 
			
		||||
        return shortcuts.redirect("horizon:nova:access_and_security:index")
 | 
			
		||||
                              _('Unable to add rule to security group.'),
 | 
			
		||||
                              redirect=redirect)
 | 
			
		||||
 
 | 
			
		||||
@@ -100,30 +100,16 @@ class SecurityGroupsViewTests(test.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_edit_rules_get_exception(self):
 | 
			
		||||
        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_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),
 | 
			
		||||
                               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()
 | 
			
		||||
 | 
			
		||||
        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):
 | 
			
		||||
        sec_group = self.security_groups.first()
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ Views for managing Nova instances.
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.core.urlresolvers import reverse_lazy
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
@@ -37,9 +38,11 @@ from .tables import RulesTable
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditRulesView(tables.DataTableView):
 | 
			
		||||
class EditRulesView(tables.DataTableView, forms.ModalFormView):
 | 
			
		||||
    table_class = RulesTable
 | 
			
		||||
    form_class = AddRule
 | 
			
		||||
    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):
 | 
			
		||||
        security_group_id = int(self.kwargs['security_group_id'])
 | 
			
		||||
@@ -55,7 +58,12 @@ class EditRulesView(tables.DataTableView):
 | 
			
		||||
                              _('Unable to retrieve security group.'))
 | 
			
		||||
        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:
 | 
			
		||||
            groups = api.security_group_list(self.request)
 | 
			
		||||
        except:
 | 
			
		||||
@@ -63,36 +71,49 @@ class EditRulesView(tables.DataTableView):
 | 
			
		||||
            exceptions.handle(self.request,
 | 
			
		||||
                              _("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'],
 | 
			
		||||
                   'security_group_list': security_groups}
 | 
			
		||||
        return AddRule.maybe_handle(self.request, initial=initial)
 | 
			
		||||
    def get_form(self):
 | 
			
		||||
        if not hasattr(self, "_form"):
 | 
			
		||||
            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):
 | 
			
		||||
        # Form handling
 | 
			
		||||
        form, handled = self.handle_form()
 | 
			
		||||
        if handled:
 | 
			
		||||
            return handled
 | 
			
		||||
        # Table action handling
 | 
			
		||||
        handled = self.construct_tables()
 | 
			
		||||
        if handled:
 | 
			
		||||
            return handled
 | 
			
		||||
        if not self.object:
 | 
			
		||||
            return shortcuts.redirect("horizon:nova:access_and_security:index")
 | 
			
		||||
        if not self.object:  # Set during table construction.
 | 
			
		||||
            return shortcuts.redirect(self.success_url)
 | 
			
		||||
        context = self.get_context_data(**kwargs)
 | 
			
		||||
        context['form'] = form
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
    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):
 | 
			
		||||
    form_class = CreateGroup
 | 
			
		||||
    template_name = 'nova/access_and_security/security_groups/create.html'
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        return {"tenant_id": self.request.user.tenant_id}
 | 
			
		||||
    success_url = reverse_lazy('horizon:nova:access_and_security:index')
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Allocate Floating IP" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include 'nova/access_and_security/floating_ips/_allocate.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Associate Floating IP" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Associate Floating IP") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include 'horizon/common/_workflow.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Access & Security{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Access & Security") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  <div id="floating_ips">
 | 
			
		||||
      {{ floating_ips_table.render }}
 | 
			
		||||
  </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Create Keypair{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create Keypair") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include 'nova/access_and_security/keypairs/_create.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% blocktrans %}Download Keypair{% endblocktrans %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Download Keypair") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  <div class="modal-header">
 | 
			
		||||
    <h3>{% blocktrans %}The keypair "{{ keypair_name }}" should download automatically. If not use the link below.{% endblocktrans %}</h3>
 | 
			
		||||
  </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Import Keypair{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Import Keypair") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include 'nova/access_and_security/keypairs/_import.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Create Security Group{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create Security Group") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'nova/access_and_security/security_groups/_create.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Edit Security Group Rules{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Edit Security Group Rules") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include "nova/access_and_security/security_groups/_edit_rules.html" %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,6 @@ Views for Instances and Volumes.
 | 
			
		||||
"""
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,6 @@
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.core import validators
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
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 exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
@@ -65,17 +64,10 @@ class CreateContainer(forms.SelfHandlingForm):
 | 
			
		||||
                                           container,
 | 
			
		||||
                                           subfolder_name)
 | 
			
		||||
                messages.success(request, _("Folder created successfully."))
 | 
			
		||||
                url = "horizon:nova:containers:object_index"
 | 
			
		||||
                if remainder:
 | 
			
		||||
                    remainder = remainder.rstrip("/")
 | 
			
		||||
                    remainder += "/"
 | 
			
		||||
                return shortcuts.redirect(url, container, remainder)
 | 
			
		||||
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _('Unable to create container.'))
 | 
			
		||||
 | 
			
		||||
        return shortcuts.redirect("horizon:nova:containers:index")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UploadObject(forms.SelfHandlingForm):
 | 
			
		||||
    path = forms.CharField(max_length=255,
 | 
			
		||||
@@ -101,10 +93,9 @@ class UploadObject(forms.SelfHandlingForm):
 | 
			
		||||
            obj.metadata['orig-filename'] = object_file.name
 | 
			
		||||
            obj.sync_metadata()
 | 
			
		||||
            messages.success(request, _("Object was successfully uploaded."))
 | 
			
		||||
            return obj
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _("Unable to upload object."))
 | 
			
		||||
        return shortcuts.redirect("horizon:nova:containers:object_index",
 | 
			
		||||
                                  data['container_name'], data['path'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CopyObject(forms.SelfHandlingForm):
 | 
			
		||||
@@ -160,12 +151,13 @@ class CopyObject(forms.SelfHandlingForm):
 | 
			
		||||
            messages.success(request,
 | 
			
		||||
                             _('Copied "%(orig)s" to "%(dest)s" as "%(new)s".')
 | 
			
		||||
                             % vals)
 | 
			
		||||
            return True
 | 
			
		||||
        except exceptions.HorizonException, exc:
 | 
			
		||||
            messages.error(request, exc)
 | 
			
		||||
            return shortcuts.redirect(object_index, orig_container)
 | 
			
		||||
            raise exceptions.Http302(reverse(object_index,
 | 
			
		||||
                                             args=[orig_container]))
 | 
			
		||||
        except:
 | 
			
		||||
            redirect = reverse(object_index, args=(orig_container,))
 | 
			
		||||
            exceptions.handle(request,
 | 
			
		||||
                              _("Unable to copy object."),
 | 
			
		||||
                              redirect=redirect)
 | 
			
		||||
        return shortcuts.redirect(object_index, new_container, data['path'])
 | 
			
		||||
 
 | 
			
		||||
@@ -17,13 +17,13 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from cloudfiles.errors import ContainerNotEmpty
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.template.defaultfilters import filesizeformat
 | 
			
		||||
from django.utils import http
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import messages
 | 
			
		||||
from horizon import tables
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Copy Object" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Copy Object") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'nova/containers/_copy.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Create Container{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create Container") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include "nova/containers/_create.html" %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Objects" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
  </div>
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  <div id="subfolders">
 | 
			
		||||
      {{ subfolders_table.render  }}
 | 
			
		||||
  </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Containers{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Containers") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Upload Object" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Upload Objects") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'nova/containers/_upload.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,9 @@ class ContainerViewTests(test.TestCase):
 | 
			
		||||
                    'method': forms.CreateContainer.__name__}
 | 
			
		||||
        res = self.client.post(reverse('horizon:nova:containers:create'),
 | 
			
		||||
                               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):
 | 
			
		||||
 
 | 
			
		||||
@@ -63,6 +63,17 @@ class IndexView(tables.DataTableView):
 | 
			
		||||
class CreateView(forms.ModalFormView):
 | 
			
		||||
    form_class = CreateContainer
 | 
			
		||||
    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):
 | 
			
		||||
        initial = super(CreateView, self).get_initial()
 | 
			
		||||
@@ -132,6 +143,12 @@ class ObjectIndexView(tables.MultiTableView):
 | 
			
		||||
class UploadView(forms.ModalFormView):
 | 
			
		||||
    form_class = UploadObject
 | 
			
		||||
    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):
 | 
			
		||||
        return {"container_name": self.kwargs["container_name"],
 | 
			
		||||
@@ -172,6 +189,12 @@ def object_download(request, container_name, object_path):
 | 
			
		||||
class CopyView(forms.ModalFormView):
 | 
			
		||||
    form_class = CopyObject
 | 
			
		||||
    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):
 | 
			
		||||
        kwargs = super(CopyView, self).get_form_kwargs()
 | 
			
		||||
 
 | 
			
		||||
@@ -24,26 +24,23 @@ Views for managing Nova images.
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CreateImageForm(forms.SelfHandlingForm):
 | 
			
		||||
    completion_view = 'horizon:nova:images_and_snapshots:index'
 | 
			
		||||
 | 
			
		||||
    name = forms.CharField(max_length="255", label=_("Name"), required=True)
 | 
			
		||||
    copy_from = forms.CharField(max_length="255",
 | 
			
		||||
                                label=_("Image Location"),
 | 
			
		||||
                                help_text=_("An external (HTTP) URL where"
 | 
			
		||||
                                    " the image should be loaded from."),
 | 
			
		||||
                                help_text=_("An external (HTTP) URL to load "
 | 
			
		||||
                                            "the image from."),
 | 
			
		||||
                                required=True)
 | 
			
		||||
    disk_format = forms.ChoiceField(label=_('Format'),
 | 
			
		||||
                                    required=True,
 | 
			
		||||
@@ -103,19 +100,16 @@ class CreateImageForm(forms.SelfHandlingForm):
 | 
			
		||||
                'name': data['name']}
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            api.glance.image_create(request, **meta)
 | 
			
		||||
            image = api.glance.image_create(request, **meta)
 | 
			
		||||
            messages.success(request,
 | 
			
		||||
                _('Your image %s has been queued for creation.' %
 | 
			
		||||
                    data['name']))
 | 
			
		||||
            return image
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _('Unable to create new image.'))
 | 
			
		||||
 | 
			
		||||
        return shortcuts.redirect(self.get_success_url())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UpdateImageForm(forms.SelfHandlingForm):
 | 
			
		||||
    completion_view = 'horizon:nova:images_and_snapshots:index'
 | 
			
		||||
 | 
			
		||||
    image_id = forms.CharField(widget=forms.HiddenInput())
 | 
			
		||||
    name = forms.CharField(max_length="255", label=_("Name"))
 | 
			
		||||
    kernel = forms.CharField(max_length="36", label=_("Kernel ID"),
 | 
			
		||||
@@ -139,13 +133,17 @@ class UpdateImageForm(forms.SelfHandlingForm):
 | 
			
		||||
    public = forms.BooleanField(label=_("Public"), required=False)
 | 
			
		||||
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        # TODO add public flag to image meta properties
 | 
			
		||||
        image_id = data['image_id']
 | 
			
		||||
        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'],
 | 
			
		||||
                'disk_format': data['disk_format'],
 | 
			
		||||
                'container_format': 'bare',
 | 
			
		||||
                'container_format': container_format,
 | 
			
		||||
                'name': data['name'],
 | 
			
		||||
                'properties': {}}
 | 
			
		||||
        if data['kernel']:
 | 
			
		||||
@@ -154,13 +152,13 @@ class UpdateImageForm(forms.SelfHandlingForm):
 | 
			
		||||
            meta['properties']['ramdisk_id'] = data['ramdisk']
 | 
			
		||||
        if 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:
 | 
			
		||||
            # Ensure we do not delete properties that have already been
 | 
			
		||||
            # set on an image.
 | 
			
		||||
            meta['features'] = {'X-Glance-Registry-Purge-Props': False}
 | 
			
		||||
            api.image_update(request, image_id, **meta)
 | 
			
		||||
            image = api.image_update(request, image_id, **meta)
 | 
			
		||||
            messages.success(request, _('Image was successfully updated.'))
 | 
			
		||||
            return image
 | 
			
		||||
        except:
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.core.urlresolvers import reverse, reverse_lazy
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
@@ -43,35 +43,39 @@ class CreateView(forms.ModalFormView):
 | 
			
		||||
    form_class = CreateImageForm
 | 
			
		||||
    template_name = 'nova/images_and_snapshots/images/create.html'
 | 
			
		||||
    context_object_name = 'image'
 | 
			
		||||
    success_url = reverse_lazy("horizon:nova:images_and_snapshots:index")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UpdateView(forms.ModalFormView):
 | 
			
		||||
    form_class = UpdateImageForm
 | 
			
		||||
    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):
 | 
			
		||||
        try:
 | 
			
		||||
            self.object = api.image_get(self.request, kwargs['image_id'])
 | 
			
		||||
        except:
 | 
			
		||||
            msg = _('Unable to retrieve image.')
 | 
			
		||||
            redirect = reverse('horizon:nova:images_and_snapshots:index')
 | 
			
		||||
            exceptions.handle(self.request, msg, redirect=redirect)
 | 
			
		||||
        return self.object
 | 
			
		||||
    def get_object(self):
 | 
			
		||||
        if not hasattr(self, "_object"):
 | 
			
		||||
            try:
 | 
			
		||||
                self._object = api.image_get(self.request,
 | 
			
		||||
                                             self.kwargs['image_id'])
 | 
			
		||||
            except:
 | 
			
		||||
                msg = _('Unable to retrieve image.')
 | 
			
		||||
                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):
 | 
			
		||||
        properties = self.object.properties
 | 
			
		||||
        # 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"
 | 
			
		||||
        image = self.get_object()
 | 
			
		||||
        return {'image_id': self.kwargs['image_id'],
 | 
			
		||||
                'name': self.object.name,
 | 
			
		||||
                'kernel': properties.get('kernel_id', ''),
 | 
			
		||||
                'ramdisk': properties.get('ramdisk_id', ''),
 | 
			
		||||
                'architecture': properties.get('architecture', ''),
 | 
			
		||||
                'container_format': self.object.container_format,
 | 
			
		||||
                'disk_format': self.object.disk_format,
 | 
			
		||||
                'public': public}
 | 
			
		||||
                'name': image.name,
 | 
			
		||||
                'kernel': image.properties.get('kernel_id', ''),
 | 
			
		||||
                'ramdisk': image.properties.get('ramdisk_id', ''),
 | 
			
		||||
                'architecture': image.properties.get('architecture', ''),
 | 
			
		||||
                'disk_format': image.disk_format,
 | 
			
		||||
                'public': image.is_public == "True"}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DetailView(tabs.TabView):
 | 
			
		||||
 
 | 
			
		||||
@@ -20,21 +20,19 @@
 | 
			
		||||
 | 
			
		||||
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 horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CreateSnapshot(forms.SelfHandlingForm):
 | 
			
		||||
    tenant_id = forms.CharField(widget=forms.HiddenInput())
 | 
			
		||||
    instance_id = forms.CharField(label=_("Instance ID"),
 | 
			
		||||
                                  widget=forms.TextInput(
 | 
			
		||||
                                        attrs={'readonly': 'readonly'}))
 | 
			
		||||
@@ -42,14 +40,15 @@ class CreateSnapshot(forms.SelfHandlingForm):
 | 
			
		||||
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        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.
 | 
			
		||||
            instance = api.server_get(request, data['instance_id'])
 | 
			
		||||
            vals = {"name": data['name'], "inst": instance.name}
 | 
			
		||||
            messages.success(request, _('Snapshot "%(name)s" created for '
 | 
			
		||||
                                        'instance "%(inst)s"') % vals)
 | 
			
		||||
            return shortcuts.redirect('horizon:nova:images_and_snapshots:'
 | 
			
		||||
                                      'index')
 | 
			
		||||
            return snapshot
 | 
			
		||||
        except:
 | 
			
		||||
            redirect = reverse("horizon:nova:instances:index")
 | 
			
		||||
            exceptions.handle(request,
 | 
			
		||||
 
 | 
			
		||||
@@ -42,18 +42,6 @@ class SnapshotsViewTests(test.TestCase):
 | 
			
		||||
        self.assertTemplateUsed(res,
 | 
			
		||||
                            '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):
 | 
			
		||||
        server = self.servers.first()
 | 
			
		||||
        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.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \
 | 
			
		||||
                            .AndReturn(snapshot)
 | 
			
		||||
        api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
 | 
			
		||||
        self.mox.ReplayAll()
 | 
			
		||||
 | 
			
		||||
        formData = {'method': 'CreateSnapshot',
 | 
			
		||||
@@ -95,7 +82,6 @@ class SnapshotsViewTests(test.TestCase):
 | 
			
		||||
 | 
			
		||||
        self.mox.StubOutWithMock(api, 'server_get')
 | 
			
		||||
        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) \
 | 
			
		||||
                            .AndRaise(self.exceptions.nova)
 | 
			
		||||
        self.mox.ReplayAll()
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ Views for managing Nova instance snapshots.
 | 
			
		||||
 | 
			
		||||
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 horizon import api
 | 
			
		||||
@@ -39,24 +39,24 @@ LOG = logging.getLogger(__name__)
 | 
			
		||||
class CreateView(forms.ModalFormView):
 | 
			
		||||
    form_class = CreateSnapshot
 | 
			
		||||
    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):
 | 
			
		||||
        redirect = reverse('horizon:nova:instances:index')
 | 
			
		||||
        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}
 | 
			
		||||
        return {"instance_id": self.kwargs["instance_id"]}
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        context = super(CreateView, self).get_context_data(**kwargs)
 | 
			
		||||
        context['instance'] = self.instance
 | 
			
		||||
        context['instance'] = self.get_object()
 | 
			
		||||
        return context
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Create An Image" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create An Image") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include 'nova/images_and_snapshots/images/_create.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
 | 
			
		||||
{% block title %}{% trans "Image Detail "%}{% endblock %}
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title="Image Detail" %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
<div class="row-fluid">
 | 
			
		||||
  <div class="span12">
 | 
			
		||||
  {{ tab_group.render }}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Update Image" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Update Image") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include 'nova/images_and_snapshots/images/_update.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Images & Snapshots" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Images & Snapshots") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  <div class="images">
 | 
			
		||||
    {{ images_table.render }}
 | 
			
		||||
  </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Create Snapshot" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create a Snapshot") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'nova/images_and_snapshots/snapshots/_create.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,13 +20,13 @@
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
@@ -39,10 +39,12 @@ class UpdateInstance(forms.SelfHandlingForm):
 | 
			
		||||
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        try:
 | 
			
		||||
            api.server_update(request, data['instance'], data['name'])
 | 
			
		||||
            server = api.server_update(request, data['instance'], data['name'])
 | 
			
		||||
            messages.success(request,
 | 
			
		||||
                             _('Instance "%s" updated.') % data['name'])
 | 
			
		||||
            return server
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _('Unable to update instance.'))
 | 
			
		||||
 | 
			
		||||
        return shortcuts.redirect('horizon:nova:instances:index')
 | 
			
		||||
            redirect = reverse("horizon:nova:instances:index")
 | 
			
		||||
            exceptions.handle(request,
 | 
			
		||||
                              _('Unable to update instance.'),
 | 
			
		||||
                              redirect=redirect)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
 | 
			
		||||
{% 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 %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n sizeformat %}
 | 
			
		||||
{% block title %}{% trans "Instance Detail" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title="Instance Detail: "|add:instance.name %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
<div class="row-fluid">
 | 
			
		||||
  <div class="span12">
 | 
			
		||||
  {{ tab_group.render }}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Instances" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Instances") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Launch Instance" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Launch Instance") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include 'horizon/common/_workflow.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Update Instance" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Update Instance") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include 'nova/instances/_update.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -491,28 +491,22 @@ class InstanceTests(test.TestCase):
 | 
			
		||||
                              'server_delete',)})
 | 
			
		||||
    def test_create_instance_snapshot(self):
 | 
			
		||||
        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.snapshot_create(IsA(http.HttpRequest),
 | 
			
		||||
                            server.id,
 | 
			
		||||
                            "snapshot1")
 | 
			
		||||
        api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
 | 
			
		||||
                            "snapshot1").AndReturn(self.snapshots.first())
 | 
			
		||||
 | 
			
		||||
        api.snapshot_list_detailed(IsA(http.HttpRequest),
 | 
			
		||||
                                marker=None).AndReturn([[], False])
 | 
			
		||||
                                   marker=None).AndReturn([[], False])
 | 
			
		||||
        api.image_list_detailed(IsA(http.HttpRequest),
 | 
			
		||||
                                marker=None).AndReturn([[], False])
 | 
			
		||||
        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()
 | 
			
		||||
 | 
			
		||||
        formData = {'instance_id': server.id,
 | 
			
		||||
                    'method': 'CreateSnapshot',
 | 
			
		||||
                    'tenant_id': server.tenant_id,
 | 
			
		||||
                    'name': 'snapshot1'}
 | 
			
		||||
        url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
 | 
			
		||||
                      args=[server.id])
 | 
			
		||||
@@ -520,10 +514,6 @@ class InstanceTests(test.TestCase):
 | 
			
		||||
        res = self.client.post(url, formData)
 | 
			
		||||
        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',)})
 | 
			
		||||
    def test_instance_update_get(self):
 | 
			
		||||
        server = self.servers.first()
 | 
			
		||||
@@ -532,12 +522,10 @@ class InstanceTests(test.TestCase):
 | 
			
		||||
 | 
			
		||||
        self.mox.ReplayAll()
 | 
			
		||||
 | 
			
		||||
        url = reverse('horizon:nova:instances:update',
 | 
			
		||||
                      args=[server.id])
 | 
			
		||||
        url = reverse('horizon:nova:instances:update', args=[server.id])
 | 
			
		||||
        res = self.client.get(url)
 | 
			
		||||
 | 
			
		||||
        self.assertTemplateUsed(res,
 | 
			
		||||
                'nova/instances/update.html')
 | 
			
		||||
        self.assertTemplateUsed(res, 'nova/instances/update.html')
 | 
			
		||||
 | 
			
		||||
    @test.create_stubs({api: ('server_get',)})
 | 
			
		||||
    def test_instance_update_get_server_get_exception(self):
 | 
			
		||||
@@ -559,7 +547,9 @@ class InstanceTests(test.TestCase):
 | 
			
		||||
        server = self.servers.first()
 | 
			
		||||
 | 
			
		||||
        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()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ import logging
 | 
			
		||||
 | 
			
		||||
from django import http
 | 
			
		||||
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.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
@@ -126,22 +126,28 @@ class UpdateView(forms.ModalFormView):
 | 
			
		||||
    form_class = UpdateInstance
 | 
			
		||||
    template_name = 'nova/instances/update.html'
 | 
			
		||||
    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):
 | 
			
		||||
        if not hasattr(self, "object"):
 | 
			
		||||
        if not hasattr(self, "_object"):
 | 
			
		||||
            instance_id = self.kwargs['instance_id']
 | 
			
		||||
            try:
 | 
			
		||||
                self.object = api.server_get(self.request, instance_id)
 | 
			
		||||
                self._object = api.server_get(self.request, instance_id)
 | 
			
		||||
            except:
 | 
			
		||||
                redirect = reverse("horizon:nova:instances:index")
 | 
			
		||||
                msg = _('Unable to retrieve instance details.')
 | 
			
		||||
                exceptions.handle(self.request, msg, redirect=redirect)
 | 
			
		||||
        return self.object
 | 
			
		||||
        return self._object
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        return {'instance': self.kwargs['instance_id'],
 | 
			
		||||
                'tenant_id': self.request.user.tenant_id,
 | 
			
		||||
                'name': getattr(self.object, 'name', '')}
 | 
			
		||||
                'name': getattr(self.get_object(), 'name', '')}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DetailView(tabs.TabView):
 | 
			
		||||
 
 | 
			
		||||
@@ -18,14 +18,14 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.utils.text import normalize_newlines
 | 
			
		||||
from django.utils.translation import ugettext as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon.openstack.common import jsonutils
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import workflows
 | 
			
		||||
from horizon.openstack.common import jsonutils
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SelectProjectUserAction(workflows.Action):
 | 
			
		||||
@@ -320,11 +320,15 @@ class SetInstanceDetails(workflows.Step):
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
KEYPAIR_IMPORT_URL = "horizon:nova:access_and_security:keypairs:import"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SetAccessControlsAction(workflows.Action):
 | 
			
		||||
    keypair = forms.ChoiceField(label=_("Keypair"),
 | 
			
		||||
                                required=False,
 | 
			
		||||
                                help_text=_("Which keypair to use for "
 | 
			
		||||
                                            "authentication."))
 | 
			
		||||
    keypair = forms.DynamicChoiceField(label=_("Keypair"),
 | 
			
		||||
                                       required=False,
 | 
			
		||||
                                       help_text=_("Which keypair to use for "
 | 
			
		||||
                                                   "authentication."),
 | 
			
		||||
                                       add_item_link=KEYPAIR_IMPORT_URL)
 | 
			
		||||
    groups = forms.MultipleChoiceField(label=_("Security Groups"),
 | 
			
		||||
                                       required=True,
 | 
			
		||||
                                       initial=["default"],
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Instance Overview{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Overview") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include "horizon/common/_usage_summary.html" %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,12 +15,3 @@
 | 
			
		||||
{% endif %}
 | 
			
		||||
{{ block.super }}
 | 
			
		||||
{% 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.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.forms import ValidationError
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
from ..instances.tables import ACTIVE_STATES
 | 
			
		||||
 | 
			
		||||
@@ -48,19 +48,19 @@ class CreateForm(forms.SelfHandlingForm):
 | 
			
		||||
                                  ' volumes.')
 | 
			
		||||
                raise ValidationError(error_message)
 | 
			
		||||
 | 
			
		||||
            api.volume_create(request, data['size'], data['name'],
 | 
			
		||||
                              data['description'])
 | 
			
		||||
            volume = api.volume_create(request,
 | 
			
		||||
                                       data['size'],
 | 
			
		||||
                                       data['name'],
 | 
			
		||||
                                       data['description'])
 | 
			
		||||
            message = 'Creating volume "%s"' % data['name']
 | 
			
		||||
 | 
			
		||||
            messages.info(request, message)
 | 
			
		||||
            return volume
 | 
			
		||||
        except ValidationError, e:
 | 
			
		||||
            return self.api_error(e.messages[0])
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, ignore=True)
 | 
			
		||||
            return self.api_error(_("Unable to create volume."))
 | 
			
		||||
 | 
			
		||||
        return shortcuts.redirect("horizon:nova:volumes:index")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AttachForm(forms.SelfHandlingForm):
 | 
			
		||||
    instance = forms.ChoiceField(label="Attach to Instance",
 | 
			
		||||
@@ -113,10 +113,12 @@ class AttachForm(forms.SelfHandlingForm):
 | 
			
		||||
                                                    "inst": instance_name,
 | 
			
		||||
                                                    "dev": data['device']}
 | 
			
		||||
            messages.info(request, message)
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            redirect = reverse("horizon:nova:volumes:index")
 | 
			
		||||
            exceptions.handle(request,
 | 
			
		||||
                              _('Unable to attach volume.'))
 | 
			
		||||
        return shortcuts.redirect("horizon:nova:volumes:index")
 | 
			
		||||
                              _('Unable to attach volume.'),
 | 
			
		||||
                              redirect=redirect)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CreateSnapshotForm(forms.SelfHandlingForm):
 | 
			
		||||
@@ -134,15 +136,16 @@ class CreateSnapshotForm(forms.SelfHandlingForm):
 | 
			
		||||
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        try:
 | 
			
		||||
            api.volume_snapshot_create(request,
 | 
			
		||||
                                       data['volume_id'],
 | 
			
		||||
                                       data['name'],
 | 
			
		||||
                                       data['description'])
 | 
			
		||||
            snapshot = api.volume_snapshot_create(request,
 | 
			
		||||
                                                  data['volume_id'],
 | 
			
		||||
                                                  data['name'],
 | 
			
		||||
                                                  data['description'])
 | 
			
		||||
 | 
			
		||||
            message = _('Creating volume snapshot "%s"') % data['name']
 | 
			
		||||
            messages.info(request, message)
 | 
			
		||||
            return snapshot
 | 
			
		||||
        except:
 | 
			
		||||
            redirect = reverse("horizon:nova:images_and_snapshots:index")
 | 
			
		||||
            exceptions.handle(request,
 | 
			
		||||
                              _('Unable to create volume snapshot.'))
 | 
			
		||||
 | 
			
		||||
        return shortcuts.redirect("horizon:nova:images_and_snapshots:index")
 | 
			
		||||
                              _('Unable to create volume snapshot.'),
 | 
			
		||||
                              redirect=redirect)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Manage Volume Attachments{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Manage Volume Attachments") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'nova/volumes/_attach.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Create Volume{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create a Volume") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'nova/volumes/_create.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Create Volume Snapshot" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create a Volume Snapshot") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'nova/volumes/_create_snapshot.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Volume Details" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Volume Detail") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
<div class="row-fluid">
 | 
			
		||||
  <div class="span12">
 | 
			
		||||
  {{ tab_group.render }}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'nova/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Volumes" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Volumes") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block dash_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ from horizon import test
 | 
			
		||||
class VolumeViewTests(test.TestCase):
 | 
			
		||||
    @test.create_stubs({api: ('tenant_quota_usages', 'volume_create',)})
 | 
			
		||||
    def test_create_volume(self):
 | 
			
		||||
        volume = self.volumes.first()
 | 
			
		||||
        usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
 | 
			
		||||
        formData = {'name': u'A Volume I Am Making',
 | 
			
		||||
                    '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),
 | 
			
		||||
                          formData['size'],
 | 
			
		||||
                          formData['name'],
 | 
			
		||||
                          formData['description'])
 | 
			
		||||
                          formData['description']).AndReturn(volume)
 | 
			
		||||
 | 
			
		||||
        self.mox.ReplayAll()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@ Views for managing Nova volumes.
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.core.urlresolvers import reverse_lazy
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from django.utils.datastructures import SortedDict
 | 
			
		||||
 | 
			
		||||
@@ -77,6 +79,7 @@ class DetailView(tabs.TabView):
 | 
			
		||||
class CreateView(forms.ModalFormView):
 | 
			
		||||
    form_class = CreateForm
 | 
			
		||||
    template_name = 'nova/volumes/create.html'
 | 
			
		||||
    success_url = reverse_lazy("horizon:nova:volumes:index")
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **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)
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(self.request)
 | 
			
		||||
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CreateSnapshotView(forms.ModalFormView):
 | 
			
		||||
    form_class = CreateSnapshotForm
 | 
			
		||||
    template_name = 'nova/volumes/create_snapshot.html'
 | 
			
		||||
    success_url = reverse_lazy("horizon:nova:images_and_snapshots:index")
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        return {'volume_id': kwargs['volume_id']}
 | 
			
		||||
        return {'volume_id': self.kwargs['volume_id']}
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        return {'volume_id': self.kwargs["volume_id"]}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditAttachmentsView(tables.DataTableView):
 | 
			
		||||
class EditAttachmentsView(tables.DataTableView, forms.ModalFormView):
 | 
			
		||||
    table_class = AttachmentsTable
 | 
			
		||||
    form_class = AttachForm
 | 
			
		||||
    template_name = 'nova/volumes/attach.html'
 | 
			
		||||
    success_url = reverse_lazy("horizon:nova:volumes:index")
 | 
			
		||||
 | 
			
		||||
    def get_object(self):
 | 
			
		||||
        if not hasattr(self, "_object"):
 | 
			
		||||
@@ -124,35 +129,40 @@ class EditAttachmentsView(tables.DataTableView):
 | 
			
		||||
                              _('Unable to retrieve volume information.'))
 | 
			
		||||
        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):
 | 
			
		||||
        context = super(EditAttachmentsView, self).get_context_data(**kwargs)
 | 
			
		||||
        context['form'] = self.form
 | 
			
		||||
        context['form'] = self.get_form()
 | 
			
		||||
        context['volume'] = self.get_object()
 | 
			
		||||
        if self.request.is_ajax():
 | 
			
		||||
            context['hide'] = True
 | 
			
		||||
        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):
 | 
			
		||||
        self.form, handled = self.handle_form()
 | 
			
		||||
        if handled:
 | 
			
		||||
            return handled
 | 
			
		||||
        # Table action handling
 | 
			
		||||
        handled = self.construct_tables()
 | 
			
		||||
        if handled:
 | 
			
		||||
            return handled
 | 
			
		||||
        context = 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)
 | 
			
		||||
        return self.render_to_response(self.get_context_data(**kwargs))
 | 
			
		||||
 | 
			
		||||
    def post(self, request, *args, **kwargs):
 | 
			
		||||
        form, handled = self.handle_form()
 | 
			
		||||
        if handled:
 | 
			
		||||
            return handled
 | 
			
		||||
        return super(EditAttachmentsView, self).post(request, *args, **kwargs)
 | 
			
		||||
        form = self.get_form()
 | 
			
		||||
        if form.is_valid():
 | 
			
		||||
            return self.form_valid(form)
 | 
			
		||||
        else:
 | 
			
		||||
            return self.get(request, *args, **kwargs)
 | 
			
		||||
 
 | 
			
		||||
@@ -34,14 +34,8 @@ LOG = logging.getLogger(__name__)
 | 
			
		||||
class DownloadX509Credentials(forms.SelfHandlingForm):
 | 
			
		||||
    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):
 | 
			
		||||
        super(DownloadX509Credentials, self).__init__(*args, **kwargs)
 | 
			
		||||
        super(DownloadX509Credentials, self).__init__(request, *args, **kwargs)
 | 
			
		||||
        # Populate tenant choices
 | 
			
		||||
        tenant_choices = []
 | 
			
		||||
        try:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'settings/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Download EC2 Credentials" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Download EC2 Credentials") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block settings_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include "settings/ec2/download_form.html" %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,3 +26,6 @@ LOG = logging.getLogger(__name__)
 | 
			
		||||
class IndexView(forms.ModalFormView):
 | 
			
		||||
    form_class = DownloadX509Credentials
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
@@ -34,14 +34,8 @@ LOG = logging.getLogger(__name__)
 | 
			
		||||
class DownloadOpenRCForm(forms.SelfHandlingForm):
 | 
			
		||||
    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):
 | 
			
		||||
        super(DownloadOpenRCForm, self).__init__(*args, **kwargs)
 | 
			
		||||
        super(DownloadOpenRCForm, self).__init__(request, *args, **kwargs)
 | 
			
		||||
        # Populate tenant choices
 | 
			
		||||
        tenant_choices = []
 | 
			
		||||
        for tenant in api.tenant_list(request):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'settings/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "OpenStack API" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("OpenStack API") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block settings_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include "settings/project/_openrc.html" %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,3 +39,6 @@ class OpenRCView(ModalFormView):
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        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 %}
 | 
			
		||||
{% block title %}{% trans "User Settings" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("User Settings") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block settings_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include "settings/user/_settings.html" %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,3 +26,6 @@ class UserSettingsView(forms.ModalFormView):
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        return {'language': self.request.LANGUAGE_CODE,
 | 
			
		||||
                '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
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
@@ -41,14 +41,16 @@ class CreateFlavor(forms.SelfHandlingForm):
 | 
			
		||||
    eph_gb = forms.IntegerField(label=_("Ephemeral Disk GB"))
 | 
			
		||||
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        api.flavor_create(request,
 | 
			
		||||
                          data['name'],
 | 
			
		||||
                          data['memory_mb'],
 | 
			
		||||
                          data['vcpus'],
 | 
			
		||||
                          data['disk_gb'],
 | 
			
		||||
                          data['flavor_id'],
 | 
			
		||||
                          ephemeral=data['eph_gb'])
 | 
			
		||||
        msg = _('%s was successfully added to flavors.') % data['name']
 | 
			
		||||
        LOG.info(msg)
 | 
			
		||||
        messages.success(request, msg)
 | 
			
		||||
        return shortcuts.redirect('horizon:syspanel:flavors:index')
 | 
			
		||||
        try:
 | 
			
		||||
            flavor = api.flavor_create(request,
 | 
			
		||||
                                       data['name'],
 | 
			
		||||
                                       data['memory_mb'],
 | 
			
		||||
                                       data['vcpus'],
 | 
			
		||||
                                       data['disk_gb'],
 | 
			
		||||
                                       data['flavor_id'],
 | 
			
		||||
                                       ephemeral=data['eph_gb'])
 | 
			
		||||
            msg = _('%s was successfully added to flavors.') % data['name']
 | 
			
		||||
            messages.success(request, msg)
 | 
			
		||||
            return flavor
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _("Unable to create flavor"))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Create Flavors{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create Flavor") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include "syspanel/flavors/_create.html" %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Flavors{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -8,6 +8,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Flavors") refresh_link=refresh_link searchable="true" %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,11 @@
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.core.urlresolvers import reverse_lazy
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from novaclient import exceptions as api_exceptions
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import tables
 | 
			
		||||
from .forms import CreateFlavor
 | 
			
		||||
@@ -43,15 +43,9 @@ class IndexView(tables.DataTableView):
 | 
			
		||||
        flavors = []
 | 
			
		||||
        try:
 | 
			
		||||
            flavors = api.flavor_list(request)
 | 
			
		||||
        except api_exceptions.Unauthorized, e:
 | 
			
		||||
            LOG.exception('Unauthorized attempt to access flavor list.')
 | 
			
		||||
            messages.error(request, _('Unauthorized.'))
 | 
			
		||||
        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)
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request,
 | 
			
		||||
                              _('Unable to retrieve flavor list.'))
 | 
			
		||||
        flavors.sort(key=lambda x: x.id, reverse=True)
 | 
			
		||||
        return flavors
 | 
			
		||||
 | 
			
		||||
@@ -59,11 +53,15 @@ class IndexView(tables.DataTableView):
 | 
			
		||||
class CreateView(forms.ModalFormView):
 | 
			
		||||
    form_class = CreateFlavor
 | 
			
		||||
    template_name = 'syspanel/flavors/create.html'
 | 
			
		||||
    success_url = reverse_lazy('horizon:syspanel:flavors:index')
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        # TODO(tres): Get rid of this hacky bit of nonsense after flavors get
 | 
			
		||||
        # converted to nova client.
 | 
			
		||||
        flavors = api.flavor_list(self.request)
 | 
			
		||||
        # TODO(tres): Get rid of this hacky bit of nonsense after flavors
 | 
			
		||||
        # id handling gets fixed.
 | 
			
		||||
        try:
 | 
			
		||||
            flavors = api.flavor_list(self.request)
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(self.request, ignore=True)
 | 
			
		||||
        if flavors:
 | 
			
		||||
            largest_id = max(flavors, key=lambda f: f.id).id
 | 
			
		||||
            return {'flavor_id': int(largest_id) + 1}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,13 +18,8 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from horizon.dashboards.nova.images_and_snapshots.images import forms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AdminUpdateImageForm(forms.UpdateImageForm):
 | 
			
		||||
    completion_view = 'horizon:syspanel:images:index'
 | 
			
		||||
    pass
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Images" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Images") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
 | 
			
		||||
{% block title %}{% trans "Update Image" %}{% endblock %}
 | 
			
		||||
@@ -7,6 +7,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Update Image") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include 'syspanel/images/_update.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django.core.urlresolvers import reverse_lazy
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
@@ -57,6 +58,7 @@ class IndexView(tables.DataTableView):
 | 
			
		||||
class UpdateView(views.UpdateView):
 | 
			
		||||
    template_name = 'syspanel/images/update.html'
 | 
			
		||||
    form_class = AdminUpdateImageForm
 | 
			
		||||
    success_url = reverse_lazy('horizon:syspanel:images:index')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DetailView(views.DetailView):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Instances" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("All Instances") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n sizeformat %}
 | 
			
		||||
{% block title %}{% trans "Usage Overview" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Overview") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% if monitoring %}
 | 
			
		||||
    <div id="monitoring">
 | 
			
		||||
      <h3>{% trans "Monitoring" %}: </h3>
 | 
			
		||||
 
 | 
			
		||||
@@ -20,13 +20,12 @@
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
@@ -50,10 +49,9 @@ class AddUser(forms.SelfHandlingForm):
 | 
			
		||||
                                     data['user_id'],
 | 
			
		||||
                                     data['role_id'])
 | 
			
		||||
            messages.success(request, _('Successfully added user to project.'))
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            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):
 | 
			
		||||
@@ -68,16 +66,16 @@ class CreateTenant(forms.SelfHandlingForm):
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        try:
 | 
			
		||||
            LOG.info('Creating project with name "%s"' % data['name'])
 | 
			
		||||
            api.tenant_create(request,
 | 
			
		||||
                              data['name'],
 | 
			
		||||
                              data['description'],
 | 
			
		||||
                              data['enabled'])
 | 
			
		||||
            project = api.tenant_create(request,
 | 
			
		||||
                                        data['name'],
 | 
			
		||||
                                        data['description'],
 | 
			
		||||
                                        data['enabled'])
 | 
			
		||||
            messages.success(request,
 | 
			
		||||
                             _('%s was successfully created.')
 | 
			
		||||
                             % data['name'])
 | 
			
		||||
            return project
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _('Unable to create project.'))
 | 
			
		||||
        return shortcuts.redirect('horizon:syspanel:projects:index')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UpdateTenant(forms.SelfHandlingForm):
 | 
			
		||||
@@ -92,17 +90,17 @@ class UpdateTenant(forms.SelfHandlingForm):
 | 
			
		||||
    def handle(self, request, data):
 | 
			
		||||
        try:
 | 
			
		||||
            LOG.info('Updating project with id "%s"' % data['id'])
 | 
			
		||||
            api.tenant_update(request,
 | 
			
		||||
                              data['id'],
 | 
			
		||||
                              data['name'],
 | 
			
		||||
                              data['description'],
 | 
			
		||||
                              data['enabled'])
 | 
			
		||||
            project = api.tenant_update(request,
 | 
			
		||||
                                        data['id'],
 | 
			
		||||
                                        data['name'],
 | 
			
		||||
                                        data['description'],
 | 
			
		||||
                                        data['enabled'])
 | 
			
		||||
            messages.success(request,
 | 
			
		||||
                             _('%s was successfully updated.')
 | 
			
		||||
                             % data['name'])
 | 
			
		||||
            return project
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _('Unable to update project.'))
 | 
			
		||||
        return shortcuts.redirect('horizon:syspanel:projects:index')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UpdateQuotas(forms.SelfHandlingForm):
 | 
			
		||||
@@ -136,6 +134,6 @@ class UpdateQuotas(forms.SelfHandlingForm):
 | 
			
		||||
            messages.success(request,
 | 
			
		||||
                             _('Quotas for %s were successfully updated.')
 | 
			
		||||
                             % data['tenant_id'])
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            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 %}
 | 
			
		||||
{% block title %}{% trans "Add User To Project" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Add User To Project") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'syspanel/projects/_add_user.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Create Project{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create Project") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'syspanel/projects/_create.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Projects{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -8,6 +8,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Projects") refresh_link=refresh_link searchable="true" %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Modify Project Quotas{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Update Project") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'syspanel/projects/_quotas.html' with form=form %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Update Project{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,6 +6,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Update Project") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'syspanel/projects/_update.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n sizeformat %}
 | 
			
		||||
{% block title %}{% trans "Project Usage Overview" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
  </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {% include "horizon/common/_usage_summary.html" %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Project Users{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
  </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  <div id="tenant_users_table">
 | 
			
		||||
    {{ tenant_users_table.render }}
 | 
			
		||||
  </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -51,8 +51,6 @@ class TenantsViewTests(test.BaseAdminViewTests):
 | 
			
		||||
        self.mox.StubOutWithMock(api.keystone, 'tenant_get')
 | 
			
		||||
        self.mox.StubOutWithMock(api.nova, 'tenant_quota_get')
 | 
			
		||||
        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_update(IgnoreArg(), tenant.id, **quota_data)
 | 
			
		||||
        self.mox.ReplayAll()
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
import logging
 | 
			
		||||
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 horizon import api
 | 
			
		||||
@@ -36,6 +36,27 @@ from .tables import TenantsTable, TenantUsersTable, AddUsersTable
 | 
			
		||||
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):
 | 
			
		||||
    table_class = TenantsTable
 | 
			
		||||
    template_name = 'syspanel/projects/index.html'
 | 
			
		||||
@@ -54,28 +75,20 @@ class IndexView(tables.DataTableView):
 | 
			
		||||
class CreateView(forms.ModalFormView):
 | 
			
		||||
    form_class = CreateTenant
 | 
			
		||||
    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
 | 
			
		||||
    template_name = 'syspanel/projects/update.html'
 | 
			
		||||
    context_object_name = 'tenant'
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    success_url = reverse_lazy('horizon:syspanel:projects:index')
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        return {'id': self.object.id,
 | 
			
		||||
                'name': self.object.name,
 | 
			
		||||
                'description': getattr(self.object, "description", ""),
 | 
			
		||||
                'enabled': self.object.enabled}
 | 
			
		||||
        project = self.get_object()
 | 
			
		||||
        return {'id': project.id,
 | 
			
		||||
                'name': project.name,
 | 
			
		||||
                'description': project.description,
 | 
			
		||||
                'enabled': project.enabled}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UsersView(tables.MultiTableView):
 | 
			
		||||
@@ -116,15 +129,14 @@ class UsersView(tables.MultiTableView):
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AddUserView(forms.ModalFormView):
 | 
			
		||||
class AddUserView(TenantContextMixin, forms.ModalFormView):
 | 
			
		||||
    form_class = AddUser
 | 
			
		||||
    template_name = 'syspanel/projects/add_user.html'
 | 
			
		||||
    context_object_name = 'tenant'
 | 
			
		||||
    success_url = 'horizon:syspanel:projects:users'
 | 
			
		||||
 | 
			
		||||
    def get_object(self, *args, **kwargs):
 | 
			
		||||
        return api.keystone.tenant_get(self.request,
 | 
			
		||||
                                       kwargs["tenant_id"],
 | 
			
		||||
                                       admin=True)
 | 
			
		||||
    def get_success_url(self):
 | 
			
		||||
        return reverse(self.success_url,
 | 
			
		||||
                       args=(self.request.POST['tenant_id'],))
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        context = super(AddUserView, self).get_context_data(**kwargs)
 | 
			
		||||
@@ -153,19 +165,19 @@ class AddUserView(forms.ModalFormView):
 | 
			
		||||
                'role_id': getattr(default_role, "id", None)}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QuotasView(forms.ModalFormView):
 | 
			
		||||
class QuotasView(TenantContextMixin, forms.ModalFormView):
 | 
			
		||||
    form_class = UpdateQuotas
 | 
			
		||||
    template_name = 'syspanel/projects/quotas.html'
 | 
			
		||||
    context_object_name = 'tenant'
 | 
			
		||||
 | 
			
		||||
    def get_object(self, *args, **kwargs):
 | 
			
		||||
        return api.keystone.tenant_get(self.request,
 | 
			
		||||
                                       kwargs["tenant_id"],
 | 
			
		||||
                                       admin=True)
 | 
			
		||||
    success_url = reverse_lazy('horizon:syspanel:projects:index')
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        quotas = api.nova.tenant_quota_get(self.request,
 | 
			
		||||
                                           self.kwargs['tenant_id'])
 | 
			
		||||
        try:
 | 
			
		||||
            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 {
 | 
			
		||||
            'tenant_id': self.kwargs['tenant_id'],
 | 
			
		||||
            'metadata_items': quotas.metadata_items,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Quotas{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -8,6 +8,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Default Quotas") refresh_link=refresh_link searchable="true" %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Services{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Services") refresh_link=refresh_link searchable="true" %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
  {{ table.render }}
 | 
			
		||||
{% 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
 | 
			
		||||
 | 
			
		||||
from django import shortcuts
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.forms import ValidationError
 | 
			
		||||
from django.utils.translation import force_unicode, ugettext_lazy as _
 | 
			
		||||
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 exceptions
 | 
			
		||||
from horizon import forms
 | 
			
		||||
from horizon import messages
 | 
			
		||||
from horizon.utils import validators
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -37,7 +36,7 @@ LOG = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
class BaseUserForm(forms.SelfHandlingForm):
 | 
			
		||||
    def __init__(self, request, *args, **kwargs):
 | 
			
		||||
        super(BaseUserForm, self).__init__(*args, **kwargs)
 | 
			
		||||
        super(BaseUserForm, self).__init__(request, *args, **kwargs)
 | 
			
		||||
        # Populate tenant choices
 | 
			
		||||
        tenant_choices = [('', _("Select a project"))]
 | 
			
		||||
 | 
			
		||||
@@ -46,10 +45,6 @@ class BaseUserForm(forms.SelfHandlingForm):
 | 
			
		||||
                tenant_choices.append((tenant.id, tenant.name))
 | 
			
		||||
        self.fields['tenant_id'].choices = tenant_choices
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _instantiate(cls, request, *args, **kwargs):
 | 
			
		||||
        return cls(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def clean(self):
 | 
			
		||||
        '''Check to make sure password fields match.'''
 | 
			
		||||
        data = super(forms.Form, self).clean()
 | 
			
		||||
@@ -103,10 +98,9 @@ class CreateUserForm(BaseUserForm):
 | 
			
		||||
            except:
 | 
			
		||||
                exceptions.handle(request,
 | 
			
		||||
                                  _('Unable to add user to primary project.'))
 | 
			
		||||
            return shortcuts.redirect('horizon:syspanel:users:index')
 | 
			
		||||
            return new_user
 | 
			
		||||
        except:
 | 
			
		||||
            exceptions.handle(request, _('Unable to create user.'))
 | 
			
		||||
            return shortcuts.redirect('horizon:syspanel:users:index')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UpdateUserForm(BaseUserForm):
 | 
			
		||||
@@ -140,7 +134,6 @@ class UpdateUserForm(BaseUserForm):
 | 
			
		||||
        user_is_editable = api.keystone_can_edit_user()
 | 
			
		||||
        user = data.pop('id')
 | 
			
		||||
        tenant = data.pop('tenant_id')
 | 
			
		||||
        data.pop('method')
 | 
			
		||||
 | 
			
		||||
        if user_is_editable:
 | 
			
		||||
            password = data.pop('password')
 | 
			
		||||
@@ -184,4 +177,4 @@ class UpdateUserForm(BaseUserForm):
 | 
			
		||||
            messages.error(request,
 | 
			
		||||
                           _('Unable to update %(attributes)s for the user.')
 | 
			
		||||
                             % {"attributes": ", ".join(failed)})
 | 
			
		||||
        return shortcuts.redirect('horizon:syspanel:users:index')
 | 
			
		||||
        return True
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from horizon import api
 | 
			
		||||
from horizon import messages
 | 
			
		||||
from horizon import tables
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Create User{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -7,6 +7,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Create User") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'syspanel/users/_create.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Users{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -8,6 +8,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Users") refresh_link=refresh_link searchable="true" %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}Update User{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -7,6 +7,6 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Update User") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {% include 'syspanel/users/_update.html' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
 | 
			
		||||
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.translation import ugettext_lazy as _
 | 
			
		||||
from django.views.decorators.debug import sensitive_post_parameters
 | 
			
		||||
@@ -50,33 +50,43 @@ class IndexView(tables.DataTableView):
 | 
			
		||||
class UpdateView(forms.ModalFormView):
 | 
			
		||||
    form_class = UpdateUserForm
 | 
			
		||||
    template_name = 'syspanel/users/update.html'
 | 
			
		||||
    context_object_name = 'user'
 | 
			
		||||
    success_url = reverse_lazy('horizon:syspanel:users:index')
 | 
			
		||||
 | 
			
		||||
    @method_decorator(sensitive_post_parameters('password',
 | 
			
		||||
                                                'confirm_password'))
 | 
			
		||||
    def dispatch(self, *args, **kwargs):
 | 
			
		||||
        return super(UpdateView, self).dispatch(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_object(self, *args, **kwargs):
 | 
			
		||||
        user_id = kwargs['user_id']
 | 
			
		||||
        try:
 | 
			
		||||
            return api.user_get(self.request, user_id, admin=True)
 | 
			
		||||
        except:
 | 
			
		||||
            redirect = reverse("horizon:syspanel:users:index")
 | 
			
		||||
            exceptions.handle(self.request,
 | 
			
		||||
                              _('Unable to update user.'),
 | 
			
		||||
                              redirect=redirect)
 | 
			
		||||
    def get_object(self):
 | 
			
		||||
        if not hasattr(self, "_object"):
 | 
			
		||||
            try:
 | 
			
		||||
                self._object = api.user_get(self.request,
 | 
			
		||||
                                            self.kwargs['user_id'],
 | 
			
		||||
                                            admin=True)
 | 
			
		||||
            except:
 | 
			
		||||
                redirect = reverse("horizon:syspanel:users:index")
 | 
			
		||||
                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):
 | 
			
		||||
        return {'id': self.object.id,
 | 
			
		||||
                'name': getattr(self.object, 'name', None),
 | 
			
		||||
                'tenant_id': getattr(self.object, 'tenantId', None),
 | 
			
		||||
                'email': getattr(self.object, 'email', '')}
 | 
			
		||||
        user = self.get_object()
 | 
			
		||||
        return {'id': user.id,
 | 
			
		||||
                'name': user.name,
 | 
			
		||||
                'tenant_id': getattr(user, 'tenantId', None),
 | 
			
		||||
                'email': user.email}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CreateView(forms.ModalFormView):
 | 
			
		||||
    form_class = CreateUserForm
 | 
			
		||||
    template_name = 'syspanel/users/create.html'
 | 
			
		||||
    success_url = reverse_lazy('horizon:syspanel:users:index')
 | 
			
		||||
 | 
			
		||||
    @method_decorator(sensitive_post_parameters('password',
 | 
			
		||||
                                                'confirm_password'))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Volume Details" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Volume Detail") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
<div class="row-fluid">
 | 
			
		||||
  <div class="span12">
 | 
			
		||||
  {{ tab_group.render }}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{% extends 'syspanel/base.html' %}
 | 
			
		||||
{% extends 'base.html' %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% block title %}{% trans "Volumes" %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  {% include "horizon/common/_page_header.html" with title=_("Volumes") %}
 | 
			
		||||
{% endblock page_header %}
 | 
			
		||||
 | 
			
		||||
{% block syspanel_main %}
 | 
			
		||||
{% block main %}
 | 
			
		||||
    {{ table.render }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,3 +22,4 @@ from django.forms import widgets
 | 
			
		||||
# Convenience imports for public API components.
 | 
			
		||||
from .base import SelfHandlingForm, DateForm
 | 
			
		||||
from .views import ModalFormView
 | 
			
		||||
from .fields import DynamicTypedChoiceField, DynamicChoiceField
 | 
			
		||||
 
 | 
			
		||||
@@ -18,41 +18,22 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.forms.forms import NON_FIELD_ERRORS
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.utils import dates, timezone
 | 
			
		||||
 | 
			
		||||
from horizon import exceptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SelfHandlingForm(forms.Form):
 | 
			
		||||
    """
 | 
			
		||||
    A base :class:`Form <django:django.forms.Form>` class which includes
 | 
			
		||||
    processing logic in its subclasses and handling errors raised during
 | 
			
		||||
    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.
 | 
			
		||||
    processing logic in its subclasses.
 | 
			
		||||
    """
 | 
			
		||||
    method = forms.CharField(required=True, widget=forms.HiddenInput)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        initial = kwargs.pop('initial', {})
 | 
			
		||||
        initial['method'] = self.__class__.__name__
 | 
			
		||||
        kwargs['initial'] = initial
 | 
			
		||||
    def __init__(self, request, *args, **kwargs):
 | 
			
		||||
        self.request = request
 | 
			
		||||
        if not hasattr(self, "handle"):
 | 
			
		||||
            raise NotImplementedError("%s does not define a handle method."
 | 
			
		||||
                                      % self.__class__.__name__)
 | 
			
		||||
        super(SelfHandlingForm, self).__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def api_error(self, message):
 | 
			
		||||
@@ -64,52 +45,6 @@ class SelfHandlingForm(forms.Form):
 | 
			
		||||
        """
 | 
			
		||||
        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):
 | 
			
		||||
    """ A simple form for selecting a start date. """
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user