diff --git a/horizon/static/horizon/js/angular/services/hz.api.security-group.js b/horizon/static/horizon/js/angular/services/hz.api.security-group.js new file mode 100644 index 0000000000..62856d08d9 --- /dev/null +++ b/horizon/static/horizon/js/angular/services/hz.api.security-group.js @@ -0,0 +1,75 @@ +/* +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. +*/ +(function () { + 'use strict'; + + /** + * @ngdoc service + * @name hz.api.SecurityGroup + * @description Provides access to Security Groups + */ + function SecurityGroup(apiService) { + + /** + * @name hz.api.SecurityGroup.list + * @description + * Get a list of security groups. + * + * The listing result is an object with property "items". Each item is + * an image. + * + * @example + * The following is an example response: + * { + * "items": [ + * { + * "description": "Default security group", + * "id": "4a4c9dd4-ffa0-454a-beaa-23e8fa569062", + * "name": "default", + * "security_group_rules": [ + * { + * "direction": "ingress", + * "ethertype": "IPv4", + * "id": "22961412-fba1-4d0d-8eb8-d4460c921346", + * "port_range_max": null, + * "port_range_min": null, + * "protocol": null, + * "remote_group_id": "4a4c9dd4-ffa0-454a-beaa-23e8fa569062", + * "remote_ip_prefix": null, + * "security_group_id": "4a4c9dd4-ffa0-454a-beaa-23e8fa569062", + * "tenant_id": "3f867827f7eb45d4aa1d1395237f426b" + * } + * ], + * "tenant_id": "3f867827f7eb45d4aa1d1395237f426b" + * } + * ] + * } + */ + this.query = function() { + return apiService.get('/api/network/securitygroups/') + .error(function () { + horizon.alert('error', gettext('Unable to retrieve security groups.')); + }); + }; + } + + // Register it with the API module so that anybody using the + // API module will have access to the Security Group APIs. + + angular.module('hz.api') + .service('securityGroup', ['apiService', SecurityGroup]); + +}()); diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py index 986b4428e1..91ddefe01b 100644 --- a/openstack_dashboard/api/neutron.py +++ b/openstack_dashboard/api/neutron.py @@ -143,6 +143,9 @@ class SecurityGroup(NeutronAPIDictWrapper): for rule in sg['security_group_rules']] super(SecurityGroup, self).__init__(sg) + def to_dict(self): + return {k: self._apidict[k] for k in self._apidict if k != 'rules'} + class SecurityGroupRule(NeutronAPIDictWrapper): # Required attributes: diff --git a/openstack_dashboard/api/nova.py b/openstack_dashboard/api/nova.py index 15bf8cb74c..fabbe45ded 100644 --- a/openstack_dashboard/api/nova.py +++ b/openstack_dashboard/api/nova.py @@ -214,6 +214,9 @@ class SecurityGroup(base.APIResourceWrapper): for rule in self._apiresource.rules] return [SecurityGroupRule(rule) for rule in rule_objs] + def to_dict(self): + return self._apiresource.to_dict() + class SecurityGroupRule(base.APIResourceWrapper): """Wrapper for individual rules in a SecurityGroup.""" diff --git a/openstack_dashboard/api/rest/__init__.py b/openstack_dashboard/api/rest/__init__.py index e670007cd5..d9c2f09fd1 100644 --- a/openstack_dashboard/api/rest/__init__.py +++ b/openstack_dashboard/api/rest/__init__.py @@ -25,4 +25,5 @@ in https://wiki.openstack.org/wiki/APIChangeGuidelines. import cinder #flake8: noqa import glance #flake8: noqa import keystone #flake8: noqa +import network #flake8: noqa import nova #flake8: noqa diff --git a/openstack_dashboard/api/rest/network.py b/openstack_dashboard/api/rest/network.py new file mode 100644 index 0000000000..c9f01cd9e0 --- /dev/null +++ b/openstack_dashboard/api/rest/network.py @@ -0,0 +1,46 @@ + +# 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 for the network abstraction APIs. +""" + +from django.views import generic + +from openstack_dashboard import api +from openstack_dashboard.api.rest import urls +from openstack_dashboard.api.rest import utils as rest_utils + + +@urls.register +class SecurityGroups(generic.View): + """API for Network Abstraction + + Handles differences between Nova and Neutron. + """ + url_regex = r'network/securitygroups/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of security groups. + + The listing result is an object with property "items". Each item is + an image. + + Example GET: + http://localhost/api/network/securitygroups + """ + + security_groups = api.network.security_group_list(request) + + return {'items': [sg.to_dict() for sg in security_groups]} diff --git a/openstack_dashboard/test/api_tests/network_rest_tests.py b/openstack_dashboard/test/api_tests/network_rest_tests.py new file mode 100644 index 0000000000..a156133eed --- /dev/null +++ b/openstack_dashboard/test/api_tests/network_rest_tests.py @@ -0,0 +1,34 @@ +# 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 mock + +from openstack_dashboard.api.rest import network +from openstack_dashboard.test.api_tests import rest_test_utils +from openstack_dashboard.test import helpers as test + + +class RestNetworkApiSecurityGroupTests(test.RestAPITestCase): + + @mock.patch.object(network.api, 'network') + def test_security_group_detailed(self, client): + request = rest_test_utils.construct_request() + client.security_group_list.return_value = [ + mock.Mock(**{'to_dict.return_value': {'name': 'default'}}), + ] + + response = network.SecurityGroups().get(request) + self.assertStatusCode(response, 200) + self.assertEqual(response.content, + '{"items": [{"name": "default"}]}') + client.security_group_list.assert_called_once_with(request) diff --git a/openstack_dashboard/test/helpers.py b/openstack_dashboard/test/helpers.py index de4b75fed7..c42d6f07be 100644 --- a/openstack_dashboard/test/helpers.py +++ b/openstack_dashboard/test/helpers.py @@ -20,6 +20,7 @@ import collections import copy from functools import wraps # noqa import os +import testtools from ceilometerclient.v2 import client as ceilometer_client from cinderclient import client as cinder_client @@ -424,6 +425,25 @@ class APITestCase(TestCase): return self.saharaclient +class RestAPITestCase(testtools.TestCase): + """Testing APIs. + + For use with tests which deal with the underlying clients rather than + stubbing out the openstack_dashboard.api.* methods. + """ + + def assertStatusCode(self, response, expected_code): + """Validates an expected status code. + + Matches camel case of other assert functions + """ + if response.status_code == expected_code: + return + self.fail('status code %r != %r: %s' % (response.status_code, + expected_code, + response.content)) + + @unittest.skipUnless(os.environ.get('WITH_SELENIUM', False), "The WITH_SELENIUM env variable is not set.") class SeleniumTestCase(horizon_helpers.SeleniumTestCase):