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
This commit is contained in:
parent
4a30b15561
commit
82257e8aec
@ -4,6 +4,7 @@ Network Resources
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
v2/address_scope
|
||||
v2/availability_zone
|
||||
v2/extension
|
||||
v2/floating_ip
|
||||
|
12
doc/source/users/resources/network/v2/address_scope.rst
Normal file
12
doc/source/users/resources/network/v2/address_scope.rst
Normal file
@ -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:
|
@ -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
|
||||
|
||||
|
40
openstack/network/v2/address_scope.py
Normal file
40
openstack/network/v2/address_scope.py
Normal file
@ -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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
62
openstack/tests/functional/network/v2/test_address_scope.py
Normal file
62
openstack/tests/functional/network/v2/test_address_scope.py
Normal file
@ -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)
|
47
openstack/tests/unit/network/v2/test_address_scope.py
Normal file
47
openstack/tests/unit/network/v2/test_address_scope.py
Normal file
@ -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)
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user