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