package org.opennaas.extensions.router.junos.actionssets.actions.logicalrouters;
import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.collect.Iterables.getOnlyElement;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.opennaas.core.resources.action.ActionException;
import org.opennaas.core.resources.action.ActionResponse;
import org.opennaas.core.resources.command.Response;
import org.opennaas.core.resources.protocol.IProtocolSession;
import org.opennaas.core.resources.protocol.ProtocolException;
import org.opennaas.extensions.router.junos.actionssets.ActionConstants;
import org.opennaas.extensions.router.junos.actionssets.actions.JunosAction;
import org.opennaas.extensions.router.junos.commandsets.commands.CommandNetconfConstants;
import org.opennaas.extensions.router.junos.commandsets.commands.CommandNetconfConstants.TargetConfiguration;
import org.opennaas.extensions.router.junos.commandsets.commands.EditNetconfCommand;
import org.opennaas.extensions.router.junos.commandsets.commands.GetNetconfCommand;
import org.opennaas.extensions.router.junos.commandsets.commands.JunosCommand;
import org.opennaas.extensions.router.model.ComputerSystem;
import org.opennaas.extensions.router.model.LogicalDevice;
import org.opennaas.extensions.router.model.NetworkPort;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class AddInterfaceToLogicalRouterAction extends JunosAction {
private String getSubInterfaceTemplate = "/VM_files/getSubInterface.vm";
private String copyInterfaceToLRTemplate = "/VM_files/mergeRawXML.vm";
private String deleteSubInterfaceTemplate = "/VM_files/deletesubinterface.vm";
public AddInterfaceToLogicalRouterAction() {
this.setActionID(ActionConstants.ADDINTERFACETOLOGICALROUTER);
this.protocolName = "netconf";
}
@Override
public boolean checkParams(Object params) throws ActionException {
if (!(params instanceof ComputerSystem))
return false;
if (((ComputerSystem) params).getElementName() == null ||
((ComputerSystem) params).getElementName().equals("")) {
return false;
}
try {
LogicalDevice interfaceToAdd = getOnlyElement(((ComputerSystem) params).getLogicalDevices(), null);
return (interfaceToAdd instanceof NetworkPort);
} catch (IllegalArgumentException e) {
// There is more than one LogicalDevice in ComputerSystem params
return false;
}
}
@Override
public void executeListCommand(ActionResponse actionResponse, IProtocolSession protocol) throws ActionException {
NetworkPort ifaceToAdd = (NetworkPort) getOnlyElement(((ComputerSystem) params).getLogicalDevices(), null);
String ifaceToAddName = ifaceToAdd.getName() + "." + ifaceToAdd.getPortNumber();
String logicalRouterName = nullToEmpty(((ComputerSystem) params).getElementName());
try {
// TODO Check LR is in opennaas resource manager and it is not active
Response getInterfaceResponse = getInterfaceFromCandidate(ifaceToAdd, protocol);
actionResponse.addResponse(getInterfaceResponse);
if (getInterfaceResponse.getStatus().equals(Response.Status.OK)) {
checkInterfaceExists(ifaceToAddName, getInterfaceResponse);
Response copyInterfaceToLRResponse = copyInterfaceToLR(ifaceToAdd, getInterfaceResponse, logicalRouterName, protocol);
actionResponse.addResponse(copyInterfaceToLRResponse);
if (copyInterfaceToLRResponse.getStatus().equals(Response.Status.OK)) {
// remove it from parent
Response deleteInterfaceResponse = deleteInterface(ifaceToAdd, protocol);
actionResponse.addResponse(deleteInterfaceResponse);
}
}
validateAction(actionResponse);
} catch (ProtocolException e) {
throw new ActionException(this.actionID, e);
}
}
@Override
public void parseResponse(Object responseMessage, Object model) throws ActionException {
// Nothing to parse
}
@Override
public void prepareMessage() throws ActionException {
// Nothing to prepare
// Messages are prepared in executeListCommand
}
private void checkInterfaceExists(String ifaceName, Response getInterfaceResponse) throws ActionException {
if (getInterfaceResponse.getInformation().equals("<configuration></configuration>")) {
// an empty configuration means filter has failed
throw new ActionException("Interface " + ifaceName + " not found in this router");
}
}
private Response getInterfaceFromCandidate(NetworkPort iface, IProtocolSession protocol) throws ActionException, ProtocolException {
String getInterfaceFilter = prepareGetInterfaceMessage(iface);
JunosCommand getCommand = new GetNetconfCommand(getInterfaceFilter, TargetConfiguration.CANDIDATE);
getCommand.initialize();
return sendCommandToProtocol(getCommand, protocol);
}
private Response copyInterfaceToLR(NetworkPort ifaceToAdd, Response getInterfaceResponse, String logicalRouterName, IProtocolSession protocol)
throws ActionException, ProtocolException {
String interfacesConfiguration = filterInterfaceConfiguration(getInterfaceResponse);
JunosCommand editCommand = new EditNetconfCommand(
prepareCopyInterfaceToLRMessage(interfacesConfiguration, logicalRouterName));
editCommand.initialize();
return sendCommandToProtocol(editCommand, protocol);
}
private Response deleteInterface(NetworkPort ifaceToAdd, IProtocolSession protocol) throws ActionException, ProtocolException {
JunosCommand deleteCommand = new EditNetconfCommand(prepareDeleteInterfaceMessage(ifaceToAdd),
CommandNetconfConstants.NONE_OPERATION);
deleteCommand.initialize();
return sendCommandToProtocol(deleteCommand, protocol);
}
private String prepareGetInterfaceMessage(NetworkPort ifaceToAdd) throws ActionException {
Map<String, Object> extraParams = new HashMap<String, Object>();
extraParams.put("elementName", nullToEmpty(((ComputerSystem) getModelToUpdate()).getElementName()));
try {
return prepareVelocityCommand(ifaceToAdd, getSubInterfaceTemplate, extraParams);
} catch (Exception e) {
throw new ActionException(e);
}
}
private String prepareCopyInterfaceToLRMessage(String interfaceConfiguration, String logicalRouterName) throws ActionException {
NetworkPort iface = (NetworkPort) getOnlyElement(((ComputerSystem) params).getLogicalDevices(), null);
Map<String, Object> extraParams = new HashMap<String, Object>();
extraParams.put("elementName", logicalRouterName);
extraParams.put("xmlToMerge", interfaceConfiguration);
try {
return prepareVelocityCommand(iface, copyInterfaceToLRTemplate, extraParams);
} catch (Exception e) {
throw new ActionException(e);
}
}
private String prepareDeleteInterfaceMessage(NetworkPort ifaceToDelete) throws ActionException {
Map<String, Object> extraParams = new HashMap<String, Object>();
extraParams.put("elementName", nullToEmpty(((ComputerSystem) getModelToUpdate()).getElementName()));
try {
return prepareVelocityCommand(ifaceToDelete, deleteSubInterfaceTemplate, extraParams);
} catch (Exception e) {
throw new ActionException(e);
}
}
private String filterInterfaceConfiguration(Response getInterfaceResponse) throws ActionException {
try {
Document completeResponseDoc = stringToDOM(getInterfaceResponse.getInformation());
Element interfaces = getInterfacesElement(completeResponseDoc);
if (interfaces == null)
throw new ActionException("Failed to get interfaces from configuration");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = factory.newDocumentBuilder();
Document filteredDocument = db.newDocument();
Node importedInterfaces = filteredDocument.importNode(interfaces, true);
filteredDocument.appendChild(importedInterfaces);
return domToString(filteredDocument);
} catch (Exception e) {
throw new ActionException("Failed to filter configuration", e);
}
}
/**
* Gets configuration/interfaces element from given doc.
*
* @param doc
* containing configuration
* @return configuration/interfaces Element or null if it does not exist.
* @throws XPathExpressionException
*/
private Element getInterfacesElement(Document doc) throws XPathExpressionException {
Element configElement = doc.getDocumentElement();
assert (configElement.getNodeName().equals("configuration"));
XPath xpath = XPathFactory.newInstance().newXPath();
String xpathExpression = "/configuration/interfaces";
Element interfaces = (Element) xpath.evaluate(xpathExpression, configElement, XPathConstants.NODE);
return interfaces;
}
private String domToString(Document doc) throws TransformerException, IOException {
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
transformer.setOutputProperty("omit-xml-declaration", "yes");
StringWriter writer = new StringWriter();
Result result = new StreamResult(writer);
Source source = new DOMSource(doc);
transformer.transform(source, result);
writer.close();
return writer.toString();
}
private Document stringToDOM(String xmlString) throws SAXException, IOException, ParserConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = factory.newDocumentBuilder();
InputSource inStream = new InputSource();
inStream.setCharacterStream(new StringReader(xmlString));
return db.parse(inStream);
}
}