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:
Aldinson Esto 2021-01-23 07:32:43 +09:00
parent 6b4cf268a9
commit 943e0b0815
8 changed files with 246 additions and 21 deletions

View File

@ -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())

View File

@ -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,

View File

@ -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()

View File

@ -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',

View File

@ -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,

View File

@ -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(

View File

@ -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)

View File

@ -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")