CORD-176, CORD-431 Switch to new VTN API
- Implemented VTN store and manager to keep network states
- Implemented the new VTN API for service network and port
Change-Id: Id8f5d0e609fa3deba782ff2265fc0d3175cbcb4c
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java
new file mode 100644
index 0000000..d645b8f
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java
@@ -0,0 +1,458 @@
+/*
+ * Copyright 2016-present 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.opencord.cordvtn.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.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.Tools;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.host.HostService;
+import org.opencord.cordvtn.api.CordVtnAdminService;
+import org.opencord.cordvtn.api.CordVtnService;
+import org.opencord.cordvtn.api.CordVtnStore;
+import org.opencord.cordvtn.api.CordVtnStoreDelegate;
+import org.opencord.cordvtn.api.Instance;
+import org.opencord.cordvtn.api.NetworkId;
+import org.opencord.cordvtn.api.PortId;
+import org.opencord.cordvtn.api.ServiceNetwork;
+import org.opencord.cordvtn.api.ServicePort;
+import org.opencord.cordvtn.api.SubnetId;
+import org.opencord.cordvtn.api.VtnNetwork;
+import org.opencord.cordvtn.api.VtnNetworkEvent;
+import org.opencord.cordvtn.api.VtnNetworkListener;
+import org.opencord.cordvtn.api.VtnPort;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.opencord.cordvtn.api.Instance.NETWORK_ID;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfacing VTN networks.
+ */
+@Component(immediate = true)
+@Service
+public class CordVtnManager extends ListenerRegistry<VtnNetworkEvent, VtnNetworkListener>
+ implements CordVtnAdminService, CordVtnService {
+
+ protected final Logger log = getLogger(getClass());
+
+ private static final String MSG_SERVICE_NET = "VTN network %s %s";
+ private static final String MSG_SERVICE_PORT = "VTN port %s %s";
+ private static final String MSG_NET = "Network %s %s";
+ private static final String MSG_PORT = "Port %s %s";
+ private static final String MSG_SUBNET = "Subnet %s %s";
+
+ private static final String CREATED = "created";
+ private static final String UPDATED = "updated";
+ private static final String REMOVED = "removed";
+
+ private static final String ERR_NULL_SERVICE_PORT = "Service port cannot be null";
+ private static final String ERR_NULL_SERVICE_NET = "Service network cannot be null";
+ private static final String ERR_NULL_PORT = "Port cannot be null";
+ private static final String ERR_NULL_NET = "Network cannot be null";
+ private static final String ERR_NULL_SUBNET = "Subnet cannot be null";
+ private static final String ERR_NULL_PORT_ID = "Port ID cannot be null";
+ private static final String ERR_NULL_NET_ID = "Network ID cannot be null";
+ private static final String ERR_NULL_SUBNET_ID = "Subnet ID cannot be null";
+
+ private static final String ERR_SYNC = "VTN store is out of sync: ";
+ private static final String ERR_NOT_FOUND = " does not exist";
+ private static final String ERR_IN_USE_INSTANCE = "There are instances still in use on the network %s";
+ private static final String ERR_IN_USE_NETWORK = "There are subscribers still in use on the network %s";
+ private static final String ERR_IN_USE_PORT = "There are ports still in use on the network %s";
+ private static final String ERR_SUBNET_DUPLICATE = "Subnet already exists for network %s";
+
+ private static final String PORT = "port ";
+ private static final String NETWORK = "network ";
+ private static final String SUBNET = "subnet for ";
+ private static final String PROVIDER = "provider ";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CordVtnStore store;
+
+ private CordVtnStoreDelegate delegate = new InternalCordVtnStoreDelegate();
+
+ @Activate
+ protected void activate() {
+ store.setDelegate(delegate);
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ store.unsetDelegate(delegate);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void createVtnNetwork(ServiceNetwork serviceNet) {
+ checkNotNull(serviceNet, ERR_NULL_SERVICE_NET);
+ synchronized (this) {
+ Network network = store.getNetwork(serviceNet.id());
+ if (network == null) {
+ final String error = ERR_SYNC + NETWORK + serviceNet.id() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+
+ Subnet subnet = getSubnet(serviceNet.id());
+ if (subnet == null) {
+ final String error = ERR_SYNC + SUBNET + serviceNet.id() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+
+ // TODO check VTN network instead of network
+ serviceNet.providers().stream().forEach(provider -> {
+ if (store.getNetwork(provider.id()) == null) {
+ final String error = ERR_SYNC + PROVIDER + provider.id() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+ });
+
+ store.createVtnNetwork(VtnNetwork.of(network, subnet, serviceNet));
+ log.info(String.format(MSG_SERVICE_NET, CREATED, serviceNet.id()));
+ }
+ }
+
+ @Override
+ public void updateVtnNetwork(ServiceNetwork serviceNet) {
+ checkNotNull(serviceNet, ERR_NULL_SERVICE_NET);
+ synchronized (this) {
+ VtnNetwork existing = store.getVtnNetwork(serviceNet.id());
+ if (existing == null) {
+ final String error = ERR_SYNC + NETWORK + serviceNet.id() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+ // only providers update is allowed
+ VtnNetwork updated = VtnNetwork.builder(existing)
+ .providers(serviceNet.providers())
+ .build();
+ store.updateVtnNetwork(updated);
+ log.info(String.format(MSG_SERVICE_NET, UPDATED, serviceNet.id()));
+ }
+ }
+
+ @Override
+ public void removeVtnNetwork(NetworkId netId) {
+ checkNotNull(netId, ERR_NULL_NET_ID);
+ // TODO check if the network still exists?
+ store.removeVtnNetwork(netId);
+ log.info(String.format(MSG_SERVICE_NET, REMOVED, netId));
+ }
+
+ @Override
+ public void createVtnPort(ServicePort servicePort) {
+ checkNotNull(servicePort, ERR_NULL_SERVICE_PORT);
+ synchronized (this) {
+ Port port = store.getPort(servicePort.id());
+ if (port == null) {
+ final String error = ERR_SYNC + PORT + servicePort.id() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+ store.createVtnPort(VtnPort.of(port, servicePort));
+ log.info(String.format(MSG_SERVICE_PORT, CREATED, servicePort.id()));
+ }
+ }
+
+ @Override
+ public void updateVtnPort(ServicePort servicePort) {
+ checkNotNull(servicePort, ERR_NULL_SERVICE_PORT);
+ synchronized (this) {
+ VtnPort vtnPort = store.getVtnPort(servicePort.id());
+ if (vtnPort == null) {
+ final String error = ERR_SYNC + PORT + servicePort.id() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+ store.updateVtnPort(VtnPort.of(vtnPort, servicePort));
+ log.info(String.format(MSG_SERVICE_PORT, UPDATED, servicePort.id()));
+ }
+ }
+
+ @Override
+ public void removeVtnPort(PortId portId) {
+ checkNotNull(portId, ERR_NULL_PORT_ID);
+ store.removeVtnPort(portId);
+ log.info(String.format(MSG_SERVICE_PORT, REMOVED, portId));
+ }
+
+ @Override
+ public void createNetwork(Network network) {
+ checkNotNull(network, ERR_NULL_NET);
+ store.createNetwork(network);
+ log.info(String.format(MSG_NET, CREATED, network.getId()));
+ }
+
+ @Override
+ public void updateNetwork(Network network) {
+ checkNotNull(network, ERR_NULL_NET);
+ store.updateNetwork(network);
+ log.info(String.format(MSG_NET, UPDATED, network.getId()));
+ }
+
+ @Override
+ public void removeNetwork(NetworkId netId) {
+ checkNotNull(netId, ERR_NULL_NET_ID);
+ // FIXME Neutron removes network anyway even if there's an exception here
+ store.removeNetwork(netId);
+ log.info(String.format(MSG_NET, REMOVED, netId));
+ }
+
+ @Override
+ public void createPort(Port port) {
+ checkNotNull(port, ERR_NULL_PORT);
+ synchronized (this) {
+ if (store.getNetwork(NetworkId.of(port.getNetworkId())) == null) {
+ final String error = ERR_SYNC + port.getNetworkId() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+ store.createPort(port);
+ log.info(String.format(MSG_PORT, CREATED, port.getId()));
+ }
+ }
+
+ @Override
+ public void updatePort(Port port) {
+ checkNotNull(port, ERR_NULL_PORT);
+ synchronized (this) {
+ if (store.getNetwork(NetworkId.of(port.getNetworkId())) == null) {
+ final String error = ERR_SYNC + port.getNetworkId() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+ store.updatePort(port);
+ log.info(String.format(MSG_PORT, UPDATED, port.getId()));
+ }
+ }
+
+ @Override
+ public void removePort(PortId portId) {
+ checkNotNull(portId, ERR_NULL_PORT_ID);
+ synchronized (this) {
+ if (getInstance(portId) != null) {
+ final String error = String.format(ERR_IN_USE_PORT, portId);
+ throw new IllegalStateException(error);
+ }
+ removeVtnPort(portId);
+ store.removePort(portId);
+ log.info(String.format(MSG_PORT, REMOVED, portId));
+ }
+ }
+
+ @Override
+ public void createSubnet(Subnet subnet) {
+ checkNotNull(subnet, ERR_NULL_SUBNET);
+ synchronized (this) {
+ if (store.getNetwork(NetworkId.of(subnet.getNetworkId())) == null) {
+ final String error = ERR_SYNC + subnet.getNetworkId() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+
+ if (getSubnet(NetworkId.of(subnet.getNetworkId())) != null) {
+ // CORD does not allow multiple subnets for a network
+ final String error = String.format(ERR_SUBNET_DUPLICATE, subnet.getNetworkId());
+ throw new IllegalStateException(error);
+ }
+ store.createSubnet(subnet);
+ log.info(String.format(MSG_SUBNET, CREATED, subnet.getId()));
+ }
+ }
+
+ @Override
+ public void updateSubnet(Subnet subnet) {
+ checkNotNull(subnet, ERR_NULL_SUBNET);
+ synchronized (this) {
+ if (store.getNetwork(NetworkId.of(subnet.getNetworkId())) == null) {
+ final String error = ERR_SYNC + subnet.getNetworkId() + ERR_NOT_FOUND;
+ throw new IllegalStateException(error);
+ }
+ store.updateSubnet(subnet);
+ log.info(String.format(MSG_SUBNET, UPDATED, subnet.getId()));
+ }
+ }
+
+ @Override
+ public void removeSubnet(SubnetId subnetId) {
+ checkNotNull(subnetId, ERR_NULL_SUBNET_ID);
+ // FIXME Neutron removes network anyway even if there's an exception here
+ synchronized (this) {
+ removeVtnNetwork(NetworkId.of(store.getSubnet(subnetId).getNetworkId()));
+ store.removeSubnet(subnetId);
+ log.info(String.format(MSG_SUBNET, REMOVED, subnetId));
+ }
+ }
+
+ @Override
+ public VtnNetwork getVtnNetwork(NetworkId netId) {
+ checkNotNull(netId, ERR_NULL_NET_ID);
+ return store.getVtnNetwork(netId);
+ }
+
+ @Override
+ public VtnNetwork getVtnNetworkOrDefault(NetworkId netId) {
+ checkNotNull(netId, ERR_NULL_NET_ID);
+
+ // return default VTN network if the network and subnet exist
+ VtnNetwork vtnNet = store.getVtnNetwork(netId);
+ return vtnNet == null ? getDefaultVtnNetwork(netId) : vtnNet;
+ }
+
+ @Override
+ public Set<VtnNetwork> getVtnNetworks() {
+ return store.getVtnNetworks();
+ }
+
+ @Override
+ public VtnPort getVtnPort(PortId portId) {
+ checkNotNull(portId, ERR_NULL_PORT_ID);
+ return store.getVtnPort(portId);
+ }
+
+ @Override
+ public VtnPort getVtnPortOrDefault(PortId portId) {
+ checkNotNull(portId, ERR_NULL_PORT_ID);
+
+ // return default VTN port if the port exists
+ VtnPort vtnPort = store.getVtnPort(portId);
+ return vtnPort == null ? getDefaultPort(portId) : vtnPort;
+ }
+
+ @Override
+ public VtnPort getVtnPort(String portName) {
+ Optional<Port> port = store.getPorts()
+ .stream()
+ .filter(p -> p.getId().contains(portName.substring(3)))
+ .findFirst();
+ if (!port.isPresent()) {
+ return null;
+ }
+ return getVtnPortOrDefault(PortId.of(port.get().getId()));
+ }
+
+ @Override
+ public Set<VtnPort> getVtnPorts() {
+ return store.getVtnPorts();
+ }
+
+ @Override
+ public Network getNetwork(NetworkId netId) {
+ checkNotNull(netId, ERR_NULL_NET_ID);
+ return store.getNetwork(netId);
+ }
+
+ @Override
+ public Set<Network> getNetworks() {
+ return store.getNetworks();
+ }
+
+ @Override
+ public Port getPort(PortId portId) {
+ checkNotNull(portId, ERR_NULL_PORT_ID);
+ return store.getPort(portId);
+ }
+
+ @Override
+ public Set<Port> getPorts() {
+ return store.getPorts();
+ }
+
+ @Override
+ public Subnet getSubnet(SubnetId subnetId) {
+ checkNotNull(subnetId, ERR_NULL_SUBNET_ID);
+ return store.getSubnet(subnetId);
+ }
+
+ @Override
+ public Set<Subnet> getSubnets() {
+ return store.getSubnets();
+ }
+
+ @Override
+ public Instance getInstance(PortId portId) {
+ VtnPort vtnPort = getVtnPortOrDefault(portId);
+ if (vtnPort == null) {
+ final String error = "Failed to build VTN port for " + portId.id();
+ throw new IllegalStateException(error);
+ }
+
+ Host host = hostService.getHost(HostId.hostId(vtnPort.mac()));
+ if (host == null) {
+ return null;
+ }
+ return Instance.of(host);
+ }
+
+ @Override
+ public Set<Instance> getInstances(NetworkId netId) {
+ return Tools.stream(hostService.getHosts())
+ .filter(host -> Objects.equals(
+ host.annotations().value(NETWORK_ID),
+ netId.id()))
+ .map(Instance::of)
+ .collect(Collectors.toSet());
+ }
+
+ private VtnNetwork getDefaultVtnNetwork(NetworkId netId) {
+ Network network = getNetwork(netId);
+ Subnet subnet = getSubnet(netId);
+ if (network == null || subnet == null) {
+ return null;
+ }
+ return VtnNetwork.of(network, subnet, null);
+ }
+
+ private VtnPort getDefaultPort(PortId portId) {
+ Port port = getPort(portId);
+ if (port == null) {
+ return null;
+ }
+ return VtnPort.of(port, null);
+ }
+
+ private Subnet getSubnet(NetworkId netId) {
+ // TODO fix networking-onos to send Network UPDATE when subnet created
+ Optional<Subnet> subnet = getSubnets().stream()
+ .filter(s -> Objects.equals(s.getNetworkId(), netId.id()))
+ .findFirst();
+ return subnet.orElse(null);
+ }
+
+ private class InternalCordVtnStoreDelegate implements CordVtnStoreDelegate {
+
+ @Override
+ public void notify(VtnNetworkEvent event) {
+ if (event != null) {
+ log.trace("send service network event {}", event);
+ process(event);
+ }
+ }
+ }
+}