Implement 'Update Service by importing Tosca Model'-story
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / jsonjanusgraph / operations / ArchiveOperation.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License 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=========================================================
19  */
20 package org.openecomp.sdc.be.model.jsonjanusgraph.operations;
21
22 import static org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArchiveOperation.Action.ARCHIVE;
23 import static org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArchiveOperation.Action.RESTORE;
24
25 import fj.data.Either;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.stream.Collectors;
32 import org.apache.commons.collections.MapUtils;
33 import org.openecomp.sdc.be.dao.api.ActionStatus;
34 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
35 import org.openecomp.sdc.be.dao.jsongraph.GraphVertex;
36 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphDao;
37 import org.openecomp.sdc.be.dao.jsongraph.types.EdgeLabelEnum;
38 import org.openecomp.sdc.be.dao.jsongraph.types.JsonParseFlagEnum;
39 import org.openecomp.sdc.be.dao.jsongraph.types.VertexTypeEnum;
40 import org.openecomp.sdc.be.datatypes.elements.ComponentInstanceDataDefinition;
41 import org.openecomp.sdc.be.datatypes.elements.CompositionDataDefinition;
42 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
43 import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum;
44 import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
45 import org.openecomp.sdc.be.model.LifecycleStateEnum;
46 import org.openecomp.sdc.be.model.jsonjanusgraph.enums.JsonConstantKeysEnum;
47 import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
48 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
49 import org.openecomp.sdc.common.log.wrappers.Logger;
50 import org.springframework.beans.factory.annotation.Autowired;
51 import org.springframework.stereotype.Component;
52
53 /**
54  * Created by yavivi on 25/03/2018.
55  */
56 @Component
57 public class ArchiveOperation extends BaseOperation {
58
59     private static final Logger log = Logger.getLogger(ArchiveOperation.class.getName());
60     @Autowired
61     private IGraphLockOperation graphLockOperation;
62
63     public ArchiveOperation(JanusGraphDao janusGraphDao, IGraphLockOperation graphLockOperation) {
64         this.janusGraphDao = janusGraphDao;
65         this.graphLockOperation = graphLockOperation;
66     }
67
68     public Either<List<String>, ActionStatus> archiveComponent(String componentId) {
69         final Either<GraphVertex, JanusGraphOperationStatus> vertexResult = this.janusGraphDao.getVertexById(componentId);
70         if (vertexResult.isLeft()) {
71             Map<String, Object> metadataJson = vertexResult.left().value().getMetadataJson();
72             String normative = JsonPresentationFields.NORMATIVE.getPresentation();
73             if (MapUtils.isNotEmpty(metadataJson) && metadataJson.containsKey(normative) && (boolean) metadataJson.get(normative)) {
74                 return Either.right(ActionStatus.CANNOT_ARCHIVE_SYSTEM_DEPLOYED_RESOURCES);
75             }
76             return doAction(ARCHIVE, vertexResult.left().value());
77         } else {
78             return Either.right(onError(ARCHIVE.name(), componentId, vertexResult.right().value()));
79         }
80     }
81
82     public Either<List<String>, ActionStatus> restoreComponent(String componentId) {
83         final Either<GraphVertex, JanusGraphOperationStatus> vertexResult = this.janusGraphDao.getVertexById(componentId);
84         if (vertexResult.isLeft()) {
85             return doAction(RESTORE, vertexResult.left().value());
86         } else {
87             return Either.right(onError(RESTORE.name(), componentId, vertexResult.right().value()));
88         }
89     }
90
91     public ActionStatus onVspRestored(String csarId) {
92         return onVspStateChanged(RESTORE, csarId);
93     }
94
95     public ActionStatus onVspArchived(String csarId) {
96         return onVspStateChanged(ARCHIVE, csarId);
97     }
98
99     private ActionStatus onVspStateChanged(Action action, String csarId) {
100         Map<GraphPropertyEnum, Object> props = new HashMap<>();
101         props.put(GraphPropertyEnum.CSAR_UUID, csarId);
102         Either<List<GraphVertex>, JanusGraphOperationStatus> vfsE = janusGraphDao.getByCriteria(VertexTypeEnum.TOPOLOGY_TEMPLATE, props);
103         return vfsE.either(vList -> setVspArchived(action, vList), s -> onError("VSP_" + action.name(), csarId, s));
104     }
105
106     private ActionStatus setVspArchived(Action action, List<GraphVertex> vList) {
107         if (!vList.isEmpty()) {
108             //Find & Lock the highest version component
109             GraphVertex highestVersion = this.getHighestVersionFrom(vList.get(0));
110             StorageOperationStatus lockStatus = this.graphLockOperation
111                 .lockComponent(highestVersion.getUniqueId(), highestVersion.getType().getNodeType());
112             if (lockStatus != StorageOperationStatus.OK) {
113                 return onError(action.name(), highestVersion.getUniqueId(), JanusGraphOperationStatus.ALREADY_LOCKED);
114             }
115             try {
116                 //Set isVspArchived flag
117                 for (GraphVertex v : vList) {
118                     boolean val = action == ARCHIVE ? true : false;
119                     v.setJsonMetadataField(JsonPresentationFields.IS_VSP_ARCHIVED, val);
120                     v.addMetadataProperty(GraphPropertyEnum.IS_VSP_ARCHIVED, val);
121                     janusGraphDao.updateVertex(v);
122                 }
123                 return commitAndCheck("VSP_" + action.name(), vList.toString());
124             } finally {
125                 this.graphLockOperation.unlockComponent(highestVersion.getUniqueId(), highestVersion.getType().getNodeType());
126             }
127         }
128         return ActionStatus.OK;
129     }
130
131     public List<String> setArchivedOriginsFlagInComponentInstances(GraphVertex compositionService) {
132         List<String> ciUidsWithArchivedOrigins = new LinkedList();
133         Either<List<GraphVertex>, JanusGraphOperationStatus> instanceOfVerticesE = janusGraphDao
134             .getChildrenVertices(compositionService, EdgeLabelEnum.INSTANCE_OF, JsonParseFlagEnum.NoParse);
135         Either<List<GraphVertex>, JanusGraphOperationStatus> proxyOfVerticesE = janusGraphDao
136             .getChildrenVertices(compositionService, EdgeLabelEnum.PROXY_OF, JsonParseFlagEnum.NoParse);
137         List<GraphVertex> all = new LinkedList<>();
138         if (instanceOfVerticesE.isLeft()) {
139             all.addAll(instanceOfVerticesE.left().value());
140         }
141         if (proxyOfVerticesE.isLeft()) {
142             all.addAll(proxyOfVerticesE.left().value());
143         }
144         List<GraphVertex> archivedOrigins = all.stream().filter(v -> Boolean.TRUE.equals(v.getMetadataProperty(GraphPropertyEnum.IS_ARCHIVED)))
145             .collect(Collectors.toList());
146         List<String> archivedOriginsUids = archivedOrigins.stream().map(GraphVertex::getUniqueId).collect(Collectors.toList());
147         Map<String, CompositionDataDefinition> compositionsJson = (Map<String, CompositionDataDefinition>) compositionService.getJson();
148         if (compositionsJson != null) {
149             CompositionDataDefinition composition = compositionsJson.get(JsonConstantKeysEnum.COMPOSITION.getValue());
150             if (composition != null) {
151                 //Get all component instances from composition
152                 Map<String, ComponentInstanceDataDefinition> componentInstances = composition.getComponentInstances();
153                 //Extract component instances uids that has archived origins
154                 ciUidsWithArchivedOrigins = componentInstances.values().stream().
155                     //filter CIs whose origins are marked as archived (componentUid is in archivedOriginsUids) the second condition handles the PROXY_OF case)
156                         filter(ci -> archivedOriginsUids.contains(ci.getComponentUid()) || archivedOriginsUids
157                         .contains(ci.getToscaPresentationValue(JsonPresentationFields.CI_SOURCE_MODEL_UID)))
158                     .map(ComponentInstanceDataDefinition::getUniqueId).collect(Collectors.toList());
159                 //set archived origins flag
160                 componentInstances.values().stream().filter(ci -> archivedOriginsUids.contains(ci.getComponentUid()) || archivedOriginsUids
161                     .contains(ci.getToscaPresentationValue(JsonPresentationFields.CI_SOURCE_MODEL_UID))).forEach(ci -> ci.setOriginArchived(true));
162             }
163         }
164         return ciUidsWithArchivedOrigins;
165     }
166
167     private Either<List<String>, ActionStatus> doAction(Action action, GraphVertex componentVertex) {
168         GraphVertex highestVersion = this.getHighestVersionFrom(componentVertex);
169         if (action.equals(ARCHIVE) && isInCheckoutState(highestVersion)) {
170             return Either.right(ActionStatus.INVALID_SERVICE_STATE);
171         }
172         //Lock the Highest Version
173         StorageOperationStatus lockStatus = this.graphLockOperation
174             .lockComponent(highestVersion.getUniqueId(), highestVersion.getType().getNodeType());
175         if (lockStatus != StorageOperationStatus.OK) {
176             return Either.right(onError(action.name(), componentVertex.getUniqueId(), JanusGraphOperationStatus.ALREADY_LOCKED));
177         }
178         //Refetch latest version with full parsing
179         highestVersion = this.janusGraphDao.getVertexById(highestVersion.getUniqueId(), JsonParseFlagEnum.ParseAll).left().value();
180         try {
181             //Get Catalog and Archive Roots
182             GraphVertex catalogRoot = janusGraphDao.getVertexByLabel(VertexTypeEnum.CATALOG_ROOT).left().value();
183             GraphVertex archiveRoot = janusGraphDao.getVertexByLabel(VertexTypeEnum.ARCHIVE_ROOT).left().value();
184             if (action == ARCHIVE) {
185                 archiveEdges(catalogRoot, archiveRoot, highestVersion);
186             } else if (action == RESTORE) {
187                 restoreEdges(catalogRoot, archiveRoot, highestVersion);
188             }
189             setPropertiesByAction(highestVersion, action);
190             janusGraphDao.updateVertex(highestVersion);
191             List<String> affectedComponentIds = handleParents(highestVersion, catalogRoot, archiveRoot, action);
192             ActionStatus sc = commitAndCheck(action.name(), highestVersion.getUniqueId());
193             return sc == ActionStatus.OK ? Either.left(affectedComponentIds) : Either.right(sc);
194         } finally {
195             this.graphLockOperation.unlockComponent(highestVersion.getUniqueId(), highestVersion.getType().getNodeType());
196         }
197     }
198
199     private ActionStatus commitAndCheck(String action, String componentId) {
200         JanusGraphOperationStatus status = janusGraphDao.commit();
201         if (!status.equals(JanusGraphOperationStatus.OK)) {
202             return onError(action, componentId, status);
203         }
204         return ActionStatus.OK;
205     }
206
207     private boolean isInCheckoutState(GraphVertex v) {
208         if (LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name().equals(v.getMetadataProperty(GraphPropertyEnum.STATE))) {
209             return true;
210         }
211         return false;
212     }
213
214     /**
215      * Walks on children until highest version is reached
216      *
217      * @param v
218      * @return
219      */
220     private GraphVertex getHighestVersionFrom(GraphVertex v) {
221         Either<GraphVertex, JanusGraphOperationStatus> childVertexE = janusGraphDao
222             .getChildVertex(v, EdgeLabelEnum.VERSION, JsonParseFlagEnum.NoParse);
223         GraphVertex highestVersionVertex = v;
224         while (childVertexE.isLeft()) {
225             highestVersionVertex = childVertexE.left().value();
226             childVertexE = janusGraphDao.getChildVertex(highestVersionVertex, EdgeLabelEnum.VERSION, JsonParseFlagEnum.NoParse);
227         }
228         return highestVersionVertex;
229     }
230
231     private boolean isHighestVersion(GraphVertex v) {
232         Boolean highest = (Boolean) v.getMetadataProperty(GraphPropertyEnum.IS_HIGHEST_VERSION);
233         return highest != null && highest;
234     }
235
236     private List<String> handleParents(GraphVertex v, GraphVertex catalogRoot, GraphVertex archiveRoot, Action action) {
237         Either<GraphVertex, JanusGraphOperationStatus> parentVertexE = janusGraphDao
238             .getParentVertex(v, EdgeLabelEnum.VERSION, JsonParseFlagEnum.ParseAll);
239         List<String> affectedCompIds = new ArrayList();
240         affectedCompIds.add(v.getUniqueId());
241         while (parentVertexE.isLeft()) {
242             GraphVertex cv = parentVertexE.left().value();
243             affectedCompIds.add(cv.getUniqueId());
244             boolean isHighestVersion = isHighestVersion(cv);
245             if (isHighestVersion) {
246                 if (action == ARCHIVE) {
247                     archiveEdges(catalogRoot, archiveRoot, cv);
248                 } else {
249                     restoreEdges(catalogRoot, archiveRoot, cv);
250                 }
251             }
252             setPropertiesByAction(cv, action);
253             janusGraphDao.updateVertex(cv);
254             parentVertexE = janusGraphDao.getParentVertex(cv, EdgeLabelEnum.VERSION, JsonParseFlagEnum.ParseAll);
255         }
256         return affectedCompIds;
257     }
258
259     private void archiveEdges(GraphVertex catalogRoot, GraphVertex archiveRoot, GraphVertex v) {
260         janusGraphDao.deleteAllEdges(catalogRoot, v, EdgeLabelEnum.CATALOG_ELEMENT);
261         janusGraphDao.createEdge(archiveRoot, v, EdgeLabelEnum.ARCHIVE_ELEMENT, null);
262         setPropertiesByAction(v, ARCHIVE);
263     }
264
265     private void restoreEdges(GraphVertex catalogRoot, GraphVertex archiveRoot, GraphVertex v) {
266         janusGraphDao.deleteAllEdges(archiveRoot, v, EdgeLabelEnum.ARCHIVE_ELEMENT);
267         janusGraphDao.createEdge(catalogRoot, v, EdgeLabelEnum.CATALOG_ELEMENT, null);
268         setPropertiesByAction(v, RESTORE);
269     }
270
271     private void setPropertiesByAction(GraphVertex v, Action action) {
272         long now = System.currentTimeMillis();
273         boolean isArchived = action == ARCHIVE ? true : false;
274         v.addMetadataProperty(GraphPropertyEnum.IS_ARCHIVED, isArchived);
275         v.addMetadataProperty(GraphPropertyEnum.ARCHIVE_TIME, now);
276         v.setJsonMetadataField(JsonPresentationFields.IS_ARCHIVED, isArchived);
277         v.setJsonMetadataField(JsonPresentationFields.ARCHIVE_TIME, now);
278     }
279
280     private ActionStatus onError(String action, String componentId, JanusGraphOperationStatus s) {
281         ActionStatus ret = ActionStatus.GENERAL_ERROR;
282         if (s == JanusGraphOperationStatus.NOT_FOUND) {
283             ret = ActionStatus.RESOURCE_NOT_FOUND;
284         } else if (s == JanusGraphOperationStatus.ALREADY_LOCKED) {
285             ret = ActionStatus.COMPONENT_IN_USE;
286         }
287         String retCodeVal = ret.name();
288         log.error("error occurred when trying to {} {}. Return code is: {}", action, componentId, retCodeVal);
289         return ret;
290     }
291
292     public enum Action {ARCHIVE, RESTORE;}
293 }