2 * ===============LICENSE_START=======================================================
4 * ===================================================================================
5 * Copyright (C) 2017 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
151 selector = Utils.jsonStringToMap(this.sub.getSelector());
154 log.error("Failed to parse selector for subscription {}", this.sub);
157 Instant lastProcessed = this.sub.getProcessed();
158 if (lastProcessed != null) {
159 selector.put("modified", lastProcessed.getEpochSecond());
161 lastProcessed = Instant.now();
163 FederationClient acumosClient = clients.getFederationClient(this.peer.getApiUrl());
164 if (acumosClient == null) {
165 log.error("Failed to get client for peer {}", this.peer);
169 List<MLPSolution> acumosSolutions = null;
171 acumosSolutions = (List)acumosClient.getSolutions(selector).getContent();
174 log.error("Processing peer " + this.peer + " subscription " + this.sub.getSubId() + ": getSolutions failed.", x);
177 log.info("Processing peer {} subscription {}, {} yielded solutions {}", this.peer, this.sub.getSubId(), selector, acumosSolutions);
179 JSONArray sdcAssets = null;
181 sdcAssets = asdc.getAssets(AssetType.resource, JSONArray.class, "Generic", "Abstract").waitForResult();
183 catch (Exception x) {
184 log.error("Failed to list ONAP SDC assets: " + x.getCause(), x);
185 // if this is a 404 NotFound, continue, otherwise, fail
186 if (ASDCException.isNotFound(x))
187 sdcAssets = new JSONArray();
191 log.info("Mapping received Acumos solutions \n{}\n to retrieved ONAP SDC assets \n{}",
192 acumosSolutions, sdcAssets);
194 for (MLPSolution acumosSolution : acumosSolutions) {
196 List<MLPSolutionRevision> acumosRevisions = null;
198 acumosRevisions = (List<MLPSolutionRevision>) acumosClient
199 .getSolutionRevisions(acumosSolution.getSolutionId()).getContent();
201 catch (Exception x) {
202 log.error("Failed to retrieve acumos revisions", x);
205 sortAcumosSolutionRevisions(acumosRevisions);
208 // does the solution already exist in sdc
209 JSONObject sdcAsset = lookupSdcAsset(acumosSolution, sdcAssets);
210 if (sdcAsset == null) {
212 sdcAsset = createSdcAsset(acumosSolution, acumosRevisions.get(acumosRevisions.size()-1));
215 // ONAP.this.asdc.checkoutResource(UUID.fromString(sdcAsset.getString("artifactUUID")),
216 // ONAP.this.asdcOperator, "updated solution import");
217 sdcAsset = updateSdcAsset(sdcAsset, acumosSolution, acumosRevisions);
219 updateAssetArtifacts(sdcAsset, acumosSolution, acumosRevisions);
220 // ONAP.this.asdc.checkinResource(UUID.fromString(sdcAsset.getString("artifactUUID")),
221 // ONAP.this.asdcOperator, "solution imported " + " the acumos revision number
224 catch (Exception x) {
225 log.error("Mapping of acumos solution failed for: " + acumosSolution + ": " + x);
230 public JSONObject lookupSdcAsset(MLPSolution theSolution, JSONArray theAssets) {
231 if (theAssets == null || theAssets.length() == 0)
233 for (int i = 0; i < theAssets.length(); i++) {
234 JSONObject asset = theAssets.optJSONObject(i);
235 if (sameId(theSolution, asset))
241 public JSONObject createSdcAsset(MLPSolution theSolution, MLPSolutionRevision theRevision) throws Exception {
242 log.info("Creating ONAP SDC VF for solution " + theSolution);
245 return ONAP.this.asdc.createVF()
246 .withCategory("Generic")
247 .withSubCategory("Abstract")
248 .withName(theSolution.getName() + "-" + theSolution.getSolutionId()) // sdc names are unique,
249 // acumos ones not so
250 .withDescription(theSolution.getTags().toString()) //the actual description has moved and is too large to fit in here
251 .withVendorName("Acumos")
252 .withVendorRelease(theRevision.getVersion()) //is this meaningful ? given that it cannot be updated ..
253 .withTags("acumos", theSolution.getSolutionId()) // can I fit an UUID as tag ??
254 .withOperator(ONAP.this.asdcOperator/* theSolution.getOwnerId() */) // probably won't work, SDC
255 // expects an att uuid
256 .execute().waitForResult();
258 catch (Exception x) {
259 log.error("Failed to create ONAP SDC VF", x);
265 * There is no such thing as updating an asset in the ASDC REST API, we can only
266 * update the artifacts ..
268 * @param theAssetInfo
272 * @param theRevisions revision
273 * @return SDC Asset info
275 public JSONObject updateSdcAsset(JSONObject theAssetInfo, MLPSolution theSolution, List<MLPSolutionRevision> theRevisions) {
276 log.info("Updating ONAP SDC VF " + theAssetInfo.optString("uuid") + " for Acumos solution " + theSolution);
280 public void updateAssetArtifacts(JSONObject theAssetInfo, MLPSolution theSolution, List<MLPSolutionRevision> theRevisions)
283 theAssetInfo = ONAP.this.asdc
284 .getAsset(AssetType.resource, UUID.fromString(theAssetInfo.getString("uuid")), JSONObject.class)
287 catch (Exception x) {
288 log.error("Failed to retrieve ONAP SDC asset metadata for " + theAssetInfo.getString("uuid") + " : " + x);
292 JSONArray sdcArtifacts = theAssetInfo.optJSONArray("artifacts");
293 if (sdcArtifacts == null) {
294 sdcArtifacts = new JSONArray();
297 //we could have a new model, a new model revision or updates to the currently mapped revision's artifacts.
298 //currently we always fast-forward to the latest revision available in acumos
299 MLPSolutionRevision mappedAcumosRevision = theRevisions.get(theRevisions.size() - 1);
301 List<MLPArtifact> acumosArtifacts = null;
303 acumosArtifacts = (List<MLPArtifact>) clients.getFederationClient(this.peer.getApiUrl())
304 .getArtifacts(theSolution.getSolutionId(), mappedAcumosRevision.getRevisionId())
307 catch (Exception x) {
308 log.error("Failed to retrieve acumos artifacts" + x);
312 if (acumosArtifacts == null)
313 acumosArtifacts = new LinkedList<MLPArtifact>();
315 //add an artifact to be mapped for revision tracking purposes
317 MLPArtifact mapper = new MLPArtifact(mappedAcumosRevision.getVersion(),
318 ArtifactTypes.Metadata.getCode(),
321 "", //owner: never sees CDS so irrelevant
323 mapper.setArtifactId("0");//a unique value among the other artifacts of this revision
324 acumosArtifacts.add(mapper);
327 // all this could be better writen but the 2 sets are expected to be small so we
330 //!! we support a 1-to-n mapping of artifacts from Acumos to SDC
332 // acumos artifacts that do not exist locally need to be added
333 List<MLPArtifact> newArtifacts = new LinkedList<MLPArtifact>();
334 Map<MLPArtifact, JSONArray> updatedArtifacts = new HashMap<MLPArtifact, JSONArray>();
335 // List<JSONObject> oldArtifacts = new LinkedList<JSONObject>();
337 log.info("Acumos artifacts: " + acumosArtifacts);
338 log.info("SDC artifacts: " + sdcArtifacts);
340 for (MLPArtifact acumosArtifact : acumosArtifacts) {
341 JSONArray sdcMappedArtifacts = new JSONArray();
342 for (int i = 0; i < sdcArtifacts.length(); i++) {
343 JSONObject sdcArtifact = sdcArtifacts.getJSONObject(i);
344 if (sameId(acumosArtifact, sdcArtifact)) {
345 sdcMappedArtifacts.put(sdcArtifact);
349 if (sdcMappedArtifacts.length() > 0) {
350 //sdc artifacts mapped to the acumos artifacts were found
351 //if not at the same version, update
352 //TODO: add a coherence check to make sure all sdcArtifacts are at the same (acumos) version
353 if (!sameVersion(acumosArtifact, sdcMappedArtifacts.getJSONObject(0))) {
354 updatedArtifacts.put(acumosArtifact, sdcMappedArtifacts);
358 newArtifacts.add(acumosArtifact);
362 log.info("New artifacts: " + newArtifacts);
363 for (MLPArtifact acumosArtifact : newArtifacts) {
365 for (ASDC.ArtifactUploadAction uploadAction:
366 mapNewArtifact(theAssetInfo, theSolution.getSolutionId(), mappedAcumosRevision.getRevisionId(),
368 uploadAction.execute().waitForResult();
371 catch (Exception x) {
372 log.error("Failed to create ONAP SDC VF Artifacts for " + acumosArtifact, x);
376 log.warn("Updated SDC artifacts: " + updatedArtifacts.keySet());
377 for (Map.Entry<MLPArtifact, JSONArray> updateEntry : updatedArtifacts.entrySet()) {
378 MLPArtifact acumosArtifact = updateEntry.getKey();
380 for (ASDC.ArtifactUpdateAction updateAction:
381 mapArtifact(theAssetInfo, theSolution.getSolutionId(), mappedAcumosRevision.getRevisionId(),
382 updateEntry.getKey(), updateEntry.getValue())) {
383 updateAction.execute().waitForResult();
386 catch (Exception x) {
387 log.error("Failed to update ONAP SDC VF Artifact for " + updateEntry.getKey(), x);
391 // sdc artifacts that do not have a acumos counterpart should be deleted (if
392 // they are labeled as having
393 // originated in acumos).
394 List<JSONObject> deletedArtifacts = new LinkedList<JSONObject>();
395 for (int i = 0; i < sdcArtifacts.length(); i++) {
396 JSONObject sdcArtifact = sdcArtifacts.getJSONObject(i);
397 boolean found = false;
398 for (MLPArtifact acumosArtifact : acumosArtifacts) {
399 if (sameId(acumosArtifact, sdcArtifact)) {
404 if (!found && isAcumosOriginated(sdcArtifact)) {
405 deletedArtifacts.add(sdcArtifact);
408 log.warn("Deleted SDC artifacts: " + deletedArtifacts);
409 for (JSONObject sdcArtifact : deletedArtifacts) {
411 asdc.deleteAssetArtifact(AssetType.resource, UUID.fromString(theAssetInfo.getString("uuid")),
412 UUID.fromString(sdcArtifact.getString("artifactUUID"))).withOperator(ONAP.this.asdcOperator)
413 .execute().waitForResult();
415 catch (Exception x) {
416 log.error("Failed to delete ONAP SDC VF Artifact", x);
423 private List<ASDC.ArtifactUploadAction> mapNewArtifact(
424 JSONObject theSDCAsset,
425 String theAcumosSolutionId, String theAcumosRevisionId, MLPArtifact theAcumosArtifact) {
427 if (isDCAEComponentSpecification(theAcumosArtifact)) {
429 byte[] content = null;
431 content = retrieveContent(theAcumosSolutionId, theAcumosRevisionId, theAcumosArtifact);
433 catch (Exception x) {
434 log.error("Failed to retrieve Acumoms artifact content from " + theAcumosArtifact.getUri(), x);
435 return Collections.EMPTY_LIST;
438 JSONObject models = null;
440 models = new JSONObject(toscalab.create_model(new ByteArrayInputStream(content)));
442 catch (JSONException jsonx) {
443 log.error("Failed to parse toscalab output", jsonx);
444 return Collections.EMPTY_LIST;
446 catch (Exception x) {
447 log.error("Failed to process DCAE component specification from " + theAcumosArtifact, x);
448 return Collections.EMPTY_LIST;
451 List<ASDC.ArtifactUploadAction> actions = new LinkedList<ASDC.ArtifactUploadAction>();
452 for (String model: models.keySet()) {
454 asdc.createAssetArtifact(AssetType.resource, UUID.fromString(theSDCAsset.getString("uuid")))
455 .withOperator(ONAP.this.asdcOperator)
456 .withEncodedContent(models.getString(model))
457 .withLabel(theAcumosArtifact.getArtifactTypeCode())
458 .withName(model/*theAcumosArtifact.getName()*/)
459 .withDisplayName(theAcumosArtifact.getMetadata())
460 .withType(ArtifactType.DCAE_TOSCA/*ArtifactType.OTHER*/)
461 .withGroupType(ArtifactGroupType.DEPLOYMENT)
462 .withDescription(theAcumosArtifact.getArtifactId() + "@" + theAcumosArtifact.getVersion())
467 else if (isMapper(theAcumosArtifact)) {
468 return Collections.singletonList(
469 asdc.createAssetArtifact(AssetType.resource, UUID.fromString(theSDCAsset.getString("uuid")))
470 .withOperator(ONAP.this.asdcOperator)
471 .withContent("{}".getBytes())
472 .withLabel(theAcumosArtifact.getArtifactTypeCode())
473 .withName(theAcumosArtifact.getName())
474 .withDisplayName("mapper")
475 .withType(ArtifactType.OTHER)
476 .withGroupType(ArtifactGroupType.DEPLOYMENT)
477 .withDescription(theAcumosArtifact.getArtifactId() + "@" + theAcumosArtifact.getVersion())
481 //everything else gets ignored at this point
482 return Collections.EMPTY_LIST;
486 private List<ASDC.ArtifactUpdateAction> mapArtifact(
487 JSONObject theSDCAsset, String theAcumosSolutionId, String theAcumosRevisionId,
488 MLPArtifact theAcumosArtifact, JSONArray theSDCArtifacts) {
490 if (isDCAEComponentSpecification(theAcumosArtifact)) {
491 byte[] content = null;
493 content = retrieveContent(theAcumosSolutionId, theAcumosRevisionId, theAcumosArtifact);
495 catch (Exception x) {
496 log.error("Failed to retrieve Acumoms artifact content from " + theAcumosArtifact.getUri(), x);
497 return Collections.EMPTY_LIST;
500 JSONObject models = null;
502 models = new JSONObject(toscalab.create_model(new ByteArrayInputStream(content)));
504 catch (JSONException jsonx) {
505 log.error("Failed to parse toscalab output", jsonx);
506 return Collections.EMPTY_LIST;
508 catch (Exception x) {
509 log.error("Failed to process DCAE component specification from " + theAcumosArtifact, x);
510 return Collections.EMPTY_LIST;
513 List<ASDC.ArtifactUpdateAction> actions = new LinkedList<ASDC.ArtifactUpdateAction>();
514 for (int i = 0; i < theSDCArtifacts.length(); i++) {
515 JSONObject sdcArtifact = theSDCArtifacts.getJSONObject(i);
517 asdc.updateAssetArtifact(AssetType.resource, UUID.fromString(theSDCAsset.getString("uuid")), sdcArtifact)
518 .withOperator(ONAP.this.asdcOperator)
519 .withEncodedContent(models.getString(sdcArtifact.getString("name")))
520 .withName(sdcArtifact.getString("name"))
521 .withDescription(theAcumosArtifact.getArtifactId() + "@" + theAcumosArtifact.getVersion())
526 else if (isMapper(theAcumosArtifact)) {
527 if (theSDCArtifacts.length() != 1)
528 log.warn("Found more than one mapper artifact {}", theSDCArtifacts);
529 return Collections.singletonList(
530 asdc.updateAssetArtifact(AssetType.resource, UUID.fromString(theSDCAsset.getString("uuid")), theSDCArtifacts.getJSONObject(0))
531 .withOperator(ONAP.this.asdcOperator)
532 .withName(theAcumosArtifact.getName())
533 .withDescription(theAcumosArtifact.getArtifactId() + "@" + theAcumosArtifact.getVersion()));
536 log.error("Found sdc artifacts for mlp artifact we do not process {}: {} ", theAcumosArtifact, theSDCArtifacts);
537 return Collections.EMPTY_LIST;
541 private boolean isDCAEComponentSpecification(MLPArtifact theArtifact) {
542 return theArtifact.getName().equals("component-specification.json");
545 private boolean isMapper(MLPArtifact theArtifact) {
546 return theArtifact.getName().equals("mapper");
549 private boolean sameId(MLPSolution theAcumosSolution, JSONObject theSDCAsset) {
551 return theSDCAsset.optString("name", "")
552 .equals(theAcumosSolution.getName() + "-" + theAcumosSolution.getSolutionId());
555 private boolean sameId(MLPArtifact theAcumosArtifact, JSONObject theSDCArtifact) {
556 return acumosArtifactId(theSDCArtifact).equals(theAcumosArtifact.getArtifactId());
560 * Only safe to call if 'same' returned true
562 private boolean sameVersion(MLPArtifact theAcumosArtifact, JSONObject theSDCArtifact) {
563 return acumosArtifactVersion(theSDCArtifact).equals(theAcumosArtifact.getVersion());
566 private String acumosArtifactId(JSONObject theSDCArtifact) {
567 return theSDCArtifact.optString("artifactDescription","@").split("@")[0];
570 private String acumosArtifactVersion(JSONObject theSDCArtifact) {
571 return theSDCArtifact.optString("artifactDescription","@").split("@")[1];
574 private boolean isAcumosOriginated(JSONObject theSDCArtifact) {
575 boolean isAcumos = theSDCArtifact.optString("artifactType").equals(ArtifactType.OTHER.toString())
576 && theSDCArtifact.optString("artifactGroupType").equals(ArtifactGroupType.DEPLOYMENT.toString());
577 String[] parts = theSDCArtifact.optString("artifactDescription", "@").split("@");
578 isAcumos &= (parts.length == 2); // and the first part can be parsed as an UUID
582 private byte[] retrieveContent(
583 String theAcumosSolutionId, String theAcumosRevisionId, MLPArtifact theAcumosArtifact)
585 if (this.peer.isLocal()) {
586 return clients.getNexusClient().getForObject(theAcumosArtifact.getUri(), byte[].class);
588 else { //non-local peer
589 ByteArrayOutputStream bos = new ByteArrayOutputStream();
591 clients.getFederationClient(this.peer.getApiUrl())
592 .getArtifactContent(theAcumosSolutionId, theAcumosRevisionId, theAcumosArtifact.getArtifactId())
595 return bos.toByteArray();
598 // return IOUtils.toByteArray(new URI(theAcumosArtifact.getUri()));
605 * Removes all (non-commited) Acumos solutions imported into ONAP SDC
607 protected void cleanup() {
609 JSONArray sdcAssets = null;
611 sdcAssets = asdc.getAssets(AssetType.resource, JSONArray.class, "Generic", "Abstract").waitForResult();
612 } catch (Throwable x) {
613 log.info("Cleanup failed to list ONAP SDC assets: " + x.getCause(), x);
616 if (sdcAssets == null)
619 for (int i = 0; i < sdcAssets.length(); i++) {
620 JSONObject sdcAsset = sdcAssets.optJSONObject(i);
621 String state = sdcAsset.optString("lifecycleState");
622 if (state != null && "NOT_CERTIFIED_CHECKEOUT".equals(state)) {
624 asdc.cycleAsset(AssetType.resource, UUID.fromString(sdcAsset.getString("uuid")),
625 LifecycleState.undocheckout, ONAP.this.asdcOperator, null).waitForResult();
627 catch (Exception x) {
628 log.error("Cleanup ONAP SDC asset: " + sdcAsset.optString("uuid"), x);
636 private void sortAcumosSolutionRevisions(List<MLPSolutionRevision> theRevisions) {
638 Collections.sort(theRevisions,
639 new Comparator<MLPSolutionRevision>() {
641 public int compare(MLPSolutionRevision theFirst, MLPSolutionRevision theSecond) {
642 return String.CASE_INSENSITIVE_ORDER.compare(theFirst.getVersion(), theSecond.getVersion());