CORD-628 Refactored VTN service network and port API
- Removed direct use of Neutron data model and Neutron API
- Extended service network and service port API to have all network
information required for VTN
- Removed unnecessary dependency manager and store
- Removed network state sync method with Neutron and XOS
- Removed Neutron and XOS access information from the network config
- Re-organized API packages
Change-Id: I18f49ec733309315f683dfb2e6be6526056118f1
diff --git a/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java b/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java
index 84226b9..331bffe 100644
--- a/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java
+++ b/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java
@@ -16,25 +16,28 @@
package org.opencord.cordvtn.codec;
import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.Sets;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
-import org.opencord.cordvtn.api.dependency.Dependency;
import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.ProviderNetwork;
+import org.opencord.cordvtn.api.net.SegmentId;
import org.opencord.cordvtn.api.net.ServiceNetwork;
-import org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType;
+import org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType;
+import org.opencord.cordvtn.impl.DefaultServiceNetwork;
-import java.util.Set;
+import java.util.Map;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
-import static org.opencord.cordvtn.api.dependency.Dependency.Type.BIDIRECTIONAL;
-import static org.opencord.cordvtn.api.dependency.Dependency.Type.UNIDIRECTIONAL;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.valueOf;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType.BIDIRECTIONAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType.UNIDIRECTIONAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.valueOf;
/**
* Service network JSON codec.
@@ -42,53 +45,176 @@
public final class ServiceNetworkCodec extends JsonCodec<ServiceNetwork> {
private static final String ID = "id";
+ private static final String NAME = "name";
private static final String TYPE = "type";
+ private static final String SEGMENT_ID = "segment_id";
+ private static final String SUBNET = "subnet";
+ private static final String SERVICE_IP = "service_ip";
+ @Deprecated
private static final String PROVIDER_NETWORKS = "providerNetworks";
- private static final String BIDIRECT = "bidirectional";
+ private static final String PROVIDERS = "providers";
+ private static final String DEP_TYPE = "bidirectional";
private static final String ERR_JSON = "Invalid ServiceNetwork received";
- private static final String ERR_ID = ": network ID cannot be null";
- private static final String ERR_TYPE = ": type cannot be null";
+ private static final String ERR_ID = "Service network ID cannot be null";
@Override
public ObjectNode encode(ServiceNetwork snet, CodecContext context) {
- ObjectNode result = context.mapper().createObjectNode()
- .put(ID, snet.id().id())
- .put(TYPE, snet.type().name().toLowerCase());
-
+ ObjectNode result = context.mapper().createObjectNode().put(ID, snet.id().id());
+ if (!Strings.isNullOrEmpty(snet.name())) {
+ result.put(NAME, snet.name());
+ }
+ if (snet.type() != null) {
+ result.put(TYPE, snet.type().name());
+ }
+ if (snet.segmentId() != null) {
+ result.put(SEGMENT_ID, snet.segmentId().id());
+ }
+ if (snet.subnet() != null) {
+ result.put(SUBNET, snet.subnet().toString());
+ }
+ if (snet.serviceIp() != null) {
+ result.put(SERVICE_IP, snet.serviceIp().toString());
+ }
ArrayNode providers = context.mapper().createArrayNode();
- snet.providers().forEach(provider -> {
+ snet.providers().entrySet().forEach(provider -> {
ObjectNode providerJson = context.mapper().createObjectNode()
- .put(ID, provider.id().id())
- .put(BIDIRECT, provider.type() == BIDIRECTIONAL ? TRUE : FALSE);
+ .put(ID, provider.getKey().id())
+ .put(DEP_TYPE, provider.getValue() == BIDIRECTIONAL ? TRUE : FALSE);
providers.add(providerJson);
});
-
- result.set(PROVIDER_NETWORKS, providers);
+ result.set(PROVIDERS, providers);
return result;
}
@Override
public ServiceNetwork decode(ObjectNode json, CodecContext context) {
- checkArgument(json != null && json.isObject(), ERR_JSON);
- checkArgument(json.get(ID) != null &&
- json.get(ID) != NullNode.getInstance(),
- ERR_JSON + ERR_ID);
- checkArgument(json.get(TYPE) != null &&
- json.get(TYPE) != NullNode.getInstance(),
- ERR_JSON + ERR_TYPE);
+ validateJson(json);
+ ServiceNetwork.Builder snetBuilder = DefaultServiceNetwork.builder()
+ .id(NetworkId.of(json.get(ID).asText()));
- NetworkId netId = NetworkId.of(json.get(ID).asText());
- ServiceNetworkType netType = valueOf(json.get(TYPE).asText().toUpperCase());
- Set<ProviderNetwork> providers = Sets.newHashSet();
+ // TODO remove existing values when explicit null received
+ if (json.get(NAME) != null && !json.get(NAME).isNull()) {
+ snetBuilder.name(json.get(NAME).asText());
+ }
+ if (json.get(TYPE) != null && !json.get(TYPE).isNull()) {
+ snetBuilder.type(valueOf(json.get(TYPE).asText().toUpperCase()));
+ }
+ if (json.get(SEGMENT_ID) != null && !json.get(SEGMENT_ID).isNull()) {
+ snetBuilder.segmentId(SegmentId.of(json.get(SEGMENT_ID).asLong()));
+ }
+ if (json.get(SUBNET) != null && !json.get(SUBNET).isNull()) {
+ snetBuilder.subnet(IpPrefix.valueOf(json.get(SUBNET).asText()));
+ }
+ if (json.get(SERVICE_IP) != null && !json.get(SERVICE_IP).isNull()) {
+ snetBuilder.serviceIp(IpAddress.valueOf(json.get(SERVICE_IP).asText()));
+ }
+ if (json.get(PROVIDERS) != null) {
+ if (json.get(PROVIDERS).isNull()) {
+ snetBuilder.providers(ImmutableMap.of());
+ } else {
+ Map<NetworkId, DependencyType> providers = Maps.newHashMap();
+ json.get(PROVIDERS).forEach(provider -> {
+ DependencyType type = provider.get(DEP_TYPE).asBoolean() ?
+ BIDIRECTIONAL : UNIDIRECTIONAL;
+ providers.put(NetworkId.of(provider.get(ID).asText()), type);
+ });
+ snetBuilder.providers(providers);
+ }
+ }
if (json.get(PROVIDER_NETWORKS) != null) {
- json.get(PROVIDER_NETWORKS).forEach(provider -> {
- NetworkId providerId = NetworkId.of(provider.get(ID).asText());
- Dependency.Type type = provider.get(BIDIRECT).asBoolean() ?
- BIDIRECTIONAL : UNIDIRECTIONAL;
- providers.add(ProviderNetwork.of(providerId, type));
+ if (json.get(PROVIDER_NETWORKS).isNull()) {
+ snetBuilder.providers(ImmutableMap.of());
+ } else {
+ Map<NetworkId, DependencyType> providers = Maps.newHashMap();
+ json.get(PROVIDER_NETWORKS).forEach(provider -> {
+ DependencyType type = provider.get(DEP_TYPE).asBoolean() ?
+ BIDIRECTIONAL : UNIDIRECTIONAL;
+ providers.put(NetworkId.of(provider.get(ID).asText()), type);
+ });
+ snetBuilder.providers(providers);
+ }
+ }
+ return snetBuilder.build();
+ }
+
+ private void validateJson(ObjectNode json) {
+ checkArgument(json != null && json.isObject(), ERR_JSON);
+ checkArgument(json.get(ID) != null && !json.get(ID).isNull(), ERR_ID);
+ checkArgument(!Strings.isNullOrEmpty(json.get(ID).asText()), ERR_ID);
+
+ // allow explicit null for removing the existing value
+ if (json.get(NAME) != null && !json.get(NAME).isNull()) {
+ if (Strings.isNullOrEmpty(json.get(NAME).asText())) {
+ final String error = "Null or empty ServiceNetwork name received";
+ throw new IllegalArgumentException(error);
+ }
+ }
+
+ if (json.get(TYPE) != null && !json.get(TYPE).isNull()) {
+ try {
+ valueOf(json.get(TYPE).asText().toUpperCase());
+ } catch (IllegalArgumentException e) {
+ final String error = "Invalid ServiceNetwork type received: ";
+ throw new IllegalArgumentException(error + json.get(TYPE).asText());
+ }
+ }
+
+ if (json.get(SEGMENT_ID) != null && !json.get(SEGMENT_ID).isNull()) {
+ if (json.get(SEGMENT_ID).asLong() == 0) {
+ final String error = "Invalid ServiecNetwork segment ID received: ";
+ throw new IllegalArgumentException(error + json.get(SEGMENT_ID).asText());
+ }
+ }
+
+ if (json.get(SUBNET) != null && !json.get(SUBNET).isNull()) {
+ try {
+ IpPrefix.valueOf(json.get(SUBNET).asText());
+ } catch (IllegalArgumentException e) {
+ final String error = "Invalid ServiceNetwork subnet received: ";
+ throw new IllegalArgumentException(error + json.get(SUBNET).asText());
+ }
+ }
+
+ if (json.get(SERVICE_IP) != null && !json.get(SERVICE_IP).isNull()) {
+ try {
+ IpAddress.valueOf(json.get(SERVICE_IP).asText());
+ } catch (IllegalArgumentException e) {
+ final String error = "Invalid ServiceNetwork service IP address received: ";
+ throw new IllegalArgumentException(error + json.get(SERVICE_IP).asText());
+ }
+ }
+
+ if (json.get(PROVIDERS) != null && !json.get(PROVIDERS).isNull()) {
+ json.get(PROVIDERS).forEach(provider -> {
+ if (provider.get(ID) == null || provider.get(ID).isNull() ||
+ Strings.isNullOrEmpty(provider.get(ID).asText())) {
+ final String error = "Null or empty provider network ID received";
+ throw new IllegalArgumentException(error);
+ }
+
+ if (provider.get(DEP_TYPE) == null || provider.get(DEP_TYPE).isNull()
+ || !provider.get(DEP_TYPE).isBoolean()) {
+ final String error = "Non-boolean bidirectional received";
+ throw new IllegalArgumentException(error);
+ }
});
}
- return new ServiceNetwork(netId, netType, providers);
+
+ if (json.get(PROVIDER_NETWORKS) != null && !json.get(PROVIDER_NETWORKS).isNull()) {
+ json.get(PROVIDER_NETWORKS).forEach(provider -> {
+ if (provider.get(ID) == null || provider.get(ID).isNull() ||
+ Strings.isNullOrEmpty(provider.get(ID).asText())) {
+ final String error = "Null or empty provider network ID received";
+ throw new IllegalArgumentException(error);
+ }
+
+ if (provider.get(DEP_TYPE) == null || provider.get(DEP_TYPE).isNull()
+ || !provider.get(DEP_TYPE).isBoolean()) {
+ final String error = "Non-boolean bidirectional received";
+ throw new IllegalArgumentException(error);
+ }
+ });
+ }
}
}
diff --git a/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java b/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java
index bb4a82b..5292604 100644
--- a/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java
+++ b/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java
@@ -16,8 +16,9 @@
package org.opencord.cordvtn.codec;
import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
@@ -27,6 +28,7 @@
import org.opencord.cordvtn.api.net.AddressPair;
import org.opencord.cordvtn.api.net.PortId;
import org.opencord.cordvtn.api.net.ServicePort;
+import org.opencord.cordvtn.impl.DefaultServicePort;
import java.util.Set;
@@ -38,22 +40,36 @@
public final class ServicePortCodec extends JsonCodec<ServicePort> {
private static final String ID = "id";
+ private static final String NAME = "name";
+ private static final String NETWORK_ID = "network_id";
+ private static final String MAC_ADDRESS = "mac_address";
+ private static final String IP_ADDRESS = "ip_address";
private static final String VLAN_ID = "vlan_id";
private static final String FLOATING_ADDRESS_PAIRS = "floating_address_pairs";
- private static final String IP_ADDRESS = "ip_address";
- private static final String MAC_ADDRESS = "mac_address";
private static final String ERR_JSON = "Invalid ServicePort received";
- private static final String ERR_ID = ": port ID cannot be null";
+ private static final String ERR_ID = "Service port ID cannot be null";
@Override
public ObjectNode encode(ServicePort sport, CodecContext context) {
ObjectNode result = context.mapper().createObjectNode()
.put(ID, sport.id().id());
- if (sport.vlanId().isPresent()) {
- result.put(VLAN_ID, sport.vlanId().get().id());
- }
+ if (sport.networkId() != null) {
+ result.put(NETWORK_ID, sport.networkId().id());
+ }
+ if (!Strings.isNullOrEmpty(sport.name())) {
+ result.put(NAME, sport.name());
+ }
+ if (sport.vlanId() != null) {
+ result.put(VLAN_ID, sport.vlanId().id());
+ }
+ if (sport.mac() != null) {
+ result.put(MAC_ADDRESS, sport.mac().toString());
+ }
+ if (sport.ip() != null) {
+ result.put(IP_ADDRESS, sport.ip().toString());
+ }
ArrayNode addressPairs = context.mapper().createArrayNode();
sport.addressPairs().forEach(pair -> {
ObjectNode pairJson = context.mapper().createObjectNode()
@@ -62,34 +78,107 @@
addressPairs.add(pairJson);
});
result.set(FLOATING_ADDRESS_PAIRS, addressPairs);
+
return result;
}
@Override
public ServicePort decode(ObjectNode json, CodecContext context) {
- checkArgument(json != null && json.isObject(), ERR_JSON);
- checkArgument(json.get(ID) != null &&
- json.get(ID) != NullNode.getInstance(),
- ERR_JSON + ERR_ID);
+ validateJson(json);
+ ServicePort.Builder sportBuilder = DefaultServicePort.builder()
+ .id(PortId.of(json.get(ID).asText()));
- PortId portId = PortId.of(json.get(ID).asText());
- VlanId vlanId = null;
- if (json.get(VLAN_ID) != NullNode.getInstance()) {
- try {
- vlanId = VlanId.vlanId(json.get(VLAN_ID).asText());
- } catch (Exception ignore) {
- }
+ // TODO allow removing existing value when explicit null received
+ if (json.get(NAME) != null && !json.get(NAME).isNull()) {
+ sportBuilder.name(json.get(NAME).asText());
}
-
- Set<AddressPair> addressPairs = Sets.newHashSet();
- if (json.get(FLOATING_ADDRESS_PAIRS) != null) {
+ if (json.get(MAC_ADDRESS) != null && !json.get(MAC_ADDRESS).isNull()) {
+ sportBuilder.mac(MacAddress.valueOf(json.get(MAC_ADDRESS).asText()));
+ }
+ if (json.get(IP_ADDRESS) != null && !json.get(IP_ADDRESS).isNull()) {
+ sportBuilder.ip(IpAddress.valueOf(json.get(IP_ADDRESS).asText()));
+ }
+ if (json.get(VLAN_ID) != null && !json.get(VLAN_ID).isNull()) {
+ sportBuilder.vlanId(VlanId.vlanId(json.get(VLAN_ID).asText()));
+ }
+ if (json.get(FLOATING_ADDRESS_PAIRS).isNull()) {
+ sportBuilder.addressPairs(ImmutableSet.of());
+ } else if (json.get(FLOATING_ADDRESS_PAIRS) != null) {
+ Set<AddressPair> addressPairs = Sets.newHashSet();
json.get(FLOATING_ADDRESS_PAIRS).forEach(pair -> {
AddressPair addrPair = AddressPair.of(
IpAddress.valueOf(pair.get(IP_ADDRESS).asText()),
MacAddress.valueOf(pair.get(MAC_ADDRESS).asText()));
addressPairs.add(addrPair);
});
+ sportBuilder.addressPairs(addressPairs);
}
- return new ServicePort(portId, vlanId, addressPairs);
+ return sportBuilder.build();
+ }
+
+ private void validateJson(ObjectNode json) {
+ checkArgument(json != null && json.isObject(), ERR_JSON);
+ checkArgument(json.get(ID) != null && !json.get(ID).isNull(), ERR_ID);
+
+ // allow explicit null for removing the existing value
+ if (json.get(NAME) != null && !json.get(NAME).isNull()) {
+ if (Strings.isNullOrEmpty(json.get(NAME).asText())) {
+ final String error = "Null or empty ServiceNetwork name received";
+ throw new IllegalArgumentException(error);
+ }
+ }
+
+ if (json.get(MAC_ADDRESS) != null && !json.get(MAC_ADDRESS).isNull()) {
+ try {
+ MacAddress.valueOf(json.get(MAC_ADDRESS).asText());
+ } catch (IllegalArgumentException e) {
+ final String error = "Invalid ServicePort MAC address received: ";
+ throw new IllegalArgumentException(error + json.get(MAC_ADDRESS).asText());
+ }
+ }
+
+ if (json.get(IP_ADDRESS) != null && !json.get(IP_ADDRESS).isNull()) {
+ try {
+ IpAddress.valueOf(json.get(IP_ADDRESS).asText());
+ } catch (IllegalArgumentException e) {
+ final String error = "Invalid ServicePort IP address received: ";
+ throw new IllegalArgumentException(error + json.get(IP_ADDRESS).asText());
+ }
+ }
+
+ if (json.get(VLAN_ID) != null && !json.get(VLAN_ID).isNull()) {
+ try {
+ VlanId.vlanId(json.get(VLAN_ID).asText());
+ } catch (IllegalArgumentException e) {
+ final String error = "Invalid VLAN ID is received: ";
+ throw new IllegalArgumentException(error + json.get(VLAN_ID).asText());
+ }
+ }
+
+ if (json.get(FLOATING_ADDRESS_PAIRS) != null &&
+ !json.get(FLOATING_ADDRESS_PAIRS).isNull()) {
+ json.get(FLOATING_ADDRESS_PAIRS).forEach(pair -> {
+ if (pair.get(IP_ADDRESS) == null ||
+ pair.get(IP_ADDRESS).isNull() ||
+ pair.get(MAC_ADDRESS) == null ||
+ pair.get(MAC_ADDRESS).isNull()) {
+ final String error = "Invalid floating address pair received";
+ throw new IllegalArgumentException(error);
+ }
+ try {
+ IpAddress.valueOf(pair.get(IP_ADDRESS).asText());
+ } catch (IllegalArgumentException e) {
+ final String error = "Invalid floating address pair IP: ";
+ throw new IllegalArgumentException(error + pair.get(IP_ADDRESS).asText());
+ }
+
+ try {
+ MacAddress.valueOf(pair.get(MAC_ADDRESS).asText());
+ } catch (IllegalArgumentException e) {
+ final String error = "Invalid floating address pair MAC: ";
+ throw new IllegalArgumentException(error + pair.get(MAC_ADDRESS).asText());
+ }
+ });
+ }
}
}