<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>checkstyle</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<profiles>
<profile>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
notification:
enabled: true
- data-updated:
- topic: ${CPS_CHANGE_EVENT_TOPIC:cps.data-updated-events}
- filters:
- enabled-dataspaces: ${NOTIFICATION_DATASPACE_FILTER_PATTERNS:""}
async:
executor:
core-pool-size: 2
cps: INFO
ncmp:
dmi:
+ httpclient:
+ connectionTimeoutInSeconds: 180
+ maximumConnectionsPerRoute: 50
+ maximumConnectionsTotal: 100
+ idleConnectionEvictionThresholdInSeconds: 5
auth:
username: ${DMI_USERNAME}
password: ${DMI_PASSWORD}
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>cps-bom</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<packaging>pom</packaging>
<description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>cps-dependencies</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.groupId}:${project.artifactId}</name>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
"$ref": "#/definitions/CmSubscriptionDmiInEvent",
"definitions": {
"CmSubscriptionDmiInEvent": {
- "description": "The payload for subscription event to be forwarded to dmi plugins.",
- "javaType": "org.onap.cps.ncmp.events.cmsubscription1_0_0.ncmp_to_dmi.CmSubscriptionDmiInEvent",
+ "description": "The payload for cm subscription merge event incoming message from NCMP.",
+ "type": "object",
+ "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_dmi.CmSubscriptionDmiInEvent",
+ "additionalProperties": false,
"properties": {
"data": {
- "properties": {
- "dataType": {
- "description": "The datatype content.",
- "properties": {
- "dataCategory": {
- "description": "The category type of the data",
- "type": "string"
- },
- "dataProvider": {
- "description": "The provider name of the data",
+ "$ref": "#/definitions/data"
+ }
+ },
+ "required": [
+ "data"
+ ]
+ },
+ "data": {
+ "type": "object",
+ "description": "Information about the targets and subscription",
+ "additionalProperties": false,
+ "properties": {
+ "cmhandles": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Details for the target cmhandles",
+ "additionalProperties": false,
+ "properties": {
+ "cmhandleId": {
+ "type": "string"
+ },
+ "private-properties": {
+ "type": "object",
+ "existingJavaType": "java.util.Map<String,String>",
+ "items": {
"type": "string"
- },
- "dataspace": {
- "description": "The dataspace name",
+ }
+ }
+ }
+ }
+ },
+ "predicates": {
+ "type": "array",
+ "description": "Additional values to be added into the subscription",
+ "items": {
+ "type": "object",
+ "properties": {
+ "targetFilter": {
+ "description": "CM Handles to be targeted by the subscription",
+ "type": "array",
+ "items": {
"type": "string"
}
},
- "required": [
- "dataCategory",
- "dataProvider",
- "dataspace"
- ],
- "type": "object",
- "additionalProperties": false
- },
- "predicates": {
- "description": "Additional values to be added into the subscription",
- "properties": {
- "datastore": {
- "description": "datastore which is to be used by the subscription",
- "type": "string"
- },
- "targets": {
- "description": "CM Handles to be targeted by the subscription",
- "type": "array",
- "items": {
- "$ref": "#/definitions/CmHandle"
+ "scopeFilter": {
+ "type": "object",
+ "properties": {
+ "datastore": {
+ "description": "Datastore which is to be used by the subscription",
+ "type": "string",
+ "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"]
+ },
+ "xpath-filter": {
+ "description": "Filter to be applied to the CM Handles through this event",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
}
},
- "datastore-xpath-filter": {
- "description": "filter to be applied to the CM Handles through this event",
- "type": "string"
- }
- },
- "required": [
- "datastore",
- "targets",
- "datastore-xpath-filter"
- ],
- "type": "object",
- "additionalProperties": false
+ "additionalProperties": false,
+ "required": [
+ "xpath-filter"
+ ]
+ }
},
- "subscription": {
- "description": "The subscription details.",
- "properties": {
- "clientID": {
- "description": "The clientID",
- "type": "string"
- },
- "name": {
- "description": "The name of the subscription",
- "type": "string"
- },
- "isTagged": {
- "description": "optional parameter, default is no",
- "type": "boolean",
- "default": false
- }
- },
- "required": [
- "clientID",
- "name"
- ],
- "type": "object",
- "additionalProperties": false
- }
+ "additionalProperties": false,
+ "required": [
+ "targetFilter"
+ ]
},
- "required": [
- "dataType",
- "predicates",
- "subscription"
- ],
- "type": "object",
"additionalProperties": false
}
},
- "type": "object",
- "additionalProperties": false,
"required": [
- "data"
+ "cmhandles",
+ "predicates"
]
- },
- "CmHandle": {
- "description": "The CM handle information",
- "type": "object",
- "properties": {
- "id": {
- "type": "string"
- },
- "additional-properties": {
- "existingJavaType": "java.util.Map<String,String>"
- }
- },
- "required": [
- "id",
- "additional-properties"
- ],
- "additionalProperties": false
}
}
}
\ No newline at end of file
"$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-dmi-out-event-schema:1.0.0",
"$ref": "#/definitions/CmSubscriptionDmiOutEvent",
"definitions": {
- "SubscriptionStatus": {
- "description": "The subscription status information",
+ "CmSubscriptionDmiOutEvent": {
+ "description": "The payload for cm subscription merge event coming out from DMI Plugin.",
"type": "object",
+ "additionalProperties": false,
+ "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.dmi_to_ncmp.CmSubscriptionDmiOutEvent",
"properties": {
- "id": {
- "type": "string"
- },
- "status" : {
- "type": "string",
- "enum": [
- "ACCEPTED",
- "REJECTED"
- ]
- },
- "details" : {
- "type": "string"
+ "data": {
+ "$ref": "#/definitions/Data"
}
},
"required": [
- "id",
- "status"
+ "data"
],
- "additionalProperties": false
+ "title": "CmSubscriptionDmiOutEvent"
},
- "CmSubscriptionDmiOutEvent" : {
- "description": "The payload for subscription response event.",
+ "Data": {
"type": "object",
- "javaType": "org.onap.cps.ncmp.events.cmsubscription1_0_0.dmi_to_ncmp.CmSubscriptionDmiOutEvent",
+ "description": "Information about the targets and subscription",
+ "additionalProperties": false,
"properties": {
- "data": {
- "type": "object",
- "properties": {
- "clientId": {
- "type": "string"
- },
- "subscriptionName": {
- "type": "string"
- },
- "dmiName": {
- "type": "string"
- },
- "subscriptionStatus": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/SubscriptionStatus"
- }
- }
- },
- "required": [
- "clientId",
- "subscriptionName",
- "dmiName",
- "subscriptionStatus"
- ],
- "additionalProperties": false
+ "statusCode": {
+ "type": "string",
+ "format": "integer",
+ "description": "The common status as defined in CPS"
+ },
+ "statusMessage": {
+ "type": "string",
+ "description": "The common status message as defined in CPS"
}
},
- "additionalProperties": false,
"required": [
- "data"
- ]
+ "statusCode",
+ "statusMessage"
+ ],
+ "title": "Data"
}
}
}
\ No newline at end of file
"$schema": "https://json-schema.org/draft/2019-09/schema",
"definitions": {
"CmSubscriptionNcmpInEvent": {
- "description": "The payload for subscription event.",
- "javaType": "org.onap.cps.ncmp.events.cmsubscription1_0_0.client_to_ncmp.CmSubscriptionNcmpInEvent",
+ "description": "The payload for subscription merge event.",
+ "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.client_to_ncmp.CmSubscriptionNcmpInEvent",
"properties": {
"data": {
"properties": {
- "dataType": {
- "description": "The datatype content.",
- "properties": {
- "dataCategory": {
- "description": "The category type of the data",
- "type": "string"
- },
- "dataProvider": {
- "description": "The provider name of the data",
- "type": "string"
- },
- "dataspace": {
- "description": "The dataspace name",
- "type": "string"
- }
- },
- "required": [
- "dataCategory",
- "dataProvider",
- "dataspace"
- ],
- "type": "object",
- "additionalProperties": false
+ "subscriptionId": {
+ "description": "The subscription details.",
+ "type": "string"
},
"predicates": {
+ "type": "array",
"description": "Additional values to be added into the subscription",
- "properties": {
- "datastore": {
- "description": "datastore which is to be used by the subscription",
- "type": "string"
- },
- "targets": {
- "description": "CM Handles to be targeted by the subscription",
- "type": "array",
- "items": {
- "type": "string"
+ "items": {
+ "type": "object",
+ "properties": {
+ "targetFilter": {
+ "description": "CM Handles to be targeted by the subscription",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "scopeFilter": {
+ "type": "object",
+ "properties": {
+ "datastore": {
+ "description": "Datastore which is to be used by the subscription",
+ "type": "string",
+ "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"]
+ },
+ "xpath-filter": {
+ "description": "Filter to be applied to the CM Handles through this event",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "xpath-filter"
+ ]
}
},
- "datastore-xpath-filter": {
- "description": "filter to be applied to the CM Handles through this event",
- "type": "string"
- }
- },
- "required": [
- "datastore",
- "targets",
- "datastore-xpath-filter"
- ],
- "type": "object",
- "additionalProperties": false
- },
- "subscription": {
- "description": "The subscription details.",
- "properties": {
- "clientID": {
- "description": "The clientID",
- "type": "string"
- },
- "name": {
- "description": "The name of the subscription",
- "type": "string"
- }
+ "additionalProperties": false,
+ "required": [
+ "targetFilter"
+ ]
},
- "required": [
- "clientID",
- "name"
- ],
- "type": "object",
"additionalProperties": false
}
},
"required": [
- "dataType",
- "predicates",
- "subscription"
+ "subscriptionId",
+ "predicates"
],
"type": "object",
"additionalProperties": false
"$ref": "#/definitions/CmSubscriptionNcmpOutEvent",
"definitions": {
"CmSubscriptionNcmpOutEvent": {
- "description": "The payload for avc subscription event outcome message.",
"type": "object",
- "javaType": "org.onap.cps.ncmp.events.cmsubscription1_0_0.ncmp_to_client.CmSubscriptionNcmpOutEvent",
+ "description": "The payload applied cm subscription merge event coming out from NCMP.",
+ "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmSubscriptionNcmpOutEvent",
"additionalProperties": false,
"properties": {
"data": {
- "$ref": "#/definitions/data"
+ "$ref": "#/definitions/Data"
}
},
"required": [
"data"
- ]
+ ],
+ "title": "CmSubscriptionNcmpOutEvent"
},
- "data": {
+ "Data": {
"type": "object",
- "description": "The actual data containing information about the pending and rejected targets",
+ "description": "Information about the targets and subscription",
"additionalProperties": false,
"properties": {
- "statusCode": {
- "type": "integer"
+ "subscriptionId": {
+ "type": "string",
+ "description": "The unique subscription id"
},
- "statusMessage": {
- "type": "string"
+ "accepted-targets": {
+ "type": "array",
+ "description": "List of accepted targets",
+ "items": {
+ "type": "string"
+ }
},
- "additionalInfo": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "rejected": {
- "$ref": "#/definitions/additionalInfoDetails"
- },
- "pending": {
- "$ref": "#/definitions/additionalInfoDetails"
- }
+ "rejected-targets": {
+ "type": "array",
+ "description": "List of rejected targets",
+ "items": {
+ "type": "string"
}
- }
- },
- "required": [
- "statusCode",
- "statusMessage"
- ]
- },
- "additionalInfoDetails": {
- "type": "array",
- "items": {
- "type": "object",
- "description": "Details for the target cmhandles",
- "additionalProperties": false,
- "properties": {
- "details": {
+ },
+ "pending-targets": {
+ "type": "array",
+ "description": "List of pending targets",
+ "items": {
"type": "string"
- },
- "targets": {
- "type": "array",
- "items": {
- "type": "string"
- }
}
}
- }
+ },
+ "required": [
+ "accepted-targets",
+ "pending-targets",
+ "rejected-targets",
+ "subscriptionId"
+ ],
+ "title": "Data"
}
}
+
+
}
\ No newline at end of file
+++ /dev/null
-{
- "$schema": "https://json-schema.org/draft/2019-09/schema",
- "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-dmi-in-event-schema:1.0.0",
- "$ref": "#/definitions/CmSubscriptionDmiInEvent",
- "definitions": {
- "CmSubscriptionDmiInEvent": {
- "description": "The payload for cm subscription merge event incoming message from NCMP.",
- "type": "object",
- "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_dmi.CmSubscriptionDmiInEvent",
- "additionalProperties": false,
- "properties": {
- "data": {
- "$ref": "#/definitions/data"
- }
- },
- "required": [
- "data"
- ]
- },
- "data": {
- "type": "object",
- "description": "Information about the targets and subscription",
- "additionalProperties": false,
- "properties": {
- "cmhandles": {
- "type": "array",
- "items": {
- "type": "object",
- "description": "Details for the target cmhandles",
- "additionalProperties": false,
- "properties": {
- "cmhandleId": {
- "type": "string"
- },
- "private-properties": {
- "type": "object",
- "existingJavaType": "java.util.Map<String,String>",
- "items": {
- "type": "string"
- }
- }
- }
- }
- },
- "predicates": {
- "type": "array",
- "description": "Additional values to be added into the subscription",
- "items": {
- "type": "object",
- "properties": {
- "targetFilter": {
- "description": "CM Handles to be targeted by the subscription",
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "scopeFilter": {
- "type": "object",
- "properties": {
- "datastore": {
- "description": "Datastore which is to be used by the subscription",
- "type": "string",
- "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"]
- },
- "xpath-filter": {
- "description": "Filter to be applied to the CM Handles through this event",
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "additionalProperties": false,
- "required": [
- "xpath-filter"
- ]
- }
- },
- "additionalProperties": false,
- "required": [
- "targetFilter"
- ]
- },
- "additionalProperties": false
- }
- },
- "required": [
- "cmhandles",
- "predicates"
- ]
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "$schema": "https://json-schema.org/draft/2019-09/schema",
- "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-dmi-out-event-schema:1.0.0",
- "$ref": "#/definitions/CmSubscriptionDmiOutEvent",
- "definitions": {
- "CmSubscriptionDmiOutEvent": {
- "description": "The payload for cm subscription merge event coming out from DMI Plugin.",
- "type": "object",
- "additionalProperties": false,
- "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.dmi_to_ncmp.CmSubscriptionDmiOutEvent",
- "properties": {
- "data": {
- "$ref": "#/definitions/Data"
- }
- },
- "required": [
- "data"
- ],
- "title": "CmSubscriptionDmiOutEvent"
- },
- "Data": {
- "type": "object",
- "description": "Information about the targets and subscription",
- "additionalProperties": false,
- "properties": {
- "statusCode": {
- "type": "string",
- "format": "integer",
- "description": "The common status as defined in CPS"
- },
- "statusMessage": {
- "type": "string",
- "description": "The common status message as defined in CPS"
- }
- },
- "required": [
- "statusCode",
- "statusMessage"
- ],
- "title": "Data"
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-ncmp-in-event:1.0.0",
- "$ref": "#/definitions/CmSubscriptionNcmpInEvent",
- "$schema": "https://json-schema.org/draft/2019-09/schema",
- "definitions": {
- "CmSubscriptionNcmpInEvent": {
- "description": "The payload for subscription merge event.",
- "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.client_to_ncmp.CmSubscriptionNcmpInEvent",
- "properties": {
- "data": {
- "properties": {
- "subscriptionId": {
- "description": "The subscription details.",
- "type": "string"
- },
- "predicates": {
- "type": "array",
- "description": "Additional values to be added into the subscription",
- "items": {
- "type": "object",
- "properties": {
- "targetFilter": {
- "description": "CM Handles to be targeted by the subscription",
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "scopeFilter": {
- "type": "object",
- "properties": {
- "datastore": {
- "description": "Datastore which is to be used by the subscription",
- "type": "string",
- "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"]
- },
- "xpath-filter": {
- "description": "Filter to be applied to the CM Handles through this event",
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "additionalProperties": false,
- "required": [
- "xpath-filter"
- ]
- }
- },
- "additionalProperties": false,
- "required": [
- "targetFilter"
- ]
- },
- "additionalProperties": false
- }
- },
- "required": [
- "subscriptionId",
- "predicates"
- ],
- "type": "object",
- "additionalProperties": false
- }
- },
- "type": "object",
- "additionalProperties": false,
- "required": [
- "data"
- ]
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-{
- "$schema": "https://json-schema.org/draft/2019-09/schema",
- "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-ncmp-out-event-schema:1.0.0",
- "$ref": "#/definitions/CmSubscriptionNcmpOutEvent",
- "definitions": {
- "CmSubscriptionNcmpOutEvent": {
- "type": "object",
- "description": "The payload applied cm subscription merge event coming out from NCMP.",
- "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmSubscriptionNcmpOutEvent",
- "additionalProperties": false,
- "properties": {
- "data": {
- "$ref": "#/definitions/Data"
- }
- },
- "required": [
- "data"
- ],
- "title": "CmSubscriptionNcmpOutEvent"
- },
- "Data": {
- "type": "object",
- "description": "Information about the targets and subscription",
- "additionalProperties": false,
- "properties": {
- "subscriptionId": {
- "type": "string",
- "description": "The unique subscription id"
- },
- "accepted-targets": {
- "type": "array",
- "description": "List of accepted targets",
- "items": {
- "type": "string"
- }
- },
- "rejected-targets": {
- "type": "array",
- "description": "List of rejected targets",
- "items": {
- "type": "string"
- }
- },
- "pending-targets": {
- "type": "array",
- "description": "List of pending targets",
- "items": {
- "type": "string"
- }
- }
- },
- "required": [
- "accepted-targets",
- "pending-targets",
- "rejected-targets",
- "subscriptionId"
- ],
- "title": "Data"
- }
- }
-
-
-}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-dmi-in-event-schema:1.0.0",
+ "$ref": "#/definitions/CmSubscriptionDmiInEvent",
+ "definitions": {
+ "CmSubscriptionDmiInEvent": {
+ "description": "The payload for subscription event to be forwarded to dmi plugins.",
+ "javaType": "org.onap.cps.ncmp.events.cmsubscription1_0_0.ncmp_to_dmi.CmSubscriptionDmiInEvent",
+ "properties": {
+ "data": {
+ "properties": {
+ "dataType": {
+ "description": "The datatype content.",
+ "properties": {
+ "dataCategory": {
+ "description": "The category type of the data",
+ "type": "string"
+ },
+ "dataProvider": {
+ "description": "The provider name of the data",
+ "type": "string"
+ },
+ "dataspace": {
+ "description": "The dataspace name",
+ "type": "string"
+ }
+ },
+ "required": [
+ "dataCategory",
+ "dataProvider",
+ "dataspace"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "predicates": {
+ "description": "Additional values to be added into the subscription",
+ "properties": {
+ "datastore": {
+ "description": "datastore which is to be used by the subscription",
+ "type": "string"
+ },
+ "targets": {
+ "description": "CM Handles to be targeted by the subscription",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/CmHandle"
+ }
+ },
+ "datastore-xpath-filter": {
+ "description": "filter to be applied to the CM Handles through this event",
+ "type": "string"
+ }
+ },
+ "required": [
+ "datastore",
+ "targets",
+ "datastore-xpath-filter"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "subscription": {
+ "description": "The subscription details.",
+ "properties": {
+ "clientID": {
+ "description": "The clientID",
+ "type": "string"
+ },
+ "name": {
+ "description": "The name of the subscription",
+ "type": "string"
+ },
+ "isTagged": {
+ "description": "optional parameter, default is no",
+ "type": "boolean",
+ "default": false
+ }
+ },
+ "required": [
+ "clientID",
+ "name"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "dataType",
+ "predicates",
+ "subscription"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ }
+ },
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "data"
+ ]
+ },
+ "CmHandle": {
+ "description": "The CM handle information",
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "additional-properties": {
+ "existingJavaType": "java.util.Map<String,String>"
+ }
+ },
+ "required": [
+ "id",
+ "additional-properties"
+ ],
+ "additionalProperties": false
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-dmi-out-event-schema:1.0.0",
+ "$ref": "#/definitions/CmSubscriptionDmiOutEvent",
+ "definitions": {
+ "SubscriptionStatus": {
+ "description": "The subscription status information",
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "status" : {
+ "type": "string",
+ "enum": [
+ "ACCEPTED",
+ "REJECTED"
+ ]
+ },
+ "details" : {
+ "type": "string"
+ }
+ },
+ "required": [
+ "id",
+ "status"
+ ],
+ "additionalProperties": false
+ },
+ "CmSubscriptionDmiOutEvent" : {
+ "description": "The payload for subscription response event.",
+ "type": "object",
+ "javaType": "org.onap.cps.ncmp.events.cmsubscription1_0_0.dmi_to_ncmp.CmSubscriptionDmiOutEvent",
+ "properties": {
+ "data": {
+ "type": "object",
+ "properties": {
+ "clientId": {
+ "type": "string"
+ },
+ "subscriptionName": {
+ "type": "string"
+ },
+ "dmiName": {
+ "type": "string"
+ },
+ "subscriptionStatus": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/SubscriptionStatus"
+ }
+ }
+ },
+ "required": [
+ "clientId",
+ "subscriptionName",
+ "dmiName",
+ "subscriptionStatus"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "data"
+ ]
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-ncmp-in-event:1.0.0",
+ "$ref": "#/definitions/CmSubscriptionNcmpInEvent",
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "definitions": {
+ "CmSubscriptionNcmpInEvent": {
+ "description": "The payload for subscription event.",
+ "javaType": "org.onap.cps.ncmp.events.cmsubscription1_0_0.client_to_ncmp.CmSubscriptionNcmpInEvent",
+ "properties": {
+ "data": {
+ "properties": {
+ "dataType": {
+ "description": "The datatype content.",
+ "properties": {
+ "dataCategory": {
+ "description": "The category type of the data",
+ "type": "string"
+ },
+ "dataProvider": {
+ "description": "The provider name of the data",
+ "type": "string"
+ },
+ "dataspace": {
+ "description": "The dataspace name",
+ "type": "string"
+ }
+ },
+ "required": [
+ "dataCategory",
+ "dataProvider",
+ "dataspace"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "predicates": {
+ "description": "Additional values to be added into the subscription",
+ "properties": {
+ "datastore": {
+ "description": "datastore which is to be used by the subscription",
+ "type": "string"
+ },
+ "targets": {
+ "description": "CM Handles to be targeted by the subscription",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "datastore-xpath-filter": {
+ "description": "filter to be applied to the CM Handles through this event",
+ "type": "string"
+ }
+ },
+ "required": [
+ "datastore",
+ "targets",
+ "datastore-xpath-filter"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "subscription": {
+ "description": "The subscription details.",
+ "properties": {
+ "clientID": {
+ "description": "The clientID",
+ "type": "string"
+ },
+ "name": {
+ "description": "The name of the subscription",
+ "type": "string"
+ }
+ },
+ "required": [
+ "clientID",
+ "name"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "dataType",
+ "predicates",
+ "subscription"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ }
+ },
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "data"
+ ]
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-ncmp-out-event-schema:1.0.0",
+ "$ref": "#/definitions/CmSubscriptionNcmpOutEvent",
+ "definitions": {
+ "CmSubscriptionNcmpOutEvent": {
+ "description": "The payload for avc subscription event outcome message.",
+ "type": "object",
+ "javaType": "org.onap.cps.ncmp.events.cmsubscription1_0_0.ncmp_to_client.CmSubscriptionNcmpOutEvent",
+ "additionalProperties": false,
+ "properties": {
+ "data": {
+ "$ref": "#/definitions/data"
+ }
+ },
+ "required": [
+ "data"
+ ]
+ },
+ "data": {
+ "type": "object",
+ "description": "The actual data containing information about the pending and rejected targets",
+ "additionalProperties": false,
+ "properties": {
+ "statusCode": {
+ "type": "integer"
+ },
+ "statusMessage": {
+ "type": "string"
+ },
+ "additionalInfo": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "rejected": {
+ "$ref": "#/definitions/additionalInfoDetails"
+ },
+ "pending": {
+ "$ref": "#/definitions/additionalInfoDetails"
+ }
+ }
+ }
+ },
+ "required": [
+ "statusCode",
+ "statusMessage"
+ ]
+ },
+ "additionalInfoDetails": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Details for the target cmhandles",
+ "additionalProperties": false,
+ "properties": {
+ "details": {
+ "type": "string"
+ },
+ "targets": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-ncmp-rest-stub</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
</parent>
<artifactId>cps-ncmp-rest-stub-app</artifactId>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-ncmp-rest-stub</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
</parent>
<artifactId>cps-ncmp-rest-stub-service</artifactId>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
type: string
enum: [COMPLETE, NONE]
example: "COMPLETE"
+ alternateId:
+ type: string
+ example: "my-alternate-id"
RestCmHandleProperties:
type: object
additionalProperties:
example: [ my-cm-handle1, my-cm-handle2, my-cm-handle3 ]
moduleSetTag:
type: string
+ default: ""
example: 'my-module-set-tag'
#Response Schemas
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
def 'Convert a created REST CM Handle Input to an NCMP Service CM Handle with #scenario'() {
given: 'a rest cm handle input'
def inputRestCmHandle = new RestInputCmHandle(cmHandle : 'example-id', cmHandleProperties: registrationDmiProperties,
- publicCmHandleProperties: registrationPublicProperties, trustLevel: registrationTrustLevel)
+ publicCmHandleProperties: registrationPublicProperties, trustLevel: registrationTrustLevel, alternateId: 'my-alternate-id', moduleSetTag: 'my-module-set-tag')
def restDmiPluginRegistration = new RestDmiPluginRegistration(
createdCmHandles: [inputRestCmHandle])
when: 'to plugin dmi registration is called'
result.createdCmHandles[0].dmiProperties == mappedDmiProperties
result.createdCmHandles[0].publicProperties == mappedPublicProperties
result.createdCmHandles[0].registrationTrustLevel == mappedTrustLevel
+ and: 'alternate ID and module set tag converted correctly'
+ result.createdCmHandles[0].alternateId == 'my-alternate-id'
+ result.createdCmHandles[0].moduleSetTag == 'my-module-set-tag'
where: 'the following parameters are used'
scenario | registrationDmiProperties | registrationPublicProperties | registrationTrustLevel || mappedDmiProperties | mappedPublicProperties | mappedTrustLevel
'dmi and public properties' | ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property'] | 'COMPLETE' || ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property'] | TrustLevel.COMPLETE
import ch.qos.logback.core.read.ListAppender
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
-import org.onap.cps.notification.NotificationErrorHandler
import org.slf4j.LoggerFactory
import spock.lang.Specification
@AfterEach
void teardown() {
- ((Logger) LoggerFactory.getLogger(NotificationErrorHandler.class)).detachAndStopAllAppenders();
+ ((Logger) LoggerFactory.getLogger(CpsNcmpTaskExecutor.class)).detachAndStopAllAppenders();
}
def 'Execute successful task.'() {
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5</artifactId>
+ </dependency>
<dependency>
<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-json-jackson</artifactId>
dmiPluginRegistration.getDmiDataPlugin(),
dmiPluginRegistration.getDmiModelPlugin(),
cmHandle,
- cmHandle.getModuleSetTag());
+ cmHandle.getModuleSetTag(),
+ cmHandle.getAlternateId());
yangModelCmHandles.add(yangModelCmHandle);
initialTrustLevelPerCmHandleId.put(cmHandle.getCmHandleId(), cmHandle.getRegistrationTrustLevel());
});
.withLockReason(MODULE_UPGRADE, lockReasonWithModuleSetTag).build());
return YangModelCmHandle.toYangModelCmHandle(dmiPluginRegistration.getDmiPlugin(),
dmiPluginRegistration.getDmiDataPlugin(), dmiPluginRegistration.getDmiModelPlugin(),
- ncmpServiceCmHandle, moduleSetTag);
+ ncmpServiceCmHandle, moduleSetTag, ncmpServiceCmHandle.getAlternateId());
}
private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) {
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.config;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.convert.DurationUnit;
+
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "ncmp.dmi.httpclient", ignoreUnknownFields = true)
+public class HttpClientConfiguration {
+
+ /**
+ * The maximum time to establish a connection.
+ */
+ @DurationUnit(ChronoUnit.SECONDS)
+ private Duration connectionTimeoutInSeconds = Duration.ofSeconds(180);
+
+ /**
+ * The maximum number of open connections per route.
+ */
+ private int maximumConnectionsPerRoute = 50;
+
+ /**
+ * The maximum total number of open connections.
+ */
+ private int maximumConnectionsTotal = maximumConnectionsPerRoute * 2;
+
+ /**
+ * The duration after which idle connections are evicted.
+ */
+ @DurationUnit(ChronoUnit.SECONDS)
+ private Duration idleConnectionEvictionThresholdInSeconds = Duration.ofSeconds(5);
+
+}
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.cps.ncmp.api.impl.config;
-import java.time.Duration;
import java.util.Arrays;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
+import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Configuration
+@EnableConfigurationProperties(HttpClientConfiguration.class)
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public class NcmpConfiguration {
- private static final Duration CONNECTION_TIMEOUT_MILLISECONDS = Duration.ofMillis(180000);
- private static final Duration READ_TIMEOUT_MILLISECONDS = Duration.ofMillis(180000);
-
@Getter
@Component
public static class DmiProperties {
* Rest template bean.
*
* @param restTemplateBuilder the rest template builder
+ * @param httpClientConfiguration the http client configuration
* @return rest template instance
*/
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
- public static RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder) {
- final RestTemplate restTemplate = restTemplateBuilder.setConnectTimeout(CONNECTION_TIMEOUT_MILLISECONDS)
- .setReadTimeout(READ_TIMEOUT_MILLISECONDS).build();
+ public static RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder,
+ final HttpClientConfiguration httpClientConfiguration) {
+
+ final ConnectionConfig connectionConfig = ConnectionConfig.copy(ConnectionConfig.DEFAULT)
+ .setConnectTimeout(Timeout.of(httpClientConfiguration.getConnectionTimeoutInSeconds()))
+ .build();
+
+ final PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
+ .setDefaultConnectionConfig(connectionConfig)
+ .setMaxConnTotal(httpClientConfiguration.getMaximumConnectionsTotal())
+ .setMaxConnPerRoute(httpClientConfiguration.getMaximumConnectionsPerRoute())
+ .build();
+
+ final CloseableHttpClient httpClient = HttpClients.custom()
+ .setConnectionManager(connectionManager)
+ .evictExpiredConnections()
+ .evictIdleConnections(
+ TimeValue.of(httpClientConfiguration.getIdleConnectionEvictionThresholdInSeconds()))
+ .build();
+
+ final ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
+
+ final RestTemplate restTemplate = restTemplateBuilder
+ .requestFactory(() -> requestFactory)
+ .setConnectTimeout(httpClientConfiguration.getConnectionTimeoutInSeconds())
+ .build();
+
setRestTemplateMessageConverters(restTemplate);
return restTemplate;
}
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.subscriptions;
+package org.onap.cps.ncmp.api.impl.deprecated.subscriptions;
import java.util.Collection;
import org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence;
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.subscriptions;
+package org.onap.cps.ncmp.api.impl.deprecated.subscriptions;
import java.util.Collection;
import java.util.HashMap;
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.subscriptions;
+package org.onap.cps.ncmp.api.impl.deprecated.subscriptions;
public enum SubscriptionStatus {
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription;
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription;
import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.onap.cps.ncmp.api.impl.config.embeddedcache.ForwardedSubscriptionEventCacheConfig;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionPersistence;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelSubscriptionEvent;
import org.onap.cps.ncmp.api.models.CmSubscriptionEvent;
import org.onap.cps.ncmp.events.cmsubscription1_0_0.dmi_to_ncmp.CmSubscriptionDmiOutEvent;
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription;
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription;
import java.util.List;
import java.util.stream.Collectors;
default List<YangModelSubscriptionEvent.TargetCmHandle> mapSubscriptionStatusToCmHandleTargets(
List<SubscriptionStatus> subscriptionStatus) {
return subscriptionStatus.stream().map(status -> new YangModelSubscriptionEvent.TargetCmHandle(status.getId(),
- org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.fromString(status.getStatus().value()),
+ org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus.fromString(
+ status.getStatus().value()),
status.getDetails())).collect(Collectors.toList());
}
}
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription;
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription;
import java.util.List;
import java.util.Map;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus;
import org.onap.cps.ncmp.api.models.CmSubscriptionEvent;
import org.onap.cps.ncmp.api.models.CmSubscriptionStatus;
import org.onap.cps.ncmp.events.cmsubscription1_0_0.ncmp_to_client.AdditionalInfo;
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription;
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription;
import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent;
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionPersistence;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelSubscriptionEvent;
import org.onap.cps.ncmp.events.cmsubscription1_0_0.client_to_ncmp.CmSubscriptionNcmpInEvent;
import org.springframework.beans.factory.annotation.Value;
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription;
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription;
import com.hazelcast.map.IMap;
import io.cloudevents.CloudEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.impl.config.embeddedcache.ForwardedSubscriptionEventCacheConfig;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionPersistence;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus;
import org.onap.cps.ncmp.api.impl.events.EventsPublisher;
import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus;
import org.onap.cps.ncmp.api.impl.utils.CmSubscriptionEventCloudMapper;
import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription;
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription;
import java.util.List;
import java.util.stream.Collectors;
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription;
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription;
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription;
import static org.onap.cps.ncmp.api.NcmpResponseStatus.PARTIALLY_APPLIED_SUBSCRIPTION;
import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_NOT_APPLICABLE;
import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_PENDING;
import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESSFULLY_APPLIED_SUBSCRIPTION;
-import static org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.ACCEPTED;
-import static org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.PENDING;
-import static org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.REJECTED;
+import static org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus.ACCEPTED;
+import static org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus.PENDING;
+import static org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus.REJECTED;
import io.cloudevents.CloudEvent;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.NcmpResponseStatus;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionPersistence;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus;
import org.onap.cps.ncmp.api.impl.events.EventsPublisher;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus;
import org.onap.cps.ncmp.api.impl.utils.DataNodeHelper;
import org.onap.cps.ncmp.api.impl.utils.SubscriptionOutcomeCloudMapper;
import org.onap.cps.ncmp.api.models.CmSubscriptionEvent;
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription;
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription;
import com.hazelcast.map.IMap;
import java.util.Set;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsModuleService;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements InventoryPersistence {
private final CpsModuleService cpsModuleService;
- private final CpsAdminService cpsAdminService;
+ private final CpsAnchorService cpsAnchorService;
private final CpsValidator cpsValidator;
/**
* @param cpsDataService cps data service instance
* @param cpsModuleService cps module service instance
* @param cpsValidator cps validation service instance
- * @param cpsAdminService cps admin service instance
+ * @param cpsAnchorService cps anchor service instance
*/
public InventoryPersistenceImpl(final JsonObjectMapper jsonObjectMapper, final CpsDataService cpsDataService,
final CpsModuleService cpsModuleService, final CpsValidator cpsValidator,
- final CpsAdminService cpsAdminService) {
+ final CpsAnchorService cpsAnchorService) {
super(jsonObjectMapper, cpsDataService, cpsModuleService, cpsValidator);
this.cpsModuleService = cpsModuleService;
- this.cpsAdminService = cpsAdminService;
+ this.cpsAnchorService = cpsAnchorService;
this.cpsValidator = cpsValidator;
}
@Override
public Collection<String> getCmHandleIdsWithGivenModules(final Collection<String> moduleNamesForQuery) {
- return cpsAdminService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery);
+ return cpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery);
}
private static String createCmHandleXPath(final String cmHandleId) {
import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries;
import org.onap.cps.ncmp.api.impl.inventory.CmHandleState;
import org.onap.cps.ncmp.api.impl.inventory.CompositeState;
public static final String MODULE_SET_TAG_KEY = "moduleSetTag";
public static final String MODULE_SET_TAG_MESSAGE_FORMAT = "Upgrade to ModuleSetTag: {0}";
private static final String UPGRADE_FORMAT = "Upgrade to ModuleSetTag: %s";
- private static final String UPGRADE_FAILED_FORMAT = UPGRADE_FORMAT + " Attempt #%d failed: %s";
+ private static final String LOCK_REASON_DETAILS_MSG_FORMAT = UPGRADE_FORMAT + " Attempt #%d failed: %s";
private static final Pattern retryAttemptPattern = Pattern.compile("Attempt #(\\d+) failed:.+");
private static final Pattern moduleSetTagPattern = Pattern.compile("Upgrade to ModuleSetTag: (\\S+)");
int attempt = 1;
final Map<String, String> compositeStateDetails
= getLockedCompositeStateDetails(compositeState.getLockReason());
- if (!compositeStateDetails.isEmpty()) {
+ if (!compositeStateDetails.isEmpty() && compositeStateDetails.containsKey(RETRY_ATTEMPT_KEY)) {
attempt = 1 + Integer.parseInt(compositeStateDetails.get(RETRY_ATTEMPT_KEY));
}
+ final String moduleSetTag = compositeStateDetails.get(MODULE_SET_TAG_KEY);
compositeState.setLockReason(CompositeState.LockReason.builder()
- .details(String.format(UPGRADE_FAILED_FORMAT,
- compositeStateDetails.get(MODULE_SET_TAG_KEY), attempt, errorMessage))
- .lockReasonCategory(lockReasonCategory).build());
+ .details(String.format(LOCK_REASON_DETAILS_MSG_FORMAT, StringUtils.isNotBlank(moduleSetTag)
+ ? moduleSetTag : "not-specified", attempt, errorMessage)).lockReasonCategory(lockReasonCategory)
+ .build());
}
/**
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
final CompositeState.LockReason lockReason = compositeState.getLockReason();
+ final boolean moduleUpgrade = LockReasonCategory.MODULE_UPGRADE == lockReason.getLockReasonCategory();
+ if (moduleUpgrade) {
+ log.info("Locked for module upgrade");
+ return true;
+ }
+
final boolean failedDuringModuleSync = LockReasonCategory.MODULE_SYNC_FAILED
== lockReason.getLockReasonCategory();
final boolean failedDuringModuleUpgrade = LockReasonCategory.MODULE_UPGRADE_FAILED
== lockReason.getLockReasonCategory();
if (failedDuringModuleSync || failedDuringModuleUpgrade) {
- log.info("Locked for module {}.", failedDuringModuleSync ? "sync" : "upgrade");
+ log.info("Locked for module {} (last attempt failed).", failedDuringModuleSync ? "sync" : "upgrade");
return isRetryDue(lockReason, time);
}
log.info("Locked for other reason");
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
-import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsModuleService;
import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries;
private final DmiModelOperations dmiModelOperations;
private final CpsModuleService cpsModuleService;
- private final CpsAdminService cpsAdminService;
private final CmHandleQueries cmHandleQueries;
private final CpsDataService cpsDataService;
+ private final CpsAnchorService cpsAnchorService;
private final JsonObjectMapper jsonObjectMapper;
private final Map<String, Collection<ModuleReference>> moduleSetTagCache;
private static final Map<String, String> NO_NEW_MODULES = Collections.emptyMap();
*/
public void syncAndCreateOrUpgradeSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) {
- final String moduleSetTag;
final String cmHandleId = yangModelCmHandle.getId();
final CompositeState compositeState = yangModelCmHandle.getCompositeState();
final boolean inUpgrade = ModuleOperationsUtils.isInUpgradeOrUpgradeFailed(compositeState);
-
- if (inUpgrade) {
- moduleSetTag = ModuleOperationsUtils.getLockedCompositeStateDetails(compositeState.getLockReason())
- .get(ModuleOperationsUtils.MODULE_SET_TAG_KEY);
- } else {
- moduleSetTag = yangModelCmHandle.getModuleSetTag();
- }
+ final String moduleSetTag = getModuleSetTag(yangModelCmHandle, compositeState, inUpgrade);
final Collection<ModuleReference> moduleReferencesFromCache = moduleSetTagCache.get(moduleSetTag);
if (moduleReferencesFromCache == null) {
- final Optional<DataNode> optionalExistingCmHandleWithSameModuleSetTag
+ final Optional<DataNode> existingCmHandleWithSameModuleSetTag
= getFirstReadyDataNodeWithModuleSetTag(moduleSetTag);
- if (optionalExistingCmHandleWithSameModuleSetTag.isPresent()) {
- final String existingCmHandleAnchorName
- = optionalExistingCmHandleWithSameModuleSetTag.get().getAnchorName();
- createOrUpgradeSchemaSetUsingModuleSetTag(cmHandleId, moduleSetTag, existingCmHandleAnchorName);
+ if (existingCmHandleWithSameModuleSetTag.isPresent()) {
+ final String existingAnchorName = existingCmHandleWithSameModuleSetTag.get().getAnchorName();
+ final Collection<ModuleReference> moduleReferencesFromExistingCmHandle =
+ upgradeOrCreateSchemaSetUsingModuleSetTag(yangModelCmHandle.getId(), moduleSetTag,
+ existingAnchorName, inUpgrade);
+ updateModuleSetTagCache(moduleSetTag, moduleReferencesFromExistingCmHandle);
} else {
- syncAndCreateSchemaSet(yangModelCmHandle, moduleSetTag);
+ final Collection<ModuleReference> allModuleReferencesFromCmHandle
+ = syncAndCreateSchemaSet(yangModelCmHandle);
+ updateModuleSetTagCache(moduleSetTag, allModuleReferencesFromCmHandle);
}
} else {
- cpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
- cmHandleId, NO_NEW_MODULES, moduleReferencesFromCache);
+ if (inUpgrade) {
+ cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId,
+ NO_NEW_MODULES, moduleReferencesFromCache);
+ } else {
+ cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
+ cmHandleId, NO_NEW_MODULES, moduleReferencesFromCache);
+ }
}
if (!inUpgrade) {
- cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, cmHandleId);
+ cpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, cmHandleId);
}
setCmHandleModuleSetTag(yangModelCmHandle, moduleSetTag);
}
- private void syncAndCreateSchemaSet(final YangModelCmHandle yangModelCmHandle, final String moduleSetTag) {
- final Collection<ModuleReference> allModuleReferencesFromCmHandle =
- dmiModelOperations.getModuleReferences(yangModelCmHandle);
- final Collection<ModuleReference> identifiedNewModuleReferencesFromCmHandle = cpsModuleService
- .identifyNewModuleReferences(allModuleReferencesFromCmHandle);
- final Map<String, String> newModuleNameToContentMap;
- if (identifiedNewModuleReferencesFromCmHandle.isEmpty()) {
- newModuleNameToContentMap = NO_NEW_MODULES;
- } else {
- newModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
- identifiedNewModuleReferencesFromCmHandle);
- }
- cpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
- yangModelCmHandle.getId(), newModuleNameToContentMap, allModuleReferencesFromCmHandle);
- if (StringUtils.isNotBlank(moduleSetTag)) {
- moduleSetTagCache.put(moduleSetTag, allModuleReferencesFromCmHandle);
- }
- }
-
/**
* Deletes the SchemaSet for schema set id if the SchemaSet Exists.
*
jsonObjectMapper.asJsonString(dmiRegistryProperties), OffsetDateTime.now());
}
- private void createOrUpgradeSchemaSetUsingModuleSetTag(final String schemaSetName,
- final String moduleSetTag,
- final String existingCmHandleAnchorName) {
+ private Collection<ModuleReference> syncAndCreateSchemaSet(final YangModelCmHandle yangModelCmHandle) {
+ final Collection<ModuleReference> allModuleReferencesFromCmHandle =
+ dmiModelOperations.getModuleReferences(yangModelCmHandle);
+ final Collection<ModuleReference> identifiedNewModuleReferencesFromCmHandle = cpsModuleService
+ .identifyNewModuleReferences(allModuleReferencesFromCmHandle);
+ final Map<String, String> newModuleNameToContentMap;
+ if (identifiedNewModuleReferencesFromCmHandle.isEmpty()) {
+ newModuleNameToContentMap = NO_NEW_MODULES;
+ } else {
+ newModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
+ identifiedNewModuleReferencesFromCmHandle);
+ }
+ cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
+ yangModelCmHandle.getId(), newModuleNameToContentMap, allModuleReferencesFromCmHandle);
+ return allModuleReferencesFromCmHandle;
+ }
+
+ private Collection<ModuleReference> upgradeOrCreateSchemaSetUsingModuleSetTag(final String schemaSetName,
+ final String moduleSetTag,
+ final String existingAnchorName,
+ final boolean inUpgrade) {
log.info("Found cm handle having module set tag: {}", moduleSetTag);
final Collection<ModuleReference> moduleReferencesFromExistingCmHandle =
cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
- existingCmHandleAnchorName);
- cpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
- schemaSetName, NO_NEW_MODULES, moduleReferencesFromExistingCmHandle);
- moduleSetTagCache.put(moduleSetTag, moduleReferencesFromExistingCmHandle);
+ existingAnchorName);
+ if (inUpgrade) {
+ cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
+ NO_NEW_MODULES, moduleReferencesFromExistingCmHandle);
+ } else {
+ cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
+ schemaSetName, NO_NEW_MODULES, moduleReferencesFromExistingCmHandle);
+ }
+ return moduleReferencesFromExistingCmHandle;
+ }
+
+ private String getModuleSetTag(final YangModelCmHandle yangModelCmHandle,
+ final CompositeState compositeState,
+ final boolean inUpgrade) {
+ if (inUpgrade) {
+ return ModuleOperationsUtils.getLockedCompositeStateDetails(compositeState.getLockReason())
+ .get(ModuleOperationsUtils.MODULE_SET_TAG_KEY);
+ }
+ return yangModelCmHandle.getModuleSetTag();
+ }
+
+ private void updateModuleSetTagCache(final String moduleSetTag,
+ final Collection<ModuleReference> allModuleReferencesFromCmHandle) {
+ if (StringUtils.isNotBlank(moduleSetTag)) {
+ moduleSetTagCache.putIfAbsent(moduleSetTag, allModuleReferencesFromCmHandle);
+ }
}
}
final YangModelCmHandle yangModelCmHandle =
YangDataConverter.convertCmHandleToYangModel(cmHandleAsDataNode, cmHandleId);
final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId);
+ final boolean inUpgrade = ModuleOperationsUtils.isInUpgradeOrUpgradeFailed(compositeState);
try {
- moduleSyncService.deleteSchemaSetIfExists(cmHandleId);
+ if (!inUpgrade) {
+ moduleSyncService.deleteSchemaSetIfExists(cmHandleId);
+ }
moduleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle);
yangModelCmHandle.getCompositeState().setLockReason(null);
cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.READY);
} catch (final Exception e) {
log.warn("Processing of {} module failed due to reason {}.", cmHandleId, e.getMessage());
- if (ModuleOperationsUtils.isInUpgradeOrUpgradeFailed(compositeState)) {
- moduleOperationsUtils.updateLockReasonDetailsAndAttempts(compositeState,
- LockReasonCategory.MODULE_UPGRADE_FAILED, e.getMessage());
- } else {
- moduleOperationsUtils.updateLockReasonDetailsAndAttempts(compositeState,
- LockReasonCategory.MODULE_SYNC_FAILED, e.getMessage());
- }
+ final LockReasonCategory lockReasonCategory = inUpgrade ? LockReasonCategory.MODULE_UPGRADE_FAILED
+ : LockReasonCategory.MODULE_SYNC_FAILED;
+ moduleOperationsUtils.updateLockReasonDetailsAndAttempts(compositeState,
+ lockReasonCategory, e.getMessage());
setCmHandleStateLocked(yangModelCmHandle, compositeState.getLockReason());
cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED);
}
(String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"),
(String) cmHandleDataNode.getLeaves().get("dmi-model-service-name"),
ncmpServiceCmHandle,
- (String) cmHandleDataNode.getLeaves().get("module-set-tag")
+ (String) cmHandleDataNode.getLeaves().get("module-set-tag"),
+ (String) cmHandleDataNode.getLeaves().get("alternate-id")
);
}
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+
+package org.onap.cps.ncmp.api.impl.yangmodels;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * Subscription event model to persist data into DB.
+ * Yang model subscription event
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@JsonInclude(Include.NON_NULL)
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+public class YangModelCmDataSubscriptionEvent {
+
+ @EqualsAndHashCode.Include
+ @JsonProperty("name")
+ private String name;
+
+ private List<CmHandle> cmHandles;
+
+ @AllArgsConstructor
+ @Data
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public static class CmHandle {
+
+ @JsonProperty()
+ private final String id;
+
+ private final List<Filter> filters;
+ }
+
+ @AllArgsConstructor
+ @Data
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public static class Filter {
+
+ @JsonProperty()
+ private final String id;
+
+ @JsonProperty()
+ private final List<String> subscribers;
+ }
+}
+
+
@JsonProperty("module-set-tag")
private String moduleSetTag;
+ @JsonProperty("alternate-id")
+ private String alternateId;
+
@JsonProperty("additional-properties")
private List<Property> dmiProperties;
copy.dmiProperties = original.getDmiProperties() == null ? null : new ArrayList<>(original.getDmiProperties());
copy.publicProperties =
original.getPublicProperties() == null ? null : new ArrayList<>(original.getPublicProperties());
+ copy.alternateId = original.getAlternateId();
return copy;
}
final String dmiDataServiceName,
final String dmiModelServiceName,
final NcmpServiceCmHandle ncmpServiceCmHandle,
- final String moduleSetTag) {
+ final String moduleSetTag,
+ final String alternateId) {
final YangModelCmHandle yangModelCmHandle = new YangModelCmHandle();
yangModelCmHandle.setId(ncmpServiceCmHandle.getCmHandleId());
yangModelCmHandle.setDmiServiceName(dmiServiceName);
yangModelCmHandle.setDmiDataServiceName(dmiDataServiceName);
yangModelCmHandle.setDmiModelServiceName(dmiModelServiceName);
yangModelCmHandle.setModuleSetTag(moduleSetTag == null ? StringUtils.EMPTY : moduleSetTag);
+ yangModelCmHandle.setAlternateId(alternateId);
yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties()));
yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties(
ncmpServiceCmHandle.getPublicProperties()));
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus;
/**
* Subscription event model to persist data into DB.
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.Map;
import lombok.Getter;
import lombok.NoArgsConstructor;
@JsonSetter(nulls = Nulls.AS_EMPTY)
private TrustLevel registrationTrustLevel;
- /**
- * NcmpServiceCmHandle copy constructor.
- *
- * @param ncmpServiceCmHandle Ncmp Service CmHandle
- */
- public NcmpServiceCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle) {
- this.cmHandleId = ncmpServiceCmHandle.getCmHandleId();
- this.dmiProperties = new LinkedHashMap<>(ncmpServiceCmHandle.getDmiProperties());
- this.publicProperties = new LinkedHashMap<>(ncmpServiceCmHandle.getPublicProperties());
- this.compositeState = ncmpServiceCmHandle.getCompositeState() != null ? new CompositeState(
- ncmpServiceCmHandle.getCompositeState()) : null;
- this.moduleSetTag = ncmpServiceCmHandle.getModuleSetTag();
- this.registrationTrustLevel = ncmpServiceCmHandle.getRegistrationTrustLevel();
- }
+ @JsonSetter(nulls = Nulls.AS_EMPTY)
+ private String alternateId;
}
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus;
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
+import org.onap.cps.api.CpsDataspaceService;
import org.onap.cps.api.CpsModuleService;
import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException;
import org.onap.cps.spi.CascadeDeleteAllowed;
@RequiredArgsConstructor
abstract class AbstractModelLoader implements ModelLoader {
- private final CpsAdminService cpsAdminService;
+ private final CpsDataspaceService cpsDataspaceService;
private final CpsModuleService cpsModuleService;
private final CpsDataService cpsDataService;
+ private final CpsAnchorService cpsAnchorService;
private static final int EXIT_CODE_ON_ERROR = 1;
void waitUntilDataspaceIsAvailable(final String dataspaceName) {
log.info("Model Loader start-up, waiting for database to be ready");
int attemptCount = 0;
- while (cpsAdminService.getDataspace(dataspaceName) == null) {
+ while (cpsDataspaceService.getDataspace(dataspaceName) == null) {
if (attemptCount < maximumAttemptCount) {
try {
Thread.sleep(attemptCount * retryTimeMs);
void createAnchor(final String dataspaceName, final String schemaSetName, final String anchorName) {
try {
- cpsAdminService.createAnchor(dataspaceName, schemaSetName, anchorName);
+ cpsAnchorService.createAnchor(dataspaceName, schemaSetName, anchorName);
} catch (final AlreadyDefinedException alreadyDefinedException) {
log.warn("Creating new anchor failed as anchor already exists");
} catch (final Exception exception) {
void updateAnchorSchemaSet(final String dataspaceName, final String anchorName, final String schemaSetName) {
try {
- cpsAdminService.updateAnchorSchemaSet(dataspaceName, anchorName, schemaSetName);
+ cpsAnchorService.updateAnchorSchemaSet(dataspaceName, anchorName, schemaSetName);
} catch (final Exception exception) {
log.error("Updating schema set failed: {}", exception.getMessage());
throw new NcmpStartUpException("Updating schema set failed", exception.getMessage());
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
+import org.onap.cps.api.CpsDataspaceService;
import org.onap.cps.api.CpsModuleService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
- public CmDataSubscriptionModelLoader(final CpsAdminService cpsAdminService,
+ public CmDataSubscriptionModelLoader(final CpsDataspaceService cpsDataspaceService,
final CpsModuleService cpsModuleService,
- final CpsDataService cpsDataService) {
- super(cpsAdminService, cpsModuleService, cpsDataService);
+ final CpsDataService cpsDataService,
+ final CpsAnchorService cpsAnchorService) {
+ super(cpsDataspaceService, cpsModuleService, cpsDataService, cpsAnchorService);
}
@Value("${ncmp.model-loader.subscription:true}")
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
+import org.onap.cps.api.CpsDataspaceService;
import org.onap.cps.api.CpsModuleService;
import org.springframework.stereotype.Service;
@Service
public class InventoryModelLoader extends AbstractModelLoader {
- private static final String NEW_MODEL_FILE_NAME = "dmi-registry@2023-08-23.yang";
- private static final String NEW_SCHEMA_SET_NAME = "dmi-registry-2023-08-23";
+ private static final String NEW_MODEL_FILE_NAME = "dmi-registry@2023-11-27.yang";
+ private static final String NEW_SCHEMA_SET_NAME = "dmi-registry-2023-11-27";
- public InventoryModelLoader(final CpsAdminService cpsAdminService,
+ public InventoryModelLoader(final CpsDataspaceService cpsDataspaceService,
final CpsModuleService cpsModuleService,
- final CpsDataService cpsDataService) {
- super(cpsAdminService, cpsModuleService, cpsDataService);
+ final CpsDataService cpsDataService,
+ final CpsAnchorService cpsAnchorService) {
+ super(cpsDataspaceService, cpsModuleService, cpsDataService, cpsAnchorService);
}
@Override
contact "toine.siebelink@est.tech";
+ revision "2023-11-27" {
+ description
+ "Added alternate-id";
+ }
+
+ revision "2023-08-23" {
+ description
+ "Added module-set-tag";
+ }
+
revision "2022-05-10" {
description
- "Added DataSyncEnabled, SyncState with State, LastSyncTime, DataStoreSyncState with Operational and Running syncstate";
+ "Added data-sync-enabled, sync-state with state, last-sync-time, data-store-sync-state with operational and running syncstate";
}
revision "2022-02-10" {
description
- "Added State, LockReason, LockReasonDetails to aid with cmHandle sync and timestamp to aid with retry/timeout scenarios";
+ "Added state, lock-reason, lock-reason-details to aid with cmHandle sync and timestamp to aid with retry/timeout scenarios";
}
revision "2021-12-13" {
description
- "Added new list of public additional properties for a Cm-Handle which are exposed to clients of the NCMP interface";
+ "Added new list of public-properties and additional-properties for a Cm-Handle which are exposed to clients of the NCMP interface";
}
revision "2021-10-20" {
leaf dmi-model-service-name {
type string;
}
+ leaf module-set-tag {
+ type string;
+ }
+ leaf alternate-id {
+ type string;
+ }
list additional-properties {
key "name";
}
}
}
-}
\ No newline at end of file
+}
+
}
and: 'state handler is invoked with the expected parameters'
1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
- args ->
- {
- def cmHandleStatePerCmHandle = (args[0] as Map)
- cmHandleStatePerCmHandle.each {
- assert (it.id == 'cmhandle' && it.dmiServiceName == 'my-server')
- }
+ args -> {
+ def yangModelCmHandles = args[0]
+ assert yangModelCmHandles.id == ['cmhandle']
+ assert yangModelCmHandles.dmiServiceName == ['my-server']
}
}
where:
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.cps.ncmp.api.impl.config
+
+import java.time.Duration
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.context.properties.EnableConfigurationProperties
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.test.context.TestPropertySource
+import org.springframework.test.context.support.AnnotationConfigContextLoader
+import spock.lang.Specification
+
+@SpringBootTest
+@ContextConfiguration(classes = [HttpClientConfiguration])
+@EnableConfigurationProperties(HttpClientConfiguration.class)
+@TestPropertySource(properties = ["ncmp.dmi.httpclient.connectionTimeoutInSeconds=1", "ncmp.dmi.httpclient.maximumConnectionsTotal=200"])
+class HttpClientConfigurationSpec extends Specification {
+
+ @Autowired
+ private HttpClientConfiguration httpClientConfiguration
+
+ def 'Test HttpClientConfiguration properties with custom and default values'() {
+ expect: 'custom property values'
+ assert httpClientConfiguration.getConnectionTimeoutInSeconds() == Duration.ofSeconds(1)
+ assert httpClientConfiguration.getMaximumConnectionsTotal() == 200
+ and: 'default property values'
+ assert httpClientConfiguration.getMaximumConnectionsPerRoute() == 50
+ assert httpClientConfiguration.getIdleConnectionEvictionThresholdInSeconds() == Duration.ofSeconds(5)
+ }
+}
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
package org.onap.cps.ncmp.api.impl.config
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.http.MediaType
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
import org.springframework.test.context.ContextConfiguration
import org.springframework.web.client.RestTemplate
import spock.lang.Specification
@SpringBootTest
-@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties])
+@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, HttpClientConfiguration])
class NcmpConfigurationSpec extends Specification{
@Autowired
NcmpConfiguration.DmiProperties dmiProperties
-
+
+ @Autowired
+ HttpClientConfiguration httpClientConfiguration
+
def mockRestTemplateBuilder = new RestTemplateBuilder()
def 'NcmpConfiguration Construction.'() {
dmiProperties.authPassword == 'some-password'
}
- def 'Rest Template creation.'() {
+ def 'Rest Template creation with CloseableHttpClient and MappingJackson2HttpMessageConverter.'() {
when: 'a rest template is created'
- def result = NcmpConfiguration.restTemplate(mockRestTemplateBuilder)
+ def result = NcmpConfiguration.restTemplate(mockRestTemplateBuilder, httpClientConfiguration)
then: 'the rest template is returned'
assert result instanceof RestTemplate
+ and: 'the rest template is created with httpclient5'
+ assert result.getRequestFactory() instanceof HttpComponentsClientHttpRequestFactory
+ assert ((HttpComponentsClientHttpRequestFactory) result.getRequestFactory()).getHttpClient() instanceof CloseableHttpClient;
and: 'a jackson media converter has been added'
def lastMessageConverter = result.getMessageConverters().get(result.getMessageConverters().size()-1)
lastMessageConverter instanceof MappingJackson2HttpMessageConverter
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.subscriptions
+package org.onap.cps.ncmp.api.impl.deprecated.subscriptions
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NO_TIMESTAMP
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription
import com.fasterxml.jackson.databind.ObjectMapper
import org.mapstruct.factory.Mappers
def 'Map clients subscription event to ncmps subscription event'() {
given: 'a Subscription Event'
- def jsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpInEvent.json')
+ def jsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpInEvent.json')
def testEventToMap = jsonObjectMapper.convertJsonString(jsonData, CmSubscriptionNcmpInEvent.class)
when: 'the client event is mapped to a ncmp subscription event'
def result = objectUnderTest.toCmSubscriptionDmiInEvent(testEventToMap)
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
import io.cloudevents.CloudEvent
import io.cloudevents.core.builder.CloudEventBuilder
import org.apache.kafka.clients.consumer.ConsumerRecord
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistenceImpl
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionPersistenceImpl
import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
import org.onap.cps.ncmp.events.cmsubscription1_0_0.dmi_to_ncmp.CmSubscriptionDmiOutEvent
import org.onap.cps.ncmp.utils.TestUtils
}
def getDmiOutEvent() {
- def cmSubscriptionDmiOutEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionDmiOutEvent.json')
+ def cmSubscriptionDmiOutEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionDmiOutEvent.json')
return jsonObjectMapper.convertJsonString(cmSubscriptionDmiOutEventJsonData, CmSubscriptionDmiOutEvent.class)
}
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription
import com.fasterxml.jackson.databind.ObjectMapper
import org.mapstruct.factory.Mappers
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus
import org.onap.cps.ncmp.events.cmsubscription1_0_0.dmi_to_ncmp.CmSubscriptionDmiOutEvent
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
def 'Map dmi out event to yang model subscription event'() {
given: 'a dmi out event'
- def jsonData = TestUtils.getResourceFileContent('cmSubscriptionDmiOutEvent.json')
+ def jsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionDmiOutEvent.json')
def testEventToMap = jsonObjectMapper.convertJsonString(jsonData, CmSubscriptionDmiOutEvent.class)
when: 'the event is mapped to a yang model subscription'
def result = objectUnderTest.toYangModelSubscriptionEvent(testEventToMap)
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription
import com.fasterxml.jackson.databind.ObjectMapper
import org.mapstruct.factory.Mappers
def 'Map cm subscription event to ncmp out event'() {
given: 'a cm subscription event'
- def cmSubscriptionEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionEvent.json')
+ def cmSubscriptionEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionEvent.json')
def cmSubscriptionEvent = jsonObjectMapper.convertJsonString(cmSubscriptionEventJsonData, CmSubscriptionEvent.class)
when: 'cm subscription event is mapped to ncmp out event'
def result = objectUnderTest.toCmSubscriptionNcmpOutEvent(cmSubscriptionEvent)
def 'Map cm subscription event to ncmp out event with the given scenarios causes an exception'() {
given: 'a cm subscription event'
- def cmSubscriptionEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionEvent.json')
+ def cmSubscriptionEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionEvent.json')
def cmSubscriptionEvent = jsonObjectMapper.convertJsonString(cmSubscriptionEventJsonData, CmSubscriptionEvent.class)
and: 'set cm subscription status with given scenarios'
cmSubscriptionEvent.setCmSubscriptionStatus(subscriptionStatusList)
def 'Map cm subscription event to ncmp out event without any exception'() {
given: 'a cm subscription Event'
- def subscriptionResponseJsonData = TestUtils.getResourceFileContent('cmSubscriptionEvent.json')
+ def subscriptionResponseJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionEvent.json')
def subscriptionResponseEvent = jsonObjectMapper.convertJsonString(subscriptionResponseJsonData, CmSubscriptionEvent.class)
when: 'cm subscription event is mapped to ncmp out event'
objectUnderTest.toCmSubscriptionNcmpOutEvent(subscriptionResponseEvent)
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription
import com.fasterxml.jackson.databind.ObjectMapper
import io.cloudevents.CloudEvent
import io.cloudevents.core.builder.CloudEventBuilder
import org.apache.kafka.clients.consumer.ConsumerRecord
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionPersistence
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelSubscriptionEvent
import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
import org.onap.cps.ncmp.events.cmsubscription1_0_0.client_to_ncmp.CmSubscriptionNcmpInEvent
def 'Consume, persist and forward valid CM create message'() {
given: 'an event with data category CM'
- def jsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpInEvent.json')
+ def jsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpInEvent.json')
def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmSubscriptionNcmpInEvent.class)
testEventSent.getData().getDataType().setDataCategory(dataCategory)
def testCloudEventSent = CloudEventBuilder.v1()
def 'Consume event with wrong datastore causes an exception'() {
given: 'an event'
- def jsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpInEvent.json')
+ def jsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpInEvent.json')
def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmSubscriptionNcmpInEvent.class)
and: 'datastore is set to a passthrough-running datastore'
testEventSent.getData().getPredicates().setDatastore('operational')
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription
import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
import io.cloudevents.CloudEvent
import org.mapstruct.factory.Mappers
import org.onap.cps.ncmp.api.impl.events.EventsPublisher
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionPersistence
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus
import org.onap.cps.ncmp.api.impl.utils.CmSubscriptionEventCloudMapper
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelSubscriptionEvent.TargetCmHandle
def 'Forward valid CM create subscription and simulate timeout'() {
given: 'a ncmp in event'
- def ncmpInEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpInEvent.json')
+ def ncmpInEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpInEvent.json')
def ncmpInEventJson = jsonObjectMapper.convertJsonString(ncmpInEventJsonData, CmSubscriptionNcmpInEvent.class)
and: 'the InventoryPersistence returns private properties for the supplied CM Handles'
1 * mockInventoryPersistence.getYangModelCmHandles(["CMHandle1", "CMHandle2", "CMHandle3"]) >> [
def 'Forward CM create subscription where target CM Handles are #scenario'() {
given: 'a ncmp in event'
- def ncmpInEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpInEvent.json')
+ def ncmpInEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpInEvent.json')
def ncmpInEventJson = jsonObjectMapper.convertJsonString(ncmpInEventJsonData, CmSubscriptionNcmpInEvent.class)
and: 'the target CMHandles are set to #scenario'
ncmpInEventJson.getData().getPredicates().setTargets(invalidTargets)
def 'Forward valid CM create subscription where targets are not associated to any existing CMHandles'() {
given: 'a ncmp in event'
- def ncmpInEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpInEvent.json')
+ def ncmpInEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpInEvent.json')
def ncmpInEventJson = jsonObjectMapper.convertJsonString(ncmpInEventJsonData, CmSubscriptionNcmpInEvent.class)
and: 'the InventoryPersistence returns no private properties for the supplied CM Handles'
1 * mockInventoryPersistence.getYangModelCmHandles(["CMHandle1", "CMHandle2", "CMHandle3"]) >> []
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription
import com.fasterxml.jackson.databind.ObjectMapper
import org.mapstruct.factory.Mappers
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionStatus
import org.onap.cps.ncmp.events.cmsubscription1_0_0.client_to_ncmp.CmSubscriptionNcmpInEvent
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
def 'Map subscription event to yang model subscription event where #scenario'() {
given: 'a Subscription Event'
- def jsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpInEvent.json')
+ def jsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpInEvent.json')
def testEventToMap = jsonObjectMapper.convertJsonString(jsonData, CmSubscriptionNcmpInEvent.class)
when: 'the event is mapped to a yang model subscription'
def result = objectUnderTest.toYangModelSubscriptionEvent(testEventToMap)
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.events.cmsubscription
+package org.onap.cps.ncmp.api.impl.events.deprecated.cmsubscription
import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESSFULLY_APPLIED_SUBSCRIPTION
import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_PENDING
import io.cloudevents.core.builder.CloudEventBuilder
import org.mapstruct.factory.Mappers
import org.onap.cps.ncmp.api.impl.events.EventsPublisher
-import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence
+import org.onap.cps.ncmp.api.impl.deprecated.subscriptions.SubscriptionPersistence
import org.onap.cps.ncmp.api.impl.utils.DataNodeBaseSpec
import org.onap.cps.ncmp.api.impl.utils.SubscriptionOutcomeCloudMapper
import org.onap.cps.ncmp.api.models.CmSubscriptionEvent
def 'Send response to the client apps successfully'() {
given: 'a cm subscription event'
- def cmSubscriptionEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionEvent.json')
+ def cmSubscriptionEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionEvent.json')
def cmSubscriptionEvent = jsonObjectMapper.convertJsonString(cmSubscriptionEventJsonData, CmSubscriptionEvent.class)
and: 'a ncmp out event'
- def ncmpOutEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpOutEvent2.json')
+ def ncmpOutEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpOutEvent2.json')
def ncmpOutEvent = jsonObjectMapper.convertJsonString(ncmpOutEventJsonData, CmSubscriptionNcmpOutEvent.class)
and: 'a random id for the cloud event'
SubscriptionOutcomeCloudMapper.randomId = 'some-id'
def 'Create ncmp out message as expected'() {
given: 'a cm subscription event'
- def cmSubscriptionEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionEvent.json')
+ def cmSubscriptionEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionEvent.json')
def cmSubscriptionEvent = jsonObjectMapper.convertJsonString(cmSubscriptionEventJsonData, CmSubscriptionEvent.class)
and: 'a ncmp out event'
- def ncmpOutEventJsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpOutEvent.json')
+ def ncmpOutEventJsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpOutEvent.json')
def ncmpOutEvent = jsonObjectMapper.convertJsonString(ncmpOutEventJsonData, CmSubscriptionNcmpOutEvent.class)
and: 'a status code and status message a per #scenarios'
ncmpOutEvent.getData().setStatusCode(statusCode)
package org.onap.cps.ncmp.api.impl.inventory
+import org.onap.cps.api.CpsAnchorService
+
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.api.CpsAdminService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
-import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
-import org.onap.cps.ncmp.api.impl.inventory.CompositeState
-import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistenceImpl
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.spi.CascadeDeleteAllowed
import org.onap.cps.spi.FetchDescendantsOption
def mockCpsModuleService = Mock(CpsModuleService)
- def mockCpsAdminService = Mock(CpsAdminService)
+ def mockCpsAnchorService = Mock(CpsAnchorService)
def mockCpsValidator = Mock(CpsValidator)
def objectUnderTest = new InventoryPersistenceImpl(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService,
- mockCpsValidator, mockCpsAdminService)
+ mockCpsValidator, mockCpsAnchorService)
def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
.format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
when: 'the method to get cm handles is called'
objectUnderTest.getCmHandleIdsWithGivenModules(['sample-module-name'])
then: 'the admin persistence service method to query anchors is invoked once with the same parameter'
- 1 * mockCpsAdminService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, ['sample-module-name'])
+ 1 * mockCpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, ['sample-module-name'])
}
def 'Replace list content'() {
assert compositeState.lockReason.lockReasonCategory == MODULE_SYNC_FAILED
assert compositeState.lockReason.details.contains(expectedDetails)
where:
- scenario | lockReason || expectedDetails
- 'does not exist' | null || 'Attempt #1 failed: new error message'
- 'exists' | CompositeState.LockReason.builder().details("Attempt #2 failed: some error message").build() || 'Attempt #3 failed: new error message'
+ scenario | lockReason || expectedDetails
+ 'does not exist' | null || 'Attempt #1 failed: new error message'
+ 'exists' | CompositeState.LockReason.builder().details("Attempt #2 failed: some error message").build() || 'Attempt #3 failed: new error message'
+ }
+
+ def 'Update lock reason details that contains #scenario'() {
+ given: 'A locked state'
+ def compositeState = new CompositeStateBuilder().withCmHandleState(CmHandleState.LOCKED)
+ .withLockReason(MODULE_UPGRADE, "Upgrade to ModuleSetTag: " + moduleSetTag).build()
+ when: 'update cm handle details'
+ objectUnderTest.updateLockReasonDetailsAndAttempts(compositeState, MODULE_UPGRADE_FAILED, 'new error message')
+ then: 'the composite state lock reason and details are updated'
+ assert compositeState.lockReason.lockReasonCategory == MODULE_UPGRADE_FAILED
+ assert compositeState.lockReason.details.contains("Upgrade to ModuleSetTag: " + expectedDetails)
+ where:
+ scenario | moduleSetTag || expectedDetails
+ 'a module set tag' | 'someModuleSetTag' || 'someModuleSetTag'
+ 'empty module set tag' | '' || 'not-specified'
}
def 'Get all locked Cm-Handle where Lock Reason is MODULE_SYNC_FAILED cm handle #scenario'() {
package org.onap.cps.ncmp.api.impl.inventory.sync
+import org.onap.cps.api.CpsAnchorService
+
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE
import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.model.DataNode
-import org.onap.cps.api.CpsAdminService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.spi.model.DataNodeBuilder
def mockCpsModuleService = Mock(CpsModuleService)
def mockDmiModelOperations = Mock(DmiModelOperations)
- def mockCpsAdminService = Mock(CpsAdminService)
+ def mockCpsAnchorService = Mock(CpsAnchorService)
def mockCmHandleQueries = Mock(CmHandleQueries)
def mockCpsDataService = Mock(CpsDataService)
def mockJsonObjectMapper = Mock(JsonObjectMapper)
def mockModuleSetTagCache = [:]
- def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, mockCpsAdminService,
- mockCmHandleQueries, mockCpsDataService, mockJsonObjectMapper, mockModuleSetTagCache)
+ def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService,
+ mockCmHandleQueries, mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper, mockModuleSetTagCache)
def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
def static cmHandleWithModuleSetTag = new DataNodeBuilder().withXpath("//cm-handles[@module-set-tag='tag-1'][@id='otherId']").withAnchor('otherId').build()
def ncmpServiceCmHandle = new NcmpServiceCmHandle()
ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.ADVISED).build())
ncmpServiceCmHandle.cmHandleId = 'ch-1'
- def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('some service name', '', '', ncmpServiceCmHandle, moduleSetTag)
+ def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('some service name', '', '', ncmpServiceCmHandle, moduleSetTag, '')
and: 'DMI operations returns some module references'
def moduleReferences = [ new ModuleReference('module1','1'), new ModuleReference('module2','2') ]
mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences
when: 'module sync is triggered'
objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
then: 'create schema set from module is invoked with correct parameters'
- 1 * mockCpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', newModuleNameContentToMap, moduleReferences)
+ 1 * mockCpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', newModuleNameContentToMap, moduleReferences)
and: 'anchor is created with the correct parameters'
- 1 * mockCpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', 'ch-1')
+ 1 * mockCpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', 'ch-1')
where: 'the following parameters are used'
scenario | existingModuleResourcesInCps | identifiedNewModuleReferences | newModuleNameContentToMap | moduleSetTag
'one new module' | [['module2': '2'], ['module3': '3']] | [['module1': '1']] | [module1: 'some yang source'] | ''
ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder().withLockReason(MODULE_UPGRADE, 'Upgrade to ModuleSetTag: tag-1').build())
def dmiServiceName = 'some service name'
ncmpServiceCmHandle.cmHandleId = 'upgraded-ch'
- def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'tag-1')
+ def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'tag-1', '')
and: 'some module references'
def moduleReferences = [ new ModuleReference('module1','1') ]
and: 'cache or DMI operations returns some module references for upgraded cm handle'
mockCmHandleQueries.cmHandleHasState('otherId', CmHandleState.READY) >> true
when: 'module sync is triggered'
objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
- then: 'create schema set from module is invoked for the upgraded cm handle'
- 1 * mockCpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'upgraded-ch', [:], moduleReferences)
+ then: 'update schema set from module is invoked for the upgraded cm handle'
+ expectedCallsToUpgradeSchemaSet * mockCpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'upgraded-ch', [:], moduleReferences)
+ and: 'create schema set from module is invoked for the upgraded cm handle'
+ expectedCallsToCeateSchemaSet * mockCpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'upgraded-ch', [:], moduleReferences)
and: 'No anchor is created for the upgraded cm handle'
- 0 * mockCpsAdminService.createAnchor(*_)
+ 0 * mockCpsAnchorService.createAnchor(*_)
where: 'the following parameters are used'
- scenario | populateCache | existingCmHandlesWithSameTag
- 'new' | false | []
- 'in cache' | true | []
- 'in database' | false | [cmHandleWithModuleSetTag]
+ scenario | populateCache | existingCmHandlesWithSameTag || expectedCallsToUpgradeSchemaSet | expectedCallsToCeateSchemaSet
+ 'new' | false | [] || 0 | 1
+ 'in cache' | true | [] || 1 | 0
+ 'in database' | false | [cmHandleWithModuleSetTag] || 1 | 0
}
def 'upgrade model for a existing cm handle'() {
ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder()
.withLockReason(MODULE_UPGRADE, 'Upgrade to ModuleSetTag: targetModuleSetTag').build())
ncmpServiceCmHandle.setCmHandleId('cmHandleId-1')
- def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('some service name', '', '', ncmpServiceCmHandle, 'targetModuleSetTag')
+ def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('some service name', '', '', ncmpServiceCmHandle, 'targetModuleSetTag', '')
mockCmHandleQueries.cmHandleHasState('cmHandleId-1', CmHandleState.READY) >> true
and: 'the module service returns some module references'
def moduleReferences = [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
when: 'module upgrade is triggered'
objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
then: 'the upgrade is delegated to the module service (with the correct parameters)'
- 1 * mockCpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', Collections.emptyMap(), moduleReferences)
+ 1 * mockCpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', Collections.emptyMap(), moduleReferences)
}
def 'Delete Schema Set for CmHandle'() {
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.utils
+package org.onap.cps.ncmp.api.impl.util.deprecated
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import io.cloudevents.core.builder.CloudEventBuilder
+import org.onap.cps.ncmp.api.impl.utils.CmSubscriptionEventCloudMapper
import org.onap.cps.ncmp.events.cmsubscription1_0_0.ncmp_to_dmi.CmSubscriptionDmiInEvent
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
def 'Map the subscription event to data of the cloud event'() {
given: 'a subscription event'
- def jsonData = TestUtils.getResourceFileContent('cmSubscriptionDmiInEvent.json')
+ def jsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionDmiInEvent.json')
def testEventData = jsonObjectMapper.convertJsonString(jsonData,
CmSubscriptionDmiInEvent.class)
def testCloudEvent = CloudEventBuilder.v1()
def jsonProcessingException = new JsonProcessingException('The Cloud Event could not be constructed')
spyObjectMapper.writeValueAsBytes(_) >> { throw jsonProcessingException }
and: 'a subscription event of ncmp version'
- def jsonData = TestUtils.getResourceFileContent('cmSubscriptionDmiInEvent.json')
+ def jsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionDmiInEvent.json')
def testEventData = jsonObjectMapper.convertJsonString(jsonData,
CmSubscriptionDmiInEvent.class)
when: 'the subscription event map to cloud event'
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.utils
+package org.onap.cps.ncmp.api.impl.util.deprecated
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import io.cloudevents.core.builder.CloudEventBuilder
+import org.onap.cps.ncmp.api.impl.utils.SubscriptionOutcomeCloudMapper
import org.onap.cps.ncmp.events.cmsubscription1_0_0.ncmp_to_client.CmSubscriptionNcmpOutEvent
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
def 'Map the subscription outcome to cloud event'() {
given: 'a subscription event'
- def jsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpOutEvent.json')
+ def jsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpOutEvent.json')
def testEventData = jsonObjectMapper.convertJsonString(jsonData, CmSubscriptionNcmpOutEvent.class)
def testCloudEvent = CloudEventBuilder.v1()
.withData(objectMapper.writeValueAsBytes(testEventData))
def jsonProcessingException = new JsonProcessingException('The Cloud Event could not be constructed')
spyObjectMapper.writeValueAsBytes(_) >> { throw jsonProcessingException }
and: 'a cloud event having a subscription outcome in the data part'
- def jsonData = TestUtils.getResourceFileContent('cmSubscriptionNcmpOutEvent.json')
+ def jsonData = TestUtils.getResourceFileContent('deprecatedCmSubscription/cmSubscriptionNcmpOutEvent.json')
def testEventData = jsonObjectMapper.convertJsonString(jsonData, CmSubscriptionNcmpOutEvent.class)
when: 'the subscription outcome map to cloud event'
def expectedResult = objectUnderTest.toCloudEvent(testEventData, 'some-key', 'some-event-type')
class DmiServiceUrlBuilderSpec extends Specification {
static YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('dmiServiceName',
- 'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'),'')
+ 'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'),'my-module-set-tag', 'my-alternate-id')
NcmpConfiguration.DmiProperties dmiProperties = new NcmpConfiguration.DmiProperties()
.withOperationalDataStores(DataStoreSyncState.SYNCHRONIZED, 'some-sync-time').build()
ncmpServiceCmHandle.setCompositeState(compositeState)
when: 'it is converted to a yang model cm handle'
- def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('', '', '', ncmpServiceCmHandle,'')
+ def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('', '', '', ncmpServiceCmHandle,'my-module-set-tag', 'my-alternate-id')
then: 'the result has the right size'
assert objectUnderTest.dmiProperties.size() == 1
+ and: 'the result has the correct values for module set tag and alternate ID'
+ assert objectUnderTest.moduleSetTag == 'my-module-set-tag'
+ assert objectUnderTest.alternateId == 'my-alternate-id'
and: 'the DMI property in the result has the correct name and value'
assert objectUnderTest.dmiProperties[0].name == 'myDmiProperty'
assert objectUnderTest.dmiProperties[0].value == 'value1'
def 'Resolve DMI service name: #scenario and #requiredService service require.'() {
given: 'a yang model cm handle'
def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName,
- dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'),'')
+ dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'),'', '')
expect:
assert objectUnderTest.resolveDmiServiceName(requiredService) == expectedService
where:
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.api.models
-
-import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
-import org.onap.cps.ncmp.api.impl.inventory.CompositeState
-import spock.lang.Specification
-
-class NcmpServiceCmHandleSpec extends Specification {
-
-
- def 'NCMP Service CmHandle check for deep copy operation'() {
- given: 'ncmp service cm handle'
- def originalNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'cmhandleid',
- dmiProperties: ['property1': 'value1', 'property2': 'value2'],
- publicProperties: ['pubproperty1': 'value1', 'pubproperty2': 'value2'],
- compositeState: new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: Boolean.FALSE))
- when: 'we create a deep copy'
- def deepCopiedNcmpServiceCmHandle = new NcmpServiceCmHandle(originalNcmpServiceCmHandle)
- and: 'we change the original ncmp service cmhandle'
- originalNcmpServiceCmHandle.dmiProperties = ['newProperty1': 'newValue1']
- originalNcmpServiceCmHandle.publicProperties = ['newPublicProperty1': 'newPubValue1']
- originalNcmpServiceCmHandle.compositeState = new CompositeState(cmHandleState: CmHandleState.DELETED, dataSyncEnabled: Boolean.TRUE)
- then: 'no change in the copied dmi and public properties of ncmp service cmhandle'
- deepCopiedNcmpServiceCmHandle.dmiProperties == ['property1': 'value1', 'property2': 'value2']
- deepCopiedNcmpServiceCmHandle.publicProperties == ['pubproperty1': 'value1', 'pubproperty2': 'value2']
- and: 'no change in the composite state'
- deepCopiedNcmpServiceCmHandle.compositeState.cmHandleState == CmHandleState.ADVISED
- deepCopiedNcmpServiceCmHandle.compositeState.dataSyncEnabled == Boolean.FALSE
- }
-
-}
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.core.read.ListAppender
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsDataspaceService
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException
class AbstractModelLoaderSpec extends Specification {
- def mockCpsAdminService = Mock(CpsAdminService)
+ def mockCpsDataspaceService = Mock(CpsDataspaceService)
def mockCpsModuleService = Mock(CpsModuleService)
def mockCpsDataService = Mock(CpsDataService)
- def objectUnderTest = Spy(new TestModelLoader(mockCpsAdminService, mockCpsModuleService, mockCpsDataService))
+ def mockCpsAnchorService = Mock(CpsAnchorService)
+ def objectUnderTest = Spy(new TestModelLoader(mockCpsDataspaceService, mockCpsModuleService, mockCpsDataService, mockCpsAnchorService))
def applicationContext = new AnnotationConfigApplicationContext()
def loggingListAppender
void setup() {
- yangResourceToContentMap = objectUnderTest.createYangResourcesToContentMap('subscription.yang')
+ yangResourceToContentMap = objectUnderTest.createYangResourcesToContentMap('cm-data-subscriptions@2023-11-13.yang')
logger.setLevel(Level.DEBUG)
loggingListAppender = new ListAppender()
logger.addAppender(loggingListAppender)
def 'Create schema set.'() {
when: 'creating a schema set'
- objectUnderTest.createSchemaSet('some dataspace','new name','subscription.yang')
+ objectUnderTest.createSchemaSet('some dataspace','new name','cm-data-subscriptions@2023-11-13.yang')
then: 'the operation is delegated to the admin service'
1 * mockCpsModuleService.createSchemaSet('some dataspace','new name',_)
}
given: 'the module service throws an already defined exception'
mockCpsModuleService.createSchemaSet(*_) >> { throw AlreadyDefinedException.forSchemaSet('name','context',null) }
when: 'attempt to create a schema set'
- objectUnderTest.createSchemaSet('some dataspace','new name','subscription.yang')
+ objectUnderTest.createSchemaSet('some dataspace','new name','cm-data-subscriptions@2023-11-13.yang')
then: 'the exception is ignored i.e. no exception thrown up'
noExceptionThrown()
and: 'the exception message is logged'
when: 'creating an anchor'
objectUnderTest.createAnchor('some dataspace','some schema set','new name')
then: 'the operation is delegated to the admin service'
- 1 * mockCpsAdminService.createAnchor('some dataspace','some schema set', 'new name')
+ 1 * mockCpsAnchorService.createAnchor('some dataspace','some schema set', 'new name')
}
def 'Create anchor with already defined exception.'() {
given: 'the admin service throws an already defined exception'
- mockCpsAdminService.createAnchor(*_)>> { throw AlreadyDefinedException.forAnchor('name','context',null) }
+ mockCpsAnchorService.createAnchor(*_)>> { throw AlreadyDefinedException.forAnchor('name','context',null) }
when: 'attempt to create anchor'
objectUnderTest.createAnchor('some dataspace','some schema set','new name')
then: 'the exception is ignored i.e. no exception thrown up'
def 'Create anchor with any other exception.'() {
given: 'the admin service throws a exception'
- mockCpsAdminService.createAnchor(*_)>> { throw new RuntimeException('test message') }
+ mockCpsAnchorService.createAnchor(*_)>> { throw new RuntimeException('test message') }
when: 'attempt to create anchor'
objectUnderTest.createAnchor('some dataspace','some schema set','new name')
then: 'a startup exception with correct message and details is thrown'
when: 'a schema set for an anchor is updated'
objectUnderTest.updateAnchorSchemaSet('some dataspace', 'anchor', 'new schema set')
then: 'the request is delegated to the admin service'
- 1 * mockCpsAdminService.updateAnchorSchemaSet('some dataspace', 'anchor', 'new schema set')
+ 1 * mockCpsAnchorService.updateAnchorSchemaSet('some dataspace', 'anchor', 'new schema set')
}
def 'Update anchor schema set with exception.'() {
given: 'the admin service throws an exception'
- mockCpsAdminService.updateAnchorSchemaSet(*_) >> { throw new RuntimeException('test message') }
+ mockCpsAnchorService.updateAnchorSchemaSet(*_) >> { throw new RuntimeException('test message') }
when: 'a schema set for an anchor is updated'
objectUnderTest.updateAnchorSchemaSet('some dataspace', 'anchor', 'new schema set')
then: 'a startup exception with correct message and details is thrown'
class TestModelLoader extends AbstractModelLoader {
- TestModelLoader(final CpsAdminService cpsAdminService,
+ TestModelLoader(final CpsDataspaceService cpsDataspaceService,
final CpsModuleService cpsModuleService,
- final CpsDataService cpsDataService) {
- super(cpsAdminService, cpsModuleService, cpsDataService)
+ final CpsDataService cpsDataService,
+ final CpsAnchorService cpsAnchorService) {
+ super(cpsDataspaceService, cpsModuleService, cpsDataService, cpsAnchorService)
super.maximumAttemptCount = 2
super.retryTimeMs = 1
}
package org.onap.cps.ncmp.init
+import org.onap.cps.api.CpsAnchorService
+
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.core.read.ListAppender
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsDataspaceService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.spi.model.Dataspace
class CmDataSubscriptionModelLoaderSpec extends Specification {
- def mockCpsAdminService = Mock(CpsAdminService)
+ def mockCpsDataspaceService = Mock(CpsDataspaceService)
def mockCpsModuleService = Mock(CpsModuleService)
def mockCpsDataService = Mock(CpsDataService)
- def objectUnderTest = new CmDataSubscriptionModelLoader(mockCpsAdminService, mockCpsModuleService, mockCpsDataService)
+ def mockCpsAnchorService = Mock(CpsAnchorService)
+ def objectUnderTest = new CmDataSubscriptionModelLoader(mockCpsDataspaceService, mockCpsModuleService, mockCpsDataService, mockCpsAnchorService)
def applicationContext = new AnnotationConfigApplicationContext()
given:'model loader is enabled'
objectUnderTest.subscriptionModelLoaderEnabled = true
and: 'dataspace is ready for use'
- mockCpsAdminService.getDataspace(NCMP_DATASPACE_NAME) >> new Dataspace('')
+ mockCpsDataspaceService.getDataspace(NCMP_DATASPACE_NAME) >> new Dataspace('')
when: 'the application is ready'
objectUnderTest.onApplicationEvent(Mock(ApplicationReadyEvent))
then: 'the module service to create schema set is called once'
1 * mockCpsModuleService.createSchemaSet(NCMP_DATASPACE_NAME, 'cm-data-subscriptions', expectedYangResourcesToContentMap)
and: 'the admin service to create an anchor set is called once'
- 1 * mockCpsAdminService.createAnchor(NCMP_DATASPACE_NAME, 'cm-data-subscriptions', 'cm-data-subscriptions')
+ 1 * mockCpsAnchorService.createAnchor(NCMP_DATASPACE_NAME, 'cm-data-subscriptions', 'cm-data-subscriptions')
and: 'the data service to create a top level datanode is called once'
1 * mockCpsDataService.saveData(NCMP_DATASPACE_NAME, 'cm-data-subscriptions', '{"datastores":{}}', _)
}
when: 'application is ready'
objectUnderTest.onApplicationEvent(Mock(ApplicationReadyEvent))
then: 'no interaction with admin service'
- 0 * mockCpsAdminService.getDataspace(_)
+ 0 * mockCpsDataspaceService.getDataspace(_)
then: 'a message is logged that the function is disabled'
def logs = loggingListAppender.list.toString()
assert logs.contains('Subscription Model Loader is disabled')
package org.onap.cps.ncmp.init
+import org.onap.cps.api.CpsAnchorService
+
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.core.read.ListAppender
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsDataspaceService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.spi.model.Dataspace
class InventoryModelLoaderSpec extends Specification {
- def mockCpsAdminService = Mock(CpsAdminService)
+ def mockCpsAdminService = Mock(CpsDataspaceService)
def mockCpsModuleService = Mock(CpsModuleService)
def mockCpsDataService = Mock(CpsDataService)
- def objectUnderTest = new InventoryModelLoader(mockCpsAdminService, mockCpsModuleService, mockCpsDataService)
+ def mockCpsAnchorService = Mock(CpsAnchorService)
+ def objectUnderTest = new InventoryModelLoader(mockCpsAdminService, mockCpsModuleService, mockCpsDataService, mockCpsAnchorService)
def applicationContext = new AnnotationConfigApplicationContext()
def loggingListAppender
void setup() {
- expectedYangResourceToContentMap = objectUnderTest.createYangResourcesToContentMap('dmi-registry@2023-08-23.yang')
+ expectedYangResourceToContentMap = objectUnderTest.createYangResourcesToContentMap('dmi-registry@2023-11-27.yang')
logger.setLevel(Level.DEBUG)
loggingListAppender = new ListAppender()
logger.addAppender(loggingListAppender)
when: 'the application is ready'
objectUnderTest.onApplicationEvent(Mock(ApplicationReadyEvent))
then: 'the module service is used to create the new schema set from the correct resource'
- 1 * mockCpsModuleService.createSchemaSet(NCMP_DATASPACE_NAME, 'dmi-registry-2023-08-23', expectedYangResourceToContentMap)
+ 1 * mockCpsModuleService.createSchemaSet(NCMP_DATASPACE_NAME, 'dmi-registry-2023-11-27', expectedYangResourceToContentMap)
and: 'the admin service is used to update the anchor'
- 1 * mockCpsAdminService.updateAnchorSchemaSet(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, 'dmi-registry-2023-08-23')
+ 1 * mockCpsAnchorService.updateAnchorSchemaSet(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, 'dmi-registry-2023-11-27')
and: 'No schema sets are being removed by the module service (yet)'
0 * mockCpsModuleService.deleteSchemaSet(NCMP_DATASPACE_NAME, _, _)
}
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
-import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsAnchorService;
+import org.onap.cps.api.CpsDataspaceService;
import org.onap.cps.api.CpsModuleService;
import org.onap.cps.rest.api.CpsAdminApi;
import org.onap.cps.rest.model.AnchorDetails;
@RequiredArgsConstructor
public class AdminRestController implements CpsAdminApi {
- private final CpsAdminService cpsAdminService;
+ private final CpsDataspaceService cpsDataspaceService;
private final CpsModuleService cpsModuleService;
private final CpsRestInputMapper cpsRestInputMapper;
+ private final CpsAnchorService cpsAnchorService;
/**
* Create a dataspace.
*/
@Override
public ResponseEntity<String> createDataspace(@NotNull @Valid final String dataspaceName) {
- cpsAdminService.createDataspace(dataspaceName);
+ cpsDataspaceService.createDataspace(dataspaceName);
return new ResponseEntity<>(dataspaceName, HttpStatus.CREATED);
}
*/
@Override
public ResponseEntity<Void> createDataspaceV2(@NotNull @Valid final String dataspaceName) {
- cpsAdminService.createDataspace(dataspaceName);
+ cpsDataspaceService.createDataspace(dataspaceName);
return new ResponseEntity<>(HttpStatus.CREATED);
}
*/
@Override
public ResponseEntity<Void> deleteDataspace(final String apiVersion, final String dataspaceName) {
- cpsAdminService.deleteDataspace(dataspaceName);
+ cpsDataspaceService.deleteDataspace(dataspaceName);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@Override
public ResponseEntity<String> createAnchor(final String dataspaceName, @NotNull @Valid final String schemaSetName,
@NotNull @Valid final String anchorName) {
- cpsAdminService.createAnchor(dataspaceName, schemaSetName, anchorName);
+ cpsAnchorService.createAnchor(dataspaceName, schemaSetName, anchorName);
return new ResponseEntity<>(anchorName, HttpStatus.CREATED);
}
@Override
public ResponseEntity<Void> createAnchorV2(final String dataspaceName, @NotNull @Valid final String schemaSetName,
@NotNull @Valid final String anchorName) {
- cpsAdminService.createAnchor(dataspaceName, schemaSetName, anchorName);
+ cpsAnchorService.createAnchor(dataspaceName, schemaSetName, anchorName);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@Override
public ResponseEntity<Void> deleteAnchor(final String apiVersion,
final String dataspaceName, final String anchorName) {
- cpsAdminService.deleteAnchor(dataspaceName, anchorName);
+ cpsAnchorService.deleteAnchor(dataspaceName, anchorName);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@Override
public ResponseEntity<AnchorDetails> getAnchor(final String apiVersion,
final String dataspaceName, final String anchorName) {
- final var anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final var anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final var anchorDetails = cpsRestInputMapper.toAnchorDetails(anchor);
return new ResponseEntity<>(anchorDetails, HttpStatus.OK);
}
@Override
public ResponseEntity<List<AnchorDetails>> getAnchors(final String apiVersion,
final String dataspaceName) {
- final Collection<Anchor> anchors = cpsAdminService.getAnchors(dataspaceName);
+ final Collection<Anchor> anchors = cpsAnchorService.getAnchors(dataspaceName);
final List<AnchorDetails> anchorDetails = anchors.stream().map(cpsRestInputMapper::toAnchorDetails)
.collect(Collectors.toList());
return new ResponseEntity<>(anchorDetails, HttpStatus.OK);
@Override
public ResponseEntity<List<DataspaceDetails>> getAllDataspaces(final String apiVersion) {
- final Collection<Dataspace> dataspaces = cpsAdminService.getAllDataspaces();
+ final Collection<Dataspace> dataspaces = cpsDataspaceService.getAllDataspaces();
final List<DataspaceDetails> dataspaceDetails = dataspaces.stream().map(cpsRestInputMapper::toDataspaceDetails)
.collect(Collectors.toList());
return new ResponseEntity<>(dataspaceDetails, HttpStatus.OK);
@Override
public ResponseEntity<DataspaceDetails> getDataspace(final String apiVersion, final String dataspaceName) {
- final Dataspace dataspace = cpsAdminService.getDataspace(dataspaceName);
+ final Dataspace dataspace = cpsDataspaceService.getDataspace(dataspaceName);
final DataspaceDetails dataspaceDetails = cpsRestInputMapper.toDataspaceDetails(dataspace);
return new ResponseEntity<>(dataspaceDetails, HttpStatus.OK);
}
package org.onap.cps.rest.controller
+import org.onap.cps.api.CpsAnchorService
+
import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.mapstruct.factory.Mappers
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsDataspaceService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.onap.cps.spi.exceptions.SchemaSetInUseException
CpsModuleService mockCpsModuleService = Mock()
@SpringBean
- CpsAdminService mockCpsAdminService = Mock()
+ CpsDataspaceService mockCpsDataspaceService = Mock()
+
+ @SpringBean
+ CpsAnchorService mockCpsAnchorService = Mock()
@SpringBean
CpsRestInputMapper cpsRestInputMapper = Mappers.getMapper(CpsRestInputMapper)
def 'Create new dataspace with #scenario.'() {
when: 'post is invoked'
def response =
- mvc.perform(
- post("/cps/api/${apiVersion}/dataspaces")
- .param('dataspace-name', dataspaceName))
- .andReturn().response
+ mvc.perform(
+ post("/cps/api/${apiVersion}/dataspaces")
+ .param('dataspace-name', dataspaceName))
+ .andReturn().response
then: 'service method is invoked with expected parameters'
- 1 * mockCpsAdminService.createDataspace(dataspaceName)
+ 1 * mockCpsDataspaceService.createDataspace(dataspaceName)
and: 'dataspace is create successfully'
response.status == HttpStatus.CREATED.value()
assert response.getContentAsString() == expectedResponseBody
where: 'following cases are tested'
- scenario | apiVersion || expectedResponseBody
- 'V1 API' | 'v1' || 'my_dataspace'
- 'V2 API' | 'v2' || ''
- }
+ scenario | apiVersion || expectedResponseBody
+ 'V1 API' | 'v1' || 'my_dataspace'
+ 'V2 API' | 'v2' || ''
+ }
+
def 'Create dataspace over existing with same name.'() {
given: 'an endpoint'
def createDataspaceEndpoint = "$basePath/v1/dataspaces"
and: 'the service method throws an exception indicating the dataspace is already defined'
def thrownException = new AlreadyDefinedException(dataspaceName, new RuntimeException())
- mockCpsAdminService.createDataspace(dataspaceName) >> { throw thrownException }
+ mockCpsDataspaceService.createDataspace(dataspaceName) >> { throw thrownException }
when: 'post is invoked'
def response =
mvc.perform(
def 'Get a dataspace.'() {
given: 'service method returns a dataspace'
- mockCpsAdminService.getDataspace(dataspaceName) >> dataspace
+ mockCpsDataspaceService.getDataspace(dataspaceName) >> dataspace
and: 'an endpoint'
def getDataspaceEndpoint = "$basePath/v1/admin/dataspaces/$dataspaceName"
when: 'get dataspace API is invoked'
def 'Get all dataspaces.'() {
given: 'service method returns all dataspace'
- mockCpsAdminService.getAllDataspaces() >> [dataspace, new Dataspace(name: "dataspace-test2")]
+ mockCpsDataspaceService.getAllDataspaces() >> [dataspace, new Dataspace(name: "dataspace-test2")]
and: 'an endpoint'
def getAllDataspaceEndpoint = "$basePath/v1/admin/dataspaces"
when: 'get all dataspace API is invoked'
.params(requestParams as MultiValueMap))
.andReturn().response
then: 'anchor is created successfully'
- 1 * mockCpsAdminService.createAnchor(dataspaceName, schemaSetName, anchorName)
+ 1 * mockCpsAnchorService.createAnchor(dataspaceName, schemaSetName, anchorName)
assert response.status == HttpStatus.CREATED.value()
assert response.getContentAsString() == expectedResponseBody
where: 'following cases are tested'
def 'Get existing anchor.'() {
given: 'service method returns a list of anchors'
- mockCpsAdminService.getAnchors(dataspaceName) >> [anchor]
+ mockCpsAnchorService.getAnchors(dataspaceName) >> [anchor]
and: 'an endpoint'
def anchorEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors"
when: 'get all anchors API is invoked'
def 'Get existing anchor by dataspace and anchor name.'() {
given: 'service method returns an anchor'
- mockCpsAdminService.getAnchor(dataspaceName, anchorName) >>
+ mockCpsAnchorService.getAnchor(dataspaceName, anchorName) >>
new Anchor(name: anchorName, dataspaceName: dataspaceName, schemaSetName: schemaSetName)
and: 'an endpoint'
def anchorEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors/$anchorName"
when: 'delete method is invoked on anchor endpoint'
def response = mvc.perform(delete(anchorEndpoint)).andReturn().response
then: 'associated service method is invoked with expected parameters'
- 1 * mockCpsAdminService.deleteAnchor(dataspaceName, anchorName)
+ 1 * mockCpsAnchorService.deleteAnchor(dataspaceName, anchorName)
and: 'response code indicates success'
response.status == HttpStatus.NO_CONTENT.value()
}
.param('dataspace-name', dataspaceName))
.andReturn().response
then: 'associated service method is invoked with expected parameter'
- 1 * mockCpsAdminService.deleteDataspace(dataspaceName)
+ 1 * mockCpsDataspaceService.deleteDataspace(dataspaceName)
and: 'response code indicates success'
response.status == HttpStatus.NO_CONTENT.value()
}
import com.fasterxml.jackson.databind.ObjectMapper
import groovy.json.JsonSlurper
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsDataspaceService
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.api.CpsQueryService
class CpsRestExceptionHandlerSpec extends Specification {
@SpringBean
- CpsAdminService mockCpsAdminService = Stub()
+ CpsDataspaceService mockCpsAdminService = Stub()
+
+ @SpringBean
+ CpsAnchorService mockCpsAnchorService = Stub()
@SpringBean
CpsModuleService mockCpsModuleService = Stub()
*/
def setupTestException(exception) {
- mockCpsAdminService.getAnchors(_) >> { throw exception }
+ mockCpsAnchorService.getAnchors(_) >> { throw exception }
}
def performTestRequest() {
<parent>\r
<groupId>org.onap.cps</groupId>\r
<artifactId>cps-parent</artifactId>\r
- <version>3.4.1-SNAPSHOT</version>\r
+ <version>3.4.2-SNAPSHOT</version>\r
<relativePath>../cps-parent/pom.xml</relativePath>\r
</parent>\r
\r
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
schemaSetRepository.deleteByDataspaceAndNameIn(dataspaceEntity, schemaSetNames);
}
+
+ @Override
+ @Transactional
+ public void updateSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
+ final Map<String, String> newModuleNameToContentMap,
+ final Collection<ModuleReference> allModuleReferences) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final SchemaSetEntity schemaSetEntity =
+ schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName);
+ storeAndLinkNewModules(newModuleNameToContentMap, schemaSetEntity);
+ updateAllModuleReferences(allModuleReferences, schemaSetEntity.getId());
+ }
+
+
+
@Override
@Transactional
public void deleteUnusedYangResourceModules() {
return SchemaSet.builder().name(schemaSetEntity.getName())
.dataspaceName(schemaSetEntity.getDataspace().getName()).build();
}
+
+ private void storeAndLinkNewModules(final Map<String, String> newModuleNameToContentMap,
+ final SchemaSetEntity schemaSetEntity) {
+ final Set<YangResourceEntity> yangResourceEntities
+ = new HashSet<>(synchronizeYangResources(newModuleNameToContentMap));
+ schemaSetEntity.setYangResources(yangResourceEntities);
+ schemaSetRepository.save(schemaSetEntity);
+ }
+
+ private void updateAllModuleReferences(final Collection<ModuleReference> allModuleReferences,
+ final Integer schemaSetEntityId) {
+ yangResourceRepository.deleteSchemaSetYangResourceForSchemaSetId(schemaSetEntityId);
+ final List<Integer> allYangResourceIds =
+ yangResourceRepository.getResourceIdsByModuleReferences(allModuleReferences);
+ yangResourceRepository.insertSchemaSetIdYangResourceId(schemaSetEntityId, allYangResourceIds);
+ }
+
}
return findAllModuleReferencesByDataspaceAndModuleNames(dataspaceName, moduleNames.toArray(new String[0]));
}
+ @Modifying
+ @Query(value = "DELETE FROM schema_set_yang_resources WHERE schema_set_id = :schemaSetId", nativeQuery = true)
+ void deleteSchemaSetYangResourceForSchemaSetId(@Param("schemaSetId") int schemaSetId);
+
@Modifying
@Query(value = "DELETE FROM yang_resource yr WHERE NOT EXISTS "
+ "(SELECT 1 FROM schema_set_yang_resources ssyr WHERE ssyr.yang_resource_id = yr.id)", nativeQuery = true)
package org.onap.cps.spi.impl
import org.hibernate.exception.ConstraintViolationException
-import org.onap.cps.spi.CpsAdminPersistenceService
import org.onap.cps.spi.CpsModulePersistenceService
+import org.onap.cps.spi.entities.SchemaSetEntity
import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
+import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.spi.repository.DataspaceRepository
import org.onap.cps.spi.repository.ModuleReferenceRepository
import org.onap.cps.spi.repository.SchemaSetRepository
import org.onap.cps.spi.repository.YangResourceRepository
import org.springframework.dao.DataIntegrityViolationException
import spock.lang.Specification
-
import java.sql.SQLException
/**
CpsModulePersistenceService objectUnderTest
- def dataspaceRepositoryMock = Mock(DataspaceRepository)
- def yangResourceRepositoryMock = Mock(YangResourceRepository)
- def schemaSetRepositoryMock = Mock(SchemaSetRepository)
- def cpsAdminPersistenceServiceMock = Mock(CpsAdminPersistenceService)
- def moduleReferenceRepositoryMock = Mock(ModuleReferenceRepository)
+ def mockDataspaceRepository = Mock(DataspaceRepository)
+ def mockYangResourceRepository = Mock(YangResourceRepository)
+ def mockSchemaSetRepository = Mock(SchemaSetRepository)
+ def mockModuleReferenceRepository = Mock(ModuleReferenceRepository)
def yangResourceName = 'my-yang-resource-name'
def yangResourceContent = 'module stores {\n' +
static otherIntegrityException = new DataIntegrityViolationException('another integrity exception')
def setup() {
- objectUnderTest = new CpsModulePersistenceServiceImpl(yangResourceRepositoryMock, schemaSetRepositoryMock,
- dataspaceRepositoryMock, moduleReferenceRepositoryMock)
+ objectUnderTest = new CpsModulePersistenceServiceImpl(mockYangResourceRepository, mockSchemaSetRepository,
+ mockDataspaceRepository, mockModuleReferenceRepository)
}
def 'Store schema set error scenario: #scenario.'() {
given: 'no yang resource are currently saved'
- yangResourceRepositoryMock.findAllByChecksumIn(_ as Collection<String>) >> Collections.emptyList()
+ mockYangResourceRepository.findAllByChecksumIn(_ as Collection<String>) >> Collections.emptyList()
and: 'persisting yang resource raises db constraint exception (in case of concurrent requests for example)'
- yangResourceRepositoryMock.saveAll(_) >> { throw dbException }
+ mockYangResourceRepository.saveAll(_) >> { throw dbException }
when: 'attempt to store schema set '
def newYangResourcesNameToContentMap = [(yangResourceName):yangResourceContent]
objectUnderTest.storeSchemaSet('my-dataspace', 'my-schema-set', newYangResourcesNameToContentMap)
'other data failure' | otherIntegrityException || DataIntegrityViolationException | 'another integrity exception'
}
+ def 'Upgrade existing schema set'() {
+ given: 'old schema has empty yang resource'
+ mockYangResourceRepository.findAllByChecksumIn(_ as Collection<String>) >> Collections.emptyList()
+ def schemaSetEntity = new SchemaSetEntity(id: 1)
+ mockSchemaSetRepository.getByDataspaceAndName(_, _) >> schemaSetEntity
+ when: 'schema set update is requested'
+ objectUnderTest.updateSchemaSetFromModules('my-dataspace', 'my-schemaset', [:], [new ModuleReference('some module name', 'some revision name')])
+ then: 'no exception is thrown '
+ noExceptionThrown()
+ }
+
}
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2020-2023 Nordix Foundation
- * Modifications Copyright (C) 2020-2022 Bell Canada.
- * Modifications Copyright (C) 2021 Pantheon.tech
- * Modifications Copyright (C) 2022 TechMahindra Ltd.
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.cps.api;
import java.util.Collection;
-import org.onap.cps.spi.exceptions.AlreadyDefinedException;
import org.onap.cps.spi.exceptions.CpsException;
import org.onap.cps.spi.model.Anchor;
-import org.onap.cps.spi.model.Dataspace;
-/**
- * CPS Admin Service.
- */
-public interface CpsAdminService {
-
- /**
- * Create dataspace.
- *
- * @param dataspaceName dataspace name
- * @throws AlreadyDefinedException if dataspace with same name already exists
- */
- void createDataspace(String dataspaceName);
-
- /**
- * Delete dataspace.
- *
- * @param dataspaceName the name of the dataspace to delete
- */
- void deleteDataspace(String dataspaceName);
-
- /**
- * Get dataspace by given dataspace name.
- *
- * @param dataspaceName dataspace name
- * @return a dataspace
- */
- Dataspace getDataspace(String dataspaceName);
-
- /**
- * Get All Dataspaces.
- *
- *
- * @return a collection of dataspaces
- */
- Collection<Dataspace> getAllDataspaces();
+public interface CpsAnchorService {
/**
* Create an Anchor.
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020-2023 Nordix Foundation
+ * Modifications Copyright (C) 2020-2022 Bell Canada.
+ * Modifications Copyright (C) 2021 Pantheon.tech
+ * Modifications Copyright (C) 2022 TechMahindra Ltd.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.api;
+
+import java.util.Collection;
+import org.onap.cps.spi.exceptions.AlreadyDefinedException;
+import org.onap.cps.spi.model.Dataspace;
+
+/**
+ * CPS Admin Service.
+ */
+public interface CpsDataspaceService {
+
+ /**
+ * Create dataspace.
+ *
+ * @param dataspaceName dataspace name
+ * @throws AlreadyDefinedException if dataspace with same name already exists
+ */
+ void createDataspace(String dataspaceName);
+
+ /**
+ * Delete dataspace.
+ *
+ * @param dataspaceName the name of the dataspace to delete
+ */
+ void deleteDataspace(String dataspaceName);
+
+ /**
+ * Get dataspace by given dataspace name.
+ *
+ * @param dataspaceName dataspace name
+ * @return a dataspace
+ */
+ Dataspace getDataspace(String dataspaceName);
+
+ /**
+ * Get All Dataspaces.
+ *
+ *
+ * @return a collection of dataspaces
+ */
+ Collection<Dataspace> getAllDataspaces();
+
+}
* @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
* @param allModuleReferences All YANG resource module references
*/
- void createOrUpgradeSchemaSetFromModules(String dataspaceName, String schemaSetName,
+ void createSchemaSetFromModules(String dataspaceName, String schemaSetName,
Map<String, String> newModuleNameToContentMap,
Collection<ModuleReference> allModuleReferences);
*/
void deleteSchemaSetsWithCascade(String dataspaceName, Collection<String> schemaSetNames);
+
+ /**
+ * upgrade schema sets with existing or new modules.
+ *
+ * @param dataspaceName dataspace name
+ * @param schemaSetName schema set name
+ * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
+ * @param allModuleReferences All YANG resource module references
+ */
+ void upgradeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
+ final Map<String, String> newModuleNameToContentMap,
+ final Collection<ModuleReference> allModuleReferences);
+
/**
* Retrieve module references for the given dataspace name.
*
/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2020-2023 Nordix Foundation
- * Modifications Copyright (C) 2020-2022 Bell Canada.
- * Modifications Copyright (C) 2021 Pantheon.tech
- * Modifications Copyright (C) 2022 TechMahindra Ltd.
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.cps.api.impl;
-import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
-import org.onap.cps.api.CpsAdminService;
-import org.onap.cps.api.CpsDataService;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.spi.CpsAdminPersistenceService;
+import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.model.Anchor;
-import org.onap.cps.spi.model.Dataspace;
import org.onap.cps.spi.utils.CpsValidator;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
-@Component("CpsAdminServiceImpl")
-@RequiredArgsConstructor(onConstructor = @__(@Lazy))
-public class CpsAdminServiceImpl implements CpsAdminService {
+@Service
+@RequiredArgsConstructor
+public class CpsAnchorServiceImpl implements CpsAnchorService {
private final CpsAdminPersistenceService cpsAdminPersistenceService;
- @Lazy
- private final CpsDataService cpsDataService;
+ private final CpsDataPersistenceService cpsDataPersistenceService;
private final CpsValidator cpsValidator;
- @Override
- public void createDataspace(final String dataspaceName) {
- cpsValidator.validateNameCharacters(dataspaceName);
- cpsAdminPersistenceService.createDataspace(dataspaceName);
- }
-
- @Override
- public void deleteDataspace(final String dataspaceName) {
- cpsValidator.validateNameCharacters(dataspaceName);
- cpsAdminPersistenceService.deleteDataspace(dataspaceName);
- }
-
- @Override
- public Dataspace getDataspace(final String dataspaceName) {
- cpsValidator.validateNameCharacters(dataspaceName);
- return cpsAdminPersistenceService.getDataspace(dataspaceName);
- }
-
- @Override
- public Collection<Dataspace> getAllDataspaces() {
- return cpsAdminPersistenceService.getAllDataspaces();
- }
-
@Override
public void createAnchor(final String dataspaceName, final String schemaSetName, final String anchorName) {
cpsValidator.validateNameCharacters(dataspaceName, schemaSetName, anchorName);
@Override
public void deleteAnchor(final String dataspaceName, final String anchorName) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- cpsDataService.deleteDataNodes(dataspaceName, anchorName, OffsetDateTime.now());
+ cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName);
cpsAdminPersistenceService.deleteAnchor(dataspaceName, anchorName);
}
public void deleteAnchors(final String dataspaceName, final Collection<String> anchorNames) {
cpsValidator.validateNameCharacters(dataspaceName);
cpsValidator.validateNameCharacters(anchorNames);
- cpsDataService.deleteDataNodes(dataspaceName, anchorNames, OffsetDateTime.now());
+ cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorNames);
cpsAdminPersistenceService.deleteAnchors(dataspaceName, anchorNames);
}
@Override
public void updateAnchorSchemaSet(final String dataspaceName,
- final String anchorName,
- final String schemaSetName) {
+ final String anchorName,
+ final String schemaSetName) {
cpsAdminPersistenceService.updateAnchorSchemaSet(dataspaceName, anchorName, schemaSetName);
}
}
package org.onap.cps.api.impl;
-import static org.onap.cps.notification.Operation.CREATE;
-import static org.onap.cps.notification.Operation.DELETE;
-import static org.onap.cps.notification.Operation.UPDATE;
-
import io.micrometer.core.annotation.Timed;
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsDeltaService;
import org.onap.cps.cpspath.parser.CpsPathUtil;
-import org.onap.cps.notification.NotificationService;
-import org.onap.cps.notification.Operation;
import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.exceptions.DataValidationException;
private static final long DEFAULT_LOCK_TIMEOUT_IN_MILLISECONDS = 300L;
private final CpsDataPersistenceService cpsDataPersistenceService;
- private final CpsAdminService cpsAdminService;
+ private final CpsAnchorService cpsAnchorService;
private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache;
- private final NotificationService notificationService;
private final CpsValidator cpsValidator;
private final TimedYangParser timedYangParser;
private final CpsDeltaService cpsDeltaService;
public void saveData(final String dataspaceName, final String anchorName, final String nodeData,
final OffsetDateTime observedTimestamp, final ContentType contentType) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> dataNodes = buildDataNodes(anchor, ROOT_NODE_XPATH, nodeData, contentType);
cpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName, dataNodes);
- processDataUpdatedEventAsync(anchor, ROOT_NODE_XPATH, CREATE, observedTimestamp);
}
@Override
final String nodeData, final OffsetDateTime observedTimestamp,
final ContentType contentType) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> dataNodes = buildDataNodes(anchor, parentNodeXpath, nodeData, contentType);
cpsDataPersistenceService.addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodes);
- processDataUpdatedEventAsync(anchor, parentNodeXpath, CREATE, observedTimestamp);
}
@Override
public void saveListElements(final String dataspaceName, final String anchorName,
final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> listElementDataNodeCollection =
buildDataNodes(anchor, parentNodeXpath, jsonData, ContentType.JSON);
if (isRootNodeXpath(parentNodeXpath)) {
cpsDataPersistenceService.addListElements(dataspaceName, anchorName, parentNodeXpath,
listElementDataNodeCollection);
}
- processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp);
}
@Override
public void saveListElementsBatch(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<String> jsonDataList, final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<Collection<DataNode>> listElementDataNodeCollections =
buildDataNodes(anchor, parentNodeXpath, jsonDataList, ContentType.JSON);
cpsDataPersistenceService.addMultipleLists(dataspaceName, anchorName, parentNodeXpath,
listElementDataNodeCollections);
- processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp);
}
@Override
public void updateNodeLeaves(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final String jsonData, final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> dataNodesInPatch = buildDataNodes(anchor, parentNodeXpath, jsonData,
ContentType.JSON);
final Map<String, Map<String, Serializable>> xpathToUpdatedLeaves = dataNodesInPatch.stream()
.collect(Collectors.toMap(DataNode::getXpath, DataNode::getLeaves));
cpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName, xpathToUpdatedLeaves);
- processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp);
}
@Override
final String dataNodeUpdatesAsJson,
final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> dataNodeUpdates =
buildDataNodes(anchor, parentNodeXpath, dataNodeUpdatesAsJson, ContentType.JSON);
for (final DataNode dataNodeUpdate : dataNodeUpdates) {
processDataNodeUpdate(anchor, dataNodeUpdate);
}
- processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp);
}
@Override
final String parentNodeXpath, final String jsonData,
final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> dataNodes = buildDataNodes(anchor, parentNodeXpath, jsonData, ContentType.JSON);
cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
- processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp);
}
@Override
final Map<String, String> nodesJsonData,
final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> dataNodes = buildDataNodes(anchor, nodesJsonData);
cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes);
- nodesJsonData.keySet().forEach(nodeXpath ->
- processDataUpdatedEventAsync(anchor, nodeXpath, UPDATE, observedTimestamp));
}
@Override
public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final String jsonData, final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> newListElements =
buildDataNodes(anchor, parentNodeXpath, jsonData, ContentType.JSON);
replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements, observedTimestamp);
public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<DataNode> dataNodes, final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, dataNodes);
- processDataUpdatedEventAsync(anchor, parentNodeXpath, UPDATE, observedTimestamp);
}
@Override
public void deleteDataNode(final String dataspaceName, final String anchorName, final String dataNodeXpath,
final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
cpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, dataNodeXpath);
- processDataUpdatedEventAsync(anchor, dataNodeXpath, DELETE, observedTimestamp);
}
@Override
final Collection<String> dataNodeXpaths, final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName, dataNodeXpaths);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
- dataNodeXpaths.forEach(dataNodeXpath ->
- processDataUpdatedEventAsync(anchor, dataNodeXpath, DELETE, observedTimestamp));
}
@Override
public void deleteDataNodes(final String dataspaceName, final String anchorName,
final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
- processDataUpdatedEventAsync(anchor, ROOT_NODE_XPATH, DELETE, observedTimestamp);
cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName);
}
final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName);
cpsValidator.validateNameCharacters(anchorNames);
- for (final Anchor anchor : cpsAdminService.getAnchors(dataspaceName, anchorNames)) {
- processDataUpdatedEventAsync(anchor, ROOT_NODE_XPATH, DELETE, observedTimestamp);
- }
cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorNames);
}
public void deleteListOrListElement(final String dataspaceName, final String anchorName, final String listNodeXpath,
final OffsetDateTime observedTimestamp) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
cpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, listNodeXpath);
- processDataUpdatedEventAsync(anchor, listNodeXpath, DELETE, observedTimestamp);
}
private Collection<DataNode> buildDataNodes(final Anchor anchor, final Map<String, String> nodesJsonData) {
.collect(Collectors.toList());
}
- private void processDataUpdatedEventAsync(final Anchor anchor, final String xpath,
- final Operation operation, final OffsetDateTime observedTimestamp) {
- try {
- notificationService.processDataUpdatedEvent(anchor, xpath, operation, observedTimestamp);
- } catch (final Exception exception) {
- //If async message can't be queued for notification service, the initial request should not fail.
- log.error("Failed to send message to notification service", exception);
- }
- }
-
private SchemaContext getSchemaContext(final Anchor anchor) {
return yangTextSchemaSourceSetCache
.get(anchor.getDataspaceName(), anchor.getSchemaSetName()).getSchemaContext();
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020-2023 Nordix Foundation
+ * Modifications Copyright (C) 2020-2022 Bell Canada.
+ * Modifications Copyright (C) 2021 Pantheon.tech
+ * Modifications Copyright (C) 2022 TechMahindra Ltd.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.api.impl;
+
+import java.util.Collection;
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.api.CpsDataspaceService;
+import org.onap.cps.spi.CpsAdminPersistenceService;
+import org.onap.cps.spi.model.Dataspace;
+import org.onap.cps.spi.utils.CpsValidator;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class CpsDataspaceServiceImpl implements CpsDataspaceService {
+
+ private final CpsAdminPersistenceService cpsAdminPersistenceService;
+ private final CpsValidator cpsValidator;
+
+ @Override
+ public void createDataspace(final String dataspaceName) {
+ cpsValidator.validateNameCharacters(dataspaceName);
+ cpsAdminPersistenceService.createDataspace(dataspaceName);
+ }
+
+ @Override
+ public void deleteDataspace(final String dataspaceName) {
+ cpsValidator.validateNameCharacters(dataspaceName);
+ cpsAdminPersistenceService.deleteDataspace(dataspaceName);
+ }
+
+ @Override
+ public Dataspace getDataspace(final String dataspaceName) {
+ cpsValidator.validateNameCharacters(dataspaceName);
+ return cpsAdminPersistenceService.getDataspace(dataspaceName);
+ }
+
+ @Override
+ public Collection<Dataspace> getAllDataspaces() {
+ return cpsAdminPersistenceService.getAllDataspaces();
+ }
+
+}
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
-import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsModuleService;
import org.onap.cps.spi.CascadeDeleteAllowed;
import org.onap.cps.spi.CpsModulePersistenceService;
private final CpsModulePersistenceService cpsModulePersistenceService;
private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache;
- private final CpsAdminService cpsAdminService;
+ private final CpsAnchorService cpsAnchorService;
private final CpsValidator cpsValidator;
private final TimedYangTextSchemaSourceSetBuilder timedYangTextSchemaSourceSetBuilder;
}
@Override
- public void createOrUpgradeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
- final Map<String, String> newModuleNameToContentMap,
- final Collection<ModuleReference> allModuleReferences) {
+ public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
+ final Map<String, String> newModuleNameToContentMap,
+ final Collection<ModuleReference> allModuleReferences) {
cpsValidator.validateNameCharacters(dataspaceName, schemaSetName);
cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName,
newModuleNameToContentMap, allModuleReferences);
public void deleteSchemaSet(final String dataspaceName, final String schemaSetName,
final CascadeDeleteAllowed cascadeDeleteAllowed) {
cpsValidator.validateNameCharacters(dataspaceName, schemaSetName);
- final Collection<Anchor> anchors = cpsAdminService.getAnchors(dataspaceName, schemaSetName);
+ final Collection<Anchor> anchors = cpsAnchorService.getAnchors(dataspaceName, schemaSetName);
if (!anchors.isEmpty() && isCascadeDeleteProhibited(cascadeDeleteAllowed)) {
throw new SchemaSetInUseException(dataspaceName, schemaSetName);
}
for (final Anchor anchor : anchors) {
- cpsAdminService.deleteAnchor(dataspaceName, anchor.getName());
+ cpsAnchorService.deleteAnchor(dataspaceName, anchor.getName());
}
cpsModulePersistenceService.deleteSchemaSet(dataspaceName, schemaSetName);
yangTextSchemaSourceSetCache.removeFromCache(dataspaceName, schemaSetName);
public void deleteSchemaSetsWithCascade(final String dataspaceName, final Collection<String> schemaSetNames) {
cpsValidator.validateNameCharacters(dataspaceName);
cpsValidator.validateNameCharacters(schemaSetNames);
- final Collection<String> anchorNames = cpsAdminService.getAnchors(dataspaceName, schemaSetNames)
+ final Collection<String> anchorNames = cpsAnchorService.getAnchors(dataspaceName, schemaSetNames)
.stream().map(Anchor::getName).collect(Collectors.toSet());
- cpsAdminService.deleteAnchors(dataspaceName, anchorNames);
+ cpsAnchorService.deleteAnchors(dataspaceName, anchorNames);
cpsModulePersistenceService.deleteUnusedYangResourceModules();
cpsModulePersistenceService.deleteSchemaSets(dataspaceName, schemaSetNames);
for (final String schemaSetName : schemaSetNames) {
}
}
+ @Override
+ public void upgradeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
+ final Map<String, String> newModuleNameToContentMap,
+ final Collection<ModuleReference> allModuleReferences) {
+ cpsValidator.validateNameCharacters(dataspaceName, schemaSetName);
+ cpsModulePersistenceService.updateSchemaSetFromModules(dataspaceName, schemaSetName,
+ newModuleNameToContentMap, allModuleReferences);
+ yangTextSchemaSourceSetCache.removeFromCache(dataspaceName, schemaSetName);
+ }
+
+
@Override
public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName) {
cpsValidator.validateNameCharacters(dataspaceName);
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (c) 2021-2022 Bell Canada.
- * Modifications Copyright (c) 2022-2023 Nordix Foundation
- * Modifications Copyright (C) 2023 TechMahindra Ltd.
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.time.OffsetDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.UUID;
-import lombok.AllArgsConstructor;
-import lombok.SneakyThrows;
-import org.onap.cps.api.CpsDataService;
-import org.onap.cps.event.model.Content;
-import org.onap.cps.event.model.CpsDataUpdatedEvent;
-import org.onap.cps.event.model.Data;
-import org.onap.cps.spi.FetchDescendantsOption;
-import org.onap.cps.spi.model.Anchor;
-import org.onap.cps.spi.model.DataNode;
-import org.onap.cps.utils.DataMapUtils;
-import org.onap.cps.utils.PrefixResolver;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Component;
-
-@Component
-@AllArgsConstructor(onConstructor = @__(@Lazy))
-public class CpsDataUpdatedEventFactory {
-
- private static final DateTimeFormatter DATE_TIME_FORMATTER =
- DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
-
- @Lazy
- private final CpsDataService cpsDataService;
-
- @Lazy
- private final PrefixResolver prefixResolver;
-
- /**
- * Generates CPS Data Updated event. If observedTimestamp is not provided, then current timestamp is used.
- *
- * @param anchor anchor
- * @param observedTimestamp observedTimestamp
- * @param operation operation
- * @return CpsDataUpdatedEvent
- */
- public CpsDataUpdatedEvent createCpsDataUpdatedEvent(final Anchor anchor,
- final OffsetDateTime observedTimestamp, final Operation operation) {
- final var dataNode = (operation == Operation.DELETE) ? null :
- cpsDataService.getDataNodes(anchor.getDataspaceName(), anchor.getName(),
- "/", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS).iterator().next();
- return toCpsDataUpdatedEvent(anchor, dataNode, observedTimestamp, operation);
- }
-
- @SneakyThrows(URISyntaxException.class)
- private CpsDataUpdatedEvent toCpsDataUpdatedEvent(final Anchor anchor,
- final DataNode dataNode,
- final OffsetDateTime observedTimestamp,
- final Operation operation) {
- final CpsDataUpdatedEvent cpsDataUpdatedEvent = new CpsDataUpdatedEvent();
- cpsDataUpdatedEvent.withContent(createContent(anchor, dataNode, observedTimestamp, operation));
- cpsDataUpdatedEvent.withId(UUID.randomUUID().toString());
- cpsDataUpdatedEvent.withSchema(new URI("urn:cps:org.onap.cps:data-updated-event-schema:v1"));
- cpsDataUpdatedEvent.withSource(new URI("urn:cps:org.onap.cps"));
- cpsDataUpdatedEvent.withType("org.onap.cps.data-updated-event");
- return cpsDataUpdatedEvent;
- }
-
- private Data createData(final DataNode dataNode, final String prefix) {
- final Data data = new Data();
- DataMapUtils.toDataMapWithIdentifier(dataNode, prefix).forEach(data::setAdditionalProperty);
- return data;
- }
-
- private Content createContent(final Anchor anchor, final DataNode dataNode,
- final OffsetDateTime observedTimestamp, final Operation operation) {
- final var content = new Content();
- content.withAnchorName(anchor.getName());
- content.withDataspaceName(anchor.getDataspaceName());
- content.withSchemaSetName(anchor.getSchemaSetName());
- content.withOperation(Content.Operation.fromValue(operation.name()));
- content.withObservedTimestamp(
- DATE_TIME_FORMATTER.format(observedTimestamp == null ? OffsetDateTime.now() : observedTimestamp));
- if (dataNode != null) {
- final String prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath());
- content.withData(createData(dataNode, prefix));
- }
- return content;
- }
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021 Bell Canada. All rights reserved.
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.kafka.clients.producer.ProducerRecord;
-import org.apache.kafka.clients.producer.RecordMetadata;
-import org.springframework.kafka.support.ProducerListener;
-import org.springframework.stereotype.Component;
-
-@Slf4j
-@Component
-public class KafkaProducerListener<K, V> implements ProducerListener<K, V> {
-
- private NotificationErrorHandler notificationErrorHandler;
-
- public KafkaProducerListener(final NotificationErrorHandler notificationErrorHandler) {
- this.notificationErrorHandler = notificationErrorHandler;
- }
-
- @Override
- public void onSuccess(final ProducerRecord<K, V> producerRecord, final RecordMetadata recordMetadata) {
- log.debug("Message sent to event-bus topic :'{}' with body : {} ", producerRecord.topic(),
- producerRecord.value());
- }
-
- @Override
- public void onError(final ProducerRecord<K, V> producerRecord,
- final RecordMetadata recordMetadata,
- final Exception exception) {
- notificationErrorHandler.onException("Failed to send message to message bus",
- exception, producerRecord, recordMetadata);
- }
-
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021 Bell Canada. All rights reserved.
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-@Component
-@Slf4j
-public class NotificationErrorHandler {
-
- void onException(final Exception exception, final Object... context) {
- onException("Failed to process", exception, context);
- }
-
- void onException(final String message, final Exception exception, final Object... context) {
- log.error("{} \n Error cause: {} \n Error context: {}",
- message,
- exception.getCause() != null ? exception.getCause().toString() : exception.getMessage(),
- context,
- exception);
- }
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021 Bell Canada. All rights reserved.
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification;
-
-import jakarta.validation.constraints.NotNull;
-import java.util.Collections;
-import java.util.Map;
-import lombok.Data;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-import org.springframework.validation.annotation.Validated;
-
-@ConfigurationProperties(prefix = "notification.data-updated")
-@Component
-@Data
-@Validated
-public class NotificationProperties {
-
- @NotNull
- private String topic;
- private Map<String, String> filters = Collections.emptyMap();
-
- @Value("${notification.enabled:true}")
- private boolean enabled;
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (c) 2021 Bell Canada.
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification;
-
-import lombok.extern.slf4j.Slf4j;
-import org.checkerframework.checker.nullness.qual.NonNull;
-import org.onap.cps.event.model.CpsDataUpdatedEvent;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.kafka.core.KafkaTemplate;
-import org.springframework.stereotype.Component;
-
-@Component
-@Slf4j
-public class NotificationPublisher {
-
- private KafkaTemplate<String, CpsDataUpdatedEvent> kafkaTemplate;
- private String topicName;
-
- /**
- * Create an instance of Notification Publisher.
- *
- * @param kafkaTemplate kafkaTemplate is send event using kafka
- * @param topicName topic, to which cpsDataUpdatedEvent is sent, is provided by setting
- * 'notification.data-updated.topic' in the application properties
- */
- @Autowired
- public NotificationPublisher(
- final KafkaTemplate<String, CpsDataUpdatedEvent> kafkaTemplate,
- final @Value("${notification.data-updated.topic}") String topicName) {
- this.kafkaTemplate = kafkaTemplate;
- this.topicName = topicName;
- }
-
- /**
- * Send event to Kafka with correct message key.
- *
- * @param cpsDataUpdatedEvent event to be sent to kafka
- */
- public void sendNotification(@NonNull final CpsDataUpdatedEvent cpsDataUpdatedEvent) {
- final var messageKey = cpsDataUpdatedEvent.getContent().getDataspaceName() + ","
- + cpsDataUpdatedEvent.getContent().getAnchorName();
- log.debug("Data Updated event is being sent with messageKey: '{}' & body : {} ",
- messageKey, cpsDataUpdatedEvent);
- kafkaTemplate.send(topicName, messageKey, cpsDataUpdatedEvent);
- }
-
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (c) 2021-2022 Bell Canada.
- * Modifications Copyright (C) 2022-2023 Nordix Foundation
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification;
-
-import jakarta.annotation.PostConstruct;
-import java.time.OffsetDateTime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Future;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsAdminService;
-import org.onap.cps.spi.model.Anchor;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Service;
-
-@Service
-@Slf4j
-@RequiredArgsConstructor
-public class NotificationService {
-
- private final NotificationProperties notificationProperties;
- private final NotificationPublisher notificationPublisher;
- private final CpsDataUpdatedEventFactory cpsDataUpdatedEventFactory;
- private final NotificationErrorHandler notificationErrorHandler;
- private final CpsAdminService cpsAdminService;
- private List<Pattern> dataspacePatterns;
-
- @PostConstruct
- public void init() {
- log.info("Notification Properties {}", notificationProperties);
- this.dataspacePatterns = getDataspaceFilterPatterns(notificationProperties);
- }
-
- private List<Pattern> getDataspaceFilterPatterns(final NotificationProperties notificationProperties) {
- if (notificationProperties.isEnabled()) {
- return Arrays.stream(notificationProperties.getFilters()
- .getOrDefault("enabled-dataspaces", "")
- .split(","))
- .map(filterPattern -> Pattern.compile(filterPattern, Pattern.CASE_INSENSITIVE))
- .collect(Collectors.toList());
- } else {
- return Collections.emptyList();
- }
- }
-
- /**
- * Process Data Updated Event and publishes the notification.
- *
- * @param anchor anchor
- * @param xpath xpath of changed data node
- * @param operation operation
- * @param observedTimestamp observedTimestamp
- * @return future
- */
- @Async("notificationExecutor")
- public Future<Void> processDataUpdatedEvent(final Anchor anchor, final String xpath, final Operation operation,
- final OffsetDateTime observedTimestamp) {
-
- log.debug("process data updated event for anchor '{}'", anchor);
- try {
- if (shouldSendNotification(anchor.getDataspaceName())) {
- final var cpsDataUpdatedEvent =
- cpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor,
- observedTimestamp, getRootNodeOperation(xpath, operation));
- log.debug("data updated event to be published {}", cpsDataUpdatedEvent);
- notificationPublisher.sendNotification(cpsDataUpdatedEvent);
- }
- } catch (final Exception exception) {
- /* All the exceptions are handled to not to propagate it to caller.
- CPS operation should not fail if sending event fails for any reason.
- */
- notificationErrorHandler.onException("Failed to process cps-data-updated-event.",
- exception, anchor, xpath, operation);
- }
- return CompletableFuture.completedFuture(null);
- }
-
- /*
- Add more complex rules based on dataspace and anchor later
- */
- private boolean shouldSendNotification(final String dataspaceName) {
-
- return notificationProperties.isEnabled()
- && dataspacePatterns.stream()
- .anyMatch(pattern -> pattern.matcher(dataspaceName).find());
- }
-
- private Operation getRootNodeOperation(final String xpath, final Operation operation) {
- return isRootXpath(xpath) || isRootContainerNodeXpath(xpath) ? operation : Operation.UPDATE;
- }
-
- private static boolean isRootXpath(final String xpath) {
- return "/".equals(xpath) || "".equals(xpath);
- }
-
- private static boolean isRootContainerNodeXpath(final String xpath) {
- return 0 == xpath.lastIndexOf('/');
- }
-
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (c) 2022 Bell Canada.
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification;
-
-public enum Operation {
- CREATE,
- UPDATE,
- DELETE
-}
void storeSchemaSet(String dataspaceName, String schemaSetName, Map<String, String> yangResourcesNameToContentMap);
/**
- * Stores a schema set from new modules and existing modules.
+ * Stores a new schema set from new modules and existing modules.
*
* @param dataspaceName Dataspace name
* @param schemaSetName Schema set name
void storeSchemaSetFromModules(String dataspaceName, String schemaSetName,
Map<String, String> newModuleNameToContentMap, Collection<ModuleReference> allModuleReferences);
+ /**
+ * Update an existing schema set from new modules and existing modules.
+ *
+ * @param dataspaceName Dataspace name
+ * @param schemaSetName Schema set name
+ * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
+ * @param allModuleReferences All YANG resources module references
+ */
+ void updateSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
+ final Map<String, String> newModuleNameToContentMap,
+ final Collection<ModuleReference> allModuleReferences);
+
+
/**
* Get all schema sets for a given dataspace.
*
import java.util.Map;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
-import org.onap.cps.api.CpsAdminService;
+import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.impl.YangTextSchemaSourceSetCache;
import org.onap.cps.cache.AnchorDataCacheEntry;
import org.onap.cps.cpspath.parser.CpsPathPrefixType;
private static final String CACHE_ENTRY_PROPERTY_NAME = "prefixPerContainerName";
- private final CpsAdminService cpsAdminService;
+ private final CpsAnchorService cpsAnchorService;
private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache;
* @return the prefix of the module the top level element of given xpath
*/
public String getPrefix(final String dataspaceName, final String anchorName, final String xpath) {
- final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+ final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
return getPrefix(anchor, xpath);
}
/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2020-2023 Nordix Foundation
- * Modifications Copyright (C) 2020-2022 Bell Canada.
- * Modifications Copyright (C) 2021 Pantheon.tech
- * Modifications Copyright (C) 2022 TechMahindra Ltd.
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.cps.api.impl
-import org.onap.cps.api.CpsDataService
import org.onap.cps.spi.CpsAdminPersistenceService
+import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException
import org.onap.cps.spi.model.Anchor
-import org.onap.cps.spi.model.Dataspace
import org.onap.cps.spi.utils.CpsValidator
import spock.lang.Specification
-import java.time.OffsetDateTime
-class CpsAdminServiceImplSpec extends Specification {
+class CpsAnchorServiceImplSpec extends Specification {
+
def mockCpsAdminPersistenceService = Mock(CpsAdminPersistenceService)
- def mockCpsDataService = Mock(CpsDataService)
+ def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
def mockCpsValidator = Mock(CpsValidator)
- def objectUnderTest = new CpsAdminServiceImpl(mockCpsAdminPersistenceService, mockCpsDataService,mockCpsValidator)
- def 'Create dataspace method invokes persistence service.'() {
- when: 'create dataspace method is invoked'
- objectUnderTest.createDataspace('someDataspace')
- then: 'the persistence service method is invoked with same parameters'
- 1 * mockCpsAdminPersistenceService.createDataspace('someDataspace')
- and: 'the CpsValidator is called on the dataspaceName'
- 1 * mockCpsValidator.validateNameCharacters('someDataspace')
- }
+ def objectUnderTest = new CpsAnchorServiceImpl(mockCpsAdminPersistenceService, mockCpsDataPersistenceService, mockCpsValidator)
def 'Create anchor method invokes persistence service.'() {
when: 'create anchor method is invoked'
1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someAnchor')
}
- def 'Retrieve dataspace.'() {
- given: 'a dataspace is already created'
- def dataspace = new Dataspace(name: "someDataspace")
- mockCpsAdminPersistenceService.getDataspace('someDataspace') >> dataspace
- expect: 'the dataspace provided by persistence service is returned as result'
- assert objectUnderTest.getDataspace('someDataspace') == dataspace
- }
-
- def 'Retrieve all dataspaces.'() {
- given: 'that all given dataspaces are already created'
- def dataspaces = [new Dataspace(name: "test-dataspace1"), new Dataspace(name: "test-dataspace2")]
- mockCpsAdminPersistenceService.getAllDataspaces() >> dataspaces
- expect: 'the dataspace provided by persistence service is returned as result'
- assert objectUnderTest.getAllDataspaces() == dataspaces
- }
-
def 'Delete anchor.'() {
when: 'delete anchor is invoked'
objectUnderTest.deleteAnchor('someDataspace','someAnchor')
then: 'delete data nodes is invoked on the data service with expected parameters'
- 1 * mockCpsDataService.deleteDataNodes('someDataspace','someAnchor', _ as OffsetDateTime )
+ 1 * mockCpsDataPersistenceService.deleteDataNodes('someDataspace','someAnchor')
and: 'the persistence service method is invoked with same parameters to delete anchor'
- 1 * mockCpsAdminPersistenceService.deleteAnchor('someDataspace','someAnchor')
+ 1 * mockCpsAdminPersistenceService.deleteAnchor('someDataspace','someAnchor')
and: 'the CpsValidator is called on the dataspaceName, anchorName'
1 * mockCpsValidator.validateNameCharacters('someDataspace', 'someAnchor')
}
when: 'delete anchors is invoked'
objectUnderTest.deleteAnchors('someDataspace', ['anchor1', 'anchor2'])
then: 'delete data nodes is invoked on the data service with expected parameters'
- 1 * mockCpsDataService.deleteDataNodes('someDataspace', _ as Collection<String>, _ as OffsetDateTime)
+ 1 * mockCpsDataPersistenceService.deleteDataNodes('someDataspace', _ as Collection<String>)
and: 'the persistence service method is invoked with same parameters to delete anchor'
1 * mockCpsAdminPersistenceService.deleteAnchors('someDataspace',_ as Collection<String>)
and: 'the CpsValidator is called on the dataspace name and anchor names'
def 'Query all anchors with Module Names Not Found Exception in persistence layer.'() {
given: 'the persistence layer throws a Module Names Not Found Exception'
- def originalException = new ModuleNamesNotFoundException('exception-ds', [ 'm1', 'm2'])
+ def originalException = new ModuleNamesNotFoundException('exception-ds', ['m1', 'm2'])
mockCpsAdminPersistenceService.queryAnchors(*_) >> { throw originalException}
when: 'attempt query anchors'
objectUnderTest.queryAnchorNames('some-dataspace-name', [])
assert thrownUp.details.contains('m2')
}
- def 'Delete dataspace.'() {
- when: 'delete dataspace is invoked'
- objectUnderTest.deleteDataspace('someDataspace')
- then: 'associated persistence service method is invoked with correct parameter'
- 1 * mockCpsAdminPersistenceService.deleteDataspace('someDataspace')
- and: 'the CpsValidator is called on the dataspaceName'
- 1 * mockCpsValidator.validateNameCharacters('someDataspace')
- }
-
def 'Update anchor schema set.'() {
when: 'update anchor is invoked'
objectUnderTest.updateAnchorSchemaSet('someDataspace', 'someAnchor', 'someSchemaSetName')
then: 'associated persistence service method is invoked with correct parameter'
1 * mockCpsAdminPersistenceService.updateAnchorSchemaSet('someDataspace', 'someAnchor', 'someSchemaSetName')
}
+
}
package org.onap.cps.api.impl
import org.onap.cps.TestUtils
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDeltaService
-import org.onap.cps.notification.NotificationService
-import org.onap.cps.notification.Operation
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.ConcurrencyException
import org.onap.cps.spi.model.Anchor
import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.DataNodeBuilder
-import org.onap.cps.spi.model.DeltaReportBuilder
import org.onap.cps.spi.utils.CpsValidator
import org.onap.cps.utils.ContentType
import org.onap.cps.utils.TimedYangParser
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import spock.lang.Shared
import spock.lang.Specification
-
import java.time.OffsetDateTime
import java.util.stream.Collectors
class CpsDataServiceImplSpec extends Specification {
def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
- def mockCpsAdminService = Mock(CpsAdminService)
+ def mockCpsAnchorService = Mock(CpsAnchorService)
def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
- def mockNotificationService = Mock(NotificationService)
def mockCpsValidator = Mock(CpsValidator)
def timedYangParser = new TimedYangParser()
def mockCpsDeltaService = Mock(CpsDeltaService);
- def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsAdminService,
- mockYangTextSchemaSourceSetCache, mockNotificationService, mockCpsValidator, timedYangParser, mockCpsDeltaService)
+ def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsAnchorService,
+ mockYangTextSchemaSourceSetCache, mockCpsValidator, timedYangParser, mockCpsDeltaService)
def setup() {
-
- mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor
- mockCpsAdminService.getAnchor(dataspaceName, ANCHOR_NAME_1) >> anchor1
- mockCpsAdminService.getAnchor(dataspaceName, ANCHOR_NAME_2) >> anchor2
+ mockCpsAnchorService.getAnchor(dataspaceName, anchorName) >> anchor
+ mockCpsAnchorService.getAnchor(dataspaceName, ANCHOR_NAME_1) >> anchor1
+ mockCpsAnchorService.getAnchor(dataspaceName, ANCHOR_NAME_2) >> anchor2
}
@Shared
{ dataNode -> dataNode.xpath[0] == '/test-tree' })
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.CREATE, observedTimestamp)
where: 'given parameters'
scenario | dataFile | contentType
'json' | 'test-tree.json' | ContentType.JSON
'invalid xml' | '<invalid xml' | ContentType.XML || 'Failed to parse xml data'
}
- def 'Saving #scenarioDesired data exception during notification.'() {
- given: 'schema set for given anchor and dataspace references test-tree model'
- setupSchemaSetMocks('test-tree.yang')
- and: 'the notification service throws an exception'
- mockNotificationService.processDataUpdatedEvent(*_) >> { throw new RuntimeException('to be ignored')}
- when: 'save data method is invoked with test-tree json data'
- def data = TestUtils.getResourceFileContent('test-tree.json')
- objectUnderTest.saveData(dataspaceName, anchorName, data, observedTimestamp)
- then: 'the exception is ignored'
- noExceptionThrown()
- }
-
def 'Saving list element data fragment under Root node.'() {
given: 'schema set for given anchor and dataspace references bookstore model'
setupSchemaSetMocks('bookstore.yang')
)
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.UPDATE, observedTimestamp)
}
def 'Saving child data fragment under existing node.'() {
{ dataNode -> dataNode.xpath[0] == '/test-tree/branch[@name=\'New\']' })
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.CREATE, observedTimestamp)
}
def 'Saving list element data fragment under existing node.'() {
)
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.UPDATE, observedTimestamp)
}
def 'Saving collection of a batch with data fragment under existing node.'() {
assert listOfXpaths.containsAll(['/test-tree/branch[@name=\'B\']','/test-tree/branch[@name=\'A\']'])
}
}
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.UPDATE, observedTimestamp)
}
def 'Saving empty list element data fragment.'() {
1 * mockCpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName, {dataNode -> dataNode.keySet()[0] == expectedNodeXpath})
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp)
where: 'following parameters were used'
scenario | parentNodeXpath | jsonData || expectedNodeXpath
'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree'
1 * mockCpsDataPersistenceService.batchUpdateDataLeaves(dataspaceName, anchorName, {dataNode -> dataNode.keySet()[index] == expectedNodeXpath})
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp)
where: 'the following parameters were used'
index | expectedNodeXpath
0 | '/first-container'
.iterator().next() == "/bookstore/categories[@code='01']/books[@title='new']"})
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'the data updated event is sent to the notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/bookstore', Operation.UPDATE, observedTimestamp)
}
def 'Replace data node using singular data node: #scenario.'() {
then: 'the persistence service method is invoked with correct parameters'
1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
{ dataNode -> dataNode.xpath == expectedNodeXpath})
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp)
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
where: 'following parameters were used'
then: 'the persistence service method is invoked with correct parameters'
1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
{ dataNode -> dataNode.xpath == expectedNodeXpath})
- and: 'data updated event is sent to notification service'
- nodesJsonData.keySet().each {
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, it, Operation.UPDATE, observedTimestamp)
- }
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
where: 'following parameters were used'
)
and: 'the CpsValidator is called on the dataspaceName and AnchorName twice'
2 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree', Operation.UPDATE, observedTimestamp)
}
def 'Replace whole list content with empty list element.'() {
1 * mockCpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, '/test-tree/branch')
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/test-tree/branch', Operation.DELETE, observedTimestamp)
}
def 'Delete multiple list elements under existing node.'() {
1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName, ['/test-tree/branch[@name="A"]', '/test-tree/branch[@name="B"]'])
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'two data updated events are sent to notification service'
- 2 * mockNotificationService.processDataUpdatedEvent(anchor, _, Operation.DELETE, observedTimestamp)
}
def 'Delete data node under anchor and dataspace.'() {
1 * mockCpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, '/data-node')
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
- and: 'data updated event is sent to notification service'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/data-node', Operation.DELETE, observedTimestamp)
}
def 'Delete all data nodes for a given anchor and dataspace.'() {
when: 'delete data nodes method is invoked with correct parameters'
objectUnderTest.deleteDataNodes(dataspaceName, anchorName, observedTimestamp)
- then: 'data updated event is sent to notification service before the delete'
- 1 * mockNotificationService.processDataUpdatedEvent(anchor, '/', Operation.DELETE, observedTimestamp)
- and: 'the CpsValidator is called on the dataspaceName and AnchorName'
+ then: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
and: 'the persistence service method is invoked with the correct parameters'
1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName)
def 'Delete all data nodes for given dataspace and multiple anchors.'() {
given: 'schema set for given anchors and dataspace references test tree model'
setupSchemaSetMocks('test-tree.yang')
- mockCpsAdminService.getAnchors(dataspaceName, ['anchor1', 'anchor2']) >>
+ mockCpsAnchorService.getAnchors(dataspaceName, ['anchor1', 'anchor2']) >>
[new Anchor(name: 'anchor1', dataspaceName: dataspaceName),
new Anchor(name: 'anchor2', dataspaceName: dataspaceName)]
when: 'delete data node method is invoked with correct parameters'
objectUnderTest.deleteDataNodes(dataspaceName, ['anchor1', 'anchor2'], observedTimestamp)
- then: 'data updated events are sent to notification service before the delete'
- 2 * mockNotificationService.processDataUpdatedEvent(_, '/', Operation.DELETE, observedTimestamp)
- and: 'the CpsValidator is called on the dataspace name and the anchor names'
+ then: 'the CpsValidator is called on the dataspace name and the anchor names'
2 * mockCpsValidator.validateNameCharacters(_)
and: 'the persistence service method is invoked with the correct parameters'
1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, _ as Collection<String>)
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.api.impl
+
+import org.onap.cps.spi.CpsAdminPersistenceService
+import org.onap.cps.spi.model.Dataspace
+import org.onap.cps.spi.utils.CpsValidator
+import spock.lang.Specification
+
+class CpsDataspaceServiceImplSpec extends Specification {
+ def mockCpsAdminPersistenceService = Mock(CpsAdminPersistenceService)
+ def mockCpsValidator = Mock(CpsValidator)
+ def objectUnderTest = new CpsDataspaceServiceImpl(mockCpsAdminPersistenceService,mockCpsValidator)
+
+ def 'Create dataspace method invokes persistence service.'() {
+ when: 'create dataspace method is invoked'
+ objectUnderTest.createDataspace('someDataspace')
+ then: 'the persistence service method is invoked with same parameters'
+ 1 * mockCpsAdminPersistenceService.createDataspace('someDataspace')
+ and: 'the CpsValidator is called on the dataspaceName'
+ 1 * mockCpsValidator.validateNameCharacters('someDataspace')
+ }
+
+ def 'Retrieve dataspace.'() {
+ given: 'a dataspace is already created'
+ def dataspace = new Dataspace(name: "someDataspace")
+ mockCpsAdminPersistenceService.getDataspace('someDataspace') >> dataspace
+ expect: 'the dataspace provided by persistence service is returned as result'
+ assert objectUnderTest.getDataspace('someDataspace') == dataspace
+ }
+
+ def 'Retrieve all dataspaces.'() {
+ given: 'that all given dataspaces are already created'
+ def dataspaces = [new Dataspace(name: "test-dataspace1"), new Dataspace(name: "test-dataspace2")]
+ mockCpsAdminPersistenceService.getAllDataspaces() >> dataspaces
+ expect: 'the dataspace provided by persistence service is returned as result'
+ assert objectUnderTest.getAllDataspaces() == dataspaces
+ }
+
+ def 'Delete dataspace.'() {
+ when: 'delete dataspace is invoked'
+ objectUnderTest.deleteDataspace('someDataspace')
+ then: 'associated persistence service method is invoked with correct parameter'
+ 1 * mockCpsAdminPersistenceService.deleteDataspace('someDataspace')
+ and: 'the CpsValidator is called on the dataspaceName'
+ 1 * mockCpsValidator.validateNameCharacters('someDataspace')
+ }
+
+}
package org.onap.cps.api.impl
+import org.onap.cps.api.CpsAnchorService
+
+import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
+import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED
+
import org.onap.cps.TestUtils
-import org.onap.cps.api.CpsAdminService
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
import org.onap.cps.spi.exceptions.ModelValidationException
import org.onap.cps.yang.YangTextSchemaSourceSet
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import spock.lang.Specification
-import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
-import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED
class CpsModuleServiceImplSpec extends Specification {
def mockCpsModulePersistenceService = Mock(CpsModulePersistenceService)
- def mockCpsAdminService = Mock(CpsAdminService)
def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
+ def mockCpsAnchorService = Mock(CpsAnchorService)
def mockCpsValidator = Mock(CpsValidator)
def timedYangTextSchemaSourceSetBuilder = new TimedYangTextSchemaSourceSetBuilder()
- def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAdminService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder)
+ def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAnchorService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder)
def 'Create schema set.'() {
when: 'Create schema set method is invoked'
def moduleReferenceForExistingModule = new ModuleReference('test', '2021-10-12','test.org')
def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule]
when: 'create schema set from modules method is invoked'
- objectUnderTest.createOrUpgradeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
+ objectUnderTest.createSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
then: 'processing is delegated to persistence service'
1 * mockCpsModulePersistenceService.storeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
and: 'the CpsValidator is called on the dataspaceName and schemaSetName'
def 'Delete schema-set when cascade is allowed.'() {
given: '#numberOfAnchors anchors are associated with schemaset'
def associatedAnchors = createAnchors(numberOfAnchors)
- mockCpsAdminService.getAnchors('my-dataspace', 'my-schemaset') >> associatedAnchors
+ mockCpsAnchorService.getAnchors('my-dataspace', 'my-schemaset') >> associatedAnchors
when: 'schema set deletion is requested with cascade allowed'
objectUnderTest.deleteSchemaSet('my-dataspace', 'my-schemaset', CASCADE_DELETE_ALLOWED)
then: 'anchor deletion is called #numberOfAnchors times'
- numberOfAnchors * mockCpsAdminService.deleteAnchor('my-dataspace', _)
+ numberOfAnchors * mockCpsAnchorService.deleteAnchor('my-dataspace', _)
and: 'persistence service method is invoked with same parameters'
1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset')
and: 'schema set will be removed from the cache'
def 'Delete schema-set when cascade is prohibited.'() {
given: 'no anchors are associated with schemaset'
- mockCpsAdminService.getAnchors('my-dataspace', 'my-schemaset') >> Collections.emptyList()
+ mockCpsAnchorService.getAnchors('my-dataspace', 'my-schemaset') >> Collections.emptyList()
when: 'schema set deletion is requested with cascade allowed'
objectUnderTest.deleteSchemaSet('my-dataspace', 'my-schemaset', CASCADE_DELETE_PROHIBITED)
then: 'no anchors are deleted'
- 0 * mockCpsAdminService.deleteAnchor(_, _)
+ 0 * mockCpsAnchorService.deleteAnchor(_, _)
and: 'persistence service method is invoked with same parameters'
1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset')
and: 'schema set will be removed from the cache'
def 'Delete schema-set when cascade is prohibited and schema-set has anchors.'() {
given: '2 anchors are associated with schemaset'
- mockCpsAdminService.getAnchors('my-dataspace', 'my-schemaset') >> createAnchors(2)
+ mockCpsAnchorService.getAnchors('my-dataspace', 'my-schemaset') >> createAnchors(2)
when: 'schema set deletion is requested with cascade allowed'
objectUnderTest.deleteSchemaSet('my-dataspace', 'my-schemaset', CASCADE_DELETE_PROHIBITED)
then: 'Schema-Set in Use exception is thrown'
thrown(SchemaSetInUseException)
}
- def createAnchors(int anchorCount) {
- def anchors = []
- (0..<anchorCount).each { anchors.add(new Anchor("my-anchor-$it", 'my-dataspace', 'my-schemaset')) }
- return anchors
- }
-
def 'Delete multiple schema-sets when cascade is allowed.'() {
given: '#numberOfAnchors anchors are associated with each schemaset'
- mockCpsAdminService.getAnchors('my-dataspace', ['my-schemaset1', 'my-schemaset2']) >> createAnchors(numberOfAnchors * 2)
+ mockCpsAnchorService.getAnchors('my-dataspace', ['my-schemaset1', 'my-schemaset2']) >> createAnchors(numberOfAnchors * 2)
when: 'schema set deletion is requested with cascade allowed'
objectUnderTest.deleteSchemaSetsWithCascade('my-dataspace', ['my-schemaset1', 'my-schemaset2'])
then: 'anchor deletion is called #numberOfAnchors times'
- mockCpsAdminService.deleteAnchors('my-dataspace', _)
+ mockCpsAnchorService.deleteAnchors('my-dataspace', _)
and: 'persistence service method is invoked with same parameters'
mockCpsModulePersistenceService.deleteSchemaSets('my-dataspace', _)
and: 'schema sets will be removed from the cache'
numberOfAnchors << [0, 3]
}
+ def 'Upgrade existing schema set'() {
+ when: 'schema set update is requested'
+ objectUnderTest.upgradeSchemaSetFromModules('my-dataspace', 'my-schemaset', [:], moduleReferences)
+ then: 'no exception is thrown '
+ noExceptionThrown()
+ }
+
def 'Get all yang resources module references.'() {
given: 'an already present module reference'
- def moduleReferences = [new ModuleReference('some module name','some revision name')]
+ def moduleReferences = getModuleReferences()
mockCpsModulePersistenceService.getYangResourceModuleReferences('someDataspaceName') >> moduleReferences
when: 'get yang resource module references is called'
def result = objectUnderTest.getYangResourceModuleReferences('someDataspaceName')
def 'Identifying new module references.'(){
given: 'module references from cm handle'
- def moduleReferencesToCheck = [new ModuleReference('some-module', 'some-revision')]
+ def moduleReferencesToCheck = getModuleReferences()
when: 'identifyNewModuleReferences is called'
objectUnderTest.identifyNewModuleReferences(moduleReferencesToCheck)
then: 'cps module persistence service is called with module references to check'
and: 'the CpsValidator is called on the dataspaceName and schemaSetName'
1 * mockCpsValidator.validateNameCharacters('some-dataspace-name', 'some-anchor-name')
}
+
+ def getModuleReferences() {
+ return [new ModuleReference('some module name','some revision name')]
+ }
+
+ def createAnchors(int anchorCount) {
+ def anchors = []
+ (0..<anchorCount).each { anchors.add(new Anchor("my-anchor-$it", 'my-dataspace', 'my-schemaset')) }
+ return anchors
+ }
}
package org.onap.cps.api.impl\r
\r
import org.onap.cps.TestUtils\r
-import org.onap.cps.api.CpsAdminService\r
+import org.onap.cps.api.CpsAnchorService\r
import org.onap.cps.api.CpsDeltaService\r
-import org.onap.cps.notification.NotificationService\r
+import org.onap.cps.spi.CpsDataPersistenceService\r
import org.onap.cps.spi.CpsDataPersistenceService\r
import org.onap.cps.spi.CpsModulePersistenceService\r
import org.onap.cps.spi.model.Anchor\r
class E2ENetworkSliceSpec extends Specification {\r
def mockModuleStoreService = Mock(CpsModulePersistenceService)\r
def mockDataStoreService = Mock(CpsDataPersistenceService)\r
- def mockCpsAdminService = Mock(CpsAdminService)\r
- def mockNotificationService = Mock(NotificationService)\r
+ def mockCpsAnchorService = Mock(CpsAnchorService)\r
def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)\r
def mockCpsValidator = Mock(CpsValidator)\r
def timedYangTextSchemaSourceSetBuilder = new TimedYangTextSchemaSourceSetBuilder()\r
def mockCpsDeltaService = Mock(CpsDeltaService)\r
\r
def cpsModuleServiceImpl = new CpsModuleServiceImpl(mockModuleStoreService,\r
- mockYangTextSchemaSourceSetCache, mockCpsAdminService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder)\r
+ mockYangTextSchemaSourceSetCache, mockCpsAnchorService, mockCpsValidator,timedYangTextSchemaSourceSetBuilder)\r
\r
- def cpsDataServiceImpl = new CpsDataServiceImpl(mockDataStoreService, mockCpsAdminService,\r
- mockYangTextSchemaSourceSetCache, mockNotificationService, mockCpsValidator, timedYangParser, mockCpsDeltaService)\r
+ def cpsDataServiceImpl = new CpsDataServiceImpl(mockDataStoreService, mockCpsAnchorService,\r
+ mockYangTextSchemaSourceSetCache, mockCpsValidator, timedYangParser, mockCpsDeltaService)\r
\r
def dataspaceName = 'someDataspace'\r
def anchorName = 'someAnchor'\r
and : 'a valid json is provided for the model'\r
def jsonData = TestUtils.getResourceFileContent('e2e/basic/cps-Cavsta-Data.txt')\r
and : 'all the further dependencies are mocked '\r
- mockCpsAdminService.getAnchor(dataspaceName, anchorName) >>\r
+ mockCpsAnchorService.getAnchor(dataspaceName, anchorName) >>\r
new Anchor().builder().name(anchorName).schemaSetName(schemaSetName).dataspaceName(dataspaceName).build()\r
mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >>\r
YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)\r
and : 'a valid json is provided for the model'\r
def jsonData = TestUtils.getResourceFileContent('e2e/basic/cps-ran-inventory-data.json')\r
and : 'all the further dependencies are mocked '\r
- mockCpsAdminService.getAnchor('someDataspace', 'someAnchor') >>\r
+ mockCpsAnchorService.getAnchor('someDataspace', 'someAnchor') >>\r
new Anchor().builder().name('someAnchor').schemaSetName('someSchemaSet').dataspaceName(dataspaceName).build()\r
mockYangTextSchemaSourceSetCache.get('someDataspace', 'someSchemaSet') >> YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)\r
mockModuleStoreService.getYangSchemaResources('someDataspace', 'someSchemaSet') >> schemaContext\r
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.config
+
+import spock.lang.Specification
+
+class AsyncConfigSpec extends Specification {
+
+ def objectUnderTest = new AsyncConfig()
+
+ def 'Create Async Config and validate it'() {
+ when: 'we set some test properties to tune taskexecutor'
+ objectUnderTest.setCorePoolSize(5)
+ objectUnderTest.setMaxPoolSize(50)
+ objectUnderTest.setQueueCapacity(100)
+ objectUnderTest.setThreadNamePrefix('Test-')
+ objectUnderTest.setWaitForTasksToCompleteOnShutdown(true)
+ then: 'we can instantiate a Async Config object'
+ assert objectUnderTest != null
+ and: 'taskexector is configured with correct properties'
+ def tasExecutor = objectUnderTest.getThreadAsyncExecutorForNotification()
+ assert tasExecutor.properties['corePoolSize'] == 5
+ assert tasExecutor.properties['maxPoolSize'] == 50
+ assert tasExecutor.properties['queueCapacity'] == 100
+ assert tasExecutor.properties['keepAliveSeconds'] == 60
+ assert tasExecutor.properties['threadNamePrefix'] == 'Test-'
+ }
+}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (c) 2021-2022 Bell Canada.
- * Modifications Copyright (c) 2022-2023 Nordix Foundation
- * Modifications Copyright (C) 2023 TechMahindra Ltd.
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification
-
-import org.onap.cps.spi.model.DataNode
-
-import java.time.OffsetDateTime
-import java.time.format.DateTimeFormatter
-import org.onap.cps.utils.DateTimeUtility
-import org.onap.cps.utils.PrefixResolver
-import org.onap.cps.api.CpsDataService
-import org.onap.cps.event.model.Content
-import org.onap.cps.event.model.Data
-import org.onap.cps.spi.FetchDescendantsOption
-import org.onap.cps.spi.model.Anchor
-import org.onap.cps.spi.model.DataNodeBuilder
-import org.springframework.util.StringUtils
-import spock.lang.Specification
-
-class CpsDataUpdatedEventFactorySpec extends Specification {
-
- def mockCpsDataService = Mock(CpsDataService)
-
- def mockPrefixResolver = Mock(PrefixResolver)
-
- def objectUnderTest = new CpsDataUpdatedEventFactory(mockCpsDataService, mockPrefixResolver)
-
- def dateTimeFormat = 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZ'
-
- def 'Create a CPS data updated event successfully: #scenario'() {
- given: 'an anchor which has been updated'
- def anchor = new Anchor('my-anchorname', 'my-dataspace', 'my-schemaset-name')
- and: 'cps data service returns the data node details'
- def xpath = '/xpath'
- def dataNode = new DataNodeBuilder().withXpath(xpath).withLeaves(['leafName': 'leafValue']).build()
- mockCpsDataService.getDataNodes(
- 'my-dataspace', 'my-anchorname', '/', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode]
- when: 'CPS data updated event is created'
- def cpsDataUpdatedEvent = objectUnderTest.createCpsDataUpdatedEvent(anchor,
- DateTimeUtility.toOffsetDateTime(inputObservedTimestamp), Operation.CREATE)
- then: 'CPS data updated event is created with correct envelope'
- with(cpsDataUpdatedEvent) {
- type == 'org.onap.cps.data-updated-event'
- source == new URI('urn:cps:org.onap.cps')
- schema == new URI('urn:cps:org.onap.cps:data-updated-event-schema:v1')
- StringUtils.hasText(id)
- content != null
- }
- and: 'correct content'
- with(cpsDataUpdatedEvent.content) {
- assert isExpectedDateTimeFormat(observedTimestamp): "$observedTimestamp is not in $dateTimeFormat format"
- if (inputObservedTimestamp != null)
- assert observedTimestamp == inputObservedTimestamp
- else
- assert OffsetDateTime.now().minusSeconds(20).isBefore(
- DateTimeUtility.toOffsetDateTime(observedTimestamp))
- assert anchorName == 'my-anchorname'
- assert dataspaceName == 'my-dataspace'
- assert schemaSetName == 'my-schemaset-name'
- assert operation == Content.Operation.CREATE
- assert data == new Data().withAdditionalProperty('xpath', ['leafName': 'leafValue'])
- }
- where:
- scenario | inputObservedTimestamp
- 'with observed timestamp -0400' | '2021-01-01T23:00:00.345-0400'
- 'with observed timestamp +0400' | '2021-01-01T23:00:00.345+0400'
- 'missing observed timestamp' | null
- }
-
- def 'Create a delete CPS data updated event successfully'() {
- given: 'an anchor which has been deleted'
- def anchor = new Anchor('my-anchorname', 'my-dataspace', 'my-schemaset-name')
- def deletionTimestamp = '2021-01-01T23:00:00.345-0400'
- when: 'a delete root data node event is created'
- def cpsDataUpdatedEvent = objectUnderTest.createCpsDataUpdatedEvent(anchor,
- DateTimeUtility.toOffsetDateTime(deletionTimestamp), Operation.DELETE)
- then: 'CPS data updated event is created with correct envelope'
- with(cpsDataUpdatedEvent) {
- type == 'org.onap.cps.data-updated-event'
- source == new URI('urn:cps:org.onap.cps')
- schema == new URI('urn:cps:org.onap.cps:data-updated-event-schema:v1')
- StringUtils.hasText(id)
- content != null
- }
- and: 'correct content'
- with(cpsDataUpdatedEvent.content) {
- assert isExpectedDateTimeFormat(observedTimestamp): "$observedTimestamp is not in $dateTimeFormat format"
- assert observedTimestamp == deletionTimestamp
- assert anchorName == 'my-anchorname'
- assert dataspaceName == 'my-dataspace'
- assert schemaSetName == 'my-schemaset-name'
- assert operation == Content.Operation.DELETE
- assert data == null
- }
- }
-
- def 'Create CPS Data Event with URI Syntax Exception'() {
- given: 'an anchor'
- def anchor = new Anchor('my-anchorname', 'my-dataspace', 'my-schemaset-name')
- and: 'a mocked data Node (collection)'
- def mockDataNode = Mock(DataNode)
- mockCpsDataService.getDataNodes(*_) >> [ mockDataNode ]
- and: 'a URI syntax exception is thrown somewhere (using datanode as cannot manipulate hardcoded URIs'
- def originalException = new URISyntaxException('input', 'reason', 0)
- mockDataNode.getXpath() >> { throw originalException }
- when: 'attempt to create data updated event'
- objectUnderTest.createCpsDataUpdatedEvent(anchor, OffsetDateTime.now(), Operation.UPDATE)
- then: 'the same exception is thrown up'
- def thrownUp = thrown(URISyntaxException)
- assert thrownUp == originalException
- }
-
- def isExpectedDateTimeFormat(String observedTimestamp) {
- try {
- DateTimeFormatter.ofPattern(dateTimeFormat).parse(observedTimestamp)
- } catch (DateTimeParseException) {
- return false
- }
- return true
- }
-
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021 Bell Canada. All rights reserved.
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification
-
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration
-import org.springframework.boot.test.context.SpringBootTest
-import org.springframework.kafka.config.TopicBuilder
-import org.springframework.kafka.core.ConsumerFactory
-import org.springframework.kafka.core.KafkaAdmin
-import org.springframework.kafka.core.KafkaTemplate
-import org.springframework.kafka.listener.ConcurrentMessageListenerContainer
-import org.springframework.kafka.listener.ContainerProperties
-import org.springframework.kafka.listener.MessageListener
-import org.springframework.kafka.test.utils.ContainerTestUtils
-import org.springframework.test.context.ContextConfiguration
-import org.springframework.test.context.DynamicPropertyRegistry
-import org.springframework.test.context.DynamicPropertySource
-import spock.lang.Shared
-import spock.lang.Specification
-
-@ContextConfiguration(classes = [KafkaAutoConfiguration, KafkaProducerListener, NotificationErrorHandler])
-@SpringBootTest
-class KafkaPublisherSpecBase extends Specification {
-
- @Autowired
- KafkaTemplate kafkaTemplate
-
- @Autowired
- KafkaAdmin kafkaAdmin
-
- @Autowired
- ConsumerFactory consumerFactory
-
- @Shared volatile topicCreated = false
- @Shared consumedMessages = new ArrayList<>()
-
- def cpsEventTopic = 'cps-events'
-
- @DynamicPropertySource
- static void registerKafkaProperties(DynamicPropertyRegistry registry) {
- registry.add("spring.kafka.bootstrap-servers", KafkaTestContainerConfig::getBootstrapServers)
- }
-
- def setup() {
- // Kafka listener and topic should be created only once for a test-suite.
- // We are also dependent on sprint context to achieve it, and can not execute it in setupSpec
- if (!topicCreated) {
- kafkaAdmin.createOrModifyTopics(TopicBuilder.name(cpsEventTopic).partitions(1).replicas(1).build())
- startListeningToTopic()
- topicCreated = true
- }
- /* kafka message listener stores the messages to consumedMessages.
- It is important to clear the list before each test case so that test cases can fetch the message from index '0'.
- */
- consumedMessages.clear()
- }
-
- def startListeningToTopic() {
- ContainerProperties containerProperties = new ContainerProperties(cpsEventTopic)
- containerProperties.setMessageListener([
- onMessage: {
- record ->
- consumedMessages.add(record.value())
- }] as MessageListener)
-
- ConcurrentMessageListenerContainer container =
- new ConcurrentMessageListenerContainer<>(
- consumerFactory,
- containerProperties)
-
- container.start()
- ContainerTestUtils.waitForAssignment(container, 1)
- }
-
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021 Bell Canada. All rights reserved.
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification
-
-import org.testcontainers.containers.KafkaContainer
-import org.testcontainers.utility.DockerImageName
-
-class KafkaTestContainerConfig {
-
- private static KafkaContainer kafkaContainer
-
- static {
- getKafkaContainer()
- }
-
- // Not the best performance but it is good enough for test case
- private static synchronized KafkaContainer getKafkaContainer() {
- if (kafkaContainer == null) {
- kafkaContainer = new KafkaContainer(DockerImageName.parse("registry.nordix.org/onaptest/confluentinc/cp-kafka:6.2.1").asCompatibleSubstituteFor("confluentinc/cp-kafka"))
- .withEnv("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "false")
- kafkaContainer.start()
- Runtime.getRuntime().addShutdownHook(new Thread(kafkaContainer::stop))
- }
- return kafkaContainer
- }
-
- static String getBootstrapServers() {
- getKafkaContainer()
- return kafkaContainer.getBootstrapServers()
- }
-
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification
-
-import ch.qos.logback.classic.Logger
-import ch.qos.logback.classic.spi.ILoggingEvent
-import ch.qos.logback.core.read.ListAppender
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.BeforeEach
-import org.slf4j.LoggerFactory
-
-import spock.lang.Specification
-
-class NotificationErrorHandlerSpec extends Specification{
-
- NotificationErrorHandler objectUnderTest = new NotificationErrorHandler()
- def logWatcher = Spy(ListAppender<ILoggingEvent>)
-
- @BeforeEach
- void setup() {
- ((Logger) LoggerFactory.getLogger(NotificationErrorHandler.class)).addAppender(logWatcher);
- logWatcher.start();
- }
-
- @AfterEach
- void teardown() {
- ((Logger) LoggerFactory.getLogger(NotificationErrorHandler.class)).detachAndStopAllAppenders();
- }
-
- def 'Logging exception via notification error handler #scenario'() {
- when: 'exception #scenario occurs'
- objectUnderTest.onException(exception, 'some context')
- then: 'log output results contains the correct error details'
- def logMessage = logWatcher.list[0].getFormattedMessage()
- assert logMessage.contains('Failed to process')
- assert logMessage.contains("Error cause: ${exptectedCauseString}")
- assert logMessage.contains("Error context: [some context]")
- where:
- scenario | exception || exptectedCauseString
- 'with cause' | new Exception('message') || 'message'
- 'without cause' | new Exception('message', new RuntimeException('cause')) || 'java.lang.RuntimeException: cause'
- }
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021 Bell Canada. All rights reserved.
- * Modifications Copyright (C) 2021-2022 Nordix Foundation
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification
-
-import org.apache.kafka.clients.producer.ProducerRecord
-import org.onap.cps.event.model.Content
-import org.onap.cps.event.model.CpsDataUpdatedEvent
-import org.spockframework.spring.SpringBean
-import org.springframework.kafka.KafkaException
-import org.springframework.kafka.core.KafkaTemplate
-import spock.util.concurrent.PollingConditions
-
-class NotificationPublisherSpec extends KafkaPublisherSpecBase {
-
- @SpringBean
- NotificationErrorHandler spyNotificationErrorHandler = Spy(new NotificationErrorHandler())
-
- @SpringBean
- KafkaProducerListener spyKafkaProducerListener = Spy(new KafkaProducerListener<>(spyNotificationErrorHandler))
-
- KafkaTemplate spyKafkaTemplate
- NotificationPublisher objectUnderTest
-
- def myAnchorName = 'my-anchor'
- def myDataspaceName = 'my-dataspace'
-
- def cpsDataUpdatedEvent = new CpsDataUpdatedEvent()
- .withContent(new Content()
- .withDataspaceName(myDataspaceName)
- .withAnchorName(myAnchorName))
-
- def setup() {
- spyKafkaTemplate = Spy(kafkaTemplate)
- objectUnderTest = new NotificationPublisher(spyKafkaTemplate, cpsEventTopic);
- }
-
- def 'Sending event to message bus with correct message Key.'() {
-
- when: 'event is sent to publisher'
- objectUnderTest.sendNotification(cpsDataUpdatedEvent)
- kafkaTemplate.flush()
-
- then: 'event is sent to correct topic with the expected messageKey'
- interaction {
- def messageKey = myDataspaceName + "," + myAnchorName
- 1 * spyKafkaTemplate.send(cpsEventTopic, messageKey, cpsDataUpdatedEvent)
- }
- and: 'received a successful response'
- 1 * spyKafkaProducerListener.onSuccess(_ as ProducerRecord, _)
- and: 'kafka consumer returns expected message'
- def conditions = new PollingConditions(timeout: 60, initialDelay: 0, factor: 1)
- conditions.eventually {
- assert cpsDataUpdatedEvent == consumedMessages.get(0)
- }
- }
-
- def 'Handling of async errors from message bus.'() {
- given: 'topic does not exist'
- objectUnderTest.topicName = 'non-existing-topic'
-
- when: 'message to sent to a non-existing topic'
- objectUnderTest.sendNotification(cpsDataUpdatedEvent)
- kafkaTemplate.flush()
-
- then: 'error is thrown'
- thrown KafkaException
- and: 'error handler is called with exception details'
- 1 * spyKafkaProducerListener.onError(_ as ProducerRecord, _, _ as Exception)
- 1 * spyNotificationErrorHandler.onException(_ as String, _ as Exception,
- _ as ProducerRecord, _)
- }
-
-}
+++ /dev/null
-/*
- * ============LICENSE_START=======================================================
- * Copyright (c) 2021-2022 Bell Canada.
- * Modifications Copyright (C) 2022-2023 Nordix Foundation
- * ================================================================================
- * 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.notification
-
-import org.onap.cps.api.CpsAdminService
-import org.onap.cps.config.AsyncConfig
-import org.onap.cps.event.model.CpsDataUpdatedEvent
-import org.onap.cps.spi.model.Anchor
-import org.spockframework.spring.SpringBean
-import org.spockframework.spring.SpringSpy
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.context.properties.EnableConfigurationProperties
-import org.springframework.boot.test.context.SpringBootTest
-import org.springframework.test.context.ContextConfiguration
-import spock.lang.Shared
-import spock.lang.Specification
-
-import java.time.OffsetDateTime
-import java.util.concurrent.TimeUnit
-
-@SpringBootTest
-@EnableConfigurationProperties
-@ContextConfiguration(classes = [NotificationProperties, NotificationService, NotificationErrorHandler, AsyncConfig])
-class NotificationServiceSpec extends Specification {
-
- @SpringSpy
- NotificationProperties spyNotificationProperties
- @SpringBean
- NotificationPublisher mockNotificationPublisher = Mock()
- @SpringBean
- CpsDataUpdatedEventFactory mockCpsDataUpdatedEventFactory = Mock()
- @SpringSpy
- NotificationErrorHandler spyNotificationErrorHandler
- @SpringBean
- CpsAdminService mockCpsAdminService = Mock()
-
- @Autowired
- NotificationService objectUnderTest
-
- @Shared
- def dataspaceName = 'my-dataspace-published'
- @Shared
- def anchorName = 'my-anchorname'
- @Shared
- def anchor = new Anchor('my-anchorname', 'my-dataspace-published', 'my-schemaset-name')
- def myObservedTimestamp = OffsetDateTime.now()
-
- def setup() {
- mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor
- }
-
- def 'Skip sending notification when disabled.'() {
- given: 'notification is disabled'
- spyNotificationProperties.isEnabled() >> false
- when: 'dataUpdatedEvent is received'
- objectUnderTest.processDataUpdatedEvent(anchor, '/', Operation.CREATE, myObservedTimestamp)
- then: 'the notification is not sent'
- 0 * mockNotificationPublisher.sendNotification(_)
- }
-
- def 'Send notification when enabled: #scenario.'() {
- given: 'notification is enabled'
- spyNotificationProperties.isEnabled() >> true
- and: 'an anchor is in dataspace where #scenario'
- def anchor = new Anchor('my-anchorname', dataspaceName, 'my-schemaset-name')
- and: 'event factory can create event successfully'
- def cpsDataUpdatedEvent = new CpsDataUpdatedEvent()
- mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, Operation.CREATE) >> cpsDataUpdatedEvent
- when: 'dataUpdatedEvent is received'
- def future = objectUnderTest.processDataUpdatedEvent(anchor, '/', Operation.CREATE, myObservedTimestamp)
- and: 'wait for async processing to complete'
- future.get(10, TimeUnit.SECONDS)
- then: 'async process completed successfully'
- future.isDone()
- and: 'notification is sent'
- expectedSendNotificationCount * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent)
- where:
- scenario | dataspaceName || expectedSendNotificationCount
- 'dataspace name does not match filter' | 'does-not-match-pattern' || 0
- 'dataspace name matches filter' | 'my-dataspace-published' || 1
- }
-
- def '#scenario are changed with xpath #xpath and operation #operation'() {
- given: 'notification is enabled'
- spyNotificationProperties.isEnabled() >> true
- and: 'event factory creates event if operation is #operation'
- def cpsDataUpdatedEvent = new CpsDataUpdatedEvent()
- mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, expectedOperationInEvent) >>
- cpsDataUpdatedEvent
- when: 'dataUpdatedEvent is received for #xpath'
- def future = objectUnderTest.processDataUpdatedEvent(anchor, xpath, operation, myObservedTimestamp)
- and: 'wait for async processing to complete'
- future.get(10, TimeUnit.SECONDS)
- then: 'async process completed successfully'
- future.isDone()
- and: 'notification is sent'
- 1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent)
- where:
- scenario | xpath | operation || expectedOperationInEvent
- 'Same event is sent when root nodes' | '' | Operation.CREATE || Operation.CREATE
- 'Same event is sent when root nodes' | '' | Operation.UPDATE || Operation.UPDATE
- 'Same event is sent when root nodes' | '' | Operation.DELETE || Operation.DELETE
- 'Same event is sent when root nodes' | '/' | Operation.CREATE || Operation.CREATE
- 'Same event is sent when root nodes' | '/' | Operation.UPDATE || Operation.UPDATE
- 'Same event is sent when root nodes' | '/' | Operation.DELETE || Operation.DELETE
- 'Same event is sent when container nodes' | '/parent' | Operation.CREATE || Operation.CREATE
- 'Same event is sent when container nodes' | '/parent' | Operation.UPDATE || Operation.UPDATE
- 'Same event is sent when container nodes' | '/parent' | Operation.DELETE || Operation.DELETE
- 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.CREATE || Operation.UPDATE
- 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.UPDATE || Operation.UPDATE
- 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.DELETE || Operation.UPDATE
- }
-
- def 'Error handling in notification service.'() {
- given: 'notification is enabled'
- spyNotificationProperties.isEnabled() >> true
- and: 'event factory can not create event successfully'
- mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, Operation.CREATE) >>
- { throw new Exception("Could not create event") }
- when: 'event is sent for processing'
- def future = objectUnderTest.processDataUpdatedEvent(anchor, '/', Operation.CREATE, myObservedTimestamp)
- and: 'wait for async processing to complete'
- future.get(10, TimeUnit.SECONDS)
- then: 'async process completed successfully'
- future.isDone()
- and: 'error is handled and not thrown to caller'
- notThrown Exception
- 1 * spyNotificationErrorHandler.onException(_, _, _, '/', Operation.CREATE)
- }
-
- def 'Disabled Notification services'() {
- given: 'a notification service that is disabled'
- spyNotificationProperties.enabled >> false
- NotificationService notificationService = new NotificationService(spyNotificationProperties, mockNotificationPublisher, mockCpsDataUpdatedEventFactory, spyNotificationErrorHandler, mockCpsAdminService)
- notificationService.init()
- expect: 'it will not send notifications'
- assert notificationService.shouldSendNotification('') == false
- }
-}
import com.hazelcast.map.IMap
import org.onap.cps.TestUtils
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.impl.YangTextSchemaSourceSetCache
import org.onap.cps.cache.AnchorDataCacheEntry
import org.onap.cps.spi.model.Anchor
class PrefixResolverSpec extends Specification {
- def mockCpsAdminService = Mock(CpsAdminService)
+ def mockCpsAnchorService = Mock(CpsAnchorService)
def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
def mockAnchorDataCache = Mock(IMap<String, AnchorDataCacheEntry>)
- def objectUnderTest = new PrefixResolver(mockCpsAdminService, mockYangTextSchemaSourceSetCache, mockAnchorDataCache)
+ def objectUnderTest = new PrefixResolver(mockCpsAnchorService, mockYangTextSchemaSourceSetCache, mockAnchorDataCache)
def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
given: 'an anchor for the test-tree model'
def anchor = new Anchor(dataspaceName: 'testDataspace', name: 'testAnchor')
and: 'the system can get this anchor'
- mockCpsAdminService.getAnchor('testDataspace', 'testAnchor') >> anchor
+ mockCpsAnchorService.getAnchor('testDataspace', 'testAnchor') >> anchor
and: 'the schema source cache contains the schema context for the test-tree module'
mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext
}
--- /dev/null
+# ============LICENSE_START=======================================================
+# Copyright (C) 2023 Nordix Foundation
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
+# WAIT 10 minutes maximum and test every 30 seconds if SDNC is up using HealthCheck API
+TIME_OUT=600
+INTERVAL=30
+TIME=0
+while [ "$TIME" -lt "$TIME_OUT" ]; do
+ response=$(curl --write-out '%{http_code}' --silent --output /dev/null -H "Authorization: Basic YWRtaW46S3A4Yko0U1hzek0wV1hsaGFrM2VIbGNzZTJnQXc4NHZhb0dHbUp2VXkyVQ==" -X POST -H "X-FromAppId: csit-sdnc" -H "X-TransactionId: csit-sdnc" -H "Accept: application/json" -H "Content-Type: application/json" http://$SDNC_HOST:$SDNC_PORT/restconf/operations/SLI-API:healthcheck );
+ echo $response
+
+ if [ "$response" == "200" ]; then
+ echo SDNC started in $TIME seconds
+ break;
+ fi
+
+ echo Sleep: $INTERVAL seconds before testing if SDNC is up. Total wait time up now is: $TIME seconds. Timeout is: $TIME_OUT seconds
+ sleep $INTERVAL
+ TIME=$(($TIME+$INTERVAL))
+done
+
+if [ "$TIME" -ge "$TIME_OUT" ]; then
+ echo TIME OUT: karaf session not started in $TIME_OUT seconds... Could cause problems for testing activities...
+fi
+
+###################### mount pnf-sim as PNFDemo ##########################
+SDNC_TIME_OUT=250
+SDNC_INTERVAL=10
+SDNC_TIME=0
+
+while [ "$SDNC_TIME" -le "$SDNC_TIME_OUT" ]; do
+
+ # Mount netconf node
+ curl --location --request PUT 'http://'$SDNC_HOST:$SDNC_PORT'/restconf/config/network-topology:network-topology/topology/topology-netconf/node/PNFDemo' \
+ --header 'Authorization: Basic YWRtaW46S3A4Yko0U1hzek0wV1hsaGFrM2VIbGNzZTJnQXc4NHZhb0dHbUp2VXkyVQ==' \
+ --header 'Content-Type: application/json' \
+ --data-raw '{
+ "node": [
+ {
+ "node-id": "PNFDemo",
+ "netconf-node-topology:protocol": {
+ "name": "TLS"
+ },
+ "netconf-node-topology:host": "'$LOCAL_IP'",
+ "netconf-node-topology:key-based": {
+ "username": "netconf",
+ "key-id": "ODL_private_key_0"
+ },
+ "netconf-node-topology:port": 6512,
+ "netconf-node-topology:tcp-only": false,
+ "netconf-node-topology:max-connection-attempts": 5
+ }
+ ]
+ }'
+
+ # Verify node has been mounted
+
+ RESPONSE=$( curl --location --request GET 'http://'$SDNC_HOST:$SDNC_PORT'/restconf/config/network-topology:network-topology/topology/topology-netconf' --header 'Authorization: basic YWRtaW46S3A4Yko0U1hzek0wV1hsaGFrM2VIbGNzZTJnQXc4NHZhb0dHbUp2VXkyVQ==')
+
+ if [[ "$RESPONSE" == *"PNFDemo"* ]]; then
+ echo "Node mounted in $SDNC_TIME"
+ sleep 10
+ break;
+ fi
+
+ sleep $SDNC_INTERVAL
+ SDNC_TIME=$((SDNC_TIME + SDNC_INTERVAL))
+
+done
\ No newline at end of file
export SDNC_CERT_PATH=$WORKSPACE/plans/cps/sdnc/certs
#start SDNC containers with docker compose and configuration from docker-compose.yml
-docker-compose -f $WORKSPACE/plans/cps/sdnc/docker-compose.yml up -d
-
-# WAIT 10 minutes maximum and test every 30 seconds if SDNC is up using HealthCheck API
-TIME_OUT=600
-INTERVAL=30
-TIME=0
-while [ "$TIME" -lt "$TIME_OUT" ]; do
- response=$(curl --write-out '%{http_code}' --silent --output /dev/null -H "Authorization: Basic YWRtaW46S3A4Yko0U1hzek0wV1hsaGFrM2VIbGNzZTJnQXc4NHZhb0dHbUp2VXkyVQ==" -X POST -H "X-FromAppId: csit-sdnc" -H "X-TransactionId: csit-sdnc" -H "Accept: application/json" -H "Content-Type: application/json" http://$SDNC_HOST:$SDNC_PORT/restconf/operations/SLI-API:healthcheck );
- echo $response
-
- if [ "$response" == "200" ]; then
- echo SDNC started in $TIME seconds
- break;
- fi
-
- echo Sleep: $INTERVAL seconds before testing if SDNC is up. Total wait time up now is: $TIME seconds. Timeout is: $TIME_OUT seconds
- sleep $INTERVAL
- TIME=$(($TIME+$INTERVAL))
-done
-
-if [ "$TIME" -ge "$TIME_OUT" ]; then
- echo TIME OUT: karaf session not started in $TIME_OUT seconds... Could cause problems for testing activities...
-fi
\ No newline at end of file
+docker-compose -f $WORKSPACE/plans/cps/sdnc/docker-compose.yml up -d
\ No newline at end of file
###################### setup pnfsim #####################################
docker-compose -f $WORKSPACE/plans/cps/pnfsim/docker-compose.yml up -d
-# Allow time for netconf-pnp-simulator & SDNC to come up fully
-sleep 30s
-
-###################### mount pnf-sim as PNFDemo ##########################
-SDNC_TIME_OUT=250
-SDNC_INTERVAL=10
-SDNC_TIME=0
-
-while [ "$SDNC_TIME" -le "$SDNC_TIME_OUT" ]; do
-
- # Mount netconf node
- curl --location --request PUT 'http://'$SDNC_HOST:$SDNC_PORT'/restconf/config/network-topology:network-topology/topology/topology-netconf/node/PNFDemo' \
- --header 'Authorization: Basic YWRtaW46S3A4Yko0U1hzek0wV1hsaGFrM2VIbGNzZTJnQXc4NHZhb0dHbUp2VXkyVQ==' \
- --header 'Content-Type: application/json' \
- --data-raw '{
- "node": [
- {
- "node-id": "PNFDemo",
- "netconf-node-topology:protocol": {
- "name": "TLS"
- },
- "netconf-node-topology:host": "'$LOCAL_IP'",
- "netconf-node-topology:key-based": {
- "username": "netconf",
- "key-id": "ODL_private_key_0"
- },
- "netconf-node-topology:port": 6512,
- "netconf-node-topology:tcp-only": false,
- "netconf-node-topology:max-connection-attempts": 5
- }
- ]
- }'
-
- # Verify node has been mounted
-
- RESPONSE=$( curl --location --request GET 'http://'$SDNC_HOST:$SDNC_PORT'/restconf/config/network-topology:network-topology/topology/topology-netconf' --header 'Authorization: basic YWRtaW46S3A4Yko0U1hzek0wV1hsaGFrM2VIbGNzZTJnQXc4NHZhb0dHbUp2VXkyVQ==')
-
- if [[ "$RESPONSE" == *"PNFDemo"* ]]; then
- echo "Node mounted in $SDNC_TIME"
- break;
- fi
-
- sleep $SDNC_INTERVAL
- SDNC_TIME=$((SDNC_TIME + SDNC_INTERVAL))
-
-done
-
###################### verify ncmp-cps health ##########################
check_health $CPS_CORE_HOST:$CPS_CORE_PORT 'cps-ncmp'
--- /dev/null
+# ============LICENSE_START=======================================================
+# Copyright (C) 2023 Nordix Foundation
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+
+# Test suites are relative paths under csit/tests/.
+# Place the suites in run order.
+actuator
+cps-admin
+cps-data
\ No newline at end of file
# ============LICENSE_START=======================================================
-# Copyright (C) 2021-2023 Nordix Foundation
+# Copyright (C) 2023 Nordix Foundation
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# Test suites are relative paths under csit/tests/.
# Place the suites in run order.
-actuator
-cps-admin
-cps-data
cps-model-sync
cps-data-sync
cps-subscriptions
# functions
#
+# relax set options so the sourced file will not fail
+# the responsibility is shifted to the sourced file...
+function relax_set() {
+ set +e
+ set +o pipefail
+}
+
+# load the saved set options
+function load_set() {
+ _setopts="$-"
+
+ # bash shellopts
+ for i in $(echo "$SHELLOPTS" | tr ':' ' ') ; do
+ set +o ${i}
+ done
+ for i in $(echo "$RUN_CSIT_SHELLOPTS" | tr ':' ' ') ; do
+ set -o ${i}
+ done
+
+ # other options
+ for i in $(echo "$_setopts" | sed 's/./& /g') ; do
+ set +${i}
+ done
+ set -${RUN_CSIT_SAVE_SET}
+}
+
# wrapper for sourcing a file
function source_safely() {
[ -z "$1" ] && return 1
RUN_CSIT_SHELLOPTS="$SHELLOPTS"
}
-# load the saved set options
-function load_set() {
- _setopts="$-"
-
- # bash shellopts
- for i in $(echo "$SHELLOPTS" | tr ':' ' ') ; do
- set +o ${i}
- done
- for i in $(echo "$RUN_CSIT_SHELLOPTS" | tr ':' ' ') ; do
- set -o ${i}
- done
-
- # other options
- for i in $(echo "$_setopts" | sed 's/./& /g') ; do
- set +${i}
- done
- set -${RUN_CSIT_SAVE_SET}
-}
-
# set options for quick bailout when error
function harden_set() {
set -xeo pipefail
set +u # enabled it would probably fail too many often
}
-# relax set options so the sourced file will not fail
-# the responsibility is shifted to the sourced file...
-function relax_set() {
- set +e
- set +o pipefail
+function run_test_plan() {
+ testplan=$1
+
+ cd "$WORKDIR"
+ echo "Reading the testplan:"
+ cat "${TESTPLANDIR}/${testplan}.txt" | egrep -v '(^[[:space:]]*#|^[[:space:]]*$)' | sed "s|^|${WORKSPACE}/tests/|" > ${testplan}.txt
+ cat ${testplan}.txt
+ SUITES=$( xargs -a ${testplan}.txt )
+
+ python3 -m robot.run -N ${TESTPLAN} -v WORKSPACE:/tmp ${ROBOT_VARIABLES} ${TESTOPTIONS} ${SUITES}
+ RESULT=$?
+ load_set
+ echo "RESULT: $RESULT"
+ return $RESULT
}
#
export WORKSPACE=$(git rev-parse --show-toplevel)
fi
-if [ -f "${WORKSPACE}/${1}/testplan.txt" ]; then
+if [ -f "${WORKSPACE}/${1}/testplanCps.txt" ]; then
export TESTPLAN="${1}"
else
- echo "testplan not found: ${WORKSPACE}/${TESTPLAN}/testplan.txt"
+ echo "testplan not found: ${WORKSPACE}/${TESTPLAN}/testplanCps.txt or testplanNcmp.txt"
exit 2
fi
docker_stats | tee "$WORKSPACE/archives/$TESTPLAN/_sysinfo-1-after-setup.txt"
# Run test plan
-cd "$WORKDIR"
-echo "Reading the testplan:"
-cat "${TESTPLANDIR}/testplan.txt" | egrep -v '(^[[:space:]]*#|^[[:space:]]*$)' | sed "s|^|${WORKSPACE}/tests/|" > testplan.txt
-cat testplan.txt
-SUITES=$( xargs -a testplan.txt )
-
echo ROBOT_VARIABLES="${ROBOT_VARIABLES}"
echo "Starting Robot test suites ${SUITES} ..."
relax_set
pip freeze
python3 -m robot.run --version || :
-python3 -m robot.run -N ${TESTPLAN} -v WORKSPACE:/tmp ${ROBOT_VARIABLES} ${TESTOPTIONS} ${SUITES}
-RESULT=$?
-load_set
-echo "RESULT: $RESULT"
+run_test_plan "testplanCps"
+CPSRESULT="$?"
+
+cd "${TESTPLANDIR}"
+checkandmount="${TESTPLANDIR}/sdnc/check_sdnc_mount_node.sh"
+if [ -f "${checkandmount}" ]; then
+ echo "Running check_sdnc_mount_node script ${checkandmount}"
+ source_safely "${checkandmount}"
+fi
+
+run_test_plan "testplanNcmp"
+NCMPRESULT="$?"
+
# Note that the final steps are done in on_exit function after this exit!
-exit $RESULT
+exit $CPSRESULT || $NCMPRESULT
END
END
-Update Bookstore using passthrough-running for Category 01
+Update Bookstore using passthrough-running update Category 01 (replace category)
${uri}= Set Variable ${ncmpBasePath}/v1/ch/PNFDemo/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=stores:bookstore/categories=01
${headers}= Create Dictionary Content-Type=application/json Authorization=${auth}
${jsonData}= Get Binary File ${DATADIR_NCMP}${/}bookstoreUpdateExample.json
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
</parent>
<artifactId>dmi-plugin-demo-and-csit-stub-app</artifactId>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
</parent>
<artifactId>dmi-plugin-demo-and-csit-stub-service</artifactId>
final String moduleResponseContent = ResourceFileReaderUtil
.getResourceFileContent(applicationContext.getResource(
ResourceLoader.CLASSPATH_URL_PREFIX + "module/moduleResponse.json"));
+ log.info("cm handle: {} requested for modules", cmHandle);
return ResponseEntity.ok(moduleResponseContent);
}
final String moduleResourcesResponseContent = ResourceFileReaderUtil
.getResourceFileContent(applicationContext.getResource(
ResourceLoader.CLASSPATH_URL_PREFIX + "module/moduleResourcesResponse.json"));
+ log.info("cm handle: {} requested for module resources", cmHandle);
return ResponseEntity.ok(moduleResourcesResponseContent);
}
{
"moduleName": "ietf-yang-types-2",
"revision": "2013-07-16",
- "yangSource": "module ietf-yang-types-2 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-2\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
+ "yangSource": "module ietf-yang-types-2 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-2\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-16 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
},
{
"moduleName": "ietf-yang-types-3",
"revision": "2013-07-17",
- "yangSource": "module ietf-yang-types-3 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-3\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
+ "yangSource": "module ietf-yang-types-3 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-3\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-17 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
},
{
"moduleName": "ietf-yang-types-4",
"revision": "2013-07-18",
- "yangSource": "module ietf-yang-types-4 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-4\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
+ "yangSource": "module ietf-yang-types-4 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-4\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-18 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
},
{
"moduleName": "ietf-yang-types-5",
"revision": "2013-07-19",
- "yangSource": "module ietf-yang-types-5 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-5\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
+ "yangSource": "module ietf-yang-types-5 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-5\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-19 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
},
{
"moduleName": "ietf-yang-types-6",
"revision": "2013-07-20",
- "yangSource": "module ietf-yang-types-6 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-6\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
+ "yangSource": "module ietf-yang-types-6 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-6\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-20 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
},
{
"moduleName": "ietf-yang-types-7",
"revision": "2013-07-21",
- "yangSource": "module ietf-yang-types-7 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-7\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
+ "yangSource": "module ietf-yang-types-7 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-7\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-21 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
},
{
"moduleName": "ietf-yang-types-8",
"revision": "2013-07-22",
- "yangSource": "module ietf-yang-types-8 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-8\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
+ "yangSource": "module ietf-yang-types-8 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-8\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-22 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
},
{
"moduleName": "ietf-yang-types-9",
"revision": "2013-07-23",
- "yangSource": "module ietf-yang-types-9 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-9\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
+ "yangSource": "module ietf-yang-types-9 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-9\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-23 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
},
{
"moduleName": "ietf-yang-types-10",
"revision": "2013-07-24",
- "yangSource": "module ietf-yang-types-10 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-10\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-15 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
+ "yangSource": "module ietf-yang-types-10 {\n\n namespace \"urn:ietf:params:xml:ns:yang:ietf-yang-types-10\";\n prefix \"yang\";\n\n organization\n \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n contact\n \"WG Web: <http://tools.ietf.org/wg/netmod/>\n WG List: <mailto:netmod@ietf.org>\n\n WG Chair: David Kessens\n <mailto:david.kessens@nsn.com>\n\n WG Chair: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\n\n Editor: Juergen Schoenwaelder\n <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n description\n \"This module contains a collection of generally useful derived\n YANG data types.\n\n Copyright (c) 2013 IETF Trust and the persons identified as\n authors of the code. All rights reserved.\n\n Redistribution and use in source and binary forms, with or\n without modification, is permitted pursuant to, and subject\n to the license terms contained in, the Simplified BSD License\n set forth in Section 4.c of the IETF Trust's Legal Provisions\n Relating to IETF Documents\n (http://trustee.ietf.org/license-info).\n\n This version of this YANG module is part of RFC 6991; see\n the RFC itself for full legal notices.\";\n\n revision 2013-07-24 {\n description\n \"This revision adds the following new data types:\n - yang-identifier\n - hex-string\n - uuid\n - dotted-quad\";\n reference\n \"RFC 6991: Common YANG Data Types\";\n }\n\n revision 2010-09-24 {\n description\n \"Initial revision.\";\n reference\n \"RFC 6021: Common YANG Data Types\";\n }\n\n /*** collection of counter and gauge types ***/\n\n typedef counter32 {\n type uint32;\n description\n \"The counter32 type represents a non-negative integer\n that monotonically increases until it reaches a\n maximum value of 2^32-1 (4294967295 decimal), when it\n wraps around and starts increasing again from zero.\n\n Counters have no defined 'initial' value, and thus, a\n single value of a counter has (in general) no information\n content. Discontinuities in the monotonically increasing\n value normally occur at re-initialization of the\n management system, and at other times as specified in the\n description of a schema node using this type. If such\n other times can occur, for example, the creation of\n a schema node of type counter32 at times other than\n re-initialization, then a corresponding schema node\n should be defined, with an appropriate type, to indicate\n the last discontinuity.\n\n The counter32 type should not be used for configuration\n schema nodes. A default statement SHOULD NOT be used in\n combination with the type counter32.\n\n In the value set and its semantics, this type is equivalent\n to the Counter32 type of the SMIv2.\";\n reference\n \"RFC 2578: Structure of Management Information Version 2\n (SMIv2)\";\n }\n}\n"
}
]
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
2. To send data-updated events to kafka,
* uncomment the `zookeeper` and `kafka` services.
* uncomment environment variables
- * `notification.data-updated.enabled: 'true'`
* `KAFKA_BOOTSTRAP_SERVER: kafka:9092`
- * `NOTIFICATION_DATASPACE_FILTER_PATTERNS: '.*'`
2. Execute following command from `docker-compose` folder:
Use one of the below version type that has been generated in the local system's docker image list after the build.
DMI_USERNAME: ${DMI_USERNAME:-cpsuser}
DMI_PASSWORD: ${DMI_PASSWORD:-cpsr0cks!}
KAFKA_BOOTSTRAP_SERVER: kafka:29092
- notification.data-updated.enabled: 'true'
- NOTIFICATION_DATASPACE_FILTER_PATTERNS: '.*'
restart: unless-stopped
profiles:
- dmi-service
summary: Replace list content
tags:
- cps-data
+ /v2/dataspaces/{dataspace-name}/anchors/{anchor-name}/delta:
+ get:
+ description: Get delta between two anchors within a given dataspace
+ operationId: getDeltaByDataspaceAndAnchors
+ parameters:
+ - description: dataspace-name
+ in: path
+ name: dataspace-name
+ required: true
+ schema:
+ example: my-dataspace
+ type: string
+ - description: anchor-name
+ in: path
+ name: anchor-name
+ required: true
+ schema:
+ example: my-anchor
+ type: string
+ - description: target-anchor-name
+ in: query
+ name: target-anchor-name
+ required: true
+ schema:
+ example: my-anchor
+ type: string
+ - description: "For more details on xpath, please refer https://docs.onap.org/projects/onap-cps/en/latest/xpath.html"
+ examples:
+ container xpath:
+ value: /shops/bookstore
+ list attributes xpath:
+ value: "/shops/bookstore/categories[@code=1]"
+ in: query
+ name: xpath
+ required: false
+ schema:
+ default: /
+ type: string
+ - description: "Number of descendants to query. Allowed values are 'none', 'all',\
+ \ 'direct', 1 (for direct), -1 (for all), 0 (for none) and any positive\
+ \ number."
+ in: query
+ name: descendants
+ required: false
+ schema:
+ default: none
+ example: "3"
+ type: string
+ responses:
+ "200":
+ content:
+ application/json:
+ examples:
+ dataSample:
+ $ref: '#/components/examples/deltaReportSample'
+ value: null
+ schema:
+ type: object
+ description: OK
+ "400":
+ content:
+ application/json:
+ example:
+ status: 400
+ message: Bad Request
+ details: The provided request is not valid
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Bad Request
+ "401":
+ content:
+ application/json:
+ example:
+ status: 401
+ message: Unauthorized request
+ details: This request is unauthorized
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Unauthorized
+ "403":
+ content:
+ application/json:
+ example:
+ status: 403
+ message: Request Forbidden
+ details: This request is forbidden
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Forbidden
+ "500":
+ content:
+ application/json:
+ example:
+ status: 500
+ message: Internal Server Error
+ details: Internal Server Error occurred
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ description: Internal Server Error
+ summary: Get delta between anchors in the same dataspace
+ tags:
+ - cps-data
+ x-codegen-request-body-name: xpath
/v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/nodes/query:
get:
deprecated: true
value: <stores xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <bookstore xmlns="org:onap:ccsdk:sample">
<bookstore-name>Chapters</bookstore-name> <categories> <code>1</code> <name>SciFi</name>
</categories> </bookstore> </stores>
+ deltaReportSample:
+ value:
+ - action: ADD
+ xpath: "/bookstore/categories/[@code=3]"
+ target-data:
+ code: "3,"
+ name: kidz
+ - action: REMOVE
+ xpath: "/bookstore/categories/[@code=1]"
+ source-data:
+ code: "1,"
+ name: Fiction
+ - action: UPDATE
+ xpath: "/bookstore/categories/[@code=2]"
+ source-data:
+ name: Funny
+ target-data:
+ name: Comic
dataSampleAcrossAnchors:
value:
- anchorName: bookstore1
required: true
schema:
type: string
+ targetAnchorNameInQuery:
+ description: target-anchor-name
+ in: query
+ name: target-anchor-name
+ required: true
+ schema:
+ example: my-anchor
+ type: string
cpsPathInQuery:
description: "For more details on cps path, please refer https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html"
examples:
example:
updatedCmHandles:
- cmHandle: my-cm-handle
+ alternateId: my-alternate-id
publicCmHandleProperties:
key: my-property
cmHandleProperties:
moduleSetTag: my-module-set-tag
trustLevel: COMPLETE
- cmHandle: my-cm-handle
+ alternateId: my-alternate-id
publicCmHandleProperties:
key: my-property
cmHandleProperties:
trustLevel: COMPLETE
createdCmHandles:
- cmHandle: my-cm-handle
+ alternateId: my-alternate-id
publicCmHandleProperties:
key: my-property
cmHandleProperties:
moduleSetTag: my-module-set-tag
trustLevel: COMPLETE
- cmHandle: my-cm-handle
+ alternateId: my-alternate-id
publicCmHandleProperties:
key: my-property
cmHandleProperties:
RestInputCmHandle:
example:
cmHandle: my-cm-handle
+ alternateId: my-alternate-id
publicCmHandleProperties:
key: my-property
cmHandleProperties:
- NONE
example: COMPLETE
type: string
+ alternateId:
+ example: my-alternate-id
+ type: string
required:
- cmHandle
type: object
type: string
type: array
moduleSetTag:
+ default: ""
example: my-module-set-tag
type: string
required:
reason: LOCKED_MISBEHAVING
details: locked due to failure in module sync
lastUpdateTime: 2022-12-31T20:30:40.000+0000
+ trustLevel: COMPLETE
properties:
cmHandle:
example: my-cm-handle1
type: array
state:
$ref: '#/components/schemas/CmHandleCompositeState'
+ trustLevel:
+ description: Current trust level of the relevant CM handle ID.
+ example: COMPLETE
+ type: string
title: CM handle Details
type: object
CmHandlePublicProperties:
example: 2022-12-31T20:30:40.000+0000
type: string
type: object
+ CmHandleTrustLevel:
+ description: Current trust level of the relevant CM handle ID.
+ example: COMPLETE
+ type: string
RestOutputCmHandlePublicProperties:
example:
publicCmHandleProperties:
Any spring supported property can be configured by providing in ``config.additional.<spring-supported-property-name>: value`` Example: config.additional.spring.datasource.hikari.maximumPoolSize: 30
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| Property | Description | Default Value |
-+=======================================+=========================================================================================================+===============================+
-| config.appUserName | User name used by cps-core service to configure the authentication for REST API it exposes. | ``cpsuser`` |
-| | | |
-| | This is the user name to be used by cps-core REST clients to authenticate themselves. | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.appUserPassword | Password used by cps-core service to configure the authentication for REST API it exposes. | Not defined |
-| | | |
-| | If not defined, the password is generated when deploying the application. | |
-| | | |
-| | See also :ref:`cps_common_credentials_retrieval`. | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| postgres.config.pgUserName | Internal user name used by cps-core to connect to its own database. | ``cps`` |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| postgres.config.pgUserPassword | Internal password used by cps-core to connect to its own database. | Not defined |
-| | | |
-| | If not defined, the password is generated when deploying the application. | |
-| | | |
-| | See also :ref:`cps_common_credentials_retrieval`. | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| postgres.config.pgDatabase | Database name used by cps-core | ``cpsdb`` |
-| | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| logging.level | Logging level set in cps-core | info |
-| | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.useStrimziKafka | If targeting a custom kafka cluster, i.e. useStrimziKafka: false, the | true |
-| | config.eventPublisher.spring.kafka values below must be set. | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.eventPublisher. | Kafka hostname and port | ``<kafka-bootstrap>:9092`` |
-| spring.kafka.bootstrap-servers | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.eventPublisher. | Kafka consumer client id | ``cps-core`` |
-| spring.kafka.consumer.client-id | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.eventPublisher. | Kafka security protocol. | ``SASL_PLAINTEXT`` |
-| spring.kafka.security.protocol | Some possible values are: | |
-| | | |
-| | * ``PLAINTEXT`` | |
-| | * ``SASL_PLAINTEXT``, for authentication | |
-| | * ``SASL_SSL``, for authentication and encryption | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.eventPublisher. | Kafka security SASL mechanism. Required for SASL_PLAINTEXT and SASL_SSL protocols. | Not defined |
-| spring.kafka.properties. | Some possible values are: | |
-| sasl.mechanism | | |
-| | * ``PLAIN``, for PLAINTEXT | |
-| | * ``SCRAM-SHA-512``, for SSL | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.eventPublisher. | Kafka security SASL JAAS configuration. Required for SASL_PLAINTEXT and SASL_SSL protocols. | Not defined |
-| spring.kafka.properties. | Some possible values are: | |
-| sasl.jaas.config | | |
-| | * ``org.apache.kafka.common.security.plain.PlainLoginModule required username="..." password="...";``, | |
-| | for PLAINTEXT | |
-| | * ``org.apache.kafka.common.security.scram.ScramLoginModule required username="..." password="...";``, | |
-| | for SSL | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.eventPublisher. | Kafka security SASL SSL store type. Required for SASL_SSL protocol. | Not defined |
-| spring.kafka.ssl.trust-store-type | Some possible values are: | |
-| | | |
-| | * ``JKS`` | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.eventPublisher. | Kafka security SASL SSL store file location. Required for SASL_SSL protocol. | Not defined |
-| spring.kafka.ssl.trust-store-location | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.eventPublisher. | Kafka security SASL SSL store password. Required for SASL_SSL protocol. | Not defined |
-| spring.kafka.ssl.trust-store-password | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.eventPublisher. | Kafka security SASL SSL broker hostname identification verification. Required for SASL_SSL protocol. | Not defined |
-| spring.kafka.properties. | Possible value is: | |
-| ssl.endpoint.identification.algorithm | | |
-| | * ``""``, empty string to disable | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.additional. | If asynchronous messaging, user notifications, and updated event persistence should be enabled | ``true`` |
-| notification.data-updated.enabled | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.additional. | Core pool size in asynchronous execution of notification. | ``2`` |
-| notification.async.executor. | | |
-| core-pool-size | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.additional. | Max pool size in asynchronous execution of notification. | ``1`` |
-| notification.async.executor. | | |
-| max-pool-size | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.additional. | Queue Capacity in asynchronous execution of notification. | ``500`` |
-| notification.async.executor. | | |
-| queue-capacity | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.additional. | If the executor should wait for the tasks to be completed on shutdown | ``true`` |
-| notification.async.executor. | | |
-| wait-for-tasks-to-complete-on-shutdown| | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.additional. | Prefix to be added to the thread name in asynchronous execution of notifications. | ``Async-`` |
-| notification.async.executor. | | |
-| thread-name-prefix | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.additional. | Specifies number of database connections between database and application. | ``10`` |
-| spring.datasource.hikari. | This property controls the maximum size that the pool is allowed to reach, | |
-| maximumPoolSize | including both idle and in-use connections. | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| Property | Description | Default Value |
++===========================================+=========================================================================================================+===============================+
+| config.appUserName | User name used by cps-core service to configure the authentication for REST API it exposes. | ``cpsuser`` |
+| | | |
+| | This is the user name to be used by cps-core REST clients to authenticate themselves. | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.appUserPassword | Password used by cps-core service to configure the authentication for REST API it exposes. | Not defined |
+| | | |
+| | If not defined, the password is generated when deploying the application. | |
+| | | |
+| | See also :ref:`cps_common_credentials_retrieval`. | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| postgres.config.pgUserName | Internal user name used by cps-core to connect to its own database. | ``cps`` |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| postgres.config.pgUserPassword | Internal password used by cps-core to connect to its own database. | Not defined |
+| | | |
+| | If not defined, the password is generated when deploying the application. | |
+| | | |
+| | See also :ref:`cps_common_credentials_retrieval`. | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| postgres.config.pgDatabase | Database name used by cps-core | ``cpsdb`` |
+| | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| logging.level | Logging level set in cps-core | info |
+| | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.useStrimziKafka | If targeting a custom kafka cluster, i.e. useStrimziKafka: false, the | true |
+| | config.eventPublisher.spring.kafka values below must be set. | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.eventPublisher. | Kafka hostname and port | ``<kafka-bootstrap>:9092`` |
+| spring.kafka.bootstrap-servers | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.eventPublisher. | Kafka consumer client id | ``cps-core`` |
+| spring.kafka.consumer.client-id | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.eventPublisher. | Kafka security protocol. | ``SASL_PLAINTEXT`` |
+| spring.kafka.security.protocol | Some possible values are: | |
+| | | |
+| | * ``PLAINTEXT`` | |
+| | * ``SASL_PLAINTEXT``, for authentication | |
+| | * ``SASL_SSL``, for authentication and encryption | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.eventPublisher. | Kafka security SASL mechanism. Required for SASL_PLAINTEXT and SASL_SSL protocols. | Not defined |
+| spring.kafka.properties. | Some possible values are: | |
+| sasl.mechanism | | |
+| | * ``PLAIN``, for PLAINTEXT | |
+| | * ``SCRAM-SHA-512``, for SSL | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.eventPublisher. | Kafka security SASL JAAS configuration. Required for SASL_PLAINTEXT and SASL_SSL protocols. | Not defined |
+| spring.kafka.properties. | Some possible values are: | |
+| sasl.jaas.config | | |
+| | * ``org.apache.kafka.common.security.plain.PlainLoginModule required username="..." password="...";``, | |
+| | for PLAINTEXT | |
+| | * ``org.apache.kafka.common.security.scram.ScramLoginModule required username="..." password="...";``, | |
+| | for SSL | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.eventPublisher. | Kafka security SASL SSL store type. Required for SASL_SSL protocol. | Not defined |
+| spring.kafka.ssl.trust-store-type | Some possible values are: | |
+| | | |
+| | * ``JKS`` | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.eventPublisher. | Kafka security SASL SSL store file location. Required for SASL_SSL protocol. | Not defined |
+| spring.kafka.ssl.trust-store-location | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.eventPublisher. | Kafka security SASL SSL store password. Required for SASL_SSL protocol. | Not defined |
+| spring.kafka.ssl.trust-store-password | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.eventPublisher. | Kafka security SASL SSL broker hostname identification verification. Required for SASL_SSL protocol. | Not defined |
+| spring.kafka.properties. | Possible value is: | |
+| ssl.endpoint.identification.algorithm | | |
+| | * ``""``, empty string to disable | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional. | Core pool size in asynchronous execution of notification. | ``2`` |
+| notification.async.executor. | | |
+| core-pool-size | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional. | Max pool size in asynchronous execution of notification. | ``1`` |
+| notification.async.executor. | | |
+| max-pool-size | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional. | Queue Capacity in asynchronous execution of notification. | ``500`` |
+| notification.async.executor. | | |
+| queue-capacity | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional. | If the executor should wait for the tasks to be completed on shutdown | ``true`` |
+| notification.async.executor. | | |
+| wait-for-tasks-to-complete-on-shutdown | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional. | Prefix to be added to the thread name in asynchronous execution of notifications. | ``Async-`` |
+| notification.async.executor. | | |
+| thread-name-prefix | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional. | Specifies number of database connections between database and application. | ``10`` |
+| spring.datasource.hikari. | This property controls the maximum size that the pool is allowed to reach, | |
+| maximumPoolSize | including both idle and in-use connections. | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
.. _additional-cps-ncmp-customizations:
Additional CPS-NCMP Customizations
==================================
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.dmiPluginUserName | User name used by cps-core to authenticate themselves for using ncmp-dmi-plugin service. | ``dmiuser`` |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.dmiPluginUserPassword | Internal password used by cps-core to connect to ncmp-dmi-plugin service. | Not defined |
-| | | |
-| | If not defined, the password is generated when deploying the application. | |
-| | | |
-| | See also :ref:`cps_common_credentials_retrieval`. | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.ncmp.timers | Specifies the delay in milliseconds in which the module sync watch dog will wake again after finishing. | ``30000`` |
-| .advised-modules-sync.sleep-time-ms | | |
-| | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.ncmp.timers | Specifies the delay in milliseconds in which the retry mechanism watch dog | |
-| .locked-modules-sync.sleep-time-ms | will wake again after finishing. | ``300000`` |
-| | | |
-| | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.ncmp.timers | Specifies the delay in milliseconds in which the data sync watch dog will wake again after finishing. | ``30000`` |
-| .cm-handle-data-sync.sleep-time-ms | | |
-| | | |
-+---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.dmiPluginUserName | User name used by cps-core to authenticate themselves for using ncmp-dmi-plugin service. | ``dmiuser`` |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.dmiPluginUserPassword | Internal password used by cps-core to connect to ncmp-dmi-plugin service. | Not defined |
+| | | |
+| | If not defined, the password is generated when deploying the application. | |
+| | | |
+| | See also :ref:`cps_common_credentials_retrieval`. | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.ncmp.timers | Specifies the delay in milliseconds in which the module sync watch dog will wake again after finishing. | ``30000`` |
+| .advised-modules-sync.sleep-time-ms | | |
+| | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.ncmp.timers | Specifies the delay in milliseconds in which the retry mechanism watch dog | |
+| .locked-modules-sync.sleep-time-ms | will wake again after finishing. | ``300000`` |
+| | | |
+| | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.ncmp.timers | Specifies the delay in milliseconds in which the data sync watch dog will wake again after finishing. | ``30000`` |
+| .cm-handle-data-sync.sleep-time-ms | | |
+| | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional.ncmp.dmi.httpclient | Specifies the maximum time in seconds, to wait for establishing a connection for the HTTP Client. | ``180`` |
+| .connectionTimeoutInSeconds | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional.ncmp.dmi.httpclient | Specifies the maximum number of connections allowed per route in the HTTP client. | ``50`` |
+| .maximumConnectionsPerRoute | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional.ncmp.dmi.httpclient | Specifies the maximum total number of connections that can be held by the HTTP client. | ``100`` |
+| .maximumConnectionsTotal | | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
+| config.additional.ncmp.dmi.httpclient | Specifies the duration in seconds for the threshold, after which idle connections will be evicted | ``5`` |
+| .idleConnectionEvictionThresholdInSeconds | from the connection pool by the HTTP client. | |
++-------------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
CPS-Core Docker Installation
============================
.. * * * NEW DELHI * * *
.. =========================
+Version: 3.4.2
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project** | |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images** | onap/cps-and-ncmp:3.4.2 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation** | 3.4.2 New Delhi |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release date** | Not yet released |
+| | |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.4.2
+
+
+Features
+--------
+
+
Version: 3.4.1
==============
| **Release designation** | 3.4.1 New Delhi |
| | |
+--------------------------------------+--------------------------------------------------------+
-| **Release date** | Not yet released |
+| **Release date** | 2023 December 20 |
| | |
+--------------------------------------+--------------------------------------------------------+
Bug Fixes
---------
+3.4.1
- `CPS-1979 <https://jira.onap.org/browse/CPS-1979>`_ Bug fix for Invalid topic name suffix.
Features
--------
- CPS-Temporal is no longer supported and any related documentation has been removed.
+ - `CPS-1733 <https://jira.onap.org/browse/CPS-1733>`_ Upgrade YANG schema-set for CM handle without removing and adding it.
- `CPS-1980 <https://jira.onap.org/browse/CPS-1980>`_ Exposing health and cluster metrics for hazelcast.
+ - `CPS-1994 <https://jira.onap.org/browse/CPS-1994>`_ Use Apache Http Client for DMI REST requests.
+ - `CPS-2005 <https://jira.onap.org/browse/CPS-2005>`_ Removing notification feature for cps updated events ( exclusively used by cps-temporal )
+
+Known Issues
+------------
+ - `CPS-2000 <https://jira.onap.org/browse/CPS-2000>`_ Schema object cache is not distributed.
Version: 3.4.0
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
package org.onap.cps.integration.base
+import org.onap.cps.api.CpsAnchorService
+import org.onap.cps.api.CpsDataService
+import org.onap.cps.api.CpsDataspaceService
+import org.onap.cps.api.CpsModuleService
import org.onap.cps.api.CpsQueryService
-import org.onap.cps.api.impl.CpsAdminServiceImpl
-import org.onap.cps.api.impl.CpsDataServiceImpl
-import org.onap.cps.api.impl.CpsModuleServiceImpl
import org.onap.cps.integration.DatabaseTestContainer
import org.onap.cps.spi.config.CpsSessionFactory
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
import java.time.OffsetDateTime
-@SpringBootTest(classes = [TestConfig, CpsAdminServiceImpl, CpsValidatorImpl, SessionManager, CpsSessionFactory])
+@SpringBootTest(classes = [TestConfig, CpsValidatorImpl, SessionManager, CpsSessionFactory])
@Testcontainers
@EnableAutoConfiguration
@EnableJpaRepositories(basePackageClasses = [DataspaceRepository])
@Autowired
@Lazy
- CpsAdminServiceImpl cpsAdminService
+ CpsDataspaceService cpsDataspaceService
@Autowired
@Lazy
- CpsDataServiceImpl cpsDataService
+ CpsAnchorService cpsAnchorService
@Autowired
@Lazy
- CpsModuleServiceImpl cpsModuleService
+ CpsDataService cpsDataService
+
+ @Autowired
+ @Lazy
+ CpsModuleService cpsModuleService
@Autowired
@Lazy
def setup() {
if (!initialized) {
- cpsAdminService.createDataspace(GENERAL_TEST_DATASPACE)
+ cpsDataspaceService.createDataspace(GENERAL_TEST_DATASPACE)
def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
cpsModuleService.createSchemaSet(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, [bookstore : bookstoreModelFileContent])
initialized = true;
def dataspaceExists(dataspaceName) {
try {
- cpsAdminService.getDataspace(dataspaceName)
+ cpsDataspaceService.getDataspace(dataspaceName)
} catch (DataspaceNotFoundException dataspaceNotFoundException) {
return false
}
def addAnchorsWithData(numberOfAnchors, dataspaceName, schemaSetName, anchorNamePrefix, data) {
(1..numberOfAnchors).each {
- cpsAdminService.createAnchor(dataspaceName, schemaSetName, anchorNamePrefix + it)
+ cpsAnchorService.createAnchor(dataspaceName, schemaSetName, anchorNamePrefix + it)
cpsDataService.saveData(dataspaceName, anchorNamePrefix + it, data.replace("Easons", "Easons-"+it.toString()), OffsetDateTime.now())
}
}
}
def setupBookstoreInfraStructure() {
- cpsAdminService.createDataspace(FUNCTIONAL_TEST_DATASPACE_1)
- cpsAdminService.createDataspace(FUNCTIONAL_TEST_DATASPACE_2)
- cpsAdminService.createDataspace(FUNCTIONAL_TEST_DATASPACE_3)
+ cpsDataspaceService.createDataspace(FUNCTIONAL_TEST_DATASPACE_1)
+ cpsDataspaceService.createDataspace(FUNCTIONAL_TEST_DATASPACE_2)
+ cpsDataspaceService.createDataspace(FUNCTIONAL_TEST_DATASPACE_3)
def bookstoreYangModelAsString = readResourceDataFile('bookstore/bookstore.yang')
cpsModuleService.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, [bookstore: bookstoreYangModelAsString])
cpsModuleService.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_2, BOOKSTORE_SCHEMA_SET, [bookstore: bookstoreYangModelAsString])
def restoreBookstoreDataAnchor(anchorNumber) {
def anchorName = 'bookstoreAnchor' + anchorNumber
- cpsAdminService.deleteAnchor(FUNCTIONAL_TEST_DATASPACE_1, anchorName)
- cpsAdminService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, anchorName)
+ cpsAnchorService.deleteAnchor(FUNCTIONAL_TEST_DATASPACE_1, anchorName)
+ cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, anchorName)
cpsDataService.saveData(FUNCTIONAL_TEST_DATASPACE_1, anchorName, bookstoreJsonData.replace("Easons", "Easons-"+anchorNumber.toString()), OffsetDateTime.now())
}
package org.onap.cps.integration.base
import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.notification.NotificationService
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.impl.CpsAdminPersistenceServiceImpl
return new JsonObjectMapper(new ObjectMapper())
}
- @Bean
- NotificationService notificationService() {
- return Stub(NotificationService)
- }
-
@Bean
TimedYangParser timedYangParser() {
return new TimedYangParser()
package org.onap.cps.integration.functional
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.integration.base.CpsIntegrationSpecBase
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.onap.cps.spi.exceptions.AnchorNotFoundException
-import org.onap.cps.spi.exceptions.DataspaceInUseException
-import org.onap.cps.spi.exceptions.DataspaceNotFoundException
-import java.time.OffsetDateTime
-
-class CpsAdminServiceIntegrationSpec extends CpsIntegrationSpecBase {
-
- CpsAdminService objectUnderTest
- def setup() { objectUnderTest = cpsAdminService }
-
- def 'Dataspace CRUD operations.'() {
- when: 'a dataspace is created'
- objectUnderTest.createDataspace('newDataspace')
- then: 'the dataspace can be read'
- assert objectUnderTest.getDataspace('newDataspace').name == 'newDataspace'
- and: 'it can be deleted'
- objectUnderTest.deleteDataspace('newDataspace')
- then: 'the dataspace no longer exists i.e. an exception is thrown if an attempt is made to retrieve it'
- def thrown = null
- try {
- objectUnderTest.getDataspace('newDataspace')
- } catch(Exception exception) {
- thrown = exception
- }
- assert thrown instanceof DataspaceNotFoundException
- }
+import java.time.OffsetDateTime
- def 'Delete dataspace with error; #scenario.'() {
- setup: 'add some anchors if needed'
- numberOfAnchors.times {
- objectUnderTest.createAnchor(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'anchor' + it)
- }
- when: 'attempt to delete dataspace'
- objectUnderTest.deleteDataspace(dataspaceName)
- then: 'the correct exception is thrown with the relevant details'
- def thrownException = thrown(expectedException)
- thrownException.details.contains(expectedMessageDetails)
- cleanup:
- numberOfAnchors.times {
- objectUnderTest.deleteAnchor(GENERAL_TEST_DATASPACE, 'anchor' + it)
- }
- where: 'the following data is used'
- scenario | dataspaceName | numberOfAnchors || expectedException | expectedMessageDetails
- 'dataspace name does not exist' | 'unknown' | 0 || DataspaceNotFoundException | 'unknown does not exist'
- 'dataspace contains schemasets' | GENERAL_TEST_DATASPACE | 0 || DataspaceInUseException | 'contains 1 schemaset(s)'
- 'dataspace contains anchors' | GENERAL_TEST_DATASPACE | 2 || DataspaceInUseException | 'contains 2 anchor(s)'
- }
+class CpsAnchorServiceIntegrationSpec extends CpsIntegrationSpecBase {
- def 'Retrieve all dataspaces (depends on total test suite).'() {
- given: 'two addtional dataspaces are created'
- objectUnderTest.createDataspace('dataspace1')
- objectUnderTest.createDataspace('dataspace2')
- when: 'all datespaces are retreived'
- def result = objectUnderTest.getAllDataspaces()
- then: 'there are at least 3 dataspaces (2 new ones plus the general test dataspace)'
- result.size() >= 3
- assert result.name.containsAll([GENERAL_TEST_DATASPACE, 'dataspace1', 'dataspace2'])
- }
+ CpsAnchorService objectUnderTest
- def 'Duplicate dataspaces.'() {
- when: 'attempting to create a dataspace with the same name as an existing one'
- objectUnderTest.createDataspace(GENERAL_TEST_DATASPACE)
- then: 'an exception is thrown indicating the dataspace already exists'
- thrown(AlreadyDefinedException)
- }
+ def setup() { objectUnderTest = cpsAnchorService }
def 'Anchor CRUD operations.'() {
when: 'an anchor is created'
then: 'an empty result is returned (no error)'
assert result == []
where:
- scenario | dataspaceName
- 'non existing database' | 'nonExistingDataspace'
- 'just unknown module(s)' | GENERAL_TEST_DATASPACE
+ scenario | dataspaceName
+ 'non existing database' | 'nonExistingDataspace'
+ 'just unknown module(s)' | GENERAL_TEST_DATASPACE
}
def 'Update anchor schema set.'() {
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
import org.onap.cps.spi.model.DeltaReport
-import java.time.OffsetDateTime
-
import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
def 'Attempt to create a top level data node using root.'() {
given: 'a new anchor'
- cpsAdminService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, 'newAnchor1');
+ cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, 'newAnchor1');
when: 'attempt to save new top level datanode'
def json = '{"bookstore": {"bookstore-name": "New Store"} }'
objectUnderTest.saveData(FUNCTIONAL_TEST_DATASPACE_1, 'newAnchor1' , '/', json, now)
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional
+
+import org.onap.cps.api.CpsDataspaceService
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.spi.exceptions.AlreadyDefinedException
+import org.onap.cps.spi.exceptions.DataspaceInUseException
+import org.onap.cps.spi.exceptions.DataspaceNotFoundException
+
+class CpsDataspaceServiceIntegrationSpec extends CpsIntegrationSpecBase {
+
+ CpsDataspaceService objectUnderTest
+
+ def setup() { objectUnderTest = cpsDataspaceService }
+
+ def 'Dataspace CRUD operations.'() {
+ when: 'a dataspace is created'
+ objectUnderTest.createDataspace('newDataspace')
+ then: 'the dataspace can be read'
+ assert objectUnderTest.getDataspace('newDataspace').name == 'newDataspace'
+ and: 'it can be deleted'
+ objectUnderTest.deleteDataspace('newDataspace')
+ then: 'the dataspace no longer exists i.e. an exception is thrown if an attempt is made to retrieve it'
+ def thrown = null
+ try {
+ objectUnderTest.getDataspace('newDataspace')
+ } catch(Exception exception) {
+ thrown = exception
+ }
+ assert thrown instanceof DataspaceNotFoundException
+ }
+
+ def 'Attempt to delete a non-existing dataspace'() {
+ when: 'attempt to delete a non-existing dataspace'
+ objectUnderTest.deleteDataspace('non-existing-name')
+ then: 'a not found exception is thrown with the relevant dataspace name'
+ def thrownException = thrown(DataspaceNotFoundException)
+ assert thrownException.details.contains('non-existing-name does not exist')
+ }
+
+ def 'Attempt Delete dataspace with a schema set and anchor'() {
+ setup: 'a dataspace with a schema set and anchor'
+ objectUnderTest.createDataspace('targetDataspace')
+ cpsModuleService.createSchemaSet('targetDataspace','someSchemaSet',[:])
+ cpsAnchorService.createAnchor('targetDataspace', 'someSchemaSet', 'some_anchor')
+ when: 'attempt to delete dataspace'
+ objectUnderTest.deleteDataspace('targetDataspace')
+ then: 'an in-use exception is thrown mentioning anchors'
+ def thrownException = thrown(DataspaceInUseException)
+ assert thrownException.details.contains('contains 1 anchor(s)')
+ cleanup:
+ cpsModuleService.deleteSchemaSetsWithCascade('targetDataspace',['someSchemaSet'])
+ objectUnderTest.deleteDataspace('targetDataspace')
+ }
+
+ def 'Attempt to delete dataspace with just a schema set'() {
+ setup: 'a dataspace with a schema set'
+ objectUnderTest.createDataspace('targetDataspace')
+ cpsModuleService.createSchemaSet('targetDataspace','someSchemaSet',[:])
+ when: 'attempt to delete dataspace'
+ objectUnderTest.deleteDataspace('targetDataspace')
+ then: 'an in-use exception is thrown mentioning schemasets'
+ def thrownException = thrown(DataspaceInUseException)
+ assert thrownException.details.contains('contains 1 schemaset(s)')
+ cleanup:
+ cpsModuleService.deleteSchemaSetsWithCascade('targetDataspace',['someSchemaSet'])
+ objectUnderTest.deleteDataspace('targetDataspace')
+ }
+
+ def 'Retrieve all dataspaces (depends on total test suite).'() {
+ given: 'two addtional dataspaces are created'
+ objectUnderTest.createDataspace('dataspace1')
+ objectUnderTest.createDataspace('dataspace2')
+ when: 'all datespaces are retreived'
+ def result = objectUnderTest.getAllDataspaces()
+ then: 'there are at least 3 dataspaces (2 new ones plus the general test dataspace)'
+ result.size() >= 3
+ assert result.name.containsAll([GENERAL_TEST_DATASPACE, 'dataspace1', 'dataspace2'])
+ }
+
+ def 'Duplicate dataspaces.'() {
+ when: 'attempting to create a dataspace with the same name as an existing one'
+ objectUnderTest.createDataspace(GENERAL_TEST_DATASPACE)
+ then: 'an exception is thrown indicating the dataspace already exists'
+ thrown(AlreadyDefinedException)
+ }
+
+}
package org.onap.cps.integration.functional
+import org.onap.cps.api.CpsModuleService
import org.onap.cps.api.impl.CpsModuleServiceImpl
import org.onap.cps.integration.base.FunctionalSpecBase
import org.onap.cps.spi.CascadeDeleteAllowed
class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
- CpsModuleServiceImpl objectUnderTest
+ CpsModuleService objectUnderTest
private static def originalNumberOfModuleReferences = 1
private static def existingModuleReference = new ModuleReference('stores','2020-09-15')
def newYangResourcesNameToContentMap = [:]
def moduleReferences = []
+ def noNewModules = [:]
def setup() {
objectUnderTest = cpsModuleService
moduleReferences.addAll(existingModuleReferences)
when: 'the new schema set is created'
def schemaSetName = "NewSchemaWith${numberOfNewModules}Modules"
- objectUnderTest.createOrUpgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences)
+ objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences)
and: 'associated with a new anchor'
- cpsAdminService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, 'newAnchor')
+ cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, 'newAnchor')
then: 'the new anchor has the correct number of modules'
def yangResourceModuleReferences = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'newAnchor')
assert expectedNumberOfModulesForAnchor == yangResourceModuleReferences.size()
objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', newYangResourcesNameToContentMap)
and: 'optionally create anchor for the schema set'
if (associateWithAnchor) {
- cpsAdminService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', 'newAnchor')
+ cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', 'newAnchor')
}
when: 'attempt to delete the schema set'
try {
'schema set does not exists' | FUNCTIONAL_TEST_DATASPACE_1 | 'unknown' || SchemaSetNotFoundException
}
+ /*
+ U P G R A D E
+ */
+
+ def 'Upgrade schema set (with existing and new modules, no matching module set tag in NCMP)'() {
+ given: 'an anchor and schema set with 2 modules (to be upgraded)'
+ populateNewYangResourcesNameToContentMapAndAllModuleReferences('original', 2)
+ objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, [])
+ cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', 'targetAnchor')
+ def yangResourceModuleReferencesBeforeUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
+ assert yangResourceModuleReferencesBeforeUpgrade.size() == 2
+ assert yangResourceModuleReferencesBeforeUpgrade.containsAll([new ModuleReference('original_0','2000-01-01'),new ModuleReference('original_1','2001-01-01')])
+ and: 'two new 2 modules (from node)'
+ populateNewYangResourcesNameToContentMapAndAllModuleReferences('new', 2)
+ def newModuleReferences = [new ModuleReference('new_0','2000-01-01'),new ModuleReference('new_1','2001-01-01')]
+ and: 'a list of all module references (normally retrieved from node)'
+ def allModuleReferences = []
+ allModuleReferences.add(existingModuleReference)
+ allModuleReferences.addAll(newModuleReferences)
+ when: 'the schema set is upgraded'
+ objectUnderTest.upgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, allModuleReferences)
+ then: 'the new anchor has the correct new and existing modules'
+ def yangResourceModuleReferencesAfterUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
+ assert yangResourceModuleReferencesAfterUpgrade.size() == 3
+ assert yangResourceModuleReferencesAfterUpgrade.contains(existingModuleReference)
+ assert yangResourceModuleReferencesAfterUpgrade.containsAll(newModuleReferences);
+ cleanup:
+ objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['targetSchema'])
+ }
+
+ def 'Upgrade existing schema set from another anchor (used in NCMP for matching module set tag)'() {
+ given: 'an anchor and schema set with 1 module (target)'
+ populateNewYangResourcesNameToContentMapAndAllModuleReferences('target', 1)
+ objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, [])
+ cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', 'targetAnchor')
+ def moduleReferencesBeforeUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
+ assert moduleReferencesBeforeUpgrade.size() == 1
+ and: 'another anchor and schema set with 2 other modules (source for upgrade)'
+ populateNewYangResourcesNameToContentMapAndAllModuleReferences('source', 2)
+ objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'sourceSchema', newYangResourcesNameToContentMap, [])
+ cpsAnchorService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, 'sourceSchema', 'sourceAnchor')
+ assert objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'sourceAnchor').size() == 2
+ when: 'the target schema is upgraded using the module references from the source anchor'
+ def moduleReferencesFromSourceAnchor = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'sourceAnchor')
+ objectUnderTest.upgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', noNewModules, moduleReferencesFromSourceAnchor)
+ then: 'the target schema now refers to the source modules (with namespace) modules'
+ def schemaSetModuleReferencesAfterUpgrade = getObjectUnderTest().getSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema').moduleReferences
+ assert schemaSetModuleReferencesAfterUpgrade.containsAll([new ModuleReference('source_0','2000-01-01','org:onap:ccsdk:sample'),new ModuleReference('source_1','2001-01-01','org:onap:ccsdk:sample')]);
+ and: 'the associated target anchor has the same module references (without namespace but that is a legacy issue)'
+ def anchorModuleReferencesAfterUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
+ assert anchorModuleReferencesAfterUpgrade.containsAll([new ModuleReference('source_0','2000-01-01'),new ModuleReference('source_1','2001-01-01')]);
+ cleanup:
+ objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['sourceSchema', 'targetSchema'])
+ }
+
/*
H E L P E R M E T H O D S
*/
def populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfModules) {
+ populateNewYangResourcesNameToContentMapAndAllModuleReferences('name', numberOfModules)
+ }
+
+ def populateNewYangResourcesNameToContentMapAndAllModuleReferences(namePrefix, numberOfModules) {
numberOfModules.times {
- def uniqueName = 'name_' + it
+ def uniqueName = namePrefix + '_' + it
def uniqueRevision = String.valueOf(2000 + it) + '-01-01'
moduleReferences.add(new ModuleReference(uniqueName, uniqueRevision))
- def uniqueContent = NEW_RESOURCE_CONTENT.replace(NEW_RESOURCE_REVISION, uniqueRevision)
+ def uniqueContent = NEW_RESOURCE_CONTENT.replace(NEW_RESOURCE_REVISION, uniqueRevision).replace('module test_module', 'module '+uniqueName)
newYangResourcesNameToContentMap.put(uniqueRevision, uniqueContent)
}
}
}
def setupPerformanceInfraStructure() {
- cpsAdminService.createDataspace(CPS_PERFORMANCE_TEST_DATASPACE)
+ cpsDataspaceService.createDataspace(CPS_PERFORMANCE_TEST_DATASPACE)
def modelAsString = readResourceDataFile('bookstore/bookstore.yang')
cpsModuleService.createSchemaSet(CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, [bookstore: modelAsString])
}
}
def setupPerformanceInfraStructure() {
- cpsAdminService.createDataspace(NCMP_PERFORMANCE_TEST_DATASPACE)
+ cpsDataspaceService.createDataspace(NCMP_PERFORMANCE_TEST_DATASPACE)
createRegistrySchemaSet()
createCmDataSubscriptionsSchemaSet()
addCmSubscriptionData()
}
def createInitialData() {
- cpsAdminService.createAnchor(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_SCHEMA_SET, REGISTRY_ANCHOR)
+ cpsAnchorService.createAnchor(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_SCHEMA_SET, REGISTRY_ANCHOR)
def data = readResourceDataFile('ncmp-registry/1000-cmhandles.json')
cpsDataService.saveData(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, data, OffsetDateTime.now())
}
def createRegistrySchemaSet() {
- def modelAsString = readResourceDataFile('ncmp-registry/dmi-registry@2022-05-10.yang')
+ def modelAsString = readResourceDataFile('ncmp-registry/dmi-registry@2023-11-27.yang')
cpsModuleService.createSchemaSet(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_SCHEMA_SET, [registry: modelAsString])
}
}
def addCmSubscriptionData() {
- cpsAdminService.createAnchor(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_SCHEMA_SET, CM_DATA_SUBSCRIPTIONS_ANCHOR)
+ cpsAnchorService.createAnchor(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_SCHEMA_SET, CM_DATA_SUBSCRIPTIONS_ANCHOR)
cpsDataService.saveData(NCMP_PERFORMANCE_TEST_DATASPACE, CM_DATA_SUBSCRIPTIONS_ANCHOR, datastore1cmHandlePlaceHolder, now)
def subscribers = createLeafList('subscribers',numberOfCmDataSubscribers, subscriberIdPrefix)
def filters = '"filters":' + createJsonArray('filter',numberOfFiltersPerCmHandle,'xpath',xpathPrefix,subscribers)
def 'Create 33,000 books (note further tests depend on this running first).'() {
given: 'an anchor containing a bookstore with one category'
- cpsAdminService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'limitsAnchor')
+ cpsAnchorService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'limitsAnchor')
def parentNodeData = '{"bookstore": { "categories": [{ "code": 1, "name": "Test", "books" : [] }] }}'
cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor', parentNodeData, OffsetDateTime.now())
when: '33,000 books are added'
when:
resourceMeter.start()
cpsDataService.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor', OffsetDateTime.now())
- cpsAdminService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor')
+ cpsAnchorService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'limitsAnchor')
resourceMeter.stop()
def durationInSeconds = resourceMeter.getTotalTimeInSeconds()
then: 'test data is deleted in 1 second'
package org.onap.cps.integration.performance.cps
-import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.integration.performance.base.CpsPerfTestBase
-class CpsAdminServiceLimitsPerfTest extends CpsPerfTestBase {
+class CpsDataspaceServiceLimitsPerfTest extends CpsPerfTestBase {
- CpsAdminService objectUnderTest
+ CpsAnchorService objectUnderTest
- def setup() { objectUnderTest = cpsAdminService }
+ def setup() { objectUnderTest = cpsAnchorService }
def 'Get anchors from multiple schema set names limit exceeded: 32,766 (~ 2^15) schema set names.'() {
given: 'more than 32,766 schema set names'
def anchorNames = (1..10).collect {'delete' + it}
when: 'data nodes are deleted'
resourceMeter.start()
- cpsAdminService.deleteAnchors(CPS_PERFORMANCE_TEST_DATASPACE, anchorNames)
+ cpsAnchorService.deleteAnchors(CPS_PERFORMANCE_TEST_DATASPACE, anchorNames)
resourceMeter.stop()
def deleteDurationInSeconds = resourceMeter.getTotalTimeInSeconds()
then: 'delete duration is within expected time and memory used is within limit'
import org.onap.cps.api.CpsDataService
import org.onap.cps.integration.performance.base.CpsPerfTestBase
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+
class UpdatePerfTest extends CpsPerfTestBase {
+ static final def UPDATE_TEST_ANCHOR = 'updateTestAnchor'
+ static final def INNER_NODE_JSON = readResourceDataFile('openroadm/innerNode.json')
CpsDataService objectUnderTest
def now = OffsetDateTime.now()
def setup() { objectUnderTest = cpsDataService }
- def 'Update 1 data node with descendants'() {
- given: 'a list of data nodes to update as JSON'
- def parentNodeXpath = "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-10']"
- def jsonData = readResourceDataFile('openroadm/innerNode.json').replace('NODE_ID_HERE', '10')
- when: 'the fragment entities are updated by the data nodes'
- resourceMeter.start()
- objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', parentNodeXpath, jsonData, now)
- resourceMeter.stop()
- def updateDurationInSeconds = resourceMeter.getTotalTimeInSeconds()
- then: 'update completes within expected time and memory used is within limit'
- recordAndAssertResourceUsage('Update 1 data node', 0.6, updateDurationInSeconds, 100, resourceMeter.getTotalMemoryUsageInMB())
+ def 'Test setup for CPS Update API.'() {
+ given: 'an anchor and empty container node for OpenROADM devices'
+ cpsAnchorService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, UPDATE_TEST_ANCHOR)
+ cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR,
+ '{ "openroadm-devices": { "openroadm-device": []}}', now)
+ expect: 'no device nodes exist yet'
+ assert 0 == countDataNodes('/openroadm-devices/openroadm-device')
}
- def 'Batch update 100 data nodes with descendants'() {
- given: 'a list of data nodes to update as JSON'
- def innerNodeJson = readResourceDataFile('openroadm/innerNode.json')
- def nodesJsonData = (1..100).collectEntries {[
- "/openroadm-devices/openroadm-device[@device-id='C201-7-1A-" + it + "']",
- innerNodeJson.replace('NODE_ID_HERE', it.toString())
- ]}
- when: 'the fragment entities are updated by the data nodes'
+ def 'JVM warm up for update tests: #scenario.'() {
+ given: 'replacement JSON for node containing list of device nodes'
+ def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}'
+ when: 'the container node is updated'
+ objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now)
+ then: 'there are the expected number of total nodes'
+ assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
+ where:
+ scenario | totalNodes | startId | changeLeaves
+ 'Replace 0 nodes with 100' | 100 | 1 | false
+ 'Replace 100 using same data' | 100 | 1 | false
+ 'Replace 100 with new leaf values' | 100 | 1 | true
+ 'Replace 100 with 100 new nodes' | 100 | 101 | false
+ 'Replace 50 existing and 50 new' | 100 | 151 | true
+ 'Replace 100 nodes with 0' | 0 | 1 | false
+ }
+
+ def 'Replace single data node and descendants: #scenario.'() {
+ given: 'replacement JSON for node containing list of device nodes'
+ def jsonData = '{ "openroadm-devices": ' + generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves) + '}'
+ when: 'the container node is updated'
resourceMeter.start()
- objectUnderTest.updateDataNodesAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm1', nodesJsonData, now)
+ objectUnderTest.updateDataNodeAndDescendants(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/', jsonData, now)
resourceMeter.stop()
- def updateDurationInSeconds = resourceMeter.getTotalTimeInSeconds()
- then: 'update completes within expected time and memory used is within limit'
- recordAndAssertResourceUsage('Update 100 data nodes', 40, updateDurationInSeconds, 800, resourceMeter.getTotalMemoryUsageInMB())
+ then: 'there are the expected number of total nodes'
+ assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
+ and: 'data leaves have expected values'
+ assert totalNodes == countDataNodes(changeLeaves? '/openroadm-devices/openroadm-device[@status="fail"]'
+ : '/openroadm-devices/openroadm-device[@status="success"]')
+ and: 'update completes within expected time and memory used is within limit'
+ recordAndAssertResourceUsage(scenario,
+ timeLimit, resourceMeter.getTotalTimeInSeconds(),
+ memoryLimit, resourceMeter.getTotalMemoryUsageInMB())
+ where:
+ scenario | totalNodes | startId | changeLeaves || timeLimit | memoryLimit
+ 'Replace 0 nodes with 100' | 100 | 1 | false || 7 | 250
+ 'Replace 100 using same data' | 100 | 1 | false || 5 | 250
+ 'Replace 100 with new leaf values' | 100 | 1 | true || 5 | 250
+ 'Replace 100 with 100 new nodes' | 100 | 101 | false || 12 | 300
+ 'Replace 50 existing and 50 new' | 100 | 151 | true || 8 | 250
+ 'Replace 100 nodes with 0' | 0 | 1 | false || 5 | 250
}
- def 'Update leaves for 1 data node (twice)'() {
- given: 'Updated json for openroadm data'
- def jsonDataUpdated = "{'openroadm-device':{'device-id':'C201-7-1A-10','status':'fail','ne-state':'jeopardy'}}"
- def jsonDataOriginal = "{'openroadm-device':{'device-id':'C201-7-1A-10','status':'success','ne-state':'inservice'}}"
- when: 'update is performed for leaves'
+ def 'Replace list content: #scenario.'() {
+ given: 'replacement JSON for list of device nodes'
+ def jsonListData = generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves)
+ when: 'the container node is updated'
resourceMeter.start()
- objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', "/openroadm-devices", jsonDataUpdated, now)
- objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', "/openroadm-devices", jsonDataOriginal, now)
+ objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now)
resourceMeter.stop()
- def updateDurationInSeconds = resourceMeter.getTotalTimeInSeconds()
- then: 'update completes within expected time and memory used is within limit'
- recordAndAssertResourceUsage('Update leaves for 1 data node', 0.7, updateDurationInSeconds, 200, resourceMeter.getTotalMemoryUsageInMB())
+ then: 'there are the expected number of total nodes'
+ assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
+ and: 'data leaves have expected values'
+ assert totalNodes == countDataNodes(changeLeaves? '/openroadm-devices/openroadm-device[@status="fail"]'
+ : '/openroadm-devices/openroadm-device[@status="success"]')
+ and: 'update completes within expected time and memory used is within limit'
+ recordAndAssertResourceUsage(scenario,
+ timeLimit, resourceMeter.getTotalTimeInSeconds(),
+ memoryLimit, resourceMeter.getTotalMemoryUsageInMB())
+ where:
+ scenario | totalNodes | startId | changeLeaves || timeLimit | memoryLimit
+ 'Replace list of 0 with 100' | 100 | 1 | false || 7 | 250
+ 'Replace list of 100 using same data' | 100 | 1 | false || 5 | 250
+ 'Replace list of 100 with new leaf values' | 100 | 1 | true || 5 | 250
+ 'Replace list with 100 new nodes' | 100 | 101 | false || 12 | 300
+ 'Replace list with 50 existing and 50 new' | 100 | 151 | true || 8 | 250
+ 'Replace list of 100 nodes with 1' | 1 | 1 | false || 5 | 250
}
- def 'Batch update leaves for 100 data nodes (twice)'() {
- given: 'Updated json for openroadm data'
- def jsonDataUpdated = "{'openroadm-device':[" + (1..100).collect { "{'device-id':'C201-7-1A-" + it + "','status':'fail','ne-state':'jeopardy'}" }.join(",") + "]}"
- def jsonDataOriginal = "{'openroadm-device':[" + (1..100).collect { "{'device-id':'C201-7-1A-" + it + "','status':'success','ne-state':'inservice'}" }.join(",") + "]}"
+ def 'Update leaves for 100 data nodes.'() {
+ given: 'there are 200 existing data nodes'
+ def jsonListData = generateJsonForOpenRoadmDevices(1, 200, false)
+ objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now)
+ and: 'JSON for updated data leaves of 100 nodes'
+ def jsonDataUpdated = "{'openroadm-device':[" + (1..100).collect {"{'device-id':'C201-7-1A-" + it + "','status':'fail','ne-state':'jeopardy'}" }.join(",") + "]}"
when: 'update is performed for leaves'
resourceMeter.start()
- objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', "/openroadm-devices", jsonDataUpdated, now)
- objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, 'openroadm2', "/openroadm-devices", jsonDataOriginal, now)
+ objectUnderTest.updateNodeLeaves(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, "/openroadm-devices", jsonDataUpdated, now)
resourceMeter.stop()
- def updateDurationInSeconds = resourceMeter.getTotalTimeInSeconds()
- then: 'update completes within expected time and memory used is within limit'
- recordAndAssertResourceUsage('Batch update leaves for 100 data nodes', 1, updateDurationInSeconds, 200, resourceMeter.getTotalMemoryUsageInMB())
+ then: 'data leaves have expected values'
+ assert 100 == countDataNodes('/openroadm-devices/openroadm-device[@status="fail"]')
+ and: 'update completes within expected time and memory used is within limit'
+ recordAndAssertResourceUsage('Update leaves for 100 data nodes',
+ 0.5, resourceMeter.getTotalTimeInSeconds(),
+ 120, resourceMeter.getTotalMemoryUsageInMB())
+ }
+
+ def 'Clean up for CPS Update API.'() {
+ cleanup: 'test anchor and data nodes'
+ cpsAnchorService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR)
+ }
+
+ def generateJsonForOpenRoadmDevices(int startId, int totalNodes, boolean changeLeaves) {
+ return '{ "openroadm-device": [' + (0..<totalNodes).collect {makeInnerNodeJson(it + startId, changeLeaves) }.join(',') + ']}}'
+ }
+
+ def makeInnerNodeJson(int nodeId, boolean changeLeaf) {
+ def nodeJson = INNER_NODE_JSON.replace('NODE_ID_HERE', nodeId.toString())
+ if (changeLeaf) {
+ nodeJson = nodeJson.replace('"status": "success"', '"status": "fail"')
+ }
+ return nodeJson
+ }
+
+ def countDataNodes(String cpsPathQuery) {
+ return cpsQueryService.queryDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, cpsPathQuery, OMIT_DESCENDANTS).size()
}
}
def 'Writing openroadm data has linear time.'() {
given: 'an empty anchor exists for openroadm'
- cpsAdminService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'writeAnchor')
+ cpsAnchorService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, LARGE_SCHEMA_SET, 'writeAnchor')
and: 'a list of device nodes to add'
def jsonData = generateOpenRoadData(totalNodes)
when: 'device nodes are added'
recordAndAssertResourceUsage("Writing ${totalNodes} devices", expectedDuration, durationInSeconds, memoryLimit, resourceMeter.getTotalMemoryUsageInMB())
cleanup:
cpsDataService.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor', OffsetDateTime.now())
- cpsAdminService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor')
+ cpsAnchorService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor')
where:
totalNodes || expectedDuration | memoryLimit
50 || 4 | 100
def 'Writing bookstore data has exponential time.'() {
given: 'an anchor containing a bookstore with a single category'
- cpsAdminService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'writeAnchor')
+ cpsAnchorService.createAnchor(CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'writeAnchor')
def parentNodeData = '{"bookstore": { "categories": [{ "code": 1, "name": "Test", "books" : [] }] }}'
cpsDataService.saveData(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor', parentNodeData, OffsetDateTime.now())
and: 'a list of books to add'
recordAndAssertResourceUsage("Writing ${totalBooks} books", expectedDuration, durationInSeconds, memoryLimit, resourceMeter.getTotalMemoryUsageInMB())
cleanup:
cpsDataService.deleteDataNodes(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor', OffsetDateTime.now())
- cpsAdminService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor')
+ cpsAnchorService.deleteAnchor(CPS_PERFORMANCE_TEST_DATASPACE, 'writeAnchor')
where:
totalBooks || expectedDuration | memoryLimit
800 || 1 | 50
contact "toine.siebelink@est.tech";
+ revision "2023-11-27" {
+ description
+ "Added alternateId";
+ }
+
revision "2023-08-23" {
description
"Added ModuleSetTag";
leaf module-set-tag {
type string;
}
+ leaf alternate-id {
+ type string;
+ }
list additional-properties {
key "name";
}
}
}
+
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
\r
<groupId>org.onap.cps</groupId>\r
<artifactId>cps-aggregator</artifactId>\r
- <version>3.4.1-SNAPSHOT</version>\r
+ <version>3.4.2-SNAPSHOT</version>\r
<packaging>pom</packaging>\r
\r
<name>cps</name>\r
--- /dev/null
+distribution_type: container
+container_release_tag: 3.4.1
+project: cps
+log_dir: cps-maven-docker-stage-master/932/
+ref: 3e14d392a02aa6d7253418702c1dc2f713251af3
+containers:
+ - name: 'cps-and-ncmp'
+ version: '3.4.1-20231220T140720Z'
\ No newline at end of file
--- /dev/null
+distribution_type: maven
+log_dir: cps-maven-stage-master/940/
+project: cps
+version: 3.4.1
\ No newline at end of file
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>spotbugs</artifactId>
- <version>3.4.1-SNAPSHOT</version>
+ <version>3.4.2-SNAPSHOT</version>
<properties>
<nexusproxy>https://nexus.onap.org</nexusproxy>
major=3
minor=4
-patch=1
+patch=2
base_version=${major}.${minor}.${patch}