/* * Copyright 2017 Huawei Technologies Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onap.cli.fw.utils; import static org.onap.cli.fw.conf.OnapCommandConstants.ATTRIBUTES; import static org.onap.cli.fw.conf.OnapCommandConstants.DEAFULT_INPUT_PARAMETERS_NAME; import static org.onap.cli.fw.conf.OnapCommandConstants.DEFAULT_SCHEMA_PATH_PATERN; import static org.onap.cli.fw.conf.OnapCommandConstants.DESCRIPTION; import static org.onap.cli.fw.conf.OnapCommandConstants.DISCOVERY_FILE; import static org.onap.cli.fw.conf.OnapCommandConstants.IS_DEFAULT_PARAM; import static org.onap.cli.fw.conf.OnapCommandConstants.NAME; import static org.onap.cli.fw.conf.OnapCommandConstants.OPEN_CLI_SAMPLE_VERSION; import static org.onap.cli.fw.conf.OnapCommandConstants.OPEN_CLI_SCHEMA_VERSION; import static org.onap.cli.fw.conf.OnapCommandConstants.PARAMETERS; import static org.onap.cli.fw.conf.OnapCommandConstants.RESULTS; import static org.onap.cli.fw.conf.OnapCommandConstants.SCHEMA_DIRECTORY; import static org.onap.cli.fw.conf.OnapCommandConstants.SCHEMA_PATH_PATERN; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.ServiceLoader; import org.apache.commons.io.FileUtils; import org.onap.cli.fw.cmd.OnapCommand; import org.onap.cli.fw.conf.OnapCommandConfig; import org.onap.cli.fw.conf.OnapCommandConstants; import org.onap.cli.fw.error.OnapCommandDiscoveryFailed; import org.onap.cli.fw.error.OnapCommandException; import org.onap.cli.fw.error.OnapCommandInstantiationFailed; import org.onap.cli.fw.error.OnapCommandInvalidSample; import org.onap.cli.fw.error.OnapCommandInvalidSchema; import org.onap.cli.fw.error.OnapCommandNotFound; import org.onap.cli.fw.schema.OnapCommandSchemaInfo; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import com.esotericsoftware.yamlbeans.YamlReader; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.stream.JsonReader; import java.io.FileReader; import java.io.Writer; import java.io.FileWriter; import java.io.InputStream; import java.io.InputStreamReader; import java.io.FileInputStream; public class OnapCommandDiscoveryUtils { private OnapCommandDiscoveryUtils() { throw new IllegalStateException("Utility class"); } private static Gson gson = new GsonBuilder().serializeNulls().create(); /** * Fetch a particular schema details. * * @param cmd * command name * @return ExternalSchema obj * @throws OnapCommandInvalidSchema * exception * @throws OnapCommandDiscoveryFailed * exception */ public static OnapCommandSchemaInfo getSchemaInfo(String cmd, String version) throws OnapCommandException { List list = OnapCommandDiscoveryUtils.discoverOrLoadSchemas(false); OnapCommandSchemaInfo schemaInfo = null; if (list != null) { //NOSONAR for (OnapCommandSchemaInfo schema : list) { if (cmd.equals(schema.getCmdName()) && version.equals(schema.getProduct())) { schemaInfo = schema; break; } } } if (schemaInfo == null) throw new OnapCommandNotFound(cmd, version); return schemaInfo; } /** * Load the previous discovered json file. * * @return list * @throws OnapCommandInvalidSchema * exception * @throws OnapCommandDiscoveryFailed * exception */ public static List discoverOrLoadSchemas(boolean forceRefresh) throws OnapCommandException { List schemas = new ArrayList<>(); //NOSONAR if (forceRefresh || Boolean.parseBoolean(OnapCommandConfig.getPropertyValue(OnapCommandConstants.DISCOVER_ALWAYS)) || !OnapCommandDiscoveryUtils.isAlreadyDiscovered()) { schemas = OnapCommandDiscoveryUtils.discoverSchemas(); if (!schemas.isEmpty()) { //merge the existing RPC schema with discovered ones List schemasExisting = OnapCommandDiscoveryUtils.loadSchemas(); Map rpcCommands = new HashMap<>(); for (OnapCommandSchemaInfo info: schemasExisting) { if (info.isRpc()) { rpcCommands.put(info.getProduct() + ":" + info.getCmdName(), info); } } //mrkanag: Enable clustering for keeping command in more than one OCLIP engine //Remove if already an same command exists with RPC for (OnapCommandSchemaInfo info: schemas) { OnapCommandSchemaInfo infoExisting = rpcCommands.get(info.getProduct() + ":" + info.getCmdName()); if (infoExisting != null) { rpcCommands.remove(info.getProduct() + ":" + info.getCmdName()); } } //Add all RPC ones schemas.addAll(rpcCommands.values()); OnapCommandDiscoveryUtils.persistSchemaInfo(schemas); } } else { schemas = OnapCommandDiscoveryUtils.loadSchemas(); } return schemas; } public static String getDataStorePath() { return OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_DATA_DIR); } public static List loadSchemas() throws OnapCommandException { List schemas = new ArrayList<>(); if (!OnapCommandDiscoveryUtils.isAlreadyDiscovered()) return schemas; String dataDir = OnapCommandDiscoveryUtils.getDataStorePath(); File file = new File(dataDir + File.separator + DISCOVERY_FILE); try (JsonReader jsonReader = new JsonReader(new FileReader(file))){ OnapCommandSchemaInfo[] list = gson.fromJson(jsonReader, OnapCommandSchemaInfo[].class); schemas.addAll(Arrays.asList(list)); } catch (Exception e) { // NOSONAR throw new OnapCommandDiscoveryFailed(dataDir, DISCOVERY_FILE, e); } return schemas; } /** * Check if json file discovered or not. * * @return boolean * @throws OnapCommandDiscoveryFailed * exception */ public static boolean isAlreadyDiscovered() throws OnapCommandDiscoveryFailed { //NOSONAR String dataDir = OnapCommandDiscoveryUtils.getDataStorePath(); return new File(dataDir + File.separator + DISCOVERY_FILE).exists(); } /** * Persist the external schema details. * * @param schemas * list * @throws OnapCommandDiscoveryFailed * exception */ public static void persistSchemaInfo(List schemas) throws OnapCommandDiscoveryFailed { if (schemas != null) { String dataDir = OnapCommandDiscoveryUtils.getDataStorePath(); try { FileUtils.forceMkdir(new File(dataDir)); File file = new File(dataDir + File.separator + DISCOVERY_FILE); try(Writer writer = new FileWriter(file)){ gson.toJson(schemas,writer); } } catch (Exception e1) { // NOSONAR throw new OnapCommandDiscoveryFailed(dataDir, DISCOVERY_FILE, e1); } } } /** * Get schema map. * * @param resource * resource obj * @return map * @throws OnapCommandInvalidSchema * exception */ public static Map loadSchema(Resource resource) throws OnapCommandInvalidSchema { return loadYaml(resource); } /** * Returns a resource available under certain directory in class-path. * * @param pattern * search pattern * @return found resource * @throws IOException * exception */ public static Resource findResource(String fileName, String pattern) throws IOException { Resource[] resources = OnapCommandDiscoveryUtils.findResources(pattern); if (resources != null && resources.length > 0) { //NOSONAR for (Resource res : resources) { if ((res.getFilename() != null) && (res.getFilename().equals(fileName))) { //NOSONAR return res; } } } return null; } /** * Returns all resources available under certain directory in class-path. * * @param pattern * search pattern * @return resources found resources * @throws IOException * exception */ public static Resource[] findResources(String pattern) throws IOException { ClassLoader cl = OnapCommandUtils.class.getClassLoader(); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl); return resolver.getResources("classpath*:" + pattern); } public static String identitySchemaProfileType(Map schemaYamlMap) { for (String schemeType : OnapCommandConfig.getCommaSeparatedList(OnapCommandConstants.SCHEMA_TYPES_SUPPORTED)) { if (schemaYamlMap.get(schemeType) != null) { return schemeType; } } return OnapCommandConstants.BASIC_SCHEMA_PROFILE; } /** * Find external schema files. * * @return list ExternalSchema * @throws OnapCommandDiscoveryFailed * exception * @throws OnapCommandInvalidSchema * exception */ public static List discoverSchemas() throws OnapCommandException { List extSchemas = new ArrayList<>(); try { //collect default input parameters for every profile Resource[] deafultRres = findResources(DEFAULT_SCHEMA_PATH_PATERN); Map > defaultInputs = new HashMap<>(); if (deafultRres != null && deafultRres.length > 0) { Map deafultResourceMap; for (Resource resource : deafultRres) { deafultResourceMap = loadYaml(resource, true); if (deafultResourceMap != null && deafultResourceMap.size() > 0) { //default_input_parameters_http.yaml String profileName = resource.getFilename().substring( DEAFULT_INPUT_PARAMETERS_NAME.length() + 1, resource.getFilename().indexOf('.')); if (deafultResourceMap.containsKey(PARAMETERS)) { List params = new ArrayList<>(); for (Map p: (List>) deafultResourceMap.get(PARAMETERS)) { if (p.keySet().contains(IS_DEFAULT_PARAM) && ! (Boolean.getBoolean(String.valueOf(p.get(IS_DEFAULT_PARAM))))) { params.add(p); } } defaultInputs.put(profileName, params); } } } } Resource[] res = findResources(SCHEMA_PATH_PATERN); if (res != null && res.length > 0) { for (Resource resource : res) { Map resourceMap; try { //NOSONAR resourceMap = loadYaml(resource); } catch (OnapCommandException e) { String resourceURI = resource.getURI().toString(); OnapCommandUtils.log.error("Ignores invalid schema {} {}", resourceURI, e); continue; } if (resourceMap != null && resourceMap.size() > 0) { OnapCommandSchemaInfo schema = new OnapCommandSchemaInfo(); schema.setSchemaURI(resource.getURI().toString()); Object obj = resourceMap.get(OPEN_CLI_SCHEMA_VERSION); if (obj == null) { String schemaURI = schema.getSchemaURI(); OnapCommandUtils.log.info("Invalid Schema yaml {}", schemaURI); } else{ schema.setVersion(obj.toString()); if (!schema.getVersion().equalsIgnoreCase(OnapCommandConstants.OPEN_CLI_SCHEMA_VERSION_VALUE_1_0)) { String schemaURI = schema.getSchemaURI(); OnapCommandUtils.log.info("Unsupported Schema version found {} ", schemaURI); } else{ //There are schema like default input parameters and does not have command name if (resourceMap.get(NAME) != null) { schema.setSchemaName(resource.getFilename()); schema.setCmdName((String) resourceMap.get(NAME)); schema.setDescription((String) resourceMap.get(DESCRIPTION)); Map infoMap = (Map) resourceMap.get(OnapCommandConstants.INFO); if (infoMap != null && infoMap.get(OnapCommandConstants.INFO_TYPE) != null) { schema.setType(infoMap.get(OnapCommandConstants.INFO_TYPE).toString()); } if (infoMap != null && infoMap.get(OnapCommandConstants.INFO_PRODUCT) != null) { schema.setProduct(infoMap.get(OnapCommandConstants.INFO_PRODUCT).toString()); } if (infoMap != null && infoMap.get(OnapCommandConstants.INFO_IGNORE) != null) { schema.setIgnore(infoMap.get(OnapCommandConstants.INFO_IGNORE).toString()); } if (infoMap != null && infoMap.get(OnapCommandConstants.INFO_STATE) != null) { schema.setState(infoMap.get(OnapCommandConstants.INFO_STATE).toString()); } if (infoMap != null && infoMap.get(OnapCommandConstants.INFO_SERVICE) != null) { schema.setService(infoMap.get(OnapCommandConstants.INFO_SERVICE).toString()); } if (infoMap != null && infoMap.get(OnapCommandConstants.INFO_AUTHOR) != null) { schema.setAuthor(infoMap.get(OnapCommandConstants.INFO_AUTHOR).toString()); } if (infoMap != null && infoMap.get(OnapCommandConstants.INFO_METADATA) != null) { schema.setMetadata((Map)infoMap.get(OnapCommandConstants.INFO_METADATA)); } schema.setSchemaProfile(identitySchemaProfileType(resourceMap)); if (resourceMap.containsKey(PARAMETERS)) { schema.setInputs((List)resourceMap.get(PARAMETERS)); if (defaultInputs.get(schema.getSchemaProfile()) != null) { schema.getInputs().addAll(defaultInputs.get(schema.getSchemaProfile())); } } if (resourceMap.containsKey(RESULTS)) { schema.setOutputs((List)((Map)resourceMap.get(RESULTS)).get(ATTRIBUTES)); } extSchemas.add(schema); } } } } } } } catch (IOException e) { throw new OnapCommandDiscoveryFailed(SCHEMA_DIRECTORY, e); } try { Resource[] samples = findResources(OnapCommandConstants.VERIFY_SAMPLES_FILE_PATTERN); for (Resource sample : samples) { updateSchemaInfoWithSample(sample, extSchemas); } } catch (IOException e) { throw new OnapCommandDiscoveryFailed(OnapCommandConstants.VERIFY_SAMPLES_DIRECTORY, e); } return extSchemas; } private static void updateSchemaInfoWithSample(Resource sampleResourse, List schemaInfos) throws OnapCommandInvalidSchema, IOException { Map infoMap = loadSchema(sampleResourse); if (infoMap == null) { return; } Object sampleVersion = infoMap.get(OPEN_CLI_SAMPLE_VERSION); if (sampleVersion == null) { OnapCommandUtils.log.info("Invalid Sample yaml {}", sampleResourse.getURI()); return; } if (!sampleVersion.toString().equalsIgnoreCase(OnapCommandConstants.OPEN_CLI_SAMPLE_VERSION_VALUE_1_0)) { OnapCommandUtils.log.info("Unsupported Sample version found {}", sampleResourse.getURI()); return; } String cmdName = (String) infoMap.get(OnapCommandConstants.VERIFY_CMD_NAME); String version = (String) infoMap.get(OnapCommandConstants.VERIFY_CMD_VERSION); Optional optSchemaInfo = schemaInfos.stream() .filter(e -> e.getCmdName().equals(cmdName) && e.getProduct().equals(version)) .findFirst(); if (optSchemaInfo.isPresent()) { OnapCommandSchemaInfo onapCommandSchemaInfo = optSchemaInfo.get(); onapCommandSchemaInfo.getSampleFiles().add(sampleResourse.getFilename()); } } /** * Discover the Oclip commands. * * @return list */ public static List> discoverCommandPlugins() { ServiceLoader loader = ServiceLoader.load(OnapCommand.class); List> clss = new ArrayList<>(); for (OnapCommand implClass : loader) { clss.add((Class) implClass.getClass()); } return clss; } /** * Instantiate command plugin * @throws OnapCommandInstantiationFailed */ public static OnapCommand loadCommandClass(Class cls) throws OnapCommandInstantiationFailed { try { Constructor constr = cls.getConstructor(); return (OnapCommand) constr.newInstance(); } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new OnapCommandInstantiationFailed(cls.getName(), e); } } public static List> createTestSuite(String cmd, String version) throws OnapCommandException { ArrayList> testSamples = new ArrayList<>(); List resources = new ArrayList<>(); OnapCommandSchemaInfo schemaInfo = getSchemaInfo(cmd, version); List sampleFiles = new ArrayList<>(); if (schemaInfo != null && !schemaInfo.getSampleFiles().isEmpty()) { //NOSONAR sampleFiles.addAll(schemaInfo.getSampleFiles()); } for (String sampleFile : sampleFiles) { try { Resource resource = OnapCommandDiscoveryUtils.findResource(sampleFile, OnapCommandConstants.VERIFY_SAMPLES_FILE_PATTERN); resources.add(resource); } catch (IOException e) { throw new OnapCommandInvalidSample("Sample file does not exist : " + sampleFile , e); } } for (Resource resource : resources) { Map stringMap = OnapCommandDiscoveryUtils.loadYaml(resource); Map> samples = (Map>) stringMap .get(OnapCommandConstants.VERIFY_SAMPLES); for (Map.Entry> entry : samples.entrySet()) { String sampleId=entry.getKey(); Map sample = entry.getValue(); List inputArgs = new ArrayList<>(); if (sample.get(OnapCommandConstants.VERIFY_INPUT) != null) { inputArgs.addAll(Arrays.asList(sample.get(OnapCommandConstants.VERIFY_INPUT).trim().split(" "))); } inputArgs.add(OnapCommandConstants.VERIFY_LONG_OPTION); HashMap map = new HashMap<>(); map.put(OnapCommandConstants.VERIFY_INPUT, inputArgs); map.put(OnapCommandConstants.VERIFY_OUPUT, sample.get(OnapCommandConstants.VERIFY_OUPUT)); map.put(OnapCommandConstants.VERIFY_MOCO, sample.get(OnapCommandConstants.VERIFY_MOCO)); map.put(OnapCommandConstants.VERIFY_SAMPLE_FILE_ID, resource.getFilename()); map.put(OnapCommandConstants.VERIFY_SAMPLE_ID, sampleId); testSamples.add(map); } } return testSamples; } /** * Get schema map. * * @param resource * resource obj * @return map * @throws OnapCommandInvalidSchema * exception */ public static Map loadYaml(Resource resource) throws OnapCommandInvalidSchema { Map values = null; try { values = loadYaml(resource.getInputStream()); } catch (Exception e) { throw new OnapCommandInvalidSchema(resource.getFilename(), e); } return values; } /** * Get schema map. * * @param resource * resource obj * ignoreInvalidSchema boolean * @return map * @throws OnapCommandInvalidSchema * exception */ public static Map loadYaml(Resource resource, boolean ignoreInvalidSchema) throws OnapCommandInvalidSchema, IOException {//NOSONAR Map values = null; try { values = loadYaml(resource.getInputStream()); } catch (OnapCommandException | IOException e) { OnapCommandUtils.log.error("Ignores invalid schema {} {}", resource.getURI(), e); } return values; } /** * Get schema map. * * @param filePath * @return map * @throws OnapCommandInvalidSchema * exception */ public static Map loadYaml(String filePath) throws OnapCommandInvalidSchema { Map values = null; try { values = loadYaml(new FileInputStream(new File(filePath))); } catch (Exception e) { throw new OnapCommandInvalidSchema(filePath, e); } return values; } /** * Get schema map. * * @param inputStream * @return map * @throws OnapCommandInvalidSchema * exception */ public static Map loadYaml(InputStream inputStream) throws OnapCommandInvalidSchema { Map values = null; try(InputStreamReader inputStreamReader = new InputStreamReader(inputStream);){ YamlReader reader = new YamlReader(inputStreamReader); values = (Map) reader.read(); } catch (IOException e) { throw new OnapCommandInvalidSchema(inputStream.getClass().getName(),e.getMessage()); } return values; } }