summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelajkat <lajos.katona@est.tech>2023-02-22 14:20:28 +0100
committerErik Olof Gunnar Andersson <eandersson@blizzard.com>2023-03-25 06:06:30 +0000
commitfd09a0cfc392349ec3e7437c86aabef40c574cd4 (patch)
treefd3604b13161de8e2a325f41f44b72ecb48275f8
parentb8ec3b450b5242c28f19d3e8057c5573807a4954 (diff)
downloaddesignate-fd09a0cfc392349ec3e7437c86aabef40c574cd4.tar.gz
Use SDK instead of neutronclient
The python-neutronclient has been deprecated for the CLI since Ocata and the python bindings "neutronclient" has been deprecated for removal as of the 2023.1 (Antelope) release[1] in favor of using openstacksdk. This patch migrates Designate from using the neutronclient to using the openstacksdk for communicating with neutron. [1] https://docs.openstack.org/releasenotes/python-neutronclient/2023.1.html Co-Authored-By: Michael Johnson <johnsomor@gmail.com> Change-Id: I0198f38afe3d5c32ea06d9e674ab0ff849f360e6 Related-Bug: #1999774
-rw-r--r--designate/conf/network_api.py41
-rw-r--r--designate/network_api/neutron.py51
-rw-r--r--designate/tests/unit/network_api/test_neutron.py121
-rw-r--r--releasenotes/notes/Replace-neutronclient-with-openstacksdk-5ae199bc327376b9.yaml10
-rw-r--r--requirements.txt2
5 files changed, 139 insertions, 86 deletions
diff --git a/designate/conf/network_api.py b/designate/conf/network_api.py
index 3c6aaf5c..202e2255 100644
--- a/designate/conf/network_api.py
+++ b/designate/conf/network_api.py
@@ -24,25 +24,44 @@ NETWORK_API_NEUTRON_OPTS = [
cfg.IntOpt('timeout',
default=30,
help='timeout value for connecting to neutron in seconds'),
+ cfg.BoolOpt('insecure',
+ default=False,
+ help='if set, ignore any SSL validation issues'),
+ cfg.StrOpt('ca_certificates_file',
+ help='Location of ca certificates file to use for '
+ 'neutron client requests.'),
+ cfg.StrOpt('client_certificate_file',
+ help='Location of client certificate file to use for '
+ 'neutron client requests.'),
+
+
cfg.StrOpt('admin_username',
- help='username for connecting to neutron in admin context'),
+ help='username for connecting to neutron in admin context',
+ deprecated_for_removal=True,
+ deprecated_reason='This parameter is no longer used.',
+ deprecated_since='2023.2'),
cfg.StrOpt('admin_password',
help='password for connecting to neutron in admin context',
- secret=True),
+ secret=True, deprecated_for_removal=True,
+ deprecated_reason='This parameter is no longer used.',
+ deprecated_since='2023.2'),
cfg.StrOpt('admin_tenant_name',
- help='tenant name for connecting to neutron in admin context'),
+ help='tenant name for connecting to neutron in admin context',
+ deprecated_for_removal=True,
+ deprecated_reason='This parameter is no longer used.',
+ deprecated_since='2023.2'),
cfg.StrOpt('auth_url',
- help='auth url for connecting to neutron in admin context'),
- cfg.BoolOpt('insecure',
- default=False,
- help='if set, ignore any SSL validation issues'),
+ help='auth url for connecting to neutron in admin context',
+ deprecated_for_removal=True,
+ deprecated_reason='This parameter is no longer used.',
+ deprecated_since='2023.2'),
cfg.StrOpt('auth_strategy',
default='keystone',
help='auth strategy for connecting to '
- 'neutron in admin context'),
- cfg.StrOpt('ca_certificates_file',
- help='Location of ca certificates file to use for '
- 'neutron client requests.'),
+ 'neutron in admin context',
+ deprecated_for_removal=True,
+ deprecated_reason='This parameter is no longer used.',
+ deprecated_since='2023.2'),
]
diff --git a/designate/network_api/neutron.py b/designate/network_api/neutron.py
index 111da5a2..5f22da13 100644
--- a/designate/network_api/neutron.py
+++ b/designate/network_api/neutron.py
@@ -16,36 +16,39 @@
# Copied partially from nova
import concurrent.futures
import futurist
-from neutronclient.common import exceptions as neutron_exceptions
-from neutronclient.v2_0 import client as clientv20
+from keystoneauth1 import session
+from keystoneauth1 import token_endpoint
+import openstack
+from openstack import exceptions as sdk_exceptions
from oslo_config import cfg
from oslo_log import log as logging
from designate import exceptions
from designate.network_api import base
+from designate import version
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def get_client(context, endpoint):
- params = {
- 'endpoint_url': endpoint,
- 'timeout': CONF['network_api:neutron'].timeout,
- 'insecure': CONF['network_api:neutron'].insecure,
- 'ca_cert': CONF['network_api:neutron'].ca_certificates_file,
- }
+ verify = True
+ if CONF['network_api:neutron'].insecure:
+ verify = False
+ elif CONF['network_api:neutron'].ca_certificates_file:
+ verify = CONF['network_api:neutron'].ca_certificates_file
- if context.auth_token:
- params['token'] = context.auth_token
- params['auth_strategy'] = None
- elif CONF['network_api:neutron'].admin_username is not None:
- params['username'] = CONF['network_api:neutron'].admin_username
- params['project_name'] = CONF['network_api:neutron'].admin_tenant_name
- params['password'] = CONF['network_api:neutron'].admin_password
- params['auth_url'] = CONF['network_api:neutron'].auth_url
- params['auth_strategy'] = CONF['network_api:neutron'].auth_strategy
- return clientv20.Client(**params)
+ auth_token = token_endpoint.Token(endpoint, context.auth_token)
+
+ user_session = session.Session(
+ auth=auth_token,
+ verify=verify,
+ cert=CONF['network_api:neutron'].client_certificate_file,
+ timeout=CONF['network_api:neutron'].timeout,
+ app_name='designate',
+ app_version=version.version_info.version_string())
+
+ return openstack.connection.Connection(session=user_session)
class NeutronNetworkAPI(base.NetworkAPI):
@@ -91,18 +94,20 @@ class NeutronNetworkAPI(base.NetworkAPI):
{'region': region, 'endpoint': endpoint})
client = get_client(context, endpoint=endpoint)
try:
- fips = client.list_floatingips(project_id=project_id)
- for fip in fips['floatingips']:
+ fips = client.network.ips(project_id=project_id)
+ for fip in fips:
yield {
'id': fip['id'],
'address': fip['floating_ip_address'],
'region': region
}
- except neutron_exceptions.Unauthorized:
+ except sdk_exceptions.HttpException as http_ex:
LOG.warning(
'Failed fetching floating ips from %(region)s @ %(endpoint)s'
- 'due to an Unauthorized error',
- {'region': region, 'endpoint': endpoint}
+ 'due to a %(cause)s error',
+ {'region': region,
+ 'endpoint': endpoint,
+ 'cause': http_ex.message}
)
except Exception:
LOG.error(
diff --git a/designate/tests/unit/network_api/test_neutron.py b/designate/tests/unit/network_api/test_neutron.py
index 243bc2ce..cdc11ddc 100644
--- a/designate/tests/unit/network_api/test_neutron.py
+++ b/designate/tests/unit/network_api/test_neutron.py
@@ -15,8 +15,7 @@
# under the License.
from unittest import mock
-from neutronclient.common import exceptions as neutron_exceptions
-from neutronclient.v2_0 import client as clientv20
+from openstack import exceptions as sdk_exceptions
from oslo_config import cfg
from oslo_config import fixture as cfg_fixture
import oslotest.base
@@ -25,6 +24,7 @@ from designate import context
from designate import exceptions
from designate.network_api import get_network_api
from designate.network_api import neutron
+from designate import version
CONF = cfg.CONF
@@ -35,75 +35,94 @@ class NeutronNetworkAPITest(oslotest.base.BaseTestCase):
self.useFixture(cfg_fixture.Config(CONF))
CONF.set_override(
- 'endpoints', ['RegionOne|http://localhost:9696'],
+ 'endpoints', ['RegionOne|http://192.0.2.5:9696'],
'network_api:neutron'
)
+ self.ca_certificates_file = 'fake_ca_cert_file'
+ self.client_certificate_file = 'fake_client_cert_file'
+ CONF.set_override('client_certificate_file',
+ self.client_certificate_file,
+ 'network_api:neutron')
+ self.neutron_timeout = 100
+ CONF.set_override('timeout', self.neutron_timeout,
+ 'network_api:neutron')
self.api = get_network_api('neutron')
self.context = context.DesignateContext(
user_id='12345', project_id='54321',
)
- @mock.patch.object(clientv20, 'Client')
- def test_get_client(self, mock_client):
- neutron.get_client(self.context, 'http://localhost:9696')
+ @mock.patch('keystoneauth1.token_endpoint.Token')
+ @mock.patch('keystoneauth1.session.Session')
+ @mock.patch('openstack.connection.Connection')
+ def test_get_client(self, mock_client, mock_session, mock_token):
+ auth_token_mock = mock.MagicMock()
+ mock_token.return_value = auth_token_mock
- _, kwargs = mock_client.call_args
+ user_session_mock = mock.MagicMock()
+ mock_session.return_value = user_session_mock
- self.assertIn('endpoint_url', kwargs)
- self.assertIn('timeout', kwargs)
- self.assertIn('insecure', kwargs)
- self.assertIn('ca_cert', kwargs)
+ connection_mock = mock.MagicMock()
+ mock_client.return_value = connection_mock
- self.assertNotIn('token', kwargs)
- self.assertNotIn('username', kwargs)
-
- self.assertEqual('http://localhost:9696', kwargs['endpoint_url'])
-
- @mock.patch.object(clientv20, 'Client')
- def test_get_client_using_token(self, mock_client):
self.context = context.DesignateContext(
user_id='12345', project_id='54321', auth_token='token',
)
+ endpoint = 'http://192.0.2.5:9696'
- neutron.get_client(self.context, 'http://localhost:9696')
+ result = neutron.get_client(self.context, endpoint)
- _, kwargs = mock_client.call_args
+ mock_token.assert_called_once_with(endpoint, self.context.auth_token)
- self.assertIn('token', kwargs)
- self.assertIn('auth_strategy', kwargs)
- self.assertNotIn('username', kwargs)
+ mock_session.assert_called_once_with(
+ auth=auth_token_mock, verify=True,
+ cert=self.client_certificate_file, timeout=self.neutron_timeout,
+ app_name='designate',
+ app_version=version.version_info.version_string())
- self.assertEqual('http://localhost:9696', kwargs['endpoint_url'])
- self.assertEqual(self.context.auth_token, kwargs['token'])
+ self.assertEqual(connection_mock, result)
- @mock.patch.object(clientv20, 'Client')
- def test_get_client_using_admin(self, mock_client):
- CONF.set_override(
- 'admin_username', 'test',
- 'network_api:neutron'
- )
+ # Test with CA certs file configuration
+ mock_token.reset_mock()
+ mock_session.reset_mock()
- neutron.get_client(self.context, 'http://localhost:9696')
+ CONF.set_override('ca_certificates_file', self.ca_certificates_file,
+ 'network_api:neutron')
- _, kwargs = mock_client.call_args
+ result = neutron.get_client(self.context, endpoint)
- self.assertIn('auth_strategy', kwargs)
- self.assertIn('username', kwargs)
- self.assertIn('project_name', kwargs)
- self.assertIn('password', kwargs)
- self.assertIn('auth_url', kwargs)
- self.assertNotIn('token', kwargs)
+ mock_token.assert_called_once_with(endpoint, self.context.auth_token)
- self.assertEqual('http://localhost:9696', kwargs['endpoint_url'])
- self.assertEqual(
- kwargs['username'], CONF['network_api:neutron'].admin_username
- )
+ mock_session.assert_called_once_with(
+ auth=auth_token_mock, verify=self.ca_certificates_file,
+ cert=self.client_certificate_file, timeout=self.neutron_timeout,
+ app_name='designate',
+ app_version=version.version_info.version_string())
+
+ self.assertEqual(connection_mock, result)
+
+ # Test with insecure configuration
+ mock_token.reset_mock()
+ mock_session.reset_mock()
+
+ CONF.set_override('insecure', True, 'network_api:neutron')
+
+ result = neutron.get_client(self.context, endpoint)
+
+ mock_token.assert_called_once_with(endpoint, self.context.auth_token)
+
+ mock_session.assert_called_once_with(
+ auth=auth_token_mock, verify=False,
+ cert=self.client_certificate_file, timeout=self.neutron_timeout,
+ app_name='designate',
+ app_version=version.version_info.version_string())
+
+ self.assertEqual(connection_mock, result)
- @mock.patch.object(neutron, 'get_client')
+ @mock.patch('designate.network_api.neutron.get_client')
def test_list_floatingips(self, get_client):
driver = mock.Mock()
- driver.list_floatingips.return_value = {'floatingips': [
+ driver.network.ips.return_value = [
{
'id': '123',
'floating_ip_address': '192.168.0.100',
@@ -114,24 +133,24 @@ class NeutronNetworkAPITest(oslotest.base.BaseTestCase):
'floating_ip_address': '192.168.0.200',
'region': 'RegionOne'
},
- ]}
+ ]
get_client.return_value = driver
self.assertEqual(2, len(self.api.list_floatingips(self.context)))
- @mock.patch.object(neutron, 'get_client')
+ @mock.patch('designate.network_api.neutron.get_client')
def test_list_floatingips_unauthorized(self, get_client):
driver = mock.Mock()
- driver.list_floatingips.side_effect = neutron_exceptions.Unauthorized
+ driver.network.ips.side_effect = sdk_exceptions.HttpException
get_client.return_value = driver
self.assertEqual(0, len(self.api.list_floatingips(self.context)))
- @mock.patch.object(neutron, 'get_client')
+ @mock.patch('designate.network_api.neutron.get_client')
def test_list_floatingips_communication_failure(self, get_client):
driver = mock.Mock()
- driver.list_floatingips.side_effect = (
- neutron_exceptions.NeutronException
+ driver.network.ips.side_effect = (
+ Exception
)
get_client.return_value = driver
diff --git a/releasenotes/notes/Replace-neutronclient-with-openstacksdk-5ae199bc327376b9.yaml b/releasenotes/notes/Replace-neutronclient-with-openstacksdk-5ae199bc327376b9.yaml
new file mode 100644
index 00000000..5e6c67e4
--- /dev/null
+++ b/releasenotes/notes/Replace-neutronclient-with-openstacksdk-5ae199bc327376b9.yaml
@@ -0,0 +1,10 @@
+---
+upgrade:
+ - |
+ Designate will now use the openstacksdk to access neutron instead of the
+ deprecated neutronclient. The python-neutronclient module requirement has
+ been replaced by the openstacksdk module.
+other:
+ - |
+ Designate will now use the openstacksdk to access neutron instead of the
+ deprecated neutronclient.
diff --git a/requirements.txt b/requirements.txt
index b16f8094..e5152959 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,6 +13,7 @@ Jinja2>=2.10 # BSD License (3 clause)
jsonschema>=3.2.0 # MIT
keystoneauth1>=3.4.0 # Apache-2.0
keystonemiddleware>=4.17.0 # Apache-2.0
+openstacksdk>=0.103.0 # Apache-2.0
oslo.config>=6.8.0 # Apache-2.0
oslo.concurrency>=4.2.0 # Apache-2.0
oslo.messaging>=14.1.0 # Apache-2.0
@@ -31,7 +32,6 @@ PasteDeploy>=1.5.0 # MIT
pbr>=3.1.1 # Apache-2.0
pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD
python-designateclient>=2.12.0 # Apache-2.0
-python-neutronclient>=6.7.0 # Apache-2.0
requests>=2.23.0 # Apache-2.0
tenacity>=6.0.0 # Apache-2.0
SQLAlchemy>=1.4.41 # MIT