Mitigate issue when yuma is sending services/service/type with a prefix that is not referenced anywhere - it will be ignored.
When issuing edit-config with merge strategy, populate services as they appear in get-config.
Change-Id: I2c30f3b626fc25cfd3b46225d5f1c24109d655b7
Signed-off-by: Tomas Olvecky <tolvecky@cisco.com>
private final String nullableDescription, packageName, javaNamePrefix,
namespace;
- private final Map<String, String> providedServices;
+ private final Map<String /* java fully qualified name */, String/* identity local name */> providedServices;
private Collection<RuntimeBeanEntry> runtimeBeans;
return packageName;
}
+ /**
+ * @return services implemented by this module. Keys are fully qualified java names of generated
+ * ServiceInterface classes, values are identity local names.
+ */
public Map<String, String> getProvidedServices() {
return providedServices;
}
public interface YangStoreSnapshot extends AutoCloseable {
- Map<String, Map<String, ModuleMXBeanEntry>> getModuleMXBeanEntryMap();
+ Map<String/* Namespace from yang file */,
+ Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> getModuleMXBeanEntryMap();
Map<String, Entry<Module, String>> getModuleMap();
}
@Override
- public Object addingBundle(Bundle bundle, BundleEvent event) {
+ public synchronized Object addingBundle(Bundle bundle, BundleEvent event) {
// Ignore system bundle:
// system bundle might have config-api on classpath &&
return bundle;
}
- private void onSnapshotFailure(Bundle bundle, List<URL> addedURLs, Exception failureReason) {
+ private synchronized void onSnapshotFailure(Bundle bundle, List<URL> addedURLs, Exception failureReason) {
// inconsistent state
inconsistentBundlesToYangURLs.putAll(bundle, addedURLs);
consistentBundlesToYangURLs, inconsistentBundlesToYangURLs, failureReason);
logger.warn("Yang store is falling back on last consistent state containing {} files, inconsistent yang files size is {}, reason {}",
consistentBundlesToYangURLs.size(), inconsistentBundlesToYangURLs.size(), failureReason.toString());
+ cache.setInconsistentURLsForReporting(inconsistentBundlesToYangURLs.values());
}
- private void onSnapshotSuccess(Multimap<Bundle, URL> proposedNewState, YangStoreSnapshotImpl snapshot) {
+ private synchronized void onSnapshotSuccess(Multimap<Bundle, URL> proposedNewState, YangStoreSnapshotImpl snapshot) {
// consistent state
// merge into
consistentBundlesToYangURLs.clear();
inconsistentBundlesToYangURLs.clear();
updateCache(snapshot);
-
+ cache.setInconsistentURLsForReporting(Collections.<URL> emptySet());
logger.info("Yang store updated to new consistent state containing {} yang files", consistentBundlesToYangURLs.size());
logger.debug("Yang store updated to new consistent state containing {}", consistentBundlesToYangURLs);
}
- private void updateCache(YangStoreSnapshotImpl snapshot) {
+ private synchronized void updateCache(YangStoreSnapshotImpl snapshot) {
cache.cacheYangStore(consistentBundlesToYangURLs, snapshot);
}
}
class YangStoreCache {
+ private static final Logger logger = LoggerFactory.getLogger(YangStoreCache.class);
@GuardedBy("this")
private Set<URL> cachedUrls = null;
@GuardedBy("this")
private Optional<YangStoreSnapshot> cachedYangStoreSnapshot = getInitialSnapshot();
+ @GuardedBy("this")
+ private Collection<URL> inconsistentURLsForReporting = Collections.emptySet();
synchronized Optional<YangStoreSnapshot> getSnapshotIfPossible(Multimap<Bundle, URL> bundlesToYangURLs) {
Set<URL> urls = setFromMultimapValues(bundlesToYangURLs);
if (cachedUrls==null || cachedUrls.equals(urls)) {
Preconditions.checkState(cachedYangStoreSnapshot.isPresent());
YangStoreSnapshot freshSnapshot = new YangStoreSnapshotImpl(cachedYangStoreSnapshot.get());
+ if (inconsistentURLsForReporting.size() > 0){
+ logger.warn("Some yang URLs are ignored: {}", inconsistentURLsForReporting);
+ }
return Optional.of(freshSnapshot);
}
}
synchronized void cacheYangStore(Multimap<Bundle, URL> urls,
- YangStoreSnapshot yangStoreSnapshot) {
+ YangStoreSnapshot yangStoreSnapshot) {
this.cachedUrls = setFromMultimapValues(urls);
this.cachedYangStoreSnapshot = Optional.of(yangStoreSnapshot);
}
}
}
+ public synchronized void setInconsistentURLsForReporting(Collection<URL> urls){
+ inconsistentURLsForReporting = urls;
+ }
+
private Optional<YangStoreSnapshot> getInitialSnapshot() {
YangStoreSnapshot initialSnapshot = new YangStoreSnapshot() {
@Override
this.moduleMap = yangStoreSnapshot.getModuleMap();
}
+ /**
+ * @return all loaded config modules. Key of outer map is namespace of yang file.
+ * Key of inner map is name of module entry. Value is module entry.
+ */
@Override
public Map<String, Map<String, ModuleMXBeanEntry>> getModuleMXBeanEntryMap() {
return moduleMXBeanEntryMap;
Util.checkType(value, ObjectNameAttributeMappingStrategy.MappedDependency.class);
ObjectNameAttributeMappingStrategy.MappedDependency mappedDep = (ObjectNameAttributeMappingStrategy.MappedDependency) value;
- ServiceInstance byRefName = serviceTracker.getByServiceAndRefName(mappedDep.getServiceName(),
- mappedDep.getRefName());
+ String serviceName = mappedDep.getServiceName();
+ if (serviceName.contains(":")) {
+ // hack for yuma
+ serviceName = serviceName.substring(serviceName.indexOf(":") + 1);
+ }
+ String refName = mappedDep.getRefName();
+ logger.trace("Getting service instance by service name {} and ref name {}", serviceName, refName);
+ ServiceInstance byRefName = serviceTracker.getByServiceAndRefName(serviceName, refName);
ObjectName on = ObjectNameUtil.createReadOnlyModuleON(byRefName.getModuleName(), byRefName.getInstanceName());
logger.debug("Attribute {} : {} parsed to type {}", attrName, value, getOpenType());
return Optional.of(on);
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.management.ObjectName;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
public class Config {
+ private final Logger logger = LoggerFactory.getLogger(Config.class);
- private final Map<String, Map<String, ModuleConfig>> moduleConfigs;
+ private final Map<String/* Namespace from yang file */,
+ Map<String /* Name of module entry from yang file */, ModuleConfig>> moduleConfigs;
+ private final Map<String, ModuleConfig> moduleNamesToConfigs;
public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs) {
this.moduleConfigs = moduleConfigs;
+ Map<String, ModuleConfig> moduleNamesToConfigs = new HashMap<>();
+ for (Entry<String, Map<String, ModuleConfig>> entry : moduleConfigs.entrySet()) {
+ moduleNamesToConfigs.putAll(entry.getValue());
+ }
+ this.moduleNamesToConfigs = Collections.unmodifiableMap(moduleNamesToConfigs);
}
private Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
// All found instances add to service tracker in advance
// This way all instances will be serialized as all available
// services when get-config is triggered
- // (even if they are not used as services by other onstances)
+ // (even if they are not used as services by other instances)
// = more user friendly
addServices(serviceTracker, instances, mbeEntry.getValue().getProvidedServices());
// TODO refactor, replace string representing namespace with namespace class
// TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
// class
- public Map<String, Multimap<String, ModuleElementResolved>> fromXml(XmlElement xml) {
+ public Map<String, Multimap<String, ModuleElementResolved>> fromXml(XmlElement xml, Set<ObjectName> instancesForFillingServiceRefMapping) {
Map<String, Multimap<String, ModuleElementResolved>> retVal = Maps.newHashMap();
List<XmlElement> recognisedChildren = Lists.newArrayList();
- Services serviceTracker = fromXmlServices(xml, recognisedChildren);
+ Services serviceTracker = fromXmlServices(xml, recognisedChildren, instancesForFillingServiceRefMapping);
List<XmlElement> moduleElements = fromXmlModules(xml, recognisedChildren);
xml.checkUnrecognisedElements(recognisedChildren);
innerMap.put(factoryName, moduleElementResolved);
}
- private Services fromXmlServices(XmlElement xml, List<XmlElement> recognisedChildren) {
+ private Services fromXmlServices(XmlElement xml, List<XmlElement> recognisedChildren, Set<ObjectName> instancesForFillingServiceRefMapping) {
Optional<XmlElement> servicesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY,
XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
} else {
mappedServices = new HashMap<>();
}
+ Services services = Services.resolveServices(mappedServices);
+ // merge with what candidate db contains by default - ref_
+
+ for(ObjectName existingON: instancesForFillingServiceRefMapping) {
+ logger.trace("Filling services from {}", existingON);
+ // get all its services
+ String factoryName = ObjectNameUtil.getFactoryName(existingON);
+ ModuleConfig moduleConfig = moduleNamesToConfigs.get(factoryName);
+
+ checkState(moduleConfig != null, "Cannot find ModuleConfig with name " + factoryName + " in " + moduleNamesToConfigs);
+ // Set<String> services = ;
+ for (String serviceName : moduleConfig.getProvidedServices()) {
+ services.addServiceEntry(serviceName, existingON);
+ }
+ }
- return Services.resolveServices(mappedServices);
+ return services;
}
private String getFactoryName(String factoryNameWithPrefix, String prefixOrEmptyString) {
depTracker).prepareResolving(yangToAttrConfig);
for (Entry<String, AttributeConfigElement> configDefEntry : mappedConfig.getConfiguration().entrySet()) {
+ AttributeConfigElement value = configDefEntry.getValue();
+ String attributeName = configDefEntry.getKey();
try {
-
AttributeResolvingStrategy<?, ? extends OpenType<?>> attributeResolvingStrategy = resolvingStrategies
- .get(configDefEntry.getKey());
+ .get(attributeName);
+ logger.trace("Trying to set value {} of attribute {} with {}", value, attributeName, attributeResolvingStrategy);
- configDefEntry.getValue().resolveValue(attributeResolvingStrategy, configDefEntry.getKey());
- configDefEntry.getValue().setJmxName(
- yangToAttrConfig.get(configDefEntry.getKey()).getUpperCaseCammelCase());
+ value.resolveValue(attributeResolvingStrategy, attributeName);
+ value.setJmxName(
+ yangToAttrConfig.get(attributeName).getUpperCaseCammelCase());
} catch (Exception e) {
- throw new IllegalStateException("Unable to resolve value " + configDefEntry.getValue()
- + " to attribute " + configDefEntry.getKey(), e);
+ throw new IllegalStateException("Unable to resolve value " + value
+ + " to attribute " + attributeName, e);
}
}
}
import javax.management.ObjectName;
import java.util.Collection;
-import java.util.Collections;
public class ModuleConfig {
this.providedServices = providedServices;
}
- public ModuleConfig(String key, InstanceConfig instanceConfig) {
- this(key, instanceConfig, Collections.<String> emptyList());
- }
-
public InstanceConfig getMbeanMapping() {
return instanceConfig;
}
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.util.regex.Pattern;
public final class Services {
+ private static final Logger logger = LoggerFactory.getLogger(Services.class);
private static final String PROVIDER_KEY = "provider";
private static final String NAME_KEY = "name";
String moduleName = on.getKeyProperty("moduleFactoryName");
String instanceName = on.getKeyProperty("instanceName");
- return addServiceEntry(serviceName, moduleName, instanceName);
+ String refName = addServiceEntry(serviceName, moduleName, instanceName);
+ logger.trace("Added service entry to tracker. Service name {}, ref name {}, module name {}, instance name {}",
+ serviceName, refName, moduleName, instanceName);
+ return refName;
}
+ @VisibleForTesting
public String addServiceEntry(String serviceName, String moduleName, String instanceName) {
ServiceInstance serviceInstance = new ServiceInstance(moduleName, instanceName);
serviceInstance.setServiceName(serviceName);
}
public static Config getConfigMapping(ConfigRegistryClient configRegistryClient,
- Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+ Map<String/* Namespace from yang file */,
+ Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> mBeanEntries) {
Map<String, Map<String, ModuleConfig>> factories = transform(configRegistryClient, mBeanEntries);
return new Config(factories);
}
// TODO refactor
- private static Map<String, Map<String, ModuleConfig>> transform(final ConfigRegistryClient configRegistryClient,
- Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
+ private static Map<String/* Namespace from yang file */,
+ Map<String /* Name of module entry from yang file */, ModuleConfig>> transform
+ (final ConfigRegistryClient configRegistryClient, Map<String/* Namespace from yang file */,
+ Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> mBeanEntries) {
return Maps.transformEntries(mBeanEntries,
new Maps.EntryTransformer<String, Map<String, ModuleMXBeanEntry>, Map<String, ModuleConfig>>() {
new Maps.EntryTransformer<String, ModuleMXBeanEntry, ModuleConfig>() {
@Override
- public ModuleConfig transformEntry(String key, ModuleMXBeanEntry value) {
- return new ModuleConfig(key, new InstanceConfig(configRegistryClient, value
- .getAttributes()));
+ public ModuleConfig transformEntry(String key, ModuleMXBeanEntry moduleMXBeanEntry) {
+ return new ModuleConfig(key, new InstanceConfig(configRegistryClient, moduleMXBeanEntry
+ .getAttributes()), moduleMXBeanEntry.getProvidedServices().values());
}
});
}
EditConfigXmlParser.EditConfigExecution editConfigExecution;
Config cfg = getConfigMapping(configRegistryClient, yangStoreSnapshot.getModuleMXBeanEntryMap());
try {
- editConfigExecution = editConfigXmlParser.fromXml(xml, cfg);
+ editConfigExecution = editConfigXmlParser.fromXml(xml, cfg, transactionProvider, configRegistryClient);
} catch (IllegalStateException e) {
logger.warn("Error parsing xml", e);
final Map<String, String> errorInfo = new HashMap<>();
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Multimap;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementResolved;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore;
+import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.management.ObjectName;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Map;
+import java.util.Set;
public class EditConfigXmlParser {
public EditConfigXmlParser() {
}
- EditConfigXmlParser.EditConfigExecution fromXml(final XmlElement xml, final Config cfgMapping)
+ EditConfigXmlParser.EditConfigExecution fromXml(final XmlElement xml, final Config cfgMapping,
+ TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient)
throws NetconfDocumentedException {
EditStrategyType.resetDefaultStrategy();
// Default op
Optional<XmlElement> defaultContent = xml
.getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.DEFAULT_OPERATION_KEY);
- if (defaultContent.isPresent())
- EditStrategyType.setDefaultStrategy(EditStrategyType.valueOf(defaultContent.get().getTextContent()));
+ if (defaultContent.isPresent()) {
+ String mergeStrategyString = defaultContent.get().getTextContent();
+ logger.trace("Setting merge strategy to {}", mergeStrategyString);
+ EditStrategyType editStrategyType = EditStrategyType.valueOf(mergeStrategyString);
+ // FIXME: thread safety, remove global state
+ EditStrategyType.setDefaultStrategy(editStrategyType);
+ }
+ // FIXME: thread safety, remove global state
+ Set<ObjectName> instancesForFillingServiceRefMapping = Collections.emptySet();
+ if (EditStrategyType.defaultStrategy() == EditStrategyType.merge) {
+ instancesForFillingServiceRefMapping = Datastore.getInstanceQueryStrategy(targetDatastore, transactionProvider)
+ .queryInstances(configRegistryClient);
+ logger.trace("Pre-filling services from following instances: {}", instancesForFillingServiceRefMapping);
+ }
XmlElement configElement = xml.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.CONFIG_KEY);
- return new EditConfigXmlParser.EditConfigExecution(xml, cfgMapping, configElement, testOption);
+ return new EditConfigXmlParser.EditConfigExecution(xml, cfgMapping, configElement, testOption, instancesForFillingServiceRefMapping);
}
private void removeMountpointsFromConfig(XmlElement configElement, XmlElement mountpointsElement) {
Map<String, Multimap<String, ModuleElementResolved>> resolvedXmlElements;
TestOption testOption;
- EditConfigExecution(XmlElement xml, Config configResolver, XmlElement configElement, TestOption testOption) {
+ EditConfigExecution(XmlElement xml, Config configResolver, XmlElement configElement, TestOption testOption, Set<ObjectName> instancesForFillingServiceRefMapping) {
this.editConfigXml = xml;
- this.resolvedXmlElements = configResolver.fromXml(configElement);
+ this.resolvedXmlElements = configResolver.fromXml(configElement, instancesForFillingServiceRefMapping);
this.testOption = testOption;
}
import com.google.common.base.Preconditions;
+//FIXME: make thread safe
public enum EditStrategyType {
// can be default
merge, replace, none,
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import javax.management.ObjectName;
+import java.util.Collections;
import java.util.Map;
+import java.util.Set;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMap;
Config cfg = mock(Config.class);
XmlElement xmlElement = mock(XmlElement.class);
- doReturn(resolvedXmlElements).when(cfg).fromXml(xmlElement);
+ Set<ObjectName> instancesForFillingServiceRefMapping = Collections.emptySet();
+ doReturn(resolvedXmlElements).when(cfg).fromXml(xmlElement, instancesForFillingServiceRefMapping);
EditConfigExecution editConfigExecution = new EditConfigExecution(null, cfg, xmlElement,
- EditConfigXmlParser.TestOption.testThenSet);
+ EditConfigXmlParser.TestOption.testThenSet, instancesForFillingServiceRefMapping);
edit.getResponseInternal(XmlUtil.newDocument(), editConfigExecution);