package org.opennaas.extensions.genericnetwork.capability.nclprovisioner; /* * #%L * OpenNaaS :: Generic Network * %% * Copyright (C) 2007 - 2014 FundaciĆ³ Privada i2CAT, Internet i InnovaciĆ³ a Catalunya * %% * 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. * #L% */ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.Timer; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.opennaas.core.resources.ResourceException; import org.opennaas.core.resources.action.IAction; import org.opennaas.core.resources.action.IActionSet; import org.opennaas.core.resources.capability.AbstractCapability; import org.opennaas.core.resources.capability.CapabilityException; import org.opennaas.core.resources.capability.ICapability; import org.opennaas.core.resources.configurationadmin.ConfigurationAdminUtil; import org.opennaas.core.resources.descriptor.CapabilityDescriptor; import org.opennaas.extensions.genericnetwork.Activator; import org.opennaas.extensions.genericnetwork.capability.circuitaggregation.ICircuitAggregationCapability; import org.opennaas.extensions.genericnetwork.capability.circuitprovisioning.ICircuitProvisioningCapability; import org.opennaas.extensions.genericnetwork.capability.nclprovisioner.api.CircuitCollection; import org.opennaas.extensions.genericnetwork.capability.nclprovisioner.api.CircuitId; import org.opennaas.extensions.genericnetwork.capability.nclprovisioner.components.CircuitFactoryLogic; import org.opennaas.extensions.genericnetwork.capability.nclprovisioner.components.NetworkStatisticsPoller; import org.opennaas.extensions.genericnetwork.capability.pathfinding.IPathFindingCapability; import org.opennaas.extensions.genericnetwork.events.PortCongestionEvent; import org.opennaas.extensions.genericnetwork.exceptions.CircuitAllocationException; import org.opennaas.extensions.genericnetwork.model.GenericNetworkModel; import org.opennaas.extensions.genericnetwork.model.circuit.Circuit; import org.opennaas.extensions.genericnetwork.model.circuit.NetworkConnection; import org.opennaas.extensions.genericnetwork.model.circuit.Route; import org.opennaas.extensions.genericnetwork.model.circuit.request.CircuitRequest; import org.opennaas.extensions.genericnetwork.model.helpers.Circuit2RequestHelper; import org.opennaas.extensions.genericnetwork.model.helpers.GenericNetworkModelHelper; import org.osgi.framework.ServiceRegistration; import org.osgi.service.event.Event; import org.osgi.service.event.EventConstants; import org.osgi.service.event.EventHandler; /** * * @author Adrian Rosello Rey (i2CAT) * @author Isart Canyameres Gimenez (i2cat) - Use aggregation * */ public class NCLProvisionerCapability extends AbstractCapability implements INCLProvisionerCapability, EventHandler { public static final String CAPABILITY_TYPE = "nclprovisioner"; public static final String POLLING_INTERVAL_KEY = "polling.interval"; public static final String SLA_MANAGER_URI = "slamanager.uri"; private Log log = LogFactory.getLog(NCLProvisionerCapability.class); private String resourceId = ""; private ServiceRegistration eventListenerRegistration; private final static String NCL_CONFIG_FILE = "org.opennaas.extensions.ofertie.ncl"; private final static String AUTOREROUTE_KEY = "ncl.autoreroute"; private final Object lock = new Object(); private Timer statisticsPollerTimer; public NCLProvisionerCapability(CapabilityDescriptor descriptor, String resourceId) { super(descriptor); this.resourceId = resourceId; log.debug("Built new NCLProvisioner Capability"); } @Override public String getCapabilityName() { return CAPABILITY_TYPE; } @Override public void activate() throws CapabilityException { try { registerAsCongestionEventListener(); } catch (IOException e) { log.warn("Could not registrate NCLProvisionerCapability as listener for PortCongestion events.", e); eventListenerRegistration = null; } try { initializeStatisticsPusher(); } catch (CapabilityException c) { log.warn("Could not initialize Statistics Pusher for SLA Manager. Statistics will not be reported."); statisticsPollerTimer = null; } registerService(Activator.getContext(), CAPABILITY_TYPE, getResourceType(), getResourceName(), INCLProvisionerCapability.class.getName()); super.activate(); } @Override public void deactivate() throws CapabilityException { if (statisticsPollerTimer != null) { statisticsPollerTimer.cancel(); statisticsPollerTimer = null; } if (eventListenerRegistration != null) unregisterListener(); unregisterService(); super.deactivate(); } @Override public String allocateCircuit(CircuitRequest circuitRequest) throws CapabilityException { try { synchronized (lock) { IPathFindingCapability pathFindingCapab; ICircuitProvisioningCapability circuitProvCapability; ICircuitAggregationCapability circuitAggregationCapability; pathFindingCapab = (IPathFindingCapability) getCapability(IPathFindingCapability.class); circuitProvCapability = (ICircuitProvisioningCapability) getCapability(ICircuitProvisioningCapability.class); circuitAggregationCapability = (ICircuitAggregationCapability) getCapability(ICircuitAggregationCapability.class); Route route = pathFindingCapab.findPathForRequest(circuitRequest); Circuit toAllocate = CircuitFactoryLogic.generateCircuit(circuitRequest, route); log.info("Allocating circuit " + toAllocate.getCircuitId() + " with route " + toAllocate.getRoute().getId()); if (log.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); for (NetworkConnection c : toAllocate.getRoute().getNetworkConnections()) { sb.append(c.getName()); sb.append(", "); } log.debug("Route details: " + sb.toString()); } // call aggregation logic with all requested circuits and the one toAllocate Set<Circuit> toAggregate = new HashSet<Circuit>(); toAggregate.addAll(getRequestedCircuits()); toAggregate.add(toAllocate); Set<Circuit> allAggregatedCircuits = circuitAggregationCapability.aggregateCircuits(toAggregate); // replace currently allocated (already aggregated) with new aggregated circuits List<Circuit> oldAggregated = new ArrayList<Circuit>(); oldAggregated.addAll(circuitProvCapability.getCircuits()); List<Circuit> newAggregated = new ArrayList<Circuit>(allAggregatedCircuits.size()); newAggregated.addAll(allAggregatedCircuits); circuitProvCapability.replace(oldAggregated, newAggregated); // update requested circuits in the model // add allocated one (toAllocate) ((GenericNetworkModel) resource.getModel()).getRequestedCircuits().put(toAllocate.getCircuitId(), toAllocate); log.info("Allocated circuit " + toAllocate.getCircuitId() + " with route " + toAllocate.getRoute().getId()); return toAllocate.getCircuitId(); } } catch (ResourceException e) { throw new CircuitAllocationException(e); } catch (Exception e) { throw new CircuitAllocationException(e); } } @Override public void deallocateCircuit(String circuitId) throws CapabilityException { synchronized (lock) { ICircuitProvisioningCapability circuitProvCapability; ICircuitAggregationCapability circuitAggregationCapability; try { circuitProvCapability = (ICircuitProvisioningCapability) getCapability(ICircuitProvisioningCapability.class); circuitAggregationCapability = (ICircuitAggregationCapability) getCapability(ICircuitAggregationCapability.class); } catch (ResourceException e) { throw new CapabilityException(e); } // call aggregation logic with all requested circuits except the one to be removed Set<Circuit> toAggregate = new HashSet<Circuit>(); for (Circuit circuit : getRequestedCircuits()) { if (!circuit.getCircuitId().equals(circuitId)) toAggregate.add(circuit); } Set<Circuit> newAggregatedCircuits = circuitAggregationCapability.aggregateCircuits(toAggregate); // replace currently aggregated by newAggregatedCircuits List<Circuit> oldAggregated = new ArrayList<Circuit>(); oldAggregated.addAll(circuitProvCapability.getCircuits()); List<Circuit> newAggregated = new ArrayList<Circuit>(newAggregatedCircuits.size()); newAggregated.addAll(newAggregatedCircuits); circuitProvCapability.replace(oldAggregated, newAggregated); // update requested circuits in the model // remove deallocated one ((GenericNetworkModel) resource.getModel()).getRequestedCircuits().remove(circuitId); log.info("Deallocated circuit " + circuitId); } } @Override public Collection<Circuit> getAllocatedCircuits() { return getRequestedCircuits(); } public Collection<Circuit> getRequestedCircuits() { return ((GenericNetworkModel) resource.getModel()).getRequestedCircuits().values(); } @Override public void updateCircuit(String circuitId, CircuitRequest pathRequest) throws CapabilityException { deallocateCircuit(circuitId); allocateCircuit(pathRequest); } @Override public CircuitId allocateCircuitAPI(CircuitRequest circuitRequest) throws CapabilityException { String id = allocateCircuit(circuitRequest); CircuitId circuitId = new CircuitId(id); return circuitId; } @Override public CircuitCollection getAllocatedCircuitsAPI() { CircuitCollection circuitCollection = new CircuitCollection(); circuitCollection.setCircuits(getAllocatedCircuits()); return circuitCollection; } @Override public void handleEvent(Event event) { if (event instanceof PortCongestionEvent) { PortCongestionEvent congestionEvent = (PortCongestionEvent) event; String portId = (String) congestionEvent.getProperty(PortCongestionEvent.PORT_ID_KEY); log.info("PortCongestionEvent alarm received for port" + portId); boolean autoReroute = readAutorerouteOption(); if (autoReroute) { log.debug("Auto-reroute is activated. Launching auto-reroute in network " + resource.getResourceDescriptor().getInformation() .getName()); try { launchRerouteMechanism(portId); } catch (Exception e) { log.error(e.getMessage()); // TODO can not throw exception, since EventHandler interface does not allow it. } } else { log.debug("Auto-reroute is deactivated. Ignoring received LinkCongestion alarm. "); } } else log.debug("Ignoring non-LinkCongestion alarm."); } @Override public void queueAction(IAction action) throws CapabilityException { throw new UnsupportedOperationException("Not Implemented. This capability is not using the queue."); } @Override public IActionSet getActionSet() throws CapabilityException { throw new UnsupportedOperationException("This capability does not contain actionset."); } private ICapability getCapability(Class<? extends ICapability> clazz) throws ResourceException { return this.resource.getCapabilityByInterface(clazz); } private void unregisterListener() { log.debug("Unregistering NCLProvisinerCapability as listener for PortCongestion events."); eventListenerRegistration.unregister(); log.debug("NCLProvisinerCapability successfully unregistered."); } private void registerAsCongestionEventListener() throws IOException { log.debug("Registering NCLProvisinerCapability as listener for PortCongestion events."); Properties properties = new Properties(); properties.put(EventConstants.EVENT_TOPIC, PortCongestionEvent.TOPIC); properties.put(PortCongestionEvent.NETWORK_ID_KEY, resource.getResourceIdentifier().getId()); eventListenerRegistration = Activator.getContext().registerService(EventHandler.class.getName(), this, properties); log.debug("NCLProvisinerCapability successfully registered as listener for PortCongestion events."); } private boolean readAutorerouteOption() { try { return doReadAutorerouteOption(); } catch (IOException ioe) { log.error("Failed to read auto-reroute option. ", ioe); log.warn("Deactivating auto-reroute"); return false; } } private boolean doReadAutorerouteOption() throws IOException { Properties properties = ConfigurationAdminUtil.getProperties(Activator.getContext(), NCL_CONFIG_FILE); if (properties == null) throw new IOException("Failed to determine auto-reroute option. Unable to obtain configuration " + NCL_CONFIG_FILE); String value = properties.getProperty(AUTOREROUTE_KEY); if (value == null) { return false; } return Boolean.parseBoolean(value); } private void launchRerouteMechanism(String portId) throws Exception { String circuitId = selectCircuitToReallocate(portId); if (circuitId == null) { log.info("No reallocable circuit currently using port " + portId + ". Ignoring alarm."); return; } try { log.debug("Rerouting circuit " + circuitId + " that passes through congested port " + portId); rerouteCircuit(circuitId); } catch (Exception e) { throw new Exception("Could not reallocate circuit " + circuitId + ": " + e, e); } } private void rerouteCircuit(String circuitId) throws CapabilityException { synchronized (lock) { log.debug("Start of rerouteCircuit call."); IPathFindingCapability pathFindingCapab; ICircuitProvisioningCapability circuitProvCapability; ICircuitAggregationCapability circuitAggregationCapability; try { pathFindingCapab = (IPathFindingCapability) getCapability(IPathFindingCapability.class); circuitProvCapability = (ICircuitProvisioningCapability) getCapability(ICircuitProvisioningCapability.class); circuitAggregationCapability = (ICircuitAggregationCapability) getCapability(ICircuitAggregationCapability.class); } catch (ResourceException e) { throw new CapabilityException(e); } GenericNetworkModel model = (GenericNetworkModel) this.resource.getModel(); Circuit toReroute = model.getRequestedCircuits().get(circuitId); if (toReroute == null) throw new CapabilityException("Can not reroute circuit: Circuit is not allocated."); log.info("Rerouting circuit " + circuitId + " thas uses congested route " + toReroute.getRoute().getId()); CircuitRequest circuitRequest = Circuit2RequestHelper.generateCircuitRequest(toReroute.getQos(), toReroute.getTrafficFilter(), toReroute.getRoute()); log.debug("Built request in network " + resource.getResourceDescriptor().getInformation().getName() + " : " + circuitRequest.getSource() + ", " + circuitRequest .getDestination()); Route route = pathFindingCapab.findPathForRequest(circuitRequest); Circuit withNewRoute = CircuitFactoryLogic.generateCircuit(circuitRequest, route); withNewRoute.setCircuitId(toReroute.getCircuitId()); // call aggregation logic with all requested circuits // except the original one to be re-routed and with the one to be re-rerouted updated Set<Circuit> toAggregate = new HashSet<Circuit>(); for (Circuit circuit : getRequestedCircuits()) { if (!circuit.getCircuitId().equals(circuitId)) toAggregate.add(circuit); } toAggregate.add(withNewRoute); Set<Circuit> newAggregatedCircuits = circuitAggregationCapability.aggregateCircuits(toAggregate); // replace currently aggregated by newAggregatedCircuits List<Circuit> oldAggregated = new ArrayList<Circuit>(); oldAggregated.addAll(circuitProvCapability.getCircuits()); List<Circuit> newAggregated = new ArrayList<Circuit>(newAggregatedCircuits.size()); newAggregated.addAll(newAggregatedCircuits); circuitProvCapability.replace(oldAggregated, newAggregated); // update requested circuits in model model.getRequestedCircuits().put(circuitId, withNewRoute); log.info("Rerouted circuit " + circuitId + " with route " + withNewRoute.getRoute().getId()); if (log.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); for (NetworkConnection c : withNewRoute.getRoute().getNetworkConnections()) { sb.append(c.getName()); sb.append(", "); } log.debug("Route details: " + sb.toString()); } log.debug("End of rerouteCircuit call."); } } private String selectCircuitToReallocate(String portId) throws CapabilityException { List<Circuit> circuitsInPort = getAllCircuitsInPort(portId); if (log.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("["); for (Circuit c : circuitsInPort) { sb.append(c.getCircuitId() + ", "); } sb.append("]"); log.debug("Circuits using port " + portId + ": " + sb.toString()); } if (circuitsInPort.isEmpty()) { return null; } List<Circuit> reallocables = filterReallocableCircuits(circuitsInPort); if (log.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("["); for (Circuit c : reallocables) { sb.append(c.getCircuitId() + ", "); } sb.append("]"); log.debug("Reallocable circuits using port " + portId + ": " + sb.toString()); } if (reallocables.isEmpty()) return null; // TODO select in a more intelligent way, for example, based on ToS, flowCapacity, etc. return reallocables.get(0).getCircuitId(); } private List<Circuit> filterReallocableCircuits(List<Circuit> circuits) throws CapabilityException { // TODO: implement in a more efficient way IPathFindingCapability pathFindingCapab; try { pathFindingCapab = (IPathFindingCapability) getCapability(IPathFindingCapability.class); } catch (ResourceException e) { throw new CapabilityException(e); } List<Circuit> reallocableCircuits = new ArrayList<Circuit>(circuits.size()); for (Circuit circuit : circuits) { Route alternativeRoute = null; try { CircuitRequest circuitRequest = Circuit2RequestHelper.generateCircuitRequest(circuit.getQos(), circuit.getTrafficFilter(), circuit.getRoute()); alternativeRoute = pathFindingCapab.findPathForRequest(circuitRequest); } catch (CapabilityException e) { // ignored log.debug("Unable to find uncongested alternative route for circuit " + circuit.getCircuitId() + ". Cause: " + e.getMessage()); } if (alternativeRoute != null) reallocableCircuits.add(circuit); } return reallocableCircuits; } private List<Circuit> getAllCircuitsInPort(String portId) { List<Circuit> circuitsIdsInPort = new ArrayList<Circuit>(); // look here for NOT-AGGREGATED CIRCUITS. for (Circuit circuit : getRequestedCircuits()) { if (GenericNetworkModelHelper.isPortInCircuit(portId, circuit)) circuitsIdsInPort.add(circuit); } return circuitsIdsInPort; } private void initializeStatisticsPusher() throws CapabilityException { log.info("Initializing Network statistics pusher."); String slaManagerUri = descriptor.getPropertyValue(SLA_MANAGER_URI); String pollingInterval = descriptor.getPropertyValue(POLLING_INTERVAL_KEY); if (StringUtils.isEmpty(slaManagerUri) || StringUtils.isEmpty(pollingInterval)) throw new CapabilityException("SLAManager URI and Polling Interval should be indicated as capability property."); if (!StringUtils.isNumeric(pollingInterval)) throw new CapabilityException("Invalid polling interval: value must be numeric."); // initialize statistics poller try { NetworkStatisticsPoller statisticsPoller = new NetworkStatisticsPoller(new URI(slaManagerUri), this.resource); statisticsPollerTimer = new Timer("Generic Network " + this.resourceId + " statistics poller.", true); statisticsPollerTimer.scheduleAtFixedRate(statisticsPoller, 0, Integer.valueOf(pollingInterval) * 1000); } catch (URISyntaxException e) { log.error("Invalid SLA URI.", e); throw new CapabilityException(e); } log.debug("Network statistics pusher initialized."); } }