blob: fba15997d93ae449f3edfb2783c3b590186fe28d [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* 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.openstacknetworking.impl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.onlab.util.KryoNamespace;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
import org.onosproject.openstacknetworking.api.OpenstackNetworkStore;
import org.onosproject.openstacknetworking.api.OpenstackNetworkStoreDelegate;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.Versioned;
import org.openstack4j.model.network.IPVersionType;
import org.openstack4j.model.network.Ipv6AddressMode;
import org.openstack4j.model.network.Ipv6RaMode;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.NetworkType;
import org.openstack4j.model.network.Port;
import org.openstack4j.model.network.State;
import org.openstack4j.model.network.Subnet;
import org.openstack4j.openstack.networking.domain.NeutronAllowedAddressPair;
import org.openstack4j.openstack.networking.domain.NeutronExtraDhcpOptCreate;
import org.openstack4j.openstack.networking.domain.NeutronHostRoute;
import org.openstack4j.openstack.networking.domain.NeutronIP;
import org.openstack4j.openstack.networking.domain.NeutronNetwork;
import org.openstack4j.openstack.networking.domain.NeutronPool;
import org.openstack4j.openstack.networking.domain.NeutronPort;
import org.openstack4j.openstack.networking.domain.NeutronSubnet;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_CREATED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_REMOVED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_UPDATED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_CREATED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_REMOVED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_SECURITY_GROUP_ADDED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_SECURITY_GROUP_REMOVED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_UPDATED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_SUBNET_CREATED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_SUBNET_REMOVED;
import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_SUBNET_UPDATED;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages the inventory of OpenStack network, subnet, and port using a {@code ConsistentMap}.
*/
@Component(immediate = true, service = OpenstackNetworkStore.class)
public class DistributedOpenstackNetworkStore
extends AbstractStore<OpenstackNetworkEvent, OpenstackNetworkStoreDelegate>
implements OpenstackNetworkStore {
protected final Logger log = getLogger(getClass());
private static final String ERR_NOT_FOUND = " does not exist";
private static final String ERR_DUPLICATE = " already exists";
private static final KryoNamespace SERIALIZER_NEUTRON_L2 = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
.register(Network.class)
.register(NeutronNetwork.class)
.register(State.class)
.register(NetworkType.class)
.register(Port.class)
.register(NeutronPort.class)
.register(NeutronIP.class)
.register(NeutronAllowedAddressPair.class)
.register(NeutronExtraDhcpOptCreate.class)
.register(Subnet.class)
.register(NeutronSubnet.class)
.register(NeutronPool.class)
.register(NeutronHostRoute.class)
.register(IPVersionType.class)
.register(Ipv6AddressMode.class)
.register(Ipv6RaMode.class)
.register(LinkedHashMap.class)
.build();
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected StorageService storageService;
private final ExecutorService eventExecutor = newSingleThreadExecutor(
groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
private final MapEventListener<String, Network> networkMapListener = new OpenstackNetworkMapListener();
private final MapEventListener<String, Subnet> subnetMapListener = new OpenstackSubnetMapListener();
private final MapEventListener<String, Port> portMapListener = new OpenstackPortMapListener();
private ConsistentMap<String, Network> osNetworkStore;
private ConsistentMap<String, Subnet> osSubnetStore;
private ConsistentMap<String, Port> osPortStore;
@Activate
protected void activate() {
ApplicationId appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
osNetworkStore = storageService.<String, Network>consistentMapBuilder()
.withSerializer(Serializer.using(SERIALIZER_NEUTRON_L2))
.withName("openstack-networkstore")
.withApplicationId(appId)
.build();
osNetworkStore.addListener(networkMapListener);
osSubnetStore = storageService.<String, Subnet>consistentMapBuilder()
.withSerializer(Serializer.using(SERIALIZER_NEUTRON_L2))
.withName("openstack-subnetstore")
.withApplicationId(appId)
.build();
osSubnetStore.addListener(subnetMapListener);
osPortStore = storageService.<String, Port>consistentMapBuilder()
.withSerializer(Serializer.using(SERIALIZER_NEUTRON_L2))
.withName("openstack-portstore")
.withApplicationId(appId)
.build();
osPortStore.addListener(portMapListener);
log.info("Started");
}
@Deactivate
protected void deactivate() {
osNetworkStore.removeListener(networkMapListener);
osSubnetStore.removeListener(subnetMapListener);
osPortStore.removeListener(portMapListener);
eventExecutor.shutdown();
log.info("Stopped");
}
@Override
public void createNetwork(Network osNet) {
osNetworkStore.compute(osNet.getId(), (id, existing) -> {
final String error = osNet.getName() + ERR_DUPLICATE;
checkArgument(existing == null, error);
return osNet;
});
}
@Override
public void updateNetwork(Network osNet) {
osNetworkStore.compute(osNet.getId(), (id, existing) -> {
final String error = osNet.getName() + ERR_NOT_FOUND;
checkArgument(existing != null, error);
return osNet;
});
}
@Override
public Network removeNetwork(String netId) {
Versioned<Network> osNet = osNetworkStore.remove(netId);
return osNet == null ? null : osNet.value();
}
@Override
public Network network(String netId) {
return osNetworkStore.asJavaMap().get(netId);
}
@Override
public Set<Network> networks() {
return ImmutableSet.copyOf(osNetworkStore.asJavaMap().values());
}
@Override
public void createSubnet(Subnet osSubnet) {
osSubnetStore.compute(osSubnet.getId(), (id, existing) -> {
final String error = osSubnet.getId() + ERR_DUPLICATE;
checkArgument(existing == null, error);
return osSubnet;
});
}
@Override
public void updateSubnet(Subnet osSubnet) {
osSubnetStore.compute(osSubnet.getId(), (id, existing) -> {
final String error = osSubnet.getId() + ERR_NOT_FOUND;
checkArgument(existing != null, error);
return osSubnet;
});
}
@Override
public Subnet removeSubnet(String subnetId) {
Versioned<Subnet> osSubnet = osSubnetStore.remove(subnetId);
return osSubnet == null ? null : osSubnet.value();
}
@Override
public Subnet subnet(String subnetId) {
return osSubnetStore.asJavaMap().get(subnetId);
}
@Override
public Set<Subnet> subnets() {
return ImmutableSet.copyOf(osSubnetStore.asJavaMap().values());
}
@Override
public void createPort(Port osPort) {
osPortStore.compute(osPort.getId(), (id, existing) -> {
final String error = osPort.getId() + ERR_DUPLICATE;
checkArgument(existing == null, error);
return osPort;
});
}
@Override
public void updatePort(Port osPort) {
osPortStore.compute(osPort.getId(), (id, existing) -> {
final String error = osPort.getId() + ERR_NOT_FOUND;
checkArgument(existing != null, error);
return osPort;
});
}
@Override
public Port removePort(String portId) {
Versioned<Port> osPort = osPortStore.remove(portId);
return osPort == null ? null : osPort.value();
}
@Override
public Port port(String portId) {
return osPortStore.asJavaMap().get(portId);
}
@Override
public Set<Port> ports() {
return ImmutableSet.copyOf(osPortStore.asJavaMap().values());
}
@Override
public void clear() {
osPortStore.clear();
osSubnetStore.clear();
osNetworkStore.clear();
}
private class OpenstackNetworkMapListener implements MapEventListener<String, Network> {
@Override
public void event(MapEvent<String, Network> event) {
switch (event.type()) {
case UPDATE:
log.debug("OpenStack network updated");
eventExecutor.execute(() ->
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_NETWORK_UPDATED,
event.newValue().value()))
);
break;
case INSERT:
log.debug("OpenStack network created");
eventExecutor.execute(() ->
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_NETWORK_CREATED,
event.newValue().value()))
);
break;
case REMOVE:
log.debug("OpenStack network removed");
eventExecutor.execute(() ->
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_NETWORK_REMOVED,
event.oldValue().value()))
);
break;
default:
log.error("Unsupported openstack network event type");
break;
}
}
}
private class OpenstackSubnetMapListener implements MapEventListener<String, Subnet> {
@Override
public void event(MapEvent<String, Subnet> event) {
switch (event.type()) {
case UPDATE:
log.debug("OpenStack subnet updated");
eventExecutor.execute(() ->
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_SUBNET_UPDATED,
network(event.newValue().value().getNetworkId()),
event.newValue().value()))
);
break;
case INSERT:
log.debug("OpenStack subnet created");
eventExecutor.execute(() ->
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_SUBNET_CREATED,
network(event.newValue().value().getNetworkId()),
event.newValue().value()))
);
break;
case REMOVE:
log.debug("OpenStack subnet removed");
eventExecutor.execute(() ->
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_SUBNET_REMOVED,
network(event.oldValue().value().getNetworkId()),
event.oldValue().value()))
);
break;
default:
log.error("Unsupported openstack subnet event type");
break;
}
}
}
private class OpenstackPortMapListener implements MapEventListener<String, Port> {
@Override
public void event(MapEvent<String, Port> event) {
switch (event.type()) {
case UPDATE:
eventExecutor.execute(() -> {
Port oldPort = event.oldValue().value();
Port newPort = event.newValue().value();
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_PORT_UPDATED,
network(event.newValue().value().getNetworkId()), newPort));
processSecurityGroupUpdate(oldPort, newPort);
});
break;
case INSERT:
log.debug("OpenStack port created");
eventExecutor.execute(() ->
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_PORT_CREATED,
network(event.newValue().value().getNetworkId()),
event.newValue().value()))
);
break;
case REMOVE:
log.debug("OpenStack port removed");
eventExecutor.execute(() ->
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_PORT_PRE_REMOVE,
network(event.oldValue().value().getNetworkId()),
event.oldValue().value()))
);
eventExecutor.execute(() ->
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_PORT_REMOVED,
network(event.oldValue().value().getNetworkId()),
event.oldValue().value()))
);
break;
default:
log.error("Unsupported openstack port event type");
break;
}
}
private void processSecurityGroupUpdate(Port oldPort, Port newPort) {
List<String> oldSecurityGroups = oldPort.getSecurityGroups() == null ?
ImmutableList.of() : oldPort.getSecurityGroups();
List<String> newSecurityGroups = newPort.getSecurityGroups() == null ?
ImmutableList.of() : newPort.getSecurityGroups();
oldSecurityGroups.stream()
.filter(sgId -> !Objects.requireNonNull(newPort.getSecurityGroups()).contains(sgId))
.forEach(sgId -> notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_PORT_SECURITY_GROUP_REMOVED, newPort, sgId
)));
newSecurityGroups.stream()
.filter(sgId -> !oldPort.getSecurityGroups().contains(sgId))
.forEach(sgId -> notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_PORT_SECURITY_GROUP_ADDED, newPort, sgId
)));
}
}
}