diff --git a/RC_MIGRATE.md b/RC_MIGRATE.md
new file mode 100644
index 0000000000000000000000000000000000000000..8d7d505e1de99bae9261059d732ffa37b32caf2b
--- /dev/null
+++ b/RC_MIGRATE.md
@@ -0,0 +1,63 @@
+# How to use Remote Copy to migrate between 3PARs
+
+## Move VM with persistent disk from 3PAR A to B
+
+We need two Image datastores, each pointing to different 3PAR:
+
+- Image_DS_A - 3PAR A
+- Image_DS_B - 3PAR B
+
+For migration we need three System datastores:
+
+- System_DS_A - 3PAR A
+- System_DS_RC_AB - 3PAR A and SEC_config pointed to 3PAR B
+- System_DS_B - 3PAR B
+
+### Process of migration
+
+In susntone select Migrate (without any label) and modal opens. In the modal select same host and in advanced select 
+System_DS_RC_AB. VM will suspend for while, RC Group will be created and VM will be unsuspended. Now we have VM in HA
+mode, because it is running with Peer Persistance and it has two groups of paths in multipath.
+
+We can check RCG state in SSMC, and when volume(s) is fully synchronized we can proceed to next step.
+
+**!!! IMPORTANT !!! Volume have to be fully synchronized.**
+
+In sustone select again same dialog, select same host but for datastore select System_DS_B. VM goes to suspend state, 
+RC Group will be removed and VM become unsuspended, but now running from 3PAR B.
+
+### Post tasks
+
+*TODO: This updates can be implemented into driver - tm/mv action.*
+
+OpenNebula doesn't known about this migration, so we have to edit at least three DB entities using onedb command:
+
+Update body of image(s) and disk(s) in VM, change image datastore ID and Name to Image_DS_B
+
+```
+onedb change-body image --id IMAGE_ID /IMAGE/DATASTORE_ID DS_ID_B
+onedb change-body image --id IMAGE_ID /IMAGE/DATASTORE DS_NAME_B
+onedb change-body vm --id VM_ID /VM/TEMPLATE/DISK/DATASTORE_ID DS_ID_B
+onedb change-body vm --id VM_ID /VM/TEMPLATE/DISK/DATASTORE DS_NAME_B
+```
+
+Update list of images in both image datastores, remove image ID from Image_DS_A and add it to Image_DS_B
+```
+onedb change-body datastore --id DS_ID_A /DATASTORE/IMAGES/ID[.=IMAGE_ID] --delete
+# attention! following command only work properly with PR https://github.com/OpenNebula/one/pull/6170
+# do not run without this patch, otherwise it overwrites all other image IDs
+onedb change-body datastore --id DS_ID_B /DATASTORE/IMAGES/ID IMAGE_ID --append
+```
+
+## Move VM with non-persistent disk from 3PAR A to B
+
+This is pretty similar to persistent version, but we skip step with migration to System_DS_RC_AB. This driver doesn't
+support RC for non-persistent images, at least for now. We migrate VM directly to System_DS_B. Downtime will be longer,
+because disk is copied by using DD utility during VM suspension.
+
+### Post tasks
+
+Image_DS_A can have RC enabled, which on background synchronizes all non-persistent (base) images by using RCG. After
+all VMs using same particular disk are migrated, them we can update-body of each to point it to Image_DS_B,
+update-body of image itself and update-body for both image datastore's to finalize move. We have to remove the image
+from RCG in SSMC manually.
\ No newline at end of file
diff --git a/README.md b/README.md
index 5387581425e084cdaa325ca58f7099764f333b2d..08ffd64bf54204a9e448ad29a61c57cd8ad69038 100644
--- a/README.md
+++ b/README.md
@@ -23,9 +23,12 @@ More info:
 ## Compatibility
 
 This add-on is developed and tested with:
-- OpenNebula 5.6 and 3PAR OS 3.2.2.612 (MU4)+P51,P56,P59,P94,P98,P102,P106,P113,P118,P127  
-- OpenNebula 5.8 and 3PAR OS 3.3.1.410 (MU2)+P32,P34,P36,P37,P39,P40,P41,P42,P45,P48
+- OpenNebula 6.2 and 3PAR OS 3.3.1.648 (MU5)+P125,P126,P132,P135,P140,P146,P150,P151,P155,P156,P164,P170,P173
+- OpenNebula 5.10 and 3PAR OS 3.3.1.648 (MU5)+P125,P126,P132,P135,P140,P146,P150,P151
 - OpenNebula 5.8 and 3PAR OS 3.3.1.460 (MU3)+P50,P58,P61,P77,P78,P81
+- OpenNebula 5.8 and 3PAR OS 3.3.1.410 (MU2)+P32,P34,P36,P37,P39,P40,P41,P42,P45,P48
+- OpenNebula 5.6 and 3PAR OS 3.2.2.612 (MU4)+P51,P56,P59,P94,P98,P102,P106,P113,P118,P127
+
 
 ## Requirements
 
@@ -35,10 +38,9 @@ This add-on is developed and tested with:
 * Password-less SSH access from the front-end `oneadmin` user to the `node` instances.
 * 3PAR python package `python-3parclient` installed, WSAPI username, password and access to the 3PAR API network
 * libvirt-client package installed
-* xmlstarlet package installed - used in TM monitor script instead of OpenNebula native ruby script because it is slow
 
 ```bash
-yum install python-setuptools libvirt-client xmlstarlet
+yum install python-setuptools libvirt-client
 easy_install pip
 pip install python-3parclient
 ```
@@ -51,11 +53,16 @@ pip install python-3parclient
 * sg3_utils package installed
 * `/etc/multipath.conf` need to have set `user_friendly_names no`, because we use WWNs instead of `mpathx` aliasses
 * `/etc/sudoers.d/opennebula` - add `ONE_3PAR` cmd alias
+* `/etc/sudoers.d/opennebula-node-kvm` - add `ONE_3PAR` alias to the list
 
 ```
 nano /etc/sudoers.d/opennebula
 ...
-Cmnd_Alias ONE_3PAR = /sbin/multipath, /usr/sbin/multipathd, /sbin/dmsetup, /usr/sbin/blockdev, /usr/bin/tee /sys/block/*/device/delete, /usr/bin/rescan-scsi-bus.sh
+Cmnd_Alias ONE_3PAR = /sbin/multipath, /usr/sbin/multipathd, /sbin/dmsetup, /usr/sbin/blockdev, /usr/bin/tee /sys/block/*/device/delete, /usr/bin/rescan-scsi-bus.sh, /sbin/mkswap, /usr/sbin/mkfs
+...
+
+nano /etc/sudoers.d/opennebula-node-kmv
+...
 oneadmin ALL=(ALL) NOPASSWD: ONE_MISC, ..., ONE_3PAR, ...
 ...
 ```
@@ -72,23 +79,28 @@ Support standard OpenNebula datastore operations:
 * SYSTEM datastore
 * TRIM/discard in the VM when virtio-scsi driver is in use (require `DEV_PREFIX=sd` and `DISCARD=unmap`)
 * disk images can be full provisioned, thin provisioned, thin deduplicated, thin compressed or thin deduplicated and compressed RAW block devices
-* support different 3PAR CPGs as separate datastores
+* support different 3PAR CPGs as separate datastore
 * support for 3PAR Priority Optimization Policy (QoS)
 * live VM snapshots
 * live VM migrations
-* Volatile disks support (need patched KVM driver `attach_disk` script)
+* volatile disks support (need patched KVM driver `attach_disk` script)
+* support multiple storage systems
+* support Remote Copy with Peer Persistence
+* support Save As between storage systems
+* support migrations of VMs between storage systems
+* ds/clone operation support cloning image between storage systems
 * Sunstone integration - available via our enterprise repository
 
 ## Limitations
 
 1. Tested only with KVM hypervisor
 1. When SYSTEM datastore is in use the reported free/used/total space is the space on 3PAR CPG. (On the host filesystem there are mostly symlinks and small files that do not require much disk space)
-1. Tested/confirmed working on CentOS 7 (Frontend) and Oracle Linux 7, Oracle Linux 8, CentOS 7, CentOS 8, Fedora 29+ (Nodes).
+1. Tested/confirmed working on CentOS 7 and Oracle Linux 7 (Frontend), and Oracle Linux 7, Oracle Linux 8, CentOS 7, CentOS 8, Fedora 29+ (Nodes).
 
 ## ToDo
 
 1. QOS Priority per VM
-1. Configuration of API endpoint and auth in datastore template
+1. Configuration of API auth in datastore template
 
 ## Installation
 
@@ -172,6 +184,14 @@ DS_MAD_CONF = [
 ]
 ```
 
+* Edit `/etc/one/oned.conf` and update VM_MAD arguments for 3par
+
+```
+VM_MAD = [
+      ARGUMENTS = "-t 15 -r 0 kvm -l snapshotcreate=snapshot_create-3par,snapshotdelete=snapshot_delete-3par,snapshotrevert=snapshot_revert-3par",
+      ...
+```
+
 * Enable live disk snapshots support for 3PAR by adding `kvm-3par` to `LIVE_DISK_SNAPSHOTS` variable in `/etc/one/vmm_exec/vmm_execrc`
 ```
 LIVE_DISK_SNAPSHOTS="kvm-qcow2 kvm-ceph kvm-3par"
@@ -210,12 +230,18 @@ Some configuration attributes must be set to enable a datastore as 3PAR enabled
 * **DS_MAD**: [mandatory] The DS driver for the datastore. String, use value `3par`
 * **TM_MAD**: [mandatory] Transfer driver for the datastore. String, use value `3par`
 * **DISK_TYPE**: [mandatory for IMAGE datastores] Type for the VM disks using images from this datastore. String, use value `block`
+* **API_ENDPOINT**: 3PAR WSAPI Endpoint. String
+* **IP**: 3PAR IP address for SSH authentication options for the SSH based calls. String
 * **CPG**: [mandatory] Name of Common Provisioning Group created on 3PAR. String
 * **THIN**: Use thin volumes `tpvv` or no. By default enabled. `YES|NO`
 * **DEDUP**: Use deduplicated thin volumes `tdvv` or no. By default disabled. `YES|NO`
 * **COMPRESSION**: Use compressed thin volumes or no. By default disabled. `YES|NO`
 * **NAMING_TYPE**: Part of volume name defining environment. By default `dev`. String (1)
-* **BRIDGE_LIST**: Nodes to use for image datastore operations. String (2)
+* **BRIDGE_LIST**: [mandatory for IMAGE datastores] Nodes to use for image datastore operations. String (2)
+* **REMOTE_COPY**: Enable Remote Copy. `YES|NO`
+* **SEC_API_ENDPOINT**: [mandatory when Remote Copy] Secondary 3PAR WSAPI Endpoint. String
+* **SEC_IP**: [mandatory when Remote Copy] Secondary 3PAR IP address. String
+* **SEC_CPG**: [mandatory when Remote Copy] Name of Common Provisioning Group on Secondary 3PAR. String
 * **QOS_ENABLE**: Enable QoS. `YES|NO` (3)
 * **QOS_PRIORITY**: QoS Priority. `HIGH|NORMAL|LOW` (4)
 * **QOS_MAX\_IOPS**: QoS Max IOPS. Int (5)
@@ -326,4 +352,4 @@ $ onedatastore list
 
 ## 3PAR best practices guide incl. naming conventions
 
-Please follow the [best practices guide](https://h20195.www2.hpe.com/v2/GetPDF.aspx/4AA4-4524ENW.pdf).
+Please follow the [best practices guide](https://support.hpe.com/hpesc/public/docDisplay?docLocale=en_US&docId=a00116000en_us).
diff --git a/datastore/3par/3par.py b/datastore/3par/3par.py
old mode 100644
new mode 100755
index b4948b5058254756bc530b929f9ccbe495a75bea..e12c52111d7b87643f09a2fd0e5c1b5ed9ec4fca
--- a/datastore/3par/3par.py
+++ b/datastore/3par/3par.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                    #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may    #
 # not use this file except in compliance with the License. You may obtain    #
@@ -34,13 +34,16 @@ def boolarg(string):
 # Common Parser
 commonParser = argparse.ArgumentParser(add_help=False)
 commonParser.add_argument('-a', '--api', help='WSAPI Endpoint', required=True)
+commonParser.add_argument('-sapi', '--sapi', help='Secondary WSAPI Endpoint', required=False)
 commonParser.add_argument('-s', '--secure',
-                          help='WSAPI SSL certification verification is disabled. In order to override this,'
+                          help='WSAPI SSL certification verification is disabled. In order to override this, '
                                'set this to 1 or to /path/to/cert.crt',
                           type=boolarg,
                           default=False)
 commonParser.add_argument('-i', '--ip', help='3PAR IP for SSH authentication options for the SSH based calls',
                           required=True)
+commonParser.add_argument('-sip', '--sip', help='Secondary 3PAR IP for SSH authentication options for the SSH based calls',
+                          required=False)
 commonParser.add_argument('-u', '--username', help='3PAR username', required=True)
 commonParser.add_argument('-p', '--password', help='3PAR password', required=True)
 commonParser.add_argument('-sd', '--softDelete', help='Soft-delete volumes/snapshots', type=boolarg, default=True)
@@ -48,9 +51,17 @@ commonParser.add_argument('-sd', '--softDelete', help='Soft-delete volumes/snaps
 # MonitorCPG task parser
 monitorCPGParser = subparsers.add_parser('monitorCPG', parents=[commonParser], help='Get CPG Available Space')
 monitorCPGParser.add_argument('-c', '--cpg', help='CPG Name', required=True)
-monitorCPGParser.add_argument('-d', '--disks', help='Return disks info', type=boolarg, default=False)
-monitorCPGParser.add_argument('-di', '--datastoreId', help='DS ID', type=int)
-monitorCPGParser.add_argument('-nt', '--namingType', help='Best practices Naming conventions <TYPE> part', default='dev')
+
+# MonitorVmDisks task parser
+monitorVmDisksParser = subparsers.add_parser('monitorVmDisks', parents=[commonParser], help='Get VM disk stats')
+monitorVmDisksParser.add_argument('-di', '--datastoreId', help='DS ID', type=int)
+monitorVmDisksParser.add_argument('-nt', '--namingType', help='Best practices Naming conventions <TYPE> part', default='dev')
+monitorVmDisksParser.add_argument('-lf', '--legacyFormat', help='Legacy format to support OpenNebula <5.12', type=boolarg, default=False)
+
+# EnableRCOnImageDS task parser
+enableRCOnImageDSParser = subparsers.add_parser('enableRCOnImageDS', parents=[commonParser], help='Enable RC on image DS. Create RC group and add to it all non-persistent images.')
+enableRCOnImageDSParser.add_argument('-di', '--datastoreId', help='DS ID', type=int, required=True)
+enableRCOnImageDSParser.add_argument('-rcm', '--remoteCopyMode', help='Remote Copy mode', choices=['SYNC', 'PERIODIC', 'ASYNC'], required=True)
 
 # CreateVV task parser
 createVVParser = subparsers.add_parser('createVV', parents=[commonParser], help='Create new VV')
@@ -67,6 +78,7 @@ createVVParser.add_argument('-co', '--comment', help='Comment')
 deleteVVParser = subparsers.add_parser('deleteVV', parents=[commonParser], help='Delete VV')
 deleteVVParser.add_argument('-nt', '--namingType', help='Best practices Naming conventions <TYPE> part', default='dev')
 deleteVVParser.add_argument('-id', '--id', help='ID of VV to use in VV name', required=True)
+deleteVVParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # CloneVV task parser
 cloneVVParser = subparsers.add_parser('cloneVV', parents=[commonParser], help='Clone specific VV to new one')
@@ -89,6 +101,7 @@ copyVVParser = subparsers.add_parser('copyVV', parents=[commonParser], help='Cop
 copyVVParser.add_argument('-nt', '--namingType', help='Source: Best practices Naming conventions <TYPE> part',
                           default='dev')
 copyVVParser.add_argument('-id', '--id', help='ID of source VV or VM disk', required=True)
+copyVVParser.add_argument('-si', '--snapId', help='ID of snapshot', required=True)
 copyVVParser.add_argument('-d', '--destName', help='Name of the destination VV', required=True)
 copyVVParser.add_argument('-vi', '--vmId', help='Id of source VV VM')
 copyVVParser.add_argument('-vc', '--vmClone', help='Is VM clone?', type=boolarg, default=False)
@@ -98,6 +111,9 @@ copyVVParser.add_argument('-c', '--cpg', help='Destination VV CPG Name', require
 growVVParser = subparsers.add_parser('growVV', parents=[commonParser], help='Grow VV by specific size')
 growVVParser.add_argument('-n', '--name', help='Name of VV to grow', required=True)
 growVVParser.add_argument('-gb', '--growBy', help='Grow by in MiB', type=int, required=True)
+growVVParser.add_argument('-vi', '--vmId', help='Id of VM')
+growVVParser.add_argument('-nt', '--namingType', help='Best practices Naming conventions <TYPE> part', default='dev')
+growVVParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # getVVSize task parser
 getVVSizeParser = subparsers.add_parser('getVVSize', parents=[commonParser], help='Get size of VV')
@@ -109,11 +125,13 @@ getVVSizeParser.add_argument('-t', '--type', help='Type of size to get', choices
 exportVVParser = subparsers.add_parser('exportVV', parents=[commonParser], help='Export VV to host')
 exportVVParser.add_argument('-n', '--name', help='Name of VV to export', required=True)
 exportVVParser.add_argument('-hs', '--host', help='Name of host to export to', required=True)
+exportVVParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # UnexportVV task parser
 unexportVVParser = subparsers.add_parser('unexportVV', parents=[commonParser], help='Unexport VV from host')
 unexportVVParser.add_argument('-n', '--name', help='Name of VV to unexport', required=True)
 unexportVVParser.add_argument('-hs', '--host', help='Name of host to unexport from', required=True)
+unexportVVParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # CreateVmClone task parser
 createVmCloneParser = subparsers.add_parser('createVmClone', parents=[commonParser],
@@ -129,6 +147,7 @@ createVmCloneParser.add_argument('-tpvv', '--tpvv', help='Thin provision', type=
 createVmCloneParser.add_argument('-tdvv', '--tdvv', help='Thin provision with deduplication', type=boolarg, default=False)
 createVmCloneParser.add_argument('-compr', '--compression', help='Thin provision compressed volume', type=boolarg, default=False)
 createVmCloneParser.add_argument('-co', '--comment', help='Comment')
+createVmCloneParser.add_argument('-e', '--empty', help='Just create empty volume - not copy from src', type=boolarg, default=False)
 
 # CreateVmVV task parser
 createVmVVParser = subparsers.add_parser('createVmVV', parents=[commonParser], help='Create new VM VV')
@@ -162,6 +181,7 @@ createVVSetSnapshotParser.add_argument('-nt', '--namingType', help='Source: Best
                                   default='dev')
 createVVSetSnapshotParser.add_argument('-vi', '--vmId', help='Id of VM')
 createVVSetSnapshotParser.add_argument('-si', '--snapId', help='ID of snapshot', required=True)
+createVVSetSnapshotParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # DeleteVVSetSnapshot task parser
 deleteVVSetSnapshotParser = subparsers.add_parser('deleteVVSetSnapshot', parents=[commonParser], help='Delete volume set snapshot')
@@ -169,6 +189,7 @@ deleteVVSetSnapshotParser.add_argument('-nt', '--namingType', help='Source: Best
                                   default='dev')
 deleteVVSetSnapshotParser.add_argument('-vi', '--vmId', help='Id of VM')
 deleteVVSetSnapshotParser.add_argument('-si', '--snapId', help='ID of snapshot', required=True)
+deleteVVSetSnapshotParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # CreateSnapshot task parser
 createSnapshotParser = subparsers.add_parser('createSnapshot', parents=[commonParser], help='Create snapshot of VV')
@@ -178,6 +199,7 @@ createSnapshotParser.add_argument('-id', '--id', help='ID of source VV or VM dis
 createSnapshotParser.add_argument('-vi', '--vmId', help='Id of VM')
 createSnapshotParser.add_argument('-vc', '--vmClone', help='Is VM clone VV?', type=boolarg, default=False)
 createSnapshotParser.add_argument('-si', '--snapId', help='ID of snapshot', required=True)
+createSnapshotParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # RevertSnapshot task parser
 revertSnapshotParser = subparsers.add_parser('revertSnapshot', parents=[commonParser],
@@ -190,6 +212,9 @@ revertSnapshotParser.add_argument('-vc', '--vmClone', help='Is VM clone VV?', ty
 revertSnapshotParser.add_argument('-si', '--snapId', help='ID of snapshot', required=True)
 revertSnapshotParser.add_argument('-o', '--online', help='Revert snapshot while VV is online (exported)', type=boolarg,
                                   default=False)
+revertSnapshotParser.add_argument('-off', '--offline', help='Revert snapshot while VV is not attached to VM', type=boolarg,
+                                  default=False)
+revertSnapshotParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # DeleteSnapshot task parser
 deleteSnapshotParser = subparsers.add_parser('deleteSnapshot', parents=[commonParser], help='Delete snapshot of VV')
@@ -199,12 +224,14 @@ deleteSnapshotParser.add_argument('-id', '--id', help='ID of source VV or VM dis
 deleteSnapshotParser.add_argument('-vi', '--vmId', help='Id of VM')
 deleteSnapshotParser.add_argument('-vc', '--vmClone', help='Is VM clone VV?', type=boolarg, default=False)
 deleteSnapshotParser.add_argument('-si', '--snapId', help='ID of snapshot', required=True)
+deleteSnapshotParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # FlattenSnapshot task parser
 flattenSnapshotParser = subparsers.add_parser('flattenSnapshot', parents=[commonParser],
                                               help='Promote selected snapshot and delete all snapshots of source VV')
 flattenSnapshotParser.add_argument('-sn', '--srcName', help='Name of source VV to which snapshot belongs', required=True)
 flattenSnapshotParser.add_argument('-si', '--snapId', help='ID of snapshot', required=True)
+flattenSnapshotParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
 # HostExists task parser
 hostExistsParser = subparsers.add_parser('hostExists', parents=[commonParser],
@@ -234,7 +261,6 @@ createQosPolicyParser = subparsers.add_parser('createQosPolicy', parents=[common
 createQosPolicyParser.add_argument('-nt', '--namingType', help='Best practices Naming conventions <TYPE> part',
                                    default='dev')
 createQosPolicyParser.add_argument('-vi', '--vmId', help='Id of VM', required=True)
-createQosPolicyParser.add_argument('-n', '--name', help='Name of VV', required=True)
 createQosPolicyParser.add_argument('-qp', '--qosPriority', help='QoS Priority', choices=['LOW', 'NORMAL', 'HIGH'],
                                         required=True)
 createQosPolicyParser.add_argument('-qxi', '--qosMaxIops', help='QoS Max IOPS', type=int, required=True)
@@ -242,14 +268,36 @@ createQosPolicyParser.add_argument('-qmi', '--qosMinIops', help='QoS Min IOPS',
 createQosPolicyParser.add_argument('-qxb', '--qosMaxBw', help='QoS Max BW in kB/s', type=int, required=True)
 createQosPolicyParser.add_argument('-qmb', '--qosMinBw', help='QoS Min BW in kB/s', type=int, required=True)
 createQosPolicyParser.add_argument('-ql', '--qosLatency', help='QoS Latency in ms', type=int, required=True)
+createQosPolicyParser.add_argument('-rc', '--remoteCopy', help='Enable Remote Copy', type=boolarg, default=False)
 
-# DeleteQosPolicy task parser
-deleteQosPolicyParser = subparsers.add_parser('deleteQosPolicy', parents=[commonParser],
-                                              help='Delete QoS policy from VM VV set')
-deleteQosPolicyParser.add_argument('-nt', '--namingType', help='Best practices Naming conventions <TYPE> part',
+# DisableQosPolicyParser task parser
+disableQosPolicyParser = subparsers.add_parser('disableQosPolicy', parents=[commonParser],
+                                              help='Disable QoS policy on VM VV set')
+disableQosPolicyParser.add_argument('-nt', '--namingType', help='Best practices Naming conventions <TYPE> part',
                                    default='dev')
-deleteQosPolicyParser.add_argument('-vi', '--vmId', help='Id of VM', required=True)
-deleteQosPolicyParser.add_argument('-n', '--name', help='Name of VV', required=True)
+disableQosPolicyParser.add_argument('-vi', '--vmId', help='Id of VM', required=True)
+
+# addVolumeToRCGroup task parser
+addVolumeToRCGroupParser = subparsers.add_parser('addVolumeToRCGroup', parents=[commonParser],
+                                              help='Add volume to Remote Copy group. If RC group does not exists, it creates new one')
+addVolumeToRCGroupParser.add_argument('-nt', '--namingType', help='Best practices Naming conventions <TYPE> part',
+                                   default='dev')
+addVolumeToRCGroupParser.add_argument('-vi', '--vmId', help='Id of VM')
+addVolumeToRCGroupParser.add_argument('-n', '--name', help='Name of VV', required=True)
+addVolumeToRCGroupParser.add_argument('-rcgn', '--remoteCopyGroupName', help='Name of RC Group', default=None)
+addVolumeToRCGroupParser.add_argument('-rcm', '--remoteCopyMode', help='Remote Copy mode', choices=['SYNC', 'PERIODIC', 'ASYNC'], required=True)
+addVolumeToRCGroupParser.add_argument('-rcha', '--remoteCopyHA', help='Enable High-Availability mode', type=boolarg, default=True)
+addVolumeToRCGroupParser.add_argument('-c', '--cpg', help='Local CPG Name', required=True)
+addVolumeToRCGroupParser.add_argument('-sc', '--secCpg', help='Remote CPG Name', required=True)
+
+# DeleteVolumeFromRCGroup task parser
+deleteVolumeFromRCGroupParser = subparsers.add_parser('deleteVolumeFromRCGroup', parents=[commonParser],
+                                        help='Delete volume from Remote Copy group. If it is last member, it stop RC group')
+deleteVolumeFromRCGroupParser.add_argument('-nt', '--namingType', help='Best practices Naming conventions <TYPE> part',
+                                   default='dev')
+deleteVolumeFromRCGroupParser.add_argument('-vi', '--vmId', help='Id of VM')
+deleteVolumeFromRCGroupParser.add_argument('-n', '--name', help='Name of VV', required=True)
+deleteVolumeFromRCGroupParser.add_argument('-rcgn', '--remoteCopyGroupName', help='Name of RC Group', default=None)
 
 # ------------
 # Define tasks
@@ -263,54 +311,113 @@ def monitorCPG(cl, args):
     free = cpgAvailableSpace.get('usableFreeMiB')
     total = used + free
 
-    print 'USED_MB={used}'.format(used=used)
-    print 'TOTAL_MB={total}'.format(total=total)
-    print 'FREE_MB={free}'.format(free=free)
-
-    if args.disks == True:
-      import subprocess
-      import xmltodict
-      
-      vvs = cl.getVolumes()
-      diskSizes = {}
-
-      for vv in vvs.get('members'):
-        diskSizes[vv.get('name')] = vv.get('userSpace').get('usedMiB')
-      
-      vmsXml = subprocess.check_output('onevm list --extended -x', shell=True)
-      vms = xmltodict.parse(vmsXml)
-      for vm in vms.get('VM_POOL')['VM']:
-        if args.datastoreId != int(vm.get('HISTORY_RECORDS')['HISTORY'].get('DS_ID')):
-          continue
-        
+    print('USED_MB={used}'.format(used=used))
+    print('TOTAL_MB={total}'.format(total=total))
+    print('FREE_MB={free}'.format(free=free))
+
+
+def monitorVmDisks(cl, args):
+    import subprocess
+    import xmltodict
+    from base64 import b64encode
+
+    vvs = cl.getVolumes()
+    diskSizes = {}
+
+    for vv in vvs.get('members'):
+      diskSizes[vv.get('name')] = vv.get('userSpace').get('usedMiB')
+
+    vmsXml = subprocess.check_output('onevm list --extended -x', shell=True)
+    vms = xmltodict.parse(vmsXml, force_list=('VM',))
+    if vms['VM_POOL'] is None:
+      return
+    for vm in vms.get('VM_POOL')['VM']:
+      if vm.get('HISTORY_RECORDS') is None or args.datastoreId != int(vm.get('HISTORY_RECORDS')['HISTORY'].get('DS_ID')):
+        continue
+
+      if args.legacyFormat:
         result = 'VM=[ID={vmId},POLL="'.format(vmId=vm.get('ID'))
-        
-        disks = vm.get('TEMPLATE').get('DISK')
-        if isinstance(disks,dict):
-          disks = [disks]
-
-        diskResult = []
-        for disk in disks:
-          if disk.get('CLONE') == 'YES' or disk.get('SOURCE') is None or disk.get('SOURCE') == '':
-            name = '{namingType}.one.vm.{vmId}.{diskId}.vv'.format(namingType=args.namingType, vmId=vm.get('ID'), diskId=disk.get('DISK_ID'))
-          else:
-            source = disk.get('SOURCE').split(':')
-            name = source[0]
+      else:
+        result = 'VM=[ID={vmId},MONITOR="'.format(vmId=vm.get('ID'))
+
+      disks = vm.get('TEMPLATE').get('DISK')
+      if isinstance(disks,dict):
+        disks = [disks]
+
+      diskResult = []
+      for disk in disks:
+        if disk.get('CLONE') == 'YES' or disk.get('SOURCE') is None or disk.get('SOURCE') == '':
+          name = '{namingType}.one.vm.{vmId}.{diskId}.vv'.format(namingType=args.namingType, vmId=vm.get('ID'), diskId=disk.get('DISK_ID'))
+        else:
+          source = disk.get('SOURCE').split(':')
+          name = source[0]
+
+        if name in diskSizes:
           diskResult.append('DISK_SIZE=[ID={diskId},SIZE={diskSize}]'.format(diskId=disk.get('DISK_ID'), diskSize=diskSizes[name]))
-       
-        print result + ' '.join(diskResult) + '"]'
+
+      if args.legacyFormat:
+          print(result + ' '.join(diskResult) + '"]')
+      else:
+          print(result + b64encode(' '.join(diskResult).encode('ascii')).decode('ascii') + '"]')
+
+
+def enableRCOnImageDS(cl, args):
+    import subprocess
+    import xmltodict
+
+    # fetch image pool
+    imagesXml = subprocess.check_output('oneimage list -x'.format(id=args.datastoreId), shell=True)
+    images = xmltodict.parse(imagesXml, force_list=('IMAGE',))
+
+    if images['IMAGE_POOL'] is None:
+        return
+
+    # fetch ds info
+    dsXml = subprocess.check_output('onedatastore show {id} -x'.format(id=args.datastoreId), shell=True)
+    ds = xmltodict.parse(dsXml)
+    dsTemplate = ds.get('DATASTORE').get('TEMPLATE')
+
+    # check if RC is enabled
+    if dsTemplate.get('REMOTE_COPY') != 'YES':
+        print('Remote Copy is not enabled for this datastore')
+        exit(1)
+
+    # prepare RC params
+    args.remoteCopyGroupName = '{naming_type}.one.ds.{ds_id}'.format(naming_type=dsTemplate.get('NAMING_TYPE'), ds_id=args.datastoreId)
+    args.remoteCopyHA = False
+    args.cpg = dsTemplate.get('CPG')
+    args.secCpg = dsTemplate.get('SEC_CPG')
+
+    # iterate over images
+    for image in images.get('IMAGE_POOL')['IMAGE']:
+        # filter out images by datastore ID and we copy only non-persistent
+        if args.datastoreId != int(image.get('DATASTORE_ID')) or int(image.get('PERSISTENT')) == 1:
+            continue
+
+        # asign image name and add to remote copy group
+        source = image.get('SOURCE').split(':')
+        args.name = source[0]
+        print('Adding image {name} ({vv}) to RCG {rcgName}'.format(name=image.get('NAME'), vv=args.name,
+                                                                   rcgName=args.remoteCopyGroupName))
+        addVolumeToRCGroup(cl, args)
+
 
 def createVV(cl, args):
     name = createVVName(args.namingType, args.id)
 
     vv = createVVWithName(cl, name, args)
     wwn = vv.get('wwn').lower()
-    print '{name}:{wwn}'.format(name=name, wwn=wwn)
+    print('{name}:{wwn}'.format(name=name, wwn=wwn))
+
 
 def deleteVV(cl, args):
-    name = createVVName(args.namingType, args.id)
+    vvName = createVVName(args.namingType, args.id)
+    deleteVVWithName(cl, vvName)
 
-    deleteVVWithName(cl, name)
+    if args.remoteCopy:
+        vvName = '{name}'.format(name=vvName)
+        cl = getRemoteSystemClient(args)
+        deleteVVWithName(cl, vvName)
 
 def cloneVV(cl, args):
     srcName = createVVName(args.srcNamingType, args.srcId)
@@ -324,33 +431,57 @@ def cloneVV(cl, args):
     cl.copyVolume(srcName, destName, args.cpg, optional)
 
     wwn = vv.get('wwn').lower()
-    print '{name}:{wwn}'.format(name=destName, wwn=wwn)
+    print('{name}:{wwn}'.format(name=destName, wwn=wwn))
+
 
 def copyVV(cl, args):
+  snapId = args.snapId
+
   if args.vmClone == True:
     srcName = createVmCloneName(args.namingType, args.id, args.vmId)
   else:
     srcName = createVVName(args.namingType, args.id)
 
-  optional = {'skipZero': True}
+  if snapId != "-1":
+      srcName = createSnapshotName(srcName, snapId)
+
+  optional = {'priority': 1, 'skipZero': True}
 
   cl.copyVolume(srcName, args.destName, args.cpg, optional)
 
 def growVV(cl, args):
-    cl.growVolume(args.name, args.growBy)
+    rcgName = '{namingType}.one.vm.{vmId}'.format(namingType=args.namingType, vmId=args.vmId)
+
+    if args.remoteCopy:
+        cl.stopRemoteCopy(rcgName)
+
+    try:
+        cl.growVolume(args.name, args.growBy)
+    except exceptions.ClientException:
+        if args.remoteCopy:
+            cl.startRemoteCopy(rcgName)
+
+    if args.remoteCopy:
+        cl.startRemoteCopy(rcgName)
 
 def getVVSize(cl, args):
     vv = cl.getVolume(args.name)
 
     if args.type == 'USED':
-        print vv.get('userSpace').get('usedMiB')
+        print(vv.get('userSpace').get('usedMiB'))
     elif args.type == 'SNAP':
-        print vv.get('snapshotSpace').get('usedMiB')
+        print(vv.get('snapshotSpace').get('usedMiB'))
     elif args.type == 'VSIZE':
-        print vv.get('sizeMiB')
+        print(vv.get('sizeMiB'))
+
 
 def exportVV(cl, args):
-    name = args.name
+    if args.remoteCopy:
+        name = '{name}'.format(name=args.name)
+        cl = getRemoteSystemClient(args)
+    else:
+        name = args.name
+
     host = args.host
 
     # check if VLUN already exists
@@ -358,7 +489,7 @@ def exportVV(cl, args):
         vluns = cl.getHostVLUNs(host)
         for vlun in vluns:
             if vlun.get('volumeName') == name:
-                print vlun.get('lun')
+                print(vlun.get('lun'))
                 return
     except exceptions.HTTPNotFound:
         pass
@@ -368,33 +499,54 @@ def exportVV(cl, args):
     while not done:
         try:
             location = cl.createVLUN(name, None, host, None, None, None, True)
-            print location.split(',')[1]
+            print (location.split(',')[1])
             return
         except exceptions.HTTPConflict:
             time.sleep(5)
 
 def unexportVV(cl, args):
-    name = args.name
+    if args.remoteCopy:
+        name = '{name}'.format(name=args.name)
+        cl = getRemoteSystemClient(args)
+    else:
+        name = args.name
+
     host = args.host
 
     # check if VLUN exists
     found = False
-    vluns = cl.getHostVLUNs(host)
-    for vlun in vluns:
-        if vlun.get('volumeName') == name:
-            found = True
-            break
+    try:
+        vluns = cl.getHostVLUNs(host)
+        for vlun in vluns:
+            if vlun.get('volumeName') == name:
+                found = True
+                break
+    except exceptions.HTTPNotFound:
+        pass
 
-    if found == False:
-        return
+    if found:
+        cl.deleteVLUN(name, vlun.get('lun'), host)
 
-    cl.deleteVLUN(name, vlun.get('lun'), host)
 
 def createVmClone(cl, args):
     destName = createVmCloneName(args.namingType, args.id, args.vmId)
 
-    # create new VV
-    vv = createVVWithName(cl, destName, args)
+    # check if destination VV exist
+    try:
+        vv = cl.getVolume(destName)
+        if 'expirationTimeSec' in vv:
+            cl.modifyVolume(destName, {'rmExpTime': True})
+        else:
+            print("Destination volume already exists and it is not mark as deleted!")
+            exit(1)
+    except exceptions.HTTPNotFound:
+        # create new VV
+        vv = createVVWithName(cl, destName, args)
+
+    if args.empty:
+        wwn = vv.get('wwn').lower()
+        print('{name}:{wwn}'.format(name=destName, wwn=wwn))
+        exit(0)
 
     # define optional for speed up process
     optional = {'priority': 1, 'skipZero': True}
@@ -411,14 +563,15 @@ def createVmClone(cl, args):
             if i > 5:
                 cl.deleteVolume(destName)
                 cl.logout()
-                print ex
+                print(ex)
                 exit(1)
             i += 1
             time.sleep(1)
 
     # print info
     wwn = vv.get('wwn').lower()
-    print '{name}:{wwn}'.format(name=destName, wwn=wwn)
+    print('{name}:{wwn}'.format(name=destName, wwn=wwn))
+
 
 def createVmVV(cl, args):
     name = createVmCloneName(args.namingType, args.id, args.vmId)
@@ -428,13 +581,15 @@ def createVmVV(cl, args):
 
     # print info
     wwn = vv.get('wwn').lower()
-    print '{name}:{wwn}'.format(name=name, wwn=wwn)
+    print('{name}:{wwn}'.format(name=name, wwn=wwn))
+
 
 def getVmClone(cl, args):
     name = createVmCloneName(args.namingType, args.id, args.vmId)
     vv = cl.getVolume(name)
     wwn = vv.get('wwn').lower()
-    print '{name}:{wwn}'.format(name=name, wwn=wwn)
+    print('{name}:{wwn}'.format(name=name, wwn=wwn))
+
 
 def deleteVmClone(cl, args):
     name = createVmCloneName(args.namingType, args.id, args.vmId)
@@ -444,25 +599,32 @@ def deleteVmClone(cl, args):
 
 def createVVSetSnapshot(cl, args):
     snapId = 's{snapId}'.format(snapId=args.snapId)
-    vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
+
+    if args.remoteCopy:
+        vvsetName = 'RCP_{namingType}.one.vm.{vmId}'.format(namingType=args.namingType, vmId=args.vmId)
+    else:
+        vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
+        # limit length of vvset name to 27 chars
+        # it is limit of 3par system
+        vvsetName = vvsetName[0:27]
 
     # get volume set info
     try:
         vvset = cl.getVolumeSet(vvsetName)
         members = vvset.get('setmembers')
     except exceptions.HTTPNotFound:
-        print 'Volume set does not exits, exiting...'
+        print('Volume set does not exits, exiting...')
         return
 
     # no members in volume set? unexpected
     if not members or not len(members) > 0:
-        print 'Volume set has no members, exiting...'
+        print('Volume set has no members, exiting...')
         return
 
     # check for soft deleted snapshot
     if args.softDelete:
         for member in members:
-            snapName, metaKey = createSnapshotNameAndMetaKey(member, snapId)
+            snapName = createSnapshotName(member, snapId)
             try:
                 cl.getVolume(snapName)
                 # snap exists, so delete it
@@ -470,62 +632,76 @@ def createVVSetSnapshot(cl, args):
             except exceptions.HTTPNotFound:
                 pass
 
+            if args.remoteCopy:
+                scl = getRemoteSystemClient(args)
+                try:
+                    scl.getVolume(snapName)
+                    # snap exists, so delete it
+                    scl.deleteVolume(snapName)
+                except exceptions.HTTPNotFound:
+                    pass
+
     # prepare snapshot vv name pattern
     name = '@vvname@.{snapId}'.format(snapId=snapId)
 
     # create snapshots
-    cl.createSnapshotOfVolumeSet(name, vvsetName, {'readOnly': True})
-
-    # create and add snapshot metadata to all members
-    for member in members:
-        snapName, metaKey = createSnapshotNameAndMetaKey(member, snapId)
-        cl.setVolumeMetaData(member, metaKey, snapName)
+    if args.remoteCopy:
+        rcgName = 'rcgroup:{namingType}.one.vm.{vmId}'.format(namingType=args.namingType, vmId=args.vmId)
+        cmd = ['createsv', '-rcopy', '-ro', name, rcgName]
+        cl._run(cmd)
+    else:
+        cl.createSnapshotOfVolumeSet(name, vvsetName, {'readOnly': True})
 
-    print args.snapId
+    print(args.snapId)
 
 
 def deleteVVSetSnapshot(cl, args):
     snapId = 's{snapId}'.format(snapId=args.snapId)
-    vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
+    scl = None
+
+    if args.remoteCopy:
+        scl = getRemoteSystemClient(args)
+        vvsetName = 'RCP_{namingType}.one.vm.{vmId}'.format(namingType=args.namingType, vmId=args.vmId)
+    else:
+        vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
+        # limit length of vvset name to 27 chars
+        # it is limit of 3par system
+        vvsetName = vvsetName[0:27]
 
     # get volume set info
     try:
         vvset = cl.getVolumeSet(vvsetName)
         members = vvset.get('setmembers')
     except exceptions.HTTPNotFound:
-        print 'Volume set does not exits, exiting...'
+        print('Volume set does not exits, exiting...')
         return
 
     # no members in volume set? unexpected
     if not members or not len(members) > 0:
-        print 'Volume set has no members, exiting...'
+        print('Volume set has no members, exiting...')
         return
 
     # iterate over volumes and find snapshots to delete
     for member in members:
-        name, metaKey = createSnapshotNameAndMetaKey(member, snapId)
+        name = createSnapshotName(member, snapId)
 
         if args.softDelete:
             cl.modifyVolume(name, {'expirationHours': 168})
+            args.remoteCopy and scl.modifyVolume(name, {'expirationHours': 168})
         else:
             cl.deleteVolume(name)
-
-        try:
-            cl.removeVolumeMetaData(member, metaKey)
-        except exceptions.HTTPNotFound:
-            pass
-
+            args.remoteCopy and scl.deleteVolume(name)
 
 
 def createSnapshot(cl, args):
     snapId = args.snapId
 
-    if args.vmClone == True:
+    if args.vmClone:
         srcName = createVmCloneName(args.namingType, args.id, args.vmId)
     else:
         srcName = createVVName(args.namingType, args.id)
 
-    name, metaKey = createSnapshotNameAndMetaKey(srcName, snapId)
+    name = createSnapshotName(srcName, snapId)
 
     # check for soft deleted snapshot
     if args.softDelete:
@@ -536,119 +712,194 @@ def createSnapshot(cl, args):
         except exceptions.HTTPNotFound:
             pass
 
-    cl.createSnapshot(name, srcName, {'readOnly': True})
-    cl.setVolumeMetaData(srcName, metaKey, name)
+        if args.remoteCopy:
+            scl = getRemoteSystemClient(args)
+            try:
+                scl.getVolume(name)
+                # snap exists, so delete it
+                scl.deleteVolume(name)
+            except exceptions.HTTPNotFound:
+                pass
+
+    optional = {'readOnly': True}
+
+    if args.remoteCopy:
+        optional['syncSnapRcopy'] = True
+
+    cl.createSnapshot(name, srcName, optional)
 
 
 def revertSnapshot(cl, args):
     snapId = args.snapId
+    rcgName = '{namingType}.one.vm.{vmId}'.format(namingType=args.namingType, vmId=args.vmId)
 
-    if args.vmClone == True:
+    if args.vmClone:
         srcName = createVmCloneName(args.namingType, args.id, args.vmId)
     else:
         srcName = createVVName(args.namingType, args.id)
 
-    name, metaKey = createSnapshotNameAndMetaKey(srcName, snapId)
+    name = createSnapshotName(srcName, snapId)
 
     optional = {'online': args.online}
 
-    cl.promoteVirtualCopy(name, optional)
+    if not args.offline and args.remoteCopy:
+        cl.stopRemoteCopy(rcgName)
+        optional['allowRemoteCopyParent'] = True
+
+    try:
+        cl.promoteVirtualCopy(name, optional)
+    except exceptions.ClientException as ex:
+        if not args.offline and args.remoteCopy:
+            cl.startRemoteCopy(rcgName)
+
+        parser.error(ex)
+
+    if not args.offline and args.remoteCopy:
+        # need to wait for snapshot promoting
+        done = False
+        i = 0
+        while not done:
+            try:
+                cl.startRemoteCopy(rcgName)
+                done = True
+            except exceptions.HTTPBadRequest as ex:
+                # wait max 5min
+                if i > 60:
+                    # other issue, exiting
+                    cl.logout()
+                    print(ex)
+                    exit(1)
+                i += 1
+
+                time.sleep(5)
 
 
 def deleteSnapshot(cl, args):
     snapId = args.snapId
+    scl = None
 
-    if args.vmClone == True:
+    if args.remoteCopy:
+        scl = getRemoteSystemClient(args)
+
+    if args.vmClone:
         srcName = createVmCloneName(args.namingType, args.id, args.vmId)
     else:
         srcName = createVVName(args.namingType, args.id)
 
-    name, metaKey = createSnapshotNameAndMetaKey(srcName, snapId)
-
-    if args.softDelete:
-        cl.modifyVolume(name, {'expirationHours': 168})
-    else:
-        cl.deleteVolume(name)
+    name = createSnapshotName(srcName, snapId)
 
+    # local
     try:
-        cl.removeVolumeMetaData(srcName, metaKey)
+        if args.softDelete:
+            cl.modifyVolume(name, {'expirationHours': 168})
+        else:
+            cl.deleteVolume(name)
     except exceptions.HTTPNotFound:
         pass
 
+    # remote
+    if args.remoteCopy:
+        try:
+            if args.softDelete:
+                scl.modifyVolume(name, {'expirationHours': 168})
+            else:
+                scl.deleteVolume(name)
+        except exceptions.HTTPNotFound:
+            pass
+
 
 def flattenSnapshot(cl, args):
     srcName = args.srcName
     snapId = args.snapId
+    scl = None
 
-    name, metaKey = createSnapshotNameAndMetaKey(srcName, snapId)
+    if args.remoteCopy:
+        scl = getRemoteSystemClient(args)
+
+    name = createSnapshotName(srcName, snapId)
 
     # promote selected snapshot
     cl.promoteVirtualCopy(name)
 
     # delete all snapshots
-    meta = cl.getAllVolumeMetaData(srcName)
-    for data in meta.get('members'):
-        key = data.get('key')
-        if key.startswith('snap'):
-            snap = data.get('value')
+    snapshots = cl.getVolumeSnapshots(srcName)
+    for snap in snapshots:
+        # local
+        try:
+            if args.softDelete:
+                cl.modifyVolume(snap, {'expirationHours': 168})
+            else:
+                # need to wait for snapshot promoting
+                done = False
+                while not done:
+                    try:
+                        cl.deleteVolume(snap)
+                        done = True
+                    except exceptions.HTTPConflict:
+                        time.sleep(5)
+        except exceptions.HTTPNotFound:
+            pass
+
+        # remote
+        if args.remoteCopy:
             try:
                 if args.softDelete:
-                    cl.modifyVolume(snap, {'expirationHours': 168})
+                    scl.modifyVolume(snap, {'expirationHours': 168})
                 else:
-                    # need to wait for snapshot promoting
-                    done = False
-                    while not done:
-                        try:
-                            cl.deleteVolume(snap)
-                            done = True
-                        except exceptions.HTTPConflict:
-                            time.sleep(5)
-                # snapshot deleted, remove metadata
-                cl.removeVolumeMetaData(srcName, key)
+                    scl.deleteVolume(snap)
             except exceptions.HTTPNotFound:
-                # snapshot already not exists, remove metadata
-                cl.removeVolumeMetaData(srcName, key)
+                pass
+
 
 def hostExists(cl, args):
     try:
         cl.getHost(args.host)
     except exceptions.HTTPNotFound:
-        print 0
+        print(0)
         return
-    print 1
+    print(1)
+
 
 def addVolumeToVVSet(cl, args):
     vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
 
+    # limit length of vvset name to 27 chars
+    # it is limit of 3par system
+    vvsetName = vvsetName[0:27]
+
     # get or create vvset
     try:
         cl.getVolumeSet(vvsetName)
     except exceptions.HTTPNotFound:
-        print 'Volume Set does not exists, create new'
+        print('Volume Set does not exists, create new')
         cl.createVolumeSet(vvsetName, None, args.comment)
 
     # add volume to vvset
     try:
         cl.addVolumeToVolumeSet(vvsetName, args.name)
     except exceptions.HTTPConflict as ex:
-        print 'VV already mapped to VV Set'
+        print('VV already mapped to VV Set')
 
 
 def deleteVolumeFromVVSet(cl, args):
     vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
 
+    # limit length of vvset name to 27 chars
+    # it is limit of 3par system
+    vvsetName = vvsetName[0:27]
+
     # remove volume from volume set
     try:
         cl.removeVolumeFromVolumeSet(vvsetName, args.name)
     except exceptions.HTTPNotFound:
-        print 'Volume is already removed from vv set'
+        print('Volume is already removed from vv set')
 
     # get volume set info
     try:
         vvset = cl.getVolumeSet(vvsetName)
         members = vvset.get('setmembers')
     except exceptions.HTTPNotFound:
-        print 'Volume set does not exits, exiting...'
+        print('Volume set does not exits, exiting...')
         return
 
     # if there are other members them we do not remove VV Set
@@ -659,66 +910,240 @@ def deleteVolumeFromVVSet(cl, args):
     try:
         cl.deleteVolumeSet(vvsetName)
     except exceptions.HTTPNotFound:
-        print 'VV Set already does not exits'
+        print('VV Set already does not exits')
 
 
 def createQosPolicy(cl, args):
-    vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
+    if args.remoteCopy:
+        vvsetName = 'RCP_{namingType}.one.vm.{vmId}'.format(namingType=args.namingType, vmId=args.vmId)
+    else:
+        vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
+        # limit length of vvset name to 27 chars
+        # it is limit of 3par system
+        vvsetName = vvsetName[0:27]
 
     # create QoS policy if not exists
     qosRules = prepareQosRules(args)
+
+    # primary system
+    setQosRules(cl, vvsetName, qosRules)
+
+    # remote system
+    if args.remoteCopy:
+        scl = getRemoteSystemClient(args)
+        sysId = int(cl.getStorageSystemInfo().get('id'))
+        secVvsetName = '{name}.r{sysId}'.format(name=vvsetName, sysId=sysId)
+        # strip to 27 chars, like 3par
+        setQosRules(scl, secVvsetName[0:27], qosRules)
+
+
+def disableQosPolicy(cl, args):
+    vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
+    # limit length of vvset name to 27 chars
+    # it is limit of 3par system
+    vvsetName = vvsetName[0:27]
+
     try:
-        qos = cl.queryQoSRule(vvsetName)
-        # compare rules
-        for k, v in qosRules.items():
-            if k == 'enable':
-                k = 'enabled'
-            if qos.get(k) != v:
-                # not match, update
-                print 'QoS Policy Rules changed, need update'
-                cl.modifyQoSRules(vvsetName, qosRules)
-                break
+        cl.modifyQoSRules(vvsetName, {'enable': False})
     except exceptions.HTTPNotFound:
-        print 'QoS Policy does not exists, create new'
-        cl.createQoSRules(vvsetName, qosRules)
+        pass
 
 
-def deleteQosPolicy(cl, args):
-    vvsetName = '{namingType}.one.vm.{vmId}.vvset'.format(namingType=args.namingType, vmId=args.vmId)
+def addVolumeToRCGroup(cl, args):
+    shouldStartRcg = True
 
-    # get volume set info
+    if args.remoteCopyGroupName is None:
+        rcgName = '{namingType}.one.vm.{vmId}'.format(namingType=args.namingType, vmId=args.vmId)
+    else:
+        rcgName = args.remoteCopyGroupName
+
+    scl, targetName = getRemoteCopyTargetName(args)
+
+    # get or create rc group
     try:
-        vvset = cl.getVolumeSet(vvsetName)
-        members = vvset.get('setmembers')
+        rcg = cl.getRemoteCopyGroup(rcgName)
+        rcgState = rcg.get('targets')[0].get('state')
+        # rc group already in starting/started state
+        if rcgState == 2 or rcgState == 3:
+            shouldStartRcg = False
+            # check for peer persistence enabled
+            if rcg.get("targets")[0].get("policies").get("pathManagement"):
+                cl.stopRemoteCopy(rcgName)
+                shouldStartRcg = True
     except exceptions.HTTPNotFound:
-        print 'Volume set does not exits, exiting...'
-        return
+        print('Remote Copy group does not exists, create new')
+        targets, optional, policies = getRCGroupParams(targetName, args)
+        cl.createRemoteCopyGroup(rcgName, targets, optional)
+        # modify to add specific options
+        cl.modifyRemoteCopyGroup(rcgName, {'targets': [{'policies': policies}]})
+        # modify to add autoSync policy, not exposed via API
+        cl._run(['setrcopygroup', 'pol', 'auto_synchronize', rcgName])
+
+    # add volume to rc group
+    # we need to have same VV name on second system too
+    secVVName = '{name}'.format(name=args.name)
+    volumeAutoCreation = True
+    skipInitialSync = False
+
+    # check if remote VV exist
+    try:
+        secVV = scl.getVolume(secVVName)
+        if 'expirationTimeSec' in secVV:
+            scl.modifyVolume(secVVName, {'rmExpTime': True})
+        volumeAutoCreation = False
+        skipInitialSync = True
+    except exceptions.HTTPNotFound:
+        pass
 
-    # if there are other members them we do not remove QoS Policy
-    if members and len(members) > 0:
-        return
+    target = {
+        'targetName': targetName,
+        'secVolumeName': secVVName
+    }
+
+    done = False
+    i = 0
+    while not done:
+        try:
+            print('Add volume to Remote Copy group')
+            cl.addVolumeToRemoteCopyGroup(rcgName, args.name, [target], {
+                'volumeAutoCreation': volumeAutoCreation,
+                'skipInitialSync': skipInitialSync
+            })
+
+            done = True
 
-    # delete qos policy
+            # start rc group
+            if shouldStartRcg:
+                print('Start Remote Copy group')
+                cl.startRemoteCopy(rcgName)
+        except exceptions.HTTPForbidden as ex:
+            # there can be physical copy in progress, so we need wait and retry
+            # wait max 15min
+            if i > 180:
+                # other issue, exiting
+                cl.logout()
+                scl.logout()
+                print(ex)
+                exit(1)
+            i += 1
+            time.sleep(5)
+        except exceptions.HTTPConflict as ex:
+            # volume is already in RC Group
+            print(ex)
+            done = True
+
+    scl.logout()
+
+
+def deleteVolumeFromRCGroup(cl, args):
+    shouldStartRcg = False
+
+    if args.remoteCopyGroupName is None:
+        rcgName = '{namingType}.one.vm.{vmId}'.format(namingType=args.namingType, vmId=args.vmId)
+    else:
+        rcgName = args.remoteCopyGroupName
+
+    # remove volume from rc group
     try:
-        cl.deleteQoSRules(vvsetName)
+        rcg = cl.getRemoteCopyGroup(rcgName)
+        rcgState = rcg.get('targets')[0].get('state')
+        # check if rc group in starting/started state
+        if rcgState == 2 or rcgState == 3:
+            # we need stop rcg only if peer persistence enabled
+            if rcg.get("targets")[0].get("policies").get("pathManagement"):
+                cl.stopRemoteCopy(rcgName)
+                shouldStartRcg = True
+
+        cl.removeVolumeFromRemoteCopyGroup(rcgName, args.name)
     except exceptions.HTTPNotFound:
-        print 'QoS Policy already does not exits'
+        print('Volume is already removed from rc group')
+
+    # get rc group info
+    try:
+        rcg = cl.getRemoteCopyGroup(rcgName)
+        volumes = rcg.get('volumes')
+    except exceptions.HTTPNotFound:
+        print('Remote Copy group does not exits, exiting...')
+        return
+
+    # if there are other members them we do not remove VV Set
+    if volumes and len(volumes) > 0:
+        if shouldStartRcg:
+            print('Start Remote Copy group')
+            # start rc group back
+            cl.startRemoteCopy(rcgName)
+    else:
+        # delete rc group
+        try:
+            cl.removeRemoteCopyGroup(rcgName)
+        except exceptions.HTTPNotFound:
+            print('Remote Copy group does not exits')
 
 
 # ----------------
 # Helper functions
 # ----------------
-def createVVName(namingType, id):
-    return '{namingType}.one.{id}.vv'.format(namingType=namingType, id=id)
+def getRCGroupParams(targetName, args):
+    target = {'targetName': targetName}
+    policies = {'autoRecover': True}
+
+    if args.remoteCopyMode == 'SYNC':
+        target['mode'] = 1
+        if args.remoteCopyHA:
+            #policies['autoFailover'] = True
+            policies['pathManagement'] = True
+    elif args.remoteCopyMode == 'PERIODIC':
+        target['mode'] = 3
+    elif args.remoteCopyMode == 'ASYNC':
+        target['mode'] = 4
+
+    target['userCPG'] = args.secCpg
+    target['snapCPG'] = args.secCpg
+
+    optional = {
+        'localSnapCPG': args.cpg,
+        'localUserCPG': args.cpg
+    }
+
+    return [target], optional, policies
+
+def getRemoteCopyTargetName(args):
+    scl = getRemoteSystemClient(args)
+
+    targetName = scl.getStorageSystemInfo().get('name').encode('ascii', 'ignore')
+
+    return scl, targetName
+
+def getRemoteSystemClient(args):
+    # check for remoteCopy required args
+    if args.sapi is None or args.sip is None:
+        parser.error("--remoteCopy requires --sapi and --sip.")
+
+    secure = False
+    if args.secure:
+        secure = True
 
-def createVmCloneName(namingType, id, vmId):
-    return '{namingType}.one.vm.{vmId}.{id}.vv'.format(namingType=namingType, id=id, vmId=vmId)
+    scl = client.HPE3ParClient(args.sapi, False, secure, None, True)
+    scl.setSSHOptions(args.sip, args.username, args.password)
 
-def createSnapshotNameAndMetaKey(srcName, snapId):
+    try:
+        scl.login(args.username, args.password)
+    except exceptions.HTTPUnauthorized as ex:
+        print("Remote system: Login failed.")
+
+    return scl
+
+
+def createVVName(namingType, imageId):
+    return '{namingType}.one.{id}.vv'.format(namingType=namingType, id=imageId)
+
+def createVmCloneName(namingType, diskId, vmId):
+    return '{namingType}.one.vm.{vmId}.{id}.vv'.format(namingType=namingType, id=diskId, vmId=vmId)
+
+def createSnapshotName(srcName, snapId):
     name = '{srcName}.{snapId}'.format(srcName=srcName, snapId=snapId)
-    metaKey = 'snap{snapId}'.format(snapId=snapId)
 
-    return name, metaKey
+    return name
 
 def createVVWithName(cl, name, args):
     cpgName = args.cpg
@@ -727,13 +1152,13 @@ def createVVWithName(cl, name, args):
     if args.tpvv == True and args.tdvv != True and args.compression != True:
         optional['tpvv'] = True
 
-    if args.tdvv == True:
+    if args.tdvv:
         optional['tdvv'] = True
 
     if args.compression == True and args.size >= 16384:
         optional['compression'] = True
 
-    # minumum size for volume is 256MiB
+    # minimum size for volume is 256MiB
     if args.size < 256:
         args.size = 256
 
@@ -746,25 +1171,22 @@ def createVVWithName(cl, name, args):
 
 def deleteVVWithName(cl, name):
     if args.softDelete:
-        cl.modifyVolume(name, {'expirationHours': 168})
-        # find and delete snapshots
-        meta = cl.getAllVolumeMetaData(name)
-        for data in meta.get('members'):
-            key = data.get('key')
-            if key.startswith('snap'):
-                snap = data.get('value')
+        try:
+            cl.modifyVolume(name, {'expirationHours': 168})
+            # find and delete snapshots
+            snapshots = cl.getVolumeSnapshots(name)
+            for snap in snapshots:
                 cl.modifyVolume(snap, {'expirationHours': 168})
+        except exceptions.HTTPNotFound:
+            pass
     else:
         try:
             cl.deleteVolume(name)
         except exceptions.HTTPConflict:
             # try to find and delete snapshots
-            meta = cl.getAllVolumeMetaData(name)
-            for data in meta.get('members'):
-                key = data.get('key')
-                if key.startswith('snap'):
-                    snap = data.get('value')
-                    cl.deleteVolume(snap)
+            snapshots = cl.getVolumeSnapshots(name)
+            for snap in snapshots:
+                cl.deleteVolume(snap)
 
             # try delete again
             done = False
@@ -779,10 +1201,12 @@ def deleteVVWithName(cl, name):
                     if i > 180:
                         # other issue, exiting
                         cl.logout()
-                        print ex
+                        print(ex)
                         exit(1)
                     i += 1
                     time.sleep(5)
+        except exceptions.HTTPNotFound:
+            pass
 
 def prepareQosRules(args):
     qosRules = {
@@ -801,7 +1225,7 @@ def prepareQosRules(args):
         qosRules['bwMinGoalKB'] = 1
 
     if args.qosLatency == 0:
-        qosRules['latencyGoal'] = 5000
+        qosRules['latencyGoal'] = None
         qosRules['defaultLatency'] = True
 
     if args.qosPriority == 'LOW':
@@ -813,6 +1237,24 @@ def prepareQosRules(args):
 
     return qosRules
 
+def setQosRules(cl, vvsetName, qosRules):
+    try:
+        qos = cl.queryQoSRule(vvsetName)
+        # compare rules
+        for k, v in qosRules.items():
+            if k == 'enable':
+                k = 'enabled'
+            if k == 'defaultLatency' and v:
+                v = None
+            if qos.get(k) != v:
+                # not match, update
+                print('QoS Policy Rules changed, need update')
+                cl.modifyQoSRules(vvsetName, qosRules)
+                break
+    except exceptions.HTTPNotFound:
+        print('QoS Policy does not exists, create new')
+        cl.createQoSRules(vvsetName, qosRules)
+
 # -------------------------------------
 # Parse args and proceed with execution
 # -------------------------------------
@@ -822,7 +1264,7 @@ args = parser.parse_args()
 # Login and run task
 # ------------------
 secure = False
-if args.secure == True:
+if args.secure:
     secure = True
 
 cl = client.HPE3ParClient(args.api, False, secure, None, True)
@@ -831,13 +1273,13 @@ cl.setSSHOptions(args.ip, args.username, args.password)
 try:
     cl.login(args.username, args.password)
 except exceptions.HTTPUnauthorized as ex:
-    print "Login failed."
+    print("Login failed.")
 
 try:
     globals()[args.task](cl, args)
     cl.logout()
 except Exception as ex:
     # something unexpected happened
-    print ex
+    print(ex)
     cl.logout()
     exit(1)
diff --git a/datastore/3par/clone b/datastore/3par/clone
index a9fdcf798447374a9693492d97aa2d0c50bd71e9..248d90d8d3ab99ec18a5db33aeda8397adec046a 100644
--- a/datastore/3par/clone
+++ b/datastore/3par/clone
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -33,7 +33,13 @@ fi
 . $LIB_LOCATION/sh/scripts_common.sh
 
 DRIVER_PATH=$(dirname $0)
+source ${DRIVER_PATH}/../libfs.sh
 source ${DRIVER_PATH}/../../etc/datastore/3par/3par.conf
+. ${DRIVER_PATH}/scripts_3par.sh
+
+# preserve vars from conf file
+CONF_API_ENDPOINT="$API_ENDPOINT"
+CONF_IP="$IP"
 
 # -------- Get cp and datastore arguments from OpenNebula core ------------
 
@@ -42,53 +48,188 @@ ID=$2
 
 XPATH="${DRIVER_PATH}/../xpath.rb -b $DRV_ACTION"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/ID \
+done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/BASE_PATH \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/RESTRICTED_DIRS \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SAFE_DIRS \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/BRIDGE_LIST \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/STAGING_DIR \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/ID \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/IP \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CPG \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_IP \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/REMOTE_COPY \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_CPG \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/THIN \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/DEDUP \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/COMPRESSION \
-                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NAMING_TYPE \
                     /DS_DRIVER_ACTION_DATA/IMAGE/NAME \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/PERSISTENT \
                     /DS_DRIVER_ACTION_DATA/IMAGE/SIZE \
                     /DS_DRIVER_ACTION_DATA/IMAGE/CLONING_ID)
 
-unset i
-
-DSID="${XPATH_ELEMENTS[i++]}"
-CPG="${XPATH_ELEMENTS[i++]:-$CPG}"
-THIN="${XPATH_ELEMENTS[i++]:-$THIN}"
-DEDUP="${XPATH_ELEMENTS[i++]:-$DEDUP}"
-COMPRESSION="${XPATH_ELEMENTS[i++]:-$COMPRESSION}"
-NAMING_TYPE="${XPATH_ELEMENTS[i++]:-$NAMING_TYPE}"
-NAME="${XPATH_ELEMENTS[i++]}"
-SIZE="${XPATH_ELEMENTS[i++]}"
-CLONING_ID="${XPATH_ELEMENTS[i++]}"
-
-# Get source image properties
-unset i XPATH_ELEMENTS
-XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
+BASE_PATH="${XPATH_ELEMENTS[j++]}"
+RESTRICTED_DIRS="${XPATH_ELEMENTS[j++]}"
+SAFE_DIRS="${XPATH_ELEMENTS[j++]}"
+BRIDGE_LIST="${XPATH_ELEMENTS[j++]}"
+STAGING_DIR="${XPATH_ELEMENTS[j++]:-/var/tmp}"
+DSID="${XPATH_ELEMENTS[j++]}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+SEC_CPG="${XPATH_ELEMENTS[j++]:-$SEC_CPG}"
+THIN="${XPATH_ELEMENTS[j++]:-$THIN}"
+DEDUP="${XPATH_ELEMENTS[j++]:-$DEDUP}"
+COMPRESSION="${XPATH_ELEMENTS[j++]:-$COMPRESSION}"
+NAME="${XPATH_ELEMENTS[j++]//[^A-Za-z0-9\[\]() _~+-]/}"
+PERSISTENT="${XPATH_ELEMENTS[j++]}"
+SIZE="${XPATH_ELEMENTS[j++]}"
+CLONING_ID="${XPATH_ELEMENTS[j++]}"
+
+#-------------------------------------------------------------------------------
+# Get source image datastore id and source
+#-------------------------------------------------------------------------------
+XPATH="${DRIVER_PATH}/../xpath.rb --stdin"
+
+unset i j XPATH_ELEMENTS
+
+while IFS= read -r -d '' element; do
+    XPATH_ELEMENTS[i++]="$element"
+done < <(oneimage show -x $CLONING_ID | $XPATH \
+                /IMAGE/DATASTORE_ID \
+                /IMAGE/SOURCE)
+
+SRC_DS_ID="${XPATH_ELEMENTS[j++]}"
+SRC_NAME_WWN="${XPATH_ELEMENTS[j++]}"
+
+#-------------------------------------------------------------------------------
+# Get source image ds information
+#-------------------------------------------------------------------------------
+unset i j XPATH_ELEMENTS
+
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(oneimage show -x $CLONING_ID| $XPATH \
-                    /IMAGE/DATASTORE_ID)
+done < <(onedatastore show -x $SRC_DS_ID | $XPATH \
+                    /DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/IP)
+
+SRC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$CONF_API_ENDPOINT}"
+SRC_IP="${XPATH_ELEMENTS[j++]:-$CONF_IP}"
+
+# clone between storage systems
+if [ "$SRC_IP" != "$IP" ]; then
+  # get dst host from bridge list
+  DST_HOST=`get_destination_host $ID`
+
+  if [ -z "$DST_HOST" ]; then
+      error_message "Datastore template missing 'BRIDGE_LIST' attribute."
+      exit 1
+  fi
+
+  set_up_datastore "$BASE_PATH" "$RESTRICTED_DIRS" "$SAFE_DIRS"
+
+  # prepare tmp dest
+  IMAGE_HASH=`generate_image_hash`
+  TMP_DST="$STAGING_DIR/$IMAGE_HASH"
+
+  # extract VV Name and Wwn
+  SRC_NAME=$(get_vv_name "$SRC_NAME_WWN")
+  SRC_WWN=$(get_vv_wwn "$SRC_NAME_WWN")
+
+  log "Mapping $SRC_NAME to $DST_HOST"
+  LUN=$(python ${DRIVER_PATH}/3par.py exportVV -a "$SRC_API_ENDPOINT" -i "$SRC_IP" -s "$SECURE" -u "$USERNAME" \
+                      -p "$PASSWORD" -n "$SRC_NAME" -hs "$DST_HOST")
+
+  if [ $? -ne 0 ]; then
+    error_message "$LUN"
+    exit 1
+  fi
 
-SRC_DSID=${XPATH_ELEMENTS[0]}
+  map_lun "$DST_HOST" "$LUN" "$SRC_WWN" "$STAGING_DIR" "$TMP_DST"
+
+  # Create image
+  NAME_WWN=$(python ${DRIVER_PATH}/3par.py createVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
+                                            -nt $NAMING_TYPE -tpvv $THIN -tdvv $DEDUP -compr $COMPRESSION -id $ID \
+                                            -c $CPG -sz $SIZE -co "$NAME")
+
+  NEW_NAME=$(get_vv_name "$NAME_WWN")
+  NEW_WWN=$(get_vv_wwn "$NAME_WWN")
+
+  # export new vv and clone old one to it
+  LUN=$(export_vv "$NEW_NAME" "$DST_HOST")
+  map_and_copy_to_lun "$DST_HOST" "$SRC_WWN" "$LUN" "$NEW_WWN"
+
+  # unexport new vv
+  unmap_lun "$DST_HOST" "$NEW_WWN"
+  unexport_vv "$NEW_NAME" "$DST_HOST"
+
+  # unexport src vv
+  unmap_lun "$DST_HOST" "$SRC_WWN"
+
+  log "Unexporting $SRC_NAME from $DST_HOST"
+  RESULT=$(python ${DRIVER_PATH}/3par.py unexportVV -a "$SRC_API_ENDPOINT" -i "$SRC_IP" -s "$SECURE" -u "$USERNAME" \
+                                -p "$PASSWORD" -n "$SRC_NAME" -hs "$DST_HOST")
+
+  if [ $? -ne 0 ]; then
+    error_message "$RESULT"
+    exit 1
+  fi
+
+  # cleanup
+  CLEANUP_CMD=$(cat <<EOF
+      # remove original
+      $RM -f $TMP_DST
+EOF
+)
+
+  ssh_exec_and_log "$DST_HOST" "$CLEANUP_CMD" \
+      "Error performing cleanup on $DST_HOST"
+else
+  # -------- Clone image ------------
+  NAME_WWN=$(python ${DRIVER_PATH}/3par.py cloneVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
+                                  -snt $NAMING_TYPE -sid $CLONING_ID -nt $NAMING_TYPE -id $ID \
+                                  -tpvv $THIN -tdvv $DEDUP -compr $COMPRESSION -c $CPG -sz $SIZE -co "$NAME")
+
+  if [ $? -ne 0 ]; then
+    error_message "$NAME_WWN"
+    exit 1
+  fi
+fi
+
+#-------------------------------------------------------------------------------
+# Clone operation take a while and in meantime, persistent attr can be changed
+# so check again, for image persistent state
+#-------------------------------------------------------------------------------
+unset i j XPATH_ELEMENTS
 
-# Get source datastore properties
-unset i XPATH_ELEMENTS
-XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $SRC_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
+done < <(oneimage show -x $ID| $XPATH /IMAGE/PERSISTENT)
 
-SRC_NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
+PERSISTENT="${XPATH_ELEMENTS[j++]}"
 
-# -------- Clone image ------------
+# ----------- Add image to RC Group -------
+# only non-persistent images
+if [ "$PERSISTENT" == "0" ] && [ "$REMOTE_COPY" == "YES" ]; then
+    VV_NAME=$(get_vv_name "$NAME_WWN")
+
+    log "Add image to remote copy group"
+    RCG=$(python ${DRIVER_PATH}/3par.py addVolumeToRCGroup -a $API_ENDPOINT -i $IP \
+      -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $VV_NAME \
+      -c $CPG -sc $SEC_CPG -rcm $REMOTE_COPY_MODE -rcgn "$NAMING_TYPE.one.ds.$DSID" -rcha "NO")
+
+    if [ $? -ne 0 ]; then
+      error_message "$RCG"
+      exit 1
+    fi
+fi
 
-python ${DRIVER_PATH}/3par.py cloneVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                -snt $SRC_NAMING_TYPE -sid $CLONING_ID -nt $NAMING_TYPE -id $ID \
-                                -tpvv $THIN -tdvv $DEDUP -compr $COMPRESSION -c $CPG -sz $SIZE -co "$NAME"
\ No newline at end of file
+echo $NAME_WWN
diff --git a/datastore/3par/cp b/datastore/3par/cp
index 76679a1784aca034d058b36f14dc3708be416ef9..cb792972d1fe14e90a299d08c95ac9ceefa77497 100644
--- a/datastore/3par/cp
+++ b/datastore/3par/cp
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -49,7 +49,7 @@ UTILS_PATH="${DRIVER_PATH}/.."
 
 XPATH="$UTILS_PATH/xpath.rb -b $DRV_ACTION"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
@@ -67,37 +67,49 @@ done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/BASE_PATH \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CONVERT \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/DRIVER \
                     /DS_DRIVER_ACTION_DATA/IMAGE/TYPE \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/ID \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/IP \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CPG \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_IP \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/REMOTE_COPY \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_CPG \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/THIN \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/DEDUP \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/COMPRESSION \
-                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NAMING_TYPE \
                     /DS_DRIVER_ACTION_DATA/IMAGE/SIZE \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/PERSISTENT \
                     /DS_DRIVER_ACTION_DATA/IMAGE/NAME)
 
-unset i
-
-BASE_PATH="${XPATH_ELEMENTS[i++]}"
-RESTRICTED_DIRS="${XPATH_ELEMENTS[i++]}"
-SAFE_DIRS="${XPATH_ELEMENTS[i++]}"
-BRIDGE_LIST="${XPATH_ELEMENTS[i++]}"
-STAGING_DIR="${XPATH_ELEMENTS[i++]:-/var/tmp}"
-TYPE="${XPATH_ELEMENTS[i++]}"
-SRC="${XPATH_ELEMENTS[i++]}"
-MD5="${XPATH_ELEMENTS[i++]}"
-SHA1="${XPATH_ELEMENTS[i++]}"
-NO_DECOMPRESS="${XPATH_ELEMENTS[i++]}"
-LIMIT_TRANSFER_BW="${XPATH_ELEMENTS[i++]}"
-CONVERT="${XPATH_ELEMENTS[i++]:-yes}"
-DRIVER="${XPATH_ELEMENTS[i++]}"
-IMAGE_TYPE="${XPATH_ELEMENTS[i++]}"
-CPG="${XPATH_ELEMENTS[i++]:-$CPG}"
-THIN="${XPATH_ELEMENTS[i++]:-$THIN}"
-DEDUP="${XPATH_ELEMENTS[i++]:-$DEDUP}"
-COMPRESSION="${XPATH_ELEMENTS[i++]:-$COMPRESSION}"
-NAMING_TYPE="${XPATH_ELEMENTS[i++]:-$NAMING_TYPE}"
-SIZE="${XPATH_ELEMENTS[i++]:-0}"
-IMAGE_NAME="${XPATH_ELEMENTS[i++]:-0}"
+BASE_PATH="${XPATH_ELEMENTS[j++]}"
+RESTRICTED_DIRS="${XPATH_ELEMENTS[j++]}"
+SAFE_DIRS="${XPATH_ELEMENTS[j++]}"
+BRIDGE_LIST="${XPATH_ELEMENTS[j++]}"
+STAGING_DIR="${XPATH_ELEMENTS[j++]:-/var/tmp}"
+TYPE="${XPATH_ELEMENTS[j++]}"
+SRC="${XPATH_ELEMENTS[j++]}"
+MD5="${XPATH_ELEMENTS[j++]}"
+SHA1="${XPATH_ELEMENTS[j++]}"
+NO_DECOMPRESS="${XPATH_ELEMENTS[j++]}"
+LIMIT_TRANSFER_BW="${XPATH_ELEMENTS[j++]}"
+CONVERT="${XPATH_ELEMENTS[j++]:-yes}"
+DRIVER="${XPATH_ELEMENTS[j++]}"
+IMAGE_TYPE="${XPATH_ELEMENTS[j++]}"
+DSID="${XPATH_ELEMENTS[j++]}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+SEC_CPG="${XPATH_ELEMENTS[j++]:-$SEC_CPG}"
+THIN="${XPATH_ELEMENTS[j++]:-$THIN}"
+DEDUP="${XPATH_ELEMENTS[j++]:-$DEDUP}"
+COMPRESSION="${XPATH_ELEMENTS[j++]:-$COMPRESSION}"
+SIZE="${XPATH_ELEMENTS[j++]:-0}"
+PERSISTENT="${XPATH_ELEMENTS[j++]}"
+IMAGE_NAME="${XPATH_ELEMENTS[j++]//[^A-Za-z0-9\[\]() _~+-]/}"
 
 DST_HOST=`get_destination_host $ID`
 
@@ -150,6 +162,20 @@ fi
 NAME=$(get_vv_name "$NAME_WWN")
 WWN=$(get_vv_wwn "$NAME_WWN")
 
+# ----------- Add image to RC Group -------
+# only non-persistent images
+if [ "$PERSISTENT" == "0" ] && [ "$REMOTE_COPY" == "YES" ]; then
+    log "Add image to remote copy group"
+    RCG=$(python ${DRIVER_PATH}/3par.py addVolumeToRCGroup -a $API_ENDPOINT -i $IP \
+      -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME \
+      -c $CPG -sc $SEC_CPG -rcm $REMOTE_COPY_MODE -rcgn "$NAMING_TYPE.one.ds.$DSID" -rcha "NO")
+
+    if [ $? -ne 0 ]; then
+      error_message "$RCG"
+      exit 1
+    fi
+fi
+
 # Map image
 log "Mapping $WWN to $DST_HOST"
 
@@ -163,22 +189,13 @@ fi
 DISCOVER_CMD=$(cat <<EOF
     set -e
     $(discover_lun "$LUN" "$WWN")
-    echo "\$DEV"
 EOF
 )
 
 ssh_exec_and_log "$DST_HOST" "$DISCOVER_CMD" \
     "Error registering $WWN to $DST_HOST"
 
-GET_DEV_CMD=$(cat <<EOF
-    set -e
-    DEV="/dev/mapper/3$WWN"
-    
-    echo "\$DEV"
-EOF
-)
-
-DEV=$(ssh_monitor_and_log "$DST_HOST" "$GET_DEV_CMD")
+DEV="/dev/mapper/3$WWN"
 
 # copy image
 REGISTER_CMD=$(cat <<EOF
@@ -189,7 +206,7 @@ REGISTER_CMD=$(cat <<EOF
     if [ "\$FORMAT" != "raw" ]; then
         $QEMU_IMG convert -O raw $TMP_DST $DEV
     else
-        dd \if=$TMP_DST of=$DEV bs=${DD_BLOCK_SIZE:-64k}
+        dd \if=$TMP_DST of=$DEV bs=${DD_BLOCK_SIZE:-64k} oflag=direct
     fi
 
     # remove original
@@ -214,4 +231,4 @@ ssh_exec_and_log "$DST_HOST" "$FLUSH_CMD" \
 
 python ${DRIVER_PATH}/3par.py unexportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -hs $DST_HOST
 
-echo $NAME_WWN
\ No newline at end of file
+echo "$NAME_WWN raw"
diff --git a/datastore/3par/discover_lun.sh b/datastore/3par/discover_lun.sh
new file mode 100755
index 0000000000000000000000000000000000000000..393a2f3895bfd9856284b2aa7fb33ed8cd01c97f
--- /dev/null
+++ b/datastore/3par/discover_lun.sh
@@ -0,0 +1,79 @@
+#!/usr/bin/env bash
+
+SCRIPT_PATH=$(dirname $0)
+
+. ${SCRIPT_PATH}/scripts_3par.sh
+
+# Log function that knows how to deal with severities and adds the
+# script name
+function log_function
+{
+    echo "$1: $SCRIPT_NAME: $2" 1>&2
+}
+
+# Logs an info message
+function log_info
+{
+    log_function "INFO" "$1"
+}
+
+# Logs an error message
+function log_error
+{
+    log_function "ERROR" "$1"
+}
+
+# Logs a debug message
+function log_debug
+{
+    log_function "DEBUG" "$1"
+}
+
+# This function is used to pass error message to the mad
+function error_message
+{
+    (
+        echo "ERROR MESSAGE --8<------"
+        echo "$1"
+        echo "ERROR MESSAGE ------>8--"
+    ) 1>&2
+}
+
+# Executes a command, if it fails returns error message and exits
+# If a second parameter is present it is used as the error message when
+# the command fails
+function exec_and_log
+{
+    EXEC_LOG_ERR=`bash -s 2>&1 1>/dev/null <<EOF
+export LANG=C
+export LC_ALL=C
+$1
+EOF`
+    EXEC_LOG_RC=$?
+
+    if [ $EXEC_LOG_RC -ne 0 ]; then
+        log_error "Command \"$1\" failed: $EXEC_LOG_ERR"
+
+        if [ -n "$2" ]; then
+            error_message "$2"
+        else
+            error_message "Error executing $1: $EXEC_LOG_ERR"
+        fi
+        exit $EXEC_LOG_RC
+    fi
+}
+
+
+LUN=$1
+WWN=$2
+
+DISCOVER_CMD=$(cat <<EOF
+    set -e
+    $(discover_lun "$LUN" "$WWN")
+EOF
+)
+
+exec_and_log "$DISCOVER_CMD" \
+    "Error discovering LUN $LUN:$WWN"
+
+exit 0
diff --git a/datastore/3par/flush_lun.sh b/datastore/3par/flush_lun.sh
new file mode 100755
index 0000000000000000000000000000000000000000..eb8ba5e40a09b8c7a8e26a45c2bfac42abcadc89
--- /dev/null
+++ b/datastore/3par/flush_lun.sh
@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+
+SCRIPT_PATH=$(dirname $0)
+
+. ${SCRIPT_PATH}/scripts_3par.sh
+
+# Log function that knows how to deal with severities and adds the
+# script name
+function log_function
+{
+    echo "$1: $SCRIPT_NAME: $2" 1>&2
+}
+
+# Logs an info message
+function log_info
+{
+    log_function "INFO" "$1"
+}
+
+# Logs an error message
+function log_error
+{
+    log_function "ERROR" "$1"
+}
+
+# Logs a debug message
+function log_debug
+{
+    log_function "DEBUG" "$1"
+}
+
+# This function is used to pass error message to the mad
+function error_message
+{
+    (
+        echo "ERROR MESSAGE --8<------"
+        echo "$1"
+        echo "ERROR MESSAGE ------>8--"
+    ) 1>&2
+}
+
+# Executes a command, if it fails returns error message and exits
+# If a second parameter is present it is used as the error message when
+# the command fails
+function exec_and_log
+{
+    EXEC_LOG_ERR=`bash -s 2>&1 1>/dev/null <<EOF
+export LANG=C
+export LC_ALL=C
+$1
+EOF`
+    EXEC_LOG_RC=$?
+
+    if [ $EXEC_LOG_RC -ne 0 ]; then
+        log_error "Command \"$1\" failed: $EXEC_LOG_ERR"
+
+        if [ -n "$2" ]; then
+            error_message "$2"
+        else
+            error_message "Error executing $1: $EXEC_LOG_ERR"
+        fi
+        exit $EXEC_LOG_RC
+    fi
+}
+
+
+WWN=$1
+
+FLUSH_CMD=$(cat <<EOF
+    set -e
+    $(remove_lun "$WWN")
+EOF
+)
+
+exec_and_log "$FLUSH_CMD" \
+    "Error flushing LUN $WWN"
+
+exit 0
diff --git a/datastore/3par/mkfs b/datastore/3par/mkfs
index 1c7f80f72d263c68c79bd244cc43e8429472fb4f..9e8897abc613fefe0c0c25039121953525f1c66f 100644
--- a/datastore/3par/mkfs
+++ b/datastore/3par/mkfs
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -33,7 +33,9 @@ fi
 . $LIB_LOCATION/sh/scripts_common.sh
 
 DRIVER_PATH=$(dirname $0)
+source ${DRIVER_PATH}/../libfs.sh
 source ${DRIVER_PATH}/../../etc/datastore/3par/3par.conf
+. ${DRIVER_PATH}/scripts_3par.sh
 
 # -------- Get mkfs and datastore arguments from OpenNebula core ------------
 
@@ -42,29 +44,119 @@ ID=$2
 
 XPATH="${DRIVER_PATH}/../xpath.rb -b $DRV_ACTION"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CPG \
+done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/ID \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/BRIDGE_LIST \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/IP \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NAMING_TYPE \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CPG \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_IP \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/REMOTE_COPY \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_CPG \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/THIN \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/DEDUP \
                     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/COMPRESSION \
-                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NAMING_TYPE \
                     /DS_DRIVER_ACTION_DATA/IMAGE/NAME \
-                    /DS_DRIVER_ACTION_DATA/IMAGE/SIZE)
-
-unset i
+                    /DS_DRIVER_ACTION_DATA/IMAGE/PERSISTENT \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/SIZE \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/FS)
 
-CPG="${XPATH_ELEMENTS[i++]:-$CPG}"
-THIN="${XPATH_ELEMENTS[i++]:-$THIN}"
-DEDUP="${XPATH_ELEMENTS[i++]:-$DEDUP}"
-COMPRESSION="${XPATH_ELEMENTS[i++]:-$COMPRESSION}"
-NAMING_TYPE="${XPATH_ELEMENTS[i++]:-$NAMING_TYPE}"
-NAME="${XPATH_ELEMENTS[i++]}"
-SIZE="${XPATH_ELEMENTS[i++]:-0}"
+DSID="${XPATH_ELEMENTS[j++]}"
+BRIDGE_LIST="${XPATH_ELEMENTS[j++]}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
+CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+SEC_CPG="${XPATH_ELEMENTS[j++]:-$SEC_CPG}"
+THIN="${XPATH_ELEMENTS[j++]:-$THIN}"
+DEDUP="${XPATH_ELEMENTS[j++]:-$DEDUP}"
+COMPRESSION="${XPATH_ELEMENTS[j++]:-$COMPRESSION}"
+NAME="${XPATH_ELEMENTS[j++]}"
+PERSISTENT="${XPATH_ELEMENTS[j++]}"
+SIZE="${XPATH_ELEMENTS[j++]:-0}"
+FSTYPE="${XPATH_ELEMENTS[j++]}"
 
 # ------------ Create image -------------
 
-python ${DRIVER_PATH}/3par.py createVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE \
-                                    -tpvv $THIN -tdvv $DEDUP -compr $COMPRESSION -id $ID -c $CPG -sz $SIZE -co "$NAME"
\ No newline at end of file
+NAME_WWN=$(python ${DRIVER_PATH}/3par.py createVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE \
+                                    -tpvv $THIN -tdvv $DEDUP -compr $COMPRESSION -id $ID -c $CPG -sz $SIZE -co "$NAME")
+
+if [ $? -ne 0 ]; then
+  error_message "$NAME_WWN"
+  exit 1
+fi
+
+NAME=$(get_vv_name "$NAME_WWN")
+WWN=$(get_vv_wwn "$NAME_WWN")
+
+# ----------- Add image to RC Group -------
+# only non-persistent images
+if [ "$PERSISTENT" == "0" ] && [ "$REMOTE_COPY" == "YES" ]; then
+    log "Add image to remote copy group"
+    RCG=$(python ${DRIVER_PATH}/3par.py addVolumeToRCGroup -a $API_ENDPOINT -i $IP \
+      -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME \
+      -c $CPG -sc $SEC_CPG -rcm $REMOTE_COPY_MODE -rcgn "$NAMING_TYPE.one.ds.$DSID" -rcha "NO")
+
+    if [ $? -ne 0 ]; then
+      error_message "$RCG"
+      exit 1
+    fi
+fi
+
+# Filesystem defined, so we need format disk
+if [ -n "$FSTYPE" ]; then
+  if [ "$FSTYPE" == "xfs" ] || [ "$FSTYPE" == "ext4" ] || [ "$FSTYPE" == "ext3" ] || [ "$FSTYPE" == "ext2" ]; then
+    DST_HOST=`get_destination_host $ID`
+
+    if [ -z "$DST_HOST" ]; then
+        error_message "Datastore template missing 'BRIDGE_LIST' attribute."
+        exit 1
+    fi
+
+    # Map image
+    log "Mapping $WWN to $DST_HOST"
+
+    LUN=$(python ${DRIVER_PATH}/3par.py exportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -hs $DST_HOST)
+
+    if [ $? -ne 0 ]; then
+      error_message "$LUN"
+      exit 1
+    fi
+
+    log "Discover $WWN and format it to fs: $FSTYPE"
+    DISCOVER_CMD=$(cat <<EOF
+        set -e
+        $(discover_lun "$LUN" "$WWN")
+
+        sudo /usr/sbin/mkfs -t "$FSTYPE" "\$DEV"
+EOF
+)
+
+    ssh_exec_and_log "$DST_HOST" "$DISCOVER_CMD" \
+        "Error registering $WWN to $DST_HOST"
+
+    # Unmap image
+    log "Unmapping $WWN from $DST_HOST"
+
+    FLUSH_CMD=$(cat <<EOF
+        set -e
+        $(remove_lun "$WWN")
+EOF
+)
+
+    ssh_exec_and_log "$DST_HOST" "$FLUSH_CMD" \
+        "Error flushing out mapping"
+
+    python ${DRIVER_PATH}/3par.py unexportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -hs $DST_HOST
+  fi
+fi
+
+echo $NAME_WWN
diff --git a/datastore/3par/monitor b/datastore/3par/monitor
index a5629118cf843035f49891a70adf1ffe7a81f385..b4c18a9c635a9845f45c2b16311395b09fb86d2d 100644
--- a/datastore/3par/monitor
+++ b/datastore/3par/monitor
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -44,13 +44,17 @@ ID=$2
 
 XPATH="${DRIVER_PATH}/../xpath.rb -b $DRV_ACTION"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CPG)
+done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/IP \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CPG)
 
-CPG="${XPATH_ELEMENTS[0]:-$CPG}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
 
 # ------------ Compute datastore usage -------------
 
diff --git a/datastore/3par/rm b/datastore/3par/rm
index 7732bfe31082c65d6649fa2ebf03ff1a7b0ee15f..94151b8543a34a3419141481bcb2b2ca5113aff8 100644
--- a/datastore/3par/rm
+++ b/datastore/3par/rm
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -34,12 +34,85 @@ fi
 
 DRIVER_PATH=$(dirname $0)
 source ${DRIVER_PATH}/../../etc/datastore/3par/3par.conf
+. ${DRIVER_PATH}/scripts_3par.sh
 
 # -------- Get rm and datastore arguments from OpenNebula core ------------
 
 DRV_ACTION=$1
 ID=$2
 
-# ------------ Delete image -------------
+XPATH="${DRIVER_PATH}/../xpath.rb -b $DRV_ACTION"
 
-python ${DRIVER_PATH}/3par.py deleteVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $ID
\ No newline at end of file
+unset i j XPATH_ELEMENTS
+
+while IFS= read -r -d '' element; do
+    XPATH_ELEMENTS[i++]="$element"
+done < <($XPATH     /DS_DRIVER_ACTION_DATA/IMAGE/PERSISTENT \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/SOURCE \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/RC \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/RC_SYSTEM_DS_ID \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/ID \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/IP \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_IP \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/REMOTE_COPY)
+
+PERSISTENT="${XPATH_ELEMENTS[j++]}"
+SOURCE="${XPATH_ELEMENTS[j++]}"
+SYS_DS_REMOTE_COPY="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
+DSID="${XPATH_ELEMENTS[j++]}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+
+# only non-persistent images
+if [ "$PERSISTENT" == "0" ] && [ "$REMOTE_COPY" == "YES" ]; then
+    NAME=$(get_vv_name "$SOURCE")
+
+    log "Remove disk from Remote Copy group"
+    RCG=$(python ${DRIVER_PATH}/3par.py deleteVolumeFromRCGroup -a $API_ENDPOINT -i $IP \
+        -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -rcgn "$NAMING_TYPE.one.ds.$DSID")
+
+    if [ $? -ne 0 ]; then
+      error_message "$RCG"
+      exit 1
+    fi
+
+    # Delete image from both 3par systems
+  python ${DRIVER_PATH}/3par.py deleteVV -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE \
+                                      -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $ID -rc $REMOTE_COPY
+
+  exit $?
+fi
+
+# persistent disks deployed in RC enabled system datastore
+if [ "$SYS_DS_REMOTE_COPY" == "YES" ]; then
+  #-------------------------------------------------------------------------------
+  # Get system ds information
+  #-------------------------------------------------------------------------------
+
+  unset i j XPATH_ELEMENTS
+
+  while IFS= read -r -d '' element; do
+      XPATH_ELEMENTS[i++]="$element"
+  done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                          /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                          /DATASTORE/TEMPLATE/SEC_IP)
+
+  SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+  SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+
+  # Delete image from both 3par systems
+  python ${DRIVER_PATH}/3par.py deleteVV -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE \
+                                      -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $ID -rc $SYS_DS_REMOTE_COPY
+
+  exit $?
+fi
+
+# Delete image from single 3par system
+python ${DRIVER_PATH}/3par.py deleteVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
+                                        -nt $NAMING_TYPE -id $ID
diff --git a/datastore/3par/scripts_3par.sh b/datastore/3par/scripts_3par.sh
index c73317408abfea280c5cdde613b099c401362efd..6403133874cbb7a00b3b38d5ee9e3f1812417efb 100644
--- a/datastore/3par/scripts_3par.sh
+++ b/datastore/3par/scripts_3par.sh
@@ -1,6 +1,6 @@
 #@IgnoreInspection BashAddShebang
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 #                                                                            #
@@ -24,9 +24,17 @@ MULTIPATHD=multipathd
 TEE=tee
 BASENAME=basename
 
+if [ -z "${ONE_LOCATION}" ]; then
+    PY3PAR="python /var/lib/one/remotes/datastore/3par/3par.py"
+    XPATH="/var/lib/one/remotes/datastore/xpath.rb --stdin"
+else
+    PY3PAR="python $ONE_LOCATION/var/remotes/datastore/3par/3par.py"
+    XPATH="$ONE_LOCATION/var/remotes/datastore/xpath.rb --stdin"
+fi
+
 function multipath_flush {
-    local MAP_NAME
-    MAP_NAME="$1"
+    local MAP_NAME="$1"
+
     echo "$SUDO $MULTIPATH -f $MAP_NAME"
 }
 
@@ -36,15 +44,15 @@ function multipath_rescan {
 }
 
 function multipath_resize {
-    local MAP_NAME
-    MAP_NAME="$1"
+    local MAP_NAME="$1"
+
     echo "$SUDO $MULTIPATHD -k\"resize map $MAP_NAME\""
 }
 
 function rescan_scsi_bus {
-  local LUN
+  local LUN="$1"
   local FORCE
-  LUN="$1"
+
   # important to ignore rev, otherwise rescan failed when 3PAR OS get major update and device is online resized
   # https://gitlab.feldhost.cz/feldhost-public/one-addon-3par/-/issues/1
   [ "$2" == "force" ] && FORCE=" --forcerescan  --ignore-rev"
@@ -53,17 +61,39 @@ function rescan_scsi_bus {
 }
 
 function get_vv_name {
-  local NAME_WWN
-  NAME_WWN="$1"
+  local NAME_WWN="$1"
+
   echo "$NAME_WWN" | $AWK -F: '{print $1}'
 }
 
 function get_vv_wwn {
-  local NAME_WWN
-  NAME_WWN="$1"
+  local NAME_WWN="$1"
+
   echo "$NAME_WWN" | $AWK -F: '{print $2}'
 }
 
+function image_update {
+  local ID="$1"
+  local DATA="$2"
+
+  _TEMPLATE=$(mktemp -t oneimageUpdate-XXXXXXXXXX)
+  trap "rm -f \"$_TEMPLATE\"" TERM INT QUIT HUP EXIT
+  echo $DATA > $_TEMPLATE
+
+  oneimage update $ID -a $_TEMPLATE
+}
+
+function get_image_running_vms_count {
+    local IMAGE_ID="$1"
+    local i XPATH_ELEMENTS
+
+    while IFS= read -r -d '' element; do
+        XPATH_ELEMENTS[i++]="$element"
+    done < <(oneimage show -x "$IMAGE_ID" | $XPATH /IMAGE/RUNNING_VMS)
+
+    echo "${XPATH_ELEMENTS[0]}"
+}
+
 function discover_lun {
     local LUN
     local WWN
@@ -71,7 +101,6 @@ function discover_lun {
     WWN="$2"
     cat <<EOF
         $(rescan_scsi_bus "$LUN")
-        $(multipath_rescan)
 
         DEV="/dev/mapper/3$WWN"
 
@@ -81,17 +110,10 @@ function discover_lun {
             sleep 1
             COUNTER=\$((\$COUNTER + 1))
         done
-        if [ ! -e \$DEV ]; then
-            # Last chance to get our mapping
-            $(multipath_rescan)
-            COUNTER=1
-            while [ ! -e "\$DEV" ] && [ \$COUNTER -le 10 ]; do
-                sleep 1
-                COUNTER=\$((\$COUNTER + 1))
-            done
-        fi
+
         # Exit with error if mapping does not exist
         if [ ! -e \$DEV ]; then
+            echo 'Mapping does not exists'
             exit 1
         fi
 
@@ -105,6 +127,7 @@ function discover_lun {
         done
         # Exit with error if mapping has no path
         if [ ! "\${DM_SLAVE}" ]; then
+            echo 'Mapping has no path'
             exit 1
         fi
 EOF
@@ -115,18 +138,338 @@ function remove_lun {
     WWN="$1"
     cat <<EOF
       DEV="/dev/mapper/3$WWN"
-      DM_HOLDER=\$($SUDO $DMSETUP ls -o blkdevname | grep -Po "(?<=3$WWN\s\()[^)]+")
-      DM_SLAVE=\$(ls /sys/block/\${DM_HOLDER}/slaves)
+      SLAVES=\$($SUDO $MULTIPATH -l 3$WWN | grep -Eo 'sd[a-z]+')
 
-      $(multipath_flush "\$DEV")
+      if [ -z "\${SLAVES}" ]; then
+          SLAVES=\$($SUDO $MULTIPATH -r 3$WWN | grep -Eo 'sd[a-z]+')
+      fi
+
+      $(multipath_flush "3$WWN")
 
       unset device
-      for device in \${DM_SLAVE}
+      for device in \${SLAVES}
       do
           if [ -e /dev/\${device} ]; then
               $SUDO $BLOCKDEV --flushbufs /dev/\${device}
               echo 1 | $SUDO $TEE /sys/block/\${device}/device/delete
           fi
       done
+
+      # wait for auto remove multipath
+      EXISTS=1
+      COUNTER=1
+      while [ "\${SLAVES}" ] && [ \$EXISTS -gt 0 ] && [ \$COUNTER -le 30 ]; do
+          sleep 1
+          EXISTS=\$($SUDO $MULTIPATH -ll 3$WWN | head -c1 | wc -c)
+          COUNTER=\$((\$COUNTER + 1))
+      done
+
+      if [[ \$EXISTS -gt 0 ]]; then
+          # Wait for releasing device
+          OPEN_COUNT=1
+          while [ \$OPEN_COUNT -gt 0 ]; do
+              sleep 1
+              OPEN_COUNT=\$($SUDO $DMSETUP info 3$WWN | grep -P "Open\scount:" | grep -oP "[0-9]+")
+          done
+
+          $(multipath_flush "3$WWN")
+      fi
+EOF
+}
+
+function unmap_lun {
+  local HOST
+  local WWN
+  HOST="$1"
+  WWN="$2"
+  local CMD
+
+  log "Unmapping $WWN from $HOST"
+
+  CMD=$(cat <<EOF
+          set -e
+          $(remove_lun "$WWN")
+EOF
+)
+
+  ssh_exec_and_log "$HOST" "$CMD" \
+    "Error flushing out mapping"
+}
+
+function rescan_lun {
+    local HOST="$1"
+    local LUN="$2"
+    local CMD
+
+    CMD=$(cat <<EOF
+                set -e
+                $(rescan_scsi_bus "$LUN")
+EOF
+)
+
+    ssh_exec_and_log "$HOST" "$CMD" \
+      "Error registering remote $LUN to $HOST"
+}
+
+function map_lun {
+    local HOST="$1"
+    local LUN="$2"
+    local WWN="$3"
+    local DST_DIR="$4"
+    local DST_PATH="$5"
+    local CMD
+
+    CMD=$(cat <<EOF
+        set -e
+        mkdir -p "$DST_DIR"
+        $(discover_lun "$LUN" "$WWN")
+        ln -sf "\$DEV" "$DST_PATH"
+EOF
+)
+
+    ssh_exec_and_log "$HOST" "$CMD" \
+        "Error registering $WWN to $HOST"
+}
+
+function map_and_copy_to_lun {
+    local HOST="$1"
+    local SRC_WWN="$2"
+    local LUN="$3"
+    local WWN="$4"
+    local DEV SRC_DEV CMD
+
+    SRC_DEV="/dev/mapper/3$SRC_WWN"
+    DEV="/dev/mapper/3$WWN"
+
+    CMD=$(cat <<EOF
+        set -e
+        $(discover_lun "$LUN" "$WWN")
 EOF
+)
+
+    log "Map $WWN to $HOST"
+    ssh_exec_and_log "$HOST" "$CMD" \
+        "Error mapping $WWN to $HOST"
+
+
+    CMD=$(cat <<EOF
+        set -e -o pipefail
+        dd \if=$SRC_DEV of=$DEV bs=${DD_BLOCK_SIZE:-64k} oflag=direct
+EOF
+)
+
+    log "Copy $SRC_WWN to $WWN"
+    ssh_exec_and_log "$HOST" "$CMD" \
+         "Error copy from $SRC_WWN to $WWN"
+}
+
+function unexport_vv {
+    local NAME="$1"
+    local HOST="$2"
+    local RC="${3:-NO}"
+    local RESULT
+
+    if [[ "$RC" == "YES" ]]; then
+        log "Unexporting remote $NAME from $HOST"
+        RESULT=$($PY3PAR unexportVV -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" -n "$NAME" \
+                                    -hs "$HOST" -sapi "$SEC_API_ENDPOINT" -sip "$SEC_IP" -rc "$RC")
+    else
+        log "Unexporting $NAME from $HOST"
+        RESULT=$($PY3PAR unexportVV -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" -n "$NAME" \
+                                    -hs "$HOST")
+    fi
+
+    if [ $? -ne 0 ]; then
+      error_message "Error unexporting VV: $RESULT"
+      kill -s TERM $TOP_PID
+    fi
 }
+
+function export_vv {
+    local NAME="$1"
+    local HOST="$2"
+    local RC="${3:-NO}"
+    local LUN
+
+    if [[ "$RC" == "YES" ]]; then
+        log "Mapping remote $NAME to $HOST"
+        LUN=$($PY3PAR exportVV -a "$API_ENDPOINT" -i "$IP" -sapi "$SEC_API_ENDPOINT" -sip "$SEC_IP" -s "$SECURE" \
+                               -u "$USERNAME" -p "$PASSWORD" -n "$NAME" -hs "$HOST" -rc "YES")
+    else
+        log "Mapping $NAME to $HOST"
+        LUN=$($PY3PAR exportVV -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" -n "$NAME" \
+                               -hs "$HOST")
+    fi
+
+    if [ $? -ne 0 ]; then
+      error_message "$LUN"
+      kill -s TERM $TOP_PID
+    fi
+
+    echo "$LUN"
+}
+
+function delete_vm_clone_vv {
+  local API_ENDPOINT="$1"
+  local IP="$2"
+  local VMID="$3"
+  local DISK_ID="$4"
+  local DEL
+
+  DEL=$($PY3PAR deleteVmClone -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" \
+                              -nt "$NAMING_TYPE" -vi "$VMID" -id "$DISK_ID")
+
+  if [ $? -ne 0 ]; then
+      error_message "$DEL"
+      kill -s TERM $TOP_PID
+    fi
+}
+
+function host_exists {
+    local HOST="$1"
+    local HOST_3PAR
+
+    HOST_3PAR=$($PY3PAR hostExists -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" -hs "$HOST")
+
+    if [ $? -ne 0 ]; then
+      error_message "$HOST_3PAR"
+      kill -s TERM $TOP_PID
+    fi
+
+    echo "$HOST_3PAR"
+}
+
+function remove_vv_from_rcg {
+    local NAME="$1"
+    local VMID="$2"
+    local RCG
+
+    log "Remove disk from Remote Copy group"
+
+    RCG=$($PY3PAR deleteVolumeFromRCGroup -a "$API_ENDPOINT" -i "$IP" -sapi "$SEC_API_ENDPOINT" -sip "$SEC_IP" \
+                                -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" -nt "$NAMING_TYPE" -n "$NAME" -vi "$VMID")
+
+    if [ $? -ne 0 ]; then
+      error_message "$RCG"
+      kill -s TERM $TOP_PID
+    fi
+}
+
+function add_vv_to_rcg {
+    local NAME="$1"
+    local VMID="$2"
+    local RCG
+
+    log "Create remote copy group"
+
+    RCG=$($PY3PAR addVolumeToRCGroup -a "$API_ENDPOINT" -i "$IP" -sapi "$SEC_API_ENDPOINT" -sip "$SEC_IP" -s "$SECURE" \
+                -u "$USERNAME" -p "$PASSWORD" -nt "$NAMING_TYPE" -c "$CPG" -sc "$SEC_CPG" -rcm "$REMOTE_COPY_MODE" \
+                -n "$NAME" -vi "$VMID")
+
+    if [ $? -ne 0 ]; then
+      error_message "$RCG"
+      kill -s TERM $TOP_PID
+    fi
+
+    log "$RCG"
+}
+
+function remove_vv_from_vvset {
+    local NAME="$1"
+    local VMID="$2"
+    local VVSET
+
+    log "Remove disk from VM VV Set"
+    VVSET=$($PY3PAR deleteVolumeFromVVSet -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" \
+                                          -nt "$NAMING_TYPE" -n "$NAME" -vi "$VMID")
+
+    if [ $? -ne 0 ]; then
+      error_message "$VVSET"
+      kill -s TERM $TOP_PID
+    fi
+}
+
+function add_vv_to_vvset {
+    local NAME="$1"
+    local VMID="$2"
+    local VVSET
+
+    log "Add disk to VM VV Set"
+    VVSET=$($PY3PAR addVolumeToVVSet -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" \
+                                     -nt "$NAMING_TYPE" -n "$NAME" -vi "$VMID")
+
+    if [ $? -ne 0 ]; then
+      error_message "$VVSET"
+      kill -s TERM $TOP_PID
+    fi
+}
+
+function get_vm_clone_vv_source {
+    local API_ENDPOINT="$1"
+    local IP="$2"
+    local VMID="$3"
+    local DISK_ID="$4"
+    local NAME_WWN
+
+    NAME_WWN=$($PY3PAR getVmClone -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" \
+                                  -nt "$NAMING_TYPE" -vi "$VMID" -id "$DISK_ID")
+
+    if [ $? -ne 0 ]; then
+      error_message "$NAME_WWN"
+      kill -s TERM $TOP_PID
+    fi
+
+    echo "$NAME_WWN"
+}
+
+function create_qos_policy {
+    local VMID="$1"
+    local QOS
+
+    log "Create QoS Policy"
+    QOS=$($PY3PAR createQosPolicy -a "$API_ENDPOINT" -i "$IP" -sapi "$SEC_API_ENDPOINT" -sip "$SEC_IP" -s "$SECURE" \
+        -u "$USERNAME" -p "$PASSWORD" -nt "$NAMING_TYPE" -qp "$QOS_PRIORITY" -qxi "$QOS_MAX_IOPS" -qmi "$QOS_MIN_IOPS" \
+        -qxb "$QOS_MAX_BW" -qmb "$QOS_MIN_BW" -ql "$QOS_LATENCY" -rc "$REMOTE_COPY" -vi "$VMID")
+
+    if [ $? -ne 0 ]; then
+      error_message "$QOS"
+      kill -s TERM $TOP_PID
+    fi
+}
+
+function disable_qos_policy {
+    local API_ENDPOINT="$1"
+    local IP="$2"
+    local VMID="$3"
+    local QOS
+
+    log "Disable QoS Policy"
+    QOS=$($PY3PAR disableQosPolicy -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" \
+                  -nt "$NAMING_TYPE" -vi "$VMID")
+
+    if [ $? -ne 0 ]; then
+      error_message "$QOS"
+      kill -s TERM $TOP_PID
+    fi
+}
+
+function create_vm_clone_vv {
+    local SRC_NAME="$1"
+    local VMID="$2"
+    local DISK_ID="$3"
+    local SIZE="$4"
+    local COMMENT="$5"
+    local EMPTY="$6"
+    local NAME_WWN
+
+    NAME_WWN=$($PY3PAR createVmClone -a "$API_ENDPOINT" -i "$IP" -s "$SECURE" -u "$USERNAME" -p "$PASSWORD" \
+                        -nt "$NAMING_TYPE" -tpvv "$THIN" -tdvv "$DEDUP" -compr "$COMPRESSION" -sn "$SRC_NAME" -vi "$VMID" \
+                        -id "$DISK_ID" -c "$CPG" -sz "$SIZE" -co "$COMMENT" -e "$EMPTY")
+
+    if [ $? -ne 0 ]; then
+      error_message "$NAME_WWN"
+      kill -s TERM $TOP_PID
+    fi
+
+    echo "$NAME_WWN"
+}
\ No newline at end of file
diff --git a/datastore/3par/snap_delete b/datastore/3par/snap_delete
index 89c1c372c319890bc91abc5db5642c44207c4c1f..e3795fc90f43f99eb603e876ca782280112b58ac 100644
--- a/datastore/3par/snap_delete
+++ b/datastore/3par/snap_delete
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -44,17 +44,41 @@ ID=$2
 
 XPATH="${DRIVER_PATH}/../xpath.rb -b $DRV_ACTION"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
 done < <($XPATH     /DS_DRIVER_ACTION_DATA/IMAGE/TARGET_SNAPSHOT \
-                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NAMING_TYPE)
+                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/RC \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/RC_SYSTEM_DS_ID \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/IP)
 
-unset i
+SNAP_ID="${XPATH_ELEMENTS[j++]}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
 
-SNAP_ID="${XPATH_ELEMENTS[i++]}"
-NAMING_TYPE="${XPATH_ELEMENTS[i++]:-$NAMING_TYPE}"
+if [ "$REMOTE_COPY" == "YES" ]; then
+  #-------------------------------------------------------------------------------
+  # Get system ds information
+  #-------------------------------------------------------------------------------
 
-python ${DRIVER_PATH}/3par.py deleteSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE \
-                                    -id $ID -si $SNAP_ID
\ No newline at end of file
+  unset i j XPATH_ELEMENTS
+
+  while IFS= read -r -d '' element; do
+      XPATH_ELEMENTS[i++]="$element"
+  done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                          /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                          /DATASTORE/TEMPLATE/SEC_IP)
+
+  SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+  SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+
+  python ${DRIVER_PATH}/3par.py deleteSnapshot -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE \
+            -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $ID -si $SNAP_ID -rc $REMOTE_COPY
+else
+  python ${DRIVER_PATH}/3par.py deleteSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
+                                                -nt $NAMING_TYPE -id $ID -si $SNAP_ID
+fi
diff --git a/datastore/3par/snap_flatten b/datastore/3par/snap_flatten
index e572138deeb017e972e0cefde6f2e08ac8590574..539d57583f190aad86587b71b08ae1c4ed6fa45a 100644
--- a/datastore/3par/snap_flatten
+++ b/datastore/3par/snap_flatten
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -44,18 +44,45 @@ ID=$2
 
 XPATH="${DRIVER_PATH}/../xpath.rb -b $DRV_ACTION"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
 done < <($XPATH     /DS_DRIVER_ACTION_DATA/IMAGE/TARGET_SNAPSHOT \
-                    /DS_DRIVER_ACTION_DATA/IMAGE/SOURCE)
+                    /DS_DRIVER_ACTION_DATA/IMAGE/SOURCE \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/RC \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/RC_SYSTEM_DS_ID \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/IP)
+
+SNAP_ID="${XPATH_ELEMENTS[j++]}"
+SOURCE="${XPATH_ELEMENTS[j++]}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
 
-unset i
+NAME=$(get_vv_name "$SOURCE")
 
-SNAP_ID="${XPATH_ELEMENTS[i++]}"
-SOURCE="${XPATH_ELEMENTS[i++]}"
+if [ "$REMOTE_COPY" == "YES" ]; then
+  #-------------------------------------------------------------------------------
+  # Get system ds information
+  #-------------------------------------------------------------------------------
 
-NAME=$(get_vv_name "$SOURCE")
+  unset i j XPATH_ELEMENTS
+
+  while IFS= read -r -d '' element; do
+      XPATH_ELEMENTS[i++]="$element"
+  done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                          /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                          /DATASTORE/TEMPLATE/SEC_IP)
 
-python ${DRIVER_PATH}/3par.py flattenSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -sn $NAME -si $SNAP_ID
\ No newline at end of file
+  SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+  SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+
+  python ${DRIVER_PATH}/3par.py flattenSnapshot -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE \
+          -u $USERNAME -p $PASSWORD -sn $NAME -si $SNAP_ID -rc $REMOTE_COPY
+else
+  python ${DRIVER_PATH}/3par.py flattenSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
+                                                -sn $NAME -si $SNAP_ID
+fi
diff --git a/datastore/3par/snap_revert b/datastore/3par/snap_revert
index 8708a84551a3631d0395b5184620cc5c1cf71ee3..88efc0333beeb7e70a07c2fc4331f2c3f6933c35 100644
--- a/datastore/3par/snap_revert
+++ b/datastore/3par/snap_revert
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -44,17 +44,41 @@ ID=$2
 
 XPATH="${DRIVER_PATH}/../xpath.rb -b $DRV_ACTION"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
 done < <($XPATH     /DS_DRIVER_ACTION_DATA/IMAGE/TARGET_SNAPSHOT \
-                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NAMING_TYPE)
+                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/RC \
+                    /DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/RC_SYSTEM_DS_ID \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/IP)
 
-unset i
+SNAP_ID="${XPATH_ELEMENTS[j++]}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
 
-SNAP_ID="${XPATH_ELEMENTS[i++]}"
-NAMING_TYPE="${XPATH_ELEMENTS[i++]:-$NAMING_TYPE}"
+if [ "$REMOTE_COPY" == "YES" ]; then
+  #-------------------------------------------------------------------------------
+  # Get system ds information
+  #-------------------------------------------------------------------------------
 
-python ${DRIVER_PATH}/3par.py revertSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE \
-                                    -id $ID -si $SNAP_ID
\ No newline at end of file
+  unset i j XPATH_ELEMENTS
+
+  while IFS= read -r -d '' element; do
+      XPATH_ELEMENTS[i++]="$element"
+  done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                          /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                          /DATASTORE/TEMPLATE/SEC_IP)
+
+  SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+  SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+
+  python ${DRIVER_PATH}/3par.py revertSnapshot -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE \
+          -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $ID -si $SNAP_ID -rc $REMOTE_COPY -off 1
+else
+  python ${DRIVER_PATH}/3par.py revertSnapshot -a $API_ENDPOINT -i $IP -s $SECURE \
+          -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $ID -si $SNAP_ID -off 1
+fi
diff --git a/etc/datastore/3par/3par.conf b/etc/datastore/3par/3par.conf
index 85d967ab71429f509c3de7974b2d1f3685476c9b..428dafeb67cde7fc279a9780b2ec107d7d9472a5 100644
--- a/etc/datastore/3par/3par.conf
+++ b/etc/datastore/3par/3par.conf
@@ -1,5 +1,5 @@
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                    #
+# Copyright 2022, FeldHost™ (feldhost.net)                                    #
 #                                                                            #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may    #
 # not use this file except in compliance with the License. You may obtain    #
@@ -18,7 +18,9 @@
 DD_BLOCK_SIZE=64k
 
 # 3PAR WSAPI Endpoint
+# Secondary 3PAR WSAPI Endpoint (used for Remote Copy)
 API_ENDPOINT="http://{IP}:8008/api/v1"
+SEC_API_ENDPOINT="http://{SEC_IP}:8008/api/v1"
 
 # Only valid SSL certificates
 # SSL certification verification is defaulted to False. In order to
@@ -26,14 +28,25 @@ API_ENDPOINT="http://{IP}:8008/api/v1"
 SECURE=NO
 
 # 3PAR IP address for SSH authentication options for the SSH based calls
+# Secondary 3PAR IP address (used for Remote Copy)
 IP="{IP}"
+SEC_IP="{SEC_IP}"
 
 # 3PAR username and password
 USERNAME="{USERNAME}"
 PASSWORD="{PASSWORD}"
 
+# Enable Remote Copy
+REMOTE_COPY=NO
+
+# Remote Copy mode
+# SYNC|PERIODIC|ASYNC
+REMOTE_COPY_MODE="SYNC"
+
 # Default CPG to use. Can be overwritten in datastore template
+# Default CPG to use on secondary system
 CPG="SSD_r6"
+SEC_CPG="SSD_r6"
 
 # Use of thin volumes. By default enabled. You need thin provisioning license
 # Configurable in datastore template
diff --git a/hooks/3par/image_persistent.sh b/hooks/3par/image_persistent.sh
new file mode 100644
index 0000000000000000000000000000000000000000..41fb68dbfed62a0966c40d4a57f4f1083cc22e8c
--- /dev/null
+++ b/hooks/3par/image_persistent.sh
@@ -0,0 +1,145 @@
+#!/bin/bash
+
+# -------------------------------------------------------------------------- #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
+#                                                                            #
+# Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
+#                                                                            #
+# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
+# not use this file except in compliance with the License. You may obtain    #
+# a copy of the License at                                                   #
+#                                                                            #
+# http://www.apache.org/licenses/LICENSE-2.0                                 #
+#                                                                            #
+# Unless required by applicable law or agreed to in writing, software        #
+# distributed under the License is distributed on an "AS IS" BASIS,          #
+# 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.                                             #
+# -------------------------------------------------------------------------- #
+
+###############################################################################
+# This script should be called using hook when image persistent state is changed
+###############################################################################
+
+# Hook template
+# NAME      = 3par-image-persistent
+# TYPE      = api
+# COMMAND   = "3par/image_persistent.sh"
+# ARGUMENTS = $API
+# CALL      = "one.image.persistent"
+
+
+# ------------ Set up the environment to source common tools ------------
+
+if [ -z "${ONE_LOCATION}" ]; then
+    TMCOMMON=/var/lib/one/remotes/tm/tm_common.sh
+else
+    TMCOMMON=$ONE_LOCATION/var/remotes/tm/tm_common.sh
+fi
+
+. $TMCOMMON
+
+DRIVER_PATH=$(dirname $0)
+
+source ${DRIVER_PATH}/../../etc/datastore/3par/3par.conf
+. ${DRIVER_PATH}/../../datastore/3par/scripts_3par.sh
+
+# -------- Get template argument from OpenNebula core ------------
+
+API=$1
+
+#-------------------------------------------------------------------------------
+# Get dest ds information
+#-------------------------------------------------------------------------------
+
+XPATH="${DRIVER_PATH}/../../datastore/xpath.rb -b $API"
+
+unset i j XPATH_ELEMENTS
+
+while IFS= read -r -d '' element; do
+    XPATH_ELEMENTS[i++]="$element"
+done < <($XPATH  /CALL_INFO/PARAMETERS/PARAMETER[2]/VALUE \
+                 /CALL_INFO/PARAMETERS/PARAMETER[3]/VALUE \
+                 /CALL_INFO/PARAMETERS/PARAMETER[4]/VALUE)
+
+IMAGE_ID="${XPATH_ELEMENTS[j++]}"
+PERSISTENT="${XPATH_ELEMENTS[j++]}"
+SUCCESS="${XPATH_ELEMENTS[j++]}"
+
+# api call for changing image persistant state was successfull
+if [ "$SUCCESS" == "true" ]; then
+    XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
+
+    #-------------------------------------------------------------------------------
+    # Get image datastore id and source
+    #-------------------------------------------------------------------------------
+    unset i j XPATH_ELEMENTS
+
+    while IFS= read -r -d '' element; do
+        XPATH_ELEMENTS[i++]="$element"
+    done < <(oneimage show -x $IMAGE_ID| $XPATH /IMAGE/DATASTORE_ID)
+
+    DSID="${XPATH_ELEMENTS[j++]}"
+
+    #-------------------------------------------------------------------------------
+    # Get image ds information
+    #-------------------------------------------------------------------------------
+    unset i j XPATH_ELEMENTS
+
+    while IFS= read -r -d '' element; do
+        XPATH_ELEMENTS[i++]="$element"
+    done < <(onedatastore show -x $DSID | $XPATH \
+                                /DATASTORE/TEMPLATE/API_ENDPOINT \
+                                /DATASTORE/TEMPLATE/IP \
+                                /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                                /DATASTORE/TEMPLATE/SEC_IP \
+                                /DATASTORE/TEMPLATE/REMOTE_COPY \
+                                /DATASTORE/TEMPLATE/NAMING_TYPE \
+                                /DATASTORE/TEMPLATE/CPG \
+                                /DATASTORE/TEMPLATE/SEC_CPG)
+
+    API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+    IP="${XPATH_ELEMENTS[j++]:-$IP}"
+    SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+    SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+    REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+    NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
+    CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
+    SEC_CPG="${XPATH_ELEMENTS[j++]:-$SEC_CPG}"
+
+    if [ "$REMOTE_COPY" == "YES" ]; then
+        NAME="$NAMING_TYPE.one.$IMAGE_ID.vv"
+
+        if [ "$PERSISTENT" == "0" ]; then
+            log "Add image to remote copy group"
+            RCG=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py addVolumeToRCGroup -a $API_ENDPOINT -i $IP \
+              -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME \
+              -c $CPG -sc $SEC_CPG -rcm $REMOTE_COPY_MODE -rcgn "$NAMING_TYPE.one.ds.$DSID" -rcha "NO")
+
+            if [ $? -ne 0 ]; then
+              error_message "$RCG"
+              exit 1
+            fi
+        else
+            log "Remove disk from Remote Copy group"
+            RCG=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVolumeFromRCGroup -a $API_ENDPOINT -i $IP \
+                -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME \
+                -rcgn "$NAMING_TYPE.one.ds.$DSID")
+
+            if [ $? -ne 0 ]; then
+              error_message "$RCG"
+              exit 1
+            fi
+
+            log "Remove disk from remote system"
+            DEL=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVV -a $SEC_API_ENDPOINT -i $SEC_IP \
+                    -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $IMAGE_ID)
+
+            if [ $? -ne 0 ]; then
+              error_message "$DEL"
+              exit 1
+            fi
+        fi
+    fi
+fi
diff --git a/install.sh b/install.sh
index 3d050ccb18e57044acbfe969b67958f524f8f185..e7c0775a4e8f2c51d26762640878e80ef01d23d7 100755
--- a/install.sh
+++ b/install.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2015-2018, Storpool (storpool.com)                      #
 #                                                                            #
diff --git a/scripts/install-5.6.0.sh b/scripts/install-5.6.0.sh
index 789df434251b3edfd0fccb61c0c45dcdfd1ec824..b3fd86babe7c7ab776272c7431e7a74f0e3edff6 100755
--- a/scripts/install-5.6.0.sh
+++ b/scripts/install-5.6.0.sh
@@ -20,8 +20,8 @@
 
 end_msg=
 
-# install datastore and tm MAD
-for MAD in datastore tm; do
+# install datastore and tm MAD, hooks
+for MAD in datastore tm hooks; do
     M_DIR="${ONE_VAR}/remotes/${MAD}"
     echo "*** Installing ${M_DIR}/3par ..."
     mkdir -pv "${M_DIR}/3par"
@@ -35,6 +35,23 @@ cp $CP_ARG "$CWD/vmm/kvm/"snapshot_*-3par "${ONE_VAR}/remotes/vmm/kvm/"
 chmod a+x "${ONE_VAR}/remotes/vmm/kvm/"snapshot_*-3par
 chown oneadmin: "${ONE_VAR}/remotes/vmm/kvm/"snapshot_*-3par
 
+echo "*** Copy VMM deploy and attach_disk scripts to ${ONE_VAR}/remotes/vmm/kvm/ ..."
+cp $CP_ARG "$CWD/vmm/kvm/deploy" "${ONE_VAR}/remotes/vmm/kvm/"
+cp $CP_ARG "$CWD/vmm/kvm/attach_disk" "${ONE_VAR}/remotes/vmm/kvm/"
+cp $CP_ARG "$CWD/vmm/kvm/restore" "${ONE_VAR}/remotes/vmm/kvm/"
+chmod a+x "${ONE_VAR}/remotes/vmm/kvm/deploy"
+chmod a+x "${ONE_VAR}/remotes/vmm/kvm/attach_disk"
+chmod a+x "${ONE_VAR}/remotes/vmm/kvm/restore"
+chown oneadmin: "${ONE_VAR}/remotes/vmm/kvm/deploy"
+chown oneadmin: "${ONE_VAR}/remotes/vmm/kvm/attach_disk"
+chown oneadmin: "${ONE_VAR}/remotes/vmm/kvm/restore"
+
+echo "*** Copy checkMultipath.py and dmmp.py scripts to ${ONE_VAR}/remotes/vmm/ ..."
+cp $CP_ARG "$CWD/vmm/checkMultipath.py" "${ONE_VAR}/remotes/vmm/"
+cp $CP_ARG "$CWD/vmm/dmmp.py" "${ONE_VAR}/remotes/vmm/"
+chown oneadmin: "${ONE_VAR}/remotes/vmm/checkMultipath.py"
+chown oneadmin: "${ONE_VAR}/remotes/vmm/dmmp.py"
+
 # Enable 3PAR in oned.conf
 if grep -q -i 3par /etc/one/oned.conf >/dev/null 2>&1; then
     echo "*** 3PAR is already enabled in /etc/one/oned.conf"
diff --git a/scripts/install-6.2.2.sh b/scripts/install-6.2.2.sh
new file mode 120000
index 0000000000000000000000000000000000000000..e2b366e8a52d1fafd4c9b8f18bc25a3865b22ab1
--- /dev/null
+++ b/scripts/install-6.2.2.sh
@@ -0,0 +1 @@
+install-5.6.0.sh
\ No newline at end of file
diff --git a/scripts/install-6.4.1.sh b/scripts/install-6.4.1.sh
new file mode 120000
index 0000000000000000000000000000000000000000..e2b366e8a52d1fafd4c9b8f18bc25a3865b22ab1
--- /dev/null
+++ b/scripts/install-6.4.1.sh
@@ -0,0 +1 @@
+install-5.6.0.sh
\ No newline at end of file
diff --git a/tm/3par/clone b/tm/3par/clone
index 2d98157f38567e66a80bc06265e99c4e77b043ff..e917a608cc679b05a46c04c3b79c183adb7aad57 100644
--- a/tm/3par/clone
+++ b/tm/3par/clone
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -79,7 +79,7 @@ done < <(onevm show -x $VMID| $XPATH \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/ORIGINAL_SIZE \
                     /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-VM_NAME="${XPATH_ELEMENTS[j++]}"
+VM_NAME="${XPATH_ELEMENTS[j++]//[^A-Za-z0-9\[\]() _~+-]/}"
 SIZE="${XPATH_ELEMENTS[j++]}"
 ORIGINAL_SIZE="${XPATH_ELEMENTS[j++]}"
 SYS_DSID="${XPATH_ELEMENTS[j++]}"
@@ -93,11 +93,15 @@ unset i j XPATH_ELEMENTS
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
 done < <(onedatastore show -x $SYS_DSID| $XPATH \
+                    /DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/IP \
+                    /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/SEC_IP \
+                    /DATASTORE/TEMPLATE/REMOTE_COPY \
                     /DATASTORE/TEMPLATE/CPG \
                     /DATASTORE/TEMPLATE/THIN \
                     /DATASTORE/TEMPLATE/DEDUP \
                     /DATASTORE/TEMPLATE/COMPRESSION \
-                    /DATASTORE/TEMPLATE/NAMING_TYPE \
                     /DATASTORE/TEMPLATE/QOS_ENABLE \
                     /DATASTORE/TEMPLATE/QOS_PRIORITY \
                     /DATASTORE/TEMPLATE/QOS_MAX_IOPS \
@@ -106,11 +110,15 @@ done < <(onedatastore show -x $SYS_DSID| $XPATH \
                     /DATASTORE/TEMPLATE/QOS_MIN_BW \
                     /DATASTORE/TEMPLATE/QOS_LATENCY)
 
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
 CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
 THIN="${XPATH_ELEMENTS[j++]:-$THIN}"
 DEDUP="${XPATH_ELEMENTS[j++]:-$DEDUP}"
 COMPRESSION="${XPATH_ELEMENTS[j++]:-$COMPRESSION}"
-NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
 QOS_ENABLE="${XPATH_ELEMENTS[j++]:-$QOS_ENABLE}"
 QOS_PRIORITY="${XPATH_ELEMENTS[j++]:-$QOS_PRIORITY}"
 QOS_MAX_IOPS="${XPATH_ELEMENTS[j++]:-$QOS_MAX_IOPS}"
@@ -122,7 +130,7 @@ QOS_LATENCY="${XPATH_ELEMENTS[j++]:-$QOS_LATENCY}"
 #-------------------------------------------------------------------------------
 # Start actions
 #-------------------------------------------------------------------------------
-
+# TODO: remote copy support
 NEW_NAME_WWN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py createVmClone -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
                                 -p $PASSWORD -nt $NAMING_TYPE -tpvv $THIN -tdvv $DEDUP -compr $COMPRESSION -sn $NAME \
                                 -vi $VMID -id $DISK_ID -c $CPG -sz $SIZE -co "$VM_NAME")
@@ -147,7 +155,7 @@ fi
 if [ "$QOS_ENABLE" == "YES" ]; then
     log "Create QoS Policy"
     QOS=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py createQosPolicy -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                        -nt $NAMING_TYPE -n $NEW_NAME -vi $VMID -qp $QOS_PRIORITY -qxi $QOS_MAX_IOPS -qmi $QOS_MIN_IOPS \
+                        -nt $NAMING_TYPE -vi $VMID -qp $QOS_PRIORITY -qxi $QOS_MAX_IOPS -qmi $QOS_MIN_IOPS \
                         -qxb $QOS_MAX_BW -qmb $QOS_MIN_BW -ql $QOS_LATENCY)
 
     if [ $? -ne 0 ]; then
diff --git a/tm/3par/context b/tm/3par/context
index 43ce0fc2efbe552887eee9d9accda528b1bde063..c5e290a41bb3a46dbca1eeba81b3049e97c8e0ea 100644
--- a/tm/3par/context
+++ b/tm/3par/context
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2002-2018, OpenNebula Project, OpenNebula Systems                #
+# Copyright 2002-2022, OpenNebula Project, OpenNebula Systems                #
 #                                                                            #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may    #
 # not use this file except in compliance with the License. You may obtain    #
@@ -51,6 +51,7 @@ function exit_error
 DST_PATH=`arg_path $DST`
 DST_HOST=`arg_host $DST`
 DST_DIR=`dirname $DST_PATH`
+DST_FILE=`basename $DST_PATH`
 
 #-------------------------------------------------------------------------------
 # Create DST path
@@ -65,10 +66,14 @@ log "Generating context block device at $DST"
 
 VM_ID=`basename $DST_DIR`
 ISO_DIR="$DS_DIR/.isofiles/$VM_ID"
-ISO_FILE="$ISO_DIR/$VM_ID.iso"
+ISO_FILE="$VM_ID.iso"
+ISO_PATH="$ISO_DIR/$ISO_FILE"
+
+exec_and_log "rm -rf $ISO_DIR" \
+    "Could not delete temp. to make context dev"
 
 exec_and_set_error "mkdir -p $ISO_DIR" \
-    "Could not create tmp dir to make context dev"
+    "Could not create temp. dir to make context dev"
 [ -n "$ERROR" ] && exit_error
 
 for f in "${SRC[@]}"; do
@@ -93,13 +98,30 @@ for f in "${SRC[@]}"; do
     [ -n "$ERROR" ] && exit_error
 done
 
-exec_and_set_error "$MKISOFS -o $ISO_FILE -V CONTEXT -J -R $ISO_DIR" \
-    "Error creating iso fs"
+# This generates context ISO first into a temporary file and renames to final
+# file, to workaround problem when datastores are on FUSE mounted volume
+# (e.g,. fuse-overlayfs), cached files metadata are not consistent and
+# and tar sparse detection algorithm could identify file as empty.
+MKCONTEXT_CMD=$(cat <<EOF
+    set -e -o pipefail
+    $MKISOFS -o $ISO_PATH.tmp -V CONTEXT -J -R $ISO_DIR
+    mv $ISO_PATH.tmp $ISO_PATH
+EOF
+)
+
+multiline_exec_and_set_error "$MKCONTEXT_CMD" "Error creating iso fs"
 [ -n "$ERROR" ] && exit_error
 
-exec_and_set_error "$SCP $ISO_FILE $DST" "Error copying context ISO to $DST"
+COPY_CMD=$(cat <<EOF
+    set -e -o pipefail
+    $TAR -C $ISO_DIR --transform="flags=r;s|$ISO_FILE|$DST_FILE|" -cSf - $ISO_FILE | \
+        $SSH $DST_HOST "$TAR -xSf - -C $DST_DIR"
+EOF
+)
+
+multiline_exec_and_set_error "$COPY_CMD" "Error copying context ISO to $DST"
 [ -n "$ERROR" ] && exit_error
 
 rm -rf $ISO_DIR > /dev/null 2>&1
 
-exit 0
+exit 0
\ No newline at end of file
diff --git a/tm/3par/cpds b/tm/3par/cpds
index 394eb26d74000b1cad77db868b49313c4f0d7402..d2c47f7b5e5bc9228461a08752023d612a800727 100644
--- a/tm/3par/cpds
+++ b/tm/3par/cpds
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -46,6 +46,10 @@ DRIVER_PATH=$(dirname $0)
 source ${DRIVER_PATH}/../../etc/datastore/3par/3par.conf
 . ${DRIVER_PATH}/../../datastore/3par/scripts_3par.sh
 
+# preserve vars from conf file
+CONF_API_ENDPOINT="$API_ENDPOINT"
+CONF_IP="$IP"
+
 # -------- Get cpds and datastore arguments from OpenNebula core ------------
 
 SRC=$1
@@ -54,19 +58,28 @@ SNAP_ID=$3
 VMID=$4
 DSID=$5
 
+SRC=`fix_dir_slashes $SRC`
+SRC_PATH=`arg_path $SRC`
+SRC_HOST=`arg_host $SRC`
+
 #-------------------------------------------------------------------------------
 # Get dest ds information
 #-------------------------------------------------------------------------------
 
 XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $DSID | $XPATH /DATASTORE/TEMPLATE/CPG)
+done < <(onedatastore show -x $DSID | $XPATH \
+                    /DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/IP \
+                    /DATASTORE/TEMPLATE/CPG)
 
-CPG="${XPATH_ELEMENTS[0]:-$CPG}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$CONF_API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$CONF_IP}"
+CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
 
 #-------------------------------------------------------------------------------
 # Get Image information
@@ -74,39 +87,76 @@ CPG="${XPATH_ELEMENTS[0]:-$CPG}"
 
 DISK_ID=$(basename ${SRC} | cut -d. -f2)
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
 done < <(onevm show -x $VMID| $XPATH \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/IMAGE_ID \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/CLONE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DISK_TYPE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \
                     /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-IMAGE_ID="${XPATH_ELEMENTS[0]}"
-CLONE="${XPATH_ELEMENTS[1]}"
-SYS_DSID="${XPATH_ELEMENTS[2]}"
+IMAGE_ID="${XPATH_ELEMENTS[j++]}"
+CLONE="${XPATH_ELEMENTS[j++]}"
+DISK_TYPE="${XPATH_ELEMENTS[j++]}"
+IMAGE_NAME_WWN="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
+
+NAME=$(get_vv_name "$DST")
+WWN=$(get_vv_wwn "$DST")
 
 #-------------------------------------------------------------------------------
 # Get system ds information
 #-------------------------------------------------------------------------------
 
-unset i XPATH_ELEMENTS
+XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
+
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                    /DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/IP)
 
-NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
-
-NAME=$(get_vv_name "$DST")
+SYS_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$CONF_API_ENDPOINT}"
+SYS_IP="${XPATH_ELEMENTS[j++]:-$CONF_IP}"
 
 log "Copy disk id $DISK_ID attached on VM $VMID to new disk $NAME"
 
-if [ "$CLONE" != "YES" ]; then
+# Not clone and not volatile
+if [ "$CLONE" == "NO" ] && [ "$DISK_TYPE" == "BLOCK" ]; then
   DISK_ID=$IMAGE_ID
-  CLONE=0
 fi
 
-python ${DRIVER_PATH}/../../datastore/3par/3par.py copyVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                    -nt $NAMING_TYPE -id $DISK_ID -vi $VMID -d $NAME -c $CPG -vc $CLONE
\ No newline at end of file
+# check IPs of SYSTEM DS and IMAGE DS
+if [ "$IP" != "$SYS_IP" ]; then
+  log "SYSTEM DS is on remote storage system"
+
+  # Not clone and not volatile
+  if [ "$CLONE" == "NO" ] && [ "$DISK_TYPE" == "BLOCK" ]; then
+    SRC_WWN=$(get_vv_wwn "$IMAGE_NAME_WWN")
+  else
+    # get VM disk WWN
+    SRC_NAME_WWN=$(get_vm_clone_vv_source "$SYS_API_ENDPOINT" "$SYS_IP" "$VMID" "$DISK_ID")
+    SRC_WWN=$(get_vv_wwn "$SRC_NAME_WWN")
+  fi
+
+  # export new vv and clone old one to it
+  LUN=$(export_vv "$NAME" "$SRC_HOST")
+  map_and_copy_to_lun "$SRC_HOST" "$SRC_WWN" "$LUN" "$WWN"
+
+  # unexport new vv
+  unmap_lun "$SRC_HOST" "$WWN"
+  unexport_vv "$NAME" "$SRC_HOST"
+else
+  COPY=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py copyVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
+                                      -nt $NAMING_TYPE -id $DISK_ID -si $SNAP_ID -vi $VMID -d $NAME -c $CPG -vc $CLONE)
+
+  if [ $? -ne 0 ]; then
+    error_message "$COPY"
+    exit 1
+  fi
+fi
diff --git a/tm/3par/delete b/tm/3par/delete
index d67eaf8a7a4c5ada4f086e08218b7b1ca39028e2..57b5a812a3aa256cf4afb45aed7c64c427f81c95 100644
--- a/tm/3par/delete
+++ b/tm/3par/delete
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -96,27 +96,22 @@ DISK_ID=$(echo "$DST_PATH" | $AWK -F. '$NF!=$0 {print $NF}')
 
 XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
 done < <(onevm show -x $VMID| $XPATH \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \
-                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/PERSISTENT \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/CLONE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DISK_TYPE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/IMAGE_ID \
                     /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-NAME_WWN="${XPATH_ELEMENTS[0]}"
-PERSISTENT="${XPATH_ELEMENTS[1]}"
-CLONE="${XPATH_ELEMENTS[2]}"
-DISK_TYPE="${XPATH_ELEMENTS[3]}"
-IMAGE_ID="${XPATH_ELEMENTS[4]}"
-SYS_DSID="${XPATH_ELEMENTS[5]}"
-
-# Exit if persistent
-[ "$PERSISTENT" == "YES" ] && exit 0
+NAME_WWN="${XPATH_ELEMENTS[j++]}"
+CLONE="${XPATH_ELEMENTS[j++]}"
+DISK_TYPE="${XPATH_ELEMENTS[j++]}"
+IMAGE_ID="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
 
 # Not persistent and not clone, so this disk can be used by more VMs at the same time
 if [ "$CLONE" == "NO" ] && [ "$DISK_TYPE" == "BLOCK" ]; then
@@ -134,40 +129,36 @@ if [ "$CLONE" == "NO" ] && [ "$DISK_TYPE" == "BLOCK" ]; then
 fi
 
 #-------------------------------------------------------------------------------
-# Get image ds information
+# Get system ds information
 #-------------------------------------------------------------------------------
 
 unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
-  XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $DSID | $XPATH \
-                        /DATASTORE/TEMPLATE/NAMING_TYPE \
-                        /DATASTORE/TEMPLATE/QOS_ENABLE)
-
-NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
+    XPATH_ELEMENTS[i++]="$element"
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                            /DATASTORE/TEMPLATE/API_ENDPOINT \
+                            /DATASTORE/TEMPLATE/IP \
+                            /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                            /DATASTORE/TEMPLATE/SEC_IP \
+                            /DATASTORE/TEMPLATE/REMOTE_COPY \
+                            /DATASTORE/TEMPLATE/QOS_ENABLE)
+
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
 QOS_ENABLE="${XPATH_ELEMENTS[j++]:-$QOS_ENABLE}"
 
 # if clone or volatile = non-persistent disk, get right name and wwn
 if [ "$CLONE" == "YES" ] || [ "$DISK_TYPE" == "FILE" ]; then
-    #-------------------------------------------------------------------------------
-    # Get system ds information
-    #-------------------------------------------------------------------------------
-
-    unset i j XPATH_ELEMENTS
-
-    while IFS= read -r -d '' element; do
-        XPATH_ELEMENTS[i++]="$element"
-    done < <(onedatastore show -x $SYS_DSID | $XPATH \
-                                /DATASTORE/TEMPLATE/NAMING_TYPE \
-                                /DATASTORE/TEMPLATE/QOS_ENABLE)
-
-    NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
-    QOS_ENABLE="${XPATH_ELEMENTS[j++]:-$QOS_ENABLE}"
+    # Disable remote copy for non-persistent disk
+    REMOTE_COPY="NO"
 
     # get VM disk WWN
-    NAME_WWN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py getVmClone -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
-                                                                -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -id $DISK_ID)
+    NAME_WWN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py getVmClone -a $API_ENDPOINT -i $IP -s $SECURE \
+                                                  -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -id $DISK_ID)
 
     if [ $? -ne 0 ]; then
       error_message "$NAME_WWN"
@@ -179,8 +170,8 @@ NAME=$(get_vv_name "$NAME_WWN")
 WWN=$(get_vv_wwn "$NAME_WWN")
 
 # Check if DST host is 3PAR host, so compute node
-DST_HOST_3PAR=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py hostExists -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
-                                                                        -p $PASSWORD -hs $DST_HOST)
+DST_HOST_3PAR=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py hostExists -a $API_ENDPOINT -i $IP -s $SECURE \
+                                                          -u $USERNAME -p $PASSWORD -hs $DST_HOST)
 
 if [ $? -ne 0 ]; then
   error_message "$DST_HOST_3PAR"
@@ -196,6 +187,7 @@ if [ "$DST_HOST_3PAR" == "1" ]; then
   FLUSH_CMD=$(cat <<EOF
       set -e
       $(remove_lun "$WWN")
+      rm -f "$DST_PATH"
 EOF
 )
   
@@ -204,8 +196,8 @@ EOF
   ssh_exec_and_log "$DST_HOST" "$FLUSH_CMD" \
       "Error flushing out mapping"
   
-  python ${DRIVER_PATH}/../../datastore/3par/3par.py unexportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                        -n $NAME -hs $DST_HOST
+  python ${DRIVER_PATH}/../../datastore/3par/3par.py unexportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
+                                          -p $PASSWORD -n $NAME -hs $DST_HOST
 
   if [ $? -ne 0 ]; then
     error_message "Error unexporting VV"
@@ -213,24 +205,34 @@ EOF
   fi
 fi
 
-if [ "$QOS_ENABLE" == "YES" ]; then
-  log "Delete QoS Policy"
-  QOS=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteQosPolicy -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                    -nt $NAMING_TYPE -n $NAME -vi $VMID)
+if [ "$REMOTE_COPY" == "YES" ] && [ "$REMOTE_COPY_MODE" == "SYNC" ]; then
+    python ${DRIVER_PATH}/../../datastore/3par/3par.py unexportVV -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT \
+                  -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -hs $SRC_HOST -rc $REMOTE_COPY
 
-  if [ $? -ne 0 ]; then
-    error_message "$QOS"
-    exit 1
-  fi
+    if [ $? -ne 0 ]; then
+      error_message "Error unexporting remote VV"
+      exit 1
+    fi
 fi
 
-log "Remove disk from VM VV Set"
-VVSET=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVolumeFromVVSet -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                    -nt $NAMING_TYPE -n $NAME -vi $VMID)
+if [ "$REMOTE_COPY" == "YES" ]; then
+    log "Remove disk from Remote Copy group"
+    RCG=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVolumeFromRCGroup -a $API_ENDPOINT -i $IP \
+        -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -n $NAME -vi $VMID)
 
-if [ $? -ne 0 ]; then
-  error_message "$VVSET"
-  exit 1
+    if [ $? -ne 0 ]; then
+      error_message "$RCG"
+      exit 1
+    fi
+else
+    log "Remove disk from VM VV Set"
+    VVSET=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVolumeFromVVSet -a $API_ENDPOINT -i $IP -s $SECURE \
+                                              -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -n $NAME -vi $VMID)
+
+    if [ $? -ne 0 ]; then
+      error_message "$VVSET"
+      exit 1
+    fi
 fi
 
 # Exit if not clone and not volatile
@@ -240,5 +242,5 @@ fi
 # Delete non-persistent image copy
 #-------------------------------------------------------------------------------
 
-python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVmClone -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                            -nt $NAMING_TYPE -vi $VMID -id $DISK_ID
\ No newline at end of file
+python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVmClone -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
+                                            -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -id $DISK_ID
\ No newline at end of file
diff --git a/tm/3par/failmigrate b/tm/3par/failmigrate
index 9142fe5dc4fc5c6ca1d4b8f788c0b3f8b4e06a7c..8eec56d37e2cefbdf8cff294dd0a743934cbf7df 100755
--- a/tm/3par/failmigrate
+++ b/tm/3par/failmigrate
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
diff --git a/tm/3par/ln b/tm/3par/ln
index 2403a488c61211b6921816cac66013be3c8d8e0d..6fb2905e0347fa4ea113a7e708be462324dc132d 100644
--- a/tm/3par/ln
+++ b/tm/3par/ln
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -29,7 +29,7 @@
 #   - host is the target host to deploy the VM
 #   - remote_system_ds is the path for the system datastore in the host
 #   - vmid is the id of the VM
-#   - dsid is the target datastore (0 is the system datastore)
+#   - dsid is the source datastore (0 is the system datastore)
 
 # ------------ Set up the environment to source common tools ------------
 
@@ -77,31 +77,47 @@ unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onevm show -x $VMID | $XPATH /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DATASTORE_ID)
+done < <(onevm show -x $VMID | $XPATH \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/IMAGE_ID \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/CLONE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/TYPE \
+                    /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-SRC_DSID="${XPATH_ELEMENTS[j++]}"
+IMAGE_ID="${XPATH_ELEMENTS[j++]}"
+CLONE="${XPATH_ELEMENTS[j++]}"
+TYPE="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
 
 #-------------------------------------------------------------------------------
-# Get dest ds information
+# Get image ds information
 #-------------------------------------------------------------------------------
 
 unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
+done < <(onedatastore show -x $DSID | $XPATH \
+                                /DATASTORE/TEMPLATE/CPG \
+                                /DATASTORE/TEMPLATE/IP)
 
-NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
+CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
+IMG_IP="${XPATH_ELEMENTS[j++]:-$IP}"
 
 #-------------------------------------------------------------------------------
-# Get src ds information
+# Get system ds information
 #-------------------------------------------------------------------------------
 
 unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $SRC_DSID | $XPATH \
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                                /DATASTORE/TEMPLATE/API_ENDPOINT \
+                                /DATASTORE/TEMPLATE/IP \
+                                /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                                /DATASTORE/TEMPLATE/SEC_IP \
+                                /DATASTORE/TEMPLATE/REMOTE_COPY \
+                                /DATASTORE/TEMPLATE/SEC_CPG \
                                 /DATASTORE/TEMPLATE/QOS_ENABLE \
                                 /DATASTORE/TEMPLATE/QOS_PRIORITY \
                                 /DATASTORE/TEMPLATE/QOS_MAX_IOPS \
@@ -110,6 +126,12 @@ done < <(onedatastore show -x $SRC_DSID | $XPATH \
                                 /DATASTORE/TEMPLATE/QOS_MIN_BW \
                                 /DATASTORE/TEMPLATE/QOS_LATENCY)
 
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+SEC_CPG="${XPATH_ELEMENTS[j++]:-$SEC_CPG}"
 QOS_ENABLE="${XPATH_ELEMENTS[j++]:-$QOS_ENABLE}"
 QOS_PRIORITY="${XPATH_ELEMENTS[j++]:-$QOS_PRIORITY}"
 QOS_MAX_IOPS="${XPATH_ELEMENTS[j++]:-$QOS_MAX_IOPS}"
@@ -118,24 +140,54 @@ QOS_MAX_BW="${XPATH_ELEMENTS[j++]:-$QOS_MAX_BW}"
 QOS_MIN_BW="${XPATH_ELEMENTS[j++]:-$QOS_MIN_BW}"
 QOS_LATENCY="${XPATH_ELEMENTS[j++]:-$QOS_LATENCY}"
 
+#-------------------------------------------------------------------------------
+# Check for compatibility
+#-------------------------------------------------------------------------------
+if [ "$IMG_IP" != "$IP" ]; then
+    # TODO: add support in ds/clone to clone between storage systems and add hint to the following error_message
+    error_message "The image $IMAGE_ID  is located in different storage system. Can not deploy to this system datastore!"
+    exit 1
+fi
+
 #-------------------------------------------------------------------------------
 # Start actions
 #-------------------------------------------------------------------------------
+# Disk is CDROM
+if [ "$CLONE" = "NO" ] && [ "$TYPE" == "CDROM" ]; then
+  # Disable remote copy
+  REMOTE_COPY="NO"
+fi
 
-log "Add disk to VM VV Set"
-VVSET=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py addVolumeToVVSet -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                    -nt $NAMING_TYPE -n $NAME -vi $VMID)
+if [ "$REMOTE_COPY" == "YES" ]; then
+    log "Create remote copy group"
+    RCG=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py addVolumeToRCGroup -a $API_ENDPOINT -i $IP \
+      -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -n $NAME -vi $VMID \
+      -c $CPG -sc $SEC_CPG -rcm $REMOTE_COPY_MODE)
 
-if [ $? -ne 0 ]; then
-  error_message "$VVSET"
-  exit 1
+    if [ $? -ne 0 ]; then
+      error_message "$RCG"
+      exit 1
+    fi
+
+    log "Add info about RC to image template"
+    image_update $IMAGE_ID "RC=YES RC_SYSTEM_DS_ID=$SYS_DSID"
+else
+    log "Add disk to VM VV Set"
+    VVSET=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py addVolumeToVVSet -a $API_ENDPOINT -i $IP -s $SECURE \
+                  -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -n $NAME -vi $VMID)
+
+    if [ $? -ne 0 ]; then
+      error_message "$VVSET"
+      exit 1
+    fi
 fi
 
 if [ "$QOS_ENABLE" == "YES" ]; then
     log "Create QoS Policy"
-    QOS=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py createQosPolicy -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                        -nt $NAMING_TYPE -n $NAME -vi $VMID -qp $QOS_PRIORITY -qxi $QOS_MAX_IOPS -qmi $QOS_MIN_IOPS \
-                        -qxb $QOS_MAX_BW -qmb $QOS_MIN_BW -ql $QOS_LATENCY)
+    QOS=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py createQosPolicy -a $API_ENDPOINT -i $IP \
+          -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -vi $VMID \
+          -qp $QOS_PRIORITY -qxi $QOS_MAX_IOPS -qmi $QOS_MIN_IOPS -qxb $QOS_MAX_BW -qmb $QOS_MIN_BW -ql $QOS_LATENCY \
+          -rc $REMOTE_COPY)
 
     if [ $? -ne 0 ]; then
       error_message "$QOS"
@@ -143,6 +195,27 @@ if [ "$QOS_ENABLE" == "YES" ]; then
     fi
 fi
 
+if [ "$REMOTE_COPY" == "YES" ] && [ "$REMOTE_COPY_MODE" == "SYNC" ]; then
+    log "Mapping remote $SRC to $DST_HOST"
+
+    LUN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py exportVV -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT \
+                    -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -hs $DST_HOST -rc $REMOTE_COPY)
+
+    if [ $? -ne 0 ]; then
+      error_message "$LUN"
+      exit 1
+    fi
+
+    RESCAN_CMD=$(cat <<EOF
+        set -e
+        $(rescan_scsi_bus "$LUN")
+EOF
+)
+
+    ssh_exec_and_log "$DST_HOST" "$RESCAN_CMD" \
+      "Error registering remote $SRC to $DST_HOST"
+fi
+
 log "Mapping $SRC to $DST_HOST"
 
 LUN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py exportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
diff --git a/tm/3par/mkimage b/tm/3par/mkimage
index 2e0ad1c0396aa9c39724c19cd864ac836505705f..07170bf01d3d80395235d4330d18fce53737e7a3 100755
--- a/tm/3par/mkimage
+++ b/tm/3par/mkimage
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -71,9 +71,12 @@ unset i j XPATH_ELEMENTS
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
 done < <(onevm show -x $VMID| $XPATH \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/FS \
                     /VM/NAME)
 
-VM_NAME="${XPATH_ELEMENTS[j++]}"
+
+FS="${XPATH_ELEMENTS[j++]}"
+VM_NAME="${XPATH_ELEMENTS[j++]//[^A-Za-z0-9\[\]() _~+-]/}"
 
 #-------------------------------------------------------------------------------
 # Get system ds information
@@ -84,11 +87,16 @@ unset i j XPATH_ELEMENTS
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
 done < <(onedatastore show -x $DSID| $XPATH \
+                    /DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/IP \
+                    /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/SEC_IP \
+                    /DATASTORE/TEMPLATE/REMOTE_COPY \
                     /DATASTORE/TEMPLATE/CPG \
+                    /DATASTORE/TEMPLATE/SEC_CPG \
                     /DATASTORE/TEMPLATE/THIN \
                     /DATASTORE/TEMPLATE/DEDUP \
                     /DATASTORE/TEMPLATE/COMPRESSION \
-                    /DATASTORE/TEMPLATE/NAMING_TYPE \
                     /DATASTORE/TEMPLATE/QOS_ENABLE \
                     /DATASTORE/TEMPLATE/QOS_PRIORITY \
                     /DATASTORE/TEMPLATE/QOS_MAX_IOPS \
@@ -97,11 +105,16 @@ done < <(onedatastore show -x $DSID| $XPATH \
                     /DATASTORE/TEMPLATE/QOS_MIN_BW \
                     /DATASTORE/TEMPLATE/QOS_LATENCY)
 
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
 CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
+SEC_CPG="${XPATH_ELEMENTS[j++]:-$SEC_CPG}"
 THIN="${XPATH_ELEMENTS[j++]:-$THIN}"
 DEDUP="${XPATH_ELEMENTS[j++]:-$DEDUP}"
 COMPRESSION="${XPATH_ELEMENTS[j++]:-$COMPRESSION}"
-NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
 QOS_ENABLE="${XPATH_ELEMENTS[j++]:-$QOS_ENABLE}"
 QOS_PRIORITY="${XPATH_ELEMENTS[j++]:-$QOS_PRIORITY}"
 QOS_MAX_IOPS="${XPATH_ELEMENTS[j++]:-$QOS_MAX_IOPS}"
@@ -113,7 +126,7 @@ QOS_LATENCY="${XPATH_ELEMENTS[j++]:-$QOS_LATENCY}"
 #-------------------------------------------------------------------------------
 # Start actions
 #-------------------------------------------------------------------------------
-
+# TODO: remote copy support
 NEW_NAME_WWN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py createVmVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
                                 -p $PASSWORD -nt $NAMING_TYPE -tpvv $THIN -tdvv $DEDUP -compr $COMPRESSION \
                                 -vi $VMID -id $DISK_ID -c $CPG -sz $SIZE -co "$VM_NAME")
@@ -138,7 +151,7 @@ fi
 if [ "$QOS_ENABLE" == "YES" ]; then
     log "Create QoS Policy"
     QOS=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py createQosPolicy -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                        -nt $NAMING_TYPE -n $NEW_NAME -vi $VMID -qp $QOS_PRIORITY -qxi $QOS_MAX_IOPS -qmi $QOS_MIN_IOPS \
+                        -nt $NAMING_TYPE -vi $VMID -qp $QOS_PRIORITY -qxi $QOS_MAX_IOPS -qmi $QOS_MIN_IOPS \
                         -qxb $QOS_MAX_BW -qmb $QOS_MIN_BW -ql $QOS_LATENCY)
 
     if [ $? -ne 0 ]; then
@@ -157,6 +170,11 @@ if [ $? -ne 0 ]; then
   exit 1
 fi
 
+# Ensure filesystem for raw disks
+if [ "$FSTYPE" == "raw" ] && [ -n "$FS" ]; then
+  FSTYPE=$FS
+fi
+
 DISCOVER_CMD=$(cat <<EOF
     set -e
     mkdir -p "$DST_DIR"
@@ -165,6 +183,8 @@ DISCOVER_CMD=$(cat <<EOF
 
     if [ "$FSTYPE" == "swap" ]; then
         sudo /sbin/mkswap -L swap "\$DEV"
+    elif [ "$FSTYPE" == "xfs" ] || [ "$FSTYPE" == "ext4" ] || [ "$FSTYPE" == "ext3" ] || [ "$FSTYPE" == "ext2" ]; then
+        sudo /usr/sbin/mkfs -t "$FSTYPE" "\$DEV"
     fi
 EOF
 )
diff --git a/tm/3par/monitor b/tm/3par/monitor
index 7259f8f2dd5b9aa6b91fd3b328376854217cd417..720ec6c69e9de26c6da04b58ad6db2cc083ede50 100644
--- a/tm/3par/monitor
+++ b/tm/3par/monitor
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -44,24 +44,53 @@ ID=$2
 
 XPATH="${DRIVER_PATH}/../../datastore/xpath.rb -b $DRV_ACTION"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CPG \
-                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/NAMING_TYPE \
+done < <($XPATH     /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/IP \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/SEC_IP \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/REMOTE_COPY \
+                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/CPG \
                     /DS_DRIVER_ACTION_DATA/MONITOR_VM_DISKS)
 
-CPG="${XPATH_ELEMENTS[0]:-$CPG}"
-NAMING_TYPE="${XPATH_ELEMENTS[1]:-$NAMING_TYPE}"
-MONITOR_VM_DISKS="${XPATH_ELEMENTS[2]}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
+MONITOR_VM_DISKS="${XPATH_ELEMENTS[j++]}"
+
+if [ -d "${0%/*}/../../im/kvm-probes.d/vm/monitor" ]; then
+  LEGACY_MONITORING=0
+else
+    LEGACY_MONITORING=1
+fi
 
 # ------------ Compute datastore usage -------------
 
-python ${DRIVER_PATH}/../../datastore/3par/3par.py monitorCPG -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
-                                                  -p $PASSWORD -c $CPG -nt $NAMING_TYPE -di $ID -d $MONITOR_VM_DISKS
+MONITOR_DATA=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py monitorCPG -a $API_ENDPOINT -i $IP -s $SECURE \
+                                                                                -u $USERNAME -p $PASSWORD -c $CPG)
+MONITOR_STATUS=$?
 
-if [ $? -ne 0 ]; then
-  error_message "Error monitoring CPG"
-  exit 1
+if [ $MONITOR_VM_DISKS -eq 1 ]; then
+  MONITOR_DATA_VMS=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py monitorVmDisks -a $API_ENDPOINT -i $IP \
+                                  -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -di $ID -lf $LEGACY_MONITORING)
+  MONITOR_VMS_STATUS=$?
 fi
+
+echo $MONITOR_DATA
+
+if [ $MONITOR_VM_DISKS -eq 1 ]; then
+  if [ $LEGACY_MONITORING -eq 0 ]; then
+      send_to_monitor MONITOR_VM $MONITOR_VMS_STATUS -1 "$MONITOR_DATA_VMS"
+  else
+      echo $MONITOR_DATA_VMS
+      exit $MONITOR_VMS_STATUS
+  fi
+fi
+
+exit $MONITOR_STATUS
diff --git a/tm/3par/mv b/tm/3par/mv
index 70a918700dcda3c5e83a16aaca286e996a548d92..a180463950f66ce22e512e6ed919368eca6a2cd8 100644
--- a/tm/3par/mv
+++ b/tm/3par/mv
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -20,7 +20,9 @@
 # -------------------------------------------------------------------------- #
 
 ###############################################################################
-# This script is used to move images/directories across system_ds in different hosts. When used for the system datastore the script will received the directory 
+# This script is used to move images/directories across system_ds in different hosts.
+# When used for the system datastore the script will received the directory
+# This script is call during undeploy action too
 ###############################################################################
 
 # MV <hostA:system_ds/disk.i|hostB:system_ds/disk.i> vmid dsid
@@ -30,6 +32,9 @@
 #   - vmid is the id of the VM
 #   - dsid is the target datastore (0 is the system datastore)
 
+trap "exit 1" TERM
+export TOP_PID=$$
+
 # ------------ Set up the environment to source common tools ------------
 
 if [ -z "${ONE_LOCATION}" ]; then
@@ -45,6 +50,12 @@ DRIVER_PATH=$(dirname $0)
 source ${DRIVER_PATH}/../../etc/datastore/3par/3par.conf
 . ${DRIVER_PATH}/../../datastore/3par/scripts_3par.sh
 
+# preserve vars from conf file
+CONF_API_ENDPOINT="$API_ENDPOINT"
+CONF_IP="$IP"
+CONF_SEC_API_ENDPOINT="$SEC_API_ENDPOINT"
+CONF_SEC_IP="$SEC_IP"
+
 # -------- Get mv and datastore arguments from OpenNebula core ------------
 
 SRC=$1
@@ -74,8 +85,10 @@ fi
 
 LCM_STATE=`lcm_state`
 
+# moving system datastore deployment files, not a disk
 if [ `is_disk $DST_PATH` -eq 0 ]; then
     # VM is in unknown state, SRC_HOST probably in failed state
+    # PROLOG_MIGRATE_UNKNOWN PROLOG_MIGRATE_UNKNOWN_FAILURE
     if [ $LCM_STATE -eq 60 ] || [ $LCM_STATE -eq 61 ]; then
       log "Not moving files from $SRC_HOST in FT mode"
       exit 0
@@ -107,131 +120,292 @@ fi
 
 DISK_ID=$(echo "$DST_PATH" | $AWK -F. '{print $NF}')
 
-XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
-
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
 done < <(onevm show -x $VMID| $XPATH \
+                    /VM/NAME \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SIZE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/IMAGE_ID \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/CLONE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/PERSISTENT \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DISK_TYPE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/TYPE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DATASTORE_ID \
                     /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-NAME_WWN="${XPATH_ELEMENTS[0]}"
-IMAGE_ID="${XPATH_ELEMENTS[1]}"
-CLONE="${XPATH_ELEMENTS[2]}"
-PERSISTENT="${XPATH_ELEMENTS[3]}"
-SYS_DSID="${XPATH_ELEMENTS[4]}"
-
-# Disk os clone, so copy was created
-if [ "$CLONE" == "YES" ]; then
-  #-------------------------------------------------------------------------------
-  # Get system ds information
-  #-------------------------------------------------------------------------------
-  
-  unset i XPATH_ELEMENTS
-  
-  while IFS= read -r -d '' element; do
-      XPATH_ELEMENTS[i++]="$element"
-  done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
-  
-  NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
-  
-  # get VM disk WWN
-  NAME_WWN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py getVmClone -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
-                                                                -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -id $DISK_ID)
+VM_NAME="${XPATH_ELEMENTS[j++]//[^A-Za-z0-9\[\]() _~+-]/}"
+SIZE="${XPATH_ELEMENTS[j++]}"
+NAME_WWN="${XPATH_ELEMENTS[j++]}"
+IMAGE_ID="${XPATH_ELEMENTS[j++]}"
+CLONE="${XPATH_ELEMENTS[j++]}"
+PERSISTENT="${XPATH_ELEMENTS[j++]}"
+DISK_TYPE="${XPATH_ELEMENTS[j++]}"
+TYPE="${XPATH_ELEMENTS[j++]}"
+IMG_DSID="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
+
+# preserve original source
+ORG_NAME_WWN="$NAME_WWN"
+
+PREV_SYS_DSID=$(onevm show -x $VMID | xmllint --xpath 'string(/VM/HISTORY_RECORDS/HISTORY[last()-1]/DS_ID)' -)
+
+if [ -z "$PREV_SYS_DSID" ]; then
+  PREV_SYS_DSID=$SYS_DSID
+fi
+
+#-------------------------------------------------------------------------------
+# Get system ds information
+#-------------------------------------------------------------------------------
+unset i j XPATH_ELEMENTS
+
+while IFS= read -r -d '' element; do
+    XPATH_ELEMENTS[i++]="$element"
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                    /DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/IP \
+                    /DATASTORE/TEMPLATE/CPG \
+                    /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/SEC_IP \
+                    /DATASTORE/TEMPLATE/REMOTE_COPY \
+                    /DATASTORE/TEMPLATE/SEC_CPG \
+                    /DATASTORE/TEMPLATE/THIN \
+                    /DATASTORE/TEMPLATE/DEDUP \
+                    /DATASTORE/TEMPLATE/COMPRESSION \
+                    /DATASTORE/TEMPLATE/QOS_ENABLE \
+                    /DATASTORE/TEMPLATE/QOS_PRIORITY \
+                    /DATASTORE/TEMPLATE/QOS_MAX_IOPS \
+                    /DATASTORE/TEMPLATE/QOS_MIN_IOPS \
+                    /DATASTORE/TEMPLATE/QOS_MAX_BW \
+                    /DATASTORE/TEMPLATE/QOS_MIN_BW \
+                    /DATASTORE/TEMPLATE/QOS_LATENCY)
+
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+CPG="${XPATH_ELEMENTS[j++]:-$CPG}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+SEC_CPG="${XPATH_ELEMENTS[j++]:-$SEC_CPG}"
+THIN="${XPATH_ELEMENTS[j++]:-$THIN}"
+DEDUP="${XPATH_ELEMENTS[j++]:-$DEDUP}"
+COMPRESSION="${XPATH_ELEMENTS[j++]:-$COMPRESSION}"
+QOS_ENABLE="${XPATH_ELEMENTS[j++]:-$QOS_ENABLE}"
+QOS_PRIORITY="${XPATH_ELEMENTS[j++]:-$QOS_PRIORITY}"
+QOS_MAX_IOPS="${XPATH_ELEMENTS[j++]:-$QOS_MAX_IOPS}"
+QOS_MIN_IOPS="${XPATH_ELEMENTS[j++]:-$QOS_MIN_IOPS}"
+QOS_MAX_BW="${XPATH_ELEMENTS[j++]:-$QOS_MAX_BW}"
+QOS_MIN_BW="${XPATH_ELEMENTS[j++]:-$QOS_MIN_BW}"
+QOS_LATENCY="${XPATH_ELEMENTS[j++]:-$QOS_LATENCY}"
+
+#-------------------------------------------------------------------------------
+# Get previous system ds information
+#-------------------------------------------------------------------------------
+unset i j XPATH_ELEMENTS
 
-  if [ $? -ne 0 ]; then
-    error_message "$NAME_WWN"
+while IFS= read -r -d '' element; do
+    XPATH_ELEMENTS[i++]="$element"
+done < <(onedatastore show -x $PREV_SYS_DSID | $XPATH \
+                    /DATASTORE/TEMPLATE/API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/IP \
+                    /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                    /DATASTORE/TEMPLATE/SEC_IP \
+                    /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+PREV_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$CONF_API_ENDPOINT}"
+PREV_IP="${XPATH_ELEMENTS[j++]:-$CONF_IP}"
+PREV_SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$CONF_SEC_API_ENDPOINT}"
+PREV_SEC_IP="${XPATH_ELEMENTS[j++]:-$CONF_SEC_IP}"
+PREV_REMOTE_COPY="${XPATH_ELEMENTS[j++]:-NO}"
+
+#-------------------------------------------------------------------------------
+# Check for compatibility
+#-------------------------------------------------------------------------------
+if [ "$PREV_REMOTE_COPY" == "NO" ] && [ "$REMOTE_COPY" == "YES" ] && [ "$PREV_IP" != "$IP" ]; then
+    error_message "You can not deploy to this RC enabled SYSTEM_DS because primary storage is different from actual one.
+    It will cause overwrite from remote image!
+    Please select RC enabled SYSTEM_DS with the same primary storage."
     exit 1
-  fi
 fi
 
+# Overwrite NAME_WWN if disk is clone or volatile
+if [ "$CLONE" = "YES" ] || [ "$DISK_TYPE" == "FILE" ]; then
+  # get VM disk WWN
+  NAME_WWN=$(get_vm_clone_vv_source "$PREV_API_ENDPOINT" "$PREV_IP" "$VMID" "$DISK_ID")
+
+  # Disable remote copy for non-persistent disk
+  REMOTE_COPY="NO"
+  PREV_REMOTE_COPY="NO"
+fi
+
+# extract VV Name and Wwn
 NAME=$(get_vv_name "$NAME_WWN")
 WWN=$(get_vv_wwn "$NAME_WWN")
 
+# is src/dest host registered on 3par? so is it compute node and not frontend?
+IS_SRC_HOST_3PAR=$(host_exists "$SRC_HOST")
+IS_DST_HOST_3PAR=$(host_exists "$DST_HOST")
+
 #-------------------------------------------------------------------------------
-# Start actions
+# Subroutines
 #-------------------------------------------------------------------------------
+function remove_vv {
+    local API_ENDPOINT="$1"
+    local IP="$2"
+    local SEC_API_ENDPOINT="$3"
+    local SEC_IP="$4"
+    local NAME="$5"
+    local WWN="$6"
+    local IS_MIGRATION="${7:-NO}"
+
+    # flush disk only if src host is not in failed state
+    if [ $LCM_STATE -ne 60 ] && [ $LCM_STATE -ne 61 ]; then
+        unmap_lun "$SRC_HOST" "$WWN"
+    fi
 
-SRC_HOST_3PAR=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py hostExists -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
-                                                                        -p $PASSWORD -hs $SRC_HOST)
+    unexport_vv "$NAME" "$SRC_HOST"
 
-if [ $? -ne 0 ]; then
-  error_message "$SRC_HOST_3PAR"
-  exit 1
-fi
+    if [ "$PREV_REMOTE_COPY" == "YES" ] && [ "$REMOTE_COPY_MODE" == "SYNC" ]; then
+        unexport_vv "$NAME" "$SRC_HOST" "YES"
+    fi
 
-DST_HOST_3PAR=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py hostExists -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
-                                                                        -p $PASSWORD -hs $DST_HOST)
+    # VM lcm state is EPILOG_UNDEPLOY
+    # or migrating between RC and non-RC datastore
+    # or migrating between storage systems
+    if [ $LCM_STATE -eq 30 ] || [ "$PREV_REMOTE_COPY" != "$REMOTE_COPY" ] || [ "$IS_MIGRATION" == "YES" ]; then
+        # perform cleanup
+        if [ "$PREV_REMOTE_COPY" == "YES" ]; then
+            remove_vv_from_rcg "$NAME" "$VMID"
+        else
+            remove_vv_from_vvset "$NAME" "$VMID"
+        fi
+    fi
+}
+
+function add_vv {
+    local NAME="$1"
+    local WWN="$2"
+    local IS_MIGRATION="${3:-NO}"
+
+    # VM lcm state is PROLOG_UNDEPLOY
+    # or migrating between RC and non-RC datastore
+    # or migrating between storage systems
+    if [ $LCM_STATE -eq 31 ] || [ "$PREV_REMOTE_COPY" != "$REMOTE_COPY" ] || [ "$IS_MIGRATION" == "YES" ]; then
+        if [ "$REMOTE_COPY" == "YES" ]; then
+            add_vv_to_rcg "$NAME" "$VMID"
+
+            log "Add info about RC to image template"
+            image_update "$IMAGE_ID" "RC=YES RC_SYSTEM_DS_ID=$SYS_DSID"
+        else
+            add_vv_to_vvset "$NAME" "$VMID"
+        fi
+
+        if [ "$QOS_ENABLE" == "YES" ]; then
+            create_qos_policy "$VMID"
+        fi
+
+        if [ "$REMOTE_COPY" == "YES" ] && [ "$REMOTE_COPY_MODE" == "SYNC" ]; then
+            LUN=$(export_vv "$NAME" "$DST_HOST" "YES")
+            rescan_lun "$DST_HOST" "$LUN"
+        fi
+    fi
 
-if [ $? -ne 0 ]; then
-  error_message "$DST_HOST_3PAR"
-  exit 1
-fi
+    LUN=$(export_vv "$NAME" "$DST_HOST")
+    map_lun "$DST_HOST" "$LUN" "$WWN" "$DST_DIR" "$DST_PATH"
+}
+
+#-------------------------------------------------------------------------------
+# Start actions
+#-------------------------------------------------------------------------------
 
-# Not persistent and not clone, so this disk can be used by more VMs at the same time
+# CDROM, so this disk can be used by more VMs at the same time
 CAN_UNMAP=1
-if [ "$PERSISTENT" != "YES" ] && [ "$CLONE" == "NO" ]; then
+if [ "$CLONE" == "NO" ] && [ "$TYPE" == "CDROM" ]; then
     # check if disk is in use by other VMs
-    unset i XPATH_ELEMENTS
-
-    while IFS= read -r -d '' element; do
-        XPATH_ELEMENTS[i++]="$element"
-    done < <(oneimage show -x $IMAGE_ID| $XPATH /IMAGE/RUNNING_VMS)
-
-    RUNNING_VMS="${XPATH_ELEMENTS[0]}"
+    RUNNING_VMS=$(get_image_running_vms_count "$IMAGE_ID")
 
     # image is used, so can't unmap
     [ "$RUNNING_VMS" != "1" ] && CAN_UNMAP=0
 fi
 
-if [ "$SRC_HOST_3PAR" == "1" ] && [ "$CAN_UNMAP" == "1" ]; then
-    log "Unmapping $WWN from $SRC_HOST"
+# moving persistent disk between storage systems using remote copy, full switchover
+# only if VM lcm state not EPILOG_UNDEPLOY
+if [ $LCM_STATE -ne 30 ] && [ "$PERSISTENT" == "YES" ] && [ "$IP" != "$PREV_IP" ] && [ "$REMOTE_COPY" == "YES" ] && [ "$PREV_REMOTE_COPY" == "YES" ]; then
+    # src host is compute node
+    if [ "$IS_SRC_HOST_3PAR" == "1" ] && [ "$CAN_UNMAP" == "1" ]; then
+        remove_vv "$PREV_API_ENDPOINT" "$PREV_IP" "$PREV_SEC_API_ENDPOINT" "$PREV_SEC_IP" "$NAME" "$WWN" "YES"
+    fi
 
-    # src host in failed state, can not flush disk before unexport
-    if [ $LCM_STATE -ne 60 ] && [ $LCM_STATE -ne 61 ]; then
-        FLUSH_CMD=$(cat <<EOF
-          set -e
-          $(remove_lun "$WWN")
-EOF
-)
+    # dest host is compute node
+    if [ "$IS_DST_HOST_3PAR" == "1" ]; then
+        add_vv "$NAME" "$WWN" "YES"
+    fi
+
+    exit 0
+fi
 
-        ssh_exec_and_log "$SRC_HOST" "$FLUSH_CMD" \
-            "Error flushing out mapping"
+# if moving to different 3par, but it is not a CDROM and not migrating between RC and non-RC datastore
+# only if VM lcm state not EPILOG_UNDEPLOY
+if [ $LCM_STATE -ne 30 ] && [ "$IP" != "$PREV_IP" ] && [ "$TYPE" != "CDROM" ] && [ "$PREV_REMOTE_COPY" == "$REMOTE_COPY" ]; then
+    if [ "$CLONE" != "YES" ] && [ "$DISK_TYPE" != "FILE" ]; then
+        error_message "Not supported action yet! Can not move persistent disk between storage systems!"
+        exit 1
     fi
 
-    python ${DRIVER_PATH}/../../datastore/3par/3par.py unexportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                                -n $NAME -hs $SRC_HOST
+    # and disk is clone or volatile, lets create new vv
+    SRC_NAME=$(get_vv_name "$ORG_NAME_WWN")
+    NEW_NAME_WWN=$(create_vm_clone_vv "$SRC_NAME" "$VMID" "$DISK_ID" "$SIZE" "$VM_NAME" "YES")
+    NEW_NAME=$(get_vv_name "$NEW_NAME_WWN")
+    NEW_WWN=$(get_vv_wwn "$NEW_NAME_WWN")
 
-    if [ $? -ne 0 ]; then
-      error_message "Error unexporting VV"
-      exit 1
+    # disable qos policy to speed up copy
+    if [ "$QOS_ENABLE" == "YES" ]; then
+        disable_qos_policy "$PREV_API_ENDPOINT" "$PREV_IP" "$VMID"
     fi
-fi
 
-if [ "$DST_HOST_3PAR" == "1" ]; then
-    log "Mapping $NAME_WWN to $DST_HOST"
+    # export new vv and clone old one to it
+    LUN=$(export_vv "$NEW_NAME" "$SRC_HOST")
+    map_and_copy_to_lun "$SRC_HOST" "$WWN" "$LUN" "$NEW_WWN"
 
-    LUN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py exportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                                -n $NAME -hs $DST_HOST)
+    # unexport new vv only if destination host is different
+    if [ "$SRC_HOST" != "$DST_HOST" ]; then
+        unmap_lun "$SRC_HOST" "$NEW_WWN"
+        unexport_vv "$NEW_NAME" "$SRC_HOST"
+    fi
 
-    if [ $? -ne 0 ]; then
-      error_message "$LUN"
-      exit 1
+    # src host is compute node, flush, unexport old vv and delete it
+    if [ "$IS_SRC_HOST_3PAR" == "1" ]; then
+        remove_vv "$PREV_API_ENDPOINT" "$PREV_IP" "$PREV_SEC_API_ENDPOINT" "$PREV_SEC_IP" "$NAME" "$WWN" "YES"
+        delete_vm_clone_vv "$PREV_API_ENDPOINT" "$PREV_IP" "$VMID" "$DISK_ID"
     fi
-    
-    DISCOVER_CMD=$(cat <<EOF
-        set -e
-        mkdir -p "$DST_DIR"
-        $(discover_lun "$LUN" "$WWN")
-        ln -sf "\$DEV" "$DST_PATH"
+
+    # dest host is compute node, export and discover new vv
+    if [ "$IS_DST_HOST_3PAR" == "1" ]; then
+        add_vv "$NEW_NAME" "$NEW_WWN" "YES"
+
+        # update disk symlink
+        CMD=$(cat <<EOF
+          set -e
+          DEV="/dev/mapper/3$NEW_WWN"
+          ln -sf "\$DEV" "$SRC_PATH"
 EOF
 )
-    
-    ssh_exec_and_log "$DST_HOST" "$DISCOVER_CMD" \
-        "Error registering $NAME_WWN to $DST_HOST"
+
+        ssh_exec_and_log "$SRC_HOST" "$CMD" \
+          "Error sym-linking $SRC_PATH on $SRC_HOST"
+    fi
+
+    exit 0
+fi
+
+# src host is compute node
+if [ "$IS_SRC_HOST_3PAR" == "1" ] && [ "$CAN_UNMAP" == "1" ]; then
+    remove_vv "$PREV_API_ENDPOINT" "$PREV_IP" "$PREV_SEC_API_ENDPOINT" "$PREV_SEC_IP" "$NAME" "$WWN"
+fi
+
+# dest host is compute node
+if [ "$IS_DST_HOST_3PAR" == "1" ]; then
+    add_vv "$NAME" "$WWN"
 fi
\ No newline at end of file
diff --git a/tm/3par/mvds b/tm/3par/mvds
index 4216d3212f745149be1149f9eaf9784685a95abc..b337d1c25076b2e7d1c966446d592c5ff0a2b3cb 100644
--- a/tm/3par/mvds
+++ b/tm/3par/mvds
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -72,21 +72,41 @@ fi
 # image already unmapped by undeploy "mv" script
 [ "$SRC_HOST_3PAR" != "1" ] && exit 0
 
+XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
+
 #-------------------------------------------------------------------------------
-# Get target ds information
+# Get image information
 #-------------------------------------------------------------------------------
 
-XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
+unset i j XPATH_ELEMENTS
+
+while IFS= read -r -d '' element; do
+    XPATH_ELEMENTS[i++]="$element"
+done < <(onevm show -x $VMID| $XPATH /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
+
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
+
+#-------------------------------------------------------------------------------
+# Get system ds information
+#-------------------------------------------------------------------------------
 
 unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $DSID | $XPATH \
-                        /DATASTORE/TEMPLATE/NAMING_TYPE \
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                        /DATASTORE/TEMPLATE/API_ENDPOINT \
+                        /DATASTORE/TEMPLATE/IP \
+                        /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                        /DATASTORE/TEMPLATE/SEC_IP \
+                        /DATASTORE/TEMPLATE/REMOTE_COPY \
                         /DATASTORE/TEMPLATE/QOS_ENABLE)
 
-NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
 QOS_ENABLE="${XPATH_ELEMENTS[j++]:-$QOS_ENABLE}"
 
 #-------------------------------------------------------------------------------
@@ -107,22 +127,32 @@ ssh_exec_and_log "$SRC_HOST" "$FLUSH_CMD" \
 python ${DRIVER_PATH}/../../datastore/3par/3par.py unexportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
                                                         -n $NAME -hs $SRC_HOST
 
-if [ "$QOS_ENABLE" == "YES" ]; then
-    log "Delete QoS Policy"
-    QOS=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteQosPolicy -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                        -nt $NAMING_TYPE -n $NAME -vi $VMID)
+if [ "$REMOTE_COPY" == "YES" ] && [ "$REMOTE_COPY_MODE" == "SYNC" ]; then
+    python ${DRIVER_PATH}/../../datastore/3par/3par.py unexportVV -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT \
+                  -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -hs $SRC_HOST -rc $REMOTE_COPY
 
     if [ $? -ne 0 ]; then
-      error_message "$QOS"
+      error_message "Error unexporting remote VV"
       exit 1
     fi
 fi
 
-log "Remove disk from VM VV Set"
-VVSET=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVolumeFromVVSet -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                    -nt $NAMING_TYPE -n $NAME -vi $VMID)
+if [ "$REMOTE_COPY" == "YES" ]; then
+    log "Remove disk from Remote Copy group"
+    RCG=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVolumeFromRCGroup -a $API_ENDPOINT -i $IP \
+        -sapi $SEC_API_ENDPOINT -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -n $NAME -vi $VMID)
 
-if [ $? -ne 0 ]; then
-  error_message "$VVSET"
-  exit 1
-fi
+    if [ $? -ne 0 ]; then
+      error_message "$RCG"
+      exit 1
+    fi
+else
+    log "Remove disk from VM VV Set"
+    VVSET=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVolumeFromVVSet -a $API_ENDPOINT -i $IP -s $SECURE \
+                        -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -n $NAME -vi $VMID)
+
+    if [ $? -ne 0 ]; then
+      error_message "$VVSET"
+      exit 1
+    fi
+fi
\ No newline at end of file
diff --git a/tm/3par/postmigrate b/tm/3par/postmigrate
index ad66cf691a4648db55fd265adfcfe54ebc1e59bf..2a20c2b29f8c4f619e20d2450302c321ebbe47a5 100644
--- a/tm/3par/postmigrate
+++ b/tm/3par/postmigrate
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -70,37 +70,39 @@ fi
 # Unmap luns from host
 #-------------------------------------------------------------------------------
 
-i=1
-while read line; do
-    DISK_IDS[$i]="$line"
-    (( i++ ))
-done < <(onevm show $VMID --all | $GREP -w "DISK_ID" | $CUT -d\" -f2)
+DISK_IDS=$(echo $TEMPLATE_64 | base64 --decode | ${DRIVER_PATH}/../../datastore/xpath.rb --stdin '%m%/VM/TEMPLATE/DISK/DISK_ID')
 
-for j in `seq 1 ${#DISK_IDS[*]}`; do
+for k in $DISK_IDS; do
     XPATH="${DRIVER_PATH}/../../datastore/xpath.rb -b $TEMPLATE_64"
 
-    unset i k XPATH_ELEMENTS
+    unset i j XPATH_ELEMENTS
 
     while IFS= read -r -d '' element; do
         XPATH_ELEMENTS[i++]="$element"
-    done < <($XPATH     /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/SOURCE \
-                        /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/TM_MAD \
-                        /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/CLONE \
-                        /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/READONLY \
-                        /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/IMAGE_ID \
-                        /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/DISK_ID \
+    done < <($XPATH     /VM/TEMPLATE/DISK[DISK_ID=$k]/SOURCE \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/TM_MAD \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/CLONE \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/READONLY \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/IMAGE_ID \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/DISK_ID \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/DISK_TYPE \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/DATASTORE_ID \
                         /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-    NAME_WWN=${XPATH_ELEMENTS[k++]}
-    TM_MAD=${XPATH_ELEMENTS[k++]}
-    CLONE=${XPATH_ELEMENTS[k++]}
-    READONLY=${XPATH_ELEMENTS[k++]}
-    IMAGE_ID=${XPATH_ELEMENTS[k++]}
-    DISK_ID=${XPATH_ELEMENTS[k++]}
-    SYS_DSID=${XPATH_ELEMENTS[k++]}
+    NAME_WWN=${XPATH_ELEMENTS[j++]}
+    TM_MAD=${XPATH_ELEMENTS[j++]}
+    CLONE=${XPATH_ELEMENTS[j++]}
+    READONLY=${XPATH_ELEMENTS[j++]}
+    IMAGE_ID=${XPATH_ELEMENTS[j++]}
+    DISK_ID=${XPATH_ELEMENTS[j++]}
+    DISK_TYPE=${XPATH_ELEMENTS[j++]}
+    IMG_DSID=${XPATH_ELEMENTS[j++]}
+    SYS_DSID=${XPATH_ELEMENTS[j++]}
 
     # Readonly and not clone, so this disk can be used by more VMs at the same time
     if [ "$CLONE" == "NO" ] && [ "$READONLY" == "YES" ]; then
+        XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
+
         # check if disk is in use by other VMs
         unset i XPATH_ELEMENTS
 
@@ -115,19 +117,30 @@ for j in `seq 1 ${#DISK_IDS[*]}`; do
     fi
 
     if [ "$TM_MAD" = "3par" ]; then
-        if [ "$CLONE" == "YES" ]; then
-            #-------------------------------------------------------------------------------
-            # Get system ds information
-            #-------------------------------------------------------------------------------
-
-            unset i j XPATH_ELEMENTS
-
-            while IFS= read -r -d '' element; do
-                XPATH_ELEMENTS[i++]="$element"
-            done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
+        #-------------------------------------------------------------------------------
+        # Get ds information
+        #-------------------------------------------------------------------------------
+        XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
 
-            NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
+        unset i j XPATH_ELEMENTS
 
+        while IFS= read -r -d '' element; do
+            XPATH_ELEMENTS[i++]="$element"
+        done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                          /DATASTORE/TEMPLATE/API_ENDPOINT \
+                          /DATASTORE/TEMPLATE/IP \
+                          /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                          /DATASTORE/TEMPLATE/SEC_IP \
+                          /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+        API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+        IP="${XPATH_ELEMENTS[j++]:-$IP}"
+        SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+        SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+        REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+
+        # if clone or volatile = non-persistent disk
+        if [ "$CLONE" == "YES" ] || [ "$DISK_TYPE" == "FILE" ]; then
             # get VM disk WWN
             NAME_WWN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py getVmClone -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
                                                                         -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -id $DISK_ID)
@@ -136,6 +149,9 @@ for j in `seq 1 ${#DISK_IDS[*]}`; do
               error_message "$NAME_WWN"
               exit 1
             fi
+
+            # Disable remote copy for non-persistent disk
+            REMOTE_COPY="NO"
         fi
 
         NAME=$(get_vv_name "$NAME_WWN")
@@ -159,6 +175,16 @@ EOF
           error_message "Error unexporting VV"
           exit 1
         fi
+
+        if [ "$REMOTE_COPY" == "YES" ] && [ "$REMOTE_COPY_MODE" == "SYNC" ]; then
+            python ${DRIVER_PATH}/../../datastore/3par/3par.py unexportVV -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT \
+                          -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -hs $SRC_HOST -rc $REMOTE_COPY
+
+            if [ $? -ne 0 ]; then
+              error_message "Error unexporting remote VV"
+              exit 1
+            fi
+        fi
     fi
 done
 
diff --git a/tm/3par/premigrate b/tm/3par/premigrate
index 15abbd5479b451bcfaca0d5468f5da73ad171c8d..4708eb0863076337e3558f28f7e3c017273e6b4e 100644
--- a/tm/3par/premigrate
+++ b/tm/3par/premigrate
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -92,46 +92,57 @@ ssh_exec_and_log "$SRC_HOST" "$TAR_SSH" "Error copying disk directory to target
 # Discover luns on dst host
 #--------------------------------------------------------------------------------
 
-i=1
-while read line
-do
-    DISK_IDS[$i]="$line"
-    (( i++ ))
-done < <(onevm show $VMID --all | $GREP -w "DISK_ID" | $CUT -d\" -f2)
+DISK_IDS=$(echo $TEMPLATE_64 | base64 --decode | ${DRIVER_PATH}/../../datastore/xpath.rb --stdin '%m%/VM/TEMPLATE/DISK/DISK_ID')
 
-for j in `seq 1 ${#DISK_IDS[*]}`; do
+for k in $DISK_IDS; do
     XPATH="${DRIVER_PATH}/../../datastore/xpath.rb -b $TEMPLATE_64"
 
-    unset i k XPATH_ELEMENTS
+    unset i j XPATH_ELEMENTS
 
     while IFS= read -r -d '' element; do
         XPATH_ELEMENTS[i++]="$element"
-    done < <($XPATH     /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/SOURCE \
-                        /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/TM_MAD \
-                        /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/CLONE \
-                        /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/DISK_ID \
+    done < <($XPATH     /VM/TEMPLATE/DISK[DISK_ID=$k]/SOURCE \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/TM_MAD \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/CLONE \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/DISK_ID \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/DISK_TYPE \
+                        /VM/TEMPLATE/DISK[DISK_ID=$k]/DATASTORE_ID \
                         /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-    NAME_WWN=${XPATH_ELEMENTS[k++]}
-    TM_MAD=${XPATH_ELEMENTS[k++]}
-    CLONE=${XPATH_ELEMENTS[k++]}
-    DISK_ID=${XPATH_ELEMENTS[k++]}
-    SYS_DSID=${XPATH_ELEMENTS[k++]}
+    NAME_WWN=${XPATH_ELEMENTS[j++]}
+    TM_MAD=${XPATH_ELEMENTS[j++]}
+    CLONE=${XPATH_ELEMENTS[j++]}
+    DISK_ID=${XPATH_ELEMENTS[j++]}
+    DISK_TYPE=${XPATH_ELEMENTS[j++]}
+    IMG_DSID=${XPATH_ELEMENTS[j++]}
+    SYS_DSID=${XPATH_ELEMENTS[j++]}
 
     if [ "$TM_MAD" = "3par" ]; then
-        if [ "$CLONE" == "YES" ]; then
-            #-------------------------------------------------------------------------------
-            # Get system ds information
-            #-------------------------------------------------------------------------------
-
-            unset i j XPATH_ELEMENTS
-
-            while IFS= read -r -d '' element; do
-                XPATH_ELEMENTS[i++]="$element"
-            done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
-
-            NAMING_TYPE="${XPATH_ELEMENTS[j++]:-$NAMING_TYPE}"
 
+        #-------------------------------------------------------------------------------
+        # Get ds information
+        #-------------------------------------------------------------------------------
+        XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
+
+        unset i j XPATH_ELEMENTS
+
+        while IFS= read -r -d '' element; do
+            XPATH_ELEMENTS[i++]="$element"
+        done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                          /DATASTORE/TEMPLATE/API_ENDPOINT \
+                          /DATASTORE/TEMPLATE/IP \
+                          /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                          /DATASTORE/TEMPLATE/SEC_IP \
+                          /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+        API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+        IP="${XPATH_ELEMENTS[j++]:-$IP}"
+        SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+        SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+        REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+
+        # if clone or volatile = non-persistent disk
+        if [ "$CLONE" == "YES" ] || [ "$DISK_TYPE" == "FILE" ]; then
             # get VM disk WWN
             NAME_WWN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py getVmClone -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
                                                                         -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -id $DISK_ID)
@@ -140,11 +151,35 @@ for j in `seq 1 ${#DISK_IDS[*]}`; do
               error_message "$NAME_WWN"
               exit 1
             fi
+
+            # Disable remote copy for non-persistent disk
+            REMOTE_COPY="NO"
         fi
 
         NAME=$(get_vv_name "$NAME_WWN")
         WWN=$(get_vv_wwn "$NAME_WWN")
 
+        if [ "$REMOTE_COPY" == "YES" ] && [ "$REMOTE_COPY_MODE" == "SYNC" ]; then
+            log "Mapping remote $NAME_WWN to $DST_HOST"
+
+            LUN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py exportVV -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT \
+                          -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -hs $DST_HOST -rc $REMOTE_COPY)
+
+            if [ $? -ne 0 ]; then
+              error_message "$LUN"
+              exit 1
+            fi
+
+            RESCAN_CMD=$(cat <<EOF
+                set -e
+                $(rescan_scsi_bus "$LUN")
+EOF
+)
+
+            ssh_exec_and_log "$DST_HOST" "$RESCAN_CMD" \
+              "Error registering remote $NAME_WWN to $DST_HOST"
+        fi
+
         log "Mapping $NAME_WWN to $DST_HOST"
 
         LUN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py exportVV -a $API_ENDPOINT -i $IP -s $SECURE \
diff --git a/tm/3par/resize b/tm/3par/resize
index a350c0a76bdde3f853a7af43dba8f6e5ada278c9..93a4da9bc5bacbf2bff804e1eb7f5979ab910497 100644
--- a/tm/3par/resize
+++ b/tm/3par/resize
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -55,7 +55,7 @@ DISK_ID=$(echo "$SRC_PATH" | $AWK -F. '$NF!=$0 {print $NF}')
 
 XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
@@ -63,33 +63,43 @@ done < <(onevm show -x $VMID| $XPATH \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/PERSISTENT \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/READONLY \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DATASTORE_ID \
                     /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-NAME_WWN="${XPATH_ELEMENTS[0]}"
-PERSISTENT="${XPATH_ELEMENTS[1]}"
-READONLY="${XPATH_ELEMENTS[2]}"
-SYS_DSID="${XPATH_ELEMENTS[3]}"
+NAME_WWN="${XPATH_ELEMENTS[j++]}"
+PERSISTENT="${XPATH_ELEMENTS[j++]}"
+READONLY="${XPATH_ELEMENTS[j++]}"
+IMG_DSID="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
 
 # Exit if readonly
 [ "$READONLY" == "YES" ] && exit 1
 
 #-------------------------------------------------------------------------------
-# Start actions
+# Get system ds information
 #-------------------------------------------------------------------------------
 
-if [ "$PERSISTENT" != "YES" ]; then
-  #-------------------------------------------------------------------------------
-  # Get system ds information
-  #-------------------------------------------------------------------------------
-  
-  unset i XPATH_ELEMENTS
-  
-  while IFS= read -r -d '' element; do
-      XPATH_ELEMENTS[i++]="$element"
-  done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
-  
-  NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
+unset i j XPATH_ELEMENTS
+
+while IFS= read -r -d '' element; do
+    XPATH_ELEMENTS[i++]="$element"
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                  /DATASTORE/TEMPLATE/API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/IP \
+                  /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/SEC_IP \
+                  /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
 
+#-------------------------------------------------------------------------------
+# Start actions
+#-------------------------------------------------------------------------------
+if [ "$PERSISTENT" != "YES" ]; then
   # get VM disk WWN
   NAME_WWN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py getVmClone -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
                                                                 -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -id $DISK_ID)
@@ -98,13 +108,16 @@ if [ "$PERSISTENT" != "YES" ]; then
     error_message "$NAME_WWN"
     exit 1
   fi
+
+  # Disable remote copy for non-persistent disk
+  REMOTE_COPY="NO"
 fi
 
 NAME=$(get_vv_name "$NAME_WWN")
 WWN=$(get_vv_wwn "$NAME_WWN")
 
 CURRENT_SIZE=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py getVVSize -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
-                                                                    -p $PASSWORD -n $NAME -t VSIZE)
+                                                               -p $PASSWORD -n $NAME -t VSIZE)
 
 if [ $? -ne 0 ]; then
   error_message "$CURRENT_SIZE"
@@ -119,14 +132,34 @@ GROW_SIZE=`expr $SIZE - $CURRENT_SIZE`
 log "Resizing disk $NAME by $GROW_SIZE MB"
 
 # resize volume itself
-python ${DRIVER_PATH}/../../datastore/3par/3par.py growVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME \
-                                                        -gb $GROW_SIZE
+python ${DRIVER_PATH}/../../datastore/3par/3par.py growVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
+                                              -n $NAME -gb $GROW_SIZE -nt $NAMING_TYPE -vi $VMID -rc $REMOTE_COPY
 
 if [ $? -ne 0 ]; then
   error_message "Error resizing VV"
   exit 1
 fi
 
+# rescan remote scsi bus
+if [ "$REMOTE_COPY" == "YES" ] && [ "$REMOTE_COPY_MODE" == "SYNC" ]; then
+    LUN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py exportVV -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT \
+                -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -n $NAME -hs $SRC_HOST -rc $REMOTE_COPY)
+
+    if [ $? -ne 0 ]; then
+      error_message "$LUN"
+      exit 1
+    fi
+
+    RESCAN_CMD=$(cat <<EOF
+        set -e
+        $(rescan_scsi_bus "$LUN" "force")
+EOF
+)
+
+    ssh_exec_and_log "$SRC_HOST" "$RESCAN_CMD" \
+      "Error rescaning remote for new size"
+fi
+
 # rescan scsi bus and resize multipath device
 LUN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py exportVV -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
                                                             -n $NAME -hs $SRC_HOST)
diff --git a/tm/3par/snap_create b/tm/3par/snap_create
index 32553d9a029c20192fd4afb0a22cb216397e8f6b..069faeab642b9bdfab3d449880246b3956dbdcbe 100644
--- a/tm/3par/snap_create
+++ b/tm/3par/snap_create
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -58,12 +58,16 @@ done < <(onevm show -x $VMID| $XPATH \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/IMAGE_ID \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/CLONE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DISK_TYPE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DATASTORE_ID \
                     /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID \
                     /VM/DEPLOY_ID)
 
 IMAGE_ID="${XPATH_ELEMENTS[j++]}"
 DISK_SRC="${XPATH_ELEMENTS[j++]}"
 CLONE="${XPATH_ELEMENTS[j++]}"
+DISK_TYPE="${XPATH_ELEMENTS[j++]}"
+IMG_DSID="${XPATH_ELEMENTS[j++]}"
 SYS_DSID="${XPATH_ELEMENTS[j++]}"
 DEPLOY_ID="${XPATH_ELEMENTS[j++]}"
 
@@ -76,13 +80,27 @@ fi
 # Get system ds information
 #-------------------------------------------------------------------------------
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
-
-NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                  /DATASTORE/TEMPLATE/API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/IP \
+                  /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/SEC_IP \
+                  /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+
+if [ "$CLONE" == "YES" ] || [ "$DISK_TYPE" == "FILE" ]; then
+  # Disable remote copy for non-persistent disk
+  REMOTE_COPY="NO"
+fi
 
-python ${DRIVER_PATH}/../../datastore/3par/3par.py createSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                        -nt $NAMING_TYPE -id $DISK_ID -vi $VMID -si $SNAP_ID -vc $CLONE
\ No newline at end of file
+python ${DRIVER_PATH}/../../datastore/3par/3par.py createSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -sapi $SEC_API_ENDPOINT \
+      -sip $SEC_IP -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $DISK_ID -vi $VMID -si $SNAP_ID -vc $CLONE -rc $REMOTE_COPY
\ No newline at end of file
diff --git a/tm/3par/snap_create_live b/tm/3par/snap_create_live
index 4c04f9d061668adba48c0ab1df2aca60c0c9c0fa..3e31cf11c389382597f3f9972c6cf812c51e1d1a 100644
--- a/tm/3par/snap_create_live
+++ b/tm/3par/snap_create_live
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -59,12 +59,16 @@ done < <(onevm show -x $VMID| $XPATH \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/IMAGE_ID \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/CLONE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DISK_TYPE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DATASTORE_ID \
                     /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID \
                     /VM/DEPLOY_ID)
 
 IMAGE_ID="${XPATH_ELEMENTS[j++]}"
 DISK_SRC="${XPATH_ELEMENTS[j++]}"
 CLONE="${XPATH_ELEMENTS[j++]}"
+DISK_TYPE="${XPATH_ELEMENTS[j++]}"
+IMG_DSID="${XPATH_ELEMENTS[j++]}"
 SYS_DSID="${XPATH_ELEMENTS[j++]}"
 DEPLOY_ID="${XPATH_ELEMENTS[j++]}"
 
@@ -77,24 +81,33 @@ fi
 # Get system ds information
 #-------------------------------------------------------------------------------
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
-
-NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                  /DATASTORE/TEMPLATE/API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/IP \
+                  /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/SEC_IP \
+                  /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+
+if [ "$CLONE" == "YES" ] || [ "$DISK_TYPE" == "FILE" ]; then
+  # Disable remote copy for non-persistent disk
+  REMOTE_COPY="NO"
+fi
 
 LIBVIRT_URI="${QEMU_PROTOCOL}://${SRC_HOST}/system"
 
 if virsh -c $LIBVIRT_URI domfsfreeze $DEPLOY_ID ; then
     trap "virsh -c $LIBVIRT_URI domfsthaw $DEPLOY_ID" EXIT TERM INT HUP
-elif virsh -c $LIBVIRT_URI suspend $DEPLOY_ID; then
-    trap "virsh -c $LIBVIRT_URI resume $DEPLOY_ID" EXIT TERM INT HUP
-else
-    error_message "Could not domfsfreeze or suspend domain"
-    exit 1
 fi
 
-python ${DRIVER_PATH}/../../datastore/3par/3par.py createSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                        -nt $NAMING_TYPE -id $DISK_ID -vi $VMID -si $SNAP_ID -vc $CLONE
\ No newline at end of file
+python ${DRIVER_PATH}/../../datastore/3par/3par.py createSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -sapi $SEC_API_ENDPOINT \
+      -sip $SEC_IP -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $DISK_ID -vi $VMID -si $SNAP_ID -vc $CLONE -rc $REMOTE_COPY
\ No newline at end of file
diff --git a/tm/3par/snap_delete b/tm/3par/snap_delete
index 20e06dcbe3e124b481fb2c93ffdab04c900a6e81..339c96b3ba0c49bb27d10e1019ef65116cf69e0c 100644
--- a/tm/3par/snap_delete
+++ b/tm/3par/snap_delete
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -58,12 +58,16 @@ done < <(onevm show -x $VMID| $XPATH \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/IMAGE_ID \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/CLONE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DISK_TYPE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DATASTORE_ID \
                     /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID \
                     /VM/DEPLOY_ID)
 
 IMAGE_ID="${XPATH_ELEMENTS[j++]}"
 DISK_SRC="${XPATH_ELEMENTS[j++]}"
 CLONE="${XPATH_ELEMENTS[j++]}"
+DISK_TYPE="${XPATH_ELEMENTS[j++]}"
+IMG_DSID="${XPATH_ELEMENTS[j++]}"
 SYS_DSID="${XPATH_ELEMENTS[j++]}"
 DEPLOY_ID="${XPATH_ELEMENTS[j++]}"
 
@@ -76,13 +80,27 @@ fi
 # Get system ds information
 #-------------------------------------------------------------------------------
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
-
-NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                  /DATASTORE/TEMPLATE/API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/IP \
+                  /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/SEC_IP \
+                  /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+
+if [ "$CLONE" == "YES" ] || [ "$DISK_TYPE" == "FILE" ]; then
+  # Disable remote copy for non-persistent disk
+  REMOTE_COPY="NO"
+fi
 
-python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                        -nt $NAMING_TYPE -id $DISK_ID -vi $VMID -si $SNAP_ID -vc $CLONE
\ No newline at end of file
+python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -sapi $SEC_API_ENDPOINT \
+      -sip $SEC_IP -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $DISK_ID -vi $VMID -si $SNAP_ID -vc $CLONE -rc $REMOTE_COPY
\ No newline at end of file
diff --git a/tm/3par/snap_revert b/tm/3par/snap_revert
index bc6b9befb1910239764795e571149f4ae2ff5920..5cbfe0b8b3323ba5a6f6476ca074d7c0803b8de2 100644
--- a/tm/3par/snap_revert
+++ b/tm/3par/snap_revert
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright 2014-2016, Laurent Grawet <dev@grawet.be>               #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
@@ -67,12 +67,16 @@ done < <(onevm show -x $VMID| $XPATH \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/IMAGE_ID \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \
                     /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/CLONE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DISK_TYPE \
+                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/DATASTORE_ID \
                     /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID \
                     /VM/DEPLOY_ID)
 
 IMAGE_ID="${XPATH_ELEMENTS[j++]}"
 NAME_WWN="${XPATH_ELEMENTS[j++]}"
 CLONE="${XPATH_ELEMENTS[j++]}"
+DISK_TYPE="${XPATH_ELEMENTS[j++]}"
+IMG_DSID="${XPATH_ELEMENTS[j++]}"
 SYS_DSID="${XPATH_ELEMENTS[j++]}"
 DEPLOY_ID="${XPATH_ELEMENTS[j++]}"
 
@@ -85,19 +89,25 @@ fi
 # Get system ds information
 #-------------------------------------------------------------------------------
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
-
-NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
-
-# We need to unexport volume, revert snapshot and them export volume back
-
-# If it is not readonly, need to get non-persistent VM disk
-# Readonly is for ex. CDROM and no copy is created
-if [ "$CLONE" == "YES" ]; then
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                  /DATASTORE/TEMPLATE/API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/IP \
+                  /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/SEC_IP \
+                  /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+
+# if clone or volatile = non-persistent disk
+if [ "$CLONE" == "YES" ] || [ "$DISK_TYPE" == "FILE" ]; then
   # get VM disk WWN
   NAME_WWN=$(python ${DRIVER_PATH}/../../datastore/3par/3par.py getVmClone -a $API_ENDPOINT -i $IP -s $SECURE \
                                                 -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -id $DISK_ID)
@@ -106,15 +116,19 @@ if [ "$CLONE" == "YES" ]; then
     error_message "$NAME_WWN"
     exit 1
   fi
+
+  # Disable remote copy for non-persistent disk
+  REMOTE_COPY="NO"
 fi
 
 NAME=$(get_vv_name "$NAME_WWN")
 WWN=$(get_vv_wwn "$NAME_WWN")
 
 # revert snapshot
-log "Reverting snapshot $SNAP_ID"
-python ${DRIVER_PATH}/../../datastore/3par/3par.py revertSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME \
-                              -p $PASSWORD -nt $NAMING_TYPE -id $DISK_ID -vi $VMID -si $SNAP_ID -vc $CLONE -o $ONLINE
+log "Reverting snapshot $SNAP_ID; Online: $ONLINE"
+python ${DRIVER_PATH}/../../datastore/3par/3par.py revertSnapshot -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT \
+      -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -id $DISK_ID -vi $VMID -si $SNAP_ID \
+      -vc $CLONE -o $ONLINE -rc $REMOTE_COPY
 
 if [ $? -ne 0 ]; then
   error_message "Error promoting snapshot back to VV"
diff --git a/vmm/checkMultipath.py b/vmm/checkMultipath.py
new file mode 100644
index 0000000000000000000000000000000000000000..10e4037883c8b48f6b113e3eb02165ed336917c9
--- /dev/null
+++ b/vmm/checkMultipath.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# -------------------------------------------------------------------------- #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
+#                                                                            #
+# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
+# not use this file except in compliance with the License. You may obtain    #
+# a copy of the License at                                                   #
+#                                                                            #
+# http://www.apache.org/licenses/LICENSE-2.0                                 #
+#                                                                            #
+# Unless required by applicable law or agreed to in writing, software        #
+# distributed under the License is distributed on an "AS IS" BASIS,          #
+# 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.                                             #
+# -------------------------------------------------------------------------- #
+
+import sys
+import dmmp
+
+wwid = sys.argv[1]
+
+mpath = dmmp.mpath_get(wwid)
+print("Got mpath: wwid '%s', name '%s'" % (mpath.wwid, mpath.name))
+for pg in mpath.path_groups:
+    print("\tGot path group: id '%d', priority '%d', status '%d(%s)', "
+          "selector '%s'" %
+          (pg.id, pg.priority, pg.status, pg.status_string, pg.selector))
+
+    target = None
+    for p in pg.paths:
+        # check for target wwn, must be same for all paths
+        if target is not None and target != p.target_wwnn:
+            print("\t\tPath: blk_name '%s', status '%d(%s)' has different target wwnn '%s'!" %
+                  (p.blk_name, p.status, p.status_string, p.target_wwnn))
+            exit(1)
+        else:
+            target = p.target_wwnn
+            print("\t\tGot path: blk_name '%s', status '%d(%s)', target '%s'" %
+                  (p.blk_name, p.status, p.status_string, p.target_wwnn))
diff --git a/vmm/dmmp.py b/vmm/dmmp.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c00b4c4f7d076de3eb09c2a70d1caed042442e2
--- /dev/null
+++ b/vmm/dmmp.py
@@ -0,0 +1,356 @@
+"""
+Python API for multipath-tools
+"""
+# Copyright (C) 2016-2018 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Gris Ge <fge@redhat.com>
+#         Nir Soffer <nsoffer@redhat.com>
+#         Kristian Feldsam <feldsam@feldhost.cz>
+
+import json
+import socket
+import ctypes
+import sys
+import struct
+
+_API_VERSION_MAJOR = 0
+
+_IPC_ADDR = "\0/org/kernel/linux/storage/multipathd"
+
+_IPC_LEN_SIZE = ctypes.sizeof(ctypes.c_ssize_t(0))
+
+if sys.version_info[0] < 3:
+    _CMD_HEAD = struct.Struct("q" if _IPC_LEN_SIZE == 8 else "i")
+else:
+    _CMD_HEAD = struct.Struct("n")
+
+
+class DMMP_path(object):
+    """
+    DMMP_pathgroup is the abstraction of path in multipath-tools.
+    """
+
+    def __init__(self, path):
+        """
+        Internal function. For mpaths_get() only.
+        """
+        for key, value in path.items():
+            setattr(self, "_%s" % key, value)
+
+    STATUS_UNKNOWN = 0
+    STATUS_DOWN = 2
+    STATUS_UP = 3
+    STATUS_SHAKY = 4
+    STATUS_GHOST = 5
+    STATUS_PENDING = 6
+    STATUS_TIMEOUT = 7
+    STATUS_DELAYED = 9
+
+    _STATUS_CONV = {
+        "undef": STATUS_UNKNOWN,
+        "faulty": STATUS_DOWN,
+        "ready": STATUS_UP,
+        "shaky": STATUS_SHAKY,
+        "ghost": STATUS_GHOST,
+        "i/o pending": STATUS_PENDING,
+        "i/o timeout": STATUS_TIMEOUT,
+        "delayed": STATUS_DELAYED,
+    }
+
+    @property
+    def blk_name(self):
+        """
+        String.  Block name of current path. Examples: "sda", "nvme0n1".
+        """
+        return self._dev
+
+    @property
+    def target_wwnn(self):
+        """
+        String. Target device WWNN. Examples: "0x2ff70002ac01e918", "0x2ff70002ac01ec48".
+        """
+        return self._target_wwnn
+
+    @property
+    def status(self):
+        """
+        Integer. Status of current path. Possible values are:
+        * DMMP_path.STATUS_UNKNOWN
+            Unknown status.
+        * DMMP_path.STATUS_DOWN
+            Path is down and you shouldn't try to send commands to it.
+        * DMMP_path.STATUS_UP
+            Path is up and I/O can be sent to it.
+        * DMMP_path.STATUS_SHAKY
+            Only emc_clariion checker when path not available for "normal"
+            operations.
+        * DMMP_path.STATUS_GHOST
+            Only hp_sw and rdac checkers.  Indicates a "passive/standby" path
+            on active/passive HP arrays. These paths will return valid answers
+            to certain SCSI commands (tur, read_capacity, inquiry, start_stop),
+            but will fail I/O commands.
+            The path needs an initialization command to be sent to it in order
+            for I/Os to succeed.
+        * DMMP_path.STATUS_PENDING
+            Available for all async checkers when a check IO is in flight.
+        * DMMP_path.STATUS_TIMEOUT
+            Only tur checker when command timed out.
+        * DMMP_path.STATUS_DELAYED
+            If a path fails after being up for less than delay_watch_checks
+            checks, when it comes back up again, it will not be marked as up
+            until it has been up for delay_wait_checks checks. During this
+            time, it is marked as "delayed".
+        """
+        return self._STATUS_CONV.get(self.status_string, self.STATUS_UNKNOWN)
+
+    @property
+    def status_string(self):
+        """
+        String. Status of current path. Possible values are:
+        * "undef"
+            STATUS_UNKNOWN
+        * "faulty"
+            STATUS_DOWN
+        * "ready"
+            STATUS_UP
+        * "shaky"
+            STATUS_SHAKY
+        * "ghost"
+            STATUS_GHOST
+        * "i/o pending"
+            STATUS_PENDING
+        * "i/o timeout"
+            STATUS_TIMEOUT
+        * "delayed"
+            STATUS_DELAYED
+        """
+        return self._chk_st
+
+    def __str__(self):
+        return "%s|%s" % (self.blk_name, self.status_string)
+
+
+class DMMP_pathgroup(object):
+    """
+    DMMP_pathgroup is the abstraction of path group in multipath-tools.
+    """
+
+    def __init__(self, pg):
+        """
+        Internal function. For mpaths_get() only.
+        """
+        self._paths = []
+        for key, value in pg.items():
+            if key == "paths":
+                for path in pg["paths"]:
+                    self._paths.append(DMMP_path(path))
+            else:
+                setattr(self, "_%s" % key, value)
+
+    STATUS_UNKNOWN = 0
+    STATUS_ENABLED = 1
+    STATUS_DISABLED = 2
+    STATUS_ACTIVE = 3
+
+    _STATUS_CONV = {
+        "undef": STATUS_UNKNOWN,
+        "enabled": STATUS_ENABLED,
+        "disabled": STATUS_DISABLED,
+        "active": STATUS_ACTIVE,
+    }
+
+    @property
+    def id(self):
+        """
+        Integer. Group ID of current path group. Could be used for
+        switching active path group.
+        """
+        return self._group
+
+    @property
+    def status(self):
+        """
+        Integer. Status of current path group. Possible values are:
+        * DMMP_pathgroup.STATUS_UNKNOWN
+            Unknown status
+        * DMMP_pathgroup.STATUS_ENABLED
+            Standby to be active
+        * DMMP_pathgroup.STATUS_DISABLED
+            Disabled due to all path down
+        * DMMP_pathgroup.STATUS_ACTIVE
+            Selected to handle I/O
+        """
+        return self._STATUS_CONV.get(self.status_string, self.STATUS_UNKNOWN)
+
+    @property
+    def status_string(self):
+        """
+        String. Status of current path group. Possible values are:
+        * "undef"
+            STATUS_UNKNOWN
+        * "enabled"
+            STATUS_ENABLED
+        * "disabled"
+            STATUS_DISABLED
+        * "active"
+            STATUS_ACTIVE
+        """
+        return self._dm_st
+
+    @property
+    def priority(self):
+        """
+        Integer. Priority of current path group. The enabled path group with
+        highest priority will be next active path group if active path group
+        down.
+        """
+        return self._pri
+
+    @property
+    def selector(self):
+        """
+        String. Selector of current path group. Path group selector determines
+        which path in active path group will be use to next I/O.
+        """
+        return self._selector
+
+    @property
+    def paths(self):
+        """
+        List of DMMP_path objects.
+        """
+        return self._paths
+
+    def __str__(self):
+        return "%s|%s|%d" % (self.id, self.status_string, self.priority)
+
+
+class DMMP_mpath(object):
+    """
+    DMMP_mpath is the abstraction of mpath(aka. map) in multipath-tools.
+    """
+
+    def __init__(self, mpath):
+        """
+        Internal function. For mpaths_get() only.
+        """
+        self._path_groups = []
+        for key, value in mpath.items():
+            if key == "path_groups":
+                for pg in mpath["path_groups"]:
+                    self._path_groups.append(DMMP_pathgroup(pg))
+            else:
+                setattr(self, "_%s" % key, value)
+
+    @property
+    def wwid(self):
+        """
+        String. WWID of current mpath.
+        """
+        return self._uuid
+
+    @property
+    def name(self):
+        """
+        String. Name(alias) of current mpath.
+        """
+        return self._name
+
+    @property
+    def path_groups(self):
+        """
+        List of DMMP_mpath objects.
+        """
+        return self._path_groups
+
+    @property
+    def paths(self):
+        """
+        List of DMMP_path objects
+        """
+        rc = []
+        for pg in self.path_groups:
+            rc.extend(pg.paths)
+        return rc
+
+    @property
+    def kdev_name(self):
+        """
+        The string for DEVNAME used by kernel in uevent.
+        """
+        return self._sysfs
+
+    def __str__(self):
+        return "'%s'|'%s'" % (self.wwid, self.name)
+
+
+def _ipc_exec(s, cmd):
+    buff = _CMD_HEAD.pack(len(cmd) + 1) + bytearray(cmd, 'utf-8') + b'\0'
+    s.sendall(buff)
+    buff = s.recv(_IPC_LEN_SIZE)
+    if not buff:
+        return ""
+    output_len = _CMD_HEAD.unpack(buff)[0]
+    output = s.recv(output_len).decode("utf-8")
+    return output.strip('\x00')
+
+
+def mpaths_get():
+    """
+    Usage:
+        Query all multipath devices.
+    Parameters:
+        void
+    Returns:
+        [DMMP_mpath,]       List of DMMP_mpath objects.
+    """
+    rc = []
+    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    s.settimeout(60)
+    s.connect(_IPC_ADDR)
+    json_str = _ipc_exec(s, "show maps json")
+    s.close()
+    if len(json_str) == 0:
+        return rc
+    all_data = json.loads(json_str)
+    if all_data["major_version"] != _API_VERSION_MAJOR:
+        raise exception("incorrect version")
+
+    for mpath in all_data["maps"]:
+        rc.append(DMMP_mpath(mpath))
+    return rc
+
+
+def mpath_get(wwid):
+    """
+    Usage:
+        Query specific multipath device.
+    Parameters:
+        wwid (str): wwid of multipath device
+    Returns:
+        DMMP_mpath       DMMP_mpath object.
+    """
+    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    s.settimeout(60)
+    s.connect(_IPC_ADDR)
+    json_str = _ipc_exec(s, "list multipath {map} json".format(map=wwid))
+    s.close()
+    if len(json_str) == 0:
+        return rc
+    all_data = json.loads(json_str)
+    if all_data["major_version"] != _API_VERSION_MAJOR:
+        raise exception("incorrect version")
+
+    return DMMP_mpath(all_data["map"])
diff --git a/vmm/kvm/attach_disk b/vmm/kvm/attach_disk
index 1eb46d4bc924974ac5e56de5a1301d615e9ab039..ec84df09bfcfb47fe87fe1dd313f5eb57e2d46cd 100644
--- a/vmm/kvm/attach_disk
+++ b/vmm/kvm/attach_disk
@@ -3,7 +3,7 @@
 # -------------------------------------------------------------------------- #
 # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems                #
 #                                                                            #
-# Portions copyright 2019, FeldHost™ (feldhost.net)                          #
+# Portions copyright 2022, FeldHost™ (feldhost.net)                          #
 # Portions copyright 2015-2018, Storpool (storpool.com)                      #
 #                                                                            #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may    #
@@ -81,13 +81,27 @@ WRITE_IOPS_SEC_MAX_LENGTH=${WRITE_IOPS_SEC_MAX_LENGTH:-${DEFAULT_ATTACH_WRITE_IO
 WRITE_IOPS_SEC_MAX=${WRITE_IOPS_SEC_MAX:-${DEFAULT_ATTACH_WRITE_IOPS_SEC_MAX}}
 
 # BEGIN 3PAR patch
+DRIVER_PATH=$(dirname $0)
+
+sDev="$(readlink "$SOURCE")"
+
 if [ "$DEVICE" = "disk" ] && [ "$TYPE_XML" = "file" ] && [ "$TYPE_SOURCE" = "file" ]; then
-    sDev="$(readlink -f "$SOURCE")"
     if [ "${sDev:0:11}" = "/dev/mapper" ]; then
         TYPE_XML=block
         TYPE_SOURCE=dev
     fi
 fi
+
+if [ "${sDev:0:11}" = "/dev/mapper" ]; then
+  WWID=${sDev:12}
+
+  CHECK=$(python ${DRIVER_PATH}/../checkMultipath.py $WWID)
+
+  if [ $? -ne 0 ]; then
+    error_message "Could not attach ${SOURCE} (${TARGET}) to ${DOMAIN}, multipath device have multiple targets! $CHECK"
+    exit 1
+  fi
+fi
 # END 3PAR patch
 
 # disk XML
diff --git a/vmm/kvm/deploy b/vmm/kvm/deploy
new file mode 100644
index 0000000000000000000000000000000000000000..40d8ea48d98c51c52f350654bc05fdfeeca78770
--- /dev/null
+++ b/vmm/kvm/deploy
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+# -------------------------------------------------------------------------- #
+# Copyright 2002-2021, OpenNebula Project, OpenNebula Systems                #
+#                                                                            #
+# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
+# not use this file except in compliance with the License. You may obtain    #
+# a copy of the License at                                                   #
+#                                                                            #
+# http://www.apache.org/licenses/LICENSE-2.0                                 #
+#                                                                            #
+# Unless required by applicable law or agreed to in writing, software        #
+# distributed under the License is distributed on an "AS IS" BASIS,          #
+# 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.                                             #
+#--------------------------------------------------------------------------- #
+
+source $(dirname $0)/../../etc/vmm/kvm/kvmrc
+source $(dirname $0)/../../scripts_common.sh
+
+DEP_FILE=$1
+DEP_FILE_LOCATION=$(dirname $DEP_FILE)
+
+mkdir -p $DEP_FILE_LOCATION
+cat > $DEP_FILE
+
+# Compact memory
+if [ "x$CLEANUP_MEMORY_ON_START" = "xyes" ]; then
+    sudo -n sysctl vm.drop_caches=3 vm.compact_memory=1 >/dev/null
+fi
+
+# Create non-volatile memory to store firmware variables if needed
+nvram="$(xmllint --xpath '/domain/os/nvram/text()' $DEP_FILE 2>/dev/null)"
+if [ -n "${nvram}" ]; then
+    cp -n "${OVMF_NVRAM}" "${nvram}"
+fi
+
+# BEGIN 3PAR patch
+DRIVER_PATH=$(dirname $0)
+
+DISKS=$(xmllint --xpath "//disk[@type='block']/source/@dev" $DEP_FILE 2>/dev/null | sed 's/dev="//g' | sed 's/"//g')
+
+for DISK in $DISKS; do
+  sDev="$(readlink "$DISK")"
+  if [ "${sDev:0:11}" = "/dev/mapper" ]; then
+    WWID=${sDev:12}
+
+    CHECK=$(python ${DRIVER_PATH}/../checkMultipath.py $WWID)
+
+    if [ $? -ne 0 ]; then
+      error_message "Could not create domain, multipath device have multiple targets! $CHECK"
+      exit -1
+    fi
+  fi
+done
+# END 3PAR patch
+
+DATA=`virsh --connect $LIBVIRT_URI create $DEP_FILE`
+
+if [ "x$?" = "x0" ]; then
+
+    DOMAIN_ID=$(xmllint --xpath '/domain/name/text()' "$DEP_FILE")
+    UUID=$(virsh --connect $LIBVIRT_URI dominfo $DOMAIN_ID | awk '/UUID:/ {print $2}')
+    echo $UUID
+
+    # redefine potential snapshots
+    for SNAPSHOT_MD_XML in $(ls ${DEP_FILE_LOCATION}/snap-*.xml 2>/dev/null); do
+
+
+        # replace uuid in the snapshot metadata xml
+        sed -i "s%<uuid>[[:alnum:]-]*</uuid>%<uuid>$UUID</uuid>%" $SNAPSHOT_MD_XML
+
+        # redefine the snapshot using the xml metadata file
+        virsh --connect $LIBVIRT_URI snapshot-create $DOMAIN_ID $SNAPSHOT_MD_XML --redefine > /dev/null || true
+    done
+
+else
+    error_message "Could not create domain from $DEP_FILE"
+    exit -1
+fi
\ No newline at end of file
diff --git a/vmm/kvm/restore b/vmm/kvm/restore
new file mode 100644
index 0000000000000000000000000000000000000000..f854402ad2367f69204b3abab933d765a626804b
--- /dev/null
+++ b/vmm/kvm/restore
@@ -0,0 +1,151 @@
+#!/bin/bash
+
+# -------------------------------------------------------------------------- #
+# Copyright 2002-2022, OpenNebula Project, OpenNebula Systems                #
+#                                                                            #
+# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
+# not use this file except in compliance with the License. You may obtain    #
+# a copy of the License at                                                   #
+#                                                                            #
+# http://www.apache.org/licenses/LICENSE-2.0                                 #
+#                                                                            #
+# Unless required by applicable law or agreed to in writing, software        #
+# distributed under the License is distributed on an "AS IS" BASIS,          #
+# 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.                                             #
+#--------------------------------------------------------------------------- #
+
+DRIVER_PATH=$(dirname $0)
+
+source $DRIVER_PATH/../../etc/vmm/kvm/kvmrc
+source $DRIVER_PATH/../../scripts_common.sh
+
+FILE=$1
+HOST=$2
+DEPLOY_ID=$3
+
+VM_ID=$4
+DS_ID=$5
+
+FILE_XML=${FILE}.xml
+
+#-------------------------------------------------------------------------------
+# Handle DRV_MESSAGE coming from stdin
+#-------------------------------------------------------------------------------
+
+if [ ! -t 0 ]; then
+    # There is data in stdin, read it
+    DRV_MESSAGE=$(cat)
+
+    # The data is the driver message. Extracting the System DS TM_MAD
+    XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
+    IFS= read -r -d '' TM_MAD < <(echo "$DRV_MESSAGE" | $XPATH /VMM_DRIVER_ACTION_DATA/DATASTORE/TM_MAD)
+
+    # If there is a specific hook for this TM_MAD call it:
+    RESTORE_TM_FILE="${DRIVER_PATH}/restore.${TM_MAD}"
+
+    if [ -x "$RESTORE_TM_FILE" ]; then
+        echo "$DRV_MESSAGE" | $RESTORE_TM_FILE $@
+    fi
+fi
+
+# Checkpoint file: /var/lib/one//datastores/<DS_ID>/<VM_ID>/checkpoint
+
+DS_ID=$(basename $(dirname $(dirname $FILE)))
+DS_LOCATION=$(dirname $(dirname $(dirname $FILE)))
+DS_LOCATION_NON_DOUBLE_SLASH=$(echo "$DS_LOCATION" | sed 's|//|/|g')
+VM_DIR=$(dirname $FILE)
+
+RECALCULATE_CMD=$(cat <<EOF
+set -e -o pipefail
+
+# extract the xml from the checkpoint
+
+virsh --connect $LIBVIRT_URI save-image-dumpxml $FILE > $FILE_XML
+
+# Eeplace all occurrences of the DS_LOCATION/<DS_ID>/<VM_ID> with the specific
+# DS_ID where the checkpoint is placed. This is done in case there was a
+# system DS migration
+
+sed -i "s%$DS_LOCATION/[0-9]\+/$VM_ID/%$DS_LOCATION/$DS_ID/$VM_ID/%g" $FILE_XML
+sed -i "s%$DS_LOCATION_NON_DOUBLE_SLASH/[0-9]\+/$VM_ID/%$DS_LOCATION/$DS_ID/$VM_ID/%g" $FILE_XML
+EOF
+)
+
+multiline_exec_and_log "$RECALCULATE_CMD" \
+    "Could not recalculate paths in $FILE_XML"
+
+# Compact memory
+if [ "x$CLEANUP_MEMORY_ON_START" = "xyes" ]; then
+    (sudo -l | grep -q sysctl) && sudo -n sysctl vm.drop_caches=3 vm.compact_memory=1 >/dev/null
+fi
+
+# BEGIN 3PAR patch
+DRIVER_PATH=$(dirname $0)
+
+DISKS=$(xmllint --xpath "//disk[@type='block']/source/@dev" $FILE_XML 2>/dev/null | sed 's/dev="//g' | sed 's/"//g')
+
+for DISK in $DISKS; do
+  sDev="$(readlink "$DISK")"
+  if [ "${sDev:0:11}" = "/dev/mapper" ]; then
+    WWID=${sDev:12}
+
+    CHECK=$(python ${DRIVER_PATH}/../checkMultipath.py $WWID)
+
+    if [ $? -ne 0 ]; then
+      error_message "Could not restore domain, multipath device have multiple targets! $CHECK"
+      exit 1
+    fi
+  fi
+done
+# END 3PAR patch
+
+### Restore with retry
+
+# On RHEL/CentOS 7 with qemu-kvm (1.5), it may happen the QEMU
+# segfaults on the very first try to restore from checkpoint.
+# We retry 3 times before failing completely.
+
+function restore_domain {
+    exec_and_log "virsh --connect $LIBVIRT_URI restore $FILE --xml $FILE_XML" \
+        "Could not restore from $FILE"
+}
+
+retry ${VIRSH_RETRIES:-3} restore_domain
+
+if [ $? -ne 0 ]; then
+    exit 1
+fi
+
+# Synchronize VM time on background
+if [ "$SYNC_TIME" = "yes" ]; then
+    (
+        for I in $(seq 4 -1 1); do
+            if virsh --connect $LIBVIRT_URI --readonly dominfo $DEPLOY_ID; then
+                virsh --connect $LIBVIRT_URI domtime --sync $DEPLOY_ID && exit
+                [ "$I" -gt 1 ] && sleep 5
+            else
+                exit
+            fi
+        done
+    ) &>/dev/null &
+fi
+
+rm "$FILE"
+rm "$FILE_XML"
+
+# redefine potential snapshots
+for SNAPSHOT_MD_XML in $(ls ${VM_DIR}/snap-*.xml 2>/dev/null); do
+
+	# query UUID, but only once
+	UUID=${UUID:-$(virsh --connect $LIBVIRT_URI dominfo $DEPLOY_ID | grep UUID: | awk '{print $2}')}
+
+	# replace uuid in the snapshot metadata xml
+	sed -i "s%<uuid>[[:alnum:]-]*</uuid>%<uuid>$UUID</uuid>%" $SNAPSHOT_MD_XML
+
+	# redefine the snapshot using the xml metadata file
+	virsh --connect $LIBVIRT_URI snapshot-create $DEPLOY_ID $SNAPSHOT_MD_XML --redefine > /dev/null || true
+done
+
+exit 0
\ No newline at end of file
diff --git a/vmm/kvm/snapshot_create-3par b/vmm/kvm/snapshot_create-3par
index 8cd61c119825d42c0e2e47cdb2e615bc37a95b12..9b7710e045f3d29a8821479f432a668e13bf91cd 100644
--- a/vmm/kvm/snapshot_create-3par
+++ b/vmm/kvm/snapshot_create-3par
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -49,25 +49,41 @@ LCM_STATE=`lcm_state`
 
 XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onevm show -x $VMID| $XPATH /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
+done < <(onevm show -x $VMID| $XPATH \
+                    /VM/TEMPLATE/DISK[DISK_ID=0]/CLONE \
+                    /VM/TEMPLATE/DISK[DISK_ID=0]/DISK_TYPE \
+                    /VM/TEMPLATE/DISK[DISK_ID=0]/DATASTORE_ID \
+                    /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-SYS_DSID="${XPATH_ELEMENTS[0]}"
+CLONE="${XPATH_ELEMENTS[j++]}"
+DISK_TYPE="${XPATH_ELEMENTS[j++]}"
+IMG_DSID="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
 
 #-------------------------------------------------------------------------------
-# Get system ds information
+# Get ds information
 #-------------------------------------------------------------------------------
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
-
-NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                  /DATASTORE/TEMPLATE/API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/IP \
+                  /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/SEC_IP \
+                  /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
 
 # Live snapshoting only if lcm state is HOTPLUG_SNAPSHOT
 if [ $LCM_STATE -eq 24 ]; then
@@ -83,5 +99,5 @@ if [ $LCM_STATE -eq 24 ]; then
     fi
 fi
 
-python ${DRIVER_PATH}/../../datastore/3par/3par.py createVVSetSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                        -nt $NAMING_TYPE -vi $VMID -si $SNAP_ID
\ No newline at end of file
+python ${DRIVER_PATH}/../../datastore/3par/3par.py createVVSetSnapshot -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT \
+      -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -si $SNAP_ID -rc $REMOTE_COPY
\ No newline at end of file
diff --git a/vmm/kvm/snapshot_delete-3par b/vmm/kvm/snapshot_delete-3par
index 17e7d405d218d7f4bf981ff47ad053cdb52c289a..7de94583a479a14611c8e9d05814d2087e5251fa 100644
--- a/vmm/kvm/snapshot_delete-3par
+++ b/vmm/kvm/snapshot_delete-3par
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -45,25 +45,41 @@ source ${DRIVER_PATH}/../../etc/datastore/3par/3par.conf
 
 XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onevm show -x $VMID| $XPATH /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
+done < <(onevm show -x $VMID| $XPATH \
+                    /VM/TEMPLATE/DISK[DISK_ID=0]/CLONE \
+                    /VM/TEMPLATE/DISK[DISK_ID=0]/DISK_TYPE \
+                    /VM/TEMPLATE/DISK[DISK_ID=0]/DATASTORE_ID \
+                    /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-SYS_DSID="${XPATH_ELEMENTS[0]}"
+CLONE="${XPATH_ELEMENTS[j++]}"
+DISK_TYPE="${XPATH_ELEMENTS[j++]}"
+IMG_DSID="${XPATH_ELEMENTS[j++]}"
+SYS_DSID="${XPATH_ELEMENTS[j++]}"
 
 #-------------------------------------------------------------------------------
 # Get system ds information
 #-------------------------------------------------------------------------------
 
-unset i XPATH_ELEMENTS
+unset i j XPATH_ELEMENTS
 
 while IFS= read -r -d '' element; do
     XPATH_ELEMENTS[i++]="$element"
-done < <(onedatastore show -x $SYS_DSID | $XPATH /DATASTORE/TEMPLATE/NAMING_TYPE)
-
-NAMING_TYPE="${XPATH_ELEMENTS[0]:-$NAMING_TYPE}"
-
-python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVVSetSnapshot -a $API_ENDPOINT -i $IP -s $SECURE -u $USERNAME -p $PASSWORD \
-                                                        -nt $NAMING_TYPE -vi $VMID -si $SNAP_ID
\ No newline at end of file
+done < <(onedatastore show -x $SYS_DSID | $XPATH \
+                  /DATASTORE/TEMPLATE/API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/IP \
+                  /DATASTORE/TEMPLATE/SEC_API_ENDPOINT \
+                  /DATASTORE/TEMPLATE/SEC_IP \
+                  /DATASTORE/TEMPLATE/REMOTE_COPY)
+
+API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$API_ENDPOINT}"
+IP="${XPATH_ELEMENTS[j++]:-$IP}"
+SEC_API_ENDPOINT="${XPATH_ELEMENTS[j++]:-$SEC_API_ENDPOINT}"
+SEC_IP="${XPATH_ELEMENTS[j++]:-$SEC_IP}"
+REMOTE_COPY="${XPATH_ELEMENTS[j++]:-$REMOTE_COPY}"
+
+python ${DRIVER_PATH}/../../datastore/3par/3par.py deleteVVSetSnapshot -a $API_ENDPOINT -i $IP -sapi $SEC_API_ENDPOINT \
+      -sip $SEC_IP -s $SECURE -u $USERNAME -p $PASSWORD -nt $NAMING_TYPE -vi $VMID -si $SNAP_ID -rc $REMOTE_COPY
\ No newline at end of file
diff --git a/vmm/kvm/snapshot_revert-3par b/vmm/kvm/snapshot_revert-3par
index b8731ff87408d2a10b5d19230375a5524e9e4b1d..4534d82823bfeacd001c987728edc8024422a4d1 100644
--- a/vmm/kvm/snapshot_revert-3par
+++ b/vmm/kvm/snapshot_revert-3par
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # -------------------------------------------------------------------------- #
-# Copyright 2019, FeldHost™ (feldhost.net)                                   #
+# Copyright 2022, FeldHost™ (feldhost.net)                                   #
 #                                                                            #
 # Portions copyright OpenNebula Project (OpenNebula.org), CG12 Labs          #
 #                                                                            #
@@ -43,29 +43,24 @@ source ${DRIVER_PATH}/../../etc/datastore/3par/3par.conf
 # Get All VM Images and execute TM snap_revert action
 #-------------------------------------------------------------------------------
 
-i=1
-while read line
-do
-    DISK_IDS[$i]="$line"
-    (( i++ ))
-done < <(onevm show $VMID --all | $GREP -w "DISK_ID" | $CUT -d\" -f2)
-
 REVERT_SCRIPT="${DRIVER_PATH}/../../tm/3par/snap_revert"
 
-for j in `seq 1 ${#DISK_IDS[*]}`; do
+DISK_IDS=$(onevm show $VMID -x | ${DRIVER_PATH}/../../datastore/xpath.rb --stdin '%m%/VM/TEMPLATE/DISK/DISK_ID')
+
+for k in $DISK_IDS; do
     XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"
 
-    unset i k XPATH_ELEMENTS
+    unset i j XPATH_ELEMENTS
 
     while IFS= read -r -d '' element; do
         XPATH_ELEMENTS[i++]="$element"
-    done < <(onevm show -x $VMID| $XPATH     /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/TM_MAD \
-                                             /VM/TEMPLATE/DISK[DISK_ID=${DISK_IDS[$j]}]/DISK_ID \
+    done < <(onevm show -x $VMID| $XPATH     /VM/TEMPLATE/DISK[DISK_ID=$k]/TM_MAD \
+                                             /VM/TEMPLATE/DISK[DISK_ID=$k]/DISK_ID \
                                              /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)
 
-    TM_MAD=${XPATH_ELEMENTS[k++]}
-    DISK_ID=${XPATH_ELEMENTS[k++]}
-    SYS_DSID=${XPATH_ELEMENTS[k++]}
+    TM_MAD=${XPATH_ELEMENTS[j++]}
+    DISK_ID=${XPATH_ELEMENTS[j++]}
+    SYS_DSID=${XPATH_ELEMENTS[j++]}
 
     if [ "$TM_MAD" = "3par" ]; then
         $REVERT_SCRIPT "${HOST}:${DATASTORES}/${SYS_DSID}/${VMID}/disk.${DISK_ID}" "s${SNAP_ID}" "${VMID}" 0