From d3d71ff7e8f57169eb28a79e39840f01a1ac735d Mon Sep 17 00:00:00 2001 From: Tres Henry Date: Tue, 1 Nov 2011 13:37:24 -0700 Subject: [PATCH] Fixing some pep8 issues with volumes. Change-Id: I908c7457e47ba7da3d3df37679298ed98ce097b0 --- django-openstack/django_openstack/api.py | 41 +++- .../django_openstack/dash/urls.py | 13 +- .../django_openstack/dash/views/instances.py | 2 + .../django_openstack/dash/views/volumes.py | 182 ++++++++++++++++++ .../django_openstack/dash/_sidebar.html | 1 + .../dash/instances/detail.html | 54 ++++-- .../dash/volumes/_attach_form.html | 14 ++ .../dash/volumes/_delete.html | 10 + .../dash/volumes/_detach_form.html | 11 ++ .../django_openstack/dash/volumes/_form.html | 13 ++ .../django_openstack/dash/volumes/_list.html | 58 ++++++ .../django_openstack/dash/volumes/attach.html | 27 +++ .../django_openstack/dash/volumes/create.html | 35 ++++ .../django_openstack/dash/volumes/detail.html | 52 +++++ .../django_openstack/dash/volumes/index.html | 26 +++ .../django_openstack/tests/api_tests.py | 86 +++++++++ 16 files changed, 604 insertions(+), 21 deletions(-) create mode 100644 django-openstack/django_openstack/dash/views/volumes.py create mode 100644 django-openstack/django_openstack/templates/django_openstack/dash/volumes/_attach_form.html create mode 100644 django-openstack/django_openstack/templates/django_openstack/dash/volumes/_delete.html create mode 100644 django-openstack/django_openstack/templates/django_openstack/dash/volumes/_detach_form.html create mode 100644 django-openstack/django_openstack/templates/django_openstack/dash/volumes/_form.html create mode 100644 django-openstack/django_openstack/templates/django_openstack/dash/volumes/_list.html create mode 100644 django-openstack/django_openstack/templates/django_openstack/dash/volumes/attach.html create mode 100644 django-openstack/django_openstack/templates/django_openstack/dash/volumes/create.html create mode 100644 django-openstack/django_openstack/templates/django_openstack/dash/volumes/detail.html create mode 100644 django-openstack/django_openstack/templates/django_openstack/dash/volumes/index.html diff --git a/django-openstack/django_openstack/api.py b/django-openstack/django_openstack/api.py index 7b096178c81..60b9f16e3b0 100644 --- a/django-openstack/django_openstack/api.py +++ b/django-openstack/django_openstack/api.py @@ -166,6 +166,12 @@ class KeyPair(APIResourceWrapper): _attrs = ['fingerprint', 'name', 'private_key'] +class Volume(APIResourceWrapper): + """Nova Volume representation""" + _attrs = ['id', 'status', 'displayName', 'size', 'volumeType', 'createdAt', + 'attachments', 'displayDescription'] + + class Server(APIResourceWrapper): """Simple wrapper around openstackx.extras.server.Server @@ -555,6 +561,37 @@ def keypair_list(request): return [KeyPair(key) for key in novaclient(request).keypairs.list()] +def volume_list(request): + return [Volume(vol) for vol in novaclient(request).volumes.list()] + + +def volume_get(request, volume_id): + return Volume(novaclient(request).volumes.get(volume_id)) + + +def volume_instance_list(request, instance_id): + return novaclient(request).volumes.get_server_volumes(instance_id) + + +def volume_create(request, size, name, description): + return Volume(novaclient(request).volumes.create( + size, name, description)) + + +def volume_delete(request, volume_id): + novaclient(request).volumes.delete(volume_id) + + +def volume_attach(request, volume_id, instance_id, device): + novaclient(request).volumes.create_server_volume( + instance_id, volume_id, device) + + +def volume_detach(request, instance_id, attachment_id): + novaclient(request).volumes.delete_server_volume( + instance_id, attachment_id) + + def server_create(request, name, image, flavor, key_name, user_data, security_groups): return Server(novaclient(request).servers.create( @@ -991,8 +1028,8 @@ def service(self): self.service_list = service_list(self.request) except api_exceptions.ApiException, e: self.service_list = [] - LOG.exception('ApiException fetching service list ' - 'in instance usage') + LOG.exception('ApiException fetching service list in instance ' + 'usage') messages.error(self.request, _('Unable to get service info: %s') % e.message) return diff --git a/django-openstack/django_openstack/dash/urls.py b/django-openstack/django_openstack/dash/urls.py index 629e9863fd7..82c3a5f2ba2 100644 --- a/django-openstack/django_openstack/dash/urls.py +++ b/django-openstack/django_openstack/dash/urls.py @@ -20,12 +20,13 @@ from django.conf.urls.defaults import * -SECURITY_GROUPS = r('^(?P[^/]+)/security_groups/' - '(?P[^/]+)/%s$') +SECURITY_GROUPS = r'^(?P[^/]+)/security_groups/' \ + '(?P[^/]+)/%s$' INSTANCES = r'^(?P[^/]+)/instances/(?P[^/]+)/%s$' IMAGES = r'^(?P[^/]+)/images/(?P[^/]+)/%s$' KEYPAIRS = r'^(?P[^/]+)/keypairs/%s$' SNAPSHOTS = r'^(?P[^/]+)/snapshots/(?P[^/]+)/%s$' +VOLUMES = r'^(?P[^/]+)/volumes/(?P[^/]+)/%s$' CONTAINERS = r'^(?P[^/]+)/containers/%s$' FLOATING_IPS = r'^(?P[^/]+)/floating_ips/(?P[^/]+)/%s$' OBJECTS = r'^(?P[^/]+)/containers/(?P[^/]+)/%s$' @@ -78,6 +79,14 @@ url(SNAPSHOTS % 'create', 'create', name='dash_snapshots_create'), ) +urlpatterns += patterns('django_openstack.dash.views.volumes', + url(r'^(?P[^/]+)/volumes/$', 'index', name='dash_volumes'), + url(r'^(?P[^/]+)/volumes/create', 'create', + name='dash_volumes_create'), + url(VOLUMES % 'attach', 'attach', name='dash_volumes_attach'), + url(VOLUMES % 'detail', 'detail', name='dash_volumes_detail'), +) + # Swift containers and objects. urlpatterns += patterns('django_openstack.dash.views.containers', url(CONTAINERS % '', 'index', name='dash_containers'), diff --git a/django-openstack/django_openstack/dash/views/instances.py b/django-openstack/django_openstack/dash/views/instances.py index a7c3478f62d..f4a05e3c0ad 100644 --- a/django-openstack/django_openstack/dash/views/instances.py +++ b/django-openstack/django_openstack/dash/views/instances.py @@ -293,6 +293,7 @@ def update(request, tenant_id, instance_id): def detail(request, tenant_id, instance_id): try: instance = api.server_get(request, instance_id) + volumes = api.volume_instance_list(request, instance_id) try: console = api.console_create(request, instance_id, 'vnc') vnc_url = "%s&title=%s(%s)" % (console.output, @@ -316,4 +317,5 @@ def detail(request, tenant_id, instance_id): 'django_openstack/dash/instances/detail.html', { 'instance': instance, 'vnc_url': vnc_url, + 'volumes': volumes }, context_instance=template.RequestContext(request)) diff --git a/django-openstack/django_openstack/dash/views/volumes.py b/django-openstack/django_openstack/dash/views/volumes.py new file mode 100644 index 00000000000..69b225b3c38 --- /dev/null +++ b/django-openstack/django_openstack/dash/views/volumes.py @@ -0,0 +1,182 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Nebula, Inc. +# All rights reserved. + +""" +Views for managing Nova volumes. +""" + +import logging + +from django import template +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.shortcuts import redirect, render_to_response +from django.utils.translation import ugettext as _ + +from django_openstack import api +from django_openstack import forms +from novaclient import exceptions as novaclient_exceptions + + +LOG = logging.getLogger('django_openstack.dash.views.volumes') + + +class CreateForm(forms.SelfHandlingForm): + name = forms.CharField(max_length="255", label="Volume Name") + description = forms.CharField(widget=forms.Textarea, + label=_("Description"), required=False) + size = forms.IntegerField(min_value=1, label="Size (GB)") + + def handle(self, request, data): + try: + api.volume_create(request, data['size'], data['name'], + data['description']) + message = 'Creating volume "%s"' % data['name'] + LOG.info(message) + messages.info(request, message) + except novaclient_exceptions.ClientException, e: + LOG.exception("ClientException in CreateVolume") + messages.error(request, + _('Error Creating Volume: %s') % e.message) + return redirect(request.build_absolute_uri()) + + +class DeleteForm(forms.SelfHandlingForm): + volume_id = forms.CharField(widget=forms.HiddenInput()) + volume_name = forms.CharField(widget=forms.HiddenInput()) + + def handle(self, request, data): + try: + api.volume_delete(request, data['volume_id']) + message = 'Deleting volume "%s"' % data['volume_id'] + LOG.info(message) + messages.info(request, message) + except novaclient_exceptions.ClientException, e: + LOG.exception("ClientException in DeleteVolume") + messages.error(request, + _('Error deleting volume: %s') % e.message) + return redirect(request.build_absolute_uri()) + + +class AttachForm(forms.SelfHandlingForm): + volume_id = forms.CharField(widget=forms.HiddenInput()) + device = forms.CharField(label="Device Name", initial="/dev/vdb") + + def __init__(self, *args, **kwargs): + super(AttachForm, self).__init__(*args, **kwargs) + instance_list = kwargs.get('initial', {}).get('instance_list', []) + self.fields['instance'] = forms.ChoiceField( + choices=instance_list, + label="Attach to Instance", + help_text="Select an instance to attach to.") + + def handle(self, request, data): + try: + api.volume_attach(request, data['volume_id'], data['instance'], + data['device']) + message = (_('Attaching volume %s to instance %s at %s') % + (data['volume_id'], data['instance'], + data['device'])) + LOG.info(message) + messages.info(request, message) + except novaclient_exceptions.ClientException, e: + LOG.exception("ClientException in AttachVolume") + messages.error(request, + _('Error attaching volume: %s') % e.message) + return redirect(request.build_absolute_uri()) + + +class DetachForm(forms.SelfHandlingForm): + volume_id = forms.CharField(widget=forms.HiddenInput()) + instance_id = forms.CharField(widget=forms.HiddenInput()) + attachment_id = forms.CharField(widget=forms.HiddenInput()) + + def handle(self, request, data): + try: + api.volume_detach(request, data['instance_id'], + data['attachment_id']) + message = (_('Detaching volume %s from instance %s') % + (data['volume_id'], data['instance_id'])) + LOG.info(message) + messages.info(request, message) + except novaclient_exceptions.ClientException, e: + LOG.exception("ClientException in DetachVolume") + messages.error(request, + _('Error detaching volume: %s') % e.message) + return redirect(request.build_absolute_uri()) + + +@login_required +def index(request, tenant_id): + delete_form, handled = DeleteForm.maybe_handle(request) + detach_form, handled = DetachForm.maybe_handle(request) + + if handled: + return handled + + try: + volumes = api.volume_list(request) + except novaclient_exceptions.ClientException, e: + volumes = [] + LOG.exception("ClientException in volume index") + messages.error(request, _('Error fetching volumes: %s') % e.message) + + return render_to_response('django_openstack/dash/volumes/index.html', { + 'volumes': volumes, 'delete_form': delete_form, + 'detach_form': detach_form + }, context_instance=template.RequestContext(request)) + + +@login_required +def detail(request, tenant_id, volume_id): + try: + volume = api.volume_get(request, volume_id) + attachment = volume.attachments[0] + if attachment: + instance = api.server_get( + request, volume.attachments[0]['serverId']) + else: + instance = None + except novaclient_exceptions.ClientException, e: + LOG.exception("ClientException in volume get") + messages.error(request, _('Error fetching volume: %s') % e.message) + return redirect('dash_volumes', tenant_id) + + return render_to_response('django_openstack/dash/volumes/detail.html', { + 'volume': volume, + 'attachment': attachment, + 'instance': instance + }, context_instance=template.RequestContext(request)) + + +@login_required +def create(request, tenant_id): + create_form, handled = CreateForm.maybe_handle(request) + + if handled: + return handled + + return render_to_response('django_openstack/dash/volumes/create.html', { + 'create_form': create_form + }, context_instance=template.RequestContext(request)) + + +@login_required +def attach(request, tenant_id, volume_id): + + def instances(): + insts = api.server_list(request) + return [(inst.id, '%s (Instance %s)' % (inst.name, inst.id)) + for inst in insts] + + attach_form, handled = AttachForm.maybe_handle( + request, initial={'instance_list': instances()}) + + if handled: + return handled + + return render_to_response('django_openstack/dash/volumes/attach.html', { + 'attach_form': attach_form, 'volume_id': volume_id + }, context_instance=template.RequestContext(request)) diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/_sidebar.html b/django-openstack/django_openstack/templates/django_openstack/dash/_sidebar.html index 0f877f4f9bc..3a398a3f34a 100644 --- a/django-openstack/django_openstack/templates/django_openstack/dash/_sidebar.html +++ b/django-openstack/django_openstack/templates/django_openstack/dash/_sidebar.html @@ -11,6 +11,7 @@

{% trans "Manage Compute"%}

  • {% trans "Keypairs"%}
  • {% trans "Floating IPs"%}
  • {% trans "Security Groups"%}
  • +
  • {% trans "Volumes"%}
  • {% if quantum_configured %}
  • {% trans "Networks"%}
  • {% endif %} diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/instances/detail.html b/django-openstack/django_openstack/templates/django_openstack/dash/instances/detail.html index 25b58bd403e..414017f89a8 100644 --- a/django-openstack/django_openstack/templates/django_openstack/dash/instances/detail.html +++ b/django-openstack/django_openstack/templates/django_openstack/dash/instances/detail.html @@ -1,4 +1,5 @@ {% extends 'django_openstack/dash/base.html' %} +{% load i18n %} {% block sidebar %} {% with current_sidebar="instances" %} @@ -13,9 +14,9 @@ {% block dash_main %}
    @@ -23,33 +24,52 @@
    • -

      Status

      +

      {% trans "Status" %}

        -
      • Status: {{instance.status}}
      • -
      • Instance Name: {{instance.name}}
      • -
      • Instance ID: {{instance.id}}
      • +
      • {% trans "Status:" %} {{instance.status}}
      • +
      • {% trans "Instance Name:" %} {{instance.name}}
      • +
      • {% trans "Instance ID:" %} {{instance.id}}
    • -

      Specs

      +

      {% trans "Specs" %}

        -
      • RAM: {{instance.attrs.memory_mb}}
      • -
      • VCPUs: {{instance.attrs.vcpus}} VCPU
      • -
      • Disk: {{instance.attrs.disk_gb}}GB Disk
      • +
      • {% trans "RAM:" %} {{instance.attrs.memory_mb}}
      • +
      • {% trans "VCPUs:" %} {{instance.attrs.vcpus}} {% trans "VCPU" %}
      • +
      • {% trans "Disk:" %} {{instance.attrs.disk_gb}}{% trans "GB Disk" %}
    • -

      Meta

      +

      {% trans "Meta" %}

        -
      • Key name: {{instance.attrs.key_name}}
      • -
      • Security Group(s): {% for group in instance.attrs.security_groups %}{{group}}, {% endfor %}
      • -
      • Image Name: {{instance.image_name}}
      • +
      • {% trans "Key name:" %} {{instance.attrs.key_name}}
      • +
      • {% trans "Security Group(s):" %} {% for group in instance.attrs.security_groups %}{{group}}, {% endfor %}
      • +
      • {% trans "Image Name:" %} {{instance.image_name}}
      • +
      +
      +
    • +
    • +
      +

      {% trans "Volumes" %}

      +
    • @@ -57,14 +77,14 @@

      Meta

    - + {% endblock %} diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_attach_form.html b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_attach_form.html new file mode 100644 index 00000000000..6ad7d603321 --- /dev/null +++ b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_attach_form.html @@ -0,0 +1,14 @@ +{% load i18n %} +
    +
    + {% csrf_token %} + {% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %} + {% for field in form.visible_fields %} + {{ field.label_tag }} + {{ field.errors }} + {{ field }} + {% endfor %} + + +
    +
    diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_delete.html b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_delete.html new file mode 100644 index 00000000000..77310986052 --- /dev/null +++ b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_delete.html @@ -0,0 +1,10 @@ +{% load i18n %} +
    + {% csrf_token %} + {% for hidden in form.hidden_fields %} + {{ hidden }} + {% endfor %} + + + +
    diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_detach_form.html b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_detach_form.html new file mode 100644 index 00000000000..0f383a879a4 --- /dev/null +++ b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_detach_form.html @@ -0,0 +1,11 @@ +{% load i18n %} +
    + {% csrf_token %} + {% for hidden in form.hidden_fields %} + {{ hidden }} + {% endfor %} + + + + +
    diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_form.html b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_form.html new file mode 100644 index 00000000000..758fb185c36 --- /dev/null +++ b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_form.html @@ -0,0 +1,13 @@ +{% load i18n %} +
    +
    + {% csrf_token %} + {% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %} + {% for field in form.visible_fields %} + {{ field.label_tag }} + {{ field.errors }} + {{ field }} + {% endfor %} + +
    +
    diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_list.html b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_list.html new file mode 100644 index 00000000000..18910ff7609 --- /dev/null +++ b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/_list.html @@ -0,0 +1,58 @@ +{% load i18n %} +{% load parse_date %} + + + + + + + + + + + {% for volume in volumes %} + + + + + + + + + + {% endfor %} +
    {% trans "ID" %}{% trans "Name" %}{% trans "Status" %}{% trans "Size" %}{% trans "Created" %}{% trans "Attached To" %}{% trans "Actions" %}
    {{ volume.id }} + + {{ volume.displayName }} + + {{ volume.status|capfirst }}{{ volume.size }} {% trans "GB" %}{{ volume.createdAt|parse_date }} + {% for attachment in volume.attachments %} + {% if attachment %} + + + Instance {{ attachment.serverId }} + ({{ attachment.device }}) + + {% else %} + {% trans "Not Attached" %} + {% endif %} + {% endfor %} + +
      + {% if volume.status == "in-use" %} + {% for attachment in volume.attachments %} +
    • + {% include "django_openstack/dash/volumes/_detach_form.html" with form=detach_form %} +
    • + {% endfor %} + {% endif %} + {% if volume.status == "available" %} +
    • + {% trans "Attach" %} +
    • +
    • + {% include "django_openstack/dash/volumes/_delete.html" with form=delete_form %} +
    • + {% endif %} +
    +
    diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/volumes/attach.html b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/attach.html new file mode 100644 index 00000000000..ec0456f7920 --- /dev/null +++ b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/attach.html @@ -0,0 +1,27 @@ +{% extends 'django_openstack/dash/base.html' %} +{% load i18n %} + +{% block sidebar %} + {% with current_sidebar="volumes" %} + {{ block.super }} + {% endwith %} +{% endblock %} + +{% block page_header %} + {% include "django_openstack/common/_page_header.html" with title=_("Attach a Volume") %} +{% endblock page_header %} + +{% block dash_main %} +
    +
    + {% include 'django_openstack/dash/volumes/_attach_form.html' with form=attach_form %} +

    << {% trans "Return to volumes list" %}

    +
    + +
    +

    {% trans "Description" %}:

    +

    {% trans "Attach a volume to an instance." %}

    +
    +
     
    +
    +{% endblock %} diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/volumes/create.html b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/create.html new file mode 100644 index 00000000000..9f57c8b650f --- /dev/null +++ b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/create.html @@ -0,0 +1,35 @@ +{% extends 'django_openstack/dash/base.html' %} +{%load i18n%} + +{% block sidebar %} + {% with current_sidebar="volumes" %} + {{ block.super }} + {% endwith %} +{% endblock %} + +{% block headerjs %} + +{% endblock %} + +{% block page_header %} + {% include "django_openstack/common/_page_header.html" with title=_("Create a Volume") %} +{% endblock page_header %} + +{% block dash_main %} +
    +
    + {% include 'django_openstack/dash/volumes/_form.html' with form=create_form %} +

    << {% trans "Return to volumes list"%}

    +
    + +
    +

    {% trans "Description" %}:

    +

    {% trans "Volumes are block devices that can be attached to instances." %}

    +
    +
     
    +
    +{% endblock %} diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/volumes/detail.html b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/detail.html new file mode 100644 index 00000000000..edd673481f3 --- /dev/null +++ b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/detail.html @@ -0,0 +1,52 @@ +{% extends 'django_openstack/dash/base.html' %} +{% load i18n %} +{% load parse_date %} + +{% block sidebar %} + {% with current_sidebar="volumes" %} + {{block.super}} + {% endwith %} +{% endblock %} + +{% block page_header %} + {# to make searchable false, just remove it from the include statement #} + {% include "django_openstack/common/_page_header.html" with title="Volume Detail: "|add:volume.displayName %} +{% endblock page_header %} + +{% block dash_main %} + + +
    +
    +
      +
    • +
      +

      {% trans "Details" %}

      +
        +
      • {% trans "ID:" %} {{ volume.id }}
      • +
      • {% trans "Name:" %} {{ volume.displayName }}
      • +
      • {% trans "Size:" %} {{ volume.size }} {% trans "GB" %}
      • +
      • {% trans "Description:" %} {{ volume.displayDescription }}
      • +
      • {% trans "Status:" %} {{ volume.status|capfirst }}
      • +
      • {% trans "Created:" %} {{ volume.createdAt|parse_date }}
      • +
      • + {% trans "Attached To:" %} + {% if instance %} + + {% trans "Instance" %} {{ instance.id }} + ({{ instance.name }}) + + {% trans "on" %} {{ attachment.device }} + {% else %} + {% trans "Not Attached" %} + {% endif %} +
      • +
      +
      +
    • +
    +
    +
    +{% endblock %} diff --git a/django-openstack/django_openstack/templates/django_openstack/dash/volumes/index.html b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/index.html new file mode 100644 index 00000000000..e6fbf98317f --- /dev/null +++ b/django-openstack/django_openstack/templates/django_openstack/dash/volumes/index.html @@ -0,0 +1,26 @@ +{% extends 'django_openstack/dash/base.html' %} +{% load i18n %} + +{% block sidebar %} + {% with current_sidebar="volumes" %} + {{block.super}} + {% endwith %} +{% endblock %} + +{% block page_header %} + {% url dash_volumes request.user.tenant_id as refresh_link %} + {# to make searchable false, just remove it from the include statement #} + {% include "django_openstack/common/_page_header.html" with title=_("Volumes") refresh_link=refresh_link searchable="true" %} +{% endblock page_header %} + +{% block dash_main %} + {% if volumes %} + {% include 'django_openstack/dash/volumes/_list.html' %} + {% else %} +
    +

    {% trans "Info"%}

    +

    {% blocktrans %}There are currently no volumes.{% endblocktrans %}

    +
    + {% endif %} + {% trans "Create New Volume" %} +{% endblock %} diff --git a/django-openstack/django_openstack/tests/api_tests.py b/django-openstack/django_openstack/tests/api_tests.py index 43b0cae8335..f50d214a599 100644 --- a/django-openstack/django_openstack/tests/api_tests.py +++ b/django-openstack/django_openstack/tests/api_tests.py @@ -971,6 +971,92 @@ def test_server_get(self): self.mox.VerifyAll() +class VolumeTests(APITestCase): + def setUp(self): + super(VolumeTests, self).setUp() + volume = api.Volume(APIResource.get_instance()) + volume.id = 1 + volume.displayName = "displayName" + volume.attachments = [{"device": "/dev/vdb", + "serverId": 1, + "id": 1, + "volumeId": 1}] + self.volume = volume + self.volumes = [volume, ] + + self.novaclient = self.stub_novaclient() + self.novaclient.volumes = self.mox.CreateMockAnything() + + def test_volume_list(self): + self.novaclient.volumes.list().AndReturn(self.volumes) + self.mox.ReplayAll() + + volumes = api.volume_list(self.request) + + self.assertIsInstance(volumes[0], api.Volume) + self.mox.VerifyAll() + + def test_volume_get(self): + self.novaclient.volumes.get(IsA(int)).AndReturn(self.volume) + self.mox.ReplayAll() + + volume = api.volume_get(self.request, 1) + + self.assertIsInstance(volume, api.Volume) + self.mox.VerifyAll() + + def test_volume_instance_list(self): + self.novaclient.volumes.get_server_volumes(IsA(int)).AndReturn( + self.volume.attachments) + self.mox.ReplayAll() + + attachments = api.volume_instance_list(self.request, 1) + + self.assertEqual(attachments, self.volume.attachments) + self.mox.VerifyAll() + + def test_volume_create(self): + self.novaclient.volumes.create(IsA(int), IsA(str), IsA(str)).AndReturn( + self.volume) + self.mox.ReplayAll() + + new_volume = api.volume_create(self.request, + 10, + "new volume", + "new description") + + self.assertIsInstance(new_volume, api.Volume) + self.mox.VerifyAll() + + def test_volume_delete(self): + self.novaclient.volumes.delete(IsA(int)) + self.mox.ReplayAll() + + ret_val = api.volume_delete(self.request, 1) + + self.assertIsNone(ret_val) + self.mox.VerifyAll() + + def test_volume_attach(self): + self.novaclient.volumes.create_server_volume( + IsA(int), IsA(int), IsA(str)) + self.mox.ReplayAll() + + ret_val = api.volume_attach(self.request, 1, 1, "/dev/vdb") + + self.assertIsNone(ret_val) + self.mox.VerifyAll() + + def test_volume_detach(self): + self.novaclient.volumes.delete_server_volume(IsA(int), IsA(int)) + self.mox.ReplayAll() + + ret_val = api.volume_detach(self.request, 1, 1) + + self.assertIsNone(ret_val) + self.mox.VerifyAll() + + class APIExtensionTests(APITestCase): def setUp(self):