diff --git a/doc/source/user/proxies/load_balancer_v2.rst b/doc/source/user/proxies/load_balancer_v2.rst index 65b5afa71..ceecfcfd3 100644 --- a/doc/source/user/proxies/load_balancer_v2.rst +++ b/doc/source/user/proxies/load_balancer_v2.rst @@ -116,3 +116,15 @@ Flavor Profile Operations .. automethod:: openstack.load_balancer.v2._proxy.Proxy.delete_flavor_profile .. automethod:: openstack.load_balancer.v2._proxy.Proxy.find_flavor_profile .. automethod:: openstack.load_balancer.v2._proxy.Proxy.update_flavor_profile + +Flavor Operations +^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.load_balancer.v2._proxy.Proxy + + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.create_flavor + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.get_flavor + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.flavors + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.delete_flavor + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.find_flavor + .. automethod:: openstack.load_balancer.v2._proxy.Proxy.update_flavor diff --git a/doc/source/user/resources/load_balancer/index.rst b/doc/source/user/resources/load_balancer/index.rst index e06159c25..dbb8226f2 100644 --- a/doc/source/user/resources/load_balancer/index.rst +++ b/doc/source/user/resources/load_balancer/index.rst @@ -13,3 +13,4 @@ Load Balancer Resources v2/l7_rule v2/provider v2/flavor_profile + v2/flavor diff --git a/doc/source/user/resources/load_balancer/v2/flavor.rst b/doc/source/user/resources/load_balancer/v2/flavor.rst new file mode 100644 index 000000000..57b97ba0b --- /dev/null +++ b/doc/source/user/resources/load_balancer/v2/flavor.rst @@ -0,0 +1,12 @@ +openstack.load_balancer.v2.flavor +================================= + +.. automodule:: openstack.load_balancer.v2.flavor + +The Flavor Class +---------------- + +The ``Flavor`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.load_balancer.v2.flavor.Flavor + :members: diff --git a/openstack/load_balancer/v2/_proxy.py b/openstack/load_balancer/v2/_proxy.py index 6741a0243..edf42be30 100644 --- a/openstack/load_balancer/v2/_proxy.py +++ b/openstack/load_balancer/v2/_proxy.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack.load_balancer.v2 import flavor as _flavor from openstack.load_balancer.v2 import flavor_profile as _flavor_profile from openstack.load_balancer.v2 import health_monitor as _hm from openstack.load_balancer.v2 import l7_policy as _l7policy @@ -841,7 +842,7 @@ class Proxy(proxy.Proxy): :class:`~openstack.exceptions.ResourceNotFound` will be raised when the flavor profile does not exist. When set to ``True``, no exception will be set when attempting to - delete a nonexistent load balancer. + delete a nonexistent flavor profile. :returns: ``None`` """ @@ -878,3 +879,77 @@ class Proxy(proxy.Proxy): """ return self._update(_flavor_profile.FlavorProfile, flavor_profile, **attrs) + + def create_flavor(self, **attrs): + """Create a new flavor from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.load_balancer.v2. + flavor.Flavor`, comprised of the properties on the + Flavorclass. + + :returns: The results of flavor creation creation + :rtype: :class:`~openstack.load_balancer.v2.flavor.Flavor` + """ + return self._create(_flavor.Flavor, **attrs) + + def get_flavor(self, *attrs): + """Get a flavor + + :param flavor: The value can be the name of a flavor + or :class:`~openstack.load_balancer.v2.flavor.Flavor` instance. + + :returns: One + :class:`~openstack.load_balancer.v2.flavor.Flavor` + """ + return self._get(_flavor.Flavor, *attrs) + + def flavors(self, **query): + """Retrieve a generator of flavors + + :returns: A generator of flavor instances + """ + return self._list(_flavor.Flavor, **query) + + def delete_flavor(self, flavor, ignore_missing=True): + """Delete a flavor + + :param flavor: The flavorcan be either the name or a + :class:`~openstack.load_balancer.v2.flavor.Flavor` instance + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the flavor does not exist. + When set to ``True``, no exception will be set when attempting to + delete a nonexistent flavor. + + :returns: ``None`` + """ + self._delete(_flavor.Flavor, flavor, ignore_missing=ignore_missing) + + def find_flavor(self, name_or_id, ignore_missing=True): + """Find a single flavor + + :param name_or_id: The name or ID of a flavor + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised + when the flavor does not exist. + When set to ``True``, no exception will be set when attempting + to delete a nonexistent flavor. + + :returns: ``None`` + """ + return self._find(_flavor.Flavor, name_or_id, + ignore_missing=ignore_missing) + + def update_flavor(self, flavor, **attrs): + """Update a flavor + + :param flavor: The flavor can be either the name or a + :class:`~openstack.load_balancer.v2.flavor.Flavor` instance + :param dict attrs: The attributes to update on the flavor + represented by ``flavor``. + + :returns: The updated flavor + :rtype: :class:`~openstack.load_balancer.v2.flavor.Flavor` + """ + return self._update(_flavor.Flavor, flavor, **attrs) diff --git a/openstack/load_balancer/v2/flavor.py b/openstack/load_balancer/v2/flavor.py new file mode 100644 index 000000000..799d97578 --- /dev/null +++ b/openstack/load_balancer/v2/flavor.py @@ -0,0 +1,44 @@ +# Copyright 2019 Rackspace, US Inc. +# +# 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 resource + + +class Flavor(resource.Resource): + resource_key = 'flavor' + resources_key = 'flavors' + base_path = '/lbaas/flavors' + + # capabilities + allow_create = True + allow_fetch = True + allow_commit = True + allow_delete = True + allow_list = True + + _query_mapping = resource.QueryParameters( + 'id', 'name', 'description', 'flavor_profile_id', is_enabled='enabled' + ) + + # Properties + #: The ID of the flavor. + id = resource.Body('id') + #: The name of the flavor. + name = resource.Body('name') + #: The flavor description. + description = resource.Body('description') + #: The associated flavor profile ID + flavor_profile_id = resource.Body('flavor_profile_id') + #: Whether the flavor is enabled for use or not. + is_enabled = resource.Body('enabled') diff --git a/openstack/tests/functional/load_balancer/v2/test_load_balancer.py b/openstack/tests/functional/load_balancer/v2/test_load_balancer.py index 08a340477..74d55b30a 100644 --- a/openstack/tests/functional/load_balancer/v2/test_load_balancer.py +++ b/openstack/tests/functional/load_balancer/v2/test_load_balancer.py @@ -12,6 +12,7 @@ import os +from openstack.load_balancer.v2 import flavor from openstack.load_balancer.v2 import flavor_profile from openstack.load_balancer.v2 import health_monitor from openstack.load_balancer.v2 import l7_policy @@ -35,6 +36,7 @@ class TestLoadBalancer(base.BaseFunctionalTest): VIP_SUBNET_ID = None PROJECT_ID = None FLAVOR_PROFILE_ID = None + FLAVOR_ID = None PROTOCOL = 'HTTP' PROTOCOL_PORT = 80 LB_ALGORITHM = 'ROUND_ROBIN' @@ -51,6 +53,7 @@ class TestLoadBalancer(base.BaseFunctionalTest): L7RULE_VALUE = 'example' AMPHORA = 'amphora' FLAVOR_DATA = '{"loadbalancer_topology": "SINGLE"}' + DESCRIPTION = 'Test description' @classmethod def setUpClass(cls): @@ -75,6 +78,7 @@ class TestLoadBalancer(base.BaseFunctionalTest): self.POOL_NAME = self.getUniqueString() self.UPDATE_NAME = self.getUniqueString() self.FLAVOR_PROFILE_NAME = self.getUniqueString() + self.FLAVOR_NAME = self.getUniqueString() subnets = list(self.conn.network.subnets()) self.VIP_SUBNET_ID = subnets[0].id self.PROJECT_ID = self.conn.session.get_project_id() @@ -94,6 +98,13 @@ class TestLoadBalancer(base.BaseFunctionalTest): self.assertEqual(self.FLAVOR_PROFILE_NAME, test_profile.name) self.FLAVOR_PROFILE_ID = test_profile.id + test_flavor = self.conn.load_balancer.create_flavor( + name=self.FLAVOR_NAME, flavor_profile_id=self.FLAVOR_PROFILE_ID, + is_enabled=True, description=self.DESCRIPTION) + assert isinstance(test_flavor, flavor.Flavor) + self.assertEqual(self.FLAVOR_NAME, test_flavor.name) + self.FLAVOR_ID = test_flavor.id + test_lb = self.conn.load_balancer.create_load_balancer( name=self.LB_NAME, vip_subnet_id=self.VIP_SUBNET_ID, project_id=self.PROJECT_ID) @@ -203,6 +214,9 @@ class TestLoadBalancer(base.BaseFunctionalTest): self.LB_ID, ignore_missing=False) super(TestLoadBalancer, self).tearDown() + self.conn.load_balancer.delete_flavor(self.FLAVOR_ID, + ignore_missing=False) + self.conn.load_balancer.delete_flavor_profile(self.FLAVOR_PROFILE_ID, ignore_missing=False) @@ -533,3 +547,29 @@ class TestLoadBalancer(base.BaseFunctionalTest): test_flavor_profile = self.conn.load_balancer.get_flavor_profile( self.FLAVOR_PROFILE_ID) self.assertEqual(self.FLAVOR_PROFILE_NAME, test_flavor_profile.name) + + def test_flavor_find(self): + test_flavor = self.conn.load_balancer.find_flavor(self.FLAVOR_NAME) + self.assertEqual(self.FLAVOR_ID, test_flavor.id) + + def test_flavor_get(self): + test_flavor = self.conn.load_balancer.get_flavor(self.FLAVOR_ID) + self.assertEqual(self.FLAVOR_NAME, test_flavor.name) + self.assertEqual(self.FLAVOR_ID, test_flavor.id) + self.assertEqual(self.DESCRIPTION, test_flavor.description) + self.assertEqual(self.FLAVOR_PROFILE_ID, test_flavor.flavor_profile_id) + + def test_flavor_list(self): + names = [fv.name for fv in self.conn.load_balancer.flavors()] + self.assertIn(self.FLAVOR_NAME, names) + + def test_flavor_update(self): + self.conn.load_balancer.update_flavor( + self.FLAVOR_ID, name=self.UPDATE_NAME) + test_flavor = self.conn.load_balancer.get_flavor(self.FLAVOR_ID) + self.assertEqual(self.UPDATE_NAME, test_flavor.name) + + self.conn.load_balancer.update_flavor( + self.FLAVOR_ID, name=self.FLAVOR_NAME) + test_flavor = self.conn.load_balancer.get_flavor(self.FLAVOR_ID) + self.assertEqual(self.FLAVOR_NAME, test_flavor.name) diff --git a/openstack/tests/unit/load_balancer/test_flavor.py b/openstack/tests/unit/load_balancer/test_flavor.py new file mode 100644 index 000000000..5d5069a75 --- /dev/null +++ b/openstack/tests/unit/load_balancer/test_flavor.py @@ -0,0 +1,60 @@ +# Copyright 2019 Rackspace, US Inc. +# +# 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.tests.unit import base +import uuid + +from openstack.load_balancer.v2 import flavor + +IDENTIFIER = uuid.uuid4() +FLAVOR_PROFILE_ID = uuid.uuid4() +EXAMPLE = { + 'id': IDENTIFIER, + 'name': 'strawberry', + 'description': 'tasty', + 'is_enabled': False, + 'flavor_profile_id': FLAVOR_PROFILE_ID} + + +class TestFlavor(base.TestCase): + + def test_basic(self): + test_flavor = flavor.Flavor() + self.assertEqual('flavor', test_flavor.resource_key) + self.assertEqual('flavors', test_flavor.resources_key) + self.assertEqual('/lbaas/flavors', test_flavor.base_path) + self.assertTrue(test_flavor.allow_create) + self.assertTrue(test_flavor.allow_fetch) + self.assertTrue(test_flavor.allow_commit) + self.assertTrue(test_flavor.allow_delete) + self.assertTrue(test_flavor.allow_list) + + def test_make_it(self): + test_flavor = flavor.Flavor(**EXAMPLE) + self.assertEqual(EXAMPLE['id'], test_flavor.id) + self.assertEqual(EXAMPLE['name'], test_flavor.name) + self.assertEqual(EXAMPLE['description'], test_flavor.description) + self.assertFalse(test_flavor.is_enabled) + self.assertEqual(EXAMPLE['flavor_profile_id'], + test_flavor.flavor_profile_id) + + self.assertDictEqual( + {'limit': 'limit', + 'marker': 'marker', + 'id': 'id', + 'name': 'name', + 'description': 'description', + 'is_enabled': 'enabled', + 'flavor_profile_id': 'flavor_profile_id'}, + test_flavor._query_mapping._mapping) diff --git a/openstack/tests/unit/load_balancer/test_proxy.py b/openstack/tests/unit/load_balancer/test_proxy.py index c257009ab..fafc9d10e 100644 --- a/openstack/tests/unit/load_balancer/test_proxy.py +++ b/openstack/tests/unit/load_balancer/test_proxy.py @@ -14,6 +14,7 @@ import uuid import mock from openstack.load_balancer.v2 import _proxy +from openstack.load_balancer.v2 import flavor from openstack.load_balancer.v2 import flavor_profile from openstack.load_balancer.v2 import health_monitor from openstack.load_balancer.v2 import l7_policy @@ -344,3 +345,21 @@ class TestLoadBalancerProxy(test_proxy_base.TestProxyBase): def test_flavor_profile_update(self): self.verify_update(self.proxy.update_flavor_profile, flavor_profile.FlavorProfile) + + def test_flavors(self): + self.verify_list(self.proxy.flavors, flavor.Flavor) + + def test_flavor_get(self): + self.verify_get(self.proxy.get_flavor, flavor.Flavor) + + def test_flavor_create(self): + self.verify_create(self.proxy.create_flavor, flavor.Flavor) + + def test_flavor_delete(self): + self.verify_delete(self.proxy.delete_flavor, flavor.Flavor, True) + + def test_flavor_find(self): + self.verify_find(self.proxy.find_flavor, flavor.Flavor) + + def test_flavor_update(self): + self.verify_update(self.proxy.update_flavor, flavor.Flavor) diff --git a/releasenotes/notes/add-load-balancer-flavor-api-d2598e30347a19fc.yaml b/releasenotes/notes/add-load-balancer-flavor-api-d2598e30347a19fc.yaml new file mode 100644 index 000000000..462acc135 --- /dev/null +++ b/releasenotes/notes/add-load-balancer-flavor-api-d2598e30347a19fc.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Adds Octavia (load_balancer) support for the flavor APIs.