5f819104173052b78f80e249231334d85308e587
[sdc.git] / openecomp-be / backend / openecomp-sdc-vendor-software-product-manager / src / main / java / org / openecomp / sdc / vendorsoftwareproduct / impl / orchestration / csar / validation / SOL004MetaDirectoryValidator.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2019 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  *  * Modifications copyright (c) 2020 Nokia
20  * ================================================================================
21  */
22
23 package org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation;
24
25
26 import static org.openecomp.sdc.be.config.NonManoArtifactType.ONAP_PM_DICTIONARY;
27 import static org.openecomp.sdc.be.config.NonManoArtifactType.ONAP_SW_INFORMATION;
28 import static org.openecomp.sdc.be.config.NonManoArtifactType.ONAP_VES_EVENTS;
29 import static org.openecomp.sdc.tosca.csar.CSARConstants.CSAR_VERSION_1_0;
30 import static org.openecomp.sdc.tosca.csar.CSARConstants.CSAR_VERSION_1_1;
31 import static org.openecomp.sdc.tosca.csar.CSARConstants.MANIFEST_METADATA_LIMIT;
32 import static org.openecomp.sdc.tosca.csar.CSARConstants.MANIFEST_PNF_METADATA;
33 import static org.openecomp.sdc.tosca.csar.CSARConstants.MANIFEST_VNF_METADATA;
34 import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_MANIFEST_FILE_EXT;
35 import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_TYPE_PNF;
36 import static org.openecomp.sdc.tosca.csar.CSARConstants.TOSCA_TYPE_VNF;
37 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.CREATED_BY_ENTRY;
38 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.CSAR_VERSION_ENTRY;
39 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.ENTRY_DEFINITIONS;
40 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.ETSI_ENTRY_CERTIFICATE;
41 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.ETSI_ENTRY_MANIFEST;
42 import static org.openecomp.sdc.tosca.csar.ToscaMetaEntry.TOSCA_META_FILE_VERSION_ENTRY;
43 import static org.openecomp.sdc.tosca.csar.ToscaMetadataFileInfo.TOSCA_META_FILE_VERSION_1_0;
44 import static org.openecomp.sdc.tosca.csar.ToscaMetadataFileInfo.TOSCA_META_PATH_FILE_NAME;
45
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.util.ArrayList;
49 import java.util.Collections;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Optional;
55 import java.util.Set;
56 import java.util.concurrent.CopyOnWriteArrayList;
57 import java.util.stream.Collectors;
58 import java.util.stream.Stream;
59
60 import com.google.common.collect.ImmutableSet;
61 import org.apache.commons.collections.CollectionUtils;
62 import org.apache.commons.io.FilenameUtils;
63 import org.openecomp.core.impl.ToscaDefinitionImportHandler;
64 import org.openecomp.core.utilities.file.FileContentHandler;
65 import org.openecomp.sdc.be.config.NonManoArtifactType;
66 import org.openecomp.sdc.be.csar.pnf.PnfSoftwareInformation;
67 import org.openecomp.sdc.be.csar.pnf.SoftwareInformationArtifactYamlParser;
68 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
69 import org.openecomp.sdc.common.errors.Messages;
70 import org.openecomp.sdc.common.utils.SdcCommon;
71 import org.openecomp.sdc.datatypes.error.ErrorLevel;
72 import org.openecomp.sdc.datatypes.error.ErrorMessage;
73 import org.openecomp.sdc.logging.api.Logger;
74 import org.openecomp.sdc.logging.api.LoggerFactory;
75 import org.openecomp.sdc.tosca.csar.Manifest;
76 import org.openecomp.sdc.tosca.csar.OnboardingToscaMetadata;
77 import org.openecomp.sdc.tosca.csar.SOL004ManifestOnboarding;
78 import org.openecomp.sdc.tosca.csar.ToscaMetaEntry;
79 import org.openecomp.sdc.tosca.csar.ToscaMetadata;
80 import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.OnboardingPackageContentHandler;
81 import org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation.exception.MissingCertificateException;
82 import org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation.utils.FileExtractor;
83 import org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation.utils.InternalFilesFilter;
84 import org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.exceptions.InvalidManifestMetadataException;
85 import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager;
86 import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException;
87 import org.yaml.snakeyaml.Yaml;
88
89 /**
90  * Validates the contents of the package to ensure it complies with the "CSAR with TOSCA-Metadata directory" structure
91  * as defined in ETSI GS NFV-SOL 004 v2.6.1.
92  */
93 class SOL004MetaDirectoryValidator implements Validator {
94
95     private static final Logger LOGGER = LoggerFactory.getLogger(SOL004MetaDirectoryValidator.class);
96
97     private static final String MANIFEST_SOURCE = "Source";
98     private static final String MANIFEST_NON_MANO_SOURCE = "Non-MANO Source";
99     private final List<ErrorMessage> errorsByFile = new CopyOnWriteArrayList<>();
100     private final SecurityManager securityManager;
101     private OnboardingPackageContentHandler contentHandler;
102     private Set<String> folderList;
103     private ToscaMetadata toscaMetadata;
104     private final InternalFilesFilter internalFilesFilter = new InternalFilesFilter();
105
106     public SOL004MetaDirectoryValidator() {
107         securityManager = SecurityManager.getInstance();
108     }
109
110     //for tests purpose
111     SOL004MetaDirectoryValidator(final SecurityManager securityManager) {
112         this.securityManager = securityManager;
113     }
114
115     @Override
116     public Map<String, List<ErrorMessage>> validateContent(final FileContentHandler fileContentHandler) {
117         this.contentHandler = (OnboardingPackageContentHandler) fileContentHandler;
118         this.folderList = contentHandler.getFolderList();
119         parseToscaMetadata();
120         verifyMetadataFile();
121
122         if (packageHasCertificate()) {
123             verifySignedFiles();
124         }
125         validatePMDictionaryContentsAgainstSchema();
126         return Collections.unmodifiableMap(getAnyValidationErrors());
127     }
128
129     private boolean packageHasCertificate() {
130         final String certificatePath = getCertificatePath().orElse(null);
131         return contentHandler.containsFile(certificatePath);
132     }
133
134     private Optional<String> getCertificatePath() {
135         return toscaMetadata.getEntry(ETSI_ENTRY_CERTIFICATE);
136     }
137
138     /**
139      * Parses the {@link org.openecomp.sdc.tosca.csar.ToscaMetadataFileInfo#TOSCA_META_PATH_FILE_NAME} file
140      */
141     private void parseToscaMetadata() {
142         try {
143             toscaMetadata =
144                     OnboardingToscaMetadata
145                             .parseToscaMetadataFile(contentHandler.getFileContentAsStream(TOSCA_META_PATH_FILE_NAME));
146         } catch (final IOException e) {
147             reportError(ErrorLevel.ERROR, Messages.METADATA_PARSER_INTERNAL.getErrorMessage());
148             LOGGER.error(Messages.METADATA_PARSER_INTERNAL.getErrorMessage(), e.getMessage(), e);
149         }
150     }
151
152     private void verifyMetadataFile() {
153         if (toscaMetadata.isValid() && hasETSIMetadata()) {
154             verifyManifestNameAndExtension();
155             handleMetadataEntries();
156         } else {
157             errorsByFile.addAll(toscaMetadata.getErrors());
158         }
159     }
160
161     private void verifySignedFiles() {
162         final Map<String, String> signedFileMap = contentHandler.getFileAndSignaturePathMap(SecurityManager.ALLOWED_SIGNATURE_EXTENSIONS);
163         final String packageCertificatePath = getCertificatePath().orElse(null);
164         final byte[] packageCert = contentHandler.getFileContent(packageCertificatePath);
165         if (packageCert == null) {
166             throw new MissingCertificateException("Expected package certificate");
167         }
168         signedFileMap.entrySet().stream().filter(entry -> entry.getValue() != null).forEach(entry -> {
169             final String filePath = entry.getKey();
170             final String fileSignaturePath = entry.getValue();
171             final byte[] fileBytes = contentHandler.getFileContent(filePath);
172             final byte[] fileSignatureBytes = contentHandler.getFileContent(fileSignaturePath);
173             try {
174                 if (!securityManager.verifySignedData(fileSignatureBytes, packageCert, fileBytes)) {
175                     reportError(ErrorLevel.ERROR,
176                             Messages.ARTIFACT_INVALID_SIGNATURE.formatMessage(fileSignaturePath, filePath));
177                 }
178             } catch (final SecurityManagerException e) {
179                 final String errorMessage = Messages.ARTIFACT_SIGNATURE_VALIDATION_ERROR
180                         .formatMessage(fileSignaturePath, filePath, packageCertificatePath, e.getMessage());
181                 reportError(ErrorLevel.ERROR, errorMessage);
182                 LOGGER.error(errorMessage, e);
183             }
184         });
185     }
186
187     private void verifyManifestNameAndExtension() {
188         final Map<String, String> entries = toscaMetadata.getMetaEntries();
189         final String manifestFileName = getFileName(entries.get(ETSI_ENTRY_MANIFEST.getName()));
190         final String manifestExtension = getFileExtension(entries.get(ETSI_ENTRY_MANIFEST.getName()));
191         final String mainDefinitionFileName = getFileName(entries.get(ENTRY_DEFINITIONS.getName()));
192         if (!(TOSCA_MANIFEST_FILE_EXT).equals(manifestExtension)) {
193             reportError(ErrorLevel.ERROR, Messages.MANIFEST_INVALID_EXT.getErrorMessage());
194         }
195         if (!mainDefinitionFileName.equals(manifestFileName)) {
196             reportError(ErrorLevel.ERROR, String.format(Messages.MANIFEST_INVALID_NAME.getErrorMessage(),
197                     manifestFileName, mainDefinitionFileName));
198         }
199     }
200
201     private String getFileExtension(final String filePath) {
202         return FilenameUtils.getExtension(filePath);
203     }
204
205     private String getFileName(final String filePath) {
206         return FilenameUtils.getBaseName(filePath);
207     }
208
209     private boolean hasETSIMetadata() {
210         final Map<String, String> entries = toscaMetadata.getMetaEntries();
211         return hasEntry(entries, TOSCA_META_FILE_VERSION_ENTRY.getName())
212                 && hasEntry(entries, CSAR_VERSION_ENTRY.getName())
213                 && hasEntry(entries, CREATED_BY_ENTRY.getName());
214     }
215
216     private boolean hasEntry(final Map<String, String> entries, final String mandatoryEntry) {
217         if (!entries.containsKey(mandatoryEntry)) {
218             reportError(ErrorLevel.ERROR,
219                     String.format(Messages.METADATA_MISSING_ENTRY.getErrorMessage(), mandatoryEntry));
220             return false;
221         }
222         return true;
223     }
224
225     private void handleMetadataEntries() {
226         toscaMetadata.getMetaEntries().entrySet().parallelStream().forEach(this::handleEntry);
227     }
228
229     private void handleEntry(final Map.Entry<String, String> entry) {
230         final String key = entry.getKey();
231         final ToscaMetaEntry toscaMetaEntry = ToscaMetaEntry.parse(entry.getKey()).orElse(null);
232         // allows any other unknown entry
233         if (toscaMetaEntry == null) {
234             return;
235         }
236         final String value = entry.getValue();
237
238         switch (toscaMetaEntry) {
239             case TOSCA_META_FILE_VERSION_ENTRY:
240             case CSAR_VERSION_ENTRY:
241             case CREATED_BY_ENTRY:
242                 verifyMetadataEntryVersions(key, value);
243                 break;
244             case ENTRY_DEFINITIONS:
245                 validateDefinitionFile(value);
246                 break;
247             case ETSI_ENTRY_MANIFEST:
248                 validateManifestFile(value);
249                 break;
250             case ETSI_ENTRY_CHANGE_LOG:
251                 validateChangeLog(value);
252                 break;
253             case ETSI_ENTRY_TESTS:
254             case ETSI_ENTRY_LICENSES:
255                 validateOtherEntries(entry);
256                 break;
257             case ETSI_ENTRY_CERTIFICATE:
258                 validateCertificate(value);
259                 break;
260             default:
261                 handleOtherEntry(entry);
262                 break;
263         }
264     }
265
266     private void validateOtherEntries(final Map.Entry<String, String> entry) {
267         final String manifestFile = toscaMetadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName());
268         if (verifyFileExists(contentHandler.getFileList(), manifestFile)) {
269             final Manifest onboardingManifest = new SOL004ManifestOnboarding();
270             onboardingManifest.parse(contentHandler.getFileContentAsStream(manifestFile));
271             final Optional<ResourceTypeEnum> resourceType = onboardingManifest.getType();
272             if (resourceType.isPresent() && resourceType.get() == ResourceTypeEnum.VF) {
273                 final String value = (String) entry.getValue();
274                 validateOtherEntries(value);
275             } else {
276                 final String key = (String) entry.getKey();
277                 reportError(ErrorLevel.ERROR,
278                         String.format(Messages.MANIFEST_INVALID_PNF_METADATA.getErrorMessage(), key));
279             }
280
281         }
282     }
283
284     private void verifyMetadataEntryVersions(final String key, final String version) {
285         if (!(isValidTOSCAVersion(key, version) || isValidCSARVersion(key, version)
286                 || CREATED_BY_ENTRY.getName().equals(key))) {
287             errorsByFile.add(new ErrorMessage(ErrorLevel.ERROR,
288                     String.format(Messages.METADATA_INVALID_VERSION.getErrorMessage(), key, version)));
289             LOGGER.error("{}: key {} - value {} ", Messages.METADATA_INVALID_VERSION.getErrorMessage(), key, version);
290         }
291     }
292
293     private boolean isValidTOSCAVersion(final String key, final String version) {
294         return TOSCA_META_FILE_VERSION_ENTRY.getName().equals(key) && TOSCA_META_FILE_VERSION_1_0.equals(version);
295     }
296
297     private boolean isValidCSARVersion(final String value, final String version) {
298         return CSAR_VERSION_ENTRY.getName().equals(value) && (CSAR_VERSION_1_1.equals(version)
299                 || CSAR_VERSION_1_0.equals(version));
300     }
301
302     protected void validateDefinitionFile(final String filePath) {
303         final Set<String> existingFiles = contentHandler.getFileList();
304
305         if (verifyFileExists(existingFiles, filePath)) {
306             final ToscaDefinitionImportHandler toscaDefinitionImportHandler =
307                     new ToscaDefinitionImportHandler(contentHandler.getFiles(), filePath);
308             final List<ErrorMessage> validationErrorList = toscaDefinitionImportHandler.getErrors();
309             if (CollectionUtils.isNotEmpty(validationErrorList)) {
310                 errorsByFile.addAll(validationErrorList);
311             }
312         } else {
313             reportError(ErrorLevel.ERROR, String.format(Messages.MISSING_DEFINITION_FILE.getErrorMessage(), filePath));
314         }
315     }
316
317     private boolean verifyFileExists(final Set<String> existingFiles, final String filePath) {
318         return existingFiles.contains(filePath);
319     }
320
321     private void validateManifestFile(final String filePath) {
322         final Set<String> existingFiles = contentHandler.getFileList();
323         if (verifyFileExists(existingFiles, filePath)) {
324             final Manifest onboardingManifest = new SOL004ManifestOnboarding();
325             onboardingManifest.parse(contentHandler.getFileContentAsStream(filePath));
326             if (onboardingManifest.isValid()) {
327                 try {
328                     verifyManifestMetadata(onboardingManifest.getMetadata());
329                 } catch (final InvalidManifestMetadataException e) {
330                     reportError(ErrorLevel.ERROR, e.getMessage());
331                     LOGGER.error(e.getMessage(), e);
332                 }
333                 verifyManifestSources(onboardingManifest);
334             } else {
335                 final List<String> manifestErrors = onboardingManifest.getErrors();
336                 manifestErrors.forEach(error -> reportError(ErrorLevel.ERROR, error));
337             }
338         } else {
339             reportError(ErrorLevel.ERROR, String.format(Messages.MANIFEST_NOT_FOUND.getErrorMessage(), filePath));
340         }
341     }
342
343     private void verifyManifestMetadata(final Map<String, String> metadata) {
344         if (!validMetaLimit(metadata)) {
345             reportError(ErrorLevel.ERROR,
346                     String.format(Messages.MANIFEST_METADATA_DOES_NOT_MATCH_LIMIT.getErrorMessage(),
347                             MANIFEST_METADATA_LIMIT));
348         }
349         handleMetadataEntries(metadata);
350     }
351
352     protected boolean isPnfMetadata(final Map<String, String> metadata) {
353         final String firstMetadataDefinition = metadata.keySet().iterator().next();
354         final String expectedMetadataType =
355                 firstMetadataDefinition.contains(TOSCA_TYPE_PNF) ? TOSCA_TYPE_PNF : TOSCA_TYPE_VNF;
356         if (metadata.keySet().stream()
357                 .anyMatch((final String metadataEntry) -> !metadataEntry.contains(expectedMetadataType))) {
358             throw new InvalidManifestMetadataException(Messages.MANIFEST_METADATA_INVALID_ENTRY.getErrorMessage());
359         }
360
361         return TOSCA_TYPE_PNF.equals(expectedMetadataType);
362     }
363
364     private void handleMetadataEntries(final Map<String, String> metadata) {
365         getManifestMetadata(metadata).stream()
366                 .filter(requiredEntry -> !metadata.containsKey(requiredEntry))
367                 .forEach(requiredEntry ->
368                         reportError(ErrorLevel.ERROR,
369                             String.format(Messages.MANIFEST_METADATA_MISSING_ENTRY.getErrorMessage(), requiredEntry)));
370     }
371
372     /**
373      * Checks if all manifest sources exists within the package and if all package files are being referred.
374      *
375      * @param onboardingManifest The manifest
376      */
377     private void verifyManifestSources(final Manifest onboardingManifest) {
378         final Set<String> packageFiles = contentHandler.getFileList();
379         final List<String> sources = internalFilesFilter.filter(onboardingManifest.getSources());
380         verifyFilesExist(packageFiles, sources, MANIFEST_SOURCE);
381
382         final Map<String, List<String>> nonManoArtifacts = onboardingManifest.getNonManoSources();
383
384         final List<String> nonManoValidFilePaths = new ArrayList<>();
385         nonManoArtifacts.forEach((nonManoType, files) -> {
386             final List<String> internalNonManoFileList = internalFilesFilter.filter(files);
387             nonManoValidFilePaths.addAll(internalNonManoFileList);
388             final NonManoArtifactType nonManoArtifactType = NonManoArtifactType.parse(nonManoType).orElse(null);
389             if (nonManoArtifactType == ONAP_PM_DICTIONARY || nonManoArtifactType == ONAP_VES_EVENTS) {
390                 internalNonManoFileList.forEach(this::validateYaml);
391             } else if (nonManoArtifactType == ONAP_SW_INFORMATION) {
392                 validateSoftwareInformationNonManoArtifact(files);
393             }
394         });
395
396         verifyFilesExist(packageFiles, nonManoValidFilePaths, MANIFEST_NON_MANO_SOURCE);
397
398         final Set<String> allReferredFiles = new HashSet<>();
399         allReferredFiles.addAll(sources);
400         allReferredFiles.addAll(nonManoValidFilePaths);
401         verifyFilesBeingReferred(allReferredFiles, packageFiles);
402     }
403
404     private void validateSoftwareInformationNonManoArtifact(final List<String> files) {
405         if (CollectionUtils.isEmpty(files)) {
406             reportError(ErrorLevel.ERROR, Messages.EMPTY_SW_INFORMATION_NON_MANO_ERROR.getErrorMessage());
407             return;
408         }
409         if (files.size() != 1) {
410             final String formattedFileList = files.stream()
411                     .map(filePath -> String.format("'%s'", filePath))
412                     .collect(Collectors.joining(", "));
413             reportError(ErrorLevel.ERROR,
414                     Messages.UNIQUE_SW_INFORMATION_NON_MANO_ERROR.formatMessage(formattedFileList));
415             return;
416         }
417         final String swInformationFilePath = files.get(0);
418         final byte[] swInformationYaml = contentHandler.getFileContent(swInformationFilePath);
419         final Optional<PnfSoftwareInformation> parsedYaml = SoftwareInformationArtifactYamlParser
420                 .parse(swInformationYaml);
421         if (!parsedYaml.isPresent()) {
422             reportError(ErrorLevel.ERROR,
423                     Messages.INVALID_SW_INFORMATION_NON_MANO_ERROR.formatMessage(swInformationFilePath));
424         } else {
425             final PnfSoftwareInformation pnfSoftwareInformation = parsedYaml.get();
426             if (!pnfSoftwareInformation.isValid()) {
427                 reportError(ErrorLevel.ERROR,
428                         Messages.INCORRECT_SW_INFORMATION_NON_MANO_ERROR.formatMessage(swInformationFilePath));
429             }
430         }
431     }
432
433     /**
434      * Validates if a YAML file has the correct extension, is not empty and the content is a valid YAML. Reports each
435      * error found.
436      *
437      * @param filePath the file path inside the package
438      */
439     private void validateYaml(final String filePath) {
440         if (!contentHandler.containsFile(filePath)) {
441             return;
442         }
443         final String fileExtension = getFileExtension(filePath);
444         if (!"yaml".equalsIgnoreCase(fileExtension) && !"yml".equalsIgnoreCase(fileExtension)) {
445             reportError(ErrorLevel.ERROR, Messages.INVALID_YAML_EXTENSION.formatMessage(filePath));
446             return;
447         }
448
449         try (final InputStream fileContent = contentHandler.getFileContentAsStream(filePath)) {
450             if (fileContent == null) {
451                 reportError(ErrorLevel.ERROR, Messages.EMPTY_YAML_FILE_1.formatMessage(filePath));
452                 return;
453             }
454             new Yaml().loadAll(fileContent).iterator().next();
455         } catch (final IOException e) {
456             final String errorMsg = Messages.FILE_LOAD_CONTENT_ERROR.formatMessage(filePath);
457             reportError(ErrorLevel.ERROR, errorMsg);
458             LOGGER.debug(errorMsg, e);
459         } catch (final Exception e) {
460             final String message = Messages.INVALID_YAML_FORMAT_1.formatMessage(filePath, e.getMessage());
461             LOGGER.debug(message, e);
462             reportError(ErrorLevel.ERROR, message);
463         }
464     }
465
466     /**
467      * Checks if all package files are referred in manifest. Reports missing references.
468      *
469      * @param referredFileSet the list of referred files path
470      * @param packageFileSet  the list of package file path
471      */
472     private void verifyFilesBeingReferred(final Set<String> referredFileSet, final Set<String> packageFileSet) {
473         packageFileSet.forEach(filePath -> {
474             if (!isManifestFile(filePath) && !referredFileSet.contains(filePath)) {
475                 reportError(ErrorLevel.ERROR,
476                         String.format(Messages.MISSING_MANIFEST_REFERENCE.getErrorMessage(), filePath));
477             }
478         });
479     }
480
481     private boolean isManifestFile(final String filePath) {
482         return filePath.equals(toscaMetadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName()));
483     }
484
485     private void validateOtherEntries(final String folderPath) {
486         if (!verifyFoldersExist(folderList, folderPath)) {
487             reportError(ErrorLevel.ERROR, String.format(Messages.METADATA_MISSING_OPTIONAL_FOLDERS.getErrorMessage(),
488                     folderPath));
489         }
490     }
491
492     private void validateCertificate(final String file) {
493         final Set<String> packageFiles = contentHandler.getFileList();
494         if (!verifyFileExist(packageFiles, file)) {
495             reportError(ErrorLevel.ERROR,
496                     String.format(Messages.MISSING_METADATA_FILES.getErrorMessage(), file, file));
497         }
498     }
499
500     private boolean verifyFoldersExist(final Set<String> folderList, final String folderPath) {
501         return folderList.contains(folderPath + "/");
502     }
503
504     private void verifyFilesExist(final Set<String> existingFiles, final List<String> sources, final String type) {
505         sources.forEach(file -> {
506             if (!existingFiles.contains(file)) {
507                 reportError(ErrorLevel.ERROR,
508                         String.format(Messages.MISSING_MANIFEST_SOURCE.getErrorMessage(), type, file));
509             }
510         });
511     }
512
513     private boolean verifyFileExist(final Set<String> existingFiles, final String file) {
514         return existingFiles.contains(file);
515     }
516
517     private void validateChangeLog(final String filePath) {
518         if (!verifyFileExists(contentHandler.getFileList(), filePath)) {
519             reportError(ErrorLevel.ERROR, String.format(Messages.MISSING_METADATA_FILES.getErrorMessage(), filePath));
520         }
521     }
522
523     protected void reportError(final ErrorLevel errorLevel, final String errorMessage) {
524         errorsByFile.add(new ErrorMessage(errorLevel, errorMessage));
525     }
526
527     protected boolean validMetaLimit(Map<String, String> metadata) {
528         return metadata.size() == MANIFEST_METADATA_LIMIT;
529     }
530
531     protected ImmutableSet<String> getManifestMetadata(final Map<String, String> metadata) {
532         return isPnfMetadata(metadata) ? MANIFEST_PNF_METADATA : MANIFEST_VNF_METADATA;
533     }
534
535     protected void handleOtherEntry(final Map.Entry<String, String> entry) {
536         reportError(ErrorLevel.ERROR, Messages.METADATA_UNSUPPORTED_ENTRY.formatMessage(entry.getKey()));
537         LOGGER.warn(Messages.METADATA_UNSUPPORTED_ENTRY.getErrorMessage(), entry.getKey());
538     }
539
540     private Map<String, List<ErrorMessage>> getAnyValidationErrors() {
541         if (errorsByFile.isEmpty()) {
542             return Collections.emptyMap();
543         }
544         final Map<String, List<ErrorMessage>> errors = new HashMap<>();
545         errors.put(SdcCommon.UPLOAD_FILE, errorsByFile);
546         return errors;
547     }
548
549     private void validatePMDictionaryContentsAgainstSchema() {
550         final Stream<byte[]> pmDictionaryFiles = new FileExtractor(getEtsiEntryManifestPath(), contentHandler)
551             .findFiles(ONAP_PM_DICTIONARY);
552         new PMDictionaryValidator()
553             .validate(pmDictionaryFiles, (String message) -> reportError(ErrorLevel.ERROR, message));
554     }
555
556     private String getEtsiEntryManifestPath() {
557         return toscaMetadata.getMetaEntries().get(ETSI_ENTRY_MANIFEST.getName());
558     }
559 }