Support Client Notification Endpoint Test
This functionality will enable tacker to test the
notification during register Subscription.
if VNFM cannot receive success response
(204 no content), VNFM will be failed Subscription
Request
- As sending management of this API, we apply
legacy CONF.
(retry_num,retry_wait, there are defined in
vnf_lcm group.)
- As sending control of this API, we add new
CONF(test_callback_uri)
- We modify(add) Notification(GET) process to
current send_notification.
Implements: blueprint support-fundamental-lcm
Spec: https://specs.openstack.org/openstack/tacker-specs/specs/wallaby/support-fundamental-vnf-lcm-based-on-ETSI-NFV.html
Change-Id: I2a1db1b34b687748ad939b0c867b2d78f45622da
This commit is contained in:
@@ -940,6 +940,15 @@ class VnfLcmController(wsgi.Controller):
|
||||
filter_uni = subscription_request_data.get('filter')
|
||||
filter = ast.literal_eval(str(filter_uni).replace("'", "'"))
|
||||
|
||||
if CONF.vnf_lcm.test_callback_uri:
|
||||
resp = self._test_notification(request.context,
|
||||
vnf_lcm_subscription)
|
||||
if resp == -1:
|
||||
LOG.exception(traceback.format_exc())
|
||||
return self._make_problem_detail(
|
||||
'Failed to Test Notification', 400,
|
||||
title='Bad Request')
|
||||
|
||||
try:
|
||||
vnf_lcm_subscription = vnf_lcm_subscription.create(filter)
|
||||
LOG.debug("vnf_lcm_subscription %s" % vnf_lcm_subscription)
|
||||
@@ -1526,6 +1535,11 @@ class VnfLcmController(wsgi.Controller):
|
||||
res.status_int = status
|
||||
return res
|
||||
|
||||
def _test_notification(self, context, vnf_lcm_subscription):
|
||||
resp = self.rpc_api.test_notification(context,
|
||||
vnf_lcm_subscription, cast=False)
|
||||
return resp
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(VnfLcmController())
|
||||
|
||||
@@ -1467,11 +1467,6 @@ class Conductor(manager.Manager):
|
||||
"Failed to send notification {}. Details: {}".format(
|
||||
vnf_lcm_op_occs_id, str(ex)))
|
||||
|
||||
def _retry_check(self, retry_count):
|
||||
time.sleep(CONF.vnf_lcm.retry_wait)
|
||||
if retry_count == CONF.vnf_lcm.retry_num:
|
||||
LOG.warn("Number of retries exceeded retry count")
|
||||
|
||||
def send_notification(self, context, notification):
|
||||
"""Function to send notification to client
|
||||
|
||||
@@ -1574,6 +1569,65 @@ class Conductor(manager.Manager):
|
||||
return -2
|
||||
return 0
|
||||
|
||||
def _retry_check(self, retry_count):
|
||||
time.sleep(CONF.vnf_lcm.retry_wait)
|
||||
if retry_count == CONF.vnf_lcm.retry_num:
|
||||
LOG.warn(
|
||||
"Number of retries exceeded retry count [%s]" %
|
||||
CONF.vnf_lcm.retry_num)
|
||||
|
||||
def test_notification(self, context, vnf_lcm_subscription=None):
|
||||
"""Function to send test notification to client
|
||||
|
||||
This function is used to send test notification
|
||||
to client during Register Subscription.
|
||||
|
||||
:returns: 0 if status code of the response is 204
|
||||
or if CONF.vnf_lcm.test_callback_uri is False,
|
||||
-1 if status code of the response is not 204
|
||||
"""
|
||||
|
||||
if not CONF.vnf_lcm.test_callback_uri:
|
||||
LOG.warning("Callback URI is %s", CONF.vnf_lcm.test_callback_uri)
|
||||
return 0
|
||||
|
||||
# Notification shipping
|
||||
for num in range(CONF.vnf_lcm.retry_num):
|
||||
try:
|
||||
auth_client = auth.auth_manager.get_auth_client(
|
||||
vnf_lcm_subscription.id)
|
||||
|
||||
notification = {}
|
||||
response = auth_client.get(
|
||||
vnf_lcm_subscription.callback_uri,
|
||||
data=json.dumps(notification),
|
||||
timeout=CONF.vnf_lcm.retry_timeout)
|
||||
|
||||
if response.status_code == 204:
|
||||
return 0
|
||||
else:
|
||||
LOG.warning(
|
||||
"Notification failed status[%s] \
|
||||
callback_uri[%s]" %
|
||||
(response.status_code,
|
||||
vnf_lcm_subscription.callback_uri))
|
||||
LOG.debug(
|
||||
"retry_wait %s" %
|
||||
CONF.vnf_lcm.retry_wait)
|
||||
self._retry_check(num)
|
||||
|
||||
continue
|
||||
except requests.Timeout as e:
|
||||
LOG.warning("Notification request timed out."
|
||||
" callback_uri[%(uri)s]"
|
||||
" reason[%(reason)s]", {
|
||||
"uri": vnf_lcm_subscription.callback_uri,
|
||||
"reason": str(e)})
|
||||
self._retry_check(num)
|
||||
|
||||
# return -1 since the response is not 204
|
||||
return -1
|
||||
|
||||
@coordination.synchronized('{vnf_instance[id]}')
|
||||
def instantiate(
|
||||
self,
|
||||
|
||||
@@ -113,6 +113,17 @@ class VNFLcmRPCAPI(object):
|
||||
return rpc_method(context, 'send_notification',
|
||||
notification=notification)
|
||||
|
||||
def test_notification(self, context,
|
||||
vnf_lcm_subscription=None, cast=False):
|
||||
serializer = objects_base.TackerObjectSerializer()
|
||||
|
||||
client = rpc.get_client(self.target, version_cap=None,
|
||||
serializer=serializer)
|
||||
cctxt = client.prepare()
|
||||
rpc_method = cctxt.cast if cast else cctxt.call
|
||||
return rpc_method(context, 'test_notification',
|
||||
vnf_lcm_subscription=vnf_lcm_subscription)
|
||||
|
||||
def rollback(self, context, vnf_info, vnf_instance,
|
||||
operation_params, cast=True):
|
||||
serializer = objects_base.TackerObjectSerializer()
|
||||
|
||||
@@ -35,7 +35,11 @@ OPTS = [
|
||||
cfg.IntOpt(
|
||||
'retry_timeout',
|
||||
default=10,
|
||||
help="Retry Timeout(sec)")]
|
||||
help="Retry Timeout(sec)"),
|
||||
cfg.BoolOpt(
|
||||
'test_callback_uri',
|
||||
default=True,
|
||||
help="Test callbackUri")]
|
||||
|
||||
vnf_lcm_group = cfg.OptGroup('vnf_lcm',
|
||||
title='vnf_lcm options',
|
||||
|
||||
@@ -237,6 +237,11 @@ class BaseVnfLcmTest(base.BaseTackerTest):
|
||||
callback_url,
|
||||
status_code=204
|
||||
)
|
||||
FAKE_SERVER_MANAGER.set_callback(
|
||||
'GET',
|
||||
callback_url,
|
||||
status_code=204
|
||||
)
|
||||
|
||||
self.tacker_client = base.BaseTackerTest.tacker_http_client()
|
||||
|
||||
@@ -1089,6 +1094,14 @@ class BaseVnfLcmTest(base.BaseTackerTest):
|
||||
'VnfLcmOperationOccurrenceNotification',
|
||||
'COMPLETED')
|
||||
|
||||
def assert_notification_get(self, callback_url):
|
||||
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
|
||||
callback_url)
|
||||
FAKE_SERVER_MANAGER.clear_history(
|
||||
callback_url)
|
||||
self.assertEqual(1, len(notify_mock_responses))
|
||||
self.assertEqual(204, notify_mock_responses[0].status_code)
|
||||
|
||||
def assert_notification_mock_response(
|
||||
self,
|
||||
notify_mock_response,
|
||||
|
||||
@@ -111,14 +111,16 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
- Delete subscription
|
||||
"""
|
||||
# Create subscription and register it.
|
||||
callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
request_body = fake_vnflcm.Subscription.make_create_request_body(
|
||||
'http://localhost:{}{}'.format(
|
||||
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
|
||||
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)))
|
||||
callback_url))
|
||||
resp, response_body = self._register_subscription(request_body)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.assert_http_header_location_for_subscription(resp.headers)
|
||||
self.assert_notification_get(callback_url)
|
||||
subscription_id = response_body.get('id')
|
||||
self.addCleanup(
|
||||
self._delete_subscription,
|
||||
@@ -237,14 +239,16 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
- Delete subscription.
|
||||
"""
|
||||
# Create subscription and register it.
|
||||
callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
request_body = fake_vnflcm.Subscription.make_create_request_body(
|
||||
'http://localhost:{}{}'.format(
|
||||
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
|
||||
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)))
|
||||
callback_url))
|
||||
resp, response_body = self._register_subscription(request_body)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.assert_http_header_location_for_subscription(resp.headers)
|
||||
self.assert_notification_get(callback_url)
|
||||
subscription_id = response_body.get('id')
|
||||
self.addCleanup(self._delete_subscription, subscription_id)
|
||||
|
||||
@@ -388,14 +392,16 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
- Delete subscription.
|
||||
"""
|
||||
# Create subscription and register it.
|
||||
callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
request_body = fake_vnflcm.Subscription.make_create_request_body(
|
||||
'http://localhost:{}{}'.format(
|
||||
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
|
||||
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)))
|
||||
callback_url))
|
||||
resp, response_body = self._register_subscription(request_body)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.assert_http_header_location_for_subscription(resp.headers)
|
||||
self.assert_notification_get(callback_url)
|
||||
subscription_id = response_body.get('id')
|
||||
self.addCleanup(self._delete_subscription, subscription_id)
|
||||
|
||||
@@ -1003,14 +1009,16 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
- Delete subscription.
|
||||
"""
|
||||
# Create subscription and register it.
|
||||
callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
request_body = fake_vnflcm.Subscription.make_create_request_body(
|
||||
'http://localhost:{}{}'.format(
|
||||
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
|
||||
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)))
|
||||
callback_url))
|
||||
resp, response_body = self._register_subscription(request_body)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.assert_http_header_location_for_subscription(resp.headers)
|
||||
self.assert_notification_get(callback_url)
|
||||
subscription_id = response_body.get('id')
|
||||
self.addCleanup(self._delete_subscription, subscription_id)
|
||||
|
||||
@@ -1098,14 +1106,16 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
- Delete subscription.
|
||||
"""
|
||||
# Create subscription and register it.
|
||||
callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
request_body = fake_vnflcm.Subscription.make_create_request_body(
|
||||
'http://localhost:{}{}'.format(
|
||||
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
|
||||
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)))
|
||||
callback_url))
|
||||
resp, response_body = self._register_subscription(request_body)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.assert_http_header_location_for_subscription(resp.headers)
|
||||
self.assert_notification_get(callback_url)
|
||||
subscription_id = response_body.get('id')
|
||||
self.addCleanup(self._delete_subscription, subscription_id)
|
||||
|
||||
@@ -1205,6 +1215,7 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
|
||||
In this test case, we do following steps.
|
||||
- Create subscription.
|
||||
- Test notification.
|
||||
- Create VNF package.
|
||||
- Upload VNF package.
|
||||
- Create VNF instance.
|
||||
@@ -1226,6 +1237,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
subscription_id = response_body.get('id')
|
||||
self.addCleanup(self._delete_subscription, subscription_id)
|
||||
|
||||
self.assert_notification_get(
|
||||
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName))
|
||||
|
||||
# Pre Setting: Create vnf package.
|
||||
sample_name = 'functional3'
|
||||
csar_package_path = os.path.abspath(
|
||||
@@ -1303,6 +1318,7 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
|
||||
In this test case, we do following steps.
|
||||
- Create subscription.
|
||||
- Test notification.
|
||||
- Create VNF package.
|
||||
- Upload VNF package.
|
||||
- Create VNF instance.
|
||||
@@ -1326,6 +1342,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
|
||||
subscription_id = response_body.get('id')
|
||||
self.addCleanup(self._delete_subscription, subscription_id)
|
||||
|
||||
self.assert_notification_get(
|
||||
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName))
|
||||
|
||||
# Pre Setting: Create vnf package.
|
||||
sample_name = 'functional4'
|
||||
csar_package_path = os.path.abspath(
|
||||
|
||||
@@ -108,15 +108,16 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest):
|
||||
glance_image = self._list_glance_image()[0]
|
||||
|
||||
# Create subscription and register it.
|
||||
callback_url = os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
request_body = fake_vnflcm.Subscription.make_create_request_body(
|
||||
'http://localhost:{}{}'.format(
|
||||
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
|
||||
os.path.join(
|
||||
vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)))
|
||||
callback_url))
|
||||
resp, response_body = self._register_subscription(request_body)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.assert_http_header_location_for_subscription(resp.headers)
|
||||
self.assert_notification_get(callback_url)
|
||||
subscription_id = response_body.get('id')
|
||||
self.addCleanup(self._delete_subscription, subscription_id)
|
||||
|
||||
|
||||
@@ -2734,7 +2734,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
|
||||
|
||||
@mock.patch.object(objects.LccnSubscriptionRequest,
|
||||
'vnf_lcm_subscriptions_get')
|
||||
def test_send_notification_rety_notification(self,
|
||||
def test_send_notification_retry_notification(self,
|
||||
mock_subscriptions_get):
|
||||
self.requests_mock.register_uri('POST',
|
||||
"https://localhost/callback",
|
||||
@@ -2760,7 +2760,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
|
||||
|
||||
@mock.patch.object(objects.LccnSubscriptionRequest,
|
||||
'vnf_lcm_subscriptions_get')
|
||||
def test_sendNotification_sendError(self,
|
||||
def test_send_notification_send_error(self,
|
||||
mock_subscriptions_get):
|
||||
self.requests_mock.register_uri(
|
||||
'POST',
|
||||
@@ -2818,10 +2818,118 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
|
||||
# return value when timeout for POST method is 0
|
||||
self.assertEqual(result, 0)
|
||||
|
||||
def test_get_notification(self):
|
||||
cfg.CONF.set_override('test_callback_uri', True,
|
||||
group='vnf_lcm')
|
||||
self.requests_mock.register_uri('GET',
|
||||
"https://localhost/callback",
|
||||
headers={
|
||||
'Content-Type': 'application/json'},
|
||||
status_code=204)
|
||||
|
||||
callback_uri = 'https://localhost/callback'
|
||||
|
||||
vnf_lcm_subscription = objects.\
|
||||
LccnSubscriptionRequest(context=self.context)
|
||||
vnf_lcm_subscription.id = uuidsentinel.lcm_subscription_id
|
||||
vnf_lcm_subscription.callback_uri = callback_uri
|
||||
|
||||
result = self.conductor.test_notification(self.context,
|
||||
vnf_lcm_subscription)
|
||||
|
||||
# return value when successful for GET method is 0
|
||||
self.assertEqual(result, 0)
|
||||
|
||||
history = self.requests_mock.request_history
|
||||
req_count = nfvo_client._count_mock_history(
|
||||
history, "https://localhost")
|
||||
self.assertEqual(1, req_count)
|
||||
cfg.CONF.set_override('test_callback_uri', False,
|
||||
group='vnf_lcm')
|
||||
|
||||
def test_get_notification_callback_uri_false(self):
|
||||
cfg.CONF.set_override('test_callback_uri', False,
|
||||
group='vnf_lcm')
|
||||
self.requests_mock.register_uri('GET',
|
||||
"https://localhost/callback",
|
||||
headers={
|
||||
'Content-Type': 'application/json'},
|
||||
status_code=204)
|
||||
|
||||
callback_uri = 'https://localhost/callback'
|
||||
|
||||
vnf_lcm_subscription = objects.\
|
||||
LccnSubscriptionRequest(context=self.context)
|
||||
vnf_lcm_subscription.id = uuidsentinel.lcm_subscription_id
|
||||
vnf_lcm_subscription.callback_uri = callback_uri
|
||||
|
||||
result = self.conductor.test_notification(self.context,
|
||||
vnf_lcm_subscription)
|
||||
|
||||
# return value when successful for GET method is 0
|
||||
self.assertEqual(result, 0)
|
||||
|
||||
history = self.requests_mock.request_history
|
||||
req_count = nfvo_client._count_mock_history(
|
||||
history, "https://localhost")
|
||||
self.assertEqual(0, req_count)
|
||||
|
||||
def test_get_notification_retry(self):
|
||||
cfg.CONF.set_override('test_callback_uri', True,
|
||||
group='vnf_lcm')
|
||||
self.requests_mock.register_uri('GET',
|
||||
"https://localhost/callback",
|
||||
headers={
|
||||
'Content-Type': 'application/json'},
|
||||
status_code=400)
|
||||
|
||||
callback_uri = 'https://localhost/callback'
|
||||
|
||||
vnf_lcm_subscription = objects.\
|
||||
LccnSubscriptionRequest(context=self.context)
|
||||
vnf_lcm_subscription.id = uuidsentinel.lcm_subscription_id
|
||||
vnf_lcm_subscription.callback_uri = callback_uri
|
||||
|
||||
result = self.conductor.test_notification(self.context,
|
||||
vnf_lcm_subscription)
|
||||
|
||||
# return value when error occurs for GET method is -1
|
||||
self.assertEqual(result, -1)
|
||||
|
||||
history = self.requests_mock.request_history
|
||||
req_count = nfvo_client._count_mock_history(
|
||||
history, "https://localhost")
|
||||
self.assertEqual(3, req_count)
|
||||
cfg.CONF.set_override('test_callback_uri', False,
|
||||
group='vnf_lcm')
|
||||
|
||||
def test_get_notification_timeout(self):
|
||||
cfg.CONF.set_override('test_callback_uri', True,
|
||||
group='vnf_lcm')
|
||||
self.requests_mock.register_uri(
|
||||
'GET',
|
||||
"https://localhost/callback",
|
||||
exc=requests.Timeout)
|
||||
|
||||
callback_uri = 'https://localhost/callback'
|
||||
|
||||
vnf_lcm_subscription = objects.\
|
||||
LccnSubscriptionRequest(context=self.context)
|
||||
vnf_lcm_subscription.id = uuidsentinel.lcm_subscription_id
|
||||
vnf_lcm_subscription.callback_uri = callback_uri
|
||||
|
||||
result = self.conductor.test_notification(self.context,
|
||||
vnf_lcm_subscription)
|
||||
|
||||
# return value when error for GET method is -1
|
||||
self.assertEqual(result, -1)
|
||||
|
||||
history = self.requests_mock.request_history
|
||||
req_count = nfvo_client._count_mock_history(
|
||||
history, "https://localhost")
|
||||
self.assertEqual(3, req_count)
|
||||
cfg.CONF.set_override('test_callback_uri', False,
|
||||
group='vnf_lcm')
|
||||
|
||||
@mock.patch.object(conductor_server, 'revert_update_lcm')
|
||||
@mock.patch.object(t_context.get_admin_context().session, "add")
|
||||
|
||||
Reference in New Issue
Block a user