Thomas Vachuska
Committed by Ray Milkey

Sketching out component configuration model & API.

Added initial cut of implementation.
Finished implementation; ready for merge.

Change-Id: I385181c0591604a5c44986b97fb881eba7e0528e
Showing 24 changed files with 1517 additions and 41 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 org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cfg.ConfigProperty;
import org.onosproject.cli.AbstractShellCommand;
import java.util.Set;
import static com.google.common.base.Strings.isNullOrEmpty;
/**
* Manages application inventory.
*/
@Command(scope = "onos", name = "cfg",
description = "Manages component configuration")
public class ComponentConfigCommand extends AbstractShellCommand {
static final String GET = "get";
static final String SET = "set";
private static final String FMT = " name=%s, type=%s, value=%s, defaultValue=%s, description=%s";
@Argument(index = 0, name = "command",
description = "Command name (activate|deactivate|uninstall)",
required = false, multiValued = false)
String command = null;
@Argument(index = 1, name = "component", description = "Component name",
required = false, multiValued = false)
String component = null;
@Argument(index = 2, name = "name", description = "Property name",
required = false, multiValued = false)
String name = null;
@Argument(index = 3, name = "value", description = "Property value",
required = false, multiValued = false)
String value = null;
ComponentConfigService service;
@Override
protected void execute() {
service = get(ComponentConfigService.class);
if (isNullOrEmpty(command)) {
listComponents();
} else if (command.equals(GET) && isNullOrEmpty(component)) {
listAllComponentsProperties();
} else if (command.equals(GET) && isNullOrEmpty(name)) {
listComponentProperties(component);
} else if (command.equals(GET)) {
listComponentProperty(component, name);
} else if (command.equals(SET) && isNullOrEmpty(value)) {
service.unsetProperty(component, name);
} else if (command.equals(SET)) {
service.setProperty(component, name, value);
} else {
error("Illegal usage");
}
}
private void listAllComponentsProperties() {
service.getComponentNames().forEach(this::listComponentProperties);
}
private void listComponents() {
service.getComponentNames().forEach(n -> print("%s", n));
}
private void listComponentProperties(String component) {
Set<ConfigProperty> props = service.getProperties(component);
print("%s", component);
props.forEach(p -> print(FMT, p.name(), p.type().toString().toLowerCase(),
p.value(), p.defaultValue(), p.description()));
}
private void listComponentProperty(String component, String name) {
// FIXME: implement after getProperty is defined and implemented
}
}
/*
* 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.console.Completer;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import java.util.List;
import java.util.SortedSet;
import static org.onosproject.cli.cfg.ComponentConfigCommand.GET;
import static org.onosproject.cli.cfg.ComponentConfigCommand.SET;
/**
* Component configuration command completer.
*/
public class ComponentConfigCommandCompleter implements Completer {
@Override
public int complete(String buffer, int cursor, List<String> candidates) {
// Delegate string completer
StringsCompleter delegate = new StringsCompleter();
SortedSet<String> strings = delegate.getStrings();
strings.add(GET);
strings.add(SET);
// Now let the completer do the work for figuring out what to offer.
return delegate.complete(buffer, cursor, candidates);
}
}
/*
* 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.console.Completer;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cli.AbstractShellCommand;
import java.util.List;
import java.util.SortedSet;
/**
* Component name completer.
*/
public class ComponentNameCompleter implements Completer {
@Override
public int complete(String buffer, int cursor, List<String> candidates) {
// Delegate string completer
StringsCompleter delegate = new StringsCompleter();
// Fetch our service and feed it's offerings to the string completer
ComponentConfigService service = AbstractShellCommand.get(ComponentConfigService.class);
SortedSet<String> strings = delegate.getStrings();
service.getComponentNames().forEach(strings::add);
// Now let the completer do the work for figuring out what to offer.
return delegate.complete(buffer, cursor, candidates);
}
}
/*
* 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.
*/
/**
* CLI commands for managing centralized component configuration.
*/
package org.onosproject.cli.cfg;
\ No newline at end of file
......@@ -33,33 +33,16 @@
</completers>
</command>
<!--
<command>
<action class="org.onosproject.cli.app.ApplicationActivateCommand"/>
<action class="org.onosproject.cli.cfg.ComponentConfigCommand"/>
<completers>
<ref component-id="appNameCompleter"/>
<null/>
</completers>
</command>
<command>
<action class="org.onosproject.cli.app.ApplicationDeactivateCommand"/>
<completers>
<ref component-id="appNameCompleter"/>
<ref component-id="cfgCommandCompleter"/>
<ref component-id="componentNameCompleter"/>
<null/>
</completers>
</command>
<command>
<action class="org.onosproject.cli.app.ApplicationUninstallCommand"/>
<completers>
<ref component-id="appNameCompleter"/>
<null/>
</completers>
</command>
-->
<command>
<action class="org.onosproject.cli.MetricsListCommand"/>
</command>
......@@ -309,6 +292,8 @@
<bean id="appCommandCompleter" class="org.onosproject.cli.app.ApplicationCommandCompleter"/>
<bean id="appNameCompleter" class="org.onosproject.cli.app.ApplicationNameCompleter"/>
<bean id="appIdNameCompleter" class="org.onosproject.cli.app.ApplicationIdNameCompleter"/>
<bean id="cfgCommandCompleter" class="org.onosproject.cli.cfg.ComponentConfigCommandCompleter"/>
<bean id="componentNameCompleter" class="org.onosproject.cli.cfg.ComponentNameCompleter"/>
<bean id="nodeIdCompleter" class="org.onosproject.cli.NodeIdCompleter"/>
<bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
<bean id="clusterIdCompleter" class="org.onosproject.cli.net.ClusterIdCompleter"/>
......
/*
* 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.cfg;
import org.onosproject.event.AbstractEvent;
/**
* Describes a component configuration event.
*/
public class ComponentConfigEvent extends AbstractEvent<ComponentConfigEvent.Type, String> {
private final String name;
private final String value;
public enum Type {
/**
* Signifies that a configuration property has set.
*/
PROPERTY_SET,
/**
* Signifies that a configuration property has been unset.
*/
PROPERTY_UNSET
}
/**
* Creates an event of a given type and for the specified app and the
* current time.
*
* @param type config property event type
* @param componentName component name event subject
* @param name config property name
* @param value config property value
*/
public ComponentConfigEvent(Type type, String componentName,
String name, String value) {
super(type, componentName);
this.name = name;
this.value = value;
}
/**
* Returns the property name.
*
* @return property name
*/
public String name() {
return name;
}
/**
* Returns the property value as a string.
*
* @return string value
*/
public String value() {
return value;
}
}
/*
* 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.cfg;
import java.util.Set;
/**
* Service for tracking system-wide configurations for various software components.
*/
public interface ComponentConfigService {
/**
* Returns names of all components that have registered their
* configuration properties.
*
* @return set of component names
*/
Set<String> getComponentNames();
/**
* Registers configuration properties for the specified component.
*
* @param componentClass class of configurable component
*/
void registerProperties(Class<?> componentClass);
/**
* Unregisters configuration properties for the specified component.
*
* @param componentClass class of configurable component
* @param clear true indicates any settings should be cleared
*/
void unregisterProperties(Class<?> componentClass, boolean clear);
/**
* Returns configuration properties of the named components.
*
* @param componentName component name
* @return set of configuration properties
*/
Set<ConfigProperty> getProperties(String componentName);
/**
* Sets the value of the specified configuration property.
*
* @param componentName component name
* @param name property name
* @param value new property value
*/
void setProperty(String componentName, String name, String value);
/**
* Clears the value of the specified configuration property thus making
* the property take on its default value.
*
* @param componentName component name
* @param name property name
*/
void unsetProperty(String componentName, String name);
}
/*
* 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.cfg;
import org.onosproject.store.Store;
/**
* Service for storing and distributing system-wide configurations for various
* software components.
*/
public interface ComponentConfigStore
extends Store<ComponentConfigEvent, ComponentConfigStoreDelegate> {
/**
* Sets the value of the specified configuration property.
*
* @param componentName component name
* @param name property name
* @param value new property value
*/
void setProperty(String componentName, String name, String value);
/**
* Clears the value of the specified configuration property thus making
* the property take on its default value.
*
* @param componentName component name
* @param name property name
*/
void unsetProperty(String componentName, String name);
}
/*
* 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.cfg;
import org.onosproject.store.StoreDelegate;
/**
* Configuration property store delegate abstraction.
*/
public interface ComponentConfigStoreDelegate extends StoreDelegate<ComponentConfigEvent> {
}
/*
* 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.cfg;
import com.google.common.base.MoreObjects;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* Component configuration property.
*/
public final class ConfigProperty {
private final String name;
private final Type type;
private final String value;
private final String defaultValue;
private final String description;
private final boolean isSet;
/**
* Representation of the type of property value.
*/
public enum Type {
/**
* Indicates the value is a string.
*/
STRING,
/**
* Indicates the value is an integer.
*/
INTEGER,
/**
* Indicates the value is a long.
*/
LONG,
/**
* Indicates the value is a float.
*/
FLOAT,
/**
* Indicates the value is a double.
*/
DOUBLE,
/**
* Indicates the value is a boolean.
*/
BOOLEAN
}
/**
* Creates a new configuration property with its default value.
*
* @param name property name
* @param type value type
* @param defaultValue default value as a string
* @param description property description
* @return newly defined property
*/
public static ConfigProperty defineProperty(String name, Type type,
String defaultValue,
String description) {
return new ConfigProperty(name, type, description, defaultValue, defaultValue, false);
}
/**
* Creates a new configuration property as a copy of an existing one, but
* with a new value.
*
* @param property property to be changed
* @param newValue new value as a string
* @return newly updated property
*/
public static ConfigProperty setProperty(ConfigProperty property, String newValue) {
return new ConfigProperty(property.name, property.type, property.description,
property.defaultValue, newValue, true);
}
/**
* Creates a new configuration property as a copy of an existing one, but
* without a specific value, thus making it take its default value.
*
* @param property property to be reset
* @return newly reset property
*/
public static ConfigProperty resetProperty(ConfigProperty property) {
return new ConfigProperty(property.name, property.type, property.description,
property.defaultValue, property.defaultValue, false);
}
/**
* Creates a new configuration property with its default value.
*
* @param name property name
* @param type value type
* @param defaultValue default value as a string
* @param description property description
* @param value property value
* @param isSet indicates whether the property is set or not
*/
private ConfigProperty(String name, Type type, String description,
String defaultValue, String value, boolean isSet) {
this.name = checkNotNull(name, "Property name cannot be null");
this.type = checkNotNull(type, "Property type cannot be null");
this.description = checkNotNull(description, "Property description cannot be null");
this.defaultValue = defaultValue;
this.value = value;
this.isSet = isSet;
}
/**
* Returns the property name.
*
* @return property name
*/
public String name() {
return name;
}
/**
* Returns the property type.
*
* @return property type
*/
public Type type() {
return type;
}
/**
* Returns the property description.
*
* @return string value
*/
public String description() {
return description;
}
/**
* Returns the property default value as a string.
*
* @return string default value
*/
public String defaultValue() {
return defaultValue;
}
/**
* Returns the property value as a string.
*
* @return string value
*/
public String value() {
return value;
}
/**
* Indicates whether the property is set or whether it assumes its
* default value.
*
* @return true if the property is set
*/
public boolean isSet() {
return isSet;
}
/**
* Returns the property value as a string.
*
* @return string value
*/
public String asString() {
return value;
}
/**
* Returns the property value as an integer.
*
* @return integer value
*/
public int asInteger() {
checkState(type == Type.INTEGER, "Value is not an integer");
return Integer.parseInt(value);
}
/**
* Returns the property value as a long.
*
* @return long value
*/
public long asLong() {
checkState(type == Type.INTEGER || type == Type.LONG, "Value is not a long or integer");
return Long.parseLong(value);
}
/**
* Returns the property value as a float.
*
* @return float value
*/
public float asFloat() {
checkState(type == Type.FLOAT, "Value is not a float");
return Float.parseFloat(value);
}
/**
* Returns the property value as a double.
*
* @return double value
*/
public double asDouble() {
checkState(type == Type.FLOAT || type == Type.DOUBLE, "Value is not a float or double");
return Double.parseDouble(value);
}
/**
* Returns the property value as a boolean.
*
* @return string value
*/
public boolean asBoolean() {
checkState(type == Type.BOOLEAN, "Value is not a boolean");
return Boolean.parseBoolean(value);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
/**
* {@inheritDoc}
*
* Equality is considered only on the basis of property name.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ConfigProperty) {
final ConfigProperty other = (ConfigProperty) obj;
return Objects.equals(this.name, other.name);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("name", name)
.add("type", type)
.add("value", value)
.add("defaultValue", defaultValue)
.add("description", description)
.add("isSet", isSet)
.toString();
}
}
/*
* 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.
*/
/**
* Set of abstractions for centrally managing component configurations.
* Configuration properties are registered for a component resource which is
* auto-generated during the build process based on information specified in
* the @Property annotations. This provides an overall inventory of all
* supported component configurations.
*/
package org.onosproject.cfg;
\ No newline at end of file
/*
* 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.cfg;
import java.util.Set;
/**
* Adapter for testing against component configuration service.
*/
public class ComponentConfigAdapter implements ComponentConfigService {
@Override
public Set<String> getComponentNames() {
return null;
}
@Override
public void registerProperties(Class<?> componentClass) {
}
@Override
public void unregisterProperties(Class<?> componentClass, boolean clear) {
}
@Override
public Set<ConfigProperty> getProperties(String componentName) {
return null;
}
@Override
public void setProperty(String componentName, String name, String value) {
}
@Override
public void unsetProperty(String componentName, String name) {
}
}
/*
* 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.cfg;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import org.onosproject.cfg.ConfigProperty.Type;
import static org.junit.Assert.*;
import static org.onosproject.cfg.ConfigProperty.Type.*;
import static org.onosproject.cfg.ConfigProperty.defineProperty;
import static org.onosproject.cfg.ConfigProperty.resetProperty;
import static org.onosproject.cfg.ConfigProperty.setProperty;
/**
* Set of tests of the configuration property class.
*/
public class ConfigPropertyTest {
@Test
public void basics() {
ConfigProperty p = defineProperty("foo", STRING, "bar", "Foo Prop");
validate(p, "foo", STRING, "bar", "bar");
p = setProperty(p, "BAR");
validate(p, "foo", STRING, "BAR", "bar");
p = resetProperty(p);
validate(p, "foo", STRING, "bar", "bar");
}
@Test
public void equality() {
new EqualsTester()
.addEqualityGroup(defineProperty("foo", STRING, "bar", "Desc"),
defineProperty("foo", STRING, "goo", "Desc"))
.addEqualityGroup(defineProperty("bar", STRING, "bar", "Desc"),
defineProperty("bar", STRING, "goo", "Desc"))
.testEquals();
}
private void validate(ConfigProperty p, String name, Type type, String v, String dv) {
assertEquals("incorrect name", name, p.name());
assertEquals("incorrect type", type, p.type());
assertEquals("incorrect value", v, p.value());
assertEquals("incorrect default", dv, p.defaultValue());
assertEquals("incorrect description", "Foo Prop", p.description());
}
@Test
public void asInteger() {
ConfigProperty p = defineProperty("foo", INTEGER, "123", "Foo Prop");
validate(p, "foo", INTEGER, "123", "123");
assertEquals("incorrect value", 123, p.asInteger());
assertEquals("incorrect value", 123L, p.asLong());
}
@Test
public void asLong() {
ConfigProperty p = defineProperty("foo", LONG, "123", "Foo Prop");
validate(p, "foo", LONG, "123", "123");
assertEquals("incorrect value", 123L, p.asLong());
}
@Test
public void asFloat() {
ConfigProperty p = defineProperty("foo", FLOAT, "123.0", "Foo Prop");
validate(p, "foo", FLOAT, "123.0", "123.0");
assertEquals("incorrect value", 123.0, p.asFloat(), 0.01);
assertEquals("incorrect value", 123.0, p.asDouble(), 0.01);
}
@Test
public void asDouble() {
ConfigProperty p = defineProperty("foo", DOUBLE, "123.0", "Foo Prop");
validate(p, "foo", DOUBLE, "123.0", "123.0");
assertEquals("incorrect value", 123.0, p.asDouble(), 0.01);
}
@Test
public void asBoolean() {
ConfigProperty p = defineProperty("foo", BOOLEAN, "true", "Foo Prop");
validate(p, "foo", BOOLEAN, "true", "true");
assertEquals("incorrect value", true, p.asBoolean());
}
}
\ No newline at end of file
/*
* 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.cfg.impl;
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.cfg.ComponentConfigEvent;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cfg.ComponentConfigStore;
import org.onosproject.cfg.ComponentConfigStoreDelegate;
import org.onosproject.cfg.ConfigProperty;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Implementation of the centralized component configuration service.
*/
@Component(immediate = true)
@Service
public class ComponentConfigManager implements ComponentConfigService {
private static final String COMPONENT_NULL = "Component name cannot be null";
private static final String PROPERTY_NULL = "Property name cannot be null";
private static final String RESOURCE_EXT = ".cfgdef";
private final Logger log = getLogger(getClass());
private final ComponentConfigStoreDelegate delegate = new InternalStoreDelegate();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigStore store;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ConfigurationAdmin cfgAdmin;
// Locally maintained catalog of definitions.
private final Map<String, Map<String, ConfigProperty>> properties =
Maps.newConcurrentMap();
@Activate
public void activate() {
store.setDelegate(delegate);
log.info("Started");
}
@Deactivate
public void deactivate() {
store.unsetDelegate(delegate);
log.info("Stopped");
}
@Override
public Set<String> getComponentNames() {
return ImmutableSet.copyOf(properties.keySet());
}
@Override
public void registerProperties(Class<?> componentClass) {
String componentName = componentClass.getName();
String resourceName = componentClass.getSimpleName() + RESOURCE_EXT;
try (InputStream ris = componentClass.getResourceAsStream(resourceName)) {
checkArgument(ris != null, "Property definitions not found at resource %s",
resourceName);
// Read the definitions
Set<ConfigProperty> defs = ConfigPropertyDefinitions.read(ris);
// Produce a new map of the properties and register it.
Map<String, ConfigProperty> map = Maps.newConcurrentMap();
defs.forEach(p -> map.put(p.name(), p));
properties.put(componentName, map);
loadExistingValues(componentName);
} catch (IOException e) {
log.error("Unable to read property definitions from resource " + resourceName, e);
}
}
@Override
public void unregisterProperties(Class<?> componentClass, boolean clear) {
String componentName = componentClass.getName();
checkNotNull(componentName, COMPONENT_NULL);
Map<String, ConfigProperty> cps = properties.remove(componentName);
if (cps != null) {
cps.keySet().forEach(name -> store.unsetProperty(componentName, name));
}
if (clear) {
clearExistingValues(componentName);
}
}
// Clears any existing values that may have been set.
private void clearExistingValues(String componentName) {
triggerUpdate(componentName);
}
@Override
public Set<ConfigProperty> getProperties(String componentName) {
return ImmutableSet.copyOf(properties.get(componentName).values());
}
@Override
public void setProperty(String componentName, String name, String value) {
checkNotNull(componentName, COMPONENT_NULL);
checkNotNull(name, PROPERTY_NULL);
store.setProperty(componentName, name, value);
}
@Override
public void unsetProperty(String componentName, String name) {
checkNotNull(componentName, COMPONENT_NULL);
checkNotNull(name, PROPERTY_NULL);
store.unsetProperty(componentName, name);
}
private class InternalStoreDelegate implements ComponentConfigStoreDelegate {
@Override
public void notify(ComponentConfigEvent event) {
String componentName = event.subject();
String name = event.name();
String value = event.value();
switch (event.type()) {
case PROPERTY_SET:
set(componentName, name, value);
break;
case PROPERTY_UNSET:
reset(componentName, name);
break;
default:
break;
}
}
}
// Locates the property in the component map and replaces it with an
// updated copy.
private void set(String componentName, String name, String value) {
Map<String, ConfigProperty> map = properties.get(componentName);
if (map != null) {
ConfigProperty prop = map.get(name);
if (prop != null) {
map.put(name, ConfigProperty.setProperty(prop, value));
triggerUpdate(componentName);
return;
}
}
log.warn("Unable to set non-existent property {} for component {}",
name, componentName);
}
// Locates the property in the component map and replaces it with an
// reset copy.
private void reset(String componentName, String name) {
Map<String, ConfigProperty> map = properties.get(componentName);
if (map != null) {
ConfigProperty prop = map.get(name);
if (prop != null) {
map.put(name, ConfigProperty.resetProperty(prop));
triggerUpdate(componentName);
return;
}
}
log.warn("Unable to reset non-existent property {} for component {}",
name, componentName);
}
// Loads existing property values that may have been set.
private void loadExistingValues(String componentName) {
// FIXME: implement this by talking to the config admin.
try {
Configuration cfg = cfgAdmin.getConfiguration(componentName);
Map<String, ConfigProperty> map = properties.get(componentName);
Dictionary<String, Object> props = cfg.getProperties();
if (props != null) {
Enumeration<String> it = props.keys();
while (it.hasMoreElements()) {
String name = it.nextElement();
ConfigProperty p = map.get(name);
if (p != null) {
map.put(name, ConfigProperty.setProperty(p, (String) props.get(name)));
}
}
}
} catch (IOException e) {
log.error("Unable to get configuration for " + componentName, e);
}
}
// FIXME: This should be a slightly deferred execution to allow changing
// values just once per component when a number of updates arrive shortly
// after each other.
private void triggerUpdate(String componentName) {
try {
Configuration cfg = cfgAdmin.getConfiguration(componentName);
Map<String, ConfigProperty> map = properties.get(componentName);
Dictionary<String, Object> props = new Hashtable<>();
map.values().forEach(p -> props.put(p.name(), p.value()));
cfg.update(props);
} catch (IOException e) {
log.warn("Unable to update configuration for " + componentName, e);
}
}
}
/*
* 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.cfg.impl;
import com.google.common.collect.ImmutableSet;
import org.onosproject.cfg.ConfigProperty;
import org.onosproject.cfg.ConfigProperty.Type;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Set;
import static org.onosproject.cfg.ConfigProperty.defineProperty;
/**
* Utility for writing and reading configuration property definition file.
*/
public final class ConfigPropertyDefinitions {
private static final String FMT = "%s|%s|%s|%s\n";
private static final String SEP = "\\|";
private static final String COMMENT = "#";
private ConfigPropertyDefinitions() {
}
/**
* Writes the specified set of property definitions into the given output
* stream.
*
* @param stream output stream
* @param props properties whose definitions are to be written
* @throws java.io.IOException if unable to write the stream
*/
public static void write(OutputStream stream, Set<ConfigProperty> props) throws IOException {
try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(stream))) {
props.forEach(p -> pw.format(FMT, p.name(), p.type(), p.description(), p.defaultValue()));
}
}
/**
* Reads the specified input stream and creates from its contents a
* set of property definitions.
*
* @param stream input stream
* @return properties whose definitions are contained in the stream
* @throws java.io.IOException if unable to read the stream
*/
public static Set<ConfigProperty> read(InputStream stream) throws IOException {
ImmutableSet.Builder<ConfigProperty> builder = ImmutableSet.builder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
String line;
while ((line = br.readLine()) != null) {
if (!line.isEmpty() && !line.startsWith(COMMENT)) {
String[] f = line.split(SEP, 4);
builder.add(defineProperty(f[0], Type.valueOf(f[1]), f[2], f[3]));
}
}
}
return builder.build();
}
}
/*
* 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.
*/
/**
* Subsystem for central management of component configurations.
*/
package org.onosproject.cfg.impl;
\ No newline at end of file
......@@ -40,6 +40,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.util.AbstractAccumulator;
import org.onlab.util.Accumulator;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.event.Event;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
......@@ -59,13 +60,14 @@ import org.slf4j.Logger;
import com.google.common.collect.ImmutableList;
/**
* Default implementation of a network topology provider that feeds off device
* and link subsystem events to trigger assembly and computation of new topology
* snapshots.
* Default implementation of a network topology provider that feeds off
* device and link subsystem events to trigger assembly and computation of
* new topology snapshots.
*/
@Component(immediate = true)
@Service
public class DefaultTopologyProvider extends AbstractProvider implements TopologyProvider {
public class DefaultTopologyProvider extends AbstractProvider
implements TopologyProvider {
private static final int MAX_THREADS = 8;
private static final int DEFAULT_MAX_EVENTS = 1000;
......@@ -73,8 +75,7 @@ public class DefaultTopologyProvider extends AbstractProvider implements Topolog
private static final int DEFAULT_MAX_BATCH_MS = 50;
// FIXME: Replace with a system-wide timer instance;
// TODO: Convert to use HashedWheelTimer or produce a variant of that; then
// decide which we want to adopt
// TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
private static final Timer TIMER = new Timer("onos-topo-event-batching");
@Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
......@@ -100,6 +101,9 @@ public class DefaultTopologyProvider extends AbstractProvider implements Topolog
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkService linkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
private volatile boolean isStarted = false;
private TopologyProviderService providerService;
......@@ -118,8 +122,8 @@ public class DefaultTopologyProvider extends AbstractProvider implements Topolog
@Activate
public synchronized void activate(ComponentContext context) {
executor = newFixedThreadPool(MAX_THREADS,
groupedThreads("onos/topo", "build-%d"));
cfgService.registerProperties(DefaultTopologyProvider.class);
executor = newFixedThreadPool(MAX_THREADS, groupedThreads("onos/topo", "build-%d"));
accumulator = new TopologyChangeAccumulator();
logConfig("Configured");
......@@ -136,6 +140,7 @@ public class DefaultTopologyProvider extends AbstractProvider implements Topolog
@Deactivate
public synchronized void deactivate(ComponentContext context) {
cfgService.unregisterProperties(DefaultTopologyProvider.class, false);
isStarted = false;
deviceService.removeListener(deviceListener);
......@@ -175,23 +180,21 @@ public class DefaultTopologyProvider extends AbstractProvider implements Topolog
newMaxIdleMs = DEFAULT_MAX_IDLE_MS;
}
if ((newMaxEvents != maxEvents) || (newMaxBatchMs != maxBatchMs)
|| (newMaxIdleMs != maxIdleMs)) {
if (newMaxEvents != maxEvents || newMaxBatchMs != maxBatchMs || newMaxIdleMs != maxIdleMs) {
maxEvents = newMaxEvents;
maxBatchMs = newMaxBatchMs;
maxIdleMs = newMaxIdleMs;
accumulator = maxEvents > 1 ? new TopologyChangeAccumulator()
: null;
accumulator = maxEvents > 1 ? new TopologyChangeAccumulator() : null;
logConfig("Reconfigured");
}
}
private void logConfig(String prefix) {
log.info(
"{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
log.info("{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
}
@Override
public void triggerRecompute() {
triggerTopologyBuild(Collections.<Event>emptyList());
......@@ -201,8 +204,7 @@ public class DefaultTopologyProvider extends AbstractProvider implements Topolog
* Triggers assembly of topology data citing the specified events as the
* reason.
*
* @param reasons
* events which triggered the topology change
* @param reasons events which triggered the topology change
*/
private synchronized void triggerTopologyBuild(List<Event> reasons) {
if (executor != null) {
......@@ -236,8 +238,8 @@ public class DefaultTopologyProvider extends AbstractProvider implements Topolog
@Override
public void event(DeviceEvent event) {
DeviceEvent.Type type = event.type();
if ((type == DEVICE_ADDED) || (type == DEVICE_REMOVED) ||
(type == DEVICE_AVAILABILITY_CHANGED)) {
if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
type == DEVICE_AVAILABILITY_CHANGED) {
processEvent(event);
}
}
......@@ -276,8 +278,7 @@ public class DefaultTopologyProvider extends AbstractProvider implements Topolog
try {
buildTopology(reasons);
} catch (Exception e) {
log.warn("Unable to compute topology due to: {}",
e.getMessage());
log.warn("Unable to compute topology due to: {}", e.getMessage());
log.debug("Unable to compute topology", e);
}
}
......
#
# 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.
#
# This is for temporary provision for testing purposes and will be auto-generated.
maxEvents|INTEGER|1000|Maximum number of events to accumulate
maxIdleMs|INTEGER|10|Maximum number of millis between events
maxBatchMs|INTEGER|50|Maximum number of millis for whole batch
/*
* 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.cfg.impl;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
import org.onosproject.cfg.ConfigProperty;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Set;
import static org.junit.Assert.*;
import static org.onosproject.cfg.ConfigProperty.Type.STRING;
import static org.onosproject.cfg.ConfigProperty.defineProperty;
import static org.onosproject.cfg.impl.ConfigPropertyDefinitions.read;
import static org.onosproject.cfg.impl.ConfigPropertyDefinitions.write;
/**
* Tests of the config property definitions utility.
*/
public class ConfigPropertyDefinitionsTest {
@Test
public void basics() throws IOException {
Set<ConfigProperty> original = ImmutableSet
.of(defineProperty("foo", STRING, "dingo", "FOO"),
defineProperty("bar", STRING, "bat", "BAR"));
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
write(out, original);
Set<ConfigProperty> read = read(new ByteArrayInputStream(out.toByteArray()));
assertEquals("incorrect defs", original, read);
}
}
\ No newline at end of file
......@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.cfg.ComponentConfigAdapter;
import org.onosproject.event.Event;
import org.onosproject.event.impl.TestEventDispatcher;
import org.onosproject.net.Device;
......@@ -67,6 +68,7 @@ public class DefaultTopologyProviderTest {
provider.deviceService = deviceService;
provider.linkService = linkService;
provider.providerRegistry = topologyService;
provider.cfgService = new ComponentConfigAdapter();
provider.activate(null);
}
......@@ -76,6 +78,7 @@ public class DefaultTopologyProviderTest {
provider.providerRegistry = null;
provider.deviceService = null;
provider.linkService = null;
provider.cfgService = null;
}
private void validateSubmission() {
......
/*
* 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.store.cfg;
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.cfg.ComponentConfigEvent;
import org.onosproject.cfg.ComponentConfigStore;
import org.onosproject.cfg.ComponentConfigStoreDelegate;
import org.onosproject.cluster.ClusterService;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
import org.onosproject.store.ecmap.EventuallyConsistentMap;
import org.onosproject.store.ecmap.EventuallyConsistentMapEvent;
import org.onosproject.store.ecmap.EventuallyConsistentMapImpl;
import org.onosproject.store.ecmap.EventuallyConsistentMapListener;
import org.onosproject.store.impl.WallclockClockManager;
import org.onosproject.store.serializers.KryoNamespaces;
import org.slf4j.Logger;
import static org.onosproject.cfg.ComponentConfigEvent.Type.PROPERTY_SET;
import static org.onosproject.cfg.ComponentConfigEvent.Type.PROPERTY_UNSET;
import static org.onosproject.store.ecmap.EventuallyConsistentMapEvent.Type.PUT;
import static org.onosproject.store.ecmap.EventuallyConsistentMapEvent.Type.REMOVE;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages inventory of component configurations in a distributed data store
* that uses optimistic replication and gossip based anti-entropy techniques.
*/
@Component(immediate = true)
@Service
public class GossipComponentConfigStore
extends AbstractStore<ComponentConfigEvent, ComponentConfigStoreDelegate>
implements ComponentConfigStore {
private static final String SEP = "#";
private final Logger log = getLogger(getClass());
private EventuallyConsistentMap<String, String> properties;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterCommunicationService clusterCommunicator;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
@Activate
public void activate() {
KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API);
properties = new EventuallyConsistentMapImpl<>("cfg", clusterService,
clusterCommunicator,
serializer,
new WallclockClockManager<>());
properties.addListener(new InternalPropertiesListener());
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
@Override
public void setProperty(String componentName, String name, String value) {
properties.put(key(componentName, name), value);
}
@Override
public void unsetProperty(String componentName, String name) {
properties.remove(key(componentName, name));
}
/**
* Listener to component configuration properties distributed map changes.
*/
private final class InternalPropertiesListener
implements EventuallyConsistentMapListener<String, String> {
@Override
public void event(EventuallyConsistentMapEvent<String, String> event) {
String[] keys = event.key().split(SEP);
String value = event.value();
if (event.type() == PUT) {
delegate.notify(new ComponentConfigEvent(PROPERTY_SET, keys[0], keys[1], value));
} else if (event.type() == REMOVE) {
delegate.notify(new ComponentConfigEvent(PROPERTY_UNSET, keys[0], keys[1], null));
}
}
}
// Generates a key from component name and property name.
private String key(String componentName, String name) {
return componentName + SEP + name;
}
}
/*
* 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.
*/
/**
* Implementation of distributed component configuration store.
*/
package org.onosproject.store.cfg;
/*
* 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.store.trivial.impl;
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.Service;
import org.onosproject.cfg.ComponentConfigEvent;
import org.onosproject.cfg.ComponentConfigStore;
import org.onosproject.cfg.ComponentConfigStoreDelegate;
import org.onosproject.store.AbstractStore;
import org.slf4j.Logger;
import static org.onosproject.cfg.ComponentConfigEvent.Type.PROPERTY_SET;
import static org.onosproject.cfg.ComponentConfigEvent.Type.PROPERTY_UNSET;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages inventory of component configuration properties.
*/
@Component(immediate = true)
@Service
public class SimpleComponentConfigStore
extends AbstractStore<ComponentConfigEvent, ComponentConfigStoreDelegate>
implements ComponentConfigStore {
private final Logger log = getLogger(getClass());
@Activate
public void activate() {
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
@Override
public void setProperty(String componentName, String name, String value) {
delegate.notify(new ComponentConfigEvent(PROPERTY_SET, componentName, name, value));
}
@Override
public void unsetProperty(String componentName, String name) {
delegate.notify(new ComponentConfigEvent(PROPERTY_UNSET, componentName, name, null));
}
}
......@@ -61,7 +61,7 @@
<group>
<title>Core Subsystems</title>
<packages>
org.onosproject.impl:org.onosproject.core.impl:org.onosproject.cluster.impl:org.onosproject.net.device.impl:org.onosproject.net.link.impl:org.onosproject.net.host.impl:org.onosproject.net.topology.impl:org.onosproject.net.packet.impl:org.onosproject.net.flow.impl:org.onosproject.net.*.impl:org.onosproject.event.impl:org.onosproject.net.intent.impl*:org.onosproject.net.proxyarp.impl:org.onosproject.mastership.impl:org.onosproject.net.resource.impl:org.onosproject.json:org.onosproject.json.*:org.onosproject.provider.host.impl:org.onosproject.provider.lldp.impl:org.onosproject.net.statistic.impl:org.onosproject.app.impl:org.onosproject.common.*:org.onosproject.net.group.impl
org.onosproject.impl:org.onosproject.core.impl:org.onosproject.cluster.impl:org.onosproject.net.device.impl:org.onosproject.net.link.impl:org.onosproject.net.host.impl:org.onosproject.net.topology.impl:org.onosproject.net.packet.impl:org.onosproject.net.flow.impl:org.onosproject.net.*.impl:org.onosproject.event.impl:org.onosproject.net.intent.impl*:org.onosproject.net.proxyarp.impl:org.onosproject.mastership.impl:org.onosproject.net.resource.impl:org.onosproject.json:org.onosproject.json.*:org.onosproject.provider.host.impl:org.onosproject.provider.lldp.impl:org.onosproject.net.statistic.impl:org.onosproject.app.impl:org.onosproject.common.*:org.onosproject.net.group.impl:org.onosproject.cfg.impl
</packages>
</group>
<group>
......