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:
parent
cfe4ed8e27
commit
ac224a85a8
@ -2613,7 +2613,7 @@ class TestPolicyEnforcementPoint(NsxPolicyLibTestCase):
|
|||||||
tenant=TEST_TENANT)
|
tenant=TEST_TENANT)
|
||||||
api_post.assert_called_once_with(
|
api_post.assert_called_once_with(
|
||||||
expected_def.get_resource_path() + '?action=reload',
|
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):
|
class TestPolicyDeploymentMap(NsxPolicyLibTestCase):
|
||||||
|
@ -22,6 +22,7 @@ from oslo_serialization import jsonutils
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from vmware_nsxlib._i18n import _
|
from vmware_nsxlib._i18n import _
|
||||||
|
from vmware_nsxlib.v3 import constants
|
||||||
from vmware_nsxlib.v3 import exceptions
|
from vmware_nsxlib.v3 import exceptions
|
||||||
from vmware_nsxlib.v3 import utils
|
from vmware_nsxlib.v3 import utils
|
||||||
|
|
||||||
@ -173,9 +174,10 @@ class RESTClient(object):
|
|||||||
expected_results=expected_results)
|
expected_results=expected_results)
|
||||||
|
|
||||||
def create(self, resource='', body=None, headers=None,
|
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,
|
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):
|
def patch(self, resource='', body=None, headers=None):
|
||||||
return self.url_patch(resource, body, headers=headers)
|
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,
|
return self._rest_call(url, method='PUT', body=body, headers=headers,
|
||||||
expected_results=expected_results)
|
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,
|
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):
|
def url_patch(self, url, body, headers=None):
|
||||||
return self._rest_call(url, method='PATCH', body=body, headers=headers)
|
return self._rest_call(url, method='PATCH', body=body, headers=headers)
|
||||||
@ -268,6 +272,7 @@ class RESTClient(object):
|
|||||||
silent=False, expected_results=None, **kwargs):
|
silent=False, expected_results=None, **kwargs):
|
||||||
request_headers = headers.copy() if headers else {}
|
request_headers = headers.copy() if headers else {}
|
||||||
request_headers.update(self._default_headers)
|
request_headers.update(self._default_headers)
|
||||||
|
retry_confirm = kwargs.pop(constants.API_RETRY_CONFIRM, False)
|
||||||
|
|
||||||
if utils.INJECT_HEADERS_CALLBACK:
|
if utils.INJECT_HEADERS_CALLBACK:
|
||||||
inject_headers = utils.INJECT_HEADERS_CALLBACK()
|
inject_headers = utils.INJECT_HEADERS_CALLBACK()
|
||||||
@ -281,7 +286,8 @@ class RESTClient(object):
|
|||||||
result = do_request(
|
result = do_request(
|
||||||
request_url,
|
request_url,
|
||||||
data=body,
|
data=body,
|
||||||
headers=request_headers)
|
headers=request_headers,
|
||||||
|
retry_confirm=retry_confirm)
|
||||||
te = time.time()
|
te = time.time()
|
||||||
if silent:
|
if silent:
|
||||||
self._conn.set_silent(False)
|
self._conn.set_silent(False)
|
||||||
|
@ -104,15 +104,23 @@ class TimeoutSession(requests.Session):
|
|||||||
# wrapper timeouts at the session level
|
# wrapper timeouts at the session level
|
||||||
# see: https://goo.gl/xNk7aM
|
# see: https://goo.gl/xNk7aM
|
||||||
def request(self, *args, **kwargs):
|
def request(self, *args, **kwargs):
|
||||||
|
retry_confirm = kwargs.pop(constants.API_RETRY_CONFIRM, False)
|
||||||
|
|
||||||
def request_with_retry_on_ssl_error(*args, **kwargs):
|
def request_with_retry_on_ssl_error(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
return super(TimeoutSession, self).request(*args, **kwargs)
|
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
|
# This can happen when connection tries to access certificate
|
||||||
# file it was opened with (renegotiation?)
|
# file it was opened with (renegotiation?)
|
||||||
# Proper way to solve this would be to pass in-memory cert
|
# Proper way to solve this would be to pass in-memory cert
|
||||||
# to ssl C code.
|
# to ssl C code.
|
||||||
# Retrying here works around the problem
|
# 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)
|
return super(TimeoutSession, self).request(*args, **kwargs)
|
||||||
|
|
||||||
def get_cert_provider():
|
def get_cert_provider():
|
||||||
|
@ -127,3 +127,6 @@ API_DEFAULT_MAX_RATE = 100
|
|||||||
# API Call Log Related const
|
# API Call Log Related const
|
||||||
API_CALL_LOG_PER_CLUSTER = 'API_LOG_PER_CLUSTER'
|
API_CALL_LOG_PER_CLUSTER = 'API_LOG_PER_CLUSTER'
|
||||||
API_CALL_LOG_PER_ENDPOINT = 'API_LOG_PER_ENDPOINT'
|
API_CALL_LOG_PER_ENDPOINT = 'API_LOG_PER_ENDPOINT'
|
||||||
|
|
||||||
|
# API Retry confirm parameter
|
||||||
|
API_RETRY_CONFIRM = 'retry_confirm'
|
||||||
|
@ -238,6 +238,10 @@ class CannotConnectToServer(ManagerError):
|
|||||||
message = _("Cannot connect to server")
|
message = _("Cannot connect to server")
|
||||||
|
|
||||||
|
|
||||||
|
class RetryConfirm(NsxLibException):
|
||||||
|
message = _("Retry will be handled by upper layer: %(msg)s")
|
||||||
|
|
||||||
|
|
||||||
class ResourceInUse(ManagerError):
|
class ResourceInUse(ManagerError):
|
||||||
message = _("The object cannot be deleted as either it has children or it "
|
message = _("The object cannot be deleted as either it has children or it "
|
||||||
"is being referenced by other objects")
|
"is being referenced by other objects")
|
||||||
|
@ -164,7 +164,7 @@ class LogicalPort(utils.NsxLibApiBase):
|
|||||||
switch_profile_ids=None, vif_type=None, app_id=None,
|
switch_profile_ids=None, vif_type=None, app_id=None,
|
||||||
allocate_addresses=nsx_constants.ALLOCATE_ADDRESS_NONE,
|
allocate_addresses=nsx_constants.ALLOCATE_ADDRESS_NONE,
|
||||||
description=None, tn_uuid=None,
|
description=None, tn_uuid=None,
|
||||||
extra_configs=None):
|
extra_configs=None, retry_confirm=False):
|
||||||
tags = tags or []
|
tags = tags or []
|
||||||
body = {'logical_switch_id': lswitch_id}
|
body = {'logical_switch_id': lswitch_id}
|
||||||
# NOTE(arosen): If parent_vif_id is specified we need to use
|
# NOTE(arosen): If parent_vif_id is specified we need to use
|
||||||
@ -181,7 +181,8 @@ class LogicalPort(utils.NsxLibApiBase):
|
|||||||
attachment=attachment,
|
attachment=attachment,
|
||||||
description=description,
|
description=description,
|
||||||
extra_configs=extra_configs))
|
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):
|
def delete(self, lport_id):
|
||||||
self._delete_with_retry('%s?detach=true' % lport_id)
|
self._delete_with_retry('%s?detach=true' % lport_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user