From 5a34dbbd091db590a9a38e5398140933ae9ed639 Mon Sep 17 00:00:00 2001 From: Jennie Jia Date: Tue, 11 Sep 2018 15:06:39 +0000 Subject: Update API and common model mapping Issue-ID: LOG-646 Change-Id: Ie56d3f697abbafecf9c2d9f73fa52bee1833a469 Signed-off-by: Jennie Jia --- .../pomba/contextbuilder/aai/util/RestUtil.java | 401 ++++++++++++++++----- 1 file changed, 314 insertions(+), 87 deletions(-) (limited to 'src/main/java/org/onap/pomba/contextbuilder/aai/util/RestUtil.java') diff --git a/src/main/java/org/onap/pomba/contextbuilder/aai/util/RestUtil.java b/src/main/java/org/onap/pomba/contextbuilder/aai/util/RestUtil.java index 905a8bc..60bbd61 100644 --- a/src/main/java/org/onap/pomba/contextbuilder/aai/util/RestUtil.java +++ b/src/main/java/org/onap/pomba/contextbuilder/aai/util/RestUtil.java @@ -14,9 +14,7 @@ package org.onap.pomba.contextbuilder.aai.util; - import com.sun.jersey.core.util.MultivaluedMapImpl; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -35,21 +33,27 @@ import org.json.JSONException; import org.json.JSONObject; import org.onap.aai.restclient.client.OperationResult; import org.onap.aai.restclient.client.RestClient; +import org.onap.pomba.common.datatypes.Attribute; +import org.onap.pomba.common.datatypes.DataQuality; import org.onap.pomba.common.datatypes.ModelContext; import org.onap.pomba.common.datatypes.Service; import org.onap.pomba.common.datatypes.VF; import org.onap.pomba.common.datatypes.VFModule; +import org.onap.pomba.common.datatypes.VM; import org.onap.pomba.common.datatypes.VNFC; import org.onap.pomba.contextbuilder.aai.common.LogMessages; +import org.onap.pomba.contextbuilder.aai.datatype.Relationship; +import org.onap.pomba.contextbuilder.aai.datatype.RelationshipList; import org.onap.pomba.contextbuilder.aai.datatype.ServiceInstance; import org.onap.pomba.contextbuilder.aai.datatype.VfModule; import org.onap.pomba.contextbuilder.aai.datatype.VnfInstance; import org.onap.pomba.contextbuilder.aai.datatype.VnfcInstance; +import org.onap.pomba.contextbuilder.aai.datatype.Vserver; import org.onap.pomba.contextbuilder.aai.exception.AuditError; import org.onap.pomba.contextbuilder.aai.exception.AuditException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; + public class RestUtil { @@ -60,8 +64,7 @@ public class RestUtil { private static final String SERVICE_INSTANCE_ID = "serviceInstanceId"; private static final String MODEL_VERSION_ID = "modelVersionId"; private static final String MODEL_INVARIANT_ID = "modelInvariantId"; - private static final String CUSTOMER_ID = "customerId"; - private static final String SERVICE_TYPE = "serviceType"; + // HTTP headers private static final String TRANSACTION_ID = "X-TransactionId"; @@ -70,10 +73,16 @@ public class RestUtil { private static final String APP_NAME = "aaiCtxBuilder"; - // Service Catalog + // Service Catalog - "related-to" private static final String CATALOG_GENERIC_VNF = "generic-vnf"; private static final String CATALOG_VNFC = "vnfc"; private static final String CATALOG_SERVICE_INSTANCE = "service-instance"; + private static final String CATALOG_VSERVER = "vserver"; + private static final String CATALOG_IMAGE = "image"; + private static final String CATALOG_PSERVER = "pserver"; + private static final String VF_MODULES = "vf-modules"; + private static final String VF_MODULE = "vf-module"; + // Relationship Json Path private static final String RELATIONSHIP_LIST = "relationship-list"; @@ -95,8 +104,8 @@ public class RestUtil { * * @throws AuditException if there is missing parameter */ - public static void validateURL(String serviceInstanceId, String modelVersionId, String modelInvariantId, - String serviceType, String customerId) throws AuditException { + public static void validateURL(String serviceInstanceId, String modelVersionId, String modelInvariantId) + throws AuditException { if (serviceInstanceId == null || serviceInstanceId.isEmpty()) { throw new AuditException(AuditError.INVALID_REQUEST_URL_MISSING_PARAMETER + SERVICE_INSTANCE_ID, @@ -112,18 +121,10 @@ public class RestUtil { Status.BAD_REQUEST); } - // serviceType - if (serviceType == null || serviceType.isEmpty()) { - throw new AuditException(AuditError.INVALID_REQUEST_URL_MISSING_PARAMETER + SERVICE_TYPE, Status.BAD_REQUEST); - } - // customerId - if (customerId == null || customerId.isEmpty()) { - throw new AuditException(AuditError.INVALID_REQUEST_URL_MISSING_PARAMETER + CUSTOMER_ID, Status.BAD_REQUEST); - } } public static void validateBasicAuthorization(HttpHeaders headers, String basicAuthorization) throws AuditException { - String authorization = null; + String authorization = null; // validation on HTTP Authorization Header authorization = headers.getRequestHeaders().getFirst(AUTHORIZATION); @@ -166,25 +167,25 @@ public class RestUtil { } /* - * Trigger external API call to AAI to retrieve Service Instance data (i.e. genericVNF and VNFC) + * Trigger external API call to AAI to collect the data in order to transform to common model + * */ - public static ModelContext retrieveAAIModelData(RestClient aaiClient, String baseURL, String aaiPathToSearchNodeQuery, String aaiServiceInstancePath, - String transactionId, String serviceInstanceId, String modelVersionId, String modelInvariantId, - String serviceType, String customerId, String aaiBasicAuthorization) throws AuditException { + public static ModelContext retrieveAAIModelData(RestClient aaiClient, String baseURL, String aaiPathToSearchNodeQuery, + String transactionId, String serviceInstanceId, String modelVersionId, String modelInvariantId, String aaiBasicAuthorization) throws AuditException { String serviceInstancePayload = null; String genericVNFPayload = null; - String vnfcPayload = null; - // Follow two variables for transform purpose List vnfLst = new ArrayList(); // List of the VNF POJO object - Map> vnfMap = new HashMap>(); // MAP the vnf-id as the - // key, and list of the vNFC - // pojo object + //Map to track multiple vnfc under the Gerneric VNF id. The key = vnf-id. The value = list of vnfc instance + Map> vnfcMap = new HashMap>(); + + //Map to track the relationship between vnf->vfmodule->verver + Map>> vnf_vfmodule_vserver_Map = new HashMap>>(); + // Obtain resource-link based on resource-type = service-Instance String resourceLink = obtainResouceLinkBasedOnServiceInstanceFromAAI(aaiClient, baseURL, aaiPathToSearchNodeQuery, serviceInstanceId, transactionId, aaiBasicAuthorization); + String url = baseURL + resourceLink; - String url = baseURL - + generateServiceInstanceURL(aaiServiceInstancePath, customerId, serviceType, serviceInstanceId); // Response from service instance API call serviceInstancePayload = getResource(aaiClient, url, aaiBasicAuthorization, transactionId, MediaType.valueOf(MediaType.APPLICATION_JSON)); @@ -200,7 +201,6 @@ public class RestUtil { log.info(LogMessages.NUMBER_OF_API_CALLS, "genericVNF", genericVNFLinkLst.size()); log.info(LogMessages.API_CALL_LIST, "genericVNF", printOutAPIList(genericVNFLinkLst)); - for (String genericVNFLink : genericVNFLinkLst) { // With latest AAI development, in order to retrieve the both generic VNF + vf_module, we can use // one API call but with depth=2 @@ -212,50 +212,121 @@ public class RestUtil { if (isEmptyJson(genericVNFPayload)) { log.info(LogMessages.NOT_FOUND, "GenericVNF with url ", genericVNFLink); } else { - // Logic to Create the Generic VNF Instance POJO object VnfInstance vnfInstance = VnfInstance.fromJson(genericVNFPayload); vnfLst.add(vnfInstance); - List vnfcLinkLst = extractRelatedLink(genericVNFPayload, CATALOG_VNFC); - log.info(LogMessages.NUMBER_OF_API_CALLS, "vnfc", vnfcLinkLst.size()); - log.info(LogMessages.API_CALL_LIST, "vnfc", printOutAPIList(vnfcLinkLst)); - - List vnfcLst = new ArrayList(); - for (String vnfcLink : vnfcLinkLst) { - String vnfcURL = baseURL + vnfcLink; - vnfcPayload = getResource(aaiClient, vnfcURL, aaiBasicAuthorization, transactionId, - MediaType.valueOf(MediaType.APPLICATION_XML)); - - if (isEmptyJson(vnfcPayload)) { - log.info(LogMessages.NOT_FOUND, "VNFC with url", vnfcLink); - } else { - // Logic to Create the VNFC POJO object - VnfcInstance vnfcInstance = VnfcInstance.fromJson(vnfcPayload); - vnfcLst.add(vnfcInstance); - } - } + // Build the vnf_vnfc relationship map + vnfcMap = buildVnfcMap(genericVNFPayload, aaiClient, baseURL, transactionId, aaiBasicAuthorization ); + + // Build vnf_vfmodule_vserver relationship map + vnf_vfmodule_vserver_Map= buildVfmoduleVserverMap(genericVNFPayload, aaiClient, baseURL, transactionId, aaiBasicAuthorization); - // Assume the vnf-id is unique as a key - vnfMap.put(vnfInstance.getVnfId(), vnfcLst); } } // Transform to common model and return - return transform(ServiceInstance.fromJson(serviceInstancePayload), vnfLst, vnfMap); + return transform(ServiceInstance.fromJson(serviceInstancePayload), vnfLst, vnfcMap, vnf_vfmodule_vserver_Map); } + /* + * The map is to track the relationship of vnf-id with multiple vnfc relationship + */ + private static Map> buildVnfcMap(String genericVNFPayload, RestClient aaiClient, String baseURL, + String transactionId, String aaiBasicAuthorization) throws AuditException { + + String vnfcPayload = null; + Map> vnfcMap = new HashMap>(); + + List vnfcLinkLst = extractRelatedLink(genericVNFPayload, CATALOG_VNFC); + log.info(LogMessages.NUMBER_OF_API_CALLS, "vnfc", vnfcLinkLst.size()); + log.info(LogMessages.API_CALL_LIST, "vnfc", printOutAPIList(vnfcLinkLst)); + + List vnfcLst = new ArrayList(); + for (String vnfcLink : vnfcLinkLst) { + String vnfcURL = baseURL + vnfcLink; + vnfcPayload = getResource(aaiClient, vnfcURL, aaiBasicAuthorization, transactionId, + MediaType.valueOf(MediaType.APPLICATION_XML)); + + if (isEmptyJson(vnfcPayload)) { + log.info(LogMessages.NOT_FOUND, "VNFC with url", vnfcLink); + } else { + // Logic to Create the VNFC POJO object + VnfcInstance vnfcInstance = VnfcInstance.fromJson(vnfcPayload); + vnfcLst.add(vnfcInstance); + } + } + // Assume the vnf-id is unique as a key + vnfcMap.put(getVnfId(genericVNFPayload), vnfcLst); + + return vnfcMap; + } + + /* + * This is a two layer map to track the relationship between vnf->vfmodule->verver + * + * The Map is to track multiple vserver under the vfModule. + * Key: combination of the model_version_id and model_invariant_id. + * Value: list of Vserver + * + * The Map>> key: vnf-id + */ + private static Map>> buildVfmoduleVserverMap(String genericVNFPayload, RestClient aaiClient, String baseURL, String transactionId, String aaiBasicAuthorization) throws AuditException { + + Map>> vnf_vfmodule_vserver_Map = new HashMap>>(); + + Map> vServerMap = new HashMap>(); + + Map> vServerRelatedLinkMap = extractRelatedLinkFromVfmodule(genericVNFPayload, CATALOG_VSERVER); + String vnfId= getVnfId(genericVNFPayload); + String vserverPayload = null; + + for(Map.Entry> entry : vServerRelatedLinkMap.entrySet()) { + + List vserverLinkLst = entry.getValue(); + log.info(LogMessages.NUMBER_OF_API_CALLS, "vserver", vserverLinkLst.size()); + log.info(LogMessages.API_CALL_LIST, "vserver", printOutAPIList(vserverLinkLst)); + + List vserverLst = new ArrayList(); + for (String vserverLink : vserverLinkLst) { + String vserverURL = baseURL + vserverLink; + vserverPayload = getResource(aaiClient, vserverURL, aaiBasicAuthorization, transactionId, + MediaType.valueOf(MediaType.APPLICATION_XML)); + + if (isEmptyJson(vserverPayload)) { + log.info(LogMessages.NOT_FOUND, "VSERVER with url", vserverURL); + } else { + // Logic to Create the Vserver POJO object + Vserver vserver = Vserver.fromJson(vserverPayload); + vserverLst.add(vserver); + } + } + + vServerMap.put(entry.getKey(), vserverLst); + } + + vnf_vfmodule_vserver_Map.put(vnfId, vServerMap); + + return vnf_vfmodule_vserver_Map; + } + + + private static String getVnfId(String genericVNFPayload) throws AuditException { + + VnfInstance vnfInstance = VnfInstance.fromJson(genericVNFPayload); + return vnfInstance.getVnfId(); + } /* * Transform AAI Representation to Common Model */ private static ModelContext transform(ServiceInstance svcInstance, List vnfLst, - Map> vnfMap) { + Map> vnfcMap, Map>> vnf_vfmodule_vserver_Map) { ModelContext context = new ModelContext(); Service service = new Service(); service.setInvariantUuid(svcInstance.getModelInvariantId()); service.setName(svcInstance.getServiceInstanceName()); service.setUuid(svcInstance.getModelVersionId()); - + service.setDataQuality(DataQuality.ok()); List vfLst = new ArrayList(); for (VnfInstance vnf : vnfLst) { @@ -264,12 +335,13 @@ public class RestUtil { vf.setName(vnf.getVnfName()); vf.setUuid(vnf.getModelVersionId()); vf.setType(vnf.getVnfType()); - vf.setNfNamingCode(vnf.getNfNamingCode()); + vf.setDataQuality(DataQuality.ok()); - String key = vnf.getVnfId(); - List vnfcLst = new ArrayList(); + String key = vnf.getVnfId(); // generic vnf-id (top level of the key) - for (Map.Entry> entry : vnfMap.entrySet()) { + // ---------------- Handle VNFC data + List vnfcLst = new ArrayList(); + for (Map.Entry> entry : vnfcMap.entrySet()) { if (key.equals(entry.getKey())) { List vnfcInstanceLst = entry.getValue(); @@ -278,55 +350,141 @@ public class RestUtil { VNFC vnfcModel = new VNFC(); vnfcModel.setInvariantUuid(vnfc.getModelInvariantId()); vnfcModel.setName(vnfc.getVnfcName()); - vnfcModel.setNfcNamingCode(vnfc.getNfcNamingCode()); vnfcModel.setUuid(vnfc.getModelVersionId()); vnfcLst.add(vnfcModel); } } } + vf.setVnfcs(vnfcLst); - vf.setVnfc(vnfcLst); - // Add the vfModule - List vfModuleLst = new ArrayList(); - if (vnf.getVfModules() != null) { - ConcurrentMap vnfModulemap = - buildMaxInstanceMap(vnf.getVfModules().getVfModule()); + // --------------- Handle the vfModule + //Map to calculate the Vf Module MaxInstance. + ConcurrentMap maxInstanceMap = + buildMaxInstanceMap(vnf.getVfModules().getVfModule()); - for (Map.Entry entry : vnfModulemap.entrySet()) { - String[] s = entry.getKey().split("\\" + DELIMITER); + List vfModuleLst = new ArrayList(); + for ( Map.Entry>> entry: vnf_vfmodule_vserver_Map.entrySet() ) { + // find the vnf-id + if (key.equals(entry.getKey())) { + + Map> vfmodule_vserver_map= entry.getValue(); + + for ( Map.Entry> vfmoduleEntry: vfmodule_vserver_map.entrySet() ){ + // The key is modelversionId$modelInvariantid + String[] s = vfmoduleEntry.getKey().split("\\" + DELIMITER); + String modelVersionId = s[0]; + String modelInvariantId = s[1]; + + VFModule vfModule = new VFModule(); + vfModule.setUuid(modelVersionId); + vfModule.setInvariantUuid(modelInvariantId); + vfModule.setMaxInstances(getMaxInstance(vfmoduleEntry.getKey(), maxInstanceMap)); + vfModule.setDataQuality(DataQuality.ok()); + + List vserverList = vfmoduleEntry.getValue(); + + // Handle VM + List vmList = new ArrayList(); + for (Vserver vserver: vserverList) { + + List attributeList = new ArrayList(); + + // Iterate through the ENUM Attribute list + for (Attribute.Name name: Attribute.Name.values()) { + if (name.toString().equals("lockedBoolean")) { + Attribute att = new Attribute(); + att.setDataQuality(DataQuality.ok()); + att.setName(Attribute.Name.lockedBoolean); + att.setValue(String.valueOf(vserver.getInMaint())); + attributeList.add(att); + } + + if (name.toString().equals("hostName")) { + Attribute att = new Attribute(); + att.setDataQuality(DataQuality.ok()); + att.setName(Attribute.Name.hostName); + att.setValue(getVserverAttribute(vserver, CATALOG_PSERVER)); + attributeList.add(att); + } + + if (name.toString().equals("imageId")) { + Attribute att = new Attribute(); + att.setDataQuality(DataQuality.ok()); + att.setName(Attribute.Name.imageId); + att.setValue(getVserverAttribute(vserver, CATALOG_IMAGE)); + attributeList.add(att); + } + } + VM vm = new VM(); + vm.setUuid(vserver.getVserverId()); + vm.setName(vserver.getVserverName()); + vm.setAttributes(attributeList); + vmList.add(vm); + } + vfModule.setVms(vmList); + vfModuleLst.add(vfModule); + } - String modelVersionId = s[0]; - String modelInvariantId = s[1]; - VFModule vfModule = new VFModule(); - vfModule.setUuid(modelVersionId); - vfModule.setInvariantUuid(modelInvariantId); - vfModule.setMaxInstances(entry.getValue().intValue()); - vfModuleLst.add(vfModule); } } - vf.setVfModules(vfModuleLst); + vf.setVfModules(vfModuleLst); vfLst.add(vf); } // done the vnfInstance context.setService(service); - context.setVf(vfLst); + context.setVfs(vfLst); return context; } + /* + * Return the Vserver Attribute value by looking through the relationship. i.e. if "related-to" is "pserver", we will get + * the value of the attribute "hostname" from the last substring of the "related-link" + * { + * "related-to": "pserver", + * "related-link": "/aai/v11/cloud-infrastructure/pservers/pserver/rdm5r10c008.rdm5a.cci.att.com", + * "relationship-data": [ { + * "relationship-key": "pserver.hostname", + * "relationship-value": "rdm5r10c008.rdm5a.cci.att.com" + * }], + * "related-to-property": [{"property-key": "pserver.pserver-name2"}] + * }, + * + */ + private static String getVserverAttribute(Vserver vserver, String key) { + RelationshipList lst = vserver.getRelationshipList(); + if (lst != null) { + List relations = lst.getRelationship(); + for (Relationship re: relations) { + if (re.getRelatedTo().equals(key)) { + return extractAttValue(re.getRelatedLink()); + } + } + } + return null; + } + + + + /* + * Get the last substring from the related-link + * For example the value of the attribute "hostname" will be "rdm5r10c008.rdm5a.cci.att.com" + * "related-to": "pserver", + * "related-link": "/aai/v11/cloud-infrastructure/pservers/pserver/rdm5r10c008.rdm5a.cci.att.com", + */ + private static String extractAttValue(String relatedLink) { + return relatedLink.substring(relatedLink.lastIndexOf("/")+1); + } /* * Build the map with key (model_version_id and model_invariant_id), and with the max occurrences of * the value in the map * - * @param vfModuleList - * - * @return */ private static ConcurrentMap buildMaxInstanceMap(List vfModuleList) { @@ -348,6 +506,20 @@ public class RestUtil { } + /* + * Return the occurrence of the VfModule complex key: (model-version-id)+(model-invariant-id) + */ + private static int getMaxInstance(String key, ConcurrentMap maxInstanceMap) { + + for (Map.Entry entry : maxInstanceMap.entrySet()) { + + if (entry.getKey().equals(key)) { + return entry.getValue().intValue(); + } + + } + return 0; + } public static boolean isEmptyJson(String serviceInstancePayload) { @@ -355,7 +527,9 @@ public class RestUtil { return (serviceInstancePayload.equals(EMPTY_JSON_STRING)); } - + /* + * Debug purpose - Display the related-link list + */ private static String printOutAPIList(List relatedLinkLst) { StringBuilder builder = new StringBuilder(); @@ -383,7 +557,7 @@ public class RestUtil { JSONObject jsonPayload = new JSONObject(payload); JSONArray relationships = null; List relatedLinkList = new ArrayList(); - log.info("Fetching the vnfc related link"); + log.debug("Fetching the related link"); try { JSONObject relationshipList = jsonPayload.getJSONObject(RELATIONSHIP_LIST); @@ -417,9 +591,67 @@ public class RestUtil { return relatedLinkList; } + /* + * Return the Map with key: Vfmodule->model-version-id + Vfmoduel->model-invariant-id + * with value: list of the related-link based on the catalog + * The catalog can be "vserver" or "l3-network" based on common model requirement. + */ + private static Map> extractRelatedLinkFromVfmodule(String payload, String catalog) throws AuditException { + + Map> vServerRelatedLinkMap = new HashMap>(); + + JSONObject jsonPayload = new JSONObject(payload); + JSONArray vfmoduleArray = null; + JSONArray relationships = null; + + try { + log.debug("Fetching the Vf-module"); + JSONObject vfmodules = jsonPayload.getJSONObject(VF_MODULES); + if (vfmodules != null) { + vfmoduleArray = vfmodules.getJSONArray(VF_MODULE); + } + + if (vfmoduleArray != null && vfmoduleArray.length() > 0) { + for (int i = 0; i < vfmoduleArray.length(); i++) { + List relatedLinkList = new ArrayList(); + JSONObject obj = vfmoduleArray.optJSONObject(i); + String key = (String)obj.get("model-version-id") + DELIMITER + (String)obj.get("model-invariant-id"); + + log.debug("Fetching the relationship"); + JSONObject relationshipList = obj.getJSONObject(RELATIONSHIP_LIST); + if (relationshipList != null) { + relationships = relationshipList.getJSONArray(RELATIONSHIP); + } + if (relationships != null && relationships.length() > 0) { + for (int j = 0; j < relationships.length(); j++) { + Object relatedToObj = null; + Object relatedLinkObj = null; + + JSONObject obj2 = relationships.optJSONObject(j); + relatedToObj = obj2.get(JSON_ATT_RELATED_TO); + + if (relatedToObj.toString().equals(catalog)) { + relatedLinkObj = obj2.get(JSON_ATT_RELATED_LINK); + if (relatedLinkObj != null) { + relatedLinkList.add(relatedLinkObj.toString()); + } + } + } //relationship + } + + vServerRelatedLinkMap.put(key, relatedLinkList); + } //vf-module + } + + } catch (Exception e) { + log.error(e.getMessage()); + throw new AuditException(AuditError.JSON_READER_PARSE_ERROR + " " + e.getMessage()); + } + + return vServerRelatedLinkMap; + } - @SuppressWarnings("unchecked") private static Map> buildHeaders(String aaiBasicAuthorization, String transactionId) { MultivaluedMap headers = new MultivaluedMapImpl(); headers.put(TRANSACTION_ID, Collections.singletonList(transactionId)); @@ -446,11 +678,6 @@ public class RestUtil { } - private static String generateServiceInstanceURL(String siPath, String customerId, String serviceType, - String serviceInstanceId) { - return MessageFormat.format(siPath, customerId, serviceType, serviceInstanceId); - } - public static String obtainResouceLinkBasedOnServiceInstanceFromAAI(RestClient aaiClient, String baseURL, String aaiPathToSearchNodeQuery, String serviceInstanceId, String transactionId, String aaiBasicAuthorization) throws AuditException { @@ -467,7 +694,7 @@ public class RestUtil { return extractResourceLinkBasedOnResourceType(customerInfoString, CATALOG_SERVICE_INSTANCE); } - private static String generateGetCustomerInfoUrl (String baseURL, String aaiPathToSearchNodeQuery ,String serviceInstanceId) { + private static String generateGetCustomerInfoUrl (String baseURL, String aaiPathToSearchNodeQuery,String serviceInstanceId) { return baseURL + aaiPathToSearchNodeQuery + serviceInstanceId; } -- cgit 1.2.3-korg