Add new SOL004 ETSI Validator
[sdc.git] / openecomp-be / lib / openecomp-sdc-vendor-software-product-lib / openecomp-sdc-vendor-software-product-core / src / main / java / org / openecomp / sdc / vendorsoftwareproduct / services / impl / etsi / ETSIServiceImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2019, Nordix Foundation. 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.vendorsoftwareproduct.services.impl.etsi;
22
23 import static org.openecomp.sdc.tosca.csar.CSARConstants.ARTIFACTS_FOLDER;
24 import static org.openecomp.sdc.tosca.csar.CSARConstants.ETSI_VERSION_2_6_1;
25 import static org.openecomp.sdc.tosca.csar.CSARConstants.MAIN_SERVICE_TEMPLATE_MF_FILE_NAME;
26 import static org.openecomp.sdc.tosca.csar.CSARConstants.MANIFEST_PNF_METADATA;
27 import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_META_ORIG_PATH_FILE_NAME;
28 import static org.openecomp.sdc.tosca.csar.ManifestTokenType.COMPATIBLE_SPECIFICATION_VERSIONS;
29 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.ENTRY_DEFINITIONS;
30 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.ETSI_ENTRY_CHANGE_LOG;
31 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.ETSI_ENTRY_MANIFEST;
32 import static org.openecomp.sdc.tosca.csar.ToscaMetadataFileInfo.TOSCA_META_PATH_FILE_NAME;
33
34 import com.vdurmont.semver4j.Semver;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.nio.file.Path;
38 import java.nio.file.Paths;
39 import java.util.Arrays;
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Objects;
44 import java.util.Optional;
45 import java.util.stream.Collectors;
46 import org.apache.commons.collections.MapUtils;
47 import org.onap.sdc.tosca.datatypes.model.ServiceTemplate;
48 import org.onap.sdc.tosca.services.YamlUtil;
49 import org.openecomp.core.utilities.file.FileContentHandler;
50 import org.openecomp.sdc.be.config.NonManoArtifactType;
51 import org.openecomp.sdc.be.config.NonManoConfiguration;
52 import org.openecomp.sdc.be.config.NonManoConfigurationManager;
53 import org.openecomp.sdc.be.config.NonManoFolderType;
54 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
55 import org.openecomp.sdc.logging.api.Logger;
56 import org.openecomp.sdc.logging.api.LoggerFactory;
57 import org.openecomp.sdc.tosca.csar.Manifest;
58 import org.openecomp.sdc.tosca.csar.OnboardingToscaMetadata;
59 import org.openecomp.sdc.tosca.csar.SOL004ManifestOnboarding;
60 import org.openecomp.sdc.tosca.csar.ToscaMetadata;
61 import org.openecomp.sdc.tosca.datatypes.ToscaServiceModel;
62
63 public class ETSIServiceImpl implements ETSIService {
64
65     private static final Logger LOGGER = LoggerFactory.getLogger(ETSIServiceImpl.class);
66
67     private final NonManoConfiguration nonManoConfiguration;
68
69     public ETSIServiceImpl() {
70         nonManoConfiguration = NonManoConfigurationManager.getInstance().getNonManoConfiguration();
71     }
72
73     public ETSIServiceImpl(final NonManoConfiguration nonManoConfiguration) {
74         this.nonManoConfiguration = nonManoConfiguration;
75     }
76
77     @Override
78     public boolean isSol004WithToscaMetaDirectory(FileContentHandler handler) throws IOException {
79         final Map<String, byte[]> templates = handler.getFiles();
80         return isMetaFilePresent(templates) && hasMetaMandatoryEntries(getMetadata(handler));
81     }
82
83     @Override
84     public Optional<Map<String, Path>> moveNonManoFileToArtifactFolder(final FileContentHandler handler) throws IOException {
85         final Manifest manifest = loadManifest(handler);
86         final Path originalManifestPath;
87         try {
88             originalManifestPath = getOriginalManifestPath(handler);
89         } catch (final IOException ex) {
90             if (LOGGER.isErrorEnabled()) {
91                 LOGGER.error("An error occurred while getting the original manifest path", ex);
92             }
93             throw ex;
94         }
95         final Map<String, Path> fromToPathMap = new HashMap<>();
96         final Map<String, NonManoFolderType> nonManoKeyFolderMapping = nonManoConfiguration.getNonManoKeyFolderMapping();
97         manifest.getNonManoSources().entrySet().stream()
98             .filter(manifestNonManoSourceEntry -> nonManoKeyFolderMapping.containsKey(manifestNonManoSourceEntry.getKey()))
99             .forEach(manifestNonManoSourceEntry -> {
100                 final NonManoFolderType nonManoFolderType = nonManoKeyFolderMapping.get(manifestNonManoSourceEntry.getKey());
101                 final List<String> nonManoFileList = manifestNonManoSourceEntry.getValue();
102                 final Map<String, Path> actualFromToPathMap = nonManoFileList.stream()
103                     .map(nonManoFilePath -> {
104                         final Path normalizedFilePath = resolveNonManoFilePath(originalManifestPath, nonManoFilePath);
105                         final Optional<Path> changedPath = updateNonManoPathInHandler(handler, nonManoFolderType, normalizedFilePath);
106                         if (changedPath.isPresent()) {
107                             final Map<String, Path> fromAndToPathMap = new HashMap<>();
108                             fromAndToPathMap.put(nonManoFilePath, Paths.get(ARTIFACTS_FOLDER).resolve(changedPath.get()));
109                             return fromAndToPathMap;
110                         }
111                         return null;
112                     })
113                     .filter(Objects::nonNull)
114                     .collect(Collectors.toMap(
115                         fromToPathEntry -> fromToPathEntry.keySet().iterator().next(),
116                         fromToPathEntry -> fromToPathEntry.values().iterator().next()
117                     ));
118                 fromToPathMap.putAll(actualFromToPathMap);
119             });
120
121         return MapUtils.isEmpty(fromToPathMap) ? Optional.empty() : Optional.of(fromToPathMap);
122     }
123
124     /**
125      * Resolves the non mano file path based on the original manifest path of the onboarded package.
126      *
127      * @param originalManifestPath The original path from the onboarded package manifest
128      * @param nonManoFilePath The non mano file path defined in the manifest
129      * @return The resolved and normalized non mano path.
130      */
131     private Path resolveNonManoFilePath(final Path originalManifestPath, final String nonManoFilePath) {
132         return originalManifestPath.resolve(Paths.get(nonManoFilePath)).normalize();
133     }
134
135     /**
136      * Updates the non mano file path in the package file handler based on the non mano type.
137      *
138      * @param handler The package file handler
139      * @param nonManoFolderType The Non Mano type of the file to update
140      * @param nonManoOriginalFilePath The Non Mano file original path
141      * @return The new file path if it was updated in the package file handler, otherwise empty.
142      */
143     private Optional<Path> updateNonManoPathInHandler(final FileContentHandler handler, final NonManoFolderType nonManoFolderType,
144                                                       final Path nonManoOriginalFilePath) {
145         final Path fixedSourcePath = fixNonManoPath(nonManoOriginalFilePath);
146         if (handler.containsFile(fixedSourcePath.toString())) {
147             final Path newNonManoPath = Paths.get(nonManoFolderType.getType(), nonManoFolderType.getLocation()
148                 , fixedSourcePath.getFileName().toString());
149             if (!handler.containsFile(newNonManoPath.toString())) {
150                 handler.addFile(newNonManoPath.toString(), handler.remove(fixedSourcePath.toString()));
151                 return Optional.of(newNonManoPath);
152             }
153         }
154
155         return Optional.empty();
156     }
157
158     /**
159      * Fix the original non mano file path to the ONAP package file path.
160      *
161      * Non mano artifacts that were inside the {@link org.openecomp.sdc.tosca.csar.CSARConstants#ARTIFACTS_FOLDER} path
162      * are not moved when parsed to ONAP package, but the Manifest declaration can still have the {@link
163      * org.openecomp.sdc.tosca.csar.CSARConstants#ARTIFACTS_FOLDER} reference in it. If so, that reference is removed.
164      *
165      * @param nonManoOriginalFilePath The original non mano file path
166      * @return The non mano fixed path to ONAP package structure.
167      */
168     private Path fixNonManoPath(final Path nonManoOriginalFilePath) {
169         final Path rootArtifactsPath = Paths.get("/", ARTIFACTS_FOLDER);
170         if (nonManoOriginalFilePath.startsWith(rootArtifactsPath)) {
171             return rootArtifactsPath.relativize(nonManoOriginalFilePath);
172         }
173         final Path relativeArtifactsPath = Paths.get(ARTIFACTS_FOLDER);
174         if (nonManoOriginalFilePath.startsWith(relativeArtifactsPath)) {
175             return relativeArtifactsPath.relativize(nonManoOriginalFilePath);
176         }
177
178         return nonManoOriginalFilePath;
179     }
180
181     @Override
182     public void updateMainDescriptorPaths(final ToscaServiceModel toscaServiceModel,
183                                           final Map<String, Path> fromToMovedArtifactMap) {
184         final ServiceTemplate entryDefinition = toscaServiceModel.getServiceTemplates()
185             .get(toscaServiceModel.getEntryDefinitionServiceTemplate());
186         final YamlUtil yamlUtil = new YamlUtil();
187         final String[] entryDefinitionYaml = {yamlUtil.objectToYaml(entryDefinition)};
188         fromToMovedArtifactMap.forEach((fromPath, toPath) -> entryDefinitionYaml[0] = entryDefinitionYaml[0]
189             .replaceAll(fromPath, toPath.toString()));
190
191         toscaServiceModel.addServiceTemplate(toscaServiceModel.getEntryDefinitionServiceTemplate()
192             , yamlUtil.yamlToObject(entryDefinitionYaml[0], ServiceTemplate.class));
193     }
194
195     private boolean hasMetaMandatoryEntries(final ToscaMetadata toscaMetadata) {
196         final Map<String, String> metaDataEntries = toscaMetadata.getMetaEntries();
197         return metaDataEntries.containsKey(ENTRY_DEFINITIONS.getName()) && metaDataEntries
198             .containsKey(ETSI_ENTRY_MANIFEST.getName())
199             && metaDataEntries.containsKey(ETSI_ENTRY_CHANGE_LOG.getName());
200     }
201
202     @Override
203     public Semver getHighestCompatibleSpecificationVersion(final FileContentHandler handler) {
204         try {
205             Map<String, String> metadata = getManifest(handler).getMetadata();
206             if (metadata.containsKey(COMPATIBLE_SPECIFICATION_VERSIONS.getToken())) {
207                 return Arrays.asList(metadata.get(COMPATIBLE_SPECIFICATION_VERSIONS.getToken()).split(","))
208                         .stream().map(Semver::new).max((v1, v2) -> v1.compareTo(v2))
209                         .orElse(new Semver(ETSI_VERSION_2_6_1));
210             }
211         } catch (Exception ex) {
212             LOGGER.error("An error occurred while getting highest compatible version from manifest file", ex);
213         }
214         return new Semver(ETSI_VERSION_2_6_1);
215
216     }
217
218     @Override
219     public boolean hasCnfEnhancements(final FileContentHandler fileContentHandler) throws IOException {
220         final Manifest manifest = loadManifest(fileContentHandler);
221         return manifest.getNonManoSources().entrySet().stream()
222             .filter(manifestNonManoSourceEntry ->  NonManoArtifactType.ONAP_CNF_HELM.getType()
223                 .equalsIgnoreCase(manifestNonManoSourceEntry.getKey())).findFirst().isPresent();
224     }
225
226     private Manifest loadManifest(final FileContentHandler handler) throws IOException {
227         final Manifest manifest;
228         try {
229             manifest = getManifest(handler);
230         } catch (final IOException ex) {
231             if (LOGGER.isErrorEnabled()) {
232                 LOGGER.error("An error occurred while getting the manifest file", ex);
233             }
234             throw ex;
235         }
236         return manifest;
237     }
238
239     private boolean isMetaFilePresent(Map<String, byte[]> handler) {
240         return handler.containsKey(TOSCA_META_PATH_FILE_NAME) || handler.containsKey(TOSCA_META_ORIG_PATH_FILE_NAME);
241     }
242
243     public ResourceTypeEnum getResourceType(FileContentHandler handler) throws IOException {
244         ToscaMetadata metadata = getMetadata(handler);
245         Manifest manifest = getManifest(handler, metadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName()));
246         return getResourceType(manifest);
247     }
248
249     public ResourceTypeEnum getResourceType(Manifest manifest) {
250         // Valid manifest should contain whether vnf or pnf related metadata data exclusively in SOL004 standard,
251         // validation of manifest done during package upload stage
252         if (manifest != null && !manifest.getMetadata().isEmpty()
253             && MANIFEST_PNF_METADATA.stream().anyMatch(e -> manifest.getMetadata().containsKey(e))) {
254             return ResourceTypeEnum.PNF;
255         }
256         // VNF is default resource type
257         return ResourceTypeEnum.VF;
258     }
259
260     public Manifest getManifest(FileContentHandler handler) throws IOException {
261         ToscaMetadata metadata = getMetadata(handler);
262         return getManifest(handler, metadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName()));
263     }
264
265     private Manifest getManifest(FileContentHandler handler, String manifestLocation) throws IOException {
266         try (InputStream manifestInputStream = getManifestInputStream(handler, manifestLocation)) {
267             Manifest onboardingManifest = new SOL004ManifestOnboarding();
268             onboardingManifest.parse(manifestInputStream);
269             return onboardingManifest;
270         }
271     }
272
273     public Path getOriginalManifestPath(final FileContentHandler handler) throws IOException {
274         final ToscaMetadata metadata = getOriginalMetadata(handler);
275         final String originalMetadataPath = metadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName());
276         final Path path = Paths.get(originalMetadataPath);
277         return path.getParent() == null ? Paths.get("") : path.getParent();
278     }
279
280     private ToscaMetadata getMetadata(FileContentHandler handler) throws IOException {
281         ToscaMetadata metadata;
282         if (handler.containsFile(TOSCA_META_PATH_FILE_NAME)) {
283             metadata = OnboardingToscaMetadata
284                 .parseToscaMetadataFile(handler.getFileContentAsStream(TOSCA_META_PATH_FILE_NAME));
285         } else if (handler.containsFile(TOSCA_META_ORIG_PATH_FILE_NAME)) {
286             metadata = OnboardingToscaMetadata
287                 .parseToscaMetadataFile(handler.getFileContentAsStream(TOSCA_META_ORIG_PATH_FILE_NAME));
288         } else {
289             throw new IOException("TOSCA.meta file not found!");
290         }
291         return metadata;
292     }
293
294     private ToscaMetadata getOriginalMetadata(final FileContentHandler handler) throws IOException {
295         if (handler.containsFile(TOSCA_META_ORIG_PATH_FILE_NAME)) {
296             return OnboardingToscaMetadata
297                 .parseToscaMetadataFile(handler.getFileContentAsStream(TOSCA_META_ORIG_PATH_FILE_NAME));
298         } else {
299             throw new IOException(String.format("%s file not found", TOSCA_META_ORIG_PATH_FILE_NAME));
300         }
301     }
302
303     private InputStream getManifestInputStream(FileContentHandler handler, String manifestLocation) throws IOException {
304         InputStream io;
305         if (manifestLocation == null || !handler.containsFile(manifestLocation)) {
306             io = handler.getFileContentAsStream(MAIN_SERVICE_TEMPLATE_MF_FILE_NAME);
307         } else {
308             io = handler.getFileContentAsStream(manifestLocation);
309         }
310
311         if (io == null) {
312             throw new IOException("Manifest file not found!");
313         }
314         return io;
315     }
316
317     public NonManoConfiguration getConfiguration() {
318         return nonManoConfiguration;
319     }
320 }