Thomas Vachuska

Implementing net config subsystem and revising its interfaces.

Added a few basic configs for device, host and links.

Added initial REST API.

Added CLI.

Tests remain to be added.

Change-Id: Ic7bba4b5ad7d553c51d69f6459b3bff146970323
Showing 28 changed files with 2243 additions and 64 deletions
/*
* Copyright 2015 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.cli.cfg;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.config.Config;
import org.onosproject.incubator.net.config.NetworkConfigService;
import org.onosproject.incubator.net.config.SubjectFactory;
import static com.google.common.base.Strings.isNullOrEmpty;
/**
* Manages network configuration.
*/
@Command(scope = "onos", name = "netcfg",
description = "Manages network configuration")
public class NetworkConfigCommand extends AbstractShellCommand {
@Argument(index = 0, name = "subjectKey", description = "Subject key",
required = false, multiValued = false)
String subjectKey = null;
@Argument(index = 1, name = "subject", description = "Subject",
required = false, multiValued = false)
String subject = null;
@Argument(index = 2, name = "configKey", description = "Config key",
required = false, multiValued = false)
String configKey = null;
private final ObjectMapper mapper = new ObjectMapper();
private NetworkConfigService service;
@Override
protected void execute() {
service = get(NetworkConfigService.class);
ObjectNode root = new ObjectMapper().createObjectNode();
if (isNullOrEmpty(subjectKey)) {
addAll(root);
} else {
SubjectFactory subjectFactory = service.getSubjectFactory(subjectKey);
if (isNullOrEmpty(subject)) {
addSubjectClass(root, subjectFactory);
} else {
Object s = subjectFactory.createSubject(subject);
if (isNullOrEmpty(configKey)) {
addSubject(root, s);
} else {
addSubjectConfig(root, getConfig(s, configKey));
}
}
}
print("%s", root.toString());
}
@SuppressWarnings("unchecked")
private void addAll(ObjectNode root) {
service.getSubjectClasses()
.forEach(sc -> {
SubjectFactory sf = service.getSubjectFactory((Class) sc);
addSubjectClass(newObject(root, sf.subjectKey()), sf);
});
}
@SuppressWarnings("unchecked")
private void addSubjectClass(ObjectNode root, SubjectFactory sf) {
service.getSubjects(sf.subjectClass())
.forEach(s -> addSubject(newObject(root, s.toString()), s));
}
private void addSubject(ObjectNode root, Object s) {
service.getConfigs(s)
.forEach(c -> addSubjectConfig(newObject(root, c.key()), getConfig(s, c.key())));
}
private void addSubjectConfig(ObjectNode root, Config config) {
if (config != null) {
root.set(config.key(), config.node());
}
}
private Config getConfig(Object s, String ck) {
Class<? extends Config> configClass = service.getConfigClass(ck);
return configClass != null ? service.getConfig(s, configClass) : null;
}
private ObjectNode newObject(ObjectNode parent, String key) {
ObjectNode node = mapper.createObjectNode();
parent.set(key, node);
return node;
}
}
/*
* Copyright 2015 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.cli.cfg;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.config.ConfigFactory;
import org.onosproject.incubator.net.config.NetworkConfigRegistry;
/**
* Displays network configuration registry contents.
*/
@Command(scope = "onos", name = "netcfg-registry",
description = "Displays network configuration registry contents")
public class NetworkConfigRegistryCommand extends AbstractShellCommand {
private static final String FMT = "subjectKey=%s, configKey=%s, subjectClass=%s, configClass=%s";
private static final String SHORT_FMT = "%-12s %-12s %-40s %s";
@Option(name = "-s", aliases = "--short", description = "Show short output only",
required = false, multiValued = false)
private boolean shortOnly = false;
@Override
protected void execute() {
get(NetworkConfigRegistry.class).getConfigFactories().forEach(this::print);
}
private void print(ConfigFactory configFactory) {
print(shortOnly ? SHORT_FMT : FMT,
configFactory.subjectFactory().subjectKey(),
configFactory.configKey(),
configFactory.subjectFactory().subjectClass().getName(),
configFactory.configClass().getName());
}
}
......@@ -15,6 +15,6 @@
*/
/**
* CLI commands for managing centralized component configuration.
* CLI commands for managing centralized component and network configurations.
*/
package org.onosproject.cli.cfg;
\ No newline at end of file
......
......@@ -51,6 +51,14 @@
</command>
<command>
<action class="org.onosproject.cli.cfg.NetworkConfigRegistryCommand"/>
</command>
<command>
<action class="org.onosproject.cli.cfg.NetworkConfigCommand"/>
</command>
<command>
<action class="org.onosproject.cli.MetricsListCommand"/>
</command>
......
......@@ -17,7 +17,11 @@ package org.onosproject.net;
/**
* Collection of keys for annotation.
* Definitions of annotation keys needs to be here to avoid scattering.
* <p>
* Number of the annotation keys have been deprecated as the use of annotations
* is being phased out and instead network configuration subsystem is being
* phased-in for majority of model meta-data.
* </p>
*/
public final class AnnotationKeys {
......@@ -26,22 +30,34 @@ public final class AnnotationKeys {
/**
* Annotation key for instance name.
*
* @deprecated since Cardinal
*/
@Deprecated
public static final String NAME = "name";
/**
* Annotation key for instance type (e.g. host type).
*
* @deprecated since Cardinal
*/
@Deprecated
public static final String TYPE = "type";
/**
* Annotation key for latitude (e.g. latitude of device).
*
* @deprecated since Cardinal
*/
@Deprecated
public static final String LATITUDE = "latitude";
/**
* Annotation key for longitute (e.g. longitude of device).
*
* @deprecated since Cardinal
*/
@Deprecated
public static final String LONGITUDE = "longitude";
/**
......@@ -51,7 +67,10 @@ public final class AnnotationKeys {
/**
* Annotation key for the device driver name.
*
* @deprecated since Cardinal
*/
@Deprecated
public static final String DRIVER = "driver";
/**
......@@ -61,13 +80,19 @@ public final class AnnotationKeys {
/**
* Annotation key for latency.
*
* @deprecated since Cardinal
*/
@Deprecated
public static final String LATENCY = "latency";
/**
* Annotation key for bandwidth.
* The value for this key is interpreted as Mbps.
*
* @deprecated since Cardinal
*/
@Deprecated
public static final String BANDWIDTH = "bandwidth";
/**
......
......@@ -16,6 +16,7 @@
package org.onosproject.rest;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onlab.rest.BaseResource;
import org.onosproject.codec.CodecContext;
......@@ -27,9 +28,11 @@ import org.onosproject.codec.JsonCodec;
*/
public class AbstractWebResource extends BaseResource implements CodecContext {
private final ObjectMapper mapper = new ObjectMapper();
@Override
public ObjectMapper mapper() {
return new ObjectMapper();
return mapper;
}
/**
......@@ -65,4 +68,31 @@ public class AbstractWebResource extends BaseResource implements CodecContext {
return get(serviceClass);
}
/**
* Creates and returns a new child object within the specified parent and
* bound to the given key.
*
* @param parent parent object
* @param key key for the new child object
* @return child object
*/
public ObjectNode newObject(ObjectNode parent, String key) {
ObjectNode node = mapper.createObjectNode();
parent.set(key, node);
return node;
}
/**
* Creates and returns a new child array within the specified parent and
* bound to the given key.
*
* @param parent parent object
* @param key key for the new child array
* @return child array
*/
public ArrayNode newArray(ObjectNode parent, String key) {
ArrayNode node = mapper.createArrayNode();
parent.set(key, node);
return node;
}
}
......
......@@ -23,48 +23,212 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* Base abstraction of a configuration facade for a specific subject. Derived
* classes should keep all state in the specified JSON tree.
* classes should keep all state in the specified JSON tree as that is the
* only state that will be distributed or persisted; this class is merely
* a facade for interacting with a particular facet of configuration on a
* given subject.
*
* @param <S> type of subject
*/
@Beta
public abstract class Config<S> {
protected ObjectMapper mapper;
protected S subject;
protected String key;
protected ObjectNode node;
private S subject;
private ConfigApplyDelegate<S> delegate;
/**
* Returns the specific subject to which this configuration pertains.
*
* @return configuration subject
*/
S subject() {
return subject;
}
protected ObjectMapper mapper;
protected ConfigApplyDelegate delegate;
/**
* Initializes the configuration behaviour with necessary context.
*
* @param subject configuration subject
* @param key configuration key
* @param node JSON object node where configuration data is stored
* @param mapper JSON object mapper
* @param delegate delegate context
*/
public void init(S subject, ObjectNode node, ObjectMapper mapper,
ConfigApplyDelegate<S> delegate) {
public void init(S subject, String key, ObjectNode node, ObjectMapper mapper,
ConfigApplyDelegate delegate) {
this.subject = checkNotNull(subject);
this.key = key;
this.node = checkNotNull(node);
this.mapper = checkNotNull(mapper);
this.delegate = checkNotNull(delegate);
}
/**
* Returns the specific subject to which this configuration pertains.
*
* @return configuration subject
*/
public S subject() {
return subject;
}
/**
* Returns the configuration key. This is primarily aimed for use in
* composite JSON trees in external representations and has no bearing on
* the internal behaviours.
*
* @return configuration key
*/
public String key() {
return key;
}
/**
* Returns the JSON node that contains the configuration data.
*
* @return JSON node backing the configuration
*/
public ObjectNode node() {
return node;
}
/**
* Applies any configuration changes made via this configuration.
*/
public void apply() {
delegate.onApply(this);
}
// Miscellaneous helpers for interacting with JSON
/**
* Gets the specified property as a string.
*
* @param name property name
* @param defaultValue default value if property not set
* @return property value or default value
*/
protected String get(String name, String defaultValue) {
return node.path(name).asText(defaultValue);
}
/**
* Sets the specified property as a string or clears it if null value given.
*
* @param name property name
* @param value new value or null to clear the property
* @return self
*/
protected Config<S> setOrClear(String name, String value) {
if (value != null) {
node.put(name, value);
} else {
node.remove(name);
}
return this;
}
/**
* Gets the specified property as a boolean.
*
* @param name property name
* @param defaultValue default value if property not set
* @return property value or default value
*/
protected boolean get(String name, boolean defaultValue) {
return node.path(name).asBoolean(defaultValue);
}
/**
* Sets the specified property as a boolean or clears it if null value given.
*
* @param name property name
* @param value new value or null to clear the property
* @return self
*/
protected Config<S> setOrClear(String name, Boolean value) {
if (value != null) {
node.put(name, value.booleanValue());
} else {
node.remove(name);
}
return this;
}
/**
* Gets the specified property as a long.
*
* @param name property name
* @param defaultValue default value if property not set
* @return property value or default value
*/
protected long get(String name, long defaultValue) {
return node.path(name).asLong(defaultValue);
}
/**
* Sets the specified property as a long or clears it if null value given.
*
* @param name property name
* @param value new value or null to clear the property
* @return self
*/
protected Config<S> setOrClear(String name, Long value) {
if (value != null) {
node.put(name, value.longValue());
} else {
node.remove(name);
}
return this;
}
/**
* Gets the specified property as a double.
*
* @param name property name
* @param defaultValue default value if property not set
* @return property value or default value
*/
protected double get(String name, double defaultValue) {
return node.path(name).asDouble(defaultValue);
}
/**
* Sets the specified property as a double or clears it if null value given.
*
* @param name property name
* @param value new value or null to clear the property
* @return self
*/
protected Config<S> setOrClear(String name, Double value) {
if (value != null) {
node.put(name, value.doubleValue());
} else {
node.remove(name);
}
return this;
}
/**
* Gets the specified property as an enum.
*
* @param name property name
* @param defaultValue default value if property not set
* @param enumClass the enum class
* @return property value or default value
*/
protected <E extends Enum<E>> E get(String name, E defaultValue, Class<E> enumClass) {
return Enum.valueOf(enumClass, node.path(name).asText(defaultValue.toString()));
}
/**
* Sets the specified property as a double or clears it if null value given.
*
* @param name property name
* @param value new value or null to clear the property
* @return self
*/
protected <E extends Enum> Config<S> setOrClear(String name, E value) {
if (value != null) {
node.put(name, value.toString());
} else {
node.remove(name);
}
return this;
}
}
......
......@@ -21,13 +21,13 @@ import com.google.common.annotations.Beta;
* Delegate for notification when configuration changes have been applied.
*/
@Beta
public interface ConfigApplyDelegate<S> {
public interface ConfigApplyDelegate {
/**
* Processes changes applied to the specified configuration.
*
* @param config changed configuration
*/
void onApply(Config<S> config);
void onApply(Config config);
}
......
......@@ -21,24 +21,31 @@ import com.google.common.annotations.Beta;
/**
* Base abstract factory for creating configurations for the specified subject type.
*
* @param <S> subject class
* @param <S> type of subject
* @param <C> type of configuration
*/
@Beta
public abstract class ConfigFactory<S> {
public abstract class ConfigFactory<S, C extends Config<S>> {
private final Class<S> subjectClass;
private final String key;
private final SubjectFactory<S> subjectFactory;
private final Class<C> configClass;
private final String configKey;
/**
* Creates a new configuration factory for the specified class of subjects
* and bound to the given subject configuration key.
* capable of generating the configurations of the specified class. The
* subject and configuration class keys are used merely as keys for use in
* composite JSON trees.
*
* @param subjectClass subject class
* @param key subject configuration key
* @param subjectFactory subject factory
* @param configClass configuration class
* @param configKey configuration class key
*/
protected ConfigFactory(Class<S> subjectClass, String key) {
this.subjectClass = subjectClass;
this.key = key;
protected ConfigFactory(SubjectFactory<S> subjectFactory,
Class<C> configClass, String configKey) {
this.subjectFactory = subjectFactory;
this.configClass = configClass;
this.configKey = configKey;
}
/**
......@@ -46,17 +53,28 @@ public abstract class ConfigFactory<S> {
*
* @return subject type
*/
public Class<S> subjectClass() {
return subjectClass;
public SubjectFactory<S> subjectFactory() {
return subjectFactory;
}
/**
* Returns the key to which produced configurations should be bound.
* Returns the class of the configuration which this factory generates.
*
* @return subject configuration key
* @return configuration type
*/
public String key() {
return key;
public Class<C> configClass() {
return configClass;
}
/**
* Returns the unique key (within subject class) of this configuration.
* This is primarily aimed for use in composite JSON trees in external
* representations and has no bearing on the internal behaviours.
*
* @return configuration key
*/
public String configKey() {
return configKey;
}
/**
......@@ -65,6 +83,6 @@ public abstract class ConfigFactory<S> {
*
* @return new uninitialized configuration
*/
public abstract Config<S> createConfig();
public abstract C createConfig();
}
......
/*
* Copyright 2015 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.incubator.net.config;
import org.onosproject.event.AbstractEvent;
/**
* Describes network configuration event.
*/
public class NetworkConfigEvent extends AbstractEvent<NetworkConfigEvent.Type, Object> {
private final Class configClass;
/**
* Type of network configuration events.
*/
public enum Type {
/**
* Signifies that network configuration was added.
*/
CONFIG_ADDED,
/**
* Signifies that network configuration was updated.
*/
CONFIG_UPDATED,
/**
* Signifies that network configuration was removed.
*/
CONFIG_REMOVED
}
/**
* Creates an event of a given type and for the specified subject and the
* current time.
*
* @param type event type
* @param subject event subject
* @param configClass configuration class
*/
public NetworkConfigEvent(Type type, Object subject, Class configClass) {
super(type, subject);
this.configClass = configClass;
}
/**
* Creates an event of a given type and for the specified subject and time.
*
* @param type device event type
* @param subject event subject
* @param configClass configuration class
* @param time occurrence time
*/
public NetworkConfigEvent(Type type, Object subject, Class configClass, long time) {
super(type, subject, time);
this.configClass = configClass;
}
/**
* Returns the class of configuration that has been changed.
*
* @return configuration class
*/
public Class configClass() {
return configClass;
}
}
/*
* Copyright 2015 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.incubator.net.config;
import org.onosproject.event.EventListener;
/**
* Entity capable of receiving network configuration related events.
*/
public interface NetworkConfigListener extends EventListener<NetworkConfigEvent> {
}
......@@ -20,7 +20,11 @@ import com.google.common.annotations.Beta;
import java.util.Set;
/**
* Service for tracking network configuration factories.
* Service for tracking network configuration factories. It is the basis for
* extensibility to allow various core subsystems or apps to register their
* own configuration factories that permit use to inject additional meta
* information about how various parts of the network should be viewed and
* treated.
*/
@Beta
public interface NetworkConfigRegistry {
......@@ -40,34 +44,32 @@ public interface NetworkConfigRegistry {
void unregisterConfigFactory(ConfigFactory configFactory);
/**
* Returns set of configuration factories available for the specified
* class of subject.
* Returns set of all registered configuration factories.
*
* @param subjectClass subject class
* @param <T> type of subject
* @return set of config factories
*/
<T> Set<ConfigFactory<T>> getConfigFactories(Class<T> subjectClass);
Set<ConfigFactory> getConfigFactories();
/**
* Returns the configuration type registered for the specified
* subject type and key.
* Returns set of all configuration factories registered for the specified
* class of subject.
*
* @param subjectClass subject class
* @param configKey configuration key
* @param <T> type of subject
* @return config factory
* @param <S> type of subject
* @param <C> type of configuration
* @return set of config factories
*/
<T> ConfigFactory<T> getConfigFactory(Class<T> subjectClass, String configKey);
<S, C extends Config<S>> Set<ConfigFactory<S, C>> getConfigFactories(Class<S> subjectClass);
/**
* Returns the configuration type registered for the specified
* configuration class.
* Returns the configuration factory that produces the specified class of
* configurations.
*
* @param configClass configuration class
* @param <T> type of subject
* @param <S> type of subject
* @param <C> type of configuration
* @return config factory
*/
<T> ConfigFactory<T> getConfigFactory(Class<Config<T>> configClass);
<S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass);
}
......
......@@ -15,26 +15,59 @@
*/
package org.onosproject.incubator.net.config;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.annotations.Beta;
import java.util.Set;
/**
* Service for tracking network configurations which specify how the discovered
* network information should be interpreted and how the network should be
* configured.
* network information should be interpreted and how the core or applications
* should act on or configure the network.
*/
@Beta
public interface NetworkConfigService {
/**
* Returns the set of subject classes for which configuration may be
* available.
*
* @return set of subject classes
*/
Set<Class> getSubjectClasses();
/**
* Returns the subject factory with the specified key.
*
* @param subjectKey subject class key
* @return subject class
*/
SubjectFactory getSubjectFactory(String subjectKey);
/**
* Returns the subject factory for the specified class.
*
* @param subjectClass subject class
* @return subject class key
*/
SubjectFactory getSubjectFactory(Class subjectClass);
/**
* Returns the configuration class with the specified key.
*
* @param configKey subject class name
* @return subject class
*/
Class<? extends Config> getConfigClass(String configKey);
/**
* Returns the set of subjects for which some configuration is available.
*
* @param subjectClass subject class
* @param <T> type of subject
* @param <S> type of subject
* @return set of configured subjects
*/
<T> Set<T> getSubjects(Class<T> subjectClass);
<S> Set<S> getSubjects(Class<S> subjectClass);
/**
* Returns the set of subjects for which the specified configuration is
......@@ -42,20 +75,20 @@ public interface NetworkConfigService {
*
* @param subjectClass subject class
* @param configClass configuration class
* @param <T> type of subject
* @param <S> type of subject
* @param <C> type of configuration
* @return set of configured subjects
*/
<T> Set<T> getSubjects(Class<T> subjectClass, Class<Config<T>> configClass);
<S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass);
/**
* Returns all configurations for the specified subject.
*
* @param subject configuration subject
* @param <T> type of subject
* @param <S> type of subject
* @return set of configurations
*/
<T> Set<Config<T>> getConfigs(T subject);
<S> Set<? extends Config<S>> getConfigs(S subject);
/**
* Returns the configuration for the specified subject and configuration
......@@ -63,9 +96,61 @@ public interface NetworkConfigService {
*
* @param subject configuration subject
* @param configClass configuration class
* @param <T> type of subject
* @param <S> type of subject
* @param <C> type of configuration
* @return configuration or null if one is not available
*/
<S, C extends Config<S>> C getConfig(S subject, Class<C> configClass);
/**
* Creates a new configuration for the specified subject and configuration
* class. If one already exists, it is simply returned.
*
* @param subject configuration subject
* @param configClass configuration class
* @param <S> type of subject
* @param <C> type of configuration
* @return configuration or null if one is not available
*/
<S, C extends Config<S>> C addConfig(S subject, Class<C> configClass);
/**
* Applies configuration for the specified subject and configuration
* class using the raw JSON object. If configuration already exists, it
* will be updated.
*
* @param subject configuration subject
* @param configClass configuration class
* @param json raw JSON node containing the configuration data
* @param <S> type of subject
* @param <C> type of configuration
* @return configuration or null if one is not available
*/
<T> Config<T> getConfig(T subject, Class<Config<T>> configClass);
<S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass,
ObjectNode json);
/**
* Clears any configuration for the specified subject and configuration
* class. If one does not exist, this call has no effect.
*
* @param subject configuration subject
* @param configClass configuration class
* @param <S> type of subject
* @param <C> type of configuration
*/
<S, C extends Config<S>> void removeConfig(S subject, Class<C> configClass);
/**
* Adds the specified network config listener.
*
* @param listener network config listener
*/
void addListener(NetworkConfigListener listener);
/**
* Removes the specified network config listener.
*
* @param listener network config listener
*/
void removeListener(NetworkConfigListener listener);
}
......
/*
* Copyright 2015 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.incubator.net.config;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.store.Store;
import java.util.Set;
/**
* Mechanism for distributing and storing network configuration information.
*/
public interface NetworkConfigStore extends Store<NetworkConfigEvent, NetworkConfigStoreDelegate> {
/**
* Adds a new configuration factory.
*
* @param configFactory configuration factory to add
*/
void addConfigFactory(ConfigFactory configFactory);
/**
* Removes a configuration factory.
*
* @param configFactory configuration factory to remove
*/
void removeConfigFactory(ConfigFactory configFactory);
/**
* Returns the configuration factory for the specified configuration class.
*
* @param configClass configuration class
* @param <S> type of subject
* @param <C> type of configuration
* @return configuration factory or null
*/
<S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass);
/**
* Returns set of subjects of the specified class, which have some
* network configuration associated with them.
*
* @param subjectClass subject class
* @param <S> type of subject
* @return set of subject
*/
<S> Set<S> getSubjects(Class<S> subjectClass);
/**
* Returns set of subjects of the specified class, which have the
* specified class of network configuration associated with them.
*
* @param subjectClass subject class
* @param configClass configuration class
* @param <S> type of subject
* @param <C> type of configuration
* @return set of subject
*/
<S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass);
/**
* Returns set of configuration classes available for the specified subject.
*
* @param subject configuration subject
* @param <S> type of subject
* @return set of configuration classes
*/
<S> Set<Class<? extends Config<S>>> getConfigClasses(S subject);
/**
* Get the configuration of the given class and for the specified subject.
*
* @param subject configuration subject
* @param configClass configuration class
* @param <S> type of subject
* @param <C> type of configuration
* @return configuration object
*/
<S, C extends Config<S>> C getConfig(S subject, Class<C> configClass);
/**
* Creates a new configuration of the given class for the specified subject.
*
* @param subject configuration subject
* @param configClass configuration class
* @param <S> type of subject
* @param <C> type of configuration
* @return configuration object
*/
<S, C extends Config<S>> C createConfig(S subject, Class<C> configClass);
/**
* Applies configuration for the specified subject and configuration
* class using the raw JSON object. If configuration already exists, it
* will be updated.
*
* @param subject configuration subject
* @param configClass configuration class
* @param json raw JSON node containing the configuration data
* @param <S> type of subject
* @param <C> type of configuration
*/
<S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass,
ObjectNode json);
/**
* Clears the configuration of the given class for the specified subject.
*
* @param subject configuration subject
* @param configClass configuration class
* @param <S> type of subject
* @param <C> type of configuration
*/
<S, C extends Config<S>> void clearConfig(S subject, Class<C> configClass);
}
/*
* Copyright 2015 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.incubator.net.config;
import org.onosproject.store.StoreDelegate;
/**
* Network configuration store delegate abstraction.
*/
public interface NetworkConfigStoreDelegate extends StoreDelegate<NetworkConfigEvent> {
}
/*
* Copyright 2015 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.incubator.net.config;
import com.google.common.annotations.Beta;
/**
* Base abstract factory for creating configuration subjects from their
* string key image.
*
* @param <S> subject class
*/
@Beta
public abstract class SubjectFactory<S> {
private final Class<S> subjectClass;
private final String subjectKey;
/**
* Creates a new configuration factory for the specified class of subjects
* capable of generating the configurations of the specified class. The
* subject and configuration class keys are used merely as keys for use in
* composite JSON trees.
*
* @param subjectClass subject class
* @param subjectKey subject class key
*/
protected SubjectFactory(Class<S> subjectClass, String subjectKey) {
this.subjectClass = subjectClass;
this.subjectKey = subjectKey;
}
/**
* Returns the class of the subject to which this factory applies.
*
* @return subject type
*/
public Class<S> subjectClass() {
return subjectClass;
}
/**
* Returns the unique key of this configuration subject class.
* This is primarily aimed for use in composite JSON trees in external
* representations and has no bearing on the internal behaviours.
*
* @return configuration key
*/
public String subjectKey() {
return subjectKey;
}
/**
* Creates a configuration subject from its key image.
*
* @return configuration subject
*/
public abstract S createSubject(String key);
}
/*
* Copyright 2015 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.incubator.net.config.basics;
import org.onosproject.incubator.net.config.Config;
/**
* Base abstraction for network entities for which admission into control
* domain can be selectively configured, e.g. devices, end-stations, links
*/
public abstract class AllowedEntityConfig<S> extends Config<S> {
private static final String ALLOWED = "allowed";
/**
* Indicates whether the element is allowed for admission into the control
* domain.
*
* @return true if element is allowed
*/
public boolean isAllowed() {
return get(ALLOWED, true);
}
/**
* Specifies whether the element is to be allowed for admission into the
* control domain.
*
* @param isAllowed true to allow; false to forbid; null to clear
* @return self
*/
public AllowedEntityConfig isAllowed(Boolean isAllowed) {
return (AllowedEntityConfig) setOrClear(ALLOWED, isAllowed);
}
}
/*
* Copyright 2015 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.incubator.net.config.basics;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
/**
* Basic configuration for network infrastructure devices.
*/
public class BasicDeviceConfig extends BasicElementConfig<DeviceId> {
public static final String TYPE = "type";
public static final String DRIVER = "driver";
/**
* Returns the device type.
*
* @return device type override
*/
public Device.Type type() {
return get(TYPE, Device.Type.SWITCH, Device.Type.class);
}
/**
* Sets the device type.
*
* @param type device type override
* @return self
*/
public BasicDeviceConfig type(Device.Type type) {
return (BasicDeviceConfig) setOrClear(TYPE, type);
}
/**
* Returns the device driver name.
*
* @return driver name of null if not set
*/
public String driver() {
return get(DRIVER, subject.toString());
}
/**
* Sets the driver name.
*
* @param driverName new driver name; null to clear
* @return self
*/
public BasicElementConfig driver(String driverName) {
return (BasicElementConfig) setOrClear(DRIVER, driverName);
}
// TODO: device port meta-data to be configured via BasicPortsConfig
// TODO: device credentials/keys
}
/*
* Copyright 2015 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.incubator.net.config.basics;
/**
* Basic configuration for network elements, e.g. devices, hosts. Such elements
* can have a friendly name, geo-coordinates, logical rack coordinates and
* an owner entity.
*/
public abstract class BasicElementConfig<S> extends AllowedEntityConfig<S> {
public static final String NAME = "name";
public static final String LATITUDE = "latitude";
public static final String LONGITUDE = "longitude";
public static final String RACK_ADDRESS = "rackAddress";
public static final String OWNER = "owner";
/**
* Returns friendly label for the element.
*
* @return friendly label or element id itself if not set
*/
public String name() {
return get(NAME, subject.toString());
}
/**
* Sets friendly label for the element.
*
* @param name new friendly label; null to clear
* @return self
*/
public BasicElementConfig name(String name) {
return (BasicElementConfig) setOrClear(NAME, name);
}
/**
* Returns element latitude.
*
* @return element latitude; -1 if not set
*/
public double latitude() {
return get(LATITUDE, -1.0);
}
/**
* Sets the element latitude.
*
* @param latitude new latitude; null to clear
* @return self
*/
public BasicElementConfig latitude(Double latitude) {
return (BasicElementConfig) setOrClear(LATITUDE, latitude);
}
/**
* Returns element latitude.
*
* @return element latitude; -1 if not set
*/
public double longitude() {
return get(LONGITUDE, -1.0);
}
/**
* Sets the element longitude.
*
* @param longitude new longitude; null to clear
* @return self
*/
public BasicElementConfig longitude(Double longitude) {
return (BasicElementConfig) setOrClear(LONGITUDE, longitude);
}
/**
* Returns the element rack address.
*
* @return rack address; null if not set
*/
public String rackAddress() {
return get(RACK_ADDRESS, null);
}
/**
* Sets element rack address.
*
* @param address new rack address; null to clear
* @return self
*/
public BasicElementConfig rackAddress(String address) {
return (BasicElementConfig) setOrClear(RACK_ADDRESS, address);
}
/**
* Returns owner of the element.
*
* @return owner or null if not set
*/
public String owner() {
return get(OWNER, null);
}
/**
* Sets the owner of the element.
*
* @param owner new owner; null to clear
* @return self
*/
public BasicElementConfig owner(String owner) {
return (BasicElementConfig) setOrClear(OWNER, owner);
}
}
/*
* Copyright 2015 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.incubator.net.config.basics;
import org.onosproject.net.HostId;
/**
* Basic configuration for network end-station hosts.
*/
public class BasicHostConfig extends BasicElementConfig<HostId> {
// TODO: determine what aspects of configuration to add for hosts
}
/*
* Copyright 2015 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.incubator.net.config.basics;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
import java.time.Duration;
/**
* Basic configuration for network infrastructure link.
*/
public class BasicLinkConfig extends AllowedEntityConfig<LinkKey> {
public static final String TYPE = "type";
public static final String LATENCY = "latency";
public static final String BANDWIDTH = "bandwidth";
/**
* Returns the link type.
*
* @return link type override
*/
public Link.Type type() {
return get(TYPE, Link.Type.DIRECT, Link.Type.class);
}
/**
* Sets the link type.
*
* @param type link type override
* @return self
*/
public BasicLinkConfig type(Link.Type type) {
return (BasicLinkConfig) setOrClear(TYPE, type);
}
/**
* Returns link latency in terms of nanos.
*
* @return link latency; -1 if not set
*/
public Duration latency() {
return Duration.ofNanos(get(LATENCY, -1));
}
/**
* Sets the link latency.
*
* @param latency new latency; null to clear
* @return self
*/
public BasicElementConfig latency(Duration latency) {
Long nanos = latency == null ? null : latency.toNanos();
return (BasicElementConfig) setOrClear(LATENCY, nanos);
}
/**
* Returns link bandwidth in terms of Mbps.
*
* @return link bandwidth; -1 if not set
*/
public long bandwidth() {
return get(BANDWIDTH, -1);
}
/**
* Sets the link bandwidth.
*
* @param bandwidth new bandwidth; null to clear
* @return self
*/
public BasicElementConfig bandwidth(Long bandwidth) {
return (BasicElementConfig) setOrClear(BANDWIDTH, bandwidth);
}
}
/*
* Copyright 2015 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.incubator.net.config.basics;
import org.onosproject.core.ApplicationId;
import org.onosproject.incubator.net.config.SubjectFactory;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
import org.onosproject.net.LinkKey;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Set of subject factories for potential configuration subjects.
*/
public final class SubjectFactories {
// Construction forbidden
private SubjectFactories() {
}
public static final SubjectFactory<ApplicationId> APP_SUBJECT_FACTORY =
new SubjectFactory<ApplicationId>(ApplicationId.class, "apps") {
@Override
public ApplicationId createSubject(String key) {
// FIXME: figure out how to safely create sanctioned app ids
return null;
}
};
public static final SubjectFactory<DeviceId> DEVICE_SUBJECT_FACTORY =
new SubjectFactory<DeviceId>(DeviceId.class, "devices") {
@Override
public DeviceId createSubject(String key) {
return DeviceId.deviceId(key);
}
};
public static final SubjectFactory<HostId> HOST_SUBJECT_FACTORY =
new SubjectFactory<HostId>(HostId.class, "hosts") {
@Override
public HostId createSubject(String key) {
return HostId.hostId(key);
}
};
public static final SubjectFactory<LinkKey> LINK_SUBJECT_FACTORY =
new SubjectFactory<LinkKey>(LinkKey.class, "links") {
@Override
public LinkKey createSubject(String key) {
String[] cps = key.split("-");
checkArgument(cps.length == 2, "Incorrect link key format: %s", key);
return LinkKey.linkKey(ConnectPoint.deviceConnectPoint(cps[0]),
ConnectPoint.deviceConnectPoint(cps[1]));
}
};
}
/*
* Copyright 2015 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.incubator.net.config.impl;
import com.google.common.collect.ImmutableSet;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.incubator.net.config.ConfigFactory;
import org.onosproject.incubator.net.config.NetworkConfigRegistry;
import org.onosproject.incubator.net.config.basics.BasicDeviceConfig;
import org.onosproject.incubator.net.config.basics.BasicHostConfig;
import org.onosproject.incubator.net.config.basics.BasicLinkConfig;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
import org.onosproject.net.LinkKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
import static org.onosproject.incubator.net.config.basics.SubjectFactories.*;
/**
* Component for registration of builtin basic network configurations.
*/
@Component(immediate = true)
public class BasicNetworkConfigs {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Set<ConfigFactory> factories = ImmutableSet.of(
new ConfigFactory<DeviceId, BasicDeviceConfig>(DEVICE_SUBJECT_FACTORY,
BasicDeviceConfig.class,
"basic") {
@Override
public BasicDeviceConfig createConfig() {
return new BasicDeviceConfig();
}
},
new ConfigFactory<HostId, BasicHostConfig>(HOST_SUBJECT_FACTORY,
BasicHostConfig.class,
"basic") {
@Override
public BasicHostConfig createConfig() {
return new BasicHostConfig();
}
},
new ConfigFactory<LinkKey, BasicLinkConfig>(LINK_SUBJECT_FACTORY,
BasicLinkConfig.class,
"basic") {
@Override
public BasicLinkConfig createConfig() {
return new BasicLinkConfig();
}
}
);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry registry;
@Activate
public void activate() {
factories.forEach(registry::registerConfigFactory);
log.info("Started");
}
@Deactivate
public void deactivate() {
factories.forEach(registry::unregisterConfigFactory);
log.info("Stopped");
}
}
/*
* Copyright 2015 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.incubator.net.config.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onosproject.event.EventDeliveryService;
import org.onosproject.event.ListenerRegistry;
import org.onosproject.incubator.net.config.Config;
import org.onosproject.incubator.net.config.ConfigFactory;
import org.onosproject.incubator.net.config.NetworkConfigEvent;
import org.onosproject.incubator.net.config.NetworkConfigListener;
import org.onosproject.incubator.net.config.NetworkConfigRegistry;
import org.onosproject.incubator.net.config.NetworkConfigService;
import org.onosproject.incubator.net.config.NetworkConfigStore;
import org.onosproject.incubator.net.config.NetworkConfigStoreDelegate;
import org.onosproject.incubator.net.config.SubjectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Implementation of the network configuration subsystem.
*/
@Component(immediate = true)
@Service
public class NetworkConfigManager implements NetworkConfigRegistry, NetworkConfigService {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String NULL_FACTORY_MSG = "Factory cannot be null";
private static final String NULL_SCLASS_MSG = "Subject class cannot be null";
private static final String NULL_CCLASS_MSG = "Config class cannot be null";
private static final String NULL_SUBJECT_MSG = "Subject cannot be null";
// Inventory of configuration factories
private final Map<ConfigKey, ConfigFactory> factories = Maps.newConcurrentMap();
// Secondary indeces to retrieve subject and config classes by keys
private final Map<String, SubjectFactory> subjectClasses = Maps.newConcurrentMap();
private final Map<Class, SubjectFactory> subjectClassKeys = Maps.newConcurrentMap();
private final Map<String, Class<? extends Config>> configClasses = Maps.newConcurrentMap();
private final ListenerRegistry<NetworkConfigEvent, NetworkConfigListener>
listenerRegistry = new ListenerRegistry<>();
private final NetworkConfigStoreDelegate storeDelegate = new InternalStoreDelegate();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigStore store;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EventDeliveryService eventDispatcher;
@Activate
public void activate() {
eventDispatcher.addSink(NetworkConfigEvent.class, listenerRegistry);
store.setDelegate(storeDelegate);
log.info("Started");
}
@Deactivate
public void deactivate() {
eventDispatcher.removeSink(NetworkConfigEvent.class);
store.unsetDelegate(storeDelegate);
log.info("Stopped");
}
@Override
@SuppressWarnings("unchecked")
public void registerConfigFactory(ConfigFactory configFactory) {
checkNotNull(configFactory, NULL_FACTORY_MSG);
factories.put(key(configFactory), configFactory);
configClasses.put(configFactory.configKey(), configFactory.configClass());
SubjectFactory subjectFactory = configFactory.subjectFactory();
subjectClasses.putIfAbsent(subjectFactory.subjectKey(), subjectFactory);
subjectClassKeys.putIfAbsent(subjectFactory.subjectClass(), subjectFactory);
store.addConfigFactory(configFactory);
}
@Override
public void unregisterConfigFactory(ConfigFactory configFactory) {
checkNotNull(configFactory, NULL_FACTORY_MSG);
factories.remove(key(configFactory));
configClasses.remove(configFactory.configKey());
// Note that we are deliberately not removing subject factory key bindings.
store.removeConfigFactory(configFactory);
}
@Override
public Set<ConfigFactory> getConfigFactories() {
return ImmutableSet.copyOf(factories.values());
}
@Override
@SuppressWarnings("unchecked")
public <S, C extends Config<S>> Set<ConfigFactory<S, C>> getConfigFactories(Class<S> subjectClass) {
ImmutableSet.Builder<ConfigFactory<S, C>> builder = ImmutableSet.builder();
factories.forEach((key, factory) -> {
if (factory.subjectFactory().subjectClass().equals(subjectClass)) {
builder.add(factory);
}
});
return builder.build();
}
@Override
public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
checkNotNull(configClass, NULL_CCLASS_MSG);
return store.getConfigFactory(configClass);
}
@Override
public Set<Class> getSubjectClasses() {
ImmutableSet.Builder<Class> builder = ImmutableSet.builder();
factories.forEach((k, v) -> builder.add(k.subjectClass));
return builder.build();
}
@Override
public SubjectFactory getSubjectFactory(String subjectKey) {
return subjectClasses.get(subjectKey);
}
@Override
public SubjectFactory getSubjectFactory(Class subjectClass) {
return subjectClassKeys.get(subjectClass);
}
@Override
public Class<? extends Config> getConfigClass(String configKey) {
return configClasses.get(configKey);
}
@Override
public <S> Set<S> getSubjects(Class<S> subjectClass) {
checkNotNull(subjectClass, NULL_SCLASS_MSG);
return store.getSubjects(subjectClass);
}
@Override
public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
checkNotNull(subjectClass, NULL_SCLASS_MSG);
checkNotNull(configClass, NULL_CCLASS_MSG);
return store.getSubjects(subjectClass, configClass);
}
@Override
public <S> Set<Config<S>> getConfigs(S subject) {
checkNotNull(subject, NULL_SUBJECT_MSG);
Set<Class<? extends Config<S>>> configClasses = store.getConfigClasses(subject);
ImmutableSet.Builder<Config<S>> cfg = ImmutableSet.builder();
configClasses.forEach(cc -> cfg.add(store.getConfig(subject, cc)));
return cfg.build();
}
@Override
public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
checkNotNull(subject, NULL_SUBJECT_MSG);
checkNotNull(configClass, NULL_CCLASS_MSG);
return store.getConfig(subject, configClass);
}
@Override
public <S, C extends Config<S>> C addConfig(S subject, Class<C> configClass) {
checkNotNull(subject, NULL_SUBJECT_MSG);
checkNotNull(configClass, NULL_CCLASS_MSG);
return store.createConfig(subject, configClass);
}
@Override
public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, ObjectNode json) {
checkNotNull(subject, NULL_SUBJECT_MSG);
checkNotNull(configClass, NULL_CCLASS_MSG);
return store.applyConfig(subject, configClass, json);
}
@Override
public <S, C extends Config<S>> void removeConfig(S subject, Class<C> configClass) {
checkNotNull(subject, NULL_SUBJECT_MSG);
checkNotNull(configClass, NULL_CCLASS_MSG);
store.clearConfig(subject, configClass);
}
@Override
public void addListener(NetworkConfigListener listener) {
listenerRegistry.addListener(listener);
}
@Override
public void removeListener(NetworkConfigListener listener) {
listenerRegistry.removeListener(listener);
}
// Auxiliary store delegate to receive notification about changes in
// the network configuration store state - by the store itself.
private class InternalStoreDelegate implements NetworkConfigStoreDelegate {
@Override
public void notify(NetworkConfigEvent event) {
eventDispatcher.post(event);
}
}
// Produces a key for uniquely tracking a config factory.
private static ConfigKey key(ConfigFactory factory) {
return new ConfigKey(factory.subjectFactory().subjectClass(), factory.configClass());
}
// Auxiliary key to track config factories.
private static final class ConfigKey {
final Class subjectClass;
final Class configClass;
private ConfigKey(Class subjectClass, Class configClass) {
this.subjectClass = subjectClass;
this.configClass = configClass;
}
@Override
public int hashCode() {
return Objects.hash(subjectClass, configClass);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ConfigKey) {
final ConfigKey other = (ConfigKey) obj;
return Objects.equals(this.subjectClass, other.subjectClass)
&& Objects.equals(this.configClass, other.configClass);
}
return false;
}
}
}
/*
* Copyright 2015 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.incubator.store.config.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.DoubleNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.LongNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ShortNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.util.KryoNamespace;
import org.onosproject.incubator.net.config.Config;
import org.onosproject.incubator.net.config.ConfigApplyDelegate;
import org.onosproject.incubator.net.config.ConfigFactory;
import org.onosproject.incubator.net.config.NetworkConfigEvent;
import org.onosproject.incubator.net.config.NetworkConfigStore;
import org.onosproject.incubator.net.config.NetworkConfigStoreDelegate;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.Versioned;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static org.onosproject.incubator.net.config.NetworkConfigEvent.Type.*;
/**
* Implementation of a distributed network configuration store.
*/
@Component(immediate = true)
@Service
public class DistributedNetworkConfigStore
extends AbstractStore<NetworkConfigEvent, NetworkConfigStoreDelegate>
implements NetworkConfigStore {
private final Logger log = LoggerFactory.getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
private ConsistentMap<ConfigKey, ObjectNode> configs;
private final Map<String, ConfigFactory> factoriesByConfig = Maps.newConcurrentMap();
private final ObjectMapper mapper = new ObjectMapper();
private final ConfigApplyDelegate applyDelegate = new InternalApplyDelegate();
private final MapEventListener<ConfigKey, ObjectNode> listener = new InternalMapListener();
@Activate
public void activate() {
KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(ConfigKey.class, ObjectNode.class,
JsonNodeFactory.class, LinkedHashMap.class,
TextNode.class, BooleanNode.class,
LongNode.class, DoubleNode.class, ShortNode.class);
configs = storageService.<ConfigKey, ObjectNode>consistentMapBuilder()
.withSerializer(Serializer.using(kryoBuilder.build()))
.withName("onos-network-configs")
.build();
configs.addListener(listener);
log.info("Started");
}
@Deactivate
public void deactivate() {
configs.removeListener(listener);
log.info("Stopped");
}
@Override
public void addConfigFactory(ConfigFactory configFactory) {
factoriesByConfig.put(configFactory.configClass().getName(), configFactory);
}
@Override
public void removeConfigFactory(ConfigFactory configFactory) {
factoriesByConfig.remove(configFactory.configClass().getName());
}
@Override
@SuppressWarnings("unchecked")
public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
return (ConfigFactory<S, C>) factoriesByConfig.get(configClass.getName());
}
@Override
@SuppressWarnings("unchecked")
public <S> Set<S> getSubjects(Class<S> subjectClass) {
ImmutableSet.Builder<S> builder = ImmutableSet.builder();
configs.keySet().forEach(k -> {
if (subjectClass.isInstance(k.subject)) {
builder.add((S) k.subject);
}
});
return builder.build();
}
@Override
@SuppressWarnings("unchecked")
public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
ImmutableSet.Builder<S> builder = ImmutableSet.builder();
String cName = configClass.getName();
configs.keySet().forEach(k -> {
if (subjectClass.isInstance(k.subject) && cName.equals(k.configClass)) {
builder.add((S) k.subject);
}
});
return builder.build();
}
@Override
@SuppressWarnings("unchecked")
public <S> Set<Class<? extends Config<S>>> getConfigClasses(S subject) {
ImmutableSet.Builder<Class<? extends Config<S>>> builder = ImmutableSet.builder();
configs.keySet().forEach(k -> {
if (Objects.equals(subject, k.subject) && delegate != null) {
builder.add(factoriesByConfig.get(k.configClass).configClass());
}
});
return builder.build();
}
@Override
public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
Versioned<ObjectNode> json = configs.get(key(subject, configClass));
return json != null ? createConfig(subject, configClass, json.value()) : null;
}
@Override
public <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass) {
Versioned<ObjectNode> json = configs.computeIfAbsent(key(subject, configClass),
k -> mapper.createObjectNode());
return createConfig(subject, configClass, json.value());
}
@Override
public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, ObjectNode json) {
return createConfig(subject, configClass,
configs.putAndGet(key(subject, configClass), json).value());
}
@Override
public <S, C extends Config<S>> void clearConfig(S subject, Class<C> configClass) {
configs.remove(key(subject, configClass));
}
/**
* Produces a config from the specified subject, config class and raw JSON.
*
* @param subject config subject
* @param configClass config class
* @param json raw JSON data
* @return config object or null of no factory found or if the specified
* JSON is null
*/
@SuppressWarnings("unchecked")
private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
ObjectNode json) {
if (json != null) {
ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
if (factory != null) {
C config = factory.createConfig();
config.init(subject, factory.configKey(), json, mapper, applyDelegate);
return config;
}
}
return null;
}
// Auxiliary delegate to receive notifications about changes applied to
// the network configuration - by the apps.
private class InternalApplyDelegate implements ConfigApplyDelegate {
@Override
public void onApply(Config config) {
configs.put(key(config.subject(), config.getClass()), config.node());
}
}
// Produces a key for uniquely tracking a subject config.
private static ConfigKey key(Object subject, Class<?> configClass) {
return new ConfigKey(subject, configClass);
}
// Auxiliary key to track subject configurations.
private static final class ConfigKey {
final Object subject;
final String configClass;
private ConfigKey(Object subject, Class<?> configClass) {
this.subject = subject;
this.configClass = configClass.getName();
}
@Override
public int hashCode() {
return Objects.hash(subject, configClass);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ConfigKey) {
final ConfigKey other = (ConfigKey) obj;
return Objects.equals(this.subject, other.subject)
&& Objects.equals(this.configClass, other.configClass);
}
return false;
}
}
private class InternalMapListener implements MapEventListener<ConfigKey, ObjectNode> {
@Override
public void event(MapEvent<ConfigKey, ObjectNode> event) {
NetworkConfigEvent.Type type;
switch (event.type()) {
case INSERT:
type = CONFIG_ADDED;
break;
case UPDATE:
type = CONFIG_UPDATED;
break;
case REMOVE:
default:
type = CONFIG_REMOVED;
break;
}
ConfigFactory factory = factoriesByConfig.get(event.key().configClass);
if (factory != null) {
notifyDelegate(new NetworkConfigEvent(type, event.key().subject,
factory.configClass()));
}
}
}
}
/*
* Copyright 2015 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.rest.resources;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.incubator.net.config.NetworkConfigService;
import org.onosproject.incubator.net.config.SubjectFactory;
import org.onosproject.rest.AbstractWebResource;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
/**
* REST resource for injecting and retrieving common network configuration.
*/
@Path("network/configuration")
public class NetworkConfigWebResource extends AbstractWebResource {
/**
* Returns entire network configuration base.
*
* @return network configuration JSON
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response download() {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = mapper().createObjectNode();
service.getSubjectClasses().forEach(sc ->
produceJson(service, newObject(root, service.getSubjectFactory(sc).subjectKey()), sc));
return ok(root).build();
}
/**
* Returns the network configuration for the specified subject class.
*
* @param subjectKey subject class key
* @return network configuration JSON
*/
@GET
@Path("{subjectKey}")
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response download(@PathParam("subjectKey") String subjectKey) {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = mapper().createObjectNode();
produceJson(service, root, service.getSubjectFactory(subjectKey).subjectClass());
return ok(root).build();
}
/**
* Returns the network configuration for the specified subject.
*
* @param subjectKey subject class key
* @param subject subject key
* @return network configuration JSON
*/
@GET
@Path("{subjectKey}/{subject}")
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response download(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject) {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = mapper().createObjectNode();
produceSubjectJson(service, root,
service.getSubjectFactory(subjectKey).createSubject(subject));
return ok(root).build();
}
/**
* Returns the network configuration for the specified subject and given
* configuration class.
*
* @param subjectKey subject class key
* @param subject subject key
* @param configKey configuration class key
* @return network configuration JSON
*/
@GET
@Path("{subjectKey}/{subject}/{configKey}")
@Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response download(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject,
@PathParam("configKey") String configKey) {
NetworkConfigService service = get(NetworkConfigService.class);
return ok(service.getConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
service.getConfigClass(configKey)).node()).build();
}
@SuppressWarnings("unchecked")
private void produceJson(NetworkConfigService service, ObjectNode node,
Class subjectClass) {
service.getSubjects(subjectClass).forEach(s ->
produceSubjectJson(service, newObject(node, s.toString()), s));
}
private void produceSubjectJson(NetworkConfigService service, ObjectNode node,
Object subject) {
service.getConfigs(subject).forEach(c -> node.set(c.key(), c.node()));
}
/**
* Uploads network configuration in bulk.
*
* @param request network configuration JSON rooted at the top node
* @throws IOException
* @return empty response
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response upload(InputStream request) throws IOException {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
root.fieldNames()
.forEachRemaining(sk -> consumeJson(service, (ObjectNode) root.path(sk),
service.getSubjectFactory(sk)));
return Response.ok().build();
}
/**
* Uploads network configuration for the specified subject class.
*
* @param subjectKey subject class key
* @param request network configuration JSON rooted at the top node
* @return empty response
* @throws IOException
*/
@POST
@Path("{subjectKey}")
@Consumes(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response upload(@PathParam("subjectKey") String subjectKey,
InputStream request) throws IOException {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
consumeJson(service, root, service.getSubjectFactory(subjectKey));
return Response.ok().build();
}
/**
* Uploads network configuration for the specified subject.
*
* @param subjectKey subject class key
* @param subject subject key
* @param request network configuration JSON rooted at the top node
* @return empty response
* @throws IOException
*/
@POST
@Path("{subjectKey}/{subject}")
@Consumes(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response upload(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject,
InputStream request) throws IOException {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
consumeSubjectJson(service, root,
service.getSubjectFactory(subjectKey).createSubject(subject));
return Response.ok().build();
}
/**
* Uploads network configuration for the specified subject and given
* configuration class.
*
* @param subjectKey subject class key
* @param subject subject key
* @param configKey configuration class key
* @param request network configuration JSON rooted at the top node
* @return empty response
* @throws IOException
*/
@POST
@Path("{subjectKey}/{subject}/{configKey}")
@Consumes(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response upload(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject,
@PathParam("configKey") String configKey,
InputStream request) throws IOException {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
service.applyConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
service.getConfigClass(configKey), root);
return Response.ok().build();
}
private void consumeJson(NetworkConfigService service, ObjectNode classNode,
SubjectFactory subjectFactory) {
classNode.fieldNames().forEachRemaining(s ->
consumeSubjectJson(service, (ObjectNode) classNode.path(s),
subjectFactory.createSubject(s)));
}
private void consumeSubjectJson(NetworkConfigService service,
ObjectNode subjectNode, Object subject) {
subjectNode.fieldNames().forEachRemaining(c ->
service.applyConfig(subject, service.getConfigClass(c),
(ObjectNode) subjectNode.path(c)));
}
/**
* Clears network configuration for the specified subject.
*
* @param subjectKey subject class key
* @param subject subject key
* @return empty response
*/
@DELETE
@Path("{subjectKey}/{subject}")
@Consumes(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response upload(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject) {
NetworkConfigService service = get(NetworkConfigService.class);
Object s = service.getSubjectFactory(subjectKey).createSubject(subject);
service.getConfigs(s).forEach(c -> service.removeConfig(s, c.getClass()));
return Response.ok().build();
}
/**
* Clears network configuration for the specified subject and given
* configuration class.
*
* @param subjectKey subject class key
* @param subject subject key
* @param configKey configuration class key
* @return empty response
*/
@DELETE
@Path("{subjectKey}/{subject}/{configKey}")
@Consumes(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked")
public Response upload(@PathParam("subjectKey") String subjectKey,
@PathParam("subject") String subject,
@PathParam("configKey") String configKey) {
NetworkConfigService service = get(NetworkConfigService.class);
service.removeConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
service.getConfigClass(configKey));
return Response.ok().build();
}
}
......@@ -67,6 +67,7 @@
org.onosproject.rest.resources.ApplicationsWebResource,
org.onosproject.rest.resources.ComponentConfigWebResource,
org.onosproject.rest.resources.NetworkConfigWebResource,
org.onosproject.rest.resources.ClusterWebResource,
org.onosproject.rest.resources.DevicesWebResource,
org.onosproject.rest.resources.LinksWebResource,
......
{
"devices": {
"of:001122334455667788": {
"basic": {
"name": "Test ROADM",
"type": "ROADM",
"latitude": -38.587,
"longitude": 134.567
}
}
}
}
\ No newline at end of file