/* * Copyright 2016-present Open Networking Laboratory * * 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.onosproject.yms.app.ysr; import org.onosproject.yangutils.datamodel.YangInclude; import org.onosproject.yangutils.datamodel.YangModule; import org.onosproject.yangutils.datamodel.YangNode; import org.onosproject.yangutils.datamodel.YangSchemaNode; import org.onosproject.yangutils.datamodel.YangSubModule; import org.onosproject.yangutils.datamodel.exceptions.DataModelException; import org.onosproject.yms.ysr.YangModuleIdentifier; import org.onosproject.yms.ysr.YangModuleInformation; import org.onosproject.yms.ysr.YangModuleLibrary; import org.slf4j.Logger; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; import static java.util.Collections.sort; import static org.apache.commons.io.FileUtils.deleteDirectory; import static org.onosproject.yangutils.datamodel.utils.DataModelUtils.deSerializeDataModel; import static org.onosproject.yangutils.utils.UtilConstants.EVENT_STRING; import static org.onosproject.yangutils.utils.UtilConstants.HYPHEN; import static org.onosproject.yangutils.utils.UtilConstants.OP_PARAM; import static org.onosproject.yangutils.utils.UtilConstants.PERIOD; import static org.onosproject.yangutils.utils.UtilConstants.SERVICE; import static org.onosproject.yangutils.utils.UtilConstants.SLASH; import static org.onosproject.yangutils.utils.io.impl.YangIoUtils.getCapitalCase; import static org.slf4j.LoggerFactory.getLogger; /** * Representation of default YANG schema registry. Yang schema registry * provides interface to an application to register its YANG schema * with YMS. It provides YANG schema nodes to YDT, YNB and YSB. */ public class MockYangSchemaRegistry implements YangSchemaRegistry { private static final String SYSTEM = SLASH + "system" + SLASH; private static final String MAVEN = "mvn:"; private static final String JAR = ".jar"; private static final String USER_DIRECTORY = "user.dir"; private static final String AT = "@"; private static final String DATE_FORMAT = "yyyy-mm-dd"; private static final String ONOS = "org.onosproject"; private static final Logger log = getLogger(MockYangSchemaRegistry.class); private static final String FS = File.separator; private static final String USER_DIR = System.getProperty("user.dir").replaceAll("ea1000driver", "ea1000yang"); // private static final String BUCK_OUT_DIR = "/buck-out/gen/drivers/microsemi/ea1000yang/"; private static final String BUCK_OUT_BIN_LOC = "/buck-out/bin/drivers/microsemi/ea1000yang/" + "lib__onos-drivers-microsemi-ea1000yang__classes/YangMetaData.ser"; private static final String PATH = FS + "target" + FS + "classes" + FS; private static final String SER_FILE_PATH = "yang" + FS + "resources" + FS + "YangMetaData.ser"; private static final String RESOURCE = "src/test/resources"; /* * Map for storing app objects. */ private final ConcurrentMap<String, Object> appObjectStore; /* * Map for storing YANG schema nodes. */ private final ConcurrentMap<String, ConcurrentMap<String, YangSchemaNode>> yangSchemaStore; /* * Map for storing YANG schema nodes with respect to root's generated * interface file name. */ private final ConcurrentMap<String, YangSchemaNode> interfaceNameKeyStore; /* * Map for storing YANG schema nodes root's generated op param file name. */ private final ConcurrentMap<String, YangSchemaNode> opParamNameKeyStore; /* * Map for storing YANG schema nodes with respect to notifications. */ private final ConcurrentMap<String, YangSchemaNode> eventNameKeyStore; /* * Map for storing YANG schema nodes with respect to app name. */ private final ConcurrentMap<String, YangSchemaNode> appNameKeyStore; /* * Map for storing registered classes. */ private final ConcurrentMap<String, Class<?>> registerClassStore; /* * Map for storing YANG file details. */ private final ConcurrentMap<YangModuleIdentifier, String> yangFileStore; /** * Map for storing schema nodes with respect to namespace. */ private final ConcurrentMap<String, YangSchemaNode> nameSpaceSchemaStore; private final ConcurrentMap<Object, Boolean> ynhRegistrationStore; private final ConcurrentMap<String, String> jarPathStore; /** * Creates an instance of default YANG schema registry. */ public MockYangSchemaRegistry() { appObjectStore = new ConcurrentHashMap<>(); yangSchemaStore = new ConcurrentHashMap<>(); interfaceNameKeyStore = new ConcurrentHashMap<>(); opParamNameKeyStore = new ConcurrentHashMap<>(); eventNameKeyStore = new ConcurrentHashMap<>(); registerClassStore = new ConcurrentHashMap<>(); yangFileStore = new ConcurrentHashMap<>(); appNameKeyStore = new ConcurrentHashMap<>(); ynhRegistrationStore = new ConcurrentHashMap<>(); jarPathStore = new ConcurrentHashMap<>(); nameSpaceSchemaStore = new ConcurrentHashMap<>(); } /** * This is overridden for Maven and Buck. * * Because they don't have a Bundle Context, and the JAR file doesn't exist * when this is called, we have to work with the .ser file */ @Override public void registerApplication(Object appObject, Class<?> serviceClass) { synchronized (MockYangSchemaRegistry.class) { doPreProcessing(serviceClass, appObject); if (!verifyIfApplicationAlreadyRegistered(serviceClass)) { List<YangNode> curNodes = new ArrayList<>(); Path serFile = Paths.get(USER_DIR, PATH, SER_FILE_PATH); if (USER_DIR.contains("ea1000yang")) { serFile = Paths.get(USER_DIR, PATH, SER_FILE_PATH); } else { serFile = Paths.get(USER_DIR, BUCK_OUT_BIN_LOC); } if (Files.notExists(serFile)) { throw new UncheckedIOException( new IOException("File " + serFile.toString() + " does not exist!")); } try { curNodes.addAll(deSerializeDataModel(serFile.toString())); } catch (IOException e) { throw new UncheckedIOException(e); } // process application registration. if (curNodes != null && !curNodes.isEmpty()) { jarPathStore.put(serviceClass.getName(), serFile.toString()); processRegistration(serviceClass, serFile.toString(), curNodes, appObject, false); } else { throw new UncheckedIOException( new IOException("Unable to find Yang Nodes in serFile: " + serFile.toString())); } } } } @Override public void unRegisterApplication(Object managerObject, Class<?> serviceClass) { synchronized (MockYangSchemaRegistry.class) { YangSchemaNode curNode; String serviceName = serviceClass.getName(); //Check if service should be unregistered? if (managerObject != null) { verifyApplicationRegistration(managerObject, serviceClass); } //Remove registered class from store. registerClassStore.remove(serviceName); //check if service is in app store. curNode = appNameKeyStore.get(serviceName); if (curNode == null) { curNode = interfaceNameKeyStore.get(serviceName); } if (curNode != null) { removeSchemaNode(curNode); eventNameKeyStore.remove(getEventClassName(curNode)); appObjectStore.remove(serviceName); interfaceNameKeyStore.remove(getInterfaceClassName(curNode)); opParamNameKeyStore.remove(getOpParamClassName(curNode)); yangFileStore.remove(getModuleIdentifier(curNode)); appNameKeyStore.remove(serviceName); nameSpaceSchemaStore.remove(curNode.getNameSpace() .getModuleNamespace()); removeYsrGeneratedTemporaryResources(jarPathStore.get(serviceName), serviceName); log.info(" service {} is unregistered.", serviceClass.getSimpleName()); } else { throw new RuntimeException(serviceClass.getSimpleName() + " service was not registered."); } } } @Override public Object getRegisteredApplication(YangSchemaNode schemaNode) { Object obj = null; if (schemaNode != null) { String name = getServiceName(schemaNode); obj = appObjectStore.get(name); if (obj == null) { log.error("{} not found.", name); } } return obj; } @Override public YangSchemaNode getYangSchemaNodeUsingSchemaName(String schemaName) { return getSchemaNodeUsingSchemaNameWithRev(schemaName); } @Override public YangSchemaNode getYangSchemaNodeUsingAppName(String appName) { YangSchemaNode node = appNameKeyStore.get(appName); if (node == null) { log.error("{} not found.", appName); } return node; } @Override public YangSchemaNode getYangSchemaNodeUsingGeneratedRootNodeInterfaceFileName(String name) { YangSchemaNode node = interfaceNameKeyStore.get(name); if (node == null) { log.error("{} not found.", name); } return node; } @Override public YangSchemaNode getYangSchemaNodeUsingGeneratedRootNodeOpPramFileName( String name) { YangSchemaNode node = opParamNameKeyStore.get(name); if (node == null) { log.error("{} not found.", name); } return node; } @Override public YangSchemaNode getRootYangSchemaNodeForNotification(String name) { YangSchemaNode node = eventNameKeyStore.get(name); if (node == null) { log.error("{} not found.", name); } return node; } @Override public Class<?> getRegisteredClass(YangSchemaNode schemaNode) { String interfaceName = getInterfaceClassName(schemaNode); String serviceName = getServiceName(schemaNode); Class<?> regClass = registerClassStore.get(serviceName); if (regClass == null) { regClass = registerClassStore.get(interfaceName); } return regClass; } @Override public YangSchemaNode getSchemaWrtNameSpace(String nameSpace) { YangSchemaNode node = nameSpaceSchemaStore.get(nameSpace); if (node == null) { log.error("node with {} namespace not found.", nameSpace); } return node; } @Override public String getYangFile(YangModuleIdentifier moduleIdentifier) { String file = yangFileStore.get(moduleIdentifier); if (file == null) { log.error("YANG files for corresponding module identifier {} not " + "found", moduleIdentifier); } return file; } @Override public boolean verifyNotificationObject(Object appObj, Class<?> service) { synchronized (MockYangSchemaRegistry.class) { YangSchemaNode node = appNameKeyStore.get(service.getName()); if (node == null) { log.error("application is not registered with YMS {}", service.getName()); return false; } try { if (node.isNotificationPresent()) { if (appObj != null) { Boolean ifPresent = ynhRegistrationStore.get(appObj); if (ifPresent == null) { ynhRegistrationStore.put(appObj, true); return true; } } } } catch (DataModelException e) { log.error("notification registration error: {} {}", e .getLocalizedMessage(), e); } return false; } } @Override public void flushYsrData() { appObjectStore.clear(); yangSchemaStore.clear(); eventNameKeyStore.clear(); opParamNameKeyStore.clear(); interfaceNameKeyStore.clear(); registerClassStore.clear(); yangFileStore.clear(); nameSpaceSchemaStore.clear(); } @Override public void processModuleLibrary(String serviceName, YangModuleLibrary library) { synchronized (MockYangSchemaRegistry.class) { YangSchemaNode node = appNameKeyStore.get(serviceName); if (node != null) { YangModuleInformation moduleInformation = new DefaultYangModuleInformation(getModuleIdentifier(node), node.getNameSpace()); addSubModuleIdentifier(node, ( DefaultYangModuleInformation) moduleInformation); //TODO: add feature list to module information. ((DefaultYangModuleLibrary) library) .addModuleInformation(moduleInformation); } } } /** * Process service class. * * @param serviceClass service class * @param appObject application object */ void doPreProcessing(Class<?> serviceClass, Object appObject) { //Check if service should be registered? if (appObject != null) { verifyApplicationRegistration(appObject, serviceClass); } String name = serviceClass.getName(); //Add app class to registered service store. if (!registerClassStore.containsKey(name)) { registerClassStore.put(name, serviceClass); } } void updateServiceClass(Class<?> service) { registerClassStore.put(service.getName(), service); } /** * Process application registration. * * @param service service class * @param jarPath jar path * @param nodes YANG nodes * @param appObj application object * @param isFromUt if registration is being called form unit test */ void processRegistration(Class<?> service, String jarPath, List<YangNode> nodes, Object appObj, boolean isFromUt) { // process storing operations. YangNode schemaNode = findNodeWhichShouldBeReg(service.getName(), nodes); if (schemaNode != null) { if (appObj != null) { appObjectStore.put(service.getName(), appObj); } //Process application context for registrations. processApplicationContext(schemaNode, service.getName(), isFromUt); //Update YANG file store. updateYangFileStore(schemaNode, jarPath); } } /** * Returns the node for which corresponding class is generated. * * @param name generated class name * @param nodes list of yang nodes * @return node for which corresponding class is generated */ private YangNode findNodeWhichShouldBeReg(String name, List<YangNode> nodes) { for (YangNode node : nodes) { if (name.equals(getServiceName(node)) || name.equals(getInterfaceClassName(node))) { return node; } } return null; } /** * Verifies if service class should be registered or not. * * @param appObject application object * @param appClass application class */ private void verifyApplicationRegistration(Object appObject, Class<?> appClass) { Class<?> managerClass = appObject.getClass(); Class<?>[] services = managerClass.getInterfaces(); List<Class<?>> classes = new ArrayList<>(); Collections.addAll(classes, services); if (!classes.contains(appClass)) { throw new RuntimeException("service class " + appClass.getName() + "is not being implemented by " + managerClass.getName()); } } /** * Verifies if application is already registered with YMS. * * @param appClass application class * @return true if application already registered */ private boolean verifyIfApplicationAlreadyRegistered(Class<?> appClass) { String appName = appClass.getName(); return appObjectStore.containsKey(appName) || interfaceNameKeyStore.containsKey(appName); } /** * Updates yang file store for YANG node. * * @param node YANG node * @param jarPath jar file path */ private void updateYangFileStore(YangNode node, String jarPath) { yangFileStore.put(getModuleIdentifier(node), getYangFilePath(jarPath, node.getFileName())); } /** * Returns yang file path. * * @param jarPath jar path * @param metaDataFileName name of yang file from metadata * @return yang file path */ private String getYangFilePath(String jarPath, String metaDataFileName) { String[] metaData = metaDataFileName.split(SLASH); return jarPath + SLASH + metaData[metaData.length - 1]; } /** * Process an application an updates the maps for YANG schema registry. * * @param appNode application YANG schema nodes * @param name class name * @param isFormUt if method is being called from unit tests */ private void processApplicationContext(YangSchemaNode appNode, String name, boolean isFormUt) { //Update map for which registrations is being called. appNameKeyStore.put(name, appNode); // Updates schema store. addToSchemaStore(appNode); // update interface store. interfaceNameKeyStore.put(getInterfaceClassName(appNode), appNode); //update op param store. opParamNameKeyStore.put(getOpParamClassName(appNode), appNode); //update namespaceSchema store. nameSpaceSchemaStore.put(appNode.getNameSpace().getModuleNamespace(), appNode); //Checks if notification is present then update notification store map. String eventSubject = null; try { if (appNode.isNotificationPresent()) { eventSubject = getEventClassName(appNode); } } catch (DataModelException e) { log.error("failed to search notification from schema map : {}", e.getLocalizedMessage()); } if (eventSubject != null) { eventNameKeyStore.put(eventSubject, appNode); } if (!isFormUt) { log.info("successfully registered this application {}", name); } } /** * Returns jar path from bundle mvnLocationPath. * * @param mvnLocationPath mvnLocationPath of bundle * @return path of jar */ private String getJarPathFromBundleLocation(String mvnLocationPath, String currentDirectory) { String path = currentDirectory + SYSTEM; if (mvnLocationPath.contains(MAVEN)) { String[] strArray = mvnLocationPath.split(MAVEN); if (strArray[1].contains(File.separator)) { String[] split = strArray[1].split(File.separator); if (split[0].contains(PERIOD)) { String[] groupId = split[0].split(Pattern.quote(PERIOD)); return path + groupId[0] + SLASH + groupId[1] + SLASH + split[1] + SLASH + split[2] + SLASH + split[1] + HYPHEN + split[2]; } } } return null; } /** * Returns schema node based on the revision. * * @param name name of the schema node * @return schema node based on the revision */ private YangSchemaNode getSchemaNodeUsingSchemaNameWithRev(String name) { ConcurrentMap<String, YangSchemaNode> revMap; YangSchemaNode schemaNode; if (name.contains(AT)) { String[] revArray = name.split(AT); revMap = yangSchemaStore.get(revArray[0]); schemaNode = revMap.get(name); if (schemaNode == null) { log.error("{} not found.", name); } return schemaNode; } if (yangSchemaStore.containsKey(name)) { revMap = yangSchemaStore.get(name); if (revMap != null && !revMap.isEmpty()) { YangSchemaNode node = revMap.get(name); if (node != null) { return node; } String revName = getLatestVersion(revMap); return revMap.get(revName); } } log.error("{} not found.", name); return null; } private String getLatestVersion(ConcurrentMap<String, YangSchemaNode> revMap) { List<String> keys = new ArrayList<>(); for (Map.Entry<String, YangSchemaNode> entry : revMap.entrySet()) { keys.add(entry.getKey()); } sort(keys); return keys.get(keys.size() - 1); } /** * Adds schema node when different revision of node has received. * * @param schemaNode schema node */ private void addToSchemaStore(YangSchemaNode schemaNode) { String date = getDateInStringFormat(schemaNode); String name = schemaNode.getName(); String revName = name; if (date != null) { revName = name + AT + date; } //check if already present. if (!yangSchemaStore.containsKey(name)) { ConcurrentMap<String, YangSchemaNode> revStore = new ConcurrentHashMap<>(); revStore.put(revName, schemaNode); yangSchemaStore.put(name, revStore); } else { yangSchemaStore.get(name).put(revName, schemaNode); } } /** * Returns date in string format. * * @param schemaNode schema node * @return date in string format */ String getDateInStringFormat(YangSchemaNode schemaNode) { if (schemaNode != null) { if (((YangNode) schemaNode).getRevision() != null) { return new SimpleDateFormat(DATE_FORMAT) .format(((YangNode) schemaNode).getRevision() .getRevDate()); } } return null; } /** * Removes schema node from schema map. * * @param removableNode schema node which needs to be removed */ private void removeSchemaNode(YangSchemaNode removableNode) { String name = removableNode.getName(); String revName = name; String date = getDateInStringFormat(removableNode); if (date != null) { revName = name + AT + date; } ConcurrentMap<String, YangSchemaNode> revMap = yangSchemaStore.get(name); if (revMap != null && !revMap.isEmpty() && revMap.size() != 1) { revMap.remove(revName); } else { yangSchemaStore.remove(removableNode.getName()); } } /** * Adds sub module identifier. * * @param node schema node * @param information module information */ private void addSubModuleIdentifier( YangSchemaNode node, DefaultYangModuleInformation information) { List<YangInclude> includeList = new ArrayList<>(); if (node instanceof YangModule) { includeList = ((YangModule) node).getIncludeList(); } else if (node instanceof YangSubModule) { includeList = ((YangSubModule) node).getIncludeList(); } for (YangInclude include : includeList) { information.addSubModuleIdentifiers(getModuleIdentifier( include.getIncludedNode())); } } /** * Returns module identifier for schema node. * * @param schemaNode schema node * @return module identifier for schema node */ private YangModuleIdentifier getModuleIdentifier( YangSchemaNode schemaNode) { return new DefaultYangModuleIdentifier( schemaNode.getName(), getDateInStringFormat(schemaNode)); } /** * Returns schema node's generated interface class name. * * @param schemaNode schema node * @return schema node's generated interface class name */ String getInterfaceClassName(YangSchemaNode schemaNode) { return schemaNode.getJavaPackage() + PERIOD + getCapitalCase(schemaNode.getJavaClassNameOrBuiltInType()); } /** * Returns schema node's generated op param class name. * * @param schemaNode schema node * @return schema node's generated op param class name */ private String getOpParamClassName(YangSchemaNode schemaNode) { return getInterfaceClassName(schemaNode) + OP_PARAM; } /** * Returns schema node's generated event class name. * * @param schemaNode schema node * @return schema node's generated event class name */ private String getEventClassName(YangSchemaNode schemaNode) { return getInterfaceClassName(schemaNode).toLowerCase() + PERIOD + getCapitalCase(schemaNode.getJavaClassNameOrBuiltInType()) + EVENT_STRING; } /** * Returns schema node's generated service class name. * * @param schemaNode schema node * @return schema node's generated service class name */ String getServiceName(YangSchemaNode schemaNode) { return getInterfaceClassName(schemaNode) + SERVICE; } /** * Removes YSR generated temporary resources. * * @param rscPath resource path * @param appName application name */ private void removeYsrGeneratedTemporaryResources(String rscPath, String appName) { if (rscPath != null) { File jarPath = new File(rscPath); if (jarPath.exists()) { try { deleteDirectory(jarPath); } catch (IOException e) { log.error("failed to delete ysr resources for {} : {}", appName, e.getLocalizedMessage()); } } } } }