aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/onapsdk/aai/business/pnf.py67
-rw-r--r--src/onapsdk/aai/templates/aai_put_pnf.json.j254
-rw-r--r--src/onapsdk/dmaap/dmaap.py19
-rw-r--r--src/onapsdk/version.py2
-rw-r--r--tests/test_aai_pnf.py78
-rw-r--r--tests/test_dmaap.py24
-rw-r--r--tests/test_version.py2
7 files changed, 231 insertions, 15 deletions
diff --git a/src/onapsdk/aai/business/pnf.py b/src/onapsdk/aai/business/pnf.py
index d8ce746..093b6e1 100644
--- a/src/onapsdk/aai/business/pnf.py
+++ b/src/onapsdk/aai/business/pnf.py
@@ -13,19 +13,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from typing import Iterator, Optional, TYPE_CHECKING
+import json
+from pathlib import Path
+from typing import TYPE_CHECKING, Iterator, Optional
+
+from jinja2 import Environment, FileSystemLoader
from onapsdk.exceptions import ResourceNotFound
from onapsdk.so.deletion import PnfDeletionRequest
+
from .instance import Instance
if TYPE_CHECKING:
from .service import ServiceInstance # pylint: disable=cyclic-import
+
class PnfInstance(Instance): # pylint: disable=too-many-instance-attributes
"""Pnf instance class."""
- def __init__(self, # NOSONAR # pylint: disable=too-many-arguments, too-many-locals
+ def __init__(self, # NOSONAR # pylint: disable=too-many-arguments, too-many-locals
service_instance: "ServiceInstance",
pnf_name: str,
in_maint: bool,
@@ -171,10 +177,10 @@ class PnfInstance(Instance): # pylint: disable=too-many-instance-attributes
"""
for pnf_data in cls.send_message_json( \
- "GET", \
- "Get all pnf instances", \
- cls.get_all_url() \
- ).get("pnf", []):
+ "GET", \
+ "Get all pnf instances", \
+ cls.get_all_url() \
+ ).get("pnf", []):
yield cls.create_from_api_response(pnf_data, None)
@property
@@ -269,3 +275,52 @@ class PnfInstance(Instance): # pylint: disable=too-many-instance-attributes
"""
self._logger.debug("Delete %s pnf", self.pnf_name)
return PnfDeletionRequest.send_request(self, a_la_carte)
+
+ def delete_from_aai(self):
+ """DELETE PNF from AAI.
+
+ Send request to AAI to delete PNF from inventory
+
+ """
+ self._logger.debug("DELETE %s pnf from AAI", self.pnf_name)
+
+ # Get resource_version from AAI which is needed in DELETE
+ pnf_get_response = self.send_message_json("GET",
+ f"Get {self.pnf_name} PNF",
+ f"{self.url}")
+ temp_pnf = PnfInstance.create_from_api_response(pnf_get_response, None)
+ self._logger.info("resource_verison of Pnf is %s", temp_pnf.resource_version)
+
+ delete_url = self.url + "?resource-version=" + temp_pnf.resource_version
+ delete_response = self.send_message("DELETE",
+ f"Delete {self.pnf_name} PNF",
+ f"{delete_url}")
+
+ self._logger.debug("AAI Delete response status code is %s", delete_response.status_code)
+
+ def put_in_aai(self):
+ """PUT PNF in AAI.
+
+ Send request to AAI to put PNF in inventory
+
+ """
+ self._logger.debug("PUT %s pnf in AAI", self.pnf_name)
+
+ environment = Environment(autoescape=True,
+ loader=FileSystemLoader(
+ (Path(__file__).parent.parent).joinpath("templates/")))
+ template = environment.get_template("aai_put_pnf.json.j2")
+ environment.globals.update(convert_bool_for_json=json.dumps)
+ pnf_str = template.render(pnf_object=self)
+
+ # Remove blank lines and comma at the end, if present
+ req_body = "\n".join(item for item in pnf_str.split('\n') if item).replace(",\n}", "\n}")
+
+ self._logger.debug("PUT request body is %s", req_body)
+
+ put_response = self.send_message("PUT",
+ f"Put {self.pnf_name} PNF",
+ f"{self.url}",
+ data=req_body)
+
+ self._logger.debug("AAI Put response status code is %s", put_response.status_code)
diff --git a/src/onapsdk/aai/templates/aai_put_pnf.json.j2 b/src/onapsdk/aai/templates/aai_put_pnf.json.j2
new file mode 100644
index 0000000..65513c7
--- /dev/null
+++ b/src/onapsdk/aai/templates/aai_put_pnf.json.j2
@@ -0,0 +1,54 @@
+{
+{% if pnf_object.admin_status %}"admin_status": "{{ pnf_object.admin_status }}",{% else %}{%endif %}
+{% if pnf_object.equip_model %}"equip_model": "{{ pnf_object.equip_model }}",{% else %}{%endif %}
+{% if pnf_object.equip_type %}"equip_type": "{{ pnf_object.equip_type }}",{% else %}{%endif %}
+{% if pnf_object.equip_vendor %}"equip_vendor": "{{ pnf_object.equip_vendor }}",{% else %}{%endif %}
+{% if pnf_object.frame_id %}"frame_id": "{{ pnf_object.frame_id }}",{% else %}{%endif %}
+{% if pnf_object.in_maint != None %}"in_maint": {{ convert_bool_for_json(pnf_object.in_maint) }},{% else %}{%endif %}
+{% if pnf_object.inv_status %}"inv_status": "{{ pnf_object.inv_status }}",{% else %}{%endif %}
+{% if pnf_object.ipaddress_v4_aim %}"ipaddress_v4_aim": "{{ pnf_object.ipaddress_v4_aim }}",{% else %}{%endif %}
+{% if pnf_object.ipaddress_v4_loopback_0 %}"ipaddress_v4_loopback_0": "{{ pnf_object.ipaddress_v4_loopback_0 }}",{% else %}{%endif %}
+{% if pnf_object.ipaddress_v4_oam %}"ipaddress_v4_oam": "{{ pnf_object.ipaddress_v4_oam }}",{% else %}{%endif %}
+{% if pnf_object.ipaddress_v6_aim %}"ipaddress_v6_aim": "{{ pnf_object.ipaddress_v6_aim }}",{% else %}{%endif %}
+{% if pnf_object.ipaddress_v6_loopback_0 %}"ipaddress_v6_loopback_0": "{{ pnf_object.ipaddress_v6_loopback_0 }}",{% else %}{%endif %}
+{% if pnf_object.ipaddress_v6_oam %}"ipaddress_v6_oam": "{{ pnf_object.ipaddress_v6_oam }}",{% else %}{%endif %}
+{% if pnf_object.management_option %}"management_option": "{{ pnf_object.management_option }}",{% else %}{%endif %}
+{% if pnf_object.model_customization_id %}"model_customization_id": "{{ pnf_object.model_customization_id }}",{% else %}{%endif %}
+{% if pnf_object.model_invariant_id %}"model_invariant_id": "{{ pnf_object.model_invariant_id }}",{% else %}{%endif %}
+{% if pnf_object.model_version_id %}"model_version_id": "{{ pnf_object.model_version_id }}",{% else %}{%endif %}
+{% if pnf_object.nf_role %}"nf_role": "{{ pnf_object.nf_role }}",{% else %}{%endif %}
+{% if pnf_object.operational_status %}"operational_status": "{{ pnf_object.operational_status }}",{% else %}{%endif %}
+{% if pnf_object.orchestration_status %}"orchestration_status": "{{ pnf_object.orchestration_status }}",{% else %}{%endif %}
+{% if pnf_object.pnf_id %}"pnf_id": "{{ pnf_object.pnf_id }}",{% else %}{%endif %}
+{% if pnf_object.pnf_ipv4_address %}"pnf_ipv4_address": "{{ pnf_object.pnf_ipv4_address }}",{% else %}{%endif %}
+{% if pnf_object.pnf_ipv6_address %}"pnf_ipv6_address": "{{ pnf_object.pnf_ipv6_address }}",{% else %}{%endif %}
+{% if pnf_object.pnf_name %}"pnf_name": "{{ pnf_object.pnf_name }}",{% else %}{%endif %}
+{% if pnf_object.prov_status %}"prov_status": "{{ pnf_object.prov_status }}",{% else %}{%endif %}
+{% if pnf_object.selflink %}"selflink": "{{ pnf_object.selflink }}",{% else %}{%endif %}
+{% if pnf_object.serial_number %}"serial_number": "{{ pnf_object.serial_number }}",{% else %}{%endif %}
+{% if pnf_object.sw_version %}"sw_version": "{{ pnf_object.sw_version }}",{% else %}{%endif %}
+{% if pnf_object.service_instance %}"service_instance": {
+{% if pnf_object.service_instance.service_subscription %}"service_subscription": "{{ pnf_object.service_instance.service_subscription }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.instance_id %}"instance_id": "{{ pnf_object.service_instance.instance_id }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.instance_name %}"instance_name": "{{ pnf_object.service_instance.instance_name }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.service_type %}"service_type": "{{ pnf_object.service_instance.service_type }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.service_role %}"service_role": "{{ pnf_object.service_instance.service_role }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.environment_context %}"environment_context": "{{ pnf_object.service_instance.environment_context }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.workload_context %}"workload_context": "{{ pnf_object.service_instance.workload_context }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.created_at %}"created_at": "{{ pnf_object.service_instance.created_at }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.updated_at %}"updated_at": "{{ pnf_object.service_instance.updated_at }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.resource_version %}"resource_version": "{{ pnf_object.service_instance.resource_version }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.description %}"description": "{{ pnf_object.service_instance.description }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.model_invariant_id %}"model_invariant_id": "{{ pnf_object.service_instance.model_invariant_id }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.model_version_id %}"model_version_id": "{{ pnf_object.service_instance.model_version_id }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.persona_model_version %}"persona_model_version": "{{ pnf_object.service_instance.persona_model_version }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.widget_model_id %}"widget_model_id": "{{ pnf_object.service_instance.widget_model_id }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.widget_model_version %}"widget_model_version": "{{ pnf_object.service_instance.widget_model_version }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.bandwith_total %}"bandwith_total": "{{ pnf_object.service_instance.bandwith_total }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.vhn_portal_url %}"vhn_portal_url": "{{ pnf_object.service_instance.vhn_portal_url }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.service_instance_location_id %}"service_instance_location_id": "{{ pnf_object.service_instance.service_instance_location_id }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.selflink %}"selflink": "{{ pnf_object.service_instance.selflink }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.orchestration_status %}"orchestration_status": "{{ pnf_object.service_instance.orchestration_status }}",{% else %}{%endif %}
+{% if pnf_object.service_instance.input_parameters %}"input_parameters": "{{ pnf_object.service_instance.input_parameters }}",{% else %}{%endif %}
+}{% else %}{%endif %}
+}
diff --git a/src/onapsdk/dmaap/dmaap.py b/src/onapsdk/dmaap/dmaap.py
index 27c72c5..888ef98 100644
--- a/src/onapsdk/dmaap/dmaap.py
+++ b/src/onapsdk/dmaap/dmaap.py
@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Dict
-
from onapsdk.dmaap.dmaap_service import DmaapService
ACTION = "Get events from Dmaap"
@@ -85,3 +84,21 @@ class Dmaap(DmaapService):
url,
basic_auth=basic_auth
)
+
+ @classmethod
+ def post_event(cls,
+ topic: str,
+ event: str):
+ """Post an event on given topic.
+
+ Post an event on given topic by calling DMaaP REST API
+
+ Args:
+ topic: (str) topic on which to publish the event
+ event: (str) event payload
+
+ """
+ cls.send_message("POST",
+ f"Publish Event via DMaaP on {topic} topic",
+ f"{cls.get_all_events_url}/{topic}",
+ data=event)
diff --git a/src/onapsdk/version.py b/src/onapsdk/version.py
index 3035ac5..699720e 100644
--- a/src/onapsdk/version.py
+++ b/src/onapsdk/version.py
@@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-__version__ = "12.6.1"
+__version__ = "12.7.0"
diff --git a/tests/test_aai_pnf.py b/tests/test_aai_pnf.py
index 9c417ba..adb5a56 100644
--- a/tests/test_aai_pnf.py
+++ b/tests/test_aai_pnf.py
@@ -15,11 +15,10 @@ from unittest import mock
import pytest
-from onapsdk.aai.business import PnfInstance, pnf, ServiceInstance
-from onapsdk.exceptions import ResourceNotFound
+from onapsdk.aai.business import PnfInstance, ServiceInstance, pnf
+from onapsdk.exceptions import ResourceNotFound, APIError, ConnectionFailed
from onapsdk.so.deletion import PnfDeletionRequest
-
PNF_INSTANCE = {
"pnf-name": "blablabla",
"pnf-id": "546b282b-2ff7-41a4-9329-55c9a2888477",
@@ -151,3 +150,76 @@ def test_pnf_instance_pnf():
def test_pnf_count(mock_send_message_json):
mock_send_message_json.return_value = COUNT
assert PnfInstance.count() == 12
+
+@mock.patch.object(PnfInstance,"send_message")
+def test_delete_from_aai_success(mock_send_message):
+
+ delete_response = mock.MagicMock()
+ delete_response.status_code = 204 #success case
+
+ mock_send_message.return_value= delete_response
+ pnf_instance = PnfInstance(service_instance=None,
+ pnf_id="test_pnf_id",
+ pnf_name="test_pnf_name",
+ serial_number="test_serial_number",
+ in_maint=False)
+ try:
+ pnf_instance.delete_from_aai()
+ except APIError:
+ assert False # Exception is not expected
+
+@mock.patch.object(PnfInstance,"send_message")
+def test_delete_from_aai_failure(mock_send_message):
+
+ mock_send_message.side_effect = ConnectionFailed('Can not connect to AAI')
+
+ pnf_instance = PnfInstance(service_instance=None,
+ pnf_id="test_pnf_id",
+ pnf_name="test_pnf_name",
+ serial_number="test_serial_number",
+ in_maint=False)
+ with pytest.raises(ConnectionFailed):
+ pnf_instance.delete_from_aai()
+
+@mock.patch.object(PnfInstance,"send_message")
+def test_put_in_aai_success(mock_send_message):
+ put_response = mock.MagicMock()
+ put_response.status_code = 201 #success case
+
+ mock_send_message.return_value = put_response
+ pnf_instance = PnfInstance(service_instance=None,
+ pnf_id="test_pnf_id",
+ pnf_name="test_pnf_name",
+ serial_number="test_serial_number",
+ in_maint=False)
+ try:
+ pnf_instance.put_in_aai()
+ except APIError:
+ assert False # Exception is not expected
+
+@mock.patch.object(PnfInstance,"send_message")
+def test_put_in_aai_success_with_none_attribute(mock_send_message):
+ put_response = mock.MagicMock()
+ put_response.status_code = 201 #success case
+
+ mock_send_message.return_value = put_response
+ pnf_instance = PnfInstance(service_instance=None,
+ pnf_id="test_pnf_id",
+ pnf_name="test_pnf_name",
+ serial_number=None,
+ in_maint=False)
+ try:
+ pnf_instance.put_in_aai()
+ except APIError:
+ assert False # Exception is not expected
+
+@mock.patch.object(PnfInstance,"send_message")
+def test_put_in_aai_failure(mock_send_message):
+ mock_send_message.side_effect = ConnectionFailed('Can not connect to AAI')
+ pnf_instance = PnfInstance(service_instance=None,
+ pnf_id="test_pnf_id",
+ pnf_name="test_pnf_name",
+ serial_number="test_serial_number",
+ in_maint=False)
+ with pytest.raises(ConnectionFailed):
+ pnf_instance.put_in_aai() \ No newline at end of file
diff --git a/tests/test_dmaap.py b/tests/test_dmaap.py
index 28165bc..84ab581 100644
--- a/tests/test_dmaap.py
+++ b/tests/test_dmaap.py
@@ -11,10 +11,12 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+from unittest import mock
from unittest.mock import patch
-
-from onapsdk.dmaap.dmaap import Dmaap, ACTION, GET_HTTP_METHOD
-
+from requests import RequestException
+from onapsdk.dmaap.dmaap import ACTION, GET_HTTP_METHOD, Dmaap
+from onapsdk.exceptions import APIError, ConnectionFailed
+import pytest
TOPIC = "fault"
DMAAP_EVENTS_URL = "http://dmaap.api.simpledemo.onap.org:3904/events"
@@ -45,3 +47,19 @@ def verify_send_event_to_ves_called(send_message_mock, dmaap_url):
basic_auth=BASIC_AUTH
)
+@patch.object(Dmaap, "send_message")
+def test_post_event_success(send_message_mock):
+ post_response = mock.MagicMock()
+ post_response.status_code = 200 #success case
+ send_message_mock.return_value = post_response
+ try:
+ Dmaap.post_event("test_topic", "test_event")
+ except RequestException:
+ assert False # Exception is not expected
+
+
+@patch.object(Dmaap, "send_message")
+def test_post_event_failure(send_message_mock):
+ send_message_mock.side_effect = ConnectionFailed('Can not connect to dmaap')
+ with pytest.raises(ConnectionFailed):
+ Dmaap.post_event("test_topic", "test_event")
diff --git a/tests/test_version.py b/tests/test_version.py
index 264f488..5df9deb 100644
--- a/tests/test_version.py
+++ b/tests/test_version.py
@@ -17,4 +17,4 @@ import onapsdk.version as version
def test_version():
"""Check version is the right one."""
- assert version.__version__ == '12.6.1'
+ assert version.__version__ == '12.7.0'