Add API for container create, delete, list, show
This patch adds basic API calls for Containers, as well as the testing infrastructure necessary to test these API calls. Also adds some basic testing for Bays and Bay Models. Change-Id: I7cb5dfbd32695c95a414b8d41aa0aa178fe2472e Implements: blueprint container-api
This commit is contained in:
parent
1fe073af8b
commit
4527b6c7c5
@ -14,13 +14,11 @@
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
|
||||
from magnumclient.v1 import client as magnum_client
|
||||
|
||||
from horizon import exceptions
|
||||
|
||||
from horizon.utils.memoized import memoized
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -36,6 +34,7 @@ BAY_CREATE_ATTRS = ['name', 'baymodel_id', 'node_count', 'discovery_url',
|
||||
'bay_create_timeout', 'master_count']
|
||||
|
||||
|
||||
@memoized
|
||||
def magnumclient(request):
|
||||
magnum_url = ""
|
||||
try:
|
||||
@ -105,3 +104,45 @@ def bay_list(request, limit=None, marker=None, sort_key=None,
|
||||
|
||||
def bay_show(request, id):
|
||||
return magnumclient(request).bays.get(id)
|
||||
|
||||
|
||||
def container_create(request, bay_id, **kwargs):
|
||||
"""Creates a container object
|
||||
:param request: Request context
|
||||
:param bay_id: ID of a bay (Required)
|
||||
:param kwargs: Image ID, Name, Command, Memory
|
||||
:returns: Container object
|
||||
"""
|
||||
return magnumclient(request).containers.create(bay_id=bay_id, **kwargs)
|
||||
|
||||
|
||||
def container_delete(request, id):
|
||||
"""Deletes a container
|
||||
:param request: Request context
|
||||
:param id: The ID of the container to delete
|
||||
"""
|
||||
magnumclient(request).containers.delete(id)
|
||||
|
||||
|
||||
def container_list(request, marker=None, limit=None, sort_key=None,
|
||||
sort_dir=None, detail=False):
|
||||
"""Lists all containers
|
||||
:param request: Request context
|
||||
:param marker: Optional, ID of last container in previous results
|
||||
:param limit: '==0' return all, '> 0' specifies max, None respects max
|
||||
imposed by Magnum API
|
||||
:param sort_key: Optional, key to sort by
|
||||
:param sort_dir: Optional, direction of sorting ('asc' or 'desc')
|
||||
:param detail: Optional, boolean, return detailed info about containers
|
||||
"""
|
||||
return magnumclient(request).containers.list(
|
||||
marker=marker, limit=limit, sort_key=sort_key, sort_dir=sort_dir,
|
||||
detail=detail)
|
||||
|
||||
|
||||
def container_show(request, id):
|
||||
"""Get an individual container
|
||||
:param request: Request context
|
||||
:param id: ID of the container to get
|
||||
"""
|
||||
return magnumclient(request).containers.get(id)
|
||||
|
@ -40,11 +40,11 @@ class BayModels(generic.View):
|
||||
def get(self, request):
|
||||
"""Get a list of the BayModels for a project.
|
||||
|
||||
The returned result is an object with property 'baymodels' and each
|
||||
The returned result is an object with property 'items' and each
|
||||
item under this is a BayModel.
|
||||
"""
|
||||
result = magnum.baymodel_list(request)
|
||||
return{'baymodels': [change_to_id(n.to_dict()) for n in result]}
|
||||
return {'items': [change_to_id(n.to_dict()) for n in result]}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def delete(self, request):
|
||||
@ -63,8 +63,8 @@ class BayModels(generic.View):
|
||||
"""
|
||||
new_baymodel = magnum.baymodel_create(request, **request.DATA)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/containers/baymodel/%s' % new_baymodel.id,
|
||||
new_baymodel.to_dict())
|
||||
'/api/containers/baymodel/%s' % new_baymodel['uuid'],
|
||||
new_baymodel)
|
||||
|
||||
|
||||
@urls.register
|
||||
@ -77,11 +77,11 @@ class Bays(generic.View):
|
||||
def get(self, request):
|
||||
"""Get a list of the Bays for a project.
|
||||
|
||||
The returned result is an object with property 'bays' and each
|
||||
The returned result is an object with property 'items' and each
|
||||
item under this is a Bay.
|
||||
"""
|
||||
result = magnum.bay_list(request)
|
||||
return{'bays': [change_to_id(n.to_dict()) for n in result]}
|
||||
return {'items': [change_to_id(n.to_dict()) for n in result]}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def delete(self, request):
|
||||
@ -100,5 +100,41 @@ class Bays(generic.View):
|
||||
"""
|
||||
new_bay = magnum.bay_create(request, **request.DATA)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/containers/bay/%s' % new_bay.uuid,
|
||||
new_bay.to_dict())
|
||||
'/api/containers/bay/%s' % new_bay['uuid'],
|
||||
new_bay)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Containers(generic.View):
|
||||
"""API for Magnum Containers
|
||||
"""
|
||||
url_regex = r'containers/containers/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of the Containers for a project.
|
||||
|
||||
The returned result is an object with property 'items' and each
|
||||
item under this is a Container.
|
||||
"""
|
||||
result = magnum.container_list(request)
|
||||
return {'items': [n.to_dict() for n in result]}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def delete(self, request):
|
||||
"""Delete one or more Containers by ID.
|
||||
|
||||
Returns HTTP 204 (no content) on successful deletion.
|
||||
"""
|
||||
for container_id in request.DATA:
|
||||
magnum.container_delete(request, container_id)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create a new Container.
|
||||
|
||||
Returns the new Container object on success.
|
||||
"""
|
||||
container = magnum.container_create(request, **request.DATA)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/containers/container/%s' % container['uuid'], container)
|
||||
|
@ -49,7 +49,7 @@
|
||||
}
|
||||
|
||||
function getBaysSuccess(response) {
|
||||
ctrl.bays = response.bays;
|
||||
ctrl.bays = response.items;
|
||||
}
|
||||
|
||||
function singleDelete(bay) {
|
||||
|
@ -49,7 +49,7 @@
|
||||
}
|
||||
|
||||
function getBayModelsSuccess(response) {
|
||||
ctrl.baymodels = response.baymodels;
|
||||
ctrl.baymodels = response.items;
|
||||
}
|
||||
|
||||
function singleDelete(baymodel) {
|
||||
|
66
magnum_ui/test/api_tests/api_tests.py
Normal file
66
magnum_ui/test/api_tests/api_tests.py
Normal file
@ -0,0 +1,66 @@
|
||||
# Copyright 2015 Cisco Systems, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from magnum_ui import api
|
||||
from magnum_ui.test import helpers as test
|
||||
|
||||
|
||||
class MagnumApiTests(test.APITestCase):
|
||||
def test_container_list(self):
|
||||
containers = self.magnum_containers.list()
|
||||
form_data = {'marker': None,
|
||||
'limit': None,
|
||||
'sort_key': None,
|
||||
'sort_dir': None,
|
||||
'detail': False}
|
||||
|
||||
magnumclient = self.stub_magnumclient()
|
||||
magnumclient.containers = self.mox.CreateMockAnything()
|
||||
magnumclient.containers.list(**form_data).AndReturn(containers)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api.magnum.container_list(self.request)
|
||||
|
||||
def test_container_get(self):
|
||||
container = self.magnum_containers.first()
|
||||
|
||||
magnumclient = self.stub_magnumclient()
|
||||
magnumclient.containers = self.mox.CreateMockAnything()
|
||||
magnumclient.containers.get(container['uuid']).AndReturn(container)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api.magnum.container_show(self.request, container['uuid'])
|
||||
|
||||
def test_container_delete(self):
|
||||
container = self.magnum_containers.first()
|
||||
|
||||
magnumclient = self.stub_magnumclient()
|
||||
magnumclient.containers = self.mox.CreateMockAnything()
|
||||
magnumclient.containers.delete(container['uuid'])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api.magnum.container_delete(self.request, container['uuid'])
|
||||
|
||||
def test_container_create(self):
|
||||
container = self.magnum_containers.first()
|
||||
form_data = {'bay_id': container['bay'],
|
||||
'name': container['name']}
|
||||
|
||||
magnumclient = self.stub_magnumclient()
|
||||
magnumclient.containers = self.mox.CreateMockAnything()
|
||||
magnumclient.containers.create(**form_data)\
|
||||
.AndReturn(container)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
api.magnum.container_create(self.request, **form_data)
|
149
magnum_ui/test/api_tests/rest_api_tests.py
Normal file
149
magnum_ui/test/api_tests/rest_api_tests.py
Normal file
@ -0,0 +1,149 @@
|
||||
# Copyright 2015 Cisco Systems, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import mock
|
||||
|
||||
from magnum_ui.api.rest import magnum
|
||||
from magnum_ui.test import test_data
|
||||
from openstack_dashboard.test import helpers as test
|
||||
from openstack_dashboard.test.test_data.utils import TestData
|
||||
|
||||
TEST = TestData(test_data.data)
|
||||
|
||||
|
||||
class MagnumRestTestCase(test.TestCase):
|
||||
|
||||
# BayModels
|
||||
@mock.patch.object(magnum, 'magnum')
|
||||
def test_baymodel_get(self, client):
|
||||
request = self.mock_rest_request()
|
||||
client.baymodel_list.return_value = \
|
||||
mock_resource(TEST.baymodels.list())
|
||||
response = magnum.BayModels().get(request)
|
||||
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertItemsCollectionEqual(response, TEST.baymodels.list())
|
||||
client.baymodel_list.assert_called_once_with(request)
|
||||
|
||||
@mock.patch.object(magnum, 'magnum')
|
||||
def test_baymodel_create(self, client):
|
||||
test_baymodel = TEST.baymodels.first()
|
||||
test_body = json.dumps(test_baymodel)
|
||||
request = self.mock_rest_request(body=test_body)
|
||||
client.baymodel_create.return_value = test_baymodel
|
||||
response = magnum.BayModels().post(request)
|
||||
|
||||
self.assertStatusCode(response, 201)
|
||||
self.assertEqual(response['location'],
|
||||
'/api/containers/baymodel/%s' % test_baymodel['uuid'])
|
||||
client.baymodel_create.assert_called_once_with(request,
|
||||
**test_baymodel)
|
||||
|
||||
@mock.patch.object(magnum, 'magnum')
|
||||
def test_baymodel_delete(self, client):
|
||||
test_baymodel = TEST.baymodels.first()
|
||||
request = self.mock_rest_request(
|
||||
body='{"baymodel_id":' + str(test_baymodel['uuid']) + '}')
|
||||
response = magnum.BayModels().delete(request)
|
||||
|
||||
self.assertStatusCode(response, 204)
|
||||
client.baymodel_delete.assert_called_once_with(
|
||||
request,
|
||||
u'baymodel_id')
|
||||
|
||||
# Bays
|
||||
@mock.patch.object(magnum, 'magnum')
|
||||
def test_bay_get(self, client):
|
||||
request = self.mock_rest_request()
|
||||
client.bay_list.return_value = \
|
||||
mock_resource(TEST.bays.list())
|
||||
response = magnum.Bays().get(request)
|
||||
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertItemsCollectionEqual(response, TEST.bays.list())
|
||||
client.bay_list.assert_called_once_with(request)
|
||||
|
||||
@mock.patch.object(magnum, 'magnum')
|
||||
def test_bay_create(self, client):
|
||||
test_bay = TEST.bays.first()
|
||||
test_body = json.dumps(test_bay)
|
||||
request = self.mock_rest_request(body=test_body)
|
||||
client.bay_create.return_value = test_bay
|
||||
response = magnum.Bays().post(request)
|
||||
|
||||
self.assertStatusCode(response, 201)
|
||||
self.assertEqual(response['location'],
|
||||
'/api/containers/bay/%s' % test_bay['uuid'])
|
||||
client.bay_create.assert_called_once_with(request,
|
||||
**test_bay)
|
||||
|
||||
@mock.patch.object(magnum, 'magnum')
|
||||
def test_bay_delete(self, client):
|
||||
test_bay = TEST.bays.first()
|
||||
request = self.mock_rest_request(
|
||||
body='{"bay_id":' + str(test_bay['uuid']) + '}')
|
||||
response = magnum.Bays().delete(request)
|
||||
|
||||
self.assertStatusCode(response, 204)
|
||||
client.bay_delete.assert_called_once_with(
|
||||
request,
|
||||
u'bay_id')
|
||||
|
||||
# Containers
|
||||
@mock.patch.object(magnum, 'magnum')
|
||||
def test_container_get(self, client):
|
||||
request = self.mock_rest_request()
|
||||
client.container_list.return_value = \
|
||||
mock_resource(TEST.magnum_containers.list())
|
||||
response = magnum.Containers().get(request)
|
||||
|
||||
self.assertStatusCode(response, 200)
|
||||
self.assertItemsCollectionEqual(response,
|
||||
TEST.magnum_containers.list())
|
||||
client.container_list.assert_called_once_with(request)
|
||||
|
||||
@mock.patch.object(magnum, 'magnum')
|
||||
def test_container_create(self, client):
|
||||
test_cont = TEST.magnum_containers.first()
|
||||
test_body = json.dumps(test_cont)
|
||||
request = self.mock_rest_request(body=test_body)
|
||||
client.container_create.return_value = test_cont
|
||||
response = magnum.Containers().post(request)
|
||||
|
||||
self.assertStatusCode(response, 201)
|
||||
self.assertEqual(response['location'],
|
||||
'/api/containers/container/%s' % test_cont['uuid'])
|
||||
client.container_create.assert_called_once_with(request, **test_cont)
|
||||
|
||||
@mock.patch.object(magnum, 'magnum')
|
||||
def test_container_delete(self, client):
|
||||
test_container = TEST.magnum_containers.first()
|
||||
request = self.mock_rest_request(
|
||||
body='{"container_id":' + str(test_container['uuid']) + '}')
|
||||
response = magnum.Containers().delete(request)
|
||||
|
||||
self.assertStatusCode(response, 204)
|
||||
client.container_delete.assert_called_once_with(
|
||||
request,
|
||||
u'container_id')
|
||||
|
||||
|
||||
def mock_resource(resource):
|
||||
"""Utility function to make mocking more DRY"""
|
||||
|
||||
mocked_data = \
|
||||
[mock.Mock(**{'to_dict.return_value': item}) for item in resource]
|
||||
|
||||
return mocked_data
|
41
magnum_ui/test/helpers.py
Normal file
41
magnum_ui/test/helpers.py
Normal file
@ -0,0 +1,41 @@
|
||||
# Copyright 2015 Cisco Systems, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from magnum_ui import api
|
||||
from magnum_ui.test import test_data
|
||||
from magnumclient.v1 import client as magnum_client
|
||||
from openstack_dashboard.test import helpers
|
||||
|
||||
|
||||
class APITestCase(helpers.APITestCase):
|
||||
"""Extends the base Horizon APITestCase for magnumclient"""
|
||||
|
||||
def setUp(self):
|
||||
super(APITestCase, self).setUp()
|
||||
self._original_magnumclient = api.magnum.magnumclient
|
||||
api.magnum.magnumclient = lambda request: self.stub_magnumclient()
|
||||
|
||||
def _setup_test_data(self):
|
||||
super(APITestCase, self)._setup_test_data()
|
||||
test_data.data(self)
|
||||
|
||||
def tearDown(self):
|
||||
super(APITestCase, self).tearDown()
|
||||
api.magnum.magnumclient = self._original_magnumclient
|
||||
|
||||
def stub_magnumclient(self):
|
||||
if not hasattr(self, "magnumclient"):
|
||||
self.mox.StubOutWithMock(magnum_client, 'Client')
|
||||
self.magnumclient = self.mox.CreateMock(magnum_client.Client)
|
||||
return self.magnumclient
|
66
magnum_ui/test/test_data.py
Normal file
66
magnum_ui/test/test_data.py
Normal file
@ -0,0 +1,66 @@
|
||||
# Copyright 2015 Cisco Systems, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from openstack_dashboard.test.test_data import utils
|
||||
|
||||
|
||||
def data(TEST):
|
||||
# Test Data Containers
|
||||
TEST.baymodels = utils.TestDataContainer()
|
||||
TEST.bays = utils.TestDataContainer()
|
||||
# 'magnum_containers' to avoid Swift naming confusion
|
||||
TEST.magnum_containers = utils.TestDataContainer()
|
||||
|
||||
# Bay Models
|
||||
baymodel_dict_1 = {"uuid": 1,
|
||||
"name": "kindofabigdeal",
|
||||
"image-id": "",
|
||||
"keypair-id": "",
|
||||
"external-network-id": "",
|
||||
"coe": "",
|
||||
"fixed-network": "",
|
||||
"ssh-authorized-key": "",
|
||||
"dns-nameserver": "",
|
||||
"flavor-id": "",
|
||||
"master-flavor-id": "",
|
||||
"docker-volume-size": "",
|
||||
"http-proxy": "",
|
||||
"https-proxy": "",
|
||||
"no-proxy": "",
|
||||
"labels": "",
|
||||
"tls-disabled": "",
|
||||
"public": ""}
|
||||
|
||||
TEST.baymodels.add(baymodel_dict_1)
|
||||
|
||||
# Bays
|
||||
bay_dict_1 = {"uuid": 1,
|
||||
"name": "peopleknowme",
|
||||
"baymodel": baymodel_dict_1["uuid"],
|
||||
"node-count": "",
|
||||
"master-count": "",
|
||||
"discovery-url": "",
|
||||
"timeout": 0}
|
||||
|
||||
TEST.bays.add(bay_dict_1)
|
||||
|
||||
# Containers
|
||||
container_dict_1 = {"uuid": 1,
|
||||
"name": "myapartmentsmellsofrichmahogany",
|
||||
"image": "",
|
||||
"bay": bay_dict_1["uuid"],
|
||||
"command": "",
|
||||
"memory": ""}
|
||||
|
||||
TEST.magnum_containers.add(container_dict_1)
|
Loading…
x
Reference in New Issue
Block a user