REST API to support create instance angular (Neutron).
In this update: - fixed tests - fixed exception handling, params on ports - fixed to_dict for a number of cases - replaced use of request.DATA with request.GET for GET methods - added handling of HTTP error raised with no status/code attr - removed check for network_id in subnets as it's not required - fix to_dict for Network with Subnets Supercedes https://review.openstack.org/#/c/152243 https://review.openstack.org/#/c/151313 Partially Implements: blueprint launch-instance-redesign Co-Authored-By: Aaron Sahlin <asahlin@us.ibm.com> Co-Authored-By: Michael Hagedorn <mike.hagedorn@hp.com> Co-Authored-By: Richard Jones <r1chardj0n3s@gmail.com> Change-Id: Ia40fde6d66720a03a531b516a6a53a2e86ec0d8f
This commit is contained in:
parent
36e87c6b75
commit
255340c596
|
@ -0,0 +1,204 @@
|
||||||
|
/**
|
||||||
|
* 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.NeutronAPI
|
||||||
|
* @description Provides access to Neutron APIs.
|
||||||
|
*/
|
||||||
|
function NeutronAPI(apiService) {
|
||||||
|
|
||||||
|
// Networks
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name hz.api.neturonAPI.getNetworks
|
||||||
|
* @description
|
||||||
|
* Get a list of networks for a tenant.
|
||||||
|
*
|
||||||
|
* The listing result is an object with property "items". Each item is
|
||||||
|
* a network.
|
||||||
|
*/
|
||||||
|
this.getNetworks = function() {
|
||||||
|
return apiService.get('/api/neutron/networks/')
|
||||||
|
.error(function () {
|
||||||
|
horizon.alert('error', gettext('Unable to retrieve networks.'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name hz.api.neutronAPI.createNetwork
|
||||||
|
* @description
|
||||||
|
* Create a new network.
|
||||||
|
* @returns The new network object on success.
|
||||||
|
*
|
||||||
|
* @param {Object} newNetwork
|
||||||
|
* The network to create. Required.
|
||||||
|
*
|
||||||
|
* Example new network object
|
||||||
|
* {
|
||||||
|
* "network": {
|
||||||
|
* "name": "myNewNetwork",
|
||||||
|
* "admin_state_up": true,
|
||||||
|
* "net_profile_id" : "asdsarafssdaser",
|
||||||
|
* "shared": true,
|
||||||
|
* "tenant_id": "4fd44f30292945e481c7b8a0c8908869
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Description of properties on the network object
|
||||||
|
*
|
||||||
|
* @property {string} newNetwork.name
|
||||||
|
* The name of the new network. Optional.
|
||||||
|
*
|
||||||
|
* @property {boolean} newNetwork.admin_state_up
|
||||||
|
* The administrative state of the network, which is up (true) or
|
||||||
|
* down (false). Optional.
|
||||||
|
*
|
||||||
|
* @property {string} newNetwork.net_profile_id
|
||||||
|
* The network profile id. Optional.
|
||||||
|
*
|
||||||
|
* @property {boolean} newNetwork.shared
|
||||||
|
* Indicates whether this network is shared across all tenants.
|
||||||
|
* By default, only adminstative users can change this value. Optional.
|
||||||
|
*
|
||||||
|
* @property {string} newNetwork.tenant_id
|
||||||
|
* The UUID of the tenant that will own the network. This tenant can
|
||||||
|
* be different from the tenant that makes the create network request.
|
||||||
|
* However, only administative users can specify a tenant ID other than
|
||||||
|
* their own. You cannot change this value through authorization
|
||||||
|
* policies. Optional.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
this.createNetwork = function(newNetwork) {
|
||||||
|
return apiService.post('/api/neutron/networks/', newNetwork)
|
||||||
|
.error(function () {
|
||||||
|
horizon.alert('error', gettext('Unable to create the network.'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Subnets
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name hz.api.neutronAPI.getSubnets
|
||||||
|
* @description
|
||||||
|
* Get a list of subnets for a network.
|
||||||
|
*
|
||||||
|
* The listing result is an object with property "items". Each item is
|
||||||
|
* a subnet.
|
||||||
|
*
|
||||||
|
* @param {string} network_id
|
||||||
|
* The network id to retrieve subnets for. Required.
|
||||||
|
*/
|
||||||
|
this.getSubnets = function(network_id) {
|
||||||
|
return apiService.get('/api/neutron/subnets/', network_id)
|
||||||
|
.error(function () {
|
||||||
|
horizon.alert('error', gettext('Unable to retrieve subnets.'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name hz.api.neutronAPI.createSubnet
|
||||||
|
* @description
|
||||||
|
* Create a Subnet for given Network.
|
||||||
|
* @returns The JSON representation of Subnet on success.
|
||||||
|
*
|
||||||
|
* @param {Object} newSubnet
|
||||||
|
* The subnet to create.
|
||||||
|
*
|
||||||
|
* Example new subnet object
|
||||||
|
* {
|
||||||
|
* "subnet": {
|
||||||
|
* "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||||
|
* "ip_version": 4,
|
||||||
|
* "cidr": "192.168.199.0/24",
|
||||||
|
* "name": "mySubnet",
|
||||||
|
* "tenant_id": "4fd44f30292945e481c7b8a0c8908869,
|
||||||
|
* "allocation_pools": [
|
||||||
|
* {
|
||||||
|
* "start": "192.168.199.2",
|
||||||
|
* "end": "192.168.199.254"
|
||||||
|
* }
|
||||||
|
* ],
|
||||||
|
* "gateway_ip": "192.168.199.1",
|
||||||
|
* "id": "abce",
|
||||||
|
* "enable_dhcp": true,
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Description of properties on the subnet object
|
||||||
|
* @property {string} newSubnet.network_id
|
||||||
|
* The id of the attached network. Required.
|
||||||
|
*
|
||||||
|
* @property {number} newSubnet.ip_version
|
||||||
|
* The IP version, which is 4 or 6. Required.
|
||||||
|
*
|
||||||
|
* @property {string} newSubnet.cidr
|
||||||
|
* The CIDR. Required.
|
||||||
|
*
|
||||||
|
* @property {string} newSubnet.name
|
||||||
|
* The name of the new subnet. Optional.
|
||||||
|
*
|
||||||
|
* @property {string} newSubnet.tenant_id
|
||||||
|
* The ID of the tenant who owns the network. Only administrative users
|
||||||
|
* can specify a tenant ID other than their own. Optional.
|
||||||
|
*
|
||||||
|
* @property {string|Array} newSubnet.allocation_pools
|
||||||
|
* The start and end addresses for the allocation pools. Optional.
|
||||||
|
*
|
||||||
|
* @property {string} newSubnet.gateway_ip
|
||||||
|
* The gateway IP address. Optional.
|
||||||
|
*
|
||||||
|
* @property {string} newSubnet.id
|
||||||
|
* The ID of the subnet. Optional.
|
||||||
|
*
|
||||||
|
* @property {boolean} newSubnet.enable_dhcp
|
||||||
|
* Set to true if DHCP is enabled and false if DHCP is disabled. Optional.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
this.createSubnet = function(newSubnet) {
|
||||||
|
return apiService.post('/api/neutron/subnets/', newSubnet)
|
||||||
|
.error(function () {
|
||||||
|
horizon.alert('error', gettext('Unable to create the subnet.'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ports
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name hz.api.neutronAPI.getPorts
|
||||||
|
* @description
|
||||||
|
* Get a list of ports for a network.
|
||||||
|
*
|
||||||
|
* The listing result is an object with property "items". Each item is
|
||||||
|
* a port.
|
||||||
|
*
|
||||||
|
* @param {string} network_id
|
||||||
|
* The network id to retrieve ports for. Required.
|
||||||
|
*/
|
||||||
|
this.getPorts = function(network_id) {
|
||||||
|
return apiService.get('/api/neutron/ports/', network_id)
|
||||||
|
.error(function () {
|
||||||
|
horizon.alert('error', gettext('Unable to retrieve ports.'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('hz.api')
|
||||||
|
.service('neutronAPI', ['apiService', NeutronAPI]);
|
||||||
|
}());
|
|
@ -26,6 +26,7 @@
|
||||||
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.config.js'></script>
|
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.config.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.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.keystone.js'></script>
|
||||||
|
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.neutron.js'></script>
|
||||||
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.nova.js'></script>
|
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.nova.js'></script>
|
||||||
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.policy.js'></script>
|
<script src='{{ STATIC_URL }}horizon/js/angular/services/hz.api.policy.js'></script>
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,9 @@ class APIDictWrapper(object):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s: %s>" % (self.__class__.__name__, self._apidict)
|
return "<%s: %s>" % (self.__class__.__name__, self._apidict)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return self._apidict
|
||||||
|
|
||||||
|
|
||||||
class Quota(object):
|
class Quota(object):
|
||||||
"""Wrapper for individual limits in a quota."""
|
"""Wrapper for individual limits in a quota."""
|
||||||
|
|
|
@ -94,6 +94,11 @@ class Network(NeutronAPIDictWrapper):
|
||||||
apiresource['__'.join(key.split(':'))] = apiresource[key]
|
apiresource['__'.join(key.split(':'))] = apiresource[key]
|
||||||
super(Network, self).__init__(apiresource)
|
super(Network, self).__init__(apiresource)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
d = dict(super(NeutronAPIDictWrapper, self).to_dict())
|
||||||
|
d['subnets'] = [s.to_dict() for s in d['subnets']]
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
class Subnet(NeutronAPIDictWrapper):
|
class Subnet(NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron subnets."""
|
"""Wrapper for neutron subnets."""
|
||||||
|
|
|
@ -27,5 +27,6 @@ import config #flake8: noqa
|
||||||
import glance #flake8: noqa
|
import glance #flake8: noqa
|
||||||
import keystone #flake8: noqa
|
import keystone #flake8: noqa
|
||||||
import network #flake8: noqa
|
import network #flake8: noqa
|
||||||
|
import neutron #flake8: noqa
|
||||||
import nova #flake8: noqa
|
import nova #flake8: noqa
|
||||||
import policy #flake8: noqa
|
import policy #flake8: noqa
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
#
|
||||||
|
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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 neutron 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
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class Networks(generic.View):
|
||||||
|
"""API for Neutron Networks
|
||||||
|
http://developer.openstack.org/api-ref-networking-v2.html
|
||||||
|
"""
|
||||||
|
url_regex = r'neutron/networks/$'
|
||||||
|
|
||||||
|
@rest_utils.ajax()
|
||||||
|
def get(self, request):
|
||||||
|
"""Get a list of networks for a project
|
||||||
|
|
||||||
|
The listing result is an object with property "items". Each item is
|
||||||
|
a network.
|
||||||
|
"""
|
||||||
|
tenant_id = request.user.tenant_id
|
||||||
|
result = api.neutron.network_list_for_tenant(request, tenant_id)
|
||||||
|
return{'items': [n.to_dict() for n in result]}
|
||||||
|
|
||||||
|
@rest_utils.ajax(data_required=True)
|
||||||
|
def post(self, request):
|
||||||
|
"""Create a network
|
||||||
|
:param admin_state_up (optional): The administrative state of the
|
||||||
|
network, which is up (true) or down (false).
|
||||||
|
:param name (optional): The network name. A request body is optional:
|
||||||
|
If you include it, it can specify this optional attribute.
|
||||||
|
:param net_profile_id (optional): network profile id
|
||||||
|
:param shared (optional): Indicates whether this network is shared
|
||||||
|
across all tenants. By default, only administrative users can
|
||||||
|
change this value.
|
||||||
|
:param tenant_id (optional): Admin-only. The UUID of the tenant that
|
||||||
|
will own the network. This tenant can be different from the
|
||||||
|
tenant that makes the create network request. However, only
|
||||||
|
administrative users can specify a tenant ID other than their
|
||||||
|
own. You cannot change this value through authorization
|
||||||
|
policies.
|
||||||
|
|
||||||
|
:return: JSON representation of a Network
|
||||||
|
"""
|
||||||
|
if not api.neutron.is_port_profiles_supported():
|
||||||
|
request.DATA.pop("net_profile_id", None)
|
||||||
|
new_network = api.neutron.network_create(request, **request.DATA)
|
||||||
|
return rest_utils.CreatedResponse(
|
||||||
|
'/api/neutron/networks/%s' % new_network.id,
|
||||||
|
new_network.to_dict()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class Subnets(generic.View):
|
||||||
|
"""API for Neutron SubNets
|
||||||
|
http://developer.openstack.org/api-ref-networking-v2.html#subnets
|
||||||
|
"""
|
||||||
|
url_regex = r'neutron/subnets/$'
|
||||||
|
|
||||||
|
@rest_utils.ajax()
|
||||||
|
def get(self, request):
|
||||||
|
"""Get a list of subnets for a project
|
||||||
|
|
||||||
|
The listing result is an object with property "items". Each item is
|
||||||
|
a subnet.
|
||||||
|
|
||||||
|
"""
|
||||||
|
result = api.neutron.subnet_list(request, **request.GET)
|
||||||
|
return{'items': [n.to_dict() for n in result]}
|
||||||
|
|
||||||
|
@rest_utils.ajax(data_required=True)
|
||||||
|
def post(self, request):
|
||||||
|
"""Create a Subnet for a given Network
|
||||||
|
|
||||||
|
:param name (optional): The subnet name.
|
||||||
|
:param network_id: The ID of the attached network.
|
||||||
|
:param tenant_id (optional): The ID of the tenant who owns the network.
|
||||||
|
Only administrative users can specify a tenant ID other than
|
||||||
|
their own.
|
||||||
|
:param allocation_pools (optional): The start and end addresses for the
|
||||||
|
allocation pools.
|
||||||
|
:param gateway_ip (optional): The gateway IP address.
|
||||||
|
:param ip_version: The IP version, which is 4 or 6.
|
||||||
|
:param cidr: The CIDR.
|
||||||
|
:param id (optional): The ID of the subnet.
|
||||||
|
:param enable_dhcp (optional): Set to true if DHCP is enabled and false
|
||||||
|
if DHCP is disabled.
|
||||||
|
|
||||||
|
:return: JSON representation of a Subnet
|
||||||
|
|
||||||
|
"""
|
||||||
|
new_subnet = api.neutron.subnet_create(request, **request.DATA)
|
||||||
|
return rest_utils.CreatedResponse(
|
||||||
|
'/api/neutron/subnets/%s' % new_subnet.id,
|
||||||
|
new_subnet.to_dict()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class Ports(generic.View):
|
||||||
|
"""API for Neutron Ports
|
||||||
|
http://developer.openstack.org/api-ref-networking-v2.html#ports
|
||||||
|
"""
|
||||||
|
url_regex = r'neutron/ports/$'
|
||||||
|
|
||||||
|
@rest_utils.ajax()
|
||||||
|
def get(self, request):
|
||||||
|
"""Get a list of ports for a network
|
||||||
|
|
||||||
|
The listing result is an object with property "items". Each item is
|
||||||
|
a subnet.
|
||||||
|
"""
|
||||||
|
# see
|
||||||
|
# https://github.com/openstack/neutron/blob/master/neutron/api/v2/attributes.py
|
||||||
|
result = api.neutron.port_list(request, **request.GET)
|
||||||
|
return{'items': [n.to_dict() for n in result]}
|
|
@ -121,8 +121,11 @@ def ajax(authenticated=True, data_required=False):
|
||||||
# exception was raised with a specific HTTP status
|
# exception was raised with a specific HTTP status
|
||||||
if hasattr(e, 'http_status'):
|
if hasattr(e, 'http_status'):
|
||||||
http_status = e.http_status
|
http_status = e.http_status
|
||||||
else:
|
elif hasattr(e, 'code'):
|
||||||
http_status = e.code
|
http_status = e.code
|
||||||
|
else:
|
||||||
|
log.exception('HTTP exception with no status/code')
|
||||||
|
return JSONResponse(str(e), 500)
|
||||||
return JSONResponse(str(e), http_status)
|
return JSONResponse(str(e), http_status)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception('error invoking apiclient')
|
log.exception('error invoking apiclient')
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
#
|
||||||
|
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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 openstack_dashboard.api.rest import neutron
|
||||||
|
from openstack_dashboard.test.test_data import neutron_data
|
||||||
|
from openstack_dashboard.test.test_data.utils import TestData # noqa
|
||||||
|
|
||||||
|
from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
|
TEST = TestData(neutron_data.data)
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronNetworksTestCase(test.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls._networks = [mock_factory(n)
|
||||||
|
for n in TEST.api_networks.list()]
|
||||||
|
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
def test_get_list_for_tenant(self, client):
|
||||||
|
request = self.mock_rest_request()
|
||||||
|
networks = self._networks
|
||||||
|
client.network_list_for_tenant.return_value = networks
|
||||||
|
response = neutron.Networks().get(request)
|
||||||
|
self.assertStatusCode(response, 200)
|
||||||
|
self.assertItemsCollectionEqual(response, TEST.api_networks.list())
|
||||||
|
client.network_list_for_tenant.assert_called_once_with(
|
||||||
|
request, request.user.tenant_id)
|
||||||
|
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
def test_create(self, client):
|
||||||
|
self._test_create(
|
||||||
|
'{"name": "mynetwork"}',
|
||||||
|
{'name': 'mynetwork'}
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
def test_create_with_bogus_param(self, client):
|
||||||
|
self._test_create(
|
||||||
|
'{"name": "mynetwork","bilbo":"baggins"}',
|
||||||
|
{'name': 'mynetwork'}
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
def _test_create(self, supplied_body, expected_call, client):
|
||||||
|
request = self.mock_rest_request(body=supplied_body)
|
||||||
|
client.network_create.return_value = self._networks[0]
|
||||||
|
response = neutron.Networks().post(request)
|
||||||
|
self.assertStatusCode(response, 201)
|
||||||
|
self.assertEqual(response['location'],
|
||||||
|
'/api/neutron/networks/'
|
||||||
|
+ str(TEST.api_networks.first().get("id")))
|
||||||
|
self.assertEqual(response.content,
|
||||||
|
json.dumps(TEST.api_networks.first()))
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronSubnetsTestCase(test.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls._networks = [mock_factory(n)
|
||||||
|
for n in TEST.api_networks.list()]
|
||||||
|
cls._subnets = [mock_factory(n)
|
||||||
|
for n in TEST.api_subnets.list()]
|
||||||
|
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
def test_get(self, client):
|
||||||
|
request = self.mock_rest_request(
|
||||||
|
GET={"network_id": self._networks[0].id})
|
||||||
|
client.subnet_list.return_value = [self._subnets[0]]
|
||||||
|
response = neutron.Subnets().get(request)
|
||||||
|
self.assertStatusCode(response, 200)
|
||||||
|
client.subnet_list.assert_called_once_with(
|
||||||
|
request, network_id=TEST.api_networks.first().get("id"))
|
||||||
|
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
def test_create(self, client):
|
||||||
|
request = self.mock_rest_request(
|
||||||
|
body='{"network_id": "%s",'
|
||||||
|
' "ip_version": "4",'
|
||||||
|
' "cidr": "192.168.199.0/24"}' % self._networks[0].id)
|
||||||
|
client.subnet_create.return_value = self._subnets[0]
|
||||||
|
response = neutron.Subnets().post(request)
|
||||||
|
self.assertStatusCode(response, 201)
|
||||||
|
self.assertEqual(response['location'],
|
||||||
|
'/api/neutron/subnets/' +
|
||||||
|
str(TEST.api_subnets.first().get("id")))
|
||||||
|
self.assertEqual(response.content,
|
||||||
|
json.dumps(TEST.api_subnets.first()))
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronPortsTestCase(test.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls._networks = [mock_factory(n)
|
||||||
|
for n in TEST.api_networks.list()]
|
||||||
|
cls._ports = [mock_factory(n)
|
||||||
|
for n in TEST.api_ports.list()]
|
||||||
|
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
def test_get(self, client):
|
||||||
|
request = self.mock_rest_request(
|
||||||
|
GET={"network_id": self._networks[0].id})
|
||||||
|
client.port_list.return_value = [self._ports[0]]
|
||||||
|
response = neutron.Ports().get(request)
|
||||||
|
self.assertStatusCode(response, 200)
|
||||||
|
client.port_list.assert_called_once_with(
|
||||||
|
request, network_id=TEST.api_networks.first().get("id"))
|
||||||
|
|
||||||
|
|
||||||
|
def mock_obj_to_dict(r):
|
||||||
|
return mock.Mock(**{'to_dict.return_value': r})
|
||||||
|
|
||||||
|
|
||||||
|
def mock_factory(r):
|
||||||
|
"""mocks all the attributes as well as the to_dict """
|
||||||
|
mocked = mock_obj_to_dict(r)
|
||||||
|
mocked.configure_mock(**r)
|
||||||
|
return mocked
|
|
@ -19,8 +19,10 @@
|
||||||
import collections
|
import collections
|
||||||
import copy
|
import copy
|
||||||
from functools import wraps # noqa
|
from functools import wraps # noqa
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
from ceilometerclient.v2 import client as ceilometer_client
|
from ceilometerclient.v2 import client as ceilometer_client
|
||||||
from cinderclient import client as cinder_client
|
from cinderclient import client as cinder_client
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -278,6 +280,10 @@ class TestCase(horizon_helpers.TestCase):
|
||||||
expected_code,
|
expected_code,
|
||||||
response.content))
|
response.content))
|
||||||
|
|
||||||
|
def assertItemsCollectionEqual(self, response, items_list):
|
||||||
|
self.assertEqual(response.content,
|
||||||
|
'{"items": ' + json.dumps(items_list) + "}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mock_rest_request(**args):
|
def mock_rest_request(**args):
|
||||||
mock_args = {
|
mock_args = {
|
||||||
|
|
Loading…
Reference in New Issue