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 RemoveInterfaceFromLogicalRouterAction extends JunosAction { private String getSubInterfaceTemplate = "/VM_files/getSubInterface.vm"; private String copyInterfaceToLRTemplate = "/VM_files/mergeRawXML.vm"; private String deleteSubInterfaceTemplate = "/VM_files/deletesubinterface.vm"; public RemoveInterfaceFromLogicalRouterAction() { this.setActionID(ActionConstants.REMOVEINTERFACEFROMLOGICALROUTER); 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 // get interface from candidate JunosCommand getCommand = new GetNetconfCommand(prepareGetInterfaceMessage((ComputerSystem) params), TargetConfiguration.CANDIDATE); getCommand.initialize(); Response getInterfaceResponse = sendCommandToProtocol(getCommand, protocol); actionResponse.addResponse(getInterfaceResponse); if (getInterfaceResponse.getStatus().equals(Response.Status.OK)) { checkInterfaceExists(ifaceToAddName, logicalRouterName, getInterfaceResponse); // copy it to physical router String interfaceConfiguration = filterInterfaceConfiguration(getInterfaceResponse, ((ComputerSystem) params).getElementName()); JunosCommand editCommand = new EditNetconfCommand(prepareCopyInterfacePhysicalRouterMessage((ComputerSystem) params, interfaceConfiguration)); editCommand.initialize(); Response copyInterfaceResponse = sendCommandToProtocol(editCommand, protocol); actionResponse.addResponse(copyInterfaceResponse); if (copyInterfaceResponse.getStatus().equals(Response.Status.OK)) { // remove it from LR JunosCommand deleteCommand = new EditNetconfCommand(prepareDeleteInterfaceFromLRMessage((ComputerSystem) params), CommandNetconfConstants.NONE_OPERATION); deleteCommand.initialize(); actionResponse.addResponse(sendCommandToProtocol(deleteCommand, protocol)); } } 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, String logicalRouterName, 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 router " + logicalRouterName); } } private String prepareGetInterfaceMessage(ComputerSystem model) throws ActionException { NetworkPort iface = (NetworkPort) getOnlyElement(((ComputerSystem) params).getLogicalDevices(), null); Map<String, Object> extraParams = new HashMap<String, Object>(); extraParams.put("elementName", nullToEmpty(model.getElementName())); try { return prepareVelocityCommand(iface, getSubInterfaceTemplate, extraParams); } catch (Exception e) { throw new ActionException(e); } } private String prepareCopyInterfacePhysicalRouterMessage(ComputerSystem model, String interfaceConfiguration) throws ActionException { NetworkPort iface = (NetworkPort) getOnlyElement(((ComputerSystem) params).getLogicalDevices(), null); Map<String, Object> extraParams = new HashMap<String, Object>(); extraParams.put("elementName", nullToEmpty(((ComputerSystem) getModelToUpdate()).getElementName())); extraParams.put("xmlToMerge", interfaceConfiguration); try { return prepareVelocityCommand(iface, copyInterfaceToLRTemplate, extraParams); } catch (Exception e) { throw new ActionException(e); } } private String prepareDeleteInterfaceFromLRMessage(ComputerSystem model) throws ActionException { NetworkPort iface = (NetworkPort) getOnlyElement(((ComputerSystem) params).getLogicalDevices(), null); Map<String, Object> extraParams = new HashMap<String, Object>(); extraParams.put("elementName", nullToEmpty(model.getElementName())); try { return prepareVelocityCommand(iface, deleteSubInterfaceTemplate, extraParams); } catch (Exception e) { throw new ActionException(e); } } private String filterInterfaceConfiguration(Response getInterfaceResponse, String logicalRouterName) throws ActionException { try { Document completeResponseDoc = stringToDOM(getInterfaceResponse.getInformation()); Element interfaces = getInterfacesElement(completeResponseDoc, logicalRouterName); 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/logical-systems/name[text()="elementName"]/../interfaces element from given doc. * * @param doc * containing configuration * @return configuration/logical-systems/name[text()="elementName"]/../interfaces Element or null if it does not exist. * @throws XPathExpressionException */ private Element getInterfacesElement(Document doc, String logicalRouterName) throws XPathExpressionException { Element configElement = doc.getDocumentElement(); assert (configElement.getNodeName().equals("configuration")); XPath xpath = XPathFactory.newInstance().newXPath(); String xpathExpression = "/configuration/logical-systems/name[text()=\"" + logicalRouterName + "\"]/../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); } }