2  * ============LICENSE_START===================================================
 
   3  * Copyright (c) 2018 Amdocs
 
   4  * ============================================================================
 
   5  * Licensed under the Apache License, Version 2.0 (the "License");
 
   6  * you may not use this file except in compliance with the License.
 
   7  * You may obtain a copy of the License at
 
   9  *        http://www.apache.org/licenses/LICENSE-2.0
 
  11  * Unless required by applicable law or agreed to in writing, software
 
  12  * distributed under the License is distributed on an "AS IS" BASIS,
 
  13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  14  * See the License for the specific language governing permissions and
 
  15  * limitations under the License.
 
  16  * ============LICENSE_END=====================================================
 
  22         indices 'default-rules'
 
  26     // NDCB-AAI comparison: Context level
 
  28       name 'Attribute-comparison'
 
  29       attributes 'context-list.ndcb', 'context-list.aai'
 
  32     // NDCB-AAI comparison: Service entity
 
  34       name 'Attribute-comparison'
 
  35       attributes 'context-list.ndcb.service', 'context-list.aai.service'
 
  38     // NDCB-AAI comparison: Context level network list
 
  40       name 'Attribute-comparison'
 
  41       attributes 'context-list.ndcb.networkList[*]', 'context-list.aai.networkList[*]'
 
  44     // NDCB-AAI comparison: VNF list
 
  46       name 'Attribute-comparison'
 
  47       attributes 'context-list.ndcb.vnfList[*]', 'context-list.aai.vnfList[*]'
 
  50     // NDCB-AAI comparison: VNF network list
 
  52       name 'Attribute-comparison'
 
  53       attributes 'context-list.ndcb.vnfList[*].networkList[*]', 'context-list.aai.vnfList[*].networkList[*]'
 
  56     // NDCB-AAI comparison: VF-Module list
 
  58       name 'Attribute-comparison'
 
  59       attributes 'context-list.ndcb.vnfList[*].vfModuleList[*]', 'context-list.aai.vnfList[*].vfModuleList[*]'
 
  62     // NDCB-AAI comparison: VF-Module network list
 
  64       name 'Attribute-comparison'
 
  65       attributes 'context-list.ndcb.vnfList[*].vfModuleList[*].networkList[*]', 'context-list.aai.vnfList[*].vfModuleList[*].networkList[*]'
 
  68     // NDCB-AAI comparison: VNFC list
 
  70       name 'Attribute-comparison'
 
  71       attributes 'context-list.ndcb.vnfList[*].vnfcList[*]', 'context-list.aai.vnfList[*].vnfcList[*]'
 
  74     // NDCB-AAI comparison: VM list
 
  76       name 'Attribute-comparison'
 
  77       attributes 'context-list.ndcb.vnfList[*].vfModuleList[*].vmList[*]', 'context-list.aai.vnfList[*].vfModuleList[*].vmList[*]'
 
  80     // NDCB-AAI comparison: P-Interface list
 
  82       name 'Attribute-comparison'
 
  83       attributes 'context-list.ndcb.pnfList[*].pInterfaceList[*]', 'context-list.aai.pnfList[*].pInterfaceList[*]'
 
  87     // SDNC-AAI comparison: Context level
 
  89       name 'Attribute-comparison'
 
  90       attributes 'context-list.sdnc', 'context-list.aai'
 
  93     // SDNC-AAI comparison: Service entity
 
  95       name 'Attribute-comparison'
 
  96       attributes 'context-list.sdnc.service', 'context-list.aai.service'
 
  99     // SDNC-AAI comparison: Context level network list
 
 101       name 'Attribute-comparison'
 
 102       attributes 'context-list.sdnc.networkList[*]', 'context-list.aai.networkList[*]'
 
 105     // SDNC-AAI comparison: VNF list
 
 107       name 'Attribute-comparison'
 
 108       attributes 'context-list.sdnc.vnfList[*]', 'context-list.aai.vnfList[*]'
 
 111     // SDNC-AAI comparison: VNF network list
 
 113       name 'Attribute-comparison'
 
 114       attributes 'context-list.sdnc.vnfList[*].networkList[*]', 'context-list.aai.vnfList[*].networkList[*]'
 
 117     // SDNC-AAI comparison: VF-Module list
 
 119       name 'Attribute-comparison'
 
 120       attributes 'context-list.sdnc.vnfList[*].vfModuleList[*]', 'context-list.aai.vnfList[*].vfModuleList[*]'
 
 123     // SDNC-AAI comparison: VF-Module network list
 
 125       name 'Attribute-comparison'
 
 126       attributes 'context-list.sdnc.vnfList[*].vfModuleList[*].networkList[*]', 'context-list.aai.vnfList[*].vfModuleList[*].networkList[*]'
 
 129     // SDNC-AAI comparison: VNFC list
 
 131       name 'Attribute-comparison'
 
 132       attributes 'context-list.sdnc.vnfList[*].vnfcList[*]', 'context-list.aai.vnfList[*].vnfcList[*]'
 
 135     // SDNC-AAI comparison: VM list
 
 137       name 'Attribute-comparison'
 
 138       attributes 'context-list.sdnc.vnfList[*].vfModuleList[*].vmList[*]', 'context-list.aai.vnfList[*].vfModuleList[*].vmList[*]'
 
 141     // AAI-SDNC PNF name validation
 
 143       name 'AAI-SDNC-pnf-name-check'
 
 144       attributes 'context-list.aai.pnfList[*].name', 'context-list.sdnc.pnfList[*].name'
 
 148     // SDNC-NDCB comparison: Context level
 
 150       name 'Attribute-comparison'
 
 151       attributes 'context-list.sdnc', 'context-list.ndcb'
 
 154     // SDNC-NDCB comparison: Service entity
 
 156       name 'Attribute-comparison'
 
 157       attributes 'context-list.sdnc.service', 'context-list.ndcb.service'
 
 160     // SDNC-NDCB comparison: Context level network list
 
 162       name 'Attribute-comparison'
 
 163       attributes 'context-list.sdnc.networkList[*]', 'context-list.ndcb.networkList[*]'
 
 166     // SDNC-NDCB comparison: VNF list
 
 168       name 'Attribute-comparison'
 
 169       attributes 'context-list.sdnc.vnfList[*]', 'context-list.ndcb.vnfList[*]'
 
 172     // SDNC-NDCB comparison: VNF network list
 
 174       name 'Attribute-comparison'
 
 175       attributes 'context-list.sdnc.vnfList[*].networkList[*]', 'context-list.ndcb.vnfList[*].networkList[*]'
 
 178     // SDNC-NDCB comparison: VF-Module list
 
 180       name 'Attribute-comparison'
 
 181       attributes 'context-list.sdnc.vnfList[*].vfModuleList[*]', 'context-list.ndcb.vnfList[*].vfModuleList[*]'
 
 184     // SDNC-NDCB comparison: VF-Module network list
 
 186       name 'Attribute-comparison'
 
 187       attributes 'context-list.sdnc.vnfList[*].vfModuleList[*].networkList[*]', 'context-list.ndcb.vnfList[*].vfModuleList[*].networkList[*]'
 
 190     // SDNC-NDCB comparison: VNFC list
 
 192       name 'Attribute-comparison'
 
 193       attributes 'context-list.sdnc.vnfList[*].vnfcList[*]', 'context-list.ndcb.vnfList[*].vnfcList[*]'
 
 196     // SDNC-NDCB comparison: VM list
 
 198       name 'Attribute-comparison'
 
 199       attributes 'context-list.sdnc.vnfList[*].vfModuleList[*].vmList[*]', 'context-list.ndcb.vnfList[*].vfModuleList[*].vmList[*]'
 
 206       name 'SDC-AAI-vnfc-type'
 
 207       attributes 'context-list.sdc.vnfList[*].vnfcList[*]', 'context-list.aai.vnfList[*].vnfcList[*]'
 
 210     // SDC-AAI VNFC node count
 
 212       name 'SDC-AAI-vnfc-node-count'
 
 213       attributes 'context-list.sdc.vnfList[*].vnfcList[*]', 'context-list.aai.vnfList[*].vnfcList[*]'
 
 216     // SDC-AAI VF-Module instance
 
 218       name 'SDC-AAI-vf-module-instance-check'
 
 219       attributes 'context-list.sdc.vnfList[*].vfModuleList[*]', 'context-list.aai.vnfList[*].vfModuleList[*]'
 
 225   name        'SDC-AAI-vnfc-type'
 
 226   category    'VNFC Consistency'
 
 227   description 'Validate that each VNFC instance in AAI conforms to a VNFC type defined in SDC model'
 
 228   errorText   'AAI VNFC instance includes non-specified type in design SDC model'
 
 230   attributes  'sdcList', 'aaiList'
 
 232         def getVnfcTypes = { parsedData ->
 
 233           parsedData.collect{ it.findResult{ k, v -> if(k.equals("type")) {return "$v"}}}
 
 236         def slurper = new groovy.json.JsonSlurper()
 
 237         def sdcTypes = getVnfcTypes(slurper.parseText(sdcList.toString()))
 
 238         def aaiTypes = getVnfcTypes(slurper.parseText(aaiList.toString()))
 
 240         // each type in AAI must exist in SDC
 
 241         return sdcTypes.containsAll(aaiTypes)
 
 246   name        'SDC-AAI-vnfc-node-count'
 
 247   category    'VNFC Consistency'
 
 248   description 'Validate that for each VNFC node defined in SDC model, there is at least one VNFC instance in AAI'
 
 249   errorText   'Design has specified types but not all of them exist in AAI'
 
 251   attributes  'sdcList', 'aaiList'
 
 253         def getVnfcNodes = { parsedData ->
 
 254           parsedData.collect { new Tuple2(
 
 255               it.findResult{ k, v -> if(k.equals("name")) {return "$v"}},
 
 256               it.findResult{ k, v -> if(k.equals("type")) {return "$v"}})
 
 260         def slurper = new groovy.json.JsonSlurper()
 
 261         def sdcNodes = getVnfcNodes(slurper.parseText(sdcList.toString()))
 
 262         def aaiNodes = getVnfcNodes(slurper.parseText(aaiList.toString()))
 
 264         // each node in AAI must exist in SDC
 
 265         return aaiNodes.containsAll(sdcNodes)
 
 270   name        'SDC-AAI-vf-module-instance-check'
 
 271   category    'VF Consistency'
 
 272   description 'Validate that each VF module instance in AAI conforms to a VF module defined in SDC service model'
 
 273   errorText   'One or more AAI VF module instance(s) not defined in SDC model'
 
 275   attributes  'sdcList', 'aaiList'
 
 277         def getVfModules = { parsedData ->
 
 278           parsedData.collect{ it.findResult{ k, v -> if(k.equals("name")) {return "$v"}}}
 
 281         def slurper = new groovy.json.JsonSlurper()
 
 282         def sdcVfModules = getVfModules(slurper.parseText(sdcList.toString()))
 
 283         def aaiVfModules = getVfModules(slurper.parseText(aaiList.toString()))
 
 285         // all VF modules in AAI must exist in SDC
 
 286         return aaiVfModules.containsAll(sdcVfModules)
 
 291   name        'Attribute-comparison'
 
 292   category    'Attribute Mismatch'
 
 293   description 'Determine all discrepancies between values for attributes with matching names from each model'
 
 294   errorText   'Error found with attribute(s) and values: {0}'
 
 296   attributes  'lhsObject', 'rhsObject'
 
 298                 // This closure extracts the given object's root level attributes and contents of the attribute list.
 
 299                 // Complex items like lists are excluded.
 
 300                 // Returns a map containing attribute names as keys, mapping to a list of values for each attribute.
 
 301                 Closure<java.util.Map> getAttributes = { parsedData ->
 
 302                         java.util.Map attributeMap = new java.util.HashMap()
 
 304                         def isAttributeDataQualityOk = { attribute ->
 
 305                                 attribute.findResult{ k, v -> if(k.equals("dataQuality") ) {return v.get("status")}}.equals("ok")
 
 308                         def addToMap = { attrKey, attrValue ->
 
 309                                 java.util.Set values = attributeMap.get("$attrKey")
 
 311                                         values = new java.util.HashSet()
 
 312                                         attributeMap.put("$attrKey", values)
 
 314                                 values.add("$attrValue")
 
 317                         def addAttributeToMap = { attribute ->
 
 318                                 if(isAttributeDataQualityOk(attribute)) {
 
 320                                         attribute.each { k, v ->
 
 321                                                 if(k.equals("name")) {key = "$v"}
 
 322                                                 if(k.equals("value")) {value = "$v"}
 
 324                                         addToMap("$key", "$value")
 
 328                         def processKeyValue = { key, value ->
 
 329                                 if(value instanceof java.util.ArrayList) {
 
 330                                         if(key.equals("attributeList")) {
 
 332                                                         addAttributeToMap(it)
 
 335                                 } else if(!(value instanceof groovy.json.internal.LazyMap)) {
 
 336                                         // only add key-value attributes, skip the rest
 
 337                                         addToMap("$key", "$value")
 
 341                         if(parsedData instanceof java.util.ArrayList) {
 
 343                                         it.each { key, value -> processKeyValue(key, value) }
 
 346                                 parsedData.each { key, value -> processKeyValue(key, value) }
 
 351                 // This closure compares all values for each key from the left map, to values of the same key from the right map.
 
 352                 // Returns a map of attributes with mismatched or missing values (i.e. attribute name mapped to list of failed values).
 
 353                 Closure<java.util.Map> compareAttributes = { java.util.Map left, java.util.Map right ->
 
 354                         java.util.Map violationMap = new java.util.HashMap()
 
 355                         left.each{ leftKey, leftValueList ->
 
 356                                 def rightValueList = right.get("$leftKey")
 
 357                                 rightValueList.each{ rightValue ->
 
 358                                         if(!leftValueList.any{ it == "$rightValue" }) {
 
 359                                                 def existingValues = violationMap.get(leftKey)
 
 361                                                         existingValues.add("$rightValue")
 
 363                                                         java.util.Set newValues = new HashSet()
 
 364                                                         newValues.add("$rightValue")
 
 365                                                         violationMap.put("$leftKey", newValues)
 
 373                 // This closure merges the given maps into a new map.
 
 374                 // Returns a map containing all keys and their values from both maps.
 
 375                 Closure<java.util.Map> mergeMaps = { java.util.Map left, java.util.Map right ->
 
 376                         if(left.isEmpty() && right.isEmpty()) {
 
 378                         } else if(left.isEmpty()) {
 
 380                         } else if(right.isEmpty()) {
 
 383                         java.util.Map merged = new java.util.HashMap()
 
 385                         right.each{ rightKey, rightValues ->
 
 386                                 java.util.Set mergedValues = merged.get(rightKey)
 
 387                                 if(mergedValues == null) {
 
 388                                         merged.put(rightKey, rightValues)
 
 390                                         mergedValues.addAll(rightValues)
 
 396                 def slurper = new groovy.json.JsonSlurper()
 
 397                 java.util.Map lhsAttributes = getAttributes(slurper.parseText(lhsObject.toString()))
 
 398                 java.util.Map rhsAttributes = getAttributes(slurper.parseText(rhsObject.toString()))
 
 400                 def leftToRight = compareAttributes(lhsAttributes, rhsAttributes)
 
 401                 def rightToLeft = compareAttributes(rhsAttributes, lhsAttributes)
 
 402                 def mergedResults = mergeMaps(leftToRight, rightToLeft)
 
 404                 boolean success = true
 
 405                 List<String> details = new ArrayList<>()
 
 406                 if(!mergedResults.isEmpty()) {
 
 408                         details.add(mergedResults.toString())
 
 410                 return new Tuple2(success, details)
 
 415  * The data-dictionary rule below can be used with this useRule clause:
 
 417  *     name 'Data-Dictionary validate VF type'
 
 418  *     attributes 'context-list.ndcb.vnfList[*].vfModuleList[*].networkList[*].type'
 
 422     name        'Data-Dictionary validate VF type'
 
 423     category    'INVALID_VALUE'
 
 424     description 'Validate all VF type values against data-dictionary'
 
 425     errorText   'VF type [{0}] failed data-dictionary validation: {1}'
 
 427     attributes  'typeList'
 
 429         boolean success = true
 
 430         List<String> details = new ArrayList<>()
 
 433                 // break out of 'any' loop
 
 436             def result = org.onap.aai.validation.ruledriven.rule.builtin.DataDictionary.validate("instance", "vfModuleNetworkType", "type", "$it")
 
 437             if(!result.isEmpty()) {
 
 440                 details.add("$result")
 
 443         return new Tuple2(success, details)
 
 448   name        'AAI-SDNC-pnf-name-check'
 
 449   category    'PNF Consistency'
 
 450   description 'Validate that each PNF name in AAI matches a PNF name in the SDNC model'
 
 451   errorText   'AAI PNF names do not match SDNC - {0}'
 
 453   attributes  'aaiNames', 'sdncNames'
 
 455         def addName = { values, key ->
 
 459         List<String> errorReasons = new ArrayList();
 
 461         if (aaiNames.size() != sdncNames.size()) {
 
 462             errorReasons.add("Number of PNFs don't match; aai has ${aaiNames.size()}, sdnc has ${sdncNames.size()}")
 
 463             return new Tuple2(false, errorReasons)
 
 466         // collect all the "name" values from AAI and SDNC into two Sets.
 
 467         Set aaiNameSet = new java.util.HashSet()
 
 469            aValue -> addName(aaiNameSet, aValue)
 
 472         Set sdncNameSet = new java.util.HashSet()
 
 474             aValue -> addName(sdncNameSet, aValue)
 
 477         // Validate that the names match by comparing the size of the two Sets.
 
 478         if (aaiNameSet.size() != sdncNameSet.size()) {
 
 479             errorReasons.add("Number of distinct PNF names don't match; aai: ${aaiNameSet}, sdnc: ${sdncNameSet}")
 
 480             return new Tuple2(false, errorReasons)
 
 483         Set combinedSet = new HashSet();
 
 484         combinedSet.addAll(aaiNameSet);
 
 485         combinedSet.addAll(sdncNameSet);
 
 486         if (combinedSet.size() != aaiNameSet.size()) {
 
 487             errorReasons.add("PNF names don't match; aai names: ${aaiNameSet}, sdnc names: ${sdncNameSet}")
 
 488             return new Tuple2(false, errorReasons)