Backup-Restore support library + tests
[integration/test.git] / csit / libraries / backuprestore / README.txt
diff --git a/csit/libraries/backuprestore/README.txt b/csit/libraries/backuprestore/README.txt
new file mode 100644 (file)
index 0000000..1f4d410
--- /dev/null
@@ -0,0 +1,232 @@
+== Backup-Restore test support library ==
+
+=== 1. Introduction ===
+
+The purpose of this library is to allow the generic verification
+of feature correctness in backup + restore scenarios.
+
+A feature is correct from a backup+restore standpoint when,
+at any provisioning point, a controller backup, followed by a
+restore, can be performed, and the execution of that procedure
+will not have any impact on traffic tests or datastore state
+check in respect to the expected behaviour when no backup + restore
+procedures are performed.
+
+The library can also be used (with minimal modifications) to check
+whether a feature is safe (continues operating correctly) in the
+event of a controller reboot (e.g. SFC is known not to, because of
+keeping certain information (rendered service paths) in the
+operational DS only and being unable to reconstruct that information
+after a reboot).
+
+=== 2. Library usage ===
+
+==== 2.1. Use as Robot Library keywords ====
+
+The library is delivered as a readily-available Robot FW library in
+the ODL integration/test repository. It provides two keywords:
+
+- A new keyword ('''BackupRestoreCheck'''), which:
+
+# Performs a complete datastore export (using Daexim export rpc)
+# Does a backup, then a restore of the backup previously
+  created. NOTE: this step is purposefully not implemented in the
+  keyword (a placeholder for concrete backup & restore scripts is
+  provided instead). ODL does not provide a comprehensive B&R implementation:
+  such implementation shall include both the datastore and certain
+  configuration files, but those configuration files depend on the
+  concrete ODL distribution / deployment, so ODL provides only some
+  pieces to implement that backup (i.e. the datastore backup RPCs).
+  This library is contributed with the purpose of easing the testing
+  of any backup&restore implementation; therefore that implementation
+  shall be incorporated to this library (by modifying this step)
+# Performs a new datastore export
+# Compares both config & operational datastores for differences (that
+  is, pre-backup and post-restore exports for both datastores), optionally
+  prefiltering those exports using pre-filter files (those prefilter files
+  are passed as parameters to the keyword)
+# Fails when pre-backup and post-restore exports are different even after
+  removing the specified filtered parts
+
+- A new keyword ('''ConditionalBackupRestoreCheck'''), which performs
+  the same steps than BackupRestoreCheck only when a command-line flag ("-v
+  BR_TESTING_ENABLED:true") is present. This allows to easily add
+backup-restore verification on existing tests, allowing to toggle
+the execution of that verification
+
+===== 2.1.1. Adding the br verification keyword to an existing robot test =====
+
+The library has been designed from the ground up to allow its use in
+existing testcases, so specific feature provisioning can be tested for
+correctness in backup-restore scenarios. The design premises for the
+library were:
+* To be very easy to incorporate into existing testcases (just one resource
+ import + the verification keyword, that shall be inserted in the existing
+ testcase just after test specific provisioning and before existing test
+verification steps)
+* To be togglable (that is, to allow whether to execute / to bypass the
+export + backup + restore + export + exports comparison block)
+
+===== 2.1.2. Steps to add backup-restore verification to an existing test suite =====
+1) Suite setup: Add ClusterManagement Setup (it is needed for Daexim export
+ to work). Hint: if the testsuite already contains an init suite keyword, you
+ can use the Run Keywords construct in order to run both initialization keywords.
+
+(Subsequent examples use diff-format):
+
+*** Settings ***
+Documentation     Test suite for SFC Service Functions, Operates functions from Restconf APIs.
+-Suite Setup       Init Suite
++Suite Setup       Run Keywords    Init Suite    ClusterManagement Setup
+Suite Teardown    Delete All Sessions
+
+2) Import Backup-Restore support library
+
+Resource          ../../../libraries/TemplatedRequests.robot
++Resource          ../../../libraries/BackupRestoreKeywords.robot
+
+3) Add the verification keyword (after provisioning, before assertions/traffic verification)
+Should Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
+   ${elements}=    Create List    SFC1-100-Path-1    "parent-service-function-path":"SFC1-100"    "hop-number":0    "service-index":255    "ho
+...    "service-index":254    "hop-number":2    "service-index":253
++    ConditionalBackupRestoreCheck
+Check For Elements At URI    ${OPERATIONAL_RSPS_URI}    ${elements}
+
+4) Execute the suite without passing the enablement flag (or pass it disabled:
+  '-v BR_TESTING_ENABLED:false'). Note how the testcase runs as always
+5)  Execute the suite, now passing the enablement flag ('-v BR_TESTING_ENABLED:true'):
+5.1. If the testcases pass, that means the suite provisioning is safe for B&R
+  (that is, both config and operational DSs are identical before and after the
+  procedure, and any assert / traffic verification the cases perform are also correct.
+5.2. If a testcase fail:
+5.2.1. If it is the ConditionalBackupRestoreCheck keyword what fails: it means
+  differences are found in the datastores before the backup / after the restore. Test
+log should include the list of differences found. Two types of differences:
+5.2.1.1. If differences are non-issues (e.g. elements whose changes are expected
+  after a backup + restore, as elements containing timestamps that are recalculated
+  after the restore, or elements showing transitory states which are not important
+  regarding B&R correctness), then create as many pre-filter entries as necessary
+  in the corresponding prefilter file (4 prefilter files can be passed to the
+  keyword: prefilter for the config DS before the backup, config DS after restore,
+  operational DS before the backup and operational DS after the restore. Repeat until
+  all unimportant DS entries are filtered
+5.2.1.2. Differences found on which the former rule is not applicable should be
+  checked carefully, as they are likely to showcase application bugs (regading B&R /
+  controller reboots)
+5.2.2 Errors in the testcase execution when the BR_TESTING_ENABLED:true flag is
+  passed, in keywords other than the verification keyword, are also candidates to
+  point to application bugs (e.g. because of using runtime-required in-memory
+  state that they fail to reconstruct after the restore), thus requiring careful revision
+
+==== 2.2. Execution as a standalone commandline utility ====
+In scenarios where Robot FW is not used for testing, the library core (this is, the
+prefiltered json comparison) can also be used from the command line. The tool is
+provided as a python commandline utility. Help follows:
+
+  odluser@odluser-VirtualBox:~/odl/test/csit/libraries/backuprestore\> python JsonDiffTool.py -h
+  usage: JsonDiffTool.py [-h] -i INITIALFILE -f FINALFILE [-ipf INITIALPREFILTER] [-fpf FINALPREFILTER] [-pd] [-v]
+  both initial and final json files are compared for differences. The program
+  returns 0 when the json contents are the same, or the number of differences
+  otherwise. Both json files can be prefiltered for certain patterns before
+  checking the differences
+  optional arguments:
+  -h, --help            show this help message and exit
+  -i INITIALFILE, --initialFile INITIALFILE
+                        initial json file
+  -f FINALFILE, --finalFile FINALFILE
+                        final json file
+  -ipf INITIALPREFILTER, --initialPreFilter INITIALPREFILTER
+                        File with pre-filtering patterns to apply to the
+                        initial json file before comparing
+  -fpf FINALPREFILTER, --finalPreFilter FINALPREFILTER
+                        File with pre-filtering patterns to apply to the final
+                        json file before comparing
+  -pd, --printDifferences
+                        on differences found, prints the list of paths for the
+                        found differences before exitting
+  -v, --verbose         generate log information
+
+===== 2.2.1. Command-line usage examples =====
+- Checking for differences between two json files (showing only the number of differences)
+  odluser@odluser-VirtualBox:~/odl/test/csit/libraries/backuprestore\> python JsonDiffTool.py -i ./testinput/arrayTwoNames.json -f ./testinput/arrayThreeNamesSorted.json
+  1
+
+- Checking for differences and displaying the differences (jsonpatch format)
+  odluser@odluser-VirtualBox:~/odl/test/csit/libraries/backuprestore\> python JsonDiffTool.py -i ./testinput/arrayTwoNames.json -f ./testinput/arrayThreeNamesSorted.json -pd
+  {"path": "/2", "value": {"Name": "Tom"}, "op": "add"}
+  1
+
+- Checking for differences (and displaying them), using a pre-filter file for the initial json file
+  odluser@odluser-VirtualBox:~/odl/test/csit/libraries/backuprestore\> python JsonDiffTool.py -i ./testinput/mainTestCase/odl_backup_operational_before.json -f testinput/mainTestCase/odl_backup_operational_after.json -ipf testinput/mainTestCase/json_prefilter.conf -pd
+  {"path": "/entity-owners:entity-owners/entity-type/2", "op": "remove"}
+  {"path": "/entity-owners:entity-owners/entity-type/4", "value": {"type": "iface", "entity": [{"owner": "member-1", "id": "/general-entity:entity[general-entity:name='iface']", "candidate": [{"name": "member-1"}]}]}, "op": "add"}
+  {"path": "/network-topology:network-topology/topology/3", "value": {"node": [{"netconf-node-topology:host": "127.0.0.1", "netconf-node-topology:port": 1830, "netconf-node-topology:connection-status": "connecting", "node-id": "CONTROLLER1"}, {"netconf-node-topology:host": "127.0.0.1", "netconf-node-topology:port": 1830, "netconf-node-topology:connection-status": "connecting", "node-id": "CONTROLLER2"}], "topology-id": "topology-netconf"}, "op": "replace"}
+  {"path": "/ietf-yang-library:modules-state/module-set-id", "value": "3", "op": "replace"}
+  {"path": "/ietf-yang-library:modules-state/module/56", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/116", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/127", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/140", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/139", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/185", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/238", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/267", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/269", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/278", "op": "remove"}
+  {"path": "/ietf-yang-library:modules-state/module/277", "op": "remove"}
+  15
+
+===== 2.2.2. Unit tests =====
+A handful of unit tests (testing both jsonpath - jsonpatch expression transformation,
+difference evaluation, use of filters and error cases) are provided. They can be
+executed as standard python unittests from the commandline:
+
+  odluser@odluser-VirtualBox:~/odl/test/csit/libraries/backuprestore\> python backuprestoretest.py
+  0
+  .2
+  .usage: backuprestoretest.py [-h] -i INITIALFILE -f FINALFILE
+                              [-ipf INITIALPREFILTER] [-fpf FINALPREFILTER]
+                              [-pd] [-v]
+  backuprestoretest.py: error: argument -i/--initialFile is required
+  .14
+  14
+  .16
+  16
+  .16
+  16
+  .1
+  ..16
+  16
+  ...
+  ----------------------------------------------------------------------
+  Ran 11 tests in 0.881s
+  OK
+
+=== 3. Prefilter file format ===
+Prefilter files:
+* Can contain any number of jsonpath expressions ([http://goessner.net/articles/JsonPath/ jsonpath expressions specification])
+* Use "#" as line prefix for comments
+Example:
+  #
+  # Pre-filter file example (removes the module from ietf-yang-library:modules-state which name is 'extension-resync-message')
+  #
+  $.ietf-yang-library:modules-state.module[?(@.name=='extension-resync-message')]
+  # $.ietf-yang-library:modules-state.module[?(@.name=='extension-switchfeatures-message')]
+
+=== 4. Dependencies / discarded alternatives ===
+
+The library includes in the commit itself the jsonpath library by Phil Budne
+(https://pypi.python.org/pypi/jsonpath/). This had to be done in order to rename
+the module (from jsonpath to jsonpathl), because RIDE fails to import a class
+from a module with the same name, which is the case for this library. The library
+license (MIT) allows for including / modifying it, so this inclusion is safe license-wise.
+
+Other alternatives to this library were explored, but were found unfit for the
+purpose. Specifically, we tried to use the popular jsonpath-rw library, but it
+does not support json query filtering by attribute values (only by field names),
+which is a must. Also objectpath, but the library does not support the return of
+the path of matching objects (only matched objects themselves). Those paths are
+required (they are the ones which are transformed into jsonpatch expressions)
+
+jsonpatch library (https://pypi.python.org/pypi/jsonpatch) is expected to be
+installed in order for this library to work. The library is used for removing json
+elements via patches