| /* |
| * 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.base.Strings; |
| import com.google.common.collect.ImmutableSet; |
| 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.core.ApplicationId; |
| import org.onosproject.core.CoreService; |
| import org.onosproject.openstacknetworking.api.OpenstackNetworkService; |
| import org.onosproject.openstacknetworking.api.OpenstackRouterEvent; |
| import org.onosproject.openstacknetworking.api.OpenstackRouterStore; |
| import org.onosproject.openstacknetworking.api.OpenstackRouterStoreDelegate; |
| 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.ExternalGateway; |
| import org.openstack4j.model.network.NetFloatingIP; |
| import org.openstack4j.model.network.Router; |
| import org.openstack4j.model.network.RouterInterface; |
| import org.openstack4j.model.network.State; |
| import org.openstack4j.openstack.networking.domain.NeutronExternalGateway; |
| import org.openstack4j.openstack.networking.domain.NeutronFloatingIP; |
| import org.openstack4j.openstack.networking.domain.NeutronHostRoute; |
| import org.openstack4j.openstack.networking.domain.NeutronRouter; |
| import org.openstack4j.openstack.networking.domain.NeutronRouterInterface; |
| import org.osgi.service.component.annotations.Activate; |
| import org.slf4j.Logger; |
| |
| 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.OpenstackRouterEvent.Type.OPENSTACK_FLOATING_IP_ASSOCIATED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_FLOATING_IP_CREATED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_FLOATING_IP_DISASSOCIATED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_FLOATING_IP_REMOVED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_FLOATING_IP_UPDATED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_ROUTER_CREATED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_ROUTER_GATEWAY_ADDED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_ROUTER_GATEWAY_REMOVED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_ROUTER_INTERFACE_ADDED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_ROUTER_INTERFACE_REMOVED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_ROUTER_INTERFACE_UPDATED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_ROUTER_REMOVED; |
| import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.OPENSTACK_ROUTER_UPDATED; |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| /** |
| * Manages the inventory of OpenStack router and floating IP using a {@code ConsistentMap}. |
| */ |
| @Service |
| @Component(immediate = true) |
| public class DistributedOpenstackRouterStore |
| extends AbstractStore<OpenstackRouterEvent, OpenstackRouterStoreDelegate> |
| implements OpenstackRouterStore { |
| |
| 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_L3 = KryoNamespace.newBuilder() |
| .register(KryoNamespaces.API) |
| .register(Router.class) |
| .register(NeutronRouter.class) |
| .register(State.class) |
| .register(NeutronHostRoute.class) |
| .register(ExternalGateway.class) |
| .register(NeutronExternalGateway.class) |
| .register(RouterInterface.class) |
| .register(NeutronRouterInterface.class) |
| .register(NetFloatingIP.class) |
| .register(NeutronFloatingIP.class) |
| .build(); |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected CoreService coreService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected StorageService storageService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected OpenstackNetworkService osNetworkService; |
| |
| private final ExecutorService eventExecutor = newSingleThreadExecutor( |
| groupedThreads(this.getClass().getSimpleName(), "event-handler", log)); |
| private final MapEventListener<String, Router> routerMapListener = new OpenstackRouterMapListener(); |
| private final MapEventListener<String, RouterInterface> routerInterfaceMapListener = |
| new OpenstackRouterInterfaceMapListener(); |
| private final MapEventListener<String, NetFloatingIP> floatingIpMapListener = |
| new OpenstackFloatingIpMapListener(); |
| |
| private ConsistentMap<String, Router> osRouterStore; |
| private ConsistentMap<String, RouterInterface> osRouterInterfaceStore; |
| private ConsistentMap<String, NetFloatingIP> osFloatingIpStore; |
| |
| @Activate |
| protected void activate() { |
| ApplicationId appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID); |
| |
| osRouterStore = storageService.<String, Router>consistentMapBuilder() |
| .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L3)) |
| .withName("openstack-routerstore") |
| .withApplicationId(appId) |
| .build(); |
| osRouterStore.addListener(routerMapListener); |
| |
| osRouterInterfaceStore = storageService.<String, RouterInterface>consistentMapBuilder() |
| .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L3)) |
| .withName("openstack-routerifacestore") |
| .withApplicationId(appId) |
| .build(); |
| osRouterInterfaceStore.addListener(routerInterfaceMapListener); |
| |
| osFloatingIpStore = storageService.<String, NetFloatingIP>consistentMapBuilder() |
| .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L3)) |
| .withName("openstack-floatingipstore") |
| .withApplicationId(appId) |
| .build(); |
| osFloatingIpStore.addListener(floatingIpMapListener); |
| |
| log.info("Started"); |
| } |
| |
| @Deactivate |
| protected void deactivate() { |
| osRouterStore.removeListener(routerMapListener); |
| osRouterInterfaceStore.removeListener(routerInterfaceMapListener); |
| osFloatingIpStore.removeListener(floatingIpMapListener); |
| eventExecutor.shutdown(); |
| |
| log.info("Stopped"); |
| } |
| |
| @Override |
| public void createRouter(Router osRouter) { |
| osRouterStore.compute(osRouter.getId(), (id, existing) -> { |
| final String error = osRouter.getName() + ERR_DUPLICATE; |
| checkArgument(existing == null, error); |
| return osRouter; |
| }); |
| } |
| |
| @Override |
| public void updateRouter(Router osRouter) { |
| osRouterStore.compute(osRouter.getId(), (id, existing) -> { |
| final String error = osRouter.getName() + ERR_NOT_FOUND; |
| checkArgument(existing != null, error); |
| return osRouter; |
| }); |
| } |
| |
| @Override |
| public Router removeRouter(String routerId) { |
| Versioned<Router> osRouter = osRouterStore.remove(routerId); |
| return osRouter == null ? null : osRouter.value(); |
| } |
| |
| @Override |
| public Router router(String routerId) { |
| return osRouterStore.asJavaMap().get(routerId); |
| } |
| |
| @Override |
| public Set<Router> routers() { |
| return ImmutableSet.copyOf(osRouterStore.asJavaMap().values()); |
| } |
| |
| @Override |
| public void addRouterInterface(RouterInterface osRouterIface) { |
| osRouterInterfaceStore.compute(osRouterIface.getPortId(), (id, existing) -> { |
| final String error = osRouterIface.getPortId() + ERR_DUPLICATE; |
| checkArgument(existing == null, error); |
| return osRouterIface; |
| }); |
| } |
| |
| @Override |
| public void updateRouterInterface(RouterInterface osRouterIface) { |
| osRouterInterfaceStore.compute(osRouterIface.getPortId(), (id, existing) -> { |
| final String error = osRouterIface.getPortId() + ERR_NOT_FOUND; |
| checkArgument(existing != null, error); |
| return osRouterIface; |
| }); |
| } |
| |
| @Override |
| public RouterInterface removeRouterInterface(String routerIfaceId) { |
| Versioned<RouterInterface> osRouterIface = osRouterInterfaceStore.remove(routerIfaceId); |
| return osRouterIface == null ? null : osRouterIface.value(); |
| } |
| |
| @Override |
| public RouterInterface routerInterface(String routerIfaceId) { |
| return osRouterInterfaceStore.asJavaMap().get(routerIfaceId); |
| } |
| |
| @Override |
| public Set<RouterInterface> routerInterfaces() { |
| return ImmutableSet.copyOf(osRouterInterfaceStore.asJavaMap().values()); |
| } |
| |
| @Override |
| public void createFloatingIp(NetFloatingIP osFloatingIp) { |
| osFloatingIpStore.compute(osFloatingIp.getId(), (id, existing) -> { |
| final String error = osFloatingIp.getId() + ERR_DUPLICATE; |
| checkArgument(existing == null, error); |
| return osFloatingIp; |
| }); |
| } |
| |
| @Override |
| public void updateFloatingIp(NetFloatingIP osFloatingIp) { |
| osFloatingIpStore.compute(osFloatingIp.getId(), (id, existing) -> { |
| final String error = osFloatingIp.getId() + ERR_NOT_FOUND; |
| checkArgument(existing != null, error); |
| return osFloatingIp; |
| }); |
| } |
| |
| @Override |
| public NetFloatingIP removeFloatingIp(String floatingIpId) { |
| Versioned<NetFloatingIP> osFloatingIp = osFloatingIpStore.remove(floatingIpId); |
| return osFloatingIp == null ? null : osFloatingIp.value(); |
| } |
| |
| @Override |
| public NetFloatingIP floatingIp(String floatingIpId) { |
| return osFloatingIpStore.asJavaMap().get(floatingIpId); |
| } |
| |
| @Override |
| public Set<NetFloatingIP> floatingIps() { |
| return ImmutableSet.copyOf(osFloatingIpStore.asJavaMap().values()); |
| } |
| |
| @Override |
| public void clear() { |
| osFloatingIpStore.clear(); |
| osRouterInterfaceStore.clear(); |
| osRouterStore.clear(); |
| } |
| |
| private class OpenstackRouterMapListener implements MapEventListener<String, Router> { |
| |
| @Override |
| public void event(MapEvent<String, Router> event) { |
| switch (event.type()) { |
| case UPDATE: |
| log.debug("OpenStack router updated"); |
| eventExecutor.execute(() -> { |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_ROUTER_UPDATED, |
| event.newValue().value())); |
| processGatewayUpdate(event); |
| }); |
| break; |
| case INSERT: |
| log.debug("OpenStack router created"); |
| eventExecutor.execute(() -> |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_ROUTER_CREATED, |
| event.newValue().value())) |
| ); |
| break; |
| case REMOVE: |
| log.debug("OpenStack router removed"); |
| eventExecutor.execute(() -> |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_ROUTER_REMOVED, |
| event.oldValue().value())) |
| ); |
| break; |
| default: |
| log.error("Unsupported openstack router event type"); |
| break; |
| } |
| } |
| |
| private void processGatewayUpdate(MapEvent<String, Router> event) { |
| ExternalGateway oldGateway = event.oldValue().value().getExternalGatewayInfo(); |
| ExternalGateway newGateway = event.newValue().value().getExternalGatewayInfo(); |
| |
| if (oldGateway == null && newGateway != null) { |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_ROUTER_GATEWAY_ADDED, |
| event.newValue().value(), newGateway)); |
| } |
| if (oldGateway != null && newGateway == null) { |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_ROUTER_GATEWAY_REMOVED, |
| event.newValue().value(), oldGateway)); |
| } |
| } |
| } |
| |
| private class OpenstackRouterInterfaceMapListener implements MapEventListener<String, RouterInterface> { |
| |
| @Override |
| public void event(MapEvent<String, RouterInterface> event) { |
| switch (event.type()) { |
| case UPDATE: |
| log.debug("OpenStack router interface updated"); |
| eventExecutor.execute(() -> |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_ROUTER_INTERFACE_UPDATED, |
| router(event.newValue().value().getId()), |
| event.newValue().value())) |
| ); |
| break; |
| case INSERT: |
| log.debug("OpenStack router interface created"); |
| eventExecutor.execute(() -> |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_ROUTER_INTERFACE_ADDED, |
| router(event.newValue().value().getId()), |
| event.newValue().value())) |
| ); |
| break; |
| case REMOVE: |
| log.debug("OpenStack router interface removed"); |
| eventExecutor.execute(() -> |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_ROUTER_INTERFACE_REMOVED, |
| router(event.oldValue().value().getId()), |
| event.oldValue().value())) |
| ); |
| break; |
| default: |
| log.error("Unsupported openstack router interface event type"); |
| break; |
| } |
| } |
| } |
| |
| private class OpenstackFloatingIpMapListener implements MapEventListener<String, NetFloatingIP> { |
| |
| @Override |
| public void event(MapEvent<String, NetFloatingIP> event) { |
| switch (event.type()) { |
| case UPDATE: |
| log.debug("OpenStack floating IP updated"); |
| eventExecutor.execute(() -> { |
| Router osRouter = Strings.isNullOrEmpty( |
| event.newValue().value().getRouterId()) ? |
| null : |
| router(event.newValue().value().getRouterId()); |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_FLOATING_IP_UPDATED, |
| osRouter, |
| event.newValue().value())); |
| processFloatingIpUpdate(event, osRouter); |
| }); |
| break; |
| case INSERT: |
| log.debug("OpenStack floating IP created"); |
| eventExecutor.execute(() -> { |
| Router osRouter = Strings.isNullOrEmpty( |
| event.newValue().value().getRouterId()) ? |
| null : |
| router(event.newValue().value().getRouterId()); |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_FLOATING_IP_CREATED, |
| osRouter, |
| event.newValue().value())); |
| }); |
| break; |
| case REMOVE: |
| log.debug("OpenStack floating IP removed"); |
| eventExecutor.execute(() -> { |
| Router osRouter = Strings.isNullOrEmpty( |
| event.oldValue().value().getRouterId()) ? |
| null : |
| router(event.oldValue().value().getRouterId()); |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_FLOATING_IP_REMOVED, |
| osRouter, |
| event.oldValue().value())); |
| }); |
| break; |
| default: |
| log.error("Unsupported openstack floating IP event type"); |
| break; |
| } |
| } |
| |
| private void processFloatingIpUpdate(MapEvent<String, NetFloatingIP> event, |
| Router osRouter) { |
| String oldPortId = event.oldValue().value().getPortId(); |
| String newPortId = event.newValue().value().getPortId(); |
| |
| if (Strings.isNullOrEmpty(oldPortId) && !Strings.isNullOrEmpty(newPortId)) { |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_FLOATING_IP_ASSOCIATED, |
| osRouter, |
| event.newValue().value(), newPortId)); |
| } |
| if (!Strings.isNullOrEmpty(oldPortId) && Strings.isNullOrEmpty(newPortId)) { |
| notifyDelegate(new OpenstackRouterEvent( |
| OPENSTACK_FLOATING_IP_DISASSOCIATED, |
| osRouter, |
| event.newValue().value(), oldPortId)); |
| } |
| } |
| } |
| } |