From a4d75498b4961ddf8873d1ac05226f2de4c65c55 Mon Sep 17 00:00:00 2001 From: Ankur Gupta Date: Thu, 2 Feb 2017 14:42:24 -0600 Subject: [PATCH] Introduce Base for Octavia (load balancing) Change-Id: I3aa5b8af9e5f6dfbd3e71645fc7324e19933afef --- doc/source/users/proxies/load_balancer_v2.rst | 22 +++++ openstack/load_balancer/__init__.py | 0 .../load_balancer/load_balancer_service.py | 26 ++++++ openstack/load_balancer/v2/__init__.py | 0 openstack/load_balancer/v2/_proxy.py | 82 +++++++++++++++++++ openstack/load_balancer/v2/load_balancer.py | 55 +++++++++++++ openstack/load_balancer/version.py | 30 +++++++ openstack/profile.py | 2 + .../functional/load_balancer/__init__.py | 0 .../functional/load_balancer/v2/__init__.py | 0 .../load_balancer/v2/test_load_balancer.py | 52 ++++++++++++ .../tests/unit/load_balancer/__init__.py | 0 .../unit/load_balancer/test_load_balancer.py | 69 ++++++++++++++++ .../test_load_balancer_service.py | 28 +++++++ .../tests/unit/load_balancer/test_proxy.py | 42 ++++++++++ .../tests/unit/load_balancer/test_version.py | 43 ++++++++++ openstack/tests/unit/test_connection.py | 2 + openstack/tests/unit/test_profile.py | 2 + 18 files changed, 455 insertions(+) create mode 100644 doc/source/users/proxies/load_balancer_v2.rst create mode 100644 openstack/load_balancer/__init__.py create mode 100644 openstack/load_balancer/load_balancer_service.py create mode 100644 openstack/load_balancer/v2/__init__.py create mode 100644 openstack/load_balancer/v2/_proxy.py create mode 100644 openstack/load_balancer/v2/load_balancer.py create mode 100644 openstack/load_balancer/version.py create mode 100644 openstack/tests/functional/load_balancer/__init__.py create mode 100644 openstack/tests/functional/load_balancer/v2/__init__.py create mode 100644 openstack/tests/functional/load_balancer/v2/test_load_balancer.py create mode 100644 openstack/tests/unit/load_balancer/__init__.py create mode 100644 openstack/tests/unit/load_balancer/test_load_balancer.py create mode 100644 openstack/tests/unit/load_balancer/test_load_balancer_service.py create mode 100644 openstack/tests/unit/load_balancer/test_proxy.py create mode 100644 openstack/tests/unit/load_balancer/test_version.py diff --git a/doc/source/users/proxies/load_balancer_v2.rst b/doc/source/users/proxies/load_balancer_v2.rst new file mode 100644 index 000000000..220c4f708 --- /dev/null +++ b/doc/source/users/proxies/load_balancer_v2.rst @@ -0,0 +1,22 @@ +Load Balancer v2 API +==================== + +.. automodule:: openstack.load_balancer.v2._proxy + +The LoadBalancer Class +---------------------- + +The load_balancer high-level interface is available through the +``load_balancer`` member of a :class:`~openstack.connection.Connection` object. +The ``load_balancer`` member will only be added if the service is detected. + +Load Balancer Operations +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.load_balancer.v2._proxy.Proxy + + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.create_load_balancer + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.get_load_balancer + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.load_balancers + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.delete_load_balancer + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.find_load_balancer diff --git a/openstack/load_balancer/__init__.py b/openstack/load_balancer/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/load_balancer/load_balancer_service.py b/openstack/load_balancer/load_balancer_service.py new file mode 100644 index 000000000..1ff22b22f --- /dev/null +++ b/openstack/load_balancer/load_balancer_service.py @@ -0,0 +1,26 @@ +# 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 import service_filter + + +class LoadBalancerService(service_filter.ServiceFilter): + """The load balancer service.""" + + valid_versions = [service_filter.ValidVersion('v2')] + + def __init__(self, version=None): + """Create a load balancer service.""" + super(LoadBalancerService, self).__init__( + service_type='load_balancer', + version=version + ) diff --git a/openstack/load_balancer/v2/__init__.py b/openstack/load_balancer/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/load_balancer/v2/_proxy.py b/openstack/load_balancer/v2/_proxy.py new file mode 100644 index 000000000..224fddb57 --- /dev/null +++ b/openstack/load_balancer/v2/_proxy.py @@ -0,0 +1,82 @@ +# 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.load_balancer.v2 import load_balancer as _lb +from openstack import proxy2 + + +class Proxy(proxy2.BaseProxy): + + def create_load_balancer(self, **attrs): + """Create a new load balancer from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.load_balancer.v2. + load_balancer.LoadBalancer`, + comprised of the properties on the + LoadBalancer class. + + :returns: The results of load balancer creation + :rtype: :class:`~openstack.load_balancer.v2.load_balancer.LoadBalancer` + """ + return self._create(_lb.LoadBalancer, **attrs) + + def get_load_balancer(self, *attrs): + """Get a load balancer + + :param load_balancer: The value can be the name of a load balancer + or :class:`~openstack.load_balancer.v2.load_balancer.LoadBalancer` + instance. + + :returns: One + :class:`~openstack.load_balancer.v2.load_balancer.LoadBalancer` + """ + return self._get(_lb.LoadBalancer, *attrs) + + def load_balancers(self, **query): + """Retrieve a generator of load balancers + + :returns: A generator of load balancer instances + """ + return self._list(_lb.LoadBalancer, paginated=True, **query) + + def delete_load_balancer(self, load_balancer, ignore_missing=True): + """Delete a load balancer + + :param load_balancer: The load_balancer can be either the name or a + :class:`~openstack.load_balancer.v2.load_balancer.LoadBalancer` + instance + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the load balancer does not exist. + When set to ``True``, no exception will be set when attempting to + delete a nonexistent load balancer. + + :returns: ``None`` + """ + return self._delete(_lb.LoadBalancer, load_balancer, + ignore_missing=ignore_missing) + + def find_load_balancer(self, name_or_id, ignore_missing=True): + """Find a single load balancer + + :param name_or_id: The name or ID of a load balancer + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised + when the load balancer does not exist. + When set to ``True``, no exception will be set when attempting + to delete a nonexistent load balancer. + + :returns: ``None`` + """ + return self._find(_lb.LoadBalancer, name_or_id, + ignore_missing=ignore_missing) diff --git a/openstack/load_balancer/v2/load_balancer.py b/openstack/load_balancer/v2/load_balancer.py new file mode 100644 index 000000000..509f0ae80 --- /dev/null +++ b/openstack/load_balancer/v2/load_balancer.py @@ -0,0 +1,55 @@ +# 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.load_balancer import load_balancer_service as lb_service +from openstack import resource2 as resource + + +class LoadBalancer(resource.Resource): + resource_key = 'loadbalancer' + resources_key = 'loadbalancers' + base_path = '/loadbalancers' + service = lb_service.LoadBalancerService() + + # capabilities + allow_create = True + allow_list = True + allow_get = True + allow_delete = True + + #: Properties + #: Timestamp when the load balancer was created + created_at = resource.Body('created_at') + #: The load balancer description + description = resource.Body('description') + #: The administrative state of the load balancer *Type: bool* + is_admin_state_up = resource.Body('admin_state_up', type=bool) + #: List of listeners associated with this load balancer + listeners = resource.Body('listeners', type=list) + #: The load balancer name + name = resource.Body('name') + #: Operating status of the load balancer + operating_status = resource.Body('operating_status') + #: List of pools associated with this load balancer + pools = resource.Body('pools', type=list) + #: The ID of the project this load balancer is associated with. + project_id = resource.Body('project_id') + #: The provisioning status of this load balancer + provisioning_status = resource.Body('provisioning_status') + #: VIP address of load balancer + vip_address = resource.Body('vip_address') + #: VIP port ID + vip_port_id = resource.Body('vip_port_id') + #: VIP subnet ID + vip_subnet_id = resource.Body('vip_subnet_id') + #: Timestamp when the load balancer was last updated + updated_at = resource.Body('updated_at') diff --git a/openstack/load_balancer/version.py b/openstack/load_balancer/version.py new file mode 100644 index 000000000..4a829c2e3 --- /dev/null +++ b/openstack/load_balancer/version.py @@ -0,0 +1,30 @@ +# 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.load_balancer import load_balancer_service as lb_service +from openstack import resource2 as resource + + +class Version(resource.Resource): + resource_key = 'version' + resources_key = 'versions' + base_path = '/' + service = lb_service.LoadBalancerService( + version=lb_service.LoadBalancerService.UNVERSIONED + ) + + # capabilities + allow_list = True + + # Properties + links = resource.Body('links') + status = resource.Body('status') diff --git a/openstack/profile.py b/openstack/profile.py index 2cd7f2de8..d82b88a6d 100644 --- a/openstack/profile.py +++ b/openstack/profile.py @@ -64,6 +64,7 @@ from openstack import exceptions from openstack.identity import identity_service from openstack.image import image_service from openstack.key_manager import key_manager_service +from openstack.load_balancer import load_balancer_service as lb_service from openstack.message import message_service from openstack import module_loader from openstack.network import network_service @@ -102,6 +103,7 @@ class Profile(object): self._add_service(identity_service.IdentityService(version="v3")) self._add_service(image_service.ImageService(version="v2")) self._add_service(key_manager_service.KeyManagerService(version="v1")) + self._add_service(lb_service.LoadBalancerService(version="v2")) self._add_service(message_service.MessageService(version="v1")) self._add_service(network_service.NetworkService(version="v2")) self._add_service( diff --git a/openstack/tests/functional/load_balancer/__init__.py b/openstack/tests/functional/load_balancer/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/functional/load_balancer/v2/__init__.py b/openstack/tests/functional/load_balancer/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/functional/load_balancer/v2/test_load_balancer.py b/openstack/tests/functional/load_balancer/v2/test_load_balancer.py new file mode 100644 index 000000000..4082a1194 --- /dev/null +++ b/openstack/tests/functional/load_balancer/v2/test_load_balancer.py @@ -0,0 +1,52 @@ +# 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.load_balancer.v2 import load_balancer +from openstack.tests.functional import base + + +class TestLoadBalancer(base.BaseFunctionalTest): + + NAME = uuid.uuid4().hex + ID = None + VIP_SUBNET_ID = uuid.uuid4().hex + + @classmethod + def setUpClass(cls): + super(TestLoadBalancer, cls).setUpClass() + test_lb = cls.conn.load_balancer.create_load_balancer( + name=cls.NAME, vip_subnet_id=cls.VIP_SUBNET_ID) + assert isinstance(test_lb, load_balancer.LoadBalancer) + cls.assertIs(cls.NAME, test_lb.name) + cls.ID = test_lb.id + + @classmethod + def tearDownClass(cls): + test_lb = cls.conn.load_balancer.delete_load_balancer( + cls.ID, ignore_missing=False) + cls.assertIs(None, test_lb) + + def test_find(self): + test_lb = self.conn.load_balancer.find_load_balancer(self.NAME) + self.assertEqual(self.ID, test_lb.id) + + def test_get(self): + test_lb = self.conn.load_balancer.get_load_balancer(self.ID) + self.assertEqual(self.NAME, test_lb.name) + self.assertEqual(self.ID, test_lb.id) + self.assertEqual(self.VIP_SUBNET_ID, test_lb.vip_subnet_id) + + def test_list(self): + names = [lb.name for lb in self.conn.load_balancer.load_balancers()] + self.assertIn(self.NAME, names) diff --git a/openstack/tests/unit/load_balancer/__init__.py b/openstack/tests/unit/load_balancer/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstack/tests/unit/load_balancer/test_load_balancer.py b/openstack/tests/unit/load_balancer/test_load_balancer.py new file mode 100644 index 000000000..0e832ab82 --- /dev/null +++ b/openstack/tests/unit/load_balancer/test_load_balancer.py @@ -0,0 +1,69 @@ +# 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.load_balancer.v2 import load_balancer + +IDENTIFIER = 'IDENTIFIER' +EXAMPLE = { + 'admin_state_up': True, + 'created_at': '3', + 'description': 'fake_description', + 'id': IDENTIFIER, + 'listeners': [{'id', '4'}], + 'name': 'test_load_balancer', + 'operating_status': '6', + 'provisioning_status': '7', + 'project_id': '8', + 'vip_address': '9', + 'vip_subnet_id': '10', + 'vip_port_id': '11', + 'pools': [{'id', '13'}], +} + + +class TestLoadBalancer(testtools.TestCase): + + def test_basic(self): + test_load_balancer = load_balancer.LoadBalancer() + self.assertEqual('loadbalancer', test_load_balancer.resource_key) + self.assertEqual('loadbalancers', test_load_balancer.resources_key) + self.assertEqual('/loadbalancers', test_load_balancer.base_path) + self.assertEqual('load_balancer', + test_load_balancer.service.service_type) + self.assertTrue(test_load_balancer.allow_create) + self.assertTrue(test_load_balancer.allow_get) + self.assertTrue(test_load_balancer.allow_delete) + self.assertTrue(test_load_balancer.allow_list) + + def test_make_it(self): + test_load_balancer = load_balancer.LoadBalancer(**EXAMPLE) + self.assertTrue(test_load_balancer.is_admin_state_up) + self.assertEqual(EXAMPLE['created_at'], test_load_balancer.created_at), + self.assertEqual(EXAMPLE['description'], + test_load_balancer.description) + self.assertEqual(EXAMPLE['id'], test_load_balancer.id) + self.assertEqual(EXAMPLE['listeners'], test_load_balancer.listeners) + self.assertEqual(EXAMPLE['name'], test_load_balancer.name) + self.assertEqual(EXAMPLE['operating_status'], + test_load_balancer.operating_status) + self.assertEqual(EXAMPLE['provisioning_status'], + test_load_balancer.provisioning_status) + self.assertEqual(EXAMPLE['project_id'], test_load_balancer.project_id) + self.assertEqual(EXAMPLE['vip_address'], + test_load_balancer.vip_address) + self.assertEqual(EXAMPLE['vip_subnet_id'], + test_load_balancer.vip_subnet_id) + self.assertEqual(EXAMPLE['vip_port_id'], + test_load_balancer.vip_port_id) + self.assertEqual(EXAMPLE['pools'], test_load_balancer.pools) diff --git a/openstack/tests/unit/load_balancer/test_load_balancer_service.py b/openstack/tests/unit/load_balancer/test_load_balancer_service.py new file mode 100644 index 000000000..dd11ae55f --- /dev/null +++ b/openstack/tests/unit/load_balancer/test_load_balancer_service.py @@ -0,0 +1,28 @@ +# 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.load_balancer import load_balancer_service as lb_service + + +class TestLoadBalancingService(testtools.TestCase): + + def test_service(self): + sot = lb_service.LoadBalancerService() + self.assertEqual('load_balancer', sot.service_type) + self.assertEqual('public', sot.interface) + self.assertIsNone(sot.region) + self.assertIsNone(sot.service_name) + self.assertEqual(1, len(sot.valid_versions)) + self.assertEqual('v2', sot.valid_versions[0].module) + self.assertEqual('v2', sot.valid_versions[0].path) diff --git a/openstack/tests/unit/load_balancer/test_proxy.py b/openstack/tests/unit/load_balancer/test_proxy.py new file mode 100644 index 000000000..97cdec134 --- /dev/null +++ b/openstack/tests/unit/load_balancer/test_proxy.py @@ -0,0 +1,42 @@ +# 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.load_balancer.v2 import _proxy +from openstack.load_balancer.v2 import load_balancer as lb +from openstack.tests.unit import test_proxy_base2 + + +class TestLoadBalancerProxy(test_proxy_base2.TestProxyBase): + def setUp(self): + super(TestLoadBalancerProxy, self).setUp() + self.proxy = _proxy.Proxy(self.session) + + def test_load_balancers(self): + self.verify_list(self.proxy.load_balancers, + lb.LoadBalancer, + paginated=True) + + def test_load_balancer_get(self): + self.verify_get(self.proxy.get_load_balancer, + lb.LoadBalancer) + + def test_load_balancer_create(self): + self.verify_create(self.proxy.create_load_balancer, + lb.LoadBalancer) + + def test_load_balancer_delete(self): + self.verify_delete(self.proxy.delete_load_balancer, + lb.LoadBalancer, True) + + def test_load_balancer_find(self): + self.verify_find(self.proxy.find_load_balancer, + lb.LoadBalancer) diff --git a/openstack/tests/unit/load_balancer/test_version.py b/openstack/tests/unit/load_balancer/test_version.py new file mode 100644 index 000000000..1b77eda26 --- /dev/null +++ b/openstack/tests/unit/load_balancer/test_version.py @@ -0,0 +1,43 @@ +# 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.load_balancer import version + +IDENTIFIER = 'IDENTIFIER' +EXAMPLE = { + 'id': IDENTIFIER, + 'links': '2', + 'status': '3', +} + + +class TestVersion(testtools.TestCase): + + def test_basic(self): + sot = version.Version() + self.assertEqual('version', sot.resource_key) + self.assertEqual('versions', sot.resources_key) + self.assertEqual('/', sot.base_path) + self.assertEqual('load_balancer', sot.service.service_type) + self.assertFalse(sot.allow_create) + self.assertFalse(sot.allow_get) + self.assertFalse(sot.allow_update) + self.assertFalse(sot.allow_delete) + self.assertTrue(sot.allow_list) + + def test_make_it(self): + sot = version.Version(**EXAMPLE) + self.assertEqual(EXAMPLE['id'], sot.id) + self.assertEqual(EXAMPLE['links'], sot.links) + self.assertEqual(EXAMPLE['status'], sot.status) diff --git a/openstack/tests/unit/test_connection.py b/openstack/tests/unit/test_connection.py index f84abf7fe..229dbb728 100644 --- a/openstack/tests/unit/test_connection.py +++ b/openstack/tests/unit/test_connection.py @@ -147,6 +147,8 @@ class TestConnection(base.TestCase): conn.network.__class__.__module__) self.assertEqual('openstack.object_store.v1._proxy', conn.object_store.__class__.__module__) + self.assertEqual('openstack.load_balancer.v2._proxy', + conn.load_balancer.__class__.__module__) self.assertEqual('openstack.orchestration.v1._proxy', conn.orchestration.__class__.__module__) self.assertEqual('openstack.telemetry.v2._proxy', diff --git a/openstack/tests/unit/test_profile.py b/openstack/tests/unit/test_profile.py index 2ca67e028..272918171 100644 --- a/openstack/tests/unit/test_profile.py +++ b/openstack/tests/unit/test_profile.py @@ -27,6 +27,7 @@ class TestProfile(base.TestCase): 'identity', 'image', 'key-manager', + 'load_balancer', 'messaging', 'metering', 'network', @@ -45,6 +46,7 @@ class TestProfile(base.TestCase): self.assertEqual('v1', prof.get_filter('database').version) self.assertEqual('v3', prof.get_filter('identity').version) self.assertEqual('v2', prof.get_filter('image').version) + self.assertEqual('v2', prof.get_filter('load_balancer').version) self.assertEqual('v2', prof.get_filter('network').version) self.assertEqual('v1', prof.get_filter('object-store').version) self.assertEqual('v1', prof.get_filter('orchestration').version)