[PMSH] Add multi CDS info etc 89/112889/1
authorefiacor <fiachra.corcoran@est.tech>
Fri, 18 Sep 2020 13:59:44 +0000 (14:59 +0100)
committerefiacor <fiachra.corcoran@est.tech>
Fri, 18 Sep 2020 13:59:51 +0000 (14:59 +0100)
Signed-off-by: efiacor <fiachra.corcoran@est.tech>
Change-Id: I51ebf2034234f5e1726b2f240ad4059facc1d93e
Issue-ID: DCAEGEN2-2407

docs/sections/apis/PMSH.rst
docs/sections/apis/pmsh_swagger.json
docs/sections/apis/pmsh_swagger.yml
docs/sections/services/pm-subscription-handler/configuration.rst
docs/sections/services/pm-subscription-handler/installation.rst
docs/sections/services/pm-subscription-handler/overview.rst
docs/sections/services/pm-subscription-handler/resources/monitoring-policy.json

index 019e4ce..a23c438 100644 (file)
@@ -44,22 +44,28 @@ The Subscription details are returned successfully
 .. code-block:: javascript
 
    [
-     {
-       "network_functions": [
-         {
-           "nf_name": "pnf102",
-           "nf_sub_status": "PENDING_CREATE",
-           "orchestration_status": "Active"
-         },
-         {
-           "nf_name": "vnf101",
-           "nf_sub_status": "CREATED",
-           "orchestration_status": "Active"
-         }
-       ],
-       "subscription_name": "demo-subscription",
-       "subscription_status": "UNLOCKED"
-     }
+       {
+          "subscription_name":"subscriptiona",
+          "subscription_status":"UNLOCKED",
+          "network_functions":[
+             {
+                "nf_name":"PNF104",
+                "nf_sub_status":"PENDING_CREATE",
+                "model_invariant_id":"8a57e2e6-d7ad-445f-b37e-a9837921014f",
+                "model_version_id":"d0938fd8-6fe4-42a2-9d26-82b7fa9336ad",
+                "sdnc_model_name":"pm_control",
+                "sdnc_model_version":"1.2.4"
+             },
+             {
+                "nf_name":"PNF105",
+                "nf_sub_status":"CREATED",
+                "model_invariant_id":"9a57e2e6-d7ad-445f-b37e-d6754892",
+                "model_version_id":"a0938fd8-6fe4-42a2-9d26-82b7fa93378c",
+                "sdnc_model_name":"pm_control",
+                "sdnc_model_version":"1.2.5"
+             }
+          ]
+       }
    ]
 
 The subscription_status refers to the administrative status of the subscription.
@@ -72,16 +78,6 @@ The subscription_status refers to the administrative status of the subscription.
    UNLOCKED, The Subscription is deployed / active.
 
 
-The network_functions.orchestration_status refers to the status of the xNF in AAI ONAP.
-
-.. csv-table:: Potential Values
-   :header: "Status", "Description"
-   :widths: 4,18
-
-   Inventoried, The xNF has been on-boarded in ONAP but not yet operable.
-   Active, The xNF is active and contactable.
-
-
 The network_functions.nf_sub_status refers to the status of the subscription (PM Job) on the xNF.
 
 .. csv-table:: Potential Values
index 59be876..ac15ca7 100644 (file)
@@ -2,7 +2,7 @@
   "swagger": "2.0",
   "info": {
     "title": "PM Subscription Handler Service",
-    "version": "1.1.0",
+    "version": "1.1.1",
     "description": "PM subscription handler enables control of performance management jobs on network functions in ONAP"
   },
   "produces": [
                           "type": "string",
                           "description": "Status of the Subscription on the Network Function"
                         },
-                        "orchestration_status": {
+                        "model_invariant_id": {
                           "type": "string",
-                          "description": "Orchestration status of the Network Function"
+                          "description": "The NF model-invariant-id defined in A&AI"
+                        },
+                        "model_version_id": {
+                          "type": "string",
+                          "description": "The NF model-version-id defined in A&AI"
+                        },
+                        "sdnc_model_name": {
+                          "type": "string",
+                          "description": "The sdnc_model_name (blueprint name) defined in A&AI"
+                        },
+                        "sdnc_model_version": {
+                          "type": "string",
+                          "description": "The sdnc_model_version (blueprint version) defined in A&AI"
                         }
                       }
                     }
index 58e6a78..b934223 100644 (file)
@@ -19,7 +19,7 @@
 swagger: "2.0"
 info:
   title: PM Subscription Handler Service
-  version: "1.1.0"
+  version: "1.1.1"
   description: PM subscription handler enables control of performance management jobs on network functions in ONAP
 produces:
   - "application/json"
@@ -58,9 +58,18 @@ paths:
                       nf_sub_status:
                         type: string
                         description: Status of the Subscription on the Network Function
-                      orchestration_status:
+                      model_invariant_id:
                         type: string
-                        description: Orchestration status of the Network Function
+                        description: The NF model-invariant-id defined in A&AI
+                      model_version_id:
+                        type: string
+                        description: The NF model-version-id defined in A&AI
+                      sdnc_model_name:
+                        type: string
+                        description: The sdnc_model_name (blueprint name) defined in A&AI
+                      sdnc_model_version:
+                        type: string
+                        description: The sdnc_model_version (blueprint version) defined in A&AI
         401:
           description: Unauthorized
         403:
index 1cd6eee..61dec90 100644 (file)
@@ -61,53 +61,57 @@ The subscription is configured within the monitoring policy. The subscription mo
 
 **subscription**
 
-::
-
-         {
-           "subscription": {
-             "subscriptionName": "someExtraPM-AllKista-gNB-R2B",
-             "administrativeState": "UNLOCKED",
-             "fileBasedGP": 15,
-             "fileLocation": "/pm/pm.xml",
-             "nfFilter": {
-               "swVersions": [
-                 "1.0.0",
-                 "1.0.1"
-               ],
-               "nfNames": [
-                 "ABC",
-                 "DEF",
-                 "foo.*"
-               ]
-             },
-             "measurementGroups": [
-                "measurementGroup": {
-                  "measurementTypes": [
-                    {
-                      "measurementType": "EutranCell.*"
-                    },
-                    {
-                      "measurementType": "EutranCellRelation.pmCounter1"
-                    },
-                    {
-                      "measurementType": "EutranCellRelation.pmCounter2"
-                    }
-                  ],
-                  "managedObjectDNsBasic": [
-                    {
-                      "DN": "ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1"
-                    },
-                    {
-                      "DN": "ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1, EUtranCellRelation=CityCenter2"
-                    },
-                    {
-                      "DN": "ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1, EUtranCellRelation=CityCenter3"
-                    }
-                  ]
-                }
+.. code-block:: json
+
+    {
+       "subscription":{
+          "subscriptionName":"someExtraPM-All-gNB-R2B",
+          "administrativeState":"UNLOCKED",
+          "fileBasedGP":15,
+          "fileLocation":"/pm/pm.xml",
+          "nfFilter":{
+             "nfNames":[
+                "^pnf1.*"
+             ],
+             "modelInvariantIDs":[
+                "5845y423-g654-6fju-po78-8n53154532k6",
+                "7129e420-d396-4efb-af02-6b83499b12f8"
+             ],
+             "modelVersionIDs":[
+                "e80a6ae3-cafd-4d24-850d-e14c084a5ca9"
              ]
-           }
-         }
+          },
+          "measurementGroups":[
+             {
+                "measurementGroup":{
+                   "measurementTypes":[
+                      {
+                         "measurementType":"EutranCell.*"
+                      },
+                      {
+                         "measurementType":"EutranCellRelation.pmCounter1"
+                      },
+                      {
+                         "measurementType":"EutranCellRelation.pmCounter2"
+                      }
+                   ],
+                   "managedObjectDNsBasic":[
+                      {
+                         "DN":"ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1"
+                      },
+                      {
+                         "DN":"ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1, EUtranCellRelation=CityCenter2"
+                      },
+                      {
+                         "DN":"ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1, EUtranCellRelation=CityCenter3"
+                      }
+                   ]
+                }
+             }
+          ]
+       }
+    }
+
 
 +---------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------+----------+--------+
 | Field               | Description                                                                                                                                                                | Type | Required | Values |
@@ -131,21 +135,21 @@ The ``nfFilter`` will be used in order to filter the list of NF's retrieved from
 can be filtered on, nfNames, modelInvariantIDs and/or modelVersionIDs.  All 3 of these are optional fields but at
 least 1 must be present for the filter to work.
 
-::
-
-        "nfFilter": {
-            "nfNames":[
-               "^pnf.*",
-               "^vnf.*"
-            ],
-            "modelInvariantIDs": [
-               "5845y423-g654-6fju-po78-8n53154532k6",
-               "7129e420-d396-4efb-af02-6b83499b12f8"
-            ],
-            "modelVersionIDs": [
-               "e80a6ae3-cafd-4d24-850d-e14c084a5ca9"
-            ]
-        }
+.. code-block:: json
+
+    "nfFilter": {
+        "nfNames":[
+           "^pnf.*",
+           "^vnf.*"
+        ],
+        "modelInvariantIDs": [
+           "5845y423-g654-6fju-po78-8n53154532k6",
+           "7129e420-d396-4efb-af02-6b83499b12f8"
+        ],
+        "modelVersionIDs": [
+           "e80a6ae3-cafd-4d24-850d-e14c084a5ca9"
+        ]
+    }
 
 +------------------------+-----------------------------------------------------------------------------------------------+------+----------+
 | Field                  | Description                                                                                   | Type | Required |
@@ -161,32 +165,32 @@ least 1 must be present for the filter to work.
 
 ``measurementGroup`` is used to specify the group of measurements that will be collected.
 
-::
+.. code-block:: json
 
-         "measurementGroup": {
-           "measurementTypes": [
-             {
-               "measurementType": "EutranCell.*"
-             },
-             {
-               "measurementType": "EutranCellRelation.pmCounter1"
-             },
-             {
-               "measurementType": "EutranCellRelation.pmCounter2"
-             }
-           ],
-           "managedObjectDNsBasic": [
-             {
-               "DN": "ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1"
-             },
-             {
-               "DN": "ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1, EUtranCellRelation=CityCenter2"
-             },
-             {
-               "DN": "ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1, EUtranCellRelation=CityCenter3"
-             }
-           ]
+    "measurementGroup": {
+       "measurementTypes": [
+         {
+           "measurementType": "EutranCell.*"
+         },
+         {
+           "measurementType": "EutranCellRelation.pmCounter1"
+         },
+         {
+           "measurementType": "EutranCellRelation.pmCounter2"
          }
+       ],
+       "managedObjectDNsBasic": [
+         {
+           "DN": "ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1"
+         },
+         {
+           "DN": "ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1, EUtranCellRelation=CityCenter2"
+         },
+         {
+           "DN": "ManagedElement=1,ENodeBFunction=1,EUtranCell=CityCenter1, EUtranCellRelation=CityCenter3"
+         }
+       ]
+    }
 
 +-----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+------+----------+
 | Field                 | Description                                                                                                                                       | Type | Required |
@@ -208,22 +212,104 @@ Subscriber:
 
         AAI-EVENT
 
-This topic is used so that the PMSH can listen for new NFs getting registered. If the NF matches the NF filter (See
-:ref:`Configuration<Configuration>`) it will be added to the relevant subscription. This topic is **AAI_EVENT**.
+This topic is used so that the PMSH can listen for new NFs getting added or deleted. If the NF matches the NF filter (See
+:ref:`Configuration<Configuration>`) it will be added to the relevant subscription.
 
 ::
 
         unauthenticated.PMSH_CL_INPUT
 
-This topic enables the operational policy to provide feedback on the status of a subscription attempt back to the PMSH service.
+This topic enables the operational policy to provide feedback on the status of a subscription attempt, back to
+PMSH, with a message of either success or failed.
+
+Example of successful CREATE event sent from policy:
+
+.. code-block:: json
+
+    {
+        "name": "ResponseEvent",
+        "nameSpace": "org.onap.policy.apex.onap.pmcontrol",
+        "source": "APEX",
+        "target": "DCAE",
+        "version": "0.0.1",
+        "status": {
+            "subscriptionName": "subscriptiona",
+            "nfName": "PNF104",
+            "changeType": "CREATE",
+            "message": "success"
+        }
+    }
 
 
 Publisher:
 ^^^^^^^^^^
 
+.. _DCAE_CL_OUTPUT_Topic:
+
 ::
 
         unauthenticated.DCAE_CL_OUTPUT
 
-The PMSH publishes subscriptions to this topic. They will be consumed by an operational policy which will make a request to CDS to
+PMSH publishes subscriptions to this topic. They will be consumed by an operational policy which will make a request to CDS to
 change the state of the subscription.
+
+Example event sent from PMSH:
+
+.. code-block:: json
+
+    {
+       "nfName":"PNF104",
+       "policyName":"pmsh-operational-policy",
+       "closedLoopControlName":"pmsh-control-loop",
+       "blueprintName":"pm_control",
+       "blueprintVersion":"1.2.4",
+       "changeType":"CREATE",
+       "subscription":{
+          "administrativeState":"UNLOCKED",
+          "subscriptionName":"subscriptiona",
+          "fileBasedGP":15,
+          "fileLocation":"/pm/pm.xml",
+          "measurementGroups":[
+             {
+                "measurementGroup":{
+                   "measurementTypes":[
+                      {
+                         "measurementType":"countera"
+                      },
+                      {
+                         "measurementType":"counterb"
+                      }
+                   ],
+                   "managedObjectDNsBasic":[
+                      {
+                         "DN":"dna"
+                      },
+                      {
+                         "DN":"dnb"
+                      }
+                   ]
+                }
+             },
+             {
+                "measurementGroup":{
+                   "measurementTypes":[
+                      {
+                         "measurementType":"counterc"
+                      },
+                      {
+                         "measurementType":"counterd"
+                      }
+                   ],
+                   "managedObjectDNsBasic":[
+                      {
+                         "DN":"dnc"
+                      },
+                      {
+                         "DN":"dnd"
+                      }
+                   ]
+                }
+             }
+          ]
+       }
+    }
index f858222..5173e93 100644 (file)
@@ -6,22 +6,22 @@
 Installation
 ============
 
-In Frankfurt, the PMSH can be deployed using the DCAE Dashboard or via CLI. Steps to deploy using CLI will be shown
+In Guilin, the PMSH can be deployed using the DCAE Dashboard or via CLI. Steps to deploy using CLI will be shown
 below.
 
 Deployment Prerequisites
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-In order to successfully deploy the PMSH, one will need administrator access to the kubernetes cluster, as a service
-will need to be exposed. As well as this, the following components are required to be running. They can be verified by
-running the health checks.
+In order to successfully deploy the PMSH, one will need administrator access to the kubernetes cluster, as the following
+procedure will be run from the dcae-bootstrap pod.
+As well as this, the following components are required to be running. They can be verified by running the health checks.
 
     - DCAE Platform
     - DMaaP
     - A&AI
     - AAF
 
-The healthcheck can be run from one of the Kubernetes controllers.
+The robot healthcheck can be run from one of the Kubernetes controllers.
 
 .. code-block:: bash
 
@@ -30,41 +30,43 @@ The healthcheck can be run from one of the Kubernetes controllers.
 Deployment Procedure
 ^^^^^^^^^^^^^^^^^^^^
 
-To deploy the PMSH in the Frankfurt release, the monitoring policy needs to be pushed directly to CONSUL. The CONSUL
-service must first be exposed.
+To deploy the PMSH in the Frankfurt release, the monitoring policy needs to be pushed directly to CONSUL.
+To begin, kubectl exec on to the dcae-bootstrap pod and move to the /tmp directory.
 
 .. code-block:: bash
 
-        kubectl expose svc -n onap consul-server-ui --name=x-consul-server-ui --type=NodePort
+        kubectl exec -itn <onap-namespace> onap-dcae-bootstrap bash
 
-The monitoring policy can then be pushed with the following request, for information on creating a monitoring policy see
-See :ref:`Subscription configuration<Subscription>`
-
-.. code-block:: bash
-
-        curl -X PUT http://<k8s-node-ip>:<consul-port>/v1/kv/dcae-pmsh:policy \
-            -H 'Content-Type: application/json' \
-            -d @monitoring-policy.json
+For information on creating a monitoring policy see :ref:`Subscription configuration<Subscription>`.
 
 The following JSON is an example monitoring policy.
 
 .. literalinclude:: resources/monitoring-policy.json
     :language: json
 
-To deploy the PMSH microservice using the deployment handler API, the ``serviceTypeId`` is needed, this can be retrieved
-using the inventory API
+The monitoring-policy.json can then be PUT with the following curl request.
+
+.. code-block:: bash
+
+        curl -X PUT http://consul:8500/v1/kv/dcae-pmsh:policy \
+            -H 'Content-Type: application/json' \
+            -d @monitoring-policy.json
+
+To deploy the PMSH microservice using the deployment handler API, the ``serviceTypeId`` is needed. This can be retrieved
+using the inventory API.
 
 .. code-block:: bash
 
-        curl https://<k8s-node-ip>:<inventory-port>/dcae-service-types
+        curl -k https://inventory:8080/dcae-service-types \
+            | grep k8s-pmsh | jq '.items[] | select(.typeName == "k8s-pmsh") | .typeId'
 
-The ``serviceTypeId`` for the PMSH can be found under typeID. The PMSH can then be deployed.
+Finally, deploy the PMSH via dcae deployment handler.
 
 .. code-block:: bash
 
-        curl https://<k8s-node-ip>:<dep-handler-port>/dcae-deployments/dcae-pmsh \
+        curl -k https://deployment-handler:8443/dcae-deployments/dcae-pmsh \
             -H 'Content-Type: application/json' \
             -d '{
                 "inputs": (),
-                "serviceTypeId": "<typeId>"
+                "serviceTypeId": "<k8s-pmsh-typeId>"
             }'
index f520329..8233166 100644 (file)
@@ -8,17 +8,18 @@ Overview
 
 Introduction
 """"""""""""
-The PM Subscription Handler (PMSH) is a micro service written in Python, which allows for the definition and activation
+The PM Subscription Handler (PMSH) is a Python based micro service, which allows for the definition and activation
 of PM subscriptions on one or more network function (NF) instances.
 
-.. _Delivery: ./delivery.html
 
 Functionality
 """""""""""""
-The PMSH allows for the definition of subscriptions on a network level, which enables the configuration of PM data on a
-set of NF instances. During creation of a subscription, PM reporting configuration and a network function filter will
-be defined. This filter will then be used to produce a subset of NF's to which the subscription will be applied. If
-a NF matching the filter is registered in ONAP after the microservice has been deployed, the subscription will
+PMSH allows for the definition of subscriptions on a network level, which enables the configuration of PM data on a
+set of NF instances.
+During creation of a subscription, PM reporting configuration and a network function filter will be defined.
+This filter will then be used to produce a subset of NF's to which the subscription will be applied.
+The NF's in question must have an Active orchestration-status in A&AI.
+If an NF matching the filter is registered in ONAP after the microservice has been deployed, the subscription will
 be applied to that NF.
 
 Interaction
@@ -27,23 +28,42 @@ Interaction
 Config Binding Service
 ^^^^^^^^^^^^^^^^^^^^^^
 
-The PMSH interacts with the Config Binding Service to retrieve it's configuration information, including the
+PMSH interacts with the Config Binding Service to retrieve it's configuration information, including the
 subscription information.
 
 DMaaP
 ^^^^^
 
-The PMSH subscribes and publishes to various DMaaP Message Router topics (See :ref:`Topics<Topics>`
+PMSH subscribes and publishes to various DMaaP Message Router topics (See :ref:`Topics<Topics>`
 for more information on which topics are used).
 
 A&AI
 ^^^^
 
-The PMSH interacts with A&AI to fetch data about network functions. The ``nfFilter`` is then
+PMSH interacts with A&AI to fetch data about network functions. The ``nfFilter`` is then
 applied to this data to produce a targeted subset of NF's.
 
-Policy and CDS
-^^^^^^^^^^^^^^
+Policy
+^^^^^^
 
-The PMSH will indirectly interact with Policy and CDS in order to push subscriptions to NF's. A policy will be used to
-make a request to CDS, which will apply the subscription to the NF.
+PMSH interacts indirectly with Policy via DMaaP Message Router to trigger an action on an operational policy defined
+by the operator. The operational policy must align with the inputs provided in the event sent from PMSH.
+
+CDS
+^^^
+The operational policy will be used to make a request to CDS, which will apply/remove the subscription to/from the NF.
+The CDS blue print processor will execute the action over netconf towards the NF.
+(See :ref:`DCAE_CL_OUTPUT_Topic<DCAE_CL_OUTPUT_Topic>` for more details)
+
+Multiple CDS Blueprint support
+""""""""""""""""""""""""""""""
+When PMSH applies the nfFilter during the parsing of the NF data, it will attempt to retrieve the relevant blueprint information
+defined in A&AI related to that model.
+These are optional parameters in SDC (sdnc_model_name, sdnc_model_version), and can be defined as properties
+assignment inputs, then pushed to A&AI during distribution.
+
+If no blueprint information is available, the NF will be skipped and no subscription event sent.
+
+If successful, the sdnc_model_name and sdnc_model_version will be sent as part of the event to the policy framework as
+blueprintName and blueprintVersion respectively.
+This in turn will be sent from the operational policy towards CDS blueprint processor, to trigger the action for the relevant blueprint.
index da2d263..6b8570e 100644 (file)
@@ -1,58 +1,62 @@
 {
-  "subscription":{
-    "subscriptionName":"subscriptiona",
-    "administrativeState":"UNLOCKED",
-    "fileBasedGP":15,
-    "fileLocation":"/pm/pm.xml",
-    "nfFilter":{
-      "nfNames":[
-        "^pnf1.*"
-      ],
-      "swVersions":[
-        "1.0.0"
-      ]
-    },
-    "measurementGroups":[
-      {
-        "measurementGroup":{
-          "measurementTypes":[
-            {
-              "measurementType":"countera"
-            },
-            {
-              "measurementType":"counterb"
-            }
-          ],
-          "managedObjectDNsBasic":[
-            {
-              "DN":"dna"
-            },
-            {
-              "DN":"dnb"
-            }
-          ]
-        }
+   "subscription":{
+      "subscriptionName":"subscriptiona",
+      "administrativeState":"UNLOCKED",
+      "fileBasedGP":15,
+      "fileLocation":"/pm/pm.xml",
+      "nfFilter":{
+         "nfNames":[
+            "^pnf1.*"
+         ],
+         "modelInvariantIDs":[
+            "5845y423-g654-6fju-po78-8n53154532k6",
+            "7129e420-d396-4efb-af02-6b83499b12f8"
+         ],
+         "modelVersionIDs":[
+            "e80a6ae3-cafd-4d24-850d-e14c084a5ca9"
+         ]
       },
-      {
-        "measurementGroup":{
-          "measurementTypes":[
-            {
-              "measurementType":"counterc"
-            },
-            {
-              "measurementType":"counterd"
+      "measurementGroups":[
+         {
+            "measurementGroup":{
+               "measurementTypes":[
+                  {
+                     "measurementType":"countera"
+                  },
+                  {
+                     "measurementType":"counterb"
+                  }
+               ],
+               "managedObjectDNsBasic":[
+                  {
+                     "DN":"dna"
+                  },
+                  {
+                     "DN":"dnb"
+                  }
+               ]
             }
-          ],
-          "managedObjectDNsBasic":[
-            {
-              "DN":"dnc"
-            },
-            {
-              "DN":"dnd"
+         },
+         {
+            "measurementGroup":{
+               "measurementTypes":[
+                  {
+                     "measurementType":"counterc"
+                  },
+                  {
+                     "measurementType":"counterd"
+                  }
+               ],
+               "managedObjectDNsBasic":[
+                  {
+                     "DN":"dnc"
+                  },
+                  {
+                     "DN":"dnd"
+                  }
+               ]
             }
-          ]
-        }
-      }
-    ]
-  }
+         }
+      ]
+   }
 }
\ No newline at end of file