/*
* Copyright (c) 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.nic.impl;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.nic.api.NicConsoleProvider;
import org.opendaylight.nic.compiler.api.*;
import org.opendaylight.nic.graph.api.CompilerGraph;
import org.opendaylight.nic.graph.api.CompilerGraphException;
import org.opendaylight.nic.graph.api.CompilerGraphFactory;
import org.opendaylight.nic.graph.api.InputGraph;
import org.opendaylight.nic.mapping.api.IntentMappingService;
import org.opendaylight.nic.utils.IntentUtils;
import org.opendaylight.yang.gen.v1.urn.onf.intent.nbi.rev160920.IntentDefinitions;
import org.opendaylight.yang.gen.v1.urn.onf.intent.nbi.rev160920.IntentDefinitionsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.limiter.rev170310.IntentsLimiter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.limiter.rev170310.IntentsLimiterBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.limiter.rev170310.intents.limiter.IntentLimiter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.Intents;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.IntentsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.action.Allow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.action.Block;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.action.Log;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.action.Redirect;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.action.Mirror;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.subjects.subject.EndPointGroup;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intents.Intent;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intents.IntentBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intents.IntentKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.types.rev150122.Uuid;
import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.ActionTypes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.EdgeTypes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.Graph;
import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.graph.Edges;
import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.graph.EdgesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.graph.IntentIds;
import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.intent.graph.rev150911.graph.Nodes;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.UnknownHostException;
import java.util.*;
public class NicProvider implements NicConsoleProvider {
private static final Logger LOG = LoggerFactory.getLogger(NicProvider.class);
public static final String ACTION_ALLOW = "ALLOW";
public static final String ACTION_BLOCK = "BLOCK";
public static final String ACTION_REDIRECT = "REDIRECT";
public static final String ACTION_MIRROR = "MIRROR";
public static final String ACTION_LOG = "LOG";
public static final String CONSTRAINT_QOS = "QOS";
public static final String CONSTRAINT_CLASSIFIER = "CLASSIFIER";
protected DataBroker dataBroker;
protected IntentMappingService mappingSvc;
public NicProvider(DataBroker dataBroker, IntentMappingService mappingSvc) {
this.dataBroker = dataBroker;
this.mappingSvc = mappingSvc;
}
public static final InstanceIdentifier<IntentDefinitions> INTENTS_NBI_IID = InstanceIdentifier.builder(IntentDefinitions.class).build();
@Override
public void close() throws Exception {
// Close active registrations
LOG.info("IntentengineImpl: registrations closed");
}
public void init() throws TransactionCommitFailedException {
initIntentsOperational();
initIntentsConfiguration();
initIntentNBIOperational();
initIntentNBIConfiguration();
LOG.info("Initialization done");
}
/**
* Populates Intents' initial operational data into the MD-SAL operational
* data store.
*/
protected void initIntentNBIOperational() {
// Build the initial intents operational data
final IntentDefinitions intents = new IntentDefinitionsBuilder().build();
// Put the Intents operational data into the MD-SAL data store
WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
tx.put(LogicalDatastoreType.OPERATIONAL, INTENTS_NBI_IID, intents);
// Perform the tx.submit asynchronously
Futures.addCallback(tx.submit(), new FutureCallback<Void>() {
@Override
public void onSuccess(final Void result) {
LOG.info("Init Intents-NBI Operational: transaction succeeded");
LOG.info("Init Intents-NBI Operational: status populated: {}", intents);
}
@Override
public void onFailure(final Throwable throwable) {
LOG.error("Init Intents-NBI Operational: transaction failed: " + throwable);
}
});
}
/**
* Populates Intents' default config data into the MD-SAL configuration data
* store. Note the database write to the tree are done in a synchronous
* fashion
*/
protected void initIntentNBIConfiguration() throws TransactionCommitFailedException {
// Build the default Intents config data
final IntentDefinitions intents = new IntentDefinitionsBuilder().build();
// Place default config data in data store tree
final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
tx.put(LogicalDatastoreType.CONFIGURATION, INTENTS_NBI_IID, intents);
// Perform the tx.submit synchronously
CheckedFuture<Void, TransactionCommitFailedException> result = tx.submit();
result.checkedGet();
LOG.info("Init Intents-NBI Configuration: default config populated: {}", intents);
}
/**
* Populates Intents' initial operational data into the MD-SAL operational
* data store.
*/
protected void initIntentsOperational() {
// Build the initial intents operational data
Intents intents = new IntentsBuilder().build();
// Put the Intents operational data into the MD-SAL data store
WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
tx.put(LogicalDatastoreType.OPERATIONAL, IntentUtils.INTENTS_IID, intents);
// Perform the tx.submit asynchronously
Futures.addCallback(tx.submit(), new FutureCallback<Void>() {
@Override
public void onSuccess(final Void result) {
LOG.info("initIntentsOperational: transaction succeeded");
}
@Override
public void onFailure(final Throwable throwable) {
LOG.error("initIntentsOperational: transaction failed");
}
});
LOG.info("initIntentsOperational: operational status populated: {}", intents);
}
/**
* Populates Intents' default config data into the MD-SAL configuration data
* store. Note the database write to the tree are done in a synchronous
* fashion
*/
protected void initIntentsConfiguration() {
// Build the default Intents config data
Intents intents = new IntentsBuilder().build();
// Place default config data in data store tree
WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
tx.put(LogicalDatastoreType.CONFIGURATION, IntentUtils.INTENTS_IID, intents);
// Perform the tx.submit synchronously
tx.submit();
UUID uuid = UUID.randomUUID();
Intent intent = new IntentBuilder().
setId(new Uuid(uuid.toString()))
.build();
addIntent(intent);
removeIntent(intent.getId());
LOG.info("initIntentsConfiguration: default config populated: {}", intents);
}
@Override
public boolean addIntent(Intent intent) {
Intents intents;
List<Intent> listOfIntents = listIntents(true);
try {
listOfIntents.add(intent);
intents = new IntentsBuilder().setIntent(listOfIntents).build();
// Place default config data in data store tree
WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
tx.put(LogicalDatastoreType.CONFIGURATION, IntentUtils.INTENTS_IID, intents);
// Perform the tx.submit synchronously
tx.submit();
} catch (Exception e) {
LOG.error("addIntent: failed: {}", e);
return false;
}
return true;
}
@Override
public boolean addIntent(IntentLimiter intentLimiter) {
boolean result = false;
IntentsLimiter intentsLimiter;
List<IntentLimiter> limiterList = listIntentLimiters();
try {
limiterList.add(intentLimiter);
intentsLimiter = new IntentsLimiterBuilder().setIntentLimiter(limiterList).build();
WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
writeTransaction.put(LogicalDatastoreType.CONFIGURATION, IntentUtils.INTENTS_LIMITER_IDD, intentsLimiter);
writeTransaction.submit();
result = true;
} catch (Exception e) {
LOG.error("addIntent: failed: {}", e);
}
return result;
}
@Override
public boolean addIntents(Intents intents) {
// TODO MultiAdd will be added in a further commit
return false;
}
@Override
public boolean removeIntent(Uuid id) {
try {
InstanceIdentifier<Intent> iid = InstanceIdentifier.create(Intents.class)
.child(Intent.class, new IntentKey(id));
// Removes default config data in data store tree
WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
tx.delete(LogicalDatastoreType.CONFIGURATION, iid);
// Perform the tx.submit synchronously
tx.submit();
} catch (Exception e) {
LOG.info("RemoveIntent: failed: {}", e);
return false;
}
return true;
}
@Override
public boolean removeIntents(List<Uuid> intents) {
// TODO MultiRemove will be added in a further commit
return false;
}
@Override
public boolean enableIntent(Uuid id) {
LOG.info("Enabling intent with ID: {}.",id.toString());
//TODO: Call Intent state machine
return false;
}
@Override
public boolean disableIntent(Uuid id) {
LOG.info("Disabling intent with ID: {}.", id.toString());
//TODO: Call Intent state machine
return false;
}
@Override
public List<Intent> listIntents(boolean isConfigurationDatastore) {
List<Intent> listOfIntents = null;
try {
ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction();
Optional<Intents> intents = tx.read((isConfigurationDatastore) ? LogicalDatastoreType.CONFIGURATION
: LogicalDatastoreType.OPERATIONAL, IntentUtils.INTENTS_IID).checkedGet();
if (intents.isPresent()) {
listOfIntents = intents.get().getIntent();
}
else {
LOG.info("Intent tree was empty!");
}
} catch (Exception e) {
LOG.error("ListIntents: failed: {}", e.getMessage(), e);
}
if (listOfIntents == null) {
listOfIntents = new ArrayList<Intent>();
}
LOG.info("ListIntentsConfiguration: list of intents retrieved successfully");
return listOfIntents;
}
private List<IntentLimiter> listIntentLimiters() {
List<IntentLimiter> listOfIntents = Lists.newArrayList();
try {
ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction();
Optional<IntentsLimiter> intents = readOnlyTransaction.read(LogicalDatastoreType.CONFIGURATION,
IntentUtils.INTENTS_LIMITER_IDD).checkedGet();
if(intents.isPresent()) {
listOfIntents = intents.get().getIntentLimiter();
}
} catch (Exception e) {
}
return listOfIntents;
}
@Override
public Intent getIntent(Uuid id) {
Intent intent = null;
try {
InstanceIdentifier<Intent> iid = InstanceIdentifier.create(Intents.class)
.child(Intent.class, new IntentKey(id));
ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction();
intent = tx.read(LogicalDatastoreType.CONFIGURATION, iid).checkedGet().get();
if (intent == null) {
intent = tx.read(LogicalDatastoreType.OPERATIONAL, iid).checkedGet().get();
}
} catch (Exception e) {
LOG.error("getIntent: failed: {}", e);
return null;
}
LOG.info("getIntent: Intent retrieved sucessfully");
return intent;
}
@Override
public String compile() {
List<Intent> intents = listIntents(true);
IntentCompiler compiler = IntentCompilerFactory.createIntentCompiler();
BasicAction allow = new BasicAction(ACTION_ALLOW, ActionConflictType.COMPOSABLE);
BasicAction block = new BasicAction(ACTION_BLOCK, ActionConflictType.EXCLUSIVE);
BasicAction redirect = new BasicAction(ACTION_REDIRECT, ActionConflictType.COMPOSABLE);
BasicAction mirror = new BasicAction(ACTION_MIRROR, ActionConflictType.COMPOSABLE);
BasicAction log = new BasicAction(ACTION_LOG, ActionConflictType.COMPOSABLE);
Collection<Policy> policies = new LinkedList<>();
for (Intent intent : intents) {
EndPointGroup sourceContainer = (EndPointGroup) intent.getSubjects().get(0).getSubject();
EndPointGroup destinationContainer = (EndPointGroup) intent.getSubjects().get(1).getSubject();
org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.Action actionContainer =
(org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.Action)
intent.getActions().get(0).getAction();
String sourceSubject = sourceContainer.getEndPointGroup().getName();
String destinationSubject = destinationContainer.getEndPointGroup().getName();
Set<Endpoint> sources;
try {
sources = translateSubject(compiler, sourceSubject);
} catch (UnknownHostException e) {
LOG.error("Invalid source subject: {}", sourceSubject, e);
return "[ERROR] Invalid subject: " + sourceSubject;
}
Set<Endpoint> destinations;
try {
destinations = translateSubject(compiler, destinationSubject);
} catch (UnknownHostException e) {
LOG.error("Invalid destination subject: {}", destinationSubject, e);
return "[ERROR] Invalid subject: " + destinationSubject;
}
Action action;
if (actionContainer instanceof Allow) {
action = allow;
} else if (actionContainer instanceof Block) {
action = block;
} else if (actionContainer instanceof Redirect) {
action = redirect;
} else if (actionContainer instanceof Mirror) {
action = mirror;
} else if (actionContainer instanceof Log) {
action = log;
} else {
String actionClass = actionContainer.getClass().getName();
LOG.error("Invalid action: {}", actionClass);
return "[ERROR] Invalid action: " + actionClass;
}
Set<Action> actions = new LinkedHashSet<>();
actions.add(action);
policies.add(compiler.createPolicy(sources, destinations, actions));
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(">>> Original policies:\n");
stringBuilder.append(formatPolicies(policies));
stringBuilder.append('\n');
stringBuilder.append(">>> Compiled policies:\n");
Collection<Policy> compiledPolicies;
try {
compiledPolicies = compiler.compile(policies);
} catch (IntentCompilerException e) {
LOG.error("Compilation failure", e);
StringBuilder builder = new StringBuilder();
builder.append("[ERROR] Compilation failure: ");
builder.append(e.getMessage());
builder.append("\nRelated policies:\n");
for (Policy policy : e.getRelatedPolicies()) {
builder.append(" ");
builder.append(policy.toString());
}
builder.append('\n');
return builder.toString();
}
stringBuilder.append(formatPolicies(compiledPolicies));
return stringBuilder.toString();
}
@Override
// Function Overloading Method to compile use PGA Graph Algorithm
public String compile(int flag) {
// TODO Graph compiler should listen on Intent creation event
List<Intent> intents = listIntents(true);
CompilerGraph compiler = CompilerGraphFactory.createGraphCompiler();
Collection<InputGraph> policies = new LinkedList<>();
//Create a collection of input graphs
for (Intent intent : intents) {
// Setting the uuid of the intent for the graph
IntentIds id = (IntentIds) intent.getId();
Set<IntentIds> ids = new HashSet<>();
ids.add(id);
//Setting the source and destination nodes
EndPointGroup sourceContainer = (EndPointGroup) intent.getSubjects().get(0).getSubject();
EndPointGroup destinationContainer = (EndPointGroup) intent.getSubjects().get(1).getSubject();
org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.Action actionContainer =
(org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.Action)
intent.getActions().get(0).getAction();
String sourceSubject = sourceContainer.getEndPointGroup().getName();
String destinationSubject = destinationContainer.getEndPointGroup().getName();
Set<Nodes> sources;
// Creates nodes of the graph from sources
try {
sources = translateSubject(compiler, sourceSubject);
} catch (UnknownHostException e) {
LOG.error("Invalid source subject: {}", sourceSubject, e);
return "[ERROR] Invalid subject: " + sourceSubject;
}
// Creates nodes of the graph from sources
Set<Nodes> destinations;
try {
destinations = translateSubject(compiler, destinationSubject);
} catch (UnknownHostException e) {
LOG.error("Invalid destination subject: {}", destinationSubject, e);
return "[ERROR] Invalid subject: " + destinationSubject;
}
Edges action;
// Creation of an edge
if (actionContainer instanceof Allow) {
Edges allow = new EdgesBuilder().setType(EdgeTypes.MustAllow).setActionType(ActionTypes.Composable)
.setSrcNode(sourceSubject).setDstNode(destinationSubject).build();
action = allow;
} else if (actionContainer instanceof Block) {
Edges block = new EdgesBuilder().setType(EdgeTypes.MustDeny).setActionType(ActionTypes.Exclusive)
.setSrcNode(sourceSubject).setDstNode(destinationSubject).build();
action = block;
} else if (actionContainer instanceof Redirect) {
Edges redirect = new EdgesBuilder().setType(EdgeTypes.CanAllow).setActionType(ActionTypes.Composable)
.setSrcNode(sourceSubject).setDstNode(destinationSubject).build();
action = redirect;
} else if (actionContainer instanceof Mirror) {
Edges mirror = new EdgesBuilder().setType(EdgeTypes.CanAllow).setActionType(ActionTypes.Composable)
.setSrcNode(sourceSubject).setDstNode(destinationSubject).build();
action = mirror;
} else if (actionContainer instanceof Log) {
Edges log = new EdgesBuilder().setType(EdgeTypes.CanAllow).setActionType(ActionTypes.Composable)
.setSrcNode(sourceSubject).setDstNode(destinationSubject).build();
action = log;
} else {
String actionClass = actionContainer.getClass().getName();
LOG.error("Invalid action: {}", actionClass);
return "[ERROR] Invalid action: " + actionClass;
}
Set<Edges> actions = new LinkedHashSet<>();
actions.add(action);
// Create input graphs
policies.add(compiler.createGraph(ids, sources, destinations, actions));
}
// Convert this to compiler graph results
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(">>> Original policies:\n");
stringBuilder.append(formatPolicies(policies, 1));
stringBuilder.append('\n');
stringBuilder.append(">>> Compiled policies:\n");
Collection<InputGraph> compiledPolicies;
try {
compiledPolicies = compiler.compile(policies);
} catch (CompilerGraphException e) {
LOG.error("Compilation failure", e);
StringBuilder builder = new StringBuilder();
builder.append("[ERROR] Compilation failure: ");
builder.append(e.getMessage());
builder.append("\nRelated policies:\n");
for (InputGraph policy : e.getRelatedPolicies()) {
builder.append(" ");
builder.append(policy.toString());
}
builder.append('\n');
return builder.toString();
}
// Save the compiler graph as MD-SAL graph
// TODO add Intent uuids of compiled policies
Collection<Graph> composedGraph = compiler.storeComposedGraph(compiledPolicies);
stringBuilder.append(formatPolicies(compiledPolicies, 1));
// Return the string of compiledPolices
return stringBuilder.toString();
}
private Set<Endpoint> translateSubject(IntentCompiler compiler, String sourceSubject) throws UnknownHostException {
StringBuilder csv = new StringBuilder();
Map<String, String> innerMap = mappingSvc.get(sourceSubject);
for (String ipAddress : innerMap.values()) {
if (csv.length() == 0) csv.append(ipAddress);
else csv.append(",").append(ipAddress);
}
return compiler.parseEndpointGroup(sourceSubject);
}
private Set<Nodes> translateSubject(CompilerGraph compiler, String sourceSubject) throws UnknownHostException {
StringBuilder csv = new StringBuilder();
Map<String, String> innerMap = mappingSvc.get(sourceSubject);
for (String ipAddress : innerMap.values()) {
if (csv.length() == 0) csv.append(ipAddress);
else csv.append(",").append(ipAddress);
}
return compiler.parseEndpointGroup(sourceSubject);
}
private String formatPolicies(Collection<Policy> policies) {
StringBuilder stringBuilder = new StringBuilder();
for (Policy policy : policies) {
stringBuilder.append(policy.toString());
stringBuilder.append('\n');
}
return stringBuilder.toString();
}
private String formatPolicies(Collection<InputGraph> policies, int flag) {
StringBuilder stringBuilder = new StringBuilder();
for (InputGraph policy : policies) {
stringBuilder.append("From ");
for (Nodes node : policy.src()) {
stringBuilder.append(node.getName());
}
stringBuilder.append(" To ");
for (Nodes node : policy.dst()) {
stringBuilder.append(node.getName());
}
stringBuilder.append(" apply ");
for (Edges edge : policy.action()) {
stringBuilder.append(edge.getType());
}
stringBuilder.append('\n');
}
return stringBuilder.toString();
}
}