Update from master part 2

Squashed commit of the following:

commit 364627c364a86a85696781766326dd690a362bc4
Author: vegall <lvega@whitestack.com>
Date:   Fri Mar 17 15:09:50 2023 +0000

    Feature 10972: Support of volume multi-attach

    Change-Id: I6e88ee52e5e882dbb4ec7d66cf648fbe07d40509
    Signed-off-by: vegall <lvega@whitestack.com>

commit 0e51779fd37dc5c12f3bd19d78f7341ed0a67b7a
Author: gifrerenom <lluis.gifre@cttc.es>
Date:   Tue Apr 18 16:38:42 2023 +0000

    Feature 10937: Transport API (TAPI) WIM connector for RO

    Change-Id: If0dac9f8ba2d00945eb86a89fb0b2f174c672794
    Signed-off-by: gifrerenom <lluis.gifre@cttc.es>

commit 370e36bafdcb90f212e289b87290f39be141b3d4
Author: elumalai <deepika.e@tataelxsi.co.in>
Date:   Tue Apr 25 16:22:56 2023 +0530

    Feature 10979: Static IPv6 Dual Stack Assignment

    Added support for static dual stack IP assignment

    Change-Id: Ief10ae955fb870a3417f68e1c5f7bda570cb6470
    Signed-off-by: elumalai <deepika.e@tataelxsi.co.in>

commit b1bc66933aa392b9d7518f7cebc711700335389c
Author: Gabriel Cuba <gcuba@whitestack.com>
Date:   Fri Aug 19 18:23:00 2022 -0500

    Fix Bug 2098: Get VDUs from VNFR when Heal op has no additionalPrameters

    When Heal is requested without vdu or count-index parameters, RO will recreate all VDUs from VNFR

    Change-Id: Idf2cf158bcb33e7b0c307298c14504cc7aa77e2a
    Signed-off-by: Gabriel Cuba <gcuba@whitestack.com>
    (cherry picked from commit 2fbb3a264e4117f4a6569fede6558836d67ac4a4)

commit aba1518f487b4b65861eb30f553c4edb72ad972e
Author: Gulsum Atici <gulsum.atici@canonical.com>
Date:   Mon May 15 11:55:13 2023 +0300

    Fix VimAdminThread run method

    The run_coroutine_threadsafe() function is used to schedule a coroutine object from a different thread and returns a concurrent.futures.Future.
    run_coroutine_threadsafe is unnecessary to run the main task and replaced with asyncio.run().

    Change-Id: I8ea31828a9798140d596165443bdf26659b4eef8
    Signed-off-by: Gulsum Atici <gulsum.atici@canonical.com>

commit f17e5bb6b6da4432628dd65ce9ad633e6441f67c
Author: Gulsum Atici <gulsum.atici@canonical.com>
Date:   Wed May 10 22:52:57 2023 +0300

    Minor updates in Dockerfile

    Change-Id: I79b43654d181f6976a4e544d58fb92aa1b67e760
    Signed-off-by: Gulsum Atici <gulsum.atici@canonical.com>

commit a264b7a460b28d7454fc95fe659da46f55b0c155
Author: Gulsum Atici <gulsum.atici@canonical.com>
Date:   Tue May 9 14:57:22 2023 +0300

    Ubuntu 22.04 and Python 3.10 preparation

    Change-Id: I87164827a8849c16b5e3a804d9673a578e5a5593
    Signed-off-by: Gulsum Atici <gulsum.atici@canonical.com>

commit 1c89c08a0dd1c79b5adff3ac1cc123239762e06a
Author: garciadeblas <gerardo.garciadeblas@telefonica.com>
Date:   Tue Apr 18 15:06:30 2023 +0200

    Clean stage-archive.sh and use allowlist_extenals in tox.ini

    Change-Id: I18f0bc3e263063b5b1d2cf211f028f6bb0e4bceb
    Signed-off-by: garciadeblas <gerardo.garciadeblas@telefonica.com>

commit 51e72a0f7479b3064b4b11891eb524d42f4738b0
Author: elumalai <deepika.e@tataelxsi.co.in>
Date:   Fri Apr 28 19:41:49 2023 +0530

    Coverity CWE 330: Use of Insufficiently Random Values

    Added support to fix CWE 330: Use of Insufficiently Random Values
    Coverity issue

    Change-Id: Ib12ebeeb9b0cc10af9980fe8661eb6230c2f6d6d
    Signed-off-by: elumalai <deepika.e@tataelxsi.co.in>

commit e17cd946aed699b5feca83d37591d04f129a8f52
Author: elumalai <deepika.e@tataelxsi.co.in>
Date:   Fri Apr 28 18:04:24 2023 +0530

    Coverity CWE 688: Function Call With Incorrect Variable or Reference as Argument

    Added fix for CWE 688 Typo in Identifier

    Change-Id: I53b5142451b809be638d73626265531057722169
    Signed-off-by: elumalai <deepika.e@tataelxsi.co.in>

commit 730cfaff466fb3c9b1446ecef5213916195ff861
Author: Gabriel Cuba <gcuba@whitestack.com>
Date:   Mon Mar 13 22:26:38 2023 -0500

    Feature 10975: get flavor-id from additionalParams if specified

    Change-Id: I1c9b1ec43c80f3793b475187681f4c2005d77375
    Signed-off-by: Gabriel Cuba <gcuba@whitestack.com>

commit 2d3f63b055e6a38e95bcff56a8ddef32767b11ef
Author: garciadeblas <gerardo.garciadeblas@telefonica.com>
Date:   Tue Apr 11 10:08:26 2023 +0200

    Update stage-build to run tox sequentially

    Change-Id: I967f19a8c35700290e93c9d8bd863b63b7c2d239
    Signed-off-by: garciadeblas <gerardo.garciadeblas@telefonica.com>
    (cherry picked from commit ea063c7a6ae6a5d7e11e8c22f9707d5c8f674ac7)

commit b3dbfcad6f4b2bebc9ebc20fd7129a18879cb20c
Author: Gabriel Cuba <gcuba@whitestack.com>
Date:   Tue Mar 14 10:58:39 2023 -0500

    Feature 10978: Add support of ipv6_address_mode and ipv6_ra_mode to openstack connector

    Change-Id: I8ca741a215bd2c52999dee1ea301d4e02aafcb24
    Signed-off-by: Gabriel Cuba <gcuba@whitestack.com>

commit 01619d5b596e01ac8cd6d27bf01a1174e6b3f97b
Author: Gulsum Atici <gulsum.atici@canonical.com>
Date:   Wed Mar 22 22:57:26 2023 +0300

    Keep vim_details while reporting VM deletion

    Change-Id: I27577b2fc93a585affc947abcec8352562f23f49
    Signed-off-by: Gulsum Atici <gulsum.atici@canonical.com>

commit 98740c03567ff8c5a22f06fd3f049248a9e5f98d
Author: Pedro Escaleira <escaleira@av.it.pt>
Date:   Wed Feb 22 10:48:52 2023 +0000

    Bug 2217 fixed: modified the cloud-init merge configs and defined the default SSH keys within the system_info instead of users

    Change-Id: I12e26a88fb9b50c4a78b9fa8ee2cb5d4b4bf6d00
    Signed-off-by: Pedro Escaleira <escaleira@av.it.pt>

commit d586d89bde00acaf22debd7f657d605c9d095571
Author: Gulsum Atici <gulsum.atici@canonical.com>
Date:   Mon Feb 13 18:40:03 2023 +0300

    Feature 10960 Performance optimizations for the polling of VM status in RO

    Change-Id: If785bbeaab66e0839541bf94184ce37114e67bd4
    Signed-off-by: Gulsum Atici <gulsum.atici@canonical.com>

commit 4c1dd54ae02e82f11a60058a1b7c7b0137ac572e
Author: Gabriel Cuba <gcuba@whitestack.com>
Date:   Tue Feb 14 12:43:32 2023 -0500

    Refactor ns.py so that RO uses the IP profile as it comes from LCM

    Change-Id: I36983c86d7c76ad8a9b93eb6eae254f844717e0e
    Signed-off-by: Gabriel Cuba <gcuba@whitestack.com>

commit 3822010a26b2e21290b6acdf288db277c7f36605
Author: garciadeblas <gerardo.garciadeblas@telefonica.com>
Date:   Mon Feb 13 17:48:32 2023 +0100

    Fix bug 2216 to remove hardcoded numa affinity in VIO

    Change-Id: I0912c2841e7c5c1febe056ba092afedaea77f6a1
    Signed-off-by: garciadeblas <gerardo.garciadeblas@telefonica.com>

commit 778f3cc8c052bd17d0da32f07b880616d25f935a
Author: Lovejeet Singh <lovejeet.singh@hsc.com>
Date:   Mon Feb 13 16:15:40 2023 +0530

    Bug 2202: Adding support for cinder V3 API with V2 API for persistent volumes.

    Change-Id: I7034564b91b94e6be242cb2ce0f4a5b147b87d64
    Signed-off-by: Lovejeet Singh <lovejeet.singh@hsc.com>

Change-Id: I7ac1bd1d9896788812f456c678b1f5222a1f1ad6
Signed-off-by: Dario Faccin <dario.faccin@canonical.com>
diff --git a/NG-RO/osm_ng_ro/ns.py b/NG-RO/osm_ng_ro/ns.py
index fd0ad07..75bae1b 100644
--- a/NG-RO/osm_ng_ro/ns.py
+++ b/NG-RO/osm_ng_ro/ns.py
@@ -117,6 +117,7 @@
             "flavor": Ns._process_flavor_params,
             "vdu": Ns._process_vdu_params,
             "affinity-or-anti-affinity-group": Ns._process_affinity_group_params,
+            "shared-volumes": Ns._process_shared_volumes_params,
         }
         self.db_path_map = {
             "net": "vld",
@@ -124,6 +125,7 @@
             "flavor": "flavor",
             "vdu": "vdur",
             "affinity-or-anti-affinity-group": "affinity-or-anti-affinity-group",
+            "shared-volumes": "shared-volumes",
         }
 
     def init_db(self, target_version):
@@ -859,7 +861,6 @@
         flavor_data_name = flavor_data.copy()
         flavor_data_name["name"] = target_flavor["name"]
         extra_dict["params"] = {"flavor_data": flavor_data_name}
-
         return extra_dict
 
     @staticmethod
@@ -1053,11 +1054,38 @@
         if not virtual_storage_desc.get("vdu-storage-requirements"):
             return False
         for item in virtual_storage_desc.get("vdu-storage-requirements", {}):
-            if item.get("key") == "keep-volume" and item.get("value") == "true":
+            if item.get("key") == "keep-volume" and item.get("value").lower() == "true":
                 return True
         return False
 
     @staticmethod
+    def is_shared_volume(
+        virtual_storage_desc: Dict[str, Any], vnfd_id: str
+    ) -> (str, bool):
+        """Function to decide if the volume type is multi attached or not .
+
+        Args:
+            virtual_storage_desc (Dict[str, Any]): virtual storage description dictionary
+            vnfd_id (str): vnfd id
+
+        Returns:
+            bool (True/False)
+            name (str) New name if it is a multiattach disk
+        """
+
+        if vdu_storage_requirements := virtual_storage_desc.get(
+            "vdu-storage-requirements", {}
+        ):
+            for item in vdu_storage_requirements:
+                if (
+                    item.get("key") == "multiattach"
+                    and item.get("value").lower() == "true"
+                ):
+                    name = f"shared-{virtual_storage_desc['id']}-{vnfd_id}"
+                    return name, True
+        return virtual_storage_desc["id"], False
+
+    @staticmethod
     def _sort_vdu_interfaces(target_vdu: dict) -> None:
         """Sort the interfaces according to position number.
 
@@ -1385,7 +1413,6 @@
                         "size": root_disk["size-of-storage"],
                         "keep": Ns.is_volume_keeping_required(root_disk),
                     }
-
                     disk_list.append(persistent_root_disk[vsd["id"]])
                     break
 
@@ -1395,6 +1422,7 @@
         persistent_root_disk: dict,
         persistent_ordinary_disk: dict,
         disk_list: list,
+        vnf_id: str = None,
     ) -> None:
         """Fill the disk list by adding persistent ordinary disks.
 
@@ -1412,9 +1440,12 @@
                     == "persistent-storage:persistent-storage"
                     and disk["id"] not in persistent_root_disk.keys()
                 ):
+                    name, multiattach = Ns.is_shared_volume(disk, vnf_id)
                     persistent_ordinary_disk[disk["id"]] = {
+                        "name": name,
                         "size": disk["size-of-storage"],
                         "keep": Ns.is_volume_keeping_required(disk),
+                        "multiattach": multiattach,
                     }
                     disk_list.append(persistent_ordinary_disk[disk["id"]])
 
@@ -1486,7 +1517,6 @@
         image_text = ns_preffix + ":image." + target_vdu["ns-image-id"]
         extra_dict = {"depends_on": [image_text]}
         net_list = []
-
         persistent_root_disk = {}
         persistent_ordinary_disk = {}
         vdu_instantiation_volumes_list = []
@@ -1494,7 +1524,6 @@
         disk_list = []
         vnfd_id = vnfr["vnfd-id"]
         vnfd = db.get_one("vnfds", {"_id": vnfd_id})
-
         # If the position info is provided for all the interfaces, it will be sorted
         # according to position number ascendingly.
         if all(
@@ -1565,7 +1594,11 @@
             )
             # Add the persistent non-root disks to disk_list
             Ns._add_persistent_ordinary_disks_to_disk_list(
-                target_vdu, persistent_root_disk, persistent_ordinary_disk, disk_list
+                target_vdu,
+                persistent_root_disk,
+                persistent_ordinary_disk,
+                disk_list,
+                vnfd["id"],
             )
 
         affinity_group_list = Ns._prepare_vdu_affinity_group_list(
@@ -1590,7 +1623,23 @@
             "availability_zone_index": None,  # TODO
             "availability_zone_list": None,  # TODO
         }
+        return extra_dict
 
+    @staticmethod
+    def _process_shared_volumes_params(
+        target_shared_volume: Dict[str, Any],
+        indata: Dict[str, Any],
+        vim_info: Dict[str, Any],
+        target_record_id: str,
+        **kwargs: Dict[str, Any],
+    ) -> Dict[str, Any]:
+        extra_dict = {}
+        shared_volume_data = {
+            "size": target_shared_volume["size-of-storage"],
+            "name": target_shared_volume["id"],
+            "type": target_shared_volume["type-of-storage"],
+        }
+        extra_dict["params"] = shared_volume_data
         return extra_dict
 
     @staticmethod
@@ -1628,7 +1677,6 @@
         extra_dict["params"] = {
             "affinity_group_data": affinity_group_data,
         }
-
         return extra_dict
 
     @staticmethod
@@ -1664,6 +1712,7 @@
 
         vim_details = {}
         vim_details_text = existing_vdu["vim_info"][target_id].get("vim_details", None)
+
         if vim_details_text:
             vim_details = yaml.safe_load(f"{vim_details_text}")
 
@@ -1857,7 +1906,6 @@
         process_params = None
         vdu2cloud_init = indata.get("cloud_init_content") or {}
         ro_nsr_public_key = db_ro_nsr["public_key"]
-
         # According to the type of item, the path, the target_list,
         # the existing_list and the method to process params are set
         db_path = self.db_path_map[item]
@@ -1877,27 +1925,29 @@
                 )
                 target_list = target_vnf.get(db_path, []) if target_vnf else []
                 existing_list = vnfr.get(db_path, [])
-        elif item in ("image", "flavor", "affinity-or-anti-affinity-group"):
+        elif item in (
+            "image",
+            "flavor",
+            "affinity-or-anti-affinity-group",
+            "shared-volumes",
+        ):
             db_record = "nsrs:{}:{}".format(nsr_id, db_path)
             target_list = indata.get(item, [])
             existing_list = db_nsr.get(item, [])
         else:
             raise NsException("Item not supported: {}", item)
-
         # ensure all the target_list elements has an "id". If not assign the index as id
         if target_list is None:
             target_list = []
         for target_index, tl in enumerate(target_list):
             if tl and not tl.get("id"):
                 tl["id"] = str(target_index)
-
         # step 1 items (networks,vdus,...) to be deleted/updated
         for item_index, existing_item in enumerate(existing_list):
             target_item = next(
                 (t for t in target_list if t["id"] == existing_item["id"]),
                 None,
             )
-
             for target_vim, existing_viminfo in existing_item.get(
                 "vim_info", {}
             ).items():
@@ -1941,7 +1991,6 @@
         # step 2 items (networks,vdus,...) to be created
         for target_item in target_list:
             item_index = -1
-
             for item_index, existing_item in enumerate(existing_list):
                 if existing_item["id"] == target_item["id"]:
                     break
@@ -1998,7 +2047,6 @@
                         }
                     )
                     self.logger.debug("calculate_diff_items kwargs={}".format(kwargs))
-
                 extra_dict = process_params(
                     target_item,
                     indata,
@@ -2068,7 +2116,13 @@
         changes_list = []
 
         # NS vld, image and flavor
-        for item in ["net", "image", "flavor", "affinity-or-anti-affinity-group"]:
+        for item in [
+            "net",
+            "image",
+            "flavor",
+            "affinity-or-anti-affinity-group",
+            "shared-volumes",
+        ]:
             self.logger.debug("process NS={} {}".format(nsr_id, item))
             diff_items, task_index = self.calculate_diff_items(
                 indata=indata,
diff --git a/NG-RO/osm_ng_ro/ns_thread.py b/NG-RO/osm_ng_ro/ns_thread.py
index 03e8b30..03255e3 100644
--- a/NG-RO/osm_ng_ro/ns_thread.py
+++ b/NG-RO/osm_ng_ro/ns_thread.py
@@ -347,7 +347,6 @@
         created = False
         created_items = {}
         target_vim = self.my_vims[ro_task["target_id"]]
-
         try:
             created = True
             params = task["params"]
@@ -389,7 +388,6 @@
                         )
 
                     affinity_group["affinity_group_id"] = affinity_group_id
-
             vim_vm_id, created_items = target_vim.new_vminstance(**params_copy)
             interfaces = [iface["vim_id"] for iface in params_copy["net_list"]]
 
@@ -691,6 +689,102 @@
             return "FAILED", ro_vim_item_update
 
 
+class VimInteractionSharedVolume(VimInteractionBase):
+    def delete(self, ro_task, task_index):
+        task = ro_task["tasks"][task_index]
+        task_id = task["task_id"]
+        shared_volume_vim_id = ro_task["vim_info"]["vim_id"]
+        ro_vim_item_update_ok = {
+            "vim_status": "DELETED",
+            "created": False,
+            "vim_message": "DELETED",
+            "vim_id": None,
+        }
+        try:
+            if shared_volume_vim_id:
+                target_vim = self.my_vims[ro_task["target_id"]]
+                target_vim.delete_shared_volumes(shared_volume_vim_id)
+        except vimconn.VimConnNotFoundException:
+            ro_vim_item_update_ok["vim_message"] = "already deleted"
+        except vimconn.VimConnException as e:
+            self.logger.error(
+                "ro_task={} vim={} del-shared-volume={}: {}".format(
+                    ro_task["_id"], ro_task["target_id"], shared_volume_vim_id, e
+                )
+            )
+            ro_vim_item_update = {
+                "vim_status": "VIM_ERROR",
+                "vim_message": "Error while deleting: {}".format(e),
+            }
+
+            return "FAILED", ro_vim_item_update
+
+        self.logger.debug(
+            "task={} {} del-shared-volume={} {}".format(
+                task_id,
+                ro_task["target_id"],
+                shared_volume_vim_id,
+                ro_vim_item_update_ok.get("vim_message", ""),
+            )
+        )
+
+        return "DONE", ro_vim_item_update_ok
+
+    def new(self, ro_task, task_index, task_depends):
+        task = ro_task["tasks"][task_index]
+        task_id = task["task_id"]
+        created = False
+        created_items = {}
+        target_vim = self.my_vims[ro_task["target_id"]]
+
+        try:
+            shared_volume_name = None
+            shared_volume_vim_id = None
+            shared_volume_data = None
+
+            if task.get("params"):
+                shared_volume_data = task["params"]
+
+            if shared_volume_data:
+                self.logger.info(
+                    f"Creating the new shared_volume for {shared_volume_data}\n"
+                )
+                (
+                    shared_volume_name,
+                    shared_volume_vim_id,
+                ) = target_vim.new_shared_volumes(shared_volume_data)
+                created = True
+                created_items[shared_volume_vim_id] = shared_volume_name
+
+            ro_vim_item_update = {
+                "vim_id": shared_volume_vim_id,
+                "vim_status": "DONE",
+                "created": created,
+                "created_items": created_items,
+                "vim_details": None,
+                "vim_message": None,
+            }
+            self.logger.debug(
+                "task={} {} new-shared-volume={} created={}".format(
+                    task_id, ro_task["target_id"], shared_volume_vim_id, created
+                )
+            )
+
+            return "DONE", ro_vim_item_update
+        except (vimconn.VimConnException, NsWorkerException) as e:
+            self.logger.error(
+                "task={} vim={} new-shared-volume:"
+                " {}".format(task_id, ro_task["target_id"], e)
+            )
+            ro_vim_item_update = {
+                "vim_status": "VIM_ERROR",
+                "created": created,
+                "vim_message": str(e),
+            }
+
+            return "FAILED", ro_vim_item_update
+
+
 class VimInteractionFlavor(VimInteractionBase):
     def delete(self, ro_task, task_index):
         task = ro_task["tasks"][task_index]
@@ -739,7 +833,6 @@
         created = False
         created_items = {}
         target_vim = self.my_vims[ro_task["target_id"]]
-
         try:
             # FIND
             vim_flavor_id = None
@@ -1540,6 +1633,9 @@
         self.db = db
         self.item2class = {
             "net": VimInteractionNet(self.db, self.my_vims, self.db_vims, self.logger),
+            "shared-volumes": VimInteractionSharedVolume(
+                self.db, self.my_vims, self.db_vims, self.logger
+            ),
             "vdu": VimInteractionVdu(self.db, self.my_vims, self.db_vims, self.logger),
             "image": VimInteractionImage(
                 self.db, self.my_vims, self.db_vims, self.logger
@@ -2301,7 +2397,6 @@
                             lock_object = LockRenew.add_lock_object(
                                 "ro_tasks", ro_task, self
                             )
-
                         if task["action"] == "DELETE":
                             (
                                 new_status,
diff --git a/NG-RO/osm_ng_ro/tests/test_ns.py b/NG-RO/osm_ng_ro/tests/test_ns.py
index d966a86..a236819 100644
--- a/NG-RO/osm_ng_ro/tests/test_ns.py
+++ b/NG-RO/osm_ng_ro/tests/test_ns.py
@@ -4547,6 +4547,8 @@
             {
                 "size": "10",
                 "keep": False,
+                "multiattach": False,
+                "name": "persistent-volume2",
             }
         ]
         self.ns._add_persistent_ordinary_disks_to_disk_list(
diff --git a/NG-RO/osm_ng_ro/tests/test_ns_thread.py b/NG-RO/osm_ng_ro/tests/test_ns_thread.py
index 0914a06..4e42b4f 100644
--- a/NG-RO/osm_ng_ro/tests/test_ns_thread.py
+++ b/NG-RO/osm_ng_ro/tests/test_ns_thread.py
@@ -27,6 +27,7 @@
     VimInteractionMigration,
     VimInteractionNet,
     VimInteractionResize,
+    VimInteractionSharedVolume,
 )
 from osm_ro_plugin.vimconn import VimConnConnectionException, VimConnException
 
@@ -1450,6 +1451,218 @@
                 instance.refresh(ro_task)
 
 
+class TestVimInteractionSharedVolume(unittest.TestCase):
+    def setUp(self):
+        module_name = "osm_ro_plugin"
+        self.target_vim = MagicMock(name=f"{module_name}.vimconn.VimConnector")
+        self.task_depends = None
+
+        patches = [patch(f"{module_name}.vimconn.VimConnector", self.target_vim)]
+
+        # Enabling mocks and add cleanups
+        for mock in patches:
+            mock.start()
+            self.addCleanup(mock.stop)
+
+    def test__new_shared_volume_ok(self):
+        """
+        create a shared volume with attributes set in params
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+
+        instance = VimInteractionSharedVolume(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "target_id": 0,
+                "tasks": {
+                    "task_index_1": {
+                        "target_id": 0,
+                        "action_id": "123456",
+                        "nsr_id": "654321",
+                        "task_id": "123456:1",
+                        "status": "SCHEDULED",
+                        "action": "CREATE",
+                        "item": "test_item",
+                        "target_record": "test_target_record",
+                        "target_record_id": "test_target_record_id",
+                        # values coming from extra_dict
+                        "params": {
+                            "shared_volume_data": {
+                                "size": "10",
+                                "name": "shared-volume",
+                                "type": "multiattach",
+                            }
+                        },
+                        "find_params": {},
+                        "depends_on": "test_depends_on",
+                    },
+                },
+            }
+            task_index = "task_index_1"
+            self.target_vim.new_shared_volumes.return_value = ("", "shared-volume")
+            result = instance.new(ro_task, task_index, self.task_depends)
+            self.assertEqual(result[0], "DONE")
+            self.assertEqual(result[1].get("vim_id"), "shared-volume")
+            self.assertEqual(result[1].get("created"), True)
+            self.assertEqual(result[1].get("vim_status"), "DONE")
+
+    def test__new_shared_volume_failed(self):
+        """
+        create a shared volume with attributes set in params failed
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+
+        instance = VimInteractionSharedVolume(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "target_id": 0,
+                "tasks": {
+                    "task_index_1": {
+                        "target_id": 0,
+                        "action_id": "123456",
+                        "nsr_id": "654321",
+                        "task_id": "123456:1",
+                        "status": "SCHEDULED",
+                        "action": "CREATE",
+                        "item": "test_item",
+                        "target_record": "test_target_record",
+                        "target_record_id": "test_target_record_id",
+                        # values coming from extra_dict
+                        "params": {
+                            "shared_volume_data": {
+                                "size": "10",
+                                "name": "shared-volume",
+                                "type": "multiattach",
+                            }
+                        },
+                        "find_params": {},
+                        "depends_on": "test_depends_on",
+                    },
+                },
+            }
+            task_index = "task_index_1"
+            self.target_vim.new_shared_volumes.side_effect = VimConnException(
+                "Connection failed."
+            )
+            result = instance.new(ro_task, task_index, self.task_depends)
+            self.assertEqual(result[0], "FAILED")
+            self.assertEqual(result[1].get("vim_message"), "Connection failed.")
+            self.assertEqual(result[1].get("created"), False)
+            self.assertEqual(result[1].get("vim_status"), "VIM_ERROR")
+
+    def test__delete_shared_volume_ok(self):
+        """
+        Delete a shared volume with attributes set in params
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+
+        instance = VimInteractionSharedVolume(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "target_id": 0,
+                "tasks": {
+                    "task_index_3": {
+                        "target_id": 0,
+                        "task_id": "123456:1",
+                    },
+                },
+                "vim_info": {
+                    "created": False,
+                    "created_items": None,
+                    "vim_id": "sample_shared_volume_id_3",
+                    "vim_name": "sample_shared_volume_3",
+                    "vim_status": None,
+                    "vim_details": "some-details",
+                    "vim_message": None,
+                    "refresh_at": None,
+                },
+            }
+
+            task_index = "task_index_3"
+            self.target_vim.delete_shared_volumes.return_value = True
+            result = instance.delete(ro_task, task_index)
+            self.assertEqual(result[0], "DONE")
+            self.assertEqual(result[1].get("vim_id"), None)
+            self.assertEqual(result[1].get("created"), False)
+            self.assertEqual(result[1].get("vim_status"), "DELETED")
+
+    def test__delete_shared_volume_failed(self):
+        """
+        Delete a shared volume with attributes set in params failed
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+
+        instance = VimInteractionSharedVolume(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "_id": "122436:1",
+                "target_id": 0,
+                "tasks": {
+                    "task_index_3": {
+                        "target_id": 0,
+                        "task_id": "123456:1",
+                    },
+                },
+                "vim_info": {
+                    "created": False,
+                    "created_items": None,
+                    "vim_id": "sample_shared_volume_id_3",
+                    "vim_name": "sample_shared_volume_3",
+                    "vim_status": None,
+                    "vim_details": "some-details",
+                    "vim_message": None,
+                    "refresh_at": None,
+                },
+            }
+
+            task_index = "task_index_3"
+            self.target_vim.delete_shared_volumes.side_effect = VimConnException(
+                "Connection failed."
+            )
+            result = instance.delete(ro_task, task_index)
+            self.assertEqual(result[0], "FAILED")
+            self.assertEqual(
+                result[1].get("vim_message"), "Error while deleting: Connection failed."
+            )
+            self.assertEqual(result[1].get("vim_status"), "VIM_ERROR")
+
+
 class TestVimInteractionAffinityGroup(unittest.TestCase):
     def setUp(self):
         module_name = "osm_ro_plugin"
diff --git a/NG-RO/osm_ng_ro/validation.py b/NG-RO/osm_ng_ro/validation.py
index 2601e90..e4eed74 100644
--- a/NG-RO/osm_ng_ro/validation.py
+++ b/NG-RO/osm_ng_ro/validation.py
@@ -101,6 +101,7 @@
         },
         "image": deploy_item_list,
         "flavor": deploy_item_list,
+        "shared-volumes": deploy_item_list,
         "ns": {
             "type": "object",
             "properties": {