Fix the logical port created twice

Logical port creation is a POST request. Sometimes it will trigger
ConnectionResetError which is a IOError. request_with_retry_on_ssl_error
will retry it.
If request has parameter retry_confirm, exception will be raised so ncp
could query if port has been created to avoid creating port twice.

Change-Id: Ic97b39c7a3736f02a79ab891970c1ad67b123156
This commit is contained in:
Tao Zou 2022-01-25 09:21:18 +08:00
parent cfe4ed8e27
commit ac224a85a8
6 changed files with 31 additions and 9 deletions

View File

@ -2613,7 +2613,7 @@ class TestPolicyEnforcementPoint(NsxPolicyLibTestCase):
tenant=TEST_TENANT)
api_post.assert_called_once_with(
expected_def.get_resource_path() + '?action=reload',
None, expected_results=None, headers=None)
None, expected_results=None, headers=None, retry_confirm=False)
class TestPolicyDeploymentMap(NsxPolicyLibTestCase):

View File

@ -22,6 +22,7 @@ from oslo_serialization import jsonutils
import requests
from vmware_nsxlib._i18n import _
from vmware_nsxlib.v3 import constants
from vmware_nsxlib.v3 import exceptions
from vmware_nsxlib.v3 import utils
@ -173,9 +174,10 @@ class RESTClient(object):
expected_results=expected_results)
def create(self, resource='', body=None, headers=None,
expected_results=None):
expected_results=None, retry_confirm=False):
return self.url_post(resource, body, headers=headers,
expected_results=expected_results)
expected_results=expected_results,
retry_confirm=retry_confirm)
def patch(self, resource='', body=None, headers=None):
return self.url_patch(resource, body, headers=headers)
@ -205,9 +207,11 @@ class RESTClient(object):
return self._rest_call(url, method='PUT', body=body, headers=headers,
expected_results=expected_results)
def url_post(self, url, body, headers=None, expected_results=None):
def url_post(self, url, body, headers=None, expected_results=None,
retry_confirm=False):
return self._rest_call(url, method='POST', body=body, headers=headers,
expected_results=expected_results)
expected_results=expected_results,
retry_confirm=retry_confirm)
def url_patch(self, url, body, headers=None):
return self._rest_call(url, method='PATCH', body=body, headers=headers)
@ -268,6 +272,7 @@ class RESTClient(object):
silent=False, expected_results=None, **kwargs):
request_headers = headers.copy() if headers else {}
request_headers.update(self._default_headers)
retry_confirm = kwargs.pop(constants.API_RETRY_CONFIRM, False)
if utils.INJECT_HEADERS_CALLBACK:
inject_headers = utils.INJECT_HEADERS_CALLBACK()
@ -281,7 +286,8 @@ class RESTClient(object):
result = do_request(
request_url,
data=body,
headers=request_headers)
headers=request_headers,
retry_confirm=retry_confirm)
te = time.time()
if silent:
self._conn.set_silent(False)

View File

@ -104,15 +104,23 @@ class TimeoutSession(requests.Session):
# wrapper timeouts at the session level
# see: https://goo.gl/xNk7aM
def request(self, *args, **kwargs):
retry_confirm = kwargs.pop(constants.API_RETRY_CONFIRM, False)
def request_with_retry_on_ssl_error(*args, **kwargs):
try:
return super(TimeoutSession, self).request(*args, **kwargs)
except (IOError, OpenSSL.SSL.Error):
except (IOError, OpenSSL.SSL.Error) as err:
# This can happen when connection tries to access certificate
# file it was opened with (renegotiation?)
# Proper way to solve this would be to pass in-memory cert
# to ssl C code.
# Retrying here works around the problem
# IOError covered too much Exception, if resources created
# successfully on nsxt side but ConnectResetError happened,
# Retrying here will create resource twice
if isinstance(err, IOError) and retry_confirm:
raise exceptions.RetryConfirm(msg=err)
return super(TimeoutSession, self).request(*args, **kwargs)
def get_cert_provider():

View File

@ -127,3 +127,6 @@ API_DEFAULT_MAX_RATE = 100
# API Call Log Related const
API_CALL_LOG_PER_CLUSTER = 'API_LOG_PER_CLUSTER'
API_CALL_LOG_PER_ENDPOINT = 'API_LOG_PER_ENDPOINT'
# API Retry confirm parameter
API_RETRY_CONFIRM = 'retry_confirm'

View File

@ -238,6 +238,10 @@ class CannotConnectToServer(ManagerError):
message = _("Cannot connect to server")
class RetryConfirm(NsxLibException):
message = _("Retry will be handled by upper layer: %(msg)s")
class ResourceInUse(ManagerError):
message = _("The object cannot be deleted as either it has children or it "
"is being referenced by other objects")

View File

@ -164,7 +164,7 @@ class LogicalPort(utils.NsxLibApiBase):
switch_profile_ids=None, vif_type=None, app_id=None,
allocate_addresses=nsx_constants.ALLOCATE_ADDRESS_NONE,
description=None, tn_uuid=None,
extra_configs=None):
extra_configs=None, retry_confirm=False):
tags = tags or []
body = {'logical_switch_id': lswitch_id}
# NOTE(arosen): If parent_vif_id is specified we need to use
@ -181,7 +181,8 @@ class LogicalPort(utils.NsxLibApiBase):
attachment=attachment,
description=description,
extra_configs=extra_configs))
return self.client.create(self.get_path(), body=body)
return self.client.create(self.get_path(), body=body,
retry_confirm=retry_confirm)
def delete(self, lport_id):
self._delete_with_retry('%s?detach=true' % lport_id)