/* * Copyright 2017-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.onosproject.mapping.impl; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; 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.onosproject.mapping.DefaultMapping; import org.onosproject.mapping.DefaultMappingEntry; import org.onosproject.mapping.Mapping; import org.onosproject.mapping.MappingEntry; import org.onosproject.mapping.MappingEvent; import org.onosproject.mapping.MappingId; import org.onosproject.mapping.MappingKey; import org.onosproject.mapping.MappingStore; import org.onosproject.mapping.MappingStoreDelegate; import org.onosproject.mapping.MappingTreatment; import org.onosproject.mapping.MappingValue; import org.onosproject.mapping.actions.MappingAction; import org.onosproject.mapping.addresses.MappingAddress; import org.onosproject.mapping.instructions.MappingInstruction; import org.onosproject.net.DeviceId; import org.onosproject.net.device.DeviceService; 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.slf4j.Logger; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import static org.slf4j.LoggerFactory.getLogger; /** * Implementation of a distributed store for managing mapping information. */ @Component(immediate = true) @Service public class DistributedMappingStore extends AbstractStore<MappingEvent, MappingStoreDelegate> implements MappingStore { private final Logger log = getLogger(getClass()); private ConsistentMap<MappingId, Mapping> database; private ConsistentMap<MappingId, Mapping> cache; private Map<MappingId, Mapping> databaseMap; private Map<MappingId, Mapping> cacheMap; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected StorageService storageService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceService deviceService; private final MapEventListener<MappingId, Mapping> listener = new InternalListener(); @Activate public void activate() { Serializer serializer = Serializer.using(KryoNamespaces.API, Mapping.class, DefaultMapping.class, MappingId.class, MappingEvent.Type.class, MappingKey.class, MappingValue.class, MappingAddress.class, MappingAddress.Type.class, MappingAction.class, MappingAction.Type.class, MappingTreatment.class, MappingInstruction.class, MappingInstruction.Type.class); database = storageService.<MappingId, Mapping>consistentMapBuilder() .withName("onos-mapping-database") .withSerializer(serializer) .build(); cache = storageService.<MappingId, Mapping>consistentMapBuilder() .withName("onos-mapping-cache") .withSerializer(serializer) .build(); database.addListener(listener); cache.addListener(listener); databaseMap = database.asJavaMap(); cacheMap = cache.asJavaMap(); log.info("Started"); } @Deactivate public void deactivate() { database.removeListener(listener); cache.removeListener(listener); log.info("Stopped"); } /** * Obtains map representation of mapping store. * * @param type mapping store type * @return map representation of mapping store */ private Map<MappingId, Mapping> getStoreMap(Type type) { switch (type) { case MAP_DATABASE: return databaseMap; case MAP_CACHE: return cacheMap; default: log.warn("Unrecognized map store type {}", type); return Maps.newConcurrentMap(); } } /** * Obtains mapping store. * * @param type mapping store type * @return mapping store */ private ConsistentMap<MappingId, Mapping> getStore(Type type) { switch (type) { case MAP_DATABASE: return database; case MAP_CACHE: return cache; default: throw new IllegalArgumentException("Wrong mapping store " + type); } } @Override public int getMappingCount(Type type) { AtomicInteger sum = new AtomicInteger(0); deviceService.getDevices().forEach(device -> sum.addAndGet(Iterables.size(getMappingEntries(type, device.id())))); return sum.get(); } @Override public Iterable<MappingEntry> getAllMappingEntries(Type type) { Map<MappingId, Mapping> storeMap = getStoreMap(type); return ImmutableList.copyOf(storeMap.values().stream() .map(DefaultMappingEntry::new) .collect(Collectors.toList())); } @Override public MappingEntry getMappingEntry(Type type, Mapping mapping) { return new DefaultMappingEntry(getStoreMap(type).get(mapping.id())); } @Override public Iterable<MappingEntry> getMappingEntries(Type type, DeviceId deviceId) { Map<MappingId, Mapping> storeMap = getStoreMap(type); return ImmutableList.copyOf(storeMap.values().stream() .filter(m -> m.deviceId() == deviceId) .map(DefaultMappingEntry::new) .collect(Collectors.toList())); } @Override public void storeMapping(Type type, MappingEntry mapping) { getStore(type).put(mapping.id(), mapping); } @Override public MappingEvent removeMapping(Type type, Mapping mapping) { getStore(type).remove(mapping.id()); return null; } @Override public void pendingDeleteMapping(Type type, Mapping mapping) { // TODO: this will be implemented when management plane is ready log.error("This method will be available when management plane is ready"); } @Override public MappingEvent addOrUpdateMappingEntry(Type type, MappingEntry entry) { // TODO: this will be implemented when management plane is ready log.error("This method will be available when management plane is ready"); return null; } @Override public MappingEvent pendingMappingEntry(Type type, MappingEntry entry) { // TODO: this will be implemented when management plane is ready log.error("This method will be available when management plane is ready"); return null; } @Override public void purgeMappingEntries(Type type) { getStore(type).clear(); } /** * Event listener to notify delegates about mapping events. */ private class InternalListener implements MapEventListener<MappingId, Mapping> { @Override public void event(MapEvent<MappingId, Mapping> event) { final MappingEvent.Type type; final Mapping mapping; switch (event.type()) { case INSERT: type = MappingEvent.Type.MAPPING_ADDED; mapping = event.newValue().value(); break; case UPDATE: type = MappingEvent.Type.MAPPING_UPDATED; mapping = event.newValue().value(); break; case REMOVE: type = MappingEvent.Type.MAPPING_REMOVED; mapping = event.oldValue().value(); break; default: throw new IllegalArgumentException("Wrong event type " + event.type()); } notifyDelegate(new MappingEvent(type, mapping)); } } }