From d9e9bcab86d66c136de360bc1326cff40661bdc5 Mon Sep 17 00:00:00 2001 From: Federico Ressi Date: Tue, 18 Jun 2019 08:10:11 +0200 Subject: [PATCH] Update Neutron Client - implement CIDR management - workaround neutron client bug listing subnets Change-Id: I4619999de1d5c459af4338f93a7bc7bea9406b8b --- tobiko/openstack/neutron/__init__.py | 4 + tobiko/openstack/neutron/_cidr.py | 99 +++++++++++++++++++ tobiko/openstack/neutron/_client.py | 6 +- .../functional/openstack/test_neutron.py | 4 +- 4 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 tobiko/openstack/neutron/_cidr.py diff --git a/tobiko/openstack/neutron/__init__.py b/tobiko/openstack/neutron/__init__.py index 734abe06d..b1e9c26be 100644 --- a/tobiko/openstack/neutron/__init__.py +++ b/tobiko/openstack/neutron/__init__.py @@ -14,6 +14,7 @@ from __future__ import absolute_import from tobiko.openstack.neutron import _client +from tobiko.openstack.neutron import _cidr from tobiko.openstack.neutron import _extension @@ -29,6 +30,9 @@ show_network = _client.show_network show_router = _client.show_router show_subnet = _client.show_subnet +new_ipv4_cidr = _cidr.new_ipv4_cidr +new_ipv6_cidr = _cidr.new_ipv6_cidr + get_networking_extensions = _extension.get_networking_extensions missing_networking_extensions = _extension.missing_networking_extensions has_networking_extensions = _extension.has_networking_extensions diff --git a/tobiko/openstack/neutron/_cidr.py b/tobiko/openstack/neutron/_cidr.py new file mode 100644 index 000000000..2fd453ccd --- /dev/null +++ b/tobiko/openstack/neutron/_cidr.py @@ -0,0 +1,99 @@ +# Copyright 2019 Red Hat +# +# 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 __future__ import absolute_import + +import netaddr + +import tobiko + +from tobiko.openstack.neutron import _client + + +def new_ipv4_cidr(): + return tobiko.setup_fixture(IPv4CIDRGeneratorFixture).new_cidr() + + +def new_ipv6_cidr(): + return tobiko.setup_fixture(IPv6CIDRGeneratorFixture).new_cidr() + + +class CIDRGeneratorFixture(tobiko.SharedFixture): + + cidr = None + prefixlen = None + client = None + config = None + cidr_generator = None + subnet_cidrs = None + + def __init__(self, cidr=None, prefixlen=None, client=None): + super(CIDRGeneratorFixture, self).__init__() + if cidr: + self.cidr = cidr + if prefixlen: + self.prefixlen = prefixlen + if client: + self.client = client + + def setup_fixture(self): + self.setup_config() + self.setup_subnet_cidrs() + self.setup_cidr_generator() + + def setup_config(self): + from tobiko import config + CONF = config.CONF + self.config = CONF.tobiko.neutron + + def setup_subnet_cidrs(self): + client = _client.neutron_client(self.client) + self.subnet_cidrs = set(_client.list_subnet_cidrs(client=client)) + + def setup_cidr_generator(self): + cidr = netaddr.IPNetwork(self.cidr) + prefixlen = int(self.prefixlen) + self.cidr_generator = cidr.subnet(prefixlen) + + def new_cidr(self): + for cidr in self.cidr_generator: + if cidr not in self.subnet_cidrs: + return cidr + raise NoSuchCIDRLeft(cidr=self.cidr, prefixlen=self.prefixlen) + + +class IPv4CIDRGeneratorFixture(CIDRGeneratorFixture): + + @property + def cidr(self): + return self.config.ipv4_cidr + + @property + def prefixlen(self): + return self.config.ipv4_prefixlen + + +class IPv6CIDRGeneratorFixture(CIDRGeneratorFixture): + + @property + def cidr(self): + return self.config.ipv6_cidr + + @property + def prefixlen(self): + return self.config.ipv6_prefixlen + + +class NoSuchCIDRLeft(tobiko.TobikoException): + message = ("No such subnet CIDR left " + "(CIDR={cidr!s}, prefixlen={prefixlen!s})") diff --git a/tobiko/openstack/neutron/_client.py b/tobiko/openstack/neutron/_client.py index 316100cc1..cc6331cc5 100644 --- a/tobiko/openstack/neutron/_client.py +++ b/tobiko/openstack/neutron/_client.py @@ -13,6 +13,8 @@ # under the License. from __future__ import absolute_import +import collections + import netaddr from neutronclient.v2_0 import client as neutronclient @@ -82,7 +84,9 @@ def list_networks(show=False, client=None, **params): def list_subnets(show=False, client=None, **params): - subnets = neutron_client(client).list_subnets(**params)['subnets'] + subnets = neutron_client(client).list_subnets(**params) + if isinstance(subnets, collections.Mapping): + subnets = subnets['subnets'] if show: subnets = [show_subnet(s['id'], client=client) for s in subnets] return subnets diff --git a/tobiko/tests/functional/openstack/test_neutron.py b/tobiko/tests/functional/openstack/test_neutron.py index a8a022614..2ef3125dd 100644 --- a/tobiko/tests/functional/openstack/test_neutron.py +++ b/tobiko/tests/functional/openstack/test_neutron.py @@ -94,7 +94,7 @@ class NeutronApiTestCase(testtools.TestCase): stack=self.stack.stack_name) subnet = neutron.show_subnet(self.stack.ipv4_subnet_id) self.assertEqual(self.stack.ipv4_subnet_id, subnet['id']) - self.assertEqual(str(self.stack.ipv4_cidr), subnet['cidr']) + self.assertEqual(self.stack.ipv4_subnet_details, subnet) def test_show_ipv6_subnet(self): if not self.stack.has_ipv6: @@ -102,4 +102,4 @@ class NeutronApiTestCase(testtools.TestCase): stack=self.stack.stack_name) subnet = neutron.show_subnet(self.stack.ipv6_subnet_id) self.assertEqual(self.stack.ipv6_subnet_id, subnet['id']) - self.assertEqual(str(self.stack.ipv6_cidr), subnet['cidr']) + self.assertEqual(self.stack.ipv6_subnet_details, subnet)