Cinder REST API for angular front end
This is a very early cut at the REST API needed to support the create instance wizard work. Partially Implements: blueprint launch-instance-redesign Co-Authored-By: Michael Hagedorn <mike.hagedorn@hp.com> Co-Authored-By: Richard Jones <r1chardj0n3s@gmail.com> Co-Authored-By: Travis Tripp <travis.tripp@hp.com> Change-Id: I0c37474e81f5ee9893768974e47a20532c535b91
This commit is contained in:
parent
477faf4c0b
commit
468ce38418
83
horizon/static/horizon/js/angular/services/hz.api.cinder.js
Normal file
83
horizon/static/horizon/js/angular/services/hz.api.cinder.js
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2015 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name hz.api.cinderAPI
|
||||
* @description Provides direct access to Cinder APIs.
|
||||
*/
|
||||
function CinderAPI(apiService) {
|
||||
|
||||
// Volumes
|
||||
|
||||
/**
|
||||
* @name hz.api.cinderAPI.getVolumes
|
||||
* @description
|
||||
* Get a list of volumes.
|
||||
*
|
||||
* The listing result is an object with property "items." Each item is
|
||||
* a volume.
|
||||
*
|
||||
* @param {Object} params
|
||||
* Query parameters. Optional.
|
||||
*
|
||||
* @param {string} param.search_opts
|
||||
* Filters to pass through the API.
|
||||
* For example, "status": "available" will show all available volumes.
|
||||
*/
|
||||
this.getVolumes = function(params) {
|
||||
var config = (params) ? {'params': params} : {};
|
||||
return apiService.get('/api/cinder/volumes/' + config)
|
||||
.error(function () {
|
||||
horizon.alert('error', gettext('Unable to retrieve volumes.'));
|
||||
});
|
||||
};
|
||||
|
||||
// Volume Snapshots
|
||||
|
||||
/**
|
||||
* @name hz.api.cinderAPI.getVolumeSnapshots
|
||||
* @description
|
||||
* Get a list of volume snapshots.
|
||||
*
|
||||
* The listing result is an object with property "items." Each item is
|
||||
* a volume snapshot.
|
||||
*
|
||||
* @param {Object} params
|
||||
* Query parameters. Optional.
|
||||
*
|
||||
* @param {string} param.search_opts
|
||||
* Filters to pass through the API.
|
||||
* For example, "status": "available" will show all available volume
|
||||
* snapshots.
|
||||
*/
|
||||
this.getVolumeSnapshots = function(params) {
|
||||
var config = (params) ? {'params': params} : {};
|
||||
return apiService.get('/api/cinder/volsnaps/' + config)
|
||||
.error(function () {
|
||||
horizon.alert('error',
|
||||
gettext('Unable to retrieve volume snapshots.'));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Register it with the API module so that anybody using the
|
||||
// API module will have access to the Cinder APIs.
|
||||
angular.module('hz.api')
|
||||
.service('cinderAPI', ['apiService', CinderAPI]);
|
||||
}());
|
@ -20,8 +20,9 @@
|
||||
<script src='{{ STATIC_URL }}horizon/js/angular/controllers/metadata-widget-controller.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/angular/hz.api.module.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.service.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.keystone.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.cinder.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.glance.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.keystone.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.nova.js'></script>
|
||||
|
||||
<script src='{{ STATIC_URL }}angular/widget.module.js'></script>
|
||||
|
@ -72,6 +72,12 @@ class BaseCinderAPIResourceWrapper(base.APIResourceWrapper):
|
||||
return (getattr(self._apiresource, 'description', None) or
|
||||
getattr(self._apiresource, 'display_description', None))
|
||||
|
||||
def to_dict(self):
|
||||
obj = {}
|
||||
for key in self._attrs:
|
||||
obj[key] = getattr(self._apiresource, key, None)
|
||||
return obj
|
||||
|
||||
|
||||
class Volume(BaseCinderAPIResourceWrapper):
|
||||
|
||||
|
@ -22,6 +22,7 @@ in https://wiki.openstack.org/wiki/APIChangeGuidelines.
|
||||
"""
|
||||
|
||||
# import REST API modules here
|
||||
import cinder #flake8: noqa
|
||||
import glance #flake8: noqa
|
||||
import keystone #flake8: noqa
|
||||
import nova #flake8: noqa
|
||||
|
86
openstack_dashboard/api/rest/cinder.py
Normal file
86
openstack_dashboard/api/rest/cinder.py
Normal file
@ -0,0 +1,86 @@
|
||||
# Copyright 2015 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""API over the cinder service.
|
||||
"""
|
||||
|
||||
from django.views import generic
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.api.rest import utils as rest_utils
|
||||
|
||||
from openstack_dashboard.api.rest import urls
|
||||
|
||||
|
||||
def _parse_filters(request):
|
||||
"""Extract REST filter parameters from the request GET args.
|
||||
|
||||
We iterate like this so we avoid Django GET's odd behaviour of
|
||||
handing a list-of-param through some QueryDict accesses.
|
||||
"""
|
||||
filters = {}
|
||||
for param in request.GET:
|
||||
filters[param] = request.GET[param]
|
||||
return filters
|
||||
|
||||
|
||||
@urls.register
|
||||
class Volumes(generic.View):
|
||||
"""API for cinder volumes.
|
||||
"""
|
||||
url_regex = r'cinder/volumes/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a detailed list of volumes associated with the current user's
|
||||
project.
|
||||
|
||||
If invoked as an admin, you may set the GET parameter "all_projects"
|
||||
to 'true'.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param search_opts includes options such as name, status, bootable
|
||||
|
||||
The listing result is an object with property "items".
|
||||
"""
|
||||
# TODO(clu_): when v2 pagination stuff in Cinder API merges
|
||||
# (https://review.openstack.org/#/c/118450), handle here accordingly
|
||||
if request.GET.get('all_projects') == 'true':
|
||||
result = api.cinder.volume_list(request, {'all_tenants': 1})
|
||||
else:
|
||||
result = api.cinder.volume_list(
|
||||
request,
|
||||
search_opts=_parse_filters(request)
|
||||
)
|
||||
return {'items': [u.to_dict() for u in result]}
|
||||
|
||||
|
||||
@urls.register
|
||||
class VolumeSnapshots(generic.View):
|
||||
"""API for cinder volume snapshots.
|
||||
"""
|
||||
url_regex = r'cinder/volumesnapshots/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a detailed list of volume snapshots associated with the current
|
||||
user's project.
|
||||
|
||||
The listing result is an object with property "items".
|
||||
"""
|
||||
result = api.cinder.volume_snapshot_list(
|
||||
request,
|
||||
search_opts=_parse_filters(request)
|
||||
)
|
||||
return {'items': [u.to_dict() for u in result]}
|
88
openstack_dashboard/test/api_tests/cinder_rest_tests.py
Normal file
88
openstack_dashboard/test/api_tests/cinder_rest_tests.py
Normal file
@ -0,0 +1,88 @@
|
||||
# Copyright 2015 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from openstack_dashboard.api.rest import cinder
|
||||
|
||||
from openstack_dashboard.test.api_tests import rest_test_utils as utils
|
||||
|
||||
|
||||
class CinderRestTestCase(testtools.TestCase):
|
||||
def assertStatusCode(self, response, expected_code):
|
||||
if response.status_code == expected_code:
|
||||
return
|
||||
self.fail('status code %r != %r: %s' % (response.status_code,
|
||||
expected_code,
|
||||
response.content))
|
||||
|
||||
def test_volumes_get(self):
|
||||
self._test_volumes_get(False, {})
|
||||
|
||||
def test_volumes_get_all(self):
|
||||
self._test_volumes_get(True, {})
|
||||
|
||||
def test_volumes_get_with_filters(self):
|
||||
filters = {'status': 'available'}
|
||||
self._test_volumes_get(False, filters)
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
def _test_volumes_get(self, all, filters, cc):
|
||||
if all:
|
||||
request = utils.construct_request(GET={'all_projects': 'true'})
|
||||
else:
|
||||
request = utils.construct_request(**{'GET': filters})
|
||||
cc.volume_list.return_value = [
|
||||
mock.Mock(**{'to_dict.return_value': {'id': 'one'}}),
|
||||
mock.Mock(**{'to_dict.return_value': {'id': 'two'}}),
|
||||
]
|
||||
response = cinder.Volumes().get(request)
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertEqual(response.content,
|
||||
'{"items": [{"id": "one"}, {"id": "two"}]}')
|
||||
if all:
|
||||
cc.volume_list.assert_called_once_with(request,
|
||||
{'all_tenants': 1})
|
||||
else:
|
||||
cc.volume_list.assert_called_once_with(request,
|
||||
search_opts=filters)
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
def test_volume_snaps_get(self, cc):
|
||||
request = utils.construct_request(**{'GET': {}})
|
||||
cc.volume_snapshot_list.return_value = [
|
||||
mock.Mock(**{'to_dict.return_value': {'id': 'one'}}),
|
||||
mock.Mock(**{'to_dict.return_value': {'id': 'two'}}),
|
||||
]
|
||||
response = cinder.VolumeSnapshots().get(request)
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertEqual(response.content,
|
||||
'{"items": [{"id": "one"}, {"id": "two"}]}')
|
||||
cc.volume_snapshot_list.assert_called_once_with(request,
|
||||
search_opts={})
|
||||
|
||||
@mock.patch.object(cinder.api, 'cinder')
|
||||
def test_volume_snaps_get_with_filters(self, cc):
|
||||
filters = {'status': 'available'}
|
||||
request = utils.construct_request(**{'GET': dict(filters)})
|
||||
cc.volume_snapshot_list.return_value = [
|
||||
mock.Mock(**{'to_dict.return_value': {'id': 'one'}}),
|
||||
mock.Mock(**{'to_dict.return_value': {'id': 'two'}}),
|
||||
]
|
||||
response = cinder.VolumeSnapshots().get(request)
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertEqual(response.content,
|
||||
'{"items": [{"id": "one"}, {"id": "two"}]}')
|
||||
cc.volume_snapshot_list.assert_called_once_with(request,
|
||||
search_opts=filters)
|
Loading…
x
Reference in New Issue
Block a user