From 82257e8aec4b177d00984ff48442e2a8826070ce Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Fri, 11 Mar 2016 13:31:30 -0600 Subject: [PATCH] Add address scope CRUD Add address scope CRUD which is new in Mitaka. This patch set includes the following: - AddressScope resource - Proxy interface updates for the resource - Related resource updates - Documentation updates - Unit tests - Functional tests Applicable references for this support: - https://review.openstack.org/#/c/180267/ - https://github.com/openstack/neutron/blob/master/doc/source/devref/address_scopes.rst - https://github.com/openstack/neutron/blob/master/neutron/extensions/address_scope.py Change-Id: I8d1864f5ef44841c948c5357496e01c83f7a6341 --- doc/source/users/resources/network/index.rst | 1 + .../resources/network/v2/address_scope.rst | 12 +++ openstack/network/v2/_proxy.py | 86 +++++++++++++++++++ openstack/network/v2/address_scope.py | 40 +++++++++ openstack/network/v2/network.py | 4 + openstack/network/v2/subnet_pool.py | 2 + .../network/v2/test_address_scope.py | 62 +++++++++++++ .../unit/network/v2/test_address_scope.py | 47 ++++++++++ .../tests/unit/network/v2/test_network.py | 6 ++ openstack/tests/unit/network/v2/test_proxy.py | 32 +++++++ .../tests/unit/network/v2/test_subnet_pool.py | 2 + 11 files changed, 294 insertions(+) create mode 100644 doc/source/users/resources/network/v2/address_scope.rst create mode 100644 openstack/network/v2/address_scope.py create mode 100644 openstack/tests/functional/network/v2/test_address_scope.py create mode 100644 openstack/tests/unit/network/v2/test_address_scope.py diff --git a/doc/source/users/resources/network/index.rst b/doc/source/users/resources/network/index.rst index df650029f..005eea37b 100644 --- a/doc/source/users/resources/network/index.rst +++ b/doc/source/users/resources/network/index.rst @@ -4,6 +4,7 @@ Network Resources .. toctree:: :maxdepth: 1 + v2/address_scope v2/availability_zone v2/extension v2/floating_ip diff --git a/doc/source/users/resources/network/v2/address_scope.rst b/doc/source/users/resources/network/v2/address_scope.rst new file mode 100644 index 000000000..6b8274ab7 --- /dev/null +++ b/doc/source/users/resources/network/v2/address_scope.rst @@ -0,0 +1,12 @@ +openstack.network.v2.address_scope +================================== + +.. automodule:: openstack.network.v2.address_scope + +The AddressScope Class +---------------------- + +The ``AddressScope`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.network.v2.address_scope.AddressScope + :members: diff --git a/openstack/network/v2/_proxy.py b/openstack/network/v2/_proxy.py index 59ab084ea..417b8903a 100644 --- a/openstack/network/v2/_proxy.py +++ b/openstack/network/v2/_proxy.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack.network.v2 import address_scope as _address_scope from openstack.network.v2 import availability_zone from openstack.network.v2 import extension from openstack.network.v2 import floating_ip as _floating_ip @@ -35,6 +36,91 @@ from openstack import resource class Proxy(proxy.BaseProxy): + def create_address_scope(self, **attrs): + """Create a new address scope from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.network.v2.address_scope.AddressScope`, + comprised of the properties on the AddressScope class. + + :returns: The results of address scope creation + :rtype: :class:`~openstack.network.v2.address_scope.AddressScope` + """ + return self._create(_address_scope.AddressScope, **attrs) + + def delete_address_scope(self, address_scope, ignore_missing=True): + """Delete an address scope + + :param address_scope: The value can be either the ID of an + address scope or + a :class:`~openstack.network.v2.address_scope.AddressScope` + instance. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be + raised when the address scope does not exist. + When set to ``True``, no exception will be set when + attempting to delete a nonexistent address scope. + + :returns: ``None`` + """ + self._delete(_address_scope.AddressScope, address_scope, + ignore_missing=ignore_missing) + + def find_address_scope(self, name_or_id, ignore_missing=True): + """Find a single address scope + + :param name_or_id: The name or ID of an address scope. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be + raised when the resource does not exist. + When set to ``True``, None will be returned when + attempting to find a nonexistent resource. + :returns: One :class:`~openstack.network.v2.address_scope.AddressScope` + or None + """ + return self._find(_address_scope.AddressScope, name_or_id, + ignore_missing=ignore_missing) + + def get_address_scope(self, address_scope): + """Get a single address scope + + :param address_scope: The value can be the ID of an address scope or a + :class:`~openstack.network.v2.address_scope.AddressScope` instance. + + :returns: One :class:`~openstack.network.v2.address_scope.AddressScope` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + return self._get(_address_scope.AddressScope, address_scope) + + def address_scopes(self, **query): + """Return a generator of address scopes + + :param kwargs \*\*query: Optional query parameters to be sent to limit + the resources being returned. + + :returns: A generator of address scope objects + :rtype: :class:`~openstack.network.v2.address_scope.AddressScope` + """ + return self._list(_address_scope.AddressScope, + paginated=False, + **query) + + def update_address_scope(self, address_scope, **attrs): + """Update an address scope + + :param address_scope: Either the ID of an address scope or a + :class:`~openstack.network.v2.address_scope.AddressScope` instance. + :attrs kwargs: The attributes to update on the address scope + represented by ``value``. + + :returns: The updated address scope + :rtype: :class:`~openstack.network.v2.address_scope.AddressScope` + """ + return self._update(_address_scope.AddressScope, + address_scope, + **attrs) + def availability_zones(self): """Return a generator of availability zones diff --git a/openstack/network/v2/address_scope.py b/openstack/network/v2/address_scope.py new file mode 100644 index 000000000..2475553ed --- /dev/null +++ b/openstack/network/v2/address_scope.py @@ -0,0 +1,40 @@ +# 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.network import network_service +from openstack import resource + + +class AddressScope(resource.Resource): + resource_key = 'address_scope' + resources_key = 'address_scopes' + base_path = '/address-scopes' + service = network_service.NetworkService() + + # capabilities + allow_create = True + allow_retrieve = True + allow_update = True + allow_delete = True + allow_list = True + + # Properties + #: The address scope name. + name = resource.prop('name') + #: The ID of the project that owns the address scope. + project_id = resource.prop('tenant_id') + #: The IP address family of the address scope. + #: *Type: int* + ip_version = resource.prop('ip_version', type=int) + #: Indicates whether this address scope is shared across all projects. + #: *Type: bool* + is_shared = resource.prop('shared', type=bool) diff --git a/openstack/network/v2/network.py b/openstack/network/v2/network.py index 0e5b52976..8225f279c 100644 --- a/openstack/network/v2/network.py +++ b/openstack/network/v2/network.py @@ -34,6 +34,10 @@ class Network(resource.Resource): #: Availability zones for the network. #: *Type: list of availability zone names* availability_zones = resource.prop('availability_zones') + #: The ID of the IPv4 address scope for the network. + ipv4_address_scope_id = resource.prop('ipv4_address_scope') + #: The ID of the IPv6 address scope for the network. + ipv6_address_scope_id = resource.prop('ipv6_address_scope') #: The administrative state of the network, which is up ``True`` or #: down ``False``. *Type: bool* is_admin_state_up = resource.prop('admin_state_up', type=bool) diff --git a/openstack/network/v2/subnet_pool.py b/openstack/network/v2/subnet_pool.py index c19f5f7e9..492968e86 100644 --- a/openstack/network/v2/subnet_pool.py +++ b/openstack/network/v2/subnet_pool.py @@ -28,6 +28,8 @@ class SubnetPool(resource.Resource): allow_list = True # Properties + #: The ID of the address scope associated with the subnet pool. + address_scope_id = resource.prop('address_scope_id') #: The length of the prefix to allocate when the cidr or prefixlen #: attributes are omitted when creating a subnet. *Type: int* default_prefix_length = resource.prop('default_prefixlen', type=int) diff --git a/openstack/tests/functional/network/v2/test_address_scope.py b/openstack/tests/functional/network/v2/test_address_scope.py new file mode 100644 index 000000000..223f15732 --- /dev/null +++ b/openstack/tests/functional/network/v2/test_address_scope.py @@ -0,0 +1,62 @@ +# 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 uuid + +from openstack.network.v2 import address_scope as _address_scope +from openstack.tests.functional import base + + +class TestAddressScope(base.BaseFunctionalTest): + + ADDRESS_SCOPE_ID = None + ADDRESS_SCOPE_NAME = uuid.uuid4().hex + ADDRESS_SCOPE_NAME_UPDATED = uuid.uuid4().hex + IS_SHARED = False + IP_VERSION = 4 + + @classmethod + def setUpClass(cls): + super(TestAddressScope, cls).setUpClass() + address_scope = cls.conn.network.create_address_scope( + ip_version=cls.IP_VERSION, + name=cls.ADDRESS_SCOPE_NAME, + shared=cls.IS_SHARED, + ) + assert isinstance(address_scope, _address_scope.AddressScope) + cls.assertIs(cls.ADDRESS_SCOPE_NAME, address_scope.name) + cls.ADDRESS_SCOPE_ID = address_scope.id + + @classmethod + def tearDownClass(cls): + sot = cls.conn.network.delete_address_scope(cls.ADDRESS_SCOPE_ID) + cls.assertIs(None, sot) + + def test_find(self): + sot = self.conn.network.find_address_scope(self.ADDRESS_SCOPE_NAME) + self.assertEqual(self.ADDRESS_SCOPE_ID, sot.id) + + def test_get(self): + sot = self.conn.network.get_address_scope(self.ADDRESS_SCOPE_ID) + self.assertEqual(self.ADDRESS_SCOPE_NAME, sot.name) + self.assertEqual(self.IS_SHARED, sot.is_shared) + self.assertEqual(self.IP_VERSION, sot.ip_version) + + def test_list(self): + names = [o.name for o in self.conn.network.address_scopes()] + self.assertIn(self.ADDRESS_SCOPE_NAME, names) + + def test_update(self): + sot = self.conn.network.update_address_scope( + self.ADDRESS_SCOPE_ID, + name=self.ADDRESS_SCOPE_NAME_UPDATED) + self.assertEqual(self.ADDRESS_SCOPE_NAME_UPDATED, sot.name) diff --git a/openstack/tests/unit/network/v2/test_address_scope.py b/openstack/tests/unit/network/v2/test_address_scope.py new file mode 100644 index 000000000..616644cbd --- /dev/null +++ b/openstack/tests/unit/network/v2/test_address_scope.py @@ -0,0 +1,47 @@ +# 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 testtools + +from openstack.network.v2 import address_scope + +IDENTIFIER = 'IDENTIFIER' +EXAMPLE = { + 'id': IDENTIFIER, + 'ip_version': 4, + 'name': '1', + 'shared': True, + 'tenant_id': '2', +} + + +class TestAddressScope(testtools.TestCase): + + def test_basic(self): + sot = address_scope.AddressScope() + self.assertEqual('address_scope', sot.resource_key) + self.assertEqual('address_scopes', sot.resources_key) + self.assertEqual('/address-scopes', sot.base_path) + self.assertEqual('network', sot.service.service_type) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_retrieve) + self.assertTrue(sot.allow_update) + self.assertTrue(sot.allow_delete) + self.assertTrue(sot.allow_list) + + def test_make_it(self): + sot = address_scope.AddressScope(EXAMPLE) + self.assertEqual(EXAMPLE['id'], sot.id) + self.assertEqual(EXAMPLE['ip_version'], sot.ip_version) + self.assertEqual(EXAMPLE['name'], sot.name) + self.assertTrue(sot.is_shared) + self.assertEqual(EXAMPLE['tenant_id'], sot.project_id) diff --git a/openstack/tests/unit/network/v2/test_network.py b/openstack/tests/unit/network/v2/test_network.py index 5c66b9e61..5dab2f78c 100644 --- a/openstack/tests/unit/network/v2/test_network.py +++ b/openstack/tests/unit/network/v2/test_network.py @@ -32,6 +32,8 @@ EXAMPLE = { 'port_security_enabled': True, 'availability_zone_hints': ['15', '16'], 'availability_zones': ['16'], + 'ipv4_address_scope': '17', + 'ipv6_address_scope': '18', } @@ -72,3 +74,7 @@ class TestNetwork(testtools.TestCase): sot.availability_zone_hints) self.assertEqual(EXAMPLE['availability_zones'], sot.availability_zones) + self.assertEqual(EXAMPLE['ipv4_address_scope'], + sot.ipv4_address_scope_id) + self.assertEqual(EXAMPLE['ipv6_address_scope'], + sot.ipv6_address_scope_id) diff --git a/openstack/tests/unit/network/v2/test_proxy.py b/openstack/tests/unit/network/v2/test_proxy.py index bc79c5f45..a41e78399 100644 --- a/openstack/tests/unit/network/v2/test_proxy.py +++ b/openstack/tests/unit/network/v2/test_proxy.py @@ -13,6 +13,7 @@ import mock from openstack.network.v2 import _proxy +from openstack.network.v2 import address_scope from openstack.network.v2 import availability_zone from openstack.network.v2 import extension from openstack.network.v2 import floating_ip @@ -40,6 +41,37 @@ class TestNetworkProxy(test_proxy_base.TestProxyBase): super(TestNetworkProxy, self).setUp() self.proxy = _proxy.Proxy(self.session) + def test_address_scope_create_attrs(self): + self.verify_create(self.proxy.create_address_scope, + address_scope.AddressScope) + + def test_address_scope_delete(self): + self.verify_delete(self.proxy.delete_address_scope, + address_scope.AddressScope, + False) + + def test_address_scope_delete_ignore(self): + self.verify_delete(self.proxy.delete_address_scope, + address_scope.AddressScope, + True) + + def test_address_scope_find(self): + self.verify_find(self.proxy.find_address_scope, + address_scope.AddressScope) + + def test_address_scope_get(self): + self.verify_get(self.proxy.get_address_scope, + address_scope.AddressScope) + + def test_address_scopes(self): + self.verify_list(self.proxy.address_scopes, + address_scope.AddressScope, + paginated=False) + + def test_address_scope_update(self): + self.verify_update(self.proxy.update_address_scope, + address_scope.AddressScope) + def test_availability_zones(self): self.verify_list_no_kwargs(self.proxy.availability_zones, availability_zone.AvailabilityZone, diff --git a/openstack/tests/unit/network/v2/test_subnet_pool.py b/openstack/tests/unit/network/v2/test_subnet_pool.py index 04c7a8650..9296abee1 100644 --- a/openstack/tests/unit/network/v2/test_subnet_pool.py +++ b/openstack/tests/unit/network/v2/test_subnet_pool.py @@ -26,6 +26,7 @@ EXAMPLE = { 'prefixes': ['10.0.2.0/24', '10.0.4.0/24'], 'ip_version': 4, 'shared': True, + 'address_scope_id': '11', } @@ -58,3 +59,4 @@ class TestSubnetpool(testtools.TestCase): self.assertEqual(EXAMPLE['prefixes'], sot.prefixes) self.assertEqual(EXAMPLE['ip_version'], sot.ip_version) self.assertTrue(sot.is_shared) + self.assertEqual(EXAMPLE['address_scope_id'], sot.address_scope_id)