Jonathan Hart

Move address-bindings config to new config system

Change-Id: I6d87ddbf98789dbe8355c453a3263f50fbc5d99c
......@@ -63,7 +63,7 @@ public class NetworkConfigCommand extends AbstractShellCommand {
if (isNullOrEmpty(configKey)) {
addSubject(root, s);
} else {
root = getSubjectConfig(getConfig(s, configKey));
root = getSubjectConfig(getConfig(s, subjectKey, configKey));
}
}
}
......@@ -93,8 +93,8 @@ public class NetworkConfigCommand extends AbstractShellCommand {
return config != null ? config.node() : null;
}
private Config getConfig(Object s, String ck) {
Class<? extends Config> configClass = service.getConfigClass(ck);
private Config getConfig(Object s, String subjectKey, String ck) {
Class<? extends Config> configClass = service.getConfigClass(subjectKey, ck);
return configClass != null ? service.getConfig(s, configClass) : null;
}
......
......@@ -15,10 +15,7 @@
*/
package org.onosproject.cli.net;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.cli.Comparators;
......@@ -26,7 +23,9 @@ import org.onosproject.net.host.HostService;
import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.host.PortAddresses;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Lists all configured address port bindings.
......
......@@ -15,11 +15,12 @@
*/
package org.onosproject.net.host;
import java.util.Objects;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import static com.google.common.base.MoreObjects.toStringHelper;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
......@@ -138,6 +139,24 @@ public class InterfaceIpAddress {
return peerAddress;
}
/**
* Converts a CIDR string literal to an interface IP address.
* E.g. 10.0.0.1/24
*
* @param value an IP address value in string form
* @return an interface IP address
* @throws IllegalArgumentException if the argument is invalid
*/
public static InterfaceIpAddress valueOf(String value) {
String[] splits = value.split("/");
checkArgument(splits.length == 2, "Invalid IP address and prefix length format");
// NOTE: IpPrefix will mask-out the bits after the prefix length.
IpPrefix subnet = IpPrefix.valueOf(value);
IpAddress addr = IpAddress.valueOf(splits[0]);
return new InterfaceIpAddress(addr, subnet);
}
@Override
public boolean equals(Object other) {
if (other == this) {
......@@ -163,10 +182,11 @@ public class InterfaceIpAddress {
@Override
public String toString() {
return toStringHelper(this).add("ipAddress", ipAddress)
/*return toStringHelper(this).add("ipAddress", ipAddress)
.add("subnetAddress", subnetAddress)
.add("broadcastAddress", broadcastAddress)
.add("peerAddress", peerAddress)
.omitNullValues().toString();
.omitNullValues().toString();*/
return ipAddress.toString() + "/" + subnetAddress.prefixLength();
}
}
......
......@@ -54,22 +54,28 @@ public class InterfaceIpAddressTest {
// Regular interface address with default broadcast address
fromAddr = new InterfaceIpAddress(IP_ADDRESS, SUBNET_ADDRESS);
toAddr = new InterfaceIpAddress(fromAddr);
assertThat(toAddr.toString(),
is("InterfaceIpAddress{ipAddress=1.2.3.4, subnetAddress=1.2.0.0/16}"));
assertThat(toAddr.ipAddress(), is(fromAddr.ipAddress()));
assertThat(toAddr.subnetAddress(), is(fromAddr.subnetAddress()));
assertThat(toAddr.broadcastAddress(), is(fromAddr.broadcastAddress()));
assertThat(toAddr.peerAddress(), is(fromAddr.peerAddress()));
// Interface address with non-default broadcast address
fromAddr = new InterfaceIpAddress(IP_ADDRESS, SUBNET_ADDRESS,
BROADCAST_ADDRESS);
toAddr = new InterfaceIpAddress(fromAddr);
assertThat(toAddr.toString(),
is("InterfaceIpAddress{ipAddress=1.2.3.4, subnetAddress=1.2.0.0/16, broadcastAddress=1.2.0.255}"));
assertThat(toAddr.ipAddress(), is(fromAddr.ipAddress()));
assertThat(toAddr.subnetAddress(), is(fromAddr.subnetAddress()));
assertThat(toAddr.broadcastAddress(), is(fromAddr.broadcastAddress()));
assertThat(toAddr.peerAddress(), is(fromAddr.peerAddress()));
// Point-to-point address with peer IP address
fromAddr = new InterfaceIpAddress(IP_ADDRESS, SUBNET_ADDRESS, null,
PEER_ADDRESS);
toAddr = new InterfaceIpAddress(fromAddr);
assertThat(toAddr.toString(),
is("InterfaceIpAddress{ipAddress=1.2.3.4, subnetAddress=1.2.0.0/16, peerAddress=5.6.7.8}"));
assertThat(toAddr.ipAddress(), is(fromAddr.ipAddress()));
assertThat(toAddr.subnetAddress(), is(fromAddr.subnetAddress()));
assertThat(toAddr.broadcastAddress(), is(fromAddr.broadcastAddress()));
assertThat(toAddr.peerAddress(), is(fromAddr.peerAddress()));
}
/**
......@@ -89,8 +95,10 @@ public class InterfaceIpAddressTest {
public void testConstructorForDefaultBroadcastAddress() {
InterfaceIpAddress addr =
new InterfaceIpAddress(IP_ADDRESS, SUBNET_ADDRESS);
assertThat(addr.toString(),
is("InterfaceIpAddress{ipAddress=1.2.3.4, subnetAddress=1.2.0.0/16}"));
assertThat(addr.ipAddress(), is(IP_ADDRESS));
assertThat(addr.subnetAddress(), is(SUBNET_ADDRESS));
assertThat(addr.broadcastAddress(), nullValue());
assertThat(addr.peerAddress(), nullValue());
}
/**
......@@ -102,8 +110,11 @@ public class InterfaceIpAddressTest {
InterfaceIpAddress addr =
new InterfaceIpAddress(IP_ADDRESS, SUBNET_ADDRESS,
BROADCAST_ADDRESS);
assertThat(addr.toString(),
is("InterfaceIpAddress{ipAddress=1.2.3.4, subnetAddress=1.2.0.0/16, broadcastAddress=1.2.0.255}"));
assertThat(addr.ipAddress(), is(IP_ADDRESS));
assertThat(addr.subnetAddress(), is(SUBNET_ADDRESS));
assertThat(addr.broadcastAddress(), is(BROADCAST_ADDRESS));
assertThat(addr.peerAddress(), nullValue());
}
/**
......@@ -115,8 +126,11 @@ public class InterfaceIpAddressTest {
InterfaceIpAddress addr =
new InterfaceIpAddress(IP_ADDRESS, SUBNET_ADDRESS, null,
PEER_ADDRESS);
assertThat(addr.toString(),
is("InterfaceIpAddress{ipAddress=1.2.3.4, subnetAddress=1.2.0.0/16, peerAddress=5.6.7.8}"));
assertThat(addr.ipAddress(), is(IP_ADDRESS));
assertThat(addr.subnetAddress(), is(SUBNET_ADDRESS));
assertThat(addr.broadcastAddress(), nullValue());
assertThat(addr.peerAddress(), is(PEER_ADDRESS));
}
/**
......@@ -229,28 +243,4 @@ public class InterfaceIpAddressTest {
assertThat(addr3, is(not(addr4)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
InterfaceIpAddress addr;
// Regular interface address with default broadcast address
addr = new InterfaceIpAddress(IP_ADDRESS, SUBNET_ADDRESS);
assertThat(addr.toString(),
is("InterfaceIpAddress{ipAddress=1.2.3.4, subnetAddress=1.2.0.0/16}"));
// Interface address with non-default broadcast address
addr = new InterfaceIpAddress(IP_ADDRESS, SUBNET_ADDRESS,
BROADCAST_ADDRESS);
assertThat(addr.toString(),
is("InterfaceIpAddress{ipAddress=1.2.3.4, subnetAddress=1.2.0.0/16, broadcastAddress=1.2.0.255}"));
// Point-to-point address with peer IP address
addr = new InterfaceIpAddress(IP_ADDRESS, SUBNET_ADDRESS, null,
PEER_ADDRESS);
assertThat(addr.toString(),
is("InterfaceIpAddress{ipAddress=1.2.3.4, subnetAddress=1.2.0.0/16, peerAddress=5.6.7.8}"));
}
}
......
......@@ -151,6 +151,33 @@ public abstract class Config<S> {
}
/**
* Gets the specified property as an integer.
*
* @param name property name
* @param defaultValue default value if property not set
* @return property value or default value
*/
protected int get(String name, int defaultValue) {
return node.path(name).asInt(defaultValue);
}
/**
* Sets the specified property as an integer 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, Integer value) {
if (value != null) {
node.put(name, value.intValue());
} else {
node.remove(name);
}
return this;
}
/**
* Gets the specified property as a long.
*
* @param name property name
......@@ -231,4 +258,5 @@ public abstract class Config<S> {
}
return this;
}
}
......
......@@ -58,7 +58,7 @@ public interface NetworkConfigService {
* @param configKey subject class name
* @return subject class
*/
Class<? extends Config> getConfigClass(String configKey);
Class<? extends Config> getConfigClass(String subjectKey, String configKey);
/**
* Returns the set of subjects for which some configuration is available.
......
/*
* 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 com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.Sets;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.incubator.net.config.Config;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.host.InterfaceIpAddress;
import java.util.Iterator;
import java.util.Set;
/**
* Basic configuration for a port on a device.
*/
public class BasicPortConfig extends Config<ConnectPoint> {
public static final String IPS = "ips";
public static final String MAC = "mac";
public static final String VLAN = "vlan";
/**
* Returns the set of IP addresses assigned to the port.
*
* @return set ip IP addresses
*/
public Set<InterfaceIpAddress> ips() {
Set<InterfaceIpAddress> ips = Sets.newHashSet();
JsonNode ipsNode = node.get(IPS);
ipsNode.forEach(jsonNode -> ips.add(InterfaceIpAddress.valueOf(jsonNode.asText())));
return ips;
}
/**
* Adds an IP address to configuration of the port.
*
* @param ip ip address to add
* @return this
*/
public BasicPortConfig addIp(InterfaceIpAddress ip) {
ArrayNode ipsNode = (ArrayNode) node.get(IPS);
if (ipsNode == null) {
ipsNode = node.putArray(IPS);
}
// Check if the value is already there
if (ipsNode.findValue(ip.toString()) != null) {
ipsNode.add(ip.toString());
}
return this;
}
/**
* Removes an IP address from the configuration of the port.
*
* @param ip ip address to remove
* @return this
*/
public BasicPortConfig removeIp(InterfaceIpAddress ip) {
ArrayNode ipsNode = (ArrayNode) node.get(IPS);
if (ipsNode != null) {
if (ipsNode.size() == 1) {
node.remove(IPS);
} else {
Iterator<JsonNode> it = ipsNode.iterator();
while (it.hasNext()) {
if (it.next().asText().equals(ip.toString())) {
it.remove();
break;
}
}
}
}
return this;
}
/**
* Clear all IP addresses from the configuration.
*
* @return this
*/
public BasicPortConfig clearIps() {
node.remove(IPS);
return this;
}
/**
* Returns the MAC address configured on the port.
*
* @return MAC address
*/
public MacAddress mac() {
JsonNode macNode = node.get(MAC);
if (macNode == null) {
return null;
}
return MacAddress.valueOf(macNode.asText());
}
/**
* Sets the MAC address configured on the port.
*
* @param mac MAC address
* @return this
*/
public BasicPortConfig mac(MacAddress mac) {
String macString = (mac == null) ? null : mac.toString();
return (BasicPortConfig) setOrClear(MAC, macString);
}
/**
* Returns the VLAN configured on the port.
*
* @return VLAN ID
*/
public VlanId vlan() {
JsonNode macNode = node.get(VLAN);
if (macNode == null) {
return null;
}
return VlanId.vlanId(Short.parseShort(macNode.asText()));
}
/**
* Sets the VLAN configured on the port.
*
* @param vlan VLAN ID
* @return this
*/
public BasicPortConfig vlan(VlanId vlan) {
Integer vlanId = (vlan == null) ? null : Integer.valueOf(vlan.toShort());
return (BasicPortConfig) setOrClear(VLAN, vlanId);
}
}
......@@ -50,6 +50,14 @@ public final class SubjectFactories {
}
};
public static final SubjectFactory<ConnectPoint> CONNECT_POINT_SUBJECT_FACTORY =
new SubjectFactory<ConnectPoint>(ConnectPoint.class, "ports") {
@Override
public ConnectPoint createSubject(String key) {
return ConnectPoint.deviceConnectPoint(key);
}
};
public static final SubjectFactory<HostId> HOST_SUBJECT_FACTORY =
new SubjectFactory<HostId>(HostId.class, "hosts") {
@Override
......
......@@ -26,6 +26,8 @@ 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.incubator.net.config.basics.BasicPortConfig;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
import org.onosproject.net.LinkKey;
......@@ -34,7 +36,10 @@ import org.slf4j.LoggerFactory;
import java.util.Set;
import static org.onosproject.incubator.net.config.basics.SubjectFactories.*;
import static org.onosproject.incubator.net.config.basics.SubjectFactories.CONNECT_POINT_SUBJECT_FACTORY;
import static org.onosproject.incubator.net.config.basics.SubjectFactories.DEVICE_SUBJECT_FACTORY;
import static org.onosproject.incubator.net.config.basics.SubjectFactories.HOST_SUBJECT_FACTORY;
import static org.onosproject.incubator.net.config.basics.SubjectFactories.LINK_SUBJECT_FACTORY;
/**
* Component for registration of builtin basic network configurations.
......@@ -53,6 +58,14 @@ public class BasicNetworkConfigs {
return new BasicDeviceConfig();
}
},
new ConfigFactory<ConnectPoint, BasicPortConfig>(CONNECT_POINT_SUBJECT_FACTORY,
BasicPortConfig.class,
"basic") {
@Override
public BasicPortConfig createConfig() {
return new BasicPortConfig();
}
},
new ConfigFactory<HostId, BasicHostConfig>(HOST_SUBJECT_FACTORY,
BasicHostConfig.class,
"basic") {
......
......@@ -86,13 +86,15 @@ public class NetworkConfigLoader {
SubjectFactory subjectFactory) {
classNode.fieldNames().forEachRemaining(s ->
consumeSubjectJson(service, (ObjectNode) classNode.path(s),
subjectFactory.createSubject(s)));
subjectFactory.createSubject(s),
subjectFactory.subjectKey()));
}
private static void consumeSubjectJson(NetworkConfigService service,
ObjectNode subjectNode, Object subject) {
ObjectNode subjectNode, Object subject, String subjectKey) {
subjectNode.fieldNames().forEachRemaining(c ->
service.applyConfig(subject, service.getConfigClass(c),
service.applyConfig(subject,
service.getConfigClass(subjectKey, c),
(ObjectNode) subjectNode.path(c)));
}
......
......@@ -64,7 +64,7 @@ public class NetworkConfigManager implements NetworkConfigRegistry, NetworkConfi
// 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 Map<ConfigIdentifier, Class<? extends Config>> configClasses = Maps.newConcurrentMap();
private final ListenerRegistry<NetworkConfigEvent, NetworkConfigListener>
listenerRegistry = new ListenerRegistry<>();
......@@ -98,7 +98,7 @@ public class NetworkConfigManager implements NetworkConfigRegistry, NetworkConfi
public void registerConfigFactory(ConfigFactory configFactory) {
checkNotNull(configFactory, NULL_FACTORY_MSG);
factories.put(key(configFactory), configFactory);
configClasses.put(configFactory.configKey(), configFactory.configClass());
configClasses.put(identifier(configFactory), configFactory.configClass());
SubjectFactory subjectFactory = configFactory.subjectFactory();
subjectClasses.putIfAbsent(subjectFactory.subjectKey(), subjectFactory);
......@@ -160,8 +160,8 @@ public class NetworkConfigManager implements NetworkConfigRegistry, NetworkConfi
}
@Override
public Class<? extends Config> getConfigClass(String configKey) {
return configClasses.get(configKey);
public Class<? extends Config> getConfigClass(String subjectKey, String configKey) {
return configClasses.get(new ConfigIdentifier(subjectKey, configKey));
}
@Override
......@@ -270,4 +270,35 @@ public class NetworkConfigManager implements NetworkConfigRegistry, NetworkConfi
}
}
private static ConfigIdentifier identifier(ConfigFactory factory) {
return new ConfigIdentifier(factory.subjectFactory().subjectKey(), factory.configKey());
}
private static final class ConfigIdentifier {
final String subjectKey;
final String configKey;
private ConfigIdentifier(String subjectKey, String configKey) {
this.subjectKey = subjectKey;
this.configKey = configKey;
}
@Override
public int hashCode() {
return Objects.hash(subjectKey, configKey);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ConfigIdentifier) {
final ConfigIdentifier other = (ConfigIdentifier) obj;
return Objects.equals(this.subjectKey, other.subjectKey)
&& Objects.equals(this.configKey, other.configKey);
}
return false;
}
}
}
......
......@@ -16,6 +16,7 @@
package org.onosproject.incubator.store.config.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.DoubleNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
......@@ -81,7 +82,7 @@ public class DistributedNetworkConfigStore
public void activate() {
KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(ConfigKey.class, ObjectNode.class,
.register(ConfigKey.class, ObjectNode.class, ArrayNode.class,
JsonNodeFactory.class, LinkedHashMap.class,
TextNode.class, BooleanNode.class,
LongNode.class, DoubleNode.class, ShortNode.class);
......
......@@ -109,7 +109,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
@PathParam("configKey") String configKey) {
NetworkConfigService service = get(NetworkConfigService.class);
return ok(service.getConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
service.getConfigClass(configKey)).node()).build();
service.getConfigClass(subjectKey, configKey)).node()).build();
}
@SuppressWarnings("unchecked")
......@@ -183,7 +183,8 @@ public class NetworkConfigWebResource extends AbstractWebResource {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
consumeSubjectJson(service, root,
service.getSubjectFactory(subjectKey).createSubject(subject));
service.getSubjectFactory(subjectKey).createSubject(subject),
subjectKey);
return Response.ok().build();
}
......@@ -209,7 +210,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
service.applyConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
service.getConfigClass(configKey), root);
service.getConfigClass(subjectKey, configKey), root);
return Response.ok().build();
}
......@@ -217,13 +218,15 @@ public class NetworkConfigWebResource extends AbstractWebResource {
SubjectFactory subjectFactory) {
classNode.fieldNames().forEachRemaining(s ->
consumeSubjectJson(service, (ObjectNode) classNode.path(s),
subjectFactory.createSubject(s)));
subjectFactory.createSubject(s),
subjectFactory.subjectKey()));
}
private void consumeSubjectJson(NetworkConfigService service,
ObjectNode subjectNode, Object subject) {
ObjectNode subjectNode, Object subject,
String subjectKey) {
subjectNode.fieldNames().forEachRemaining(c ->
service.applyConfig(subject, service.getConfigClass(c),
service.applyConfig(subject, service.getConfigClass(subjectKey, c),
(ObjectNode) subjectNode.path(c)));
}
......@@ -265,7 +268,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
@PathParam("configKey") String configKey) {
NetworkConfigService service = get(NetworkConfigService.class);
service.removeConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
service.getConfigClass(configKey));
service.getConfigClass(subjectKey, configKey));
return Response.ok().build();
}
......