2 * ===============LICENSE_START=======================================================
4 * ===================================================================================
5 * Copyright (C) 2017-2019 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
6 * ===================================================================================
7 * This Acumos software file is distributed by AT&T and Tech Mahindra
8 * under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * This file is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ===============LICENSE_END=========================================================
21 package org.acumos.federation.gateway.adapter.onap;
23 import java.io.ByteArrayInputStream;
24 import java.io.ByteArrayOutputStream;
26 import java.time.Instant;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.HashMap;
30 import java.util.LinkedList;
31 import java.util.List;
33 import java.util.UUID;
35 import javax.annotation.PostConstruct;
36 import javax.annotation.PreDestroy;
38 import org.acumos.cds.domain.MLPArtifact;
39 import org.acumos.cds.domain.MLPPeer;
40 import org.acumos.cds.domain.MLPPeerSubscription;
41 import org.acumos.cds.domain.MLPSolution;
42 import org.acumos.cds.domain.MLPSolutionRevision;
43 import org.acumos.federation.gateway.adapter.onap.sdc.ASDC;
44 import org.acumos.federation.gateway.adapter.onap.sdc.ASDC.ArtifactGroupType;
45 import org.acumos.federation.gateway.adapter.onap.sdc.ASDC.ArtifactType;
46 import org.acumos.federation.gateway.adapter.onap.sdc.ASDC.AssetType;
47 import org.acumos.federation.gateway.adapter.onap.sdc.ASDC.LifecycleState;
48 import org.acumos.federation.gateway.adapter.onap.sdc.ASDCException;
49 import org.acumos.federation.gateway.cds.ArtifactTypes;
50 import org.acumos.federation.gateway.common.Clients;
51 import org.acumos.federation.gateway.common.FederationClient;
52 import org.acumos.federation.gateway.event.PeerSubscriptionEvent;
53 import org.acumos.federation.gateway.util.Utils;
54 import org.json.JSONArray;
55 import org.json.JSONException;
56 import org.json.JSONObject;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59 import org.springframework.beans.factory.BeanInitializationException;
60 import org.springframework.beans.factory.annotation.Autowired;
61 import org.springframework.boot.context.properties.ConfigurationProperties;
62 import org.springframework.context.annotation.Conditional;
63 import org.springframework.context.annotation.Scope;
64 import org.springframework.context.event.EventListener;
65 import org.springframework.core.task.TaskExecutor;
66 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
67 import org.springframework.stereotype.Component;
68 import org.springframework.util.StreamUtils;
72 @ConfigurationProperties(prefix = "onap")
73 @Conditional(ONAPAdapterCondition.class)
76 private final Logger log = LoggerFactory.getLogger(ONAP.class);
77 private ASDC asdc = new ASDC();
78 private String asdcOperator;
79 private TaskExecutor taskExecutor;
80 private ToscaLab toscalab = new ToscaLab();
82 private Clients clients;
85 log.debug("ONAP::new");
88 public void setSdcUri(URI theUri) {
89 this.asdc.setUri(theUri);
92 public void setSdcRootPath(String thePath) {
93 this.asdc.setRootPath(thePath);
96 public void setSdcOperator(String theUid) {
97 this.asdcOperator = theUid;
101 public void initOnap() {
102 log.trace("initOnap");
104 if (this.asdc.getUri() == null)
105 throw new BeanInitializationException("Forgot to configure the SDC uri ('onap.sdcUri') ??");
106 if (this.asdcOperator == null)
107 throw new BeanInitializationException("Forgot to configure the SDC user ('onap.sdcOperator) ??");
109 this.taskExecutor = new ThreadPoolTaskExecutor();
110 ((ThreadPoolTaskExecutor) this.taskExecutor).setCorePoolSize(1);
111 ((ThreadPoolTaskExecutor) this.taskExecutor).setMaxPoolSize(1);
112 ((ThreadPoolTaskExecutor) this.taskExecutor).setQueueCapacity(25);
113 ((ThreadPoolTaskExecutor) this.taskExecutor).initialize();
119 log.trace("Onap available");
123 public void cleanupOnap() {
124 log.trace("Onap destroyed");
128 public void handlePeerSubscriptionUpdate(PeerSubscriptionEvent theEvent) {
129 log.info("received peer subscription update event {}", theEvent);
130 taskExecutor.execute(new ONAPPushTask(theEvent.getPeer(), theEvent.getSubscription()));
133 public class ONAPPushTask implements Runnable {
135 private MLPPeer peer;
136 private MLPPeerSubscription sub;
138 public ONAPPushTask(MLPPeer thePeer, MLPPeerSubscription theSub) {
145 // list with category and subcategory currently used for onap
146 // more dynamic mapping to come: based on solution information it will provide
147 // sdc assettype, category and subcategory
150 String catalogId = null;
152 selector = Utils.jsonStringToMap(this.sub.getSelector());
153 catalogId = (String)selector.get("catalogId");
156 log.error("Failed to parse selector for subscription {}", this.sub);
159 Instant lastProcessed = this.sub.getProcessed();
160 if (lastProcessed != null) {
161 selector.put("modified", lastProcessed.getEpochSecond());
163 lastProcessed = Instant.now();
165 FederationClient acumosClient = clients.getFederationClient(this.peer.getApiUrl());
166 if (acumosClient == null) {
167 log.error("Failed to get client for peer {}", this.peer);
171 List<MLPSolution> acumosSolutions = null;
173 acumosSolutions = (List<MLPSolution>)acumosClient.getSolutions(catalogId).getContent();
176 log.error("Processing peer " + this.peer + " subscription " + this.sub.getSubId() + ": getSolutions failed.", x);
179 log.info("Processing peer {} subscription {}, {} yielded solutions {}", this.peer, this.sub.getSubId(), selector, acumosSolutions);
181 JSONArray sdcAssets = null;
183 sdcAssets = asdc.getAssets(AssetType.resource, JSONArray.class, "Generic", "Abstract");
185 catch (Exception x) {
186 log.error("Failed to list ONAP SDC assets: " + x.getCause(), x);
187 // if this is a 404 NotFound, continue, otherwise, fail
188 if (ASDCException.isNotFound(x))
189 sdcAssets = new JSONArray();
193 log.info("Mapping received Acumos solutions \n{}\n to retrieved ONAP SDC assets \n{}",
194 acumosSolutions, sdcAssets);
196 for (MLPSolution acumosSolution : acumosSolutions) {
198 List<MLPSolutionRevision> acumosRevisions = null;
200 acumosRevisions = acumosClient
201 .getSolutionRevisions(acumosSolution.getSolutionId()).getContent();
203 catch (Exception x) {
204 log.error("Failed to retrieve acumos revisions", x);
207 sortAcumosSolutionRevisions(acumosRevisions);
210 // does the solution already exist in sdc
211 JSONObject sdcAsset = lookupSdcAsset(acumosSolution, sdcAssets);
212 if (sdcAsset == null) {
214 sdcAsset = createSdcAsset(acumosSolution, acumosRevisions.get(acumosRevisions.size()-1));
217 sdcAsset = updateSdcAsset(sdcAsset, acumosSolution, acumosRevisions);
219 updateAssetArtifacts(sdcAsset, acumosSolution, acumosRevisions);
221 catch (Exception x) {
222 log.error("Mapping of acumos solution failed for: " + acumosSolution + ": " + x);
227 public JSONObject lookupSdcAsset(MLPSolution theSolution, JSONArray theAssets) {
228 if (theAssets == null || theAssets.length() == 0)
230 for (int i = 0; i < theAssets.length(); i++) {
231 JSONObject asset = theAssets.optJSONObject(i);
232 if (sameId(theSolution, asset))
238 public JSONObject createSdcAsset(MLPSolution theSolution, MLPSolutionRevision theRevision) throws Exception {
239 log.info("Creating ONAP SDC VF for solution {}", theSolution);
242 return ONAP.this.asdc.createVF()
243 .withCategory("Generic")
244 .withSubCategory("Abstract")
245 .withName(theSolution.getName() + "-" + theSolution.getSolutionId()) // sdc names are unique,
246 // acumos ones not so
247 .withDescription(theSolution.getTags().toString()) //the actual description has moved and is too large to fit in here
248 .withVendorName("Acumos")
249 .withVendorRelease(theRevision.getVersion()) //is this meaningful ? given that it cannot be updated ..
250 .withTags("acumos", theSolution.getSolutionId()) // can I fit an UUID as tag ??
251 .withOperator(ONAP.this.asdcOperator/* theSolution.getOwnerId() */) // probably won't work, SDC
252 // expects a user uuid
255 catch (Exception x) {
256 log.error("Failed to create ONAP SDC VF", x);
262 * There is no such thing as updating an asset in the ASDC REST API, we can only
263 * update the artifacts ..
265 * @param theAssetInfo
269 * @param theRevisions revision
270 * @return SDC Asset info
272 public JSONObject updateSdcAsset(JSONObject theAssetInfo, MLPSolution theSolution, List<MLPSolutionRevision> theRevisions) {
273 log.info("Updating ONAP SDC VF {} for Acumos solution {}", theAssetInfo.optString("uuid"), theSolution);
277 public void updateAssetArtifacts(JSONObject theAssetInfo, MLPSolution theSolution, List<MLPSolutionRevision> theRevisions)
280 theAssetInfo = ONAP.this.asdc
281 .getAsset(AssetType.resource, UUID.fromString(theAssetInfo.getString("uuid")), JSONObject.class);
283 catch (Exception x) {
284 log.error("Failed to retrieve ONAP SDC asset metadata for " + theAssetInfo.getString("uuid") + " : " + x);
288 JSONArray sdcArtifacts = theAssetInfo.optJSONArray("artifacts");
289 if (sdcArtifacts == null) {
290 sdcArtifacts = new JSONArray();
293 //we could have a new model, a new model revision or updates to the currently mapped revision's artifacts.
294 //currently we always fast-forward to the latest revision available in acumos
295 MLPSolutionRevision mappedAcumosRevision = theRevisions.get(theRevisions.size() - 1);
297 List<MLPArtifact> acumosArtifacts = null;
299 acumosArtifacts = clients.getFederationClient(this.peer.getApiUrl())
300 .getArtifacts(theSolution.getSolutionId(), mappedAcumosRevision.getRevisionId())
303 catch (Exception x) {
304 log.error("Failed to retrieve acumos artifacts" + x);
308 if (acumosArtifacts == null)
309 acumosArtifacts = new LinkedList<>();
311 //add an artifact to be mapped for revision tracking purposes
313 MLPArtifact mapper = new MLPArtifact(mappedAcumosRevision.getVersion(),
314 ArtifactTypes.Metadata.getCode(),
317 "", //owner: never sees CDS so irrelevant
319 mapper.setArtifactId("0");//a unique value among the other artifacts of this revision
320 acumosArtifacts.add(mapper);
323 // all this could be better writen but the 2 sets are expected to be small so we
326 //!! we support a 1-to-n mapping of artifacts from Acumos to SDC
328 // acumos artifacts that do not exist locally need to be added
329 List<MLPArtifact> newArtifacts = new LinkedList<>();
330 Map<MLPArtifact, JSONArray> updatedArtifacts = new HashMap<>();
332 log.info("Acumos artifacts: {}", acumosArtifacts);
333 log.info("SDC artifacts: {}", sdcArtifacts);
335 for (MLPArtifact acumosArtifact : acumosArtifacts) {
336 JSONArray sdcMappedArtifacts = new JSONArray();
337 for (int i = 0; i < sdcArtifacts.length(); i++) {
338 JSONObject sdcArtifact = sdcArtifacts.getJSONObject(i);
339 if (sameId(acumosArtifact, sdcArtifact)) {
340 sdcMappedArtifacts.put(sdcArtifact);
344 if (sdcMappedArtifacts.length() > 0) {
345 //sdc artifacts mapped to the acumos artifacts were found
346 //if not at the same version, update
347 //TODO: add a coherence check to make sure all sdcArtifacts are at the same (acumos) version
348 if (!sameVersion(acumosArtifact, sdcMappedArtifacts.getJSONObject(0))) {
349 updatedArtifacts.put(acumosArtifact, sdcMappedArtifacts);
353 newArtifacts.add(acumosArtifact);
357 log.info("New artifacts: {}", newArtifacts);
358 for (MLPArtifact acumosArtifact : newArtifacts) {
360 for (ASDC.ArtifactUploadAction uploadAction:
361 mapNewArtifact(theAssetInfo, theSolution.getSolutionId(), mappedAcumosRevision.getRevisionId(),
366 catch (Exception x) {
367 log.error("Failed to create ONAP SDC VF Artifacts for " + acumosArtifact, x);
371 log.warn("Updated SDC artifacts: {}", updatedArtifacts.keySet());
372 for (Map.Entry<MLPArtifact, JSONArray> updateEntry : updatedArtifacts.entrySet()) {
373 MLPArtifact acumosArtifact = updateEntry.getKey();
375 for (ASDC.ArtifactUpdateAction updateAction:
376 mapArtifact(theAssetInfo, theSolution.getSolutionId(), mappedAcumosRevision.getRevisionId(),
377 updateEntry.getKey(), updateEntry.getValue())) {
381 catch (Exception x) {
382 log.error("Failed to update ONAP SDC VF Artifact for " + updateEntry.getKey(), x);
386 // sdc artifacts that do not have a acumos counterpart should be deleted (if
387 // they are labeled as having
388 // originated in acumos).
389 List<JSONObject> deletedArtifacts = new LinkedList<>();
390 for (int i = 0; i < sdcArtifacts.length(); i++) {
391 JSONObject sdcArtifact = sdcArtifacts.getJSONObject(i);
392 boolean found = false;
393 for (MLPArtifact acumosArtifact : acumosArtifacts) {
394 if (sameId(acumosArtifact, sdcArtifact)) {
399 if (!found && isAcumosOriginated(sdcArtifact)) {
400 deletedArtifacts.add(sdcArtifact);
403 log.warn("Deleted SDC artifacts: {}", deletedArtifacts);
404 for (JSONObject sdcArtifact : deletedArtifacts) {
406 asdc.deleteAssetArtifact(AssetType.resource, UUID.fromString(theAssetInfo.getString("uuid")),
407 UUID.fromString(sdcArtifact.getString("artifactUUID"))).withOperator(ONAP.this.asdcOperator)
410 catch (Exception x) {
411 log.error("Failed to delete ONAP SDC VF Artifact", x);
418 private List<ASDC.ArtifactUploadAction> mapNewArtifact(
419 JSONObject theSDCAsset,
420 String theAcumosSolutionId, String theAcumosRevisionId, MLPArtifact theAcumosArtifact) {
422 if (isDCAEComponentSpecification(theAcumosArtifact)) {
424 byte[] content = null;
426 content = retrieveContent(theAcumosSolutionId, theAcumosRevisionId, theAcumosArtifact);
428 catch (Exception x) {
429 log.error("Failed to retrieve Acumoms artifact content from " + theAcumosArtifact.getUri(), x);
430 return Collections.emptyList();
433 JSONObject models = null;
435 models = new JSONObject(toscalab.create_model(new ByteArrayInputStream(content)));
437 catch (JSONException jsonx) {
438 log.error("Failed to parse toscalab output", jsonx);
439 return Collections.emptyList();
441 catch (Exception x) {
442 log.error("Failed to process DCAE component specification from " + theAcumosArtifact, x);
443 return Collections.emptyList();
446 List<ASDC.ArtifactUploadAction> actions = new LinkedList<>();
447 for (String model: models.keySet()) {
449 asdc.createAssetArtifact(AssetType.resource, UUID.fromString(theSDCAsset.getString("uuid")))
450 .withOperator(ONAP.this.asdcOperator)
451 .withEncodedContent(models.getString(model))
452 .withLabel(theAcumosArtifact.getArtifactTypeCode())
453 .withName(model/*theAcumosArtifact.getName()*/)
454 .withDisplayName(theAcumosArtifact.getMetadata())
455 .withType(ArtifactType.DCAE_TOSCA/*ArtifactType.OTHER*/)
456 .withGroupType(ArtifactGroupType.DEPLOYMENT)
457 .withDescription(theAcumosArtifact.getArtifactId() + "@" + theAcumosArtifact.getVersion())
462 else if (isMapper(theAcumosArtifact)) {
463 return Collections.singletonList(
464 asdc.createAssetArtifact(AssetType.resource, UUID.fromString(theSDCAsset.getString("uuid")))
465 .withOperator(ONAP.this.asdcOperator)
466 .withContent("{}".getBytes())
467 .withLabel(theAcumosArtifact.getArtifactTypeCode())
468 .withName(theAcumosArtifact.getName())
469 .withDisplayName("mapper")
470 .withType(ArtifactType.OTHER)
471 .withGroupType(ArtifactGroupType.DEPLOYMENT)
472 .withDescription(theAcumosArtifact.getArtifactId() + "@" + theAcumosArtifact.getVersion())
476 //everything else gets ignored at this point
477 return Collections.emptyList();
481 private List<ASDC.ArtifactUpdateAction> mapArtifact(
482 JSONObject theSDCAsset, String theAcumosSolutionId, String theAcumosRevisionId,
483 MLPArtifact theAcumosArtifact, JSONArray theSDCArtifacts) {
485 if (isDCAEComponentSpecification(theAcumosArtifact)) {
486 byte[] content = null;
488 content = retrieveContent(theAcumosSolutionId, theAcumosRevisionId, theAcumosArtifact);
490 catch (Exception x) {
491 log.error("Failed to retrieve Acumoms artifact content from " + theAcumosArtifact.getUri(), x);
492 return Collections.emptyList();
495 JSONObject models = null;
497 models = new JSONObject(toscalab.create_model(new ByteArrayInputStream(content)));
499 catch (JSONException jsonx) {
500 log.error("Failed to parse toscalab output", jsonx);
501 return Collections.emptyList();
503 catch (Exception x) {
504 log.error("Failed to process DCAE component specification from " + theAcumosArtifact, x);
505 return Collections.emptyList();
508 List<ASDC.ArtifactUpdateAction> actions = new LinkedList<>();
509 for (int i = 0; i < theSDCArtifacts.length(); i++) {
510 JSONObject sdcArtifact = theSDCArtifacts.getJSONObject(i);
512 asdc.updateAssetArtifact(AssetType.resource, UUID.fromString(theSDCAsset.getString("uuid")), sdcArtifact)
513 .withOperator(ONAP.this.asdcOperator)
514 .withEncodedContent(models.getString(sdcArtifact.getString("name")))
515 .withName(sdcArtifact.getString("name"))
516 .withDescription(theAcumosArtifact.getArtifactId() + "@" + theAcumosArtifact.getVersion())
521 else if (isMapper(theAcumosArtifact)) {
522 if (theSDCArtifacts.length() != 1)
523 log.warn("Found more than one mapper artifact {}", theSDCArtifacts);
524 return Collections.singletonList(
525 asdc.updateAssetArtifact(AssetType.resource, UUID.fromString(theSDCAsset.getString("uuid")), theSDCArtifacts.getJSONObject(0))
526 .withOperator(ONAP.this.asdcOperator)
527 .withName(theAcumosArtifact.getName())
528 .withDescription(theAcumosArtifact.getArtifactId() + "@" + theAcumosArtifact.getVersion()));
531 log.error("Found sdc artifacts for mlp artifact we do not process {}: {} ", theAcumosArtifact, theSDCArtifacts);
532 return Collections.emptyList();
536 private boolean isDCAEComponentSpecification(MLPArtifact theArtifact) {
537 return theArtifact.getName().equals("component-specification.json");
540 private boolean isMapper(MLPArtifact theArtifact) {
541 return theArtifact.getName().equals("mapper");
544 private boolean sameId(MLPSolution theAcumosSolution, JSONObject theSDCAsset) {
546 return theSDCAsset.optString("name", "")
547 .equals(theAcumosSolution.getName() + "-" + theAcumosSolution.getSolutionId());
550 private boolean sameId(MLPArtifact theAcumosArtifact, JSONObject theSDCArtifact) {
551 return acumosArtifactId(theSDCArtifact).equals(theAcumosArtifact.getArtifactId());
555 * Only safe to call if 'same' returned true
557 private boolean sameVersion(MLPArtifact theAcumosArtifact, JSONObject theSDCArtifact) {
558 return acumosArtifactVersion(theSDCArtifact).equals(theAcumosArtifact.getVersion());
561 private String acumosArtifactId(JSONObject theSDCArtifact) {
562 return theSDCArtifact.optString("artifactDescription","@").split("@")[0];
565 private String acumosArtifactVersion(JSONObject theSDCArtifact) {
566 return theSDCArtifact.optString("artifactDescription","@").split("@")[1];
569 private boolean isAcumosOriginated(JSONObject theSDCArtifact) {
570 boolean isAcumos = theSDCArtifact.optString("artifactType").equals(ArtifactType.OTHER.toString())
571 && theSDCArtifact.optString("artifactGroupType").equals(ArtifactGroupType.DEPLOYMENT.toString());
572 String[] parts = theSDCArtifact.optString("artifactDescription", "@").split("@");
573 isAcumos &= (parts.length == 2); // and the first part can be parsed as an UUID
577 private byte[] retrieveContent(
578 String theAcumosSolutionId, String theAcumosRevisionId, MLPArtifact theAcumosArtifact)
580 if (this.peer.isLocal()) {
581 return clients.getNexusClient().getForObject(theAcumosArtifact.getUri(), byte[].class);
583 else { //non-local peer
584 ByteArrayOutputStream bos = new ByteArrayOutputStream();
586 clients.getFederationClient(this.peer.getApiUrl())
587 .getArtifactContent(theAcumosArtifact.getArtifactId())
590 return bos.toByteArray();
597 * Removes all (non-commited) Acumos solutions imported into ONAP SDC
599 protected void cleanup() {
601 JSONArray sdcAssets = null;
603 sdcAssets = asdc.getAssets(AssetType.resource, JSONArray.class, "Generic", "Abstract");
604 } catch (Throwable x) {
605 log.info("Cleanup failed to list ONAP SDC assets: " + x.getCause(), x);
608 if (sdcAssets == null)
611 for (int i = 0; i < sdcAssets.length(); i++) {
612 JSONObject sdcAsset = sdcAssets.optJSONObject(i);
613 String state = sdcAsset.optString("lifecycleState");
614 if (state != null && "NOT_CERTIFIED_CHECKEOUT".equals(state)) {
616 asdc.cycleAsset(AssetType.resource, UUID.fromString(sdcAsset.getString("uuid")),
617 LifecycleState.undocheckout, ONAP.this.asdcOperator, null);
619 catch (Exception x) {
620 log.error("Cleanup ONAP SDC asset: " + sdcAsset.optString("uuid"), x);
628 private void sortAcumosSolutionRevisions(List<MLPSolutionRevision> theRevisions) {
630 Collections.sort(theRevisions,
631 new Comparator<MLPSolutionRevision>() {
633 public int compare(MLPSolutionRevision theFirst, MLPSolutionRevision theSecond) {
634 return String.CASE_INSENSITIVE_ORDER.compare(theFirst.getVersion(), theSecond.getVersion());