From 13d05bc8458758ee39cb829098241e89616717ee Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Wed, 9 Sep 2015 22:15:21 -0700 Subject: ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60 Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd --- framework/src/onos/providers/netconf/app/app.xml | 24 ++ .../src/onos/providers/netconf/app/features.xml | 27 ++ framework/src/onos/providers/netconf/app/pom.xml | 48 +++ .../src/onos/providers/netconf/device/pom.xml | 165 ++++++++ .../netconf/device/impl/NetconfDevice.java | 304 +++++++++++++++ .../netconf/device/impl/NetconfDeviceProvider.java | 358 ++++++++++++++++++ .../provider/netconf/device/impl/package-info.java | 20 + .../device/impl/NetconfDeviceProviderTest.java | 421 +++++++++++++++++++++ .../impl/NetconfDeviceProviderTestConstant.java | 46 +++ framework/src/onos/providers/netconf/flow/pom.xml | 259 +++++++++++++ .../netconf/flow/impl/NetconfFlowRuleProvider.java | 403 ++++++++++++++++++++ .../netconf/flow/impl/NetconfOperation.java | 139 +++++++ .../provider/netconf/flow/impl/XmlBuilder.java | 223 +++++++++++ .../provider/netconf/flow/impl/package-info.java | 21 + framework/src/onos/providers/netconf/pom.xml | 49 +++ 15 files changed, 2507 insertions(+) create mode 100644 framework/src/onos/providers/netconf/app/app.xml create mode 100644 framework/src/onos/providers/netconf/app/features.xml create mode 100644 framework/src/onos/providers/netconf/app/pom.xml create mode 100644 framework/src/onos/providers/netconf/device/pom.xml create mode 100644 framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDevice.java create mode 100644 framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java create mode 100644 framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/package-info.java create mode 100644 framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java create mode 100644 framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTestConstant.java create mode 100644 framework/src/onos/providers/netconf/flow/pom.xml create mode 100644 framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfFlowRuleProvider.java create mode 100644 framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java create mode 100644 framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/XmlBuilder.java create mode 100644 framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/package-info.java create mode 100644 framework/src/onos/providers/netconf/pom.xml (limited to 'framework/src/onos/providers/netconf') diff --git a/framework/src/onos/providers/netconf/app/app.xml b/framework/src/onos/providers/netconf/app/app.xml new file mode 100644 index 00000000..f2d47627 --- /dev/null +++ b/framework/src/onos/providers/netconf/app/app.xml @@ -0,0 +1,24 @@ + + + + ${project.description} + + mvn:${project.groupId}/onos-netconf-provider-device/${project.version} + + diff --git a/framework/src/onos/providers/netconf/app/features.xml b/framework/src/onos/providers/netconf/app/features.xml new file mode 100644 index 00000000..6a3d1a2c --- /dev/null +++ b/framework/src/onos/providers/netconf/app/features.xml @@ -0,0 +1,27 @@ + + + + mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features + + onos-api + mvn:io.netty/netty/3.9.2.Final + mvn:${project.groupId}/onos-netconf-provider-device/${project.version} + + + + diff --git a/framework/src/onos/providers/netconf/app/pom.xml b/framework/src/onos/providers/netconf/app/pom.xml new file mode 100644 index 00000000..8cf56b3f --- /dev/null +++ b/framework/src/onos/providers/netconf/app/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + + org.onosproject + onos-netconf-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-netconf + pom + + NetConf protocol southbound providers + + + + org.onosproject + onos-netconf-provider-device + ${project.version} + + + org.onosproject + onos-netconf-provider-flow + ${project.version} + + + + + diff --git a/framework/src/onos/providers/netconf/device/pom.xml b/framework/src/onos/providers/netconf/device/pom.xml new file mode 100644 index 00000000..be05e3cd --- /dev/null +++ b/framework/src/onos/providers/netconf/device/pom.xml @@ -0,0 +1,165 @@ + + + + 4.0.0 + + + org.onosproject + onos-netconf-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-netconf-provider-device + bundle + + ONOS Netconf protocol device provider + + + + org.osgi + org.osgi.compendium + + + ch.ethz.ganymed + ganymed-ssh2 + 262 + + + + org.onosproject + jnc + 1.0 + + + org.jdom + jdom2 + 2.0.5 + + + jaxen + jaxen + 1.1.4 + true + + + org.osgi + org.osgi.core + + + org.onosproject + onlab-junit + test + + + org.easymock + easymock + test + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + + + com.tailf:JNC + + com/tailf/jnc/** + + + + ch.ethz.ganymed:ganymed-ssh2 + + ch/ethz/ssh2/** + + + + org.jdom:jdom2 + + org/jdom2/** + + + + + + + package + + shade + + + + + + org.apache.felix + maven-scr-plugin + + + org.apache.felix + maven-bundle-plugin + + + + com.tailf.jnc, + ch.ethz.ssh2, + ch.ethz.ssh2.auth, + ch.ethz.ssh2.channel, + ch.ethz.ssh2.crypto, + ch.ethz.ssh2.crypto.cipher, + ch.ethz.ssh2.crypto.dh, + ch.ethz.ssh2.crypto.digest, + ch.ethz.ssh2.log, + ch.ethz.ssh2.packets, + ch.ethz.ssh2.server, + ch.ethz.ssh2.sftp, + ch.ethz.ssh2.signature, + ch.ethz.ssh2.transport, + ch.ethz.ssh2.util, + org.jdom2, + org.jdom2.input, + org.jdom2.output, + org.jdom2.adapters, + org.jdom2.filter, + org.jdom2.internal, + org.jdom2.located, + org.jdom2.transform, + org.jdom2.util, + org.jdom2.xpath, + org.jdom2.input.sax, + org.jdom2.input.stax, + org.jdom2.output.support, + org.jdom2.xpath.jaxen, + org.jdom2.xpath.util + + + + + + org.onosproject + onos-maven-plugin + + + + + diff --git a/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDevice.java b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDevice.java new file mode 100644 index 00000000..b3d26b06 --- /dev/null +++ b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDevice.java @@ -0,0 +1,304 @@ +/* + * Copyright 2015 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.provider.netconf.device.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.onlab.util.Tools.delay; +import static org.slf4j.LoggerFactory.getLogger; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.input.SAXBuilder; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.slf4j.Logger; + +import com.tailf.jnc.Capabilities; +import com.tailf.jnc.JNCException; +import com.tailf.jnc.SSHConnection; +import com.tailf.jnc.SSHSession; + +/** + * This is a logical representation of actual NETCONF device, carrying all the + * necessary information to connect and execute NETCONF operations. + */ +public class NetconfDevice { + private final Logger log = getLogger(NetconfDevice.class); + + /** + * The Device State is used to determine whether the device is active or + * inactive. This state infomation will help Device Creator to add or delete + * the device from the core. + */ + public static enum DeviceState { + /* Used to specify Active state of the device */ + ACTIVE, + /* Used to specify inactive state of the device */ + INACTIVE, + /* Used to specify invalid state of the device */ + INVALID + } + + private static final int DEFAULT_SSH_PORT = 22; + private static final int DEFAULT_CON_TIMEOUT = 0; + private static final String XML_CAPABILITY_KEY = "capability"; + private static final int EVENTINTERVAL = 2000; + private static final int CONNECTION_CHECK_INTERVAL = 3; + private static final String INPUT_HELLO_XML_MSG = new StringBuilder( + "") + .append("") + .append("urn:ietf:params:netconf:base:1.0") + .append("").toString(); + + private String sshHost; + private int sshPort = DEFAULT_SSH_PORT; + private int connectTimeout = DEFAULT_CON_TIMEOUT; + private String username; + private String password; + private boolean reachable = false; + + private List capabilities = new ArrayList(); + private SSHConnection sshConnection = null; + + private DeviceState deviceState = DeviceState.INVALID; + + protected NetconfDevice(String sshHost, int sshPort, String username, + String password) { + this.username = checkNotNull(username, + "Netconf Username Cannot be null"); + this.sshHost = checkNotNull(sshHost, "Netconf Device IP cannot be null"); + this.sshPort = checkNotNull(sshPort, + "Netconf Device SSH port cannot be null"); + this.password = password; + } + + /** + * This will try to connect to NETCONF device and find all the capabilities. + * + * @throws Exception if unable to connect to the device + */ + // FIXME: this should not be a generic Exception; perhaps wrap in some RuntimeException + public void init() throws Exception { + try { + if (sshConnection == null) { + sshConnection = new SSHConnection(sshHost, sshPort, connectTimeout); + sshConnection.authenticateWithPassword(username, password); + } + // Send hello message to retrieve capabilities. + } catch (IOException e) { + log.error("Fatal Error while creating connection to the device: " + + deviceInfo(), e); + throw e; + } catch (JNCException e) { + log.error("Failed to connect to the device: " + deviceInfo(), e); + throw e; + } + + hello(); + } + + private void hello() { + SSHSession ssh = null; + try { + ssh = new SSHSession(sshConnection); + String helloRequestXML = INPUT_HELLO_XML_MSG.trim(); + + log.debug("++++++++++++++++++++++++++++++++++Sending Hello: " + + sshConnection.getGanymedConnection().getHostname() + + "++++++++++++++++++++++++++++++++++"); + printPrettyXML(helloRequestXML); + ssh.print(helloRequestXML); + // ssh.print(endCharSeq); + ssh.flush(); + String xmlResponse = null; + int i = CONNECTION_CHECK_INTERVAL; + while (!ssh.ready() && i > 0) { + delay(EVENTINTERVAL); + i--; + } + + if (ssh.ready()) { + StringBuffer readOne = ssh.readOne(); + if (readOne == null) { + log.error("The Hello Contains No Capabilites"); + throw new JNCException( + JNCException.SESSION_ERROR, + "server does not support NETCONF base capability: " + + Capabilities.NETCONF_BASE_CAPABILITY); + } else { + xmlResponse = readOne.toString().trim(); + + log.debug("++++++++++++++++++++++++++++++++++Reading Capabilities: " + + sshConnection.getGanymedConnection() + .getHostname() + + "++++++++++++++++++++++++++++++++++"); + + printPrettyXML(xmlResponse); + processCapabilities(xmlResponse); + } + } + reachable = true; + } catch (IOException e) { + log.error("Fatal Error while sending Hello Message to the device: " + + deviceInfo(), e); + } catch (JNCException e) { + log.error("Fatal Error while sending Hello Message to the device: " + + deviceInfo(), e); + } finally { + log.debug("Closing the session after successful execution"); + if (ssh != null) { + ssh.close(); + } + } + } + + private void processCapabilities(String xmlResponse) throws JNCException { + if (xmlResponse.isEmpty()) { + log.error("The capability response cannot be empty"); + throw new JNCException( + JNCException.SESSION_ERROR, + "server does not support NETCONF base capability: " + + Capabilities.NETCONF_BASE_CAPABILITY); + } + try { + Document doc = new SAXBuilder() + .build(new StringReader(xmlResponse)); + Element rootElement = doc.getRootElement(); + processCapabilities(rootElement); + } catch (Exception e) { + log.error("ERROR while parsing the XML " + xmlResponse); + } + } + + private void processCapabilities(Element rootElement) { + List children = rootElement.getChildren(); + if (children.isEmpty()) { + return; + } + for (Element child : children) { + + if (child.getName().equals(XML_CAPABILITY_KEY)) { + capabilities.add(child.getValue()); + } + if (!child.getChildren().isEmpty()) { + processCapabilities(child); + } + } + } + + private void printPrettyXML(String xmlstring) { + try { + Document doc = new SAXBuilder().build(new StringReader(xmlstring)); + XMLOutputter xmOut = new XMLOutputter(Format.getPrettyFormat()); + String outputString = xmOut.outputString(doc); + log.debug(outputString); + } catch (Exception e) { + log.error("ERROR while parsing the XML " + xmlstring, e); + + } + } + + /** + * This would return host IP and host Port, used by this particular Netconf + * Device. + * @return Device Information. + */ + public String deviceInfo() { + return new StringBuilder("host: ").append(sshHost).append(". port: ") + .append(sshPort).toString(); + } + + /** + * This will terminate the device connection. + */ + public void disconnect() { + sshConnection.close(); + reachable = false; + } + + /** + * This will list down all the capabilities supported on the device. + * @return Capability list. + */ + public List getCapabilities() { + return capabilities; + } + + /** + * This api is intended to know whether the device is connected or not. + * @return true if connected + */ + public boolean isReachable() { + return reachable; + } + + /** + * This will return the IP used connect ssh on the device. + * @return Netconf Device IP + */ + public String getSshHost() { + return sshHost; + } + + /** + * This will return the SSH Port used connect the device. + * @return SSH Port number + */ + public int getSshPort() { + return sshPort; + } + + /** + * The usename used to connect Netconf Device. + * @return Device Username + */ + public String getUsername() { + return username; + } + + /** + * Retrieve current state of the device. + * @return Current Device State + */ + public DeviceState getDeviceState() { + return deviceState; + } + + /** + * This is set the state information for the device. + * @param deviceState Next Device State + */ + public void setDeviceState(DeviceState deviceState) { + this.deviceState = deviceState; + } + + /** + * Check whether the device is in Active state. + * @return true if the device is Active + */ + public boolean isActive() { + return deviceState == DeviceState.ACTIVE ? true : false; + } + + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } +} diff --git a/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java new file mode 100644 index 00000000..f9194a7e --- /dev/null +++ b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java @@ -0,0 +1,358 @@ +/* + * Copyright 2015 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.provider.netconf.device.impl; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.onlab.util.Tools.delay; +import static org.onlab.util.Tools.get; +import static org.onlab.util.Tools.groupedThreads; +import static org.slf4j.LoggerFactory.getLogger; + +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Dictionary; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +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.Modified; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onlab.packet.ChassisId; +import org.onosproject.cfg.ComponentConfigService; +import org.onosproject.cluster.ClusterService; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.device.DefaultDeviceDescription; +import org.onosproject.net.device.DeviceDescription; +import org.onosproject.net.device.DeviceProvider; +import org.onosproject.net.device.DeviceProviderRegistry; +import org.onosproject.net.device.DeviceProviderService; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.provider.netconf.device.impl.NetconfDevice.DeviceState; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; + +/** + * Provider which will try to fetch the details of NETCONF devices from the core + * and run a capability discovery on each of the device. + */ +@Component(immediate = true) +public class NetconfDeviceProvider extends AbstractProvider + implements DeviceProvider { + + private final Logger log = getLogger(NetconfDeviceProvider.class); + + protected Map netconfDeviceMap = new ConcurrentHashMap(); + + private DeviceProviderService providerService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceProviderRegistry providerRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ClusterService clusterService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ComponentConfigService cfgService; + + private ExecutorService deviceBuilder = Executors + .newFixedThreadPool(1, groupedThreads("onos/netconf", "device-creator")); + + // Delay between events in ms. + private static final int EVENTINTERVAL = 5; + + private static final String SCHEME = "netconf"; + + @Property(name = "devConfigs", value = "", label = "Instance-specific configurations") + private String devConfigs = null; + + @Property(name = "devPasswords", value = "", label = "Instance-specific password") + private String devPasswords = null; + + /** + * Creates a provider with the supplier identifier. + */ + public NetconfDeviceProvider() { + super(new ProviderId("netconf", "org.onosproject.provider.netconf")); + } + + @Activate + public void activate(ComponentContext context) { + cfgService.registerProperties(getClass()); + providerService = providerRegistry.register(this); + modified(context); + log.info("Started"); + } + + @Deactivate + public void deactivate(ComponentContext context) { + cfgService.unregisterProperties(getClass(), false); + try { + for (Entry deviceEntry : netconfDeviceMap + .entrySet()) { + deviceBuilder.submit(new DeviceCreator(deviceEntry.getValue(), + false)); + } + deviceBuilder.awaitTermination(1000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + log.error("Device builder did not terminate"); + } + deviceBuilder.shutdownNow(); + netconfDeviceMap.clear(); + providerRegistry.unregister(this); + providerService = null; + log.info("Stopped"); + } + + @Modified + public void modified(ComponentContext context) { + if (context == null) { + log.info("No configuration file"); + return; + } + Dictionary properties = context.getProperties(); + String deviceCfgValue = get(properties, "devConfigs"); + log.info("Settings: devConfigs={}", deviceCfgValue); + if (!isNullOrEmpty(deviceCfgValue)) { + addOrRemoveDevicesConfig(deviceCfgValue); + } + } + + private void addOrRemoveDevicesConfig(String deviceConfig) { + for (String deviceEntry : deviceConfig.split(",")) { + NetconfDevice device = processDeviceEntry(deviceEntry); + if (device != null) { + log.info("Device Detail: username: {}, host={}, port={}, state={}", + device.getUsername(), device.getSshHost(), + device.getSshPort(), device.getDeviceState().name()); + if (device.isActive()) { + deviceBuilder.submit(new DeviceCreator(device, true)); + } else { + deviceBuilder.submit(new DeviceCreator(device, false)); + } + } + } + } + + private NetconfDevice processDeviceEntry(String deviceEntry) { + if (deviceEntry == null) { + log.info("No content for Device Entry, so cannot proceed further."); + return null; + } + log.info("Trying to convert Device Entry String: " + deviceEntry + + " to a Netconf Device Object"); + NetconfDevice device = null; + try { + String userInfo = deviceEntry.substring(0, deviceEntry + .lastIndexOf('@')); + String hostInfo = deviceEntry.substring(deviceEntry + .lastIndexOf('@') + 1); + String[] infoSplit = userInfo.split(":"); + String username = infoSplit[0]; + String password = infoSplit[1]; + infoSplit = hostInfo.split(":"); + String hostIp = infoSplit[0]; + Integer hostPort; + try { + hostPort = Integer.parseInt(infoSplit[1]); + } catch (NumberFormatException nfe) { + log.error("Bad Configuration Data: Failed to parse host port number string: " + + infoSplit[1]); + throw nfe; + } + String deviceState = infoSplit[2]; + if (isNullOrEmpty(username) || isNullOrEmpty(password) + || isNullOrEmpty(hostIp) || hostPort == 0) { + log.warn("Bad Configuration Data: both user and device information parts of Configuration " + + deviceEntry + " should be non-nullable"); + } else { + device = new NetconfDevice(hostIp, hostPort, username, password); + if (!isNullOrEmpty(deviceState)) { + if (deviceState.toUpperCase().equals(DeviceState.ACTIVE + .name())) { + device.setDeviceState(DeviceState.ACTIVE); + } else if (deviceState.toUpperCase() + .equals(DeviceState.INACTIVE.name())) { + device.setDeviceState(DeviceState.INACTIVE); + } else { + log.warn("Device State Information can not be empty, so marking the state as INVALID"); + device.setDeviceState(DeviceState.INVALID); + } + } else { + log.warn("The device entry do not specify state information, so marking the state as INVALID"); + device.setDeviceState(DeviceState.INVALID); + } + } + } catch (ArrayIndexOutOfBoundsException aie) { + log.error("Error while reading config infromation from the config file: " + + "The user, host and device state infomation should be " + + "in the order 'userInfo@hostInfo:deviceState'" + + deviceEntry, aie); + } catch (Exception e) { + log.error("Error while parsing config information for the device entry: " + + deviceEntry, e); + } + return device; + } + + @Override + public void triggerProbe(DeviceId deviceId) { + // TODO Auto-generated method stub + } + + @Override + public void roleChanged(DeviceId deviceId, MastershipRole newRole) { + + } + + @Override + public boolean isReachable(DeviceId deviceId) { + NetconfDevice netconfDevice = netconfDeviceMap.get(deviceId); + if (netconfDevice == null) { + log.warn("BAD REQUEST: the requested device id: " + + deviceId.toString() + + " is not associated to any NETCONF Device"); + return false; + } + return netconfDevice.isReachable(); + } + + /** + * This class is intended to add or remove Configured Netconf Devices. + * Functionality relies on 'createFlag' and 'NetconfDevice' content. The + * functionality runs as a thread and dependening on the 'createFlag' value + * it will create or remove Device entry from the core. + */ + private class DeviceCreator implements Runnable { + + private NetconfDevice device; + private boolean createFlag; + + public DeviceCreator(NetconfDevice device, boolean createFlag) { + this.device = device; + this.createFlag = createFlag; + } + + @Override + public void run() { + if (createFlag) { + log.info("Trying to create Device Info on ONOS core"); + advertiseDevices(); + } else { + log.info("Trying to remove Device Info on ONOS core"); + removeDevices(); + } + } + + /** + * For each Netconf Device, remove the entry from the device store. + */ + private void removeDevices() { + if (device == null) { + log.warn("The Request Netconf Device is null, cannot proceed further"); + return; + } + try { + DeviceId did = getDeviceId(); + if (!netconfDeviceMap.containsKey(did)) { + log.error("BAD Request: 'Currently device is not discovered, " + + "so cannot remove/disconnect the device: " + + device.deviceInfo() + "'"); + return; + } + providerService.deviceDisconnected(did); + device.disconnect(); + netconfDeviceMap.remove(did); + delay(EVENTINTERVAL); + } catch (URISyntaxException uriSyntaxExcpetion) { + log.error("Syntax Error while creating URI for the device: " + + device.deviceInfo() + + " couldn't remove the device from the store", + uriSyntaxExcpetion); + } + } + + /** + * Initialize Netconf Device object, and notify core saying device + * connected. + */ + private void advertiseDevices() { + try { + if (device == null) { + log.warn("The Request Netconf Device is null, cannot proceed further"); + return; + } + device.init(); + DeviceId did = getDeviceId(); + ChassisId cid = new ChassisId(); + DeviceDescription desc = new DefaultDeviceDescription( + did.uri(), + Device.Type.OTHER, + "", "", + "", "", + cid); + log.info("Persisting Device" + did.uri().toString()); + + netconfDeviceMap.put(did, device); + providerService.deviceConnected(did, desc); + log.info("Done with Device Info Creation on ONOS core. Device Info: " + + device.deviceInfo() + " " + did.uri().toString()); + delay(EVENTINTERVAL); + } catch (URISyntaxException e) { + log.error("Syntax Error while creating URI for the device: " + + device.deviceInfo() + + " couldn't persist the device onto the store", e); + } catch (SocketTimeoutException e) { + log.error("Error while setting connection for the device: " + + device.deviceInfo(), e); + } catch (IOException e) { + log.error("Error while setting connection for the device: " + + device.deviceInfo(), e); + } catch (Exception e) { + log.error("Error while initializing session for the device: " + + (device != null ? device.deviceInfo() : null), e); + } + } + + /** + * This will build a device id for the device. + */ + private DeviceId getDeviceId() throws URISyntaxException { + String additionalSSP = new StringBuilder(device.getUsername()) + .append("@").append(device.getSshHost()).append(":") + .append(device.getSshPort()).toString(); + DeviceId did = DeviceId.deviceId(new URI(SCHEME, additionalSSP, + null)); + return did; + } + } +} diff --git a/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/package-info.java b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/package-info.java new file mode 100644 index 00000000..583aeaa2 --- /dev/null +++ b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 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. + */ + +/** + * Provider that uses Netconf capability request as a means of infrastructure device discovery. + */ +package org.onosproject.provider.netconf.device.impl; \ No newline at end of file diff --git a/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java b/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java new file mode 100644 index 00000000..e56c5959 --- /dev/null +++ b/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java @@ -0,0 +1,421 @@ +/* + * Copyright 2015 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.provider.netconf.device.impl; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertFalse; +import static org.onlab.util.Tools.delay; +import static org.onosproject.provider.netconf.device.impl.NetconfDeviceProviderTestConstant.*; +import static org.slf4j.LoggerFactory.getLogger; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collection; +import java.util.Dictionary; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.onlab.packet.ChassisId; +import org.onosproject.cfg.ComponentConfigService; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.device.DefaultDeviceDescription; +import org.onosproject.net.device.DeviceDescription; +import org.onosproject.net.device.DeviceProvider; +import org.onosproject.net.device.DeviceProviderRegistry; +import org.onosproject.net.device.DeviceProviderService; +import org.onosproject.net.device.PortDescription; +import org.onosproject.net.device.PortStatistics; +import org.onosproject.net.provider.ProviderId; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; + +import com.tailf.jnc.JNCException; + +/** + * Test Case to Validate Netconf Device Provider. + */ +public class NetconfDeviceProviderTest { + TestDeviceCreator create; + + private final Logger log = getLogger(NetconfDeviceProviderTest.class); + + private Map netconfDeviceMap = new ConcurrentHashMap(); + + private DeviceProviderService providerService; + + private static final DeviceId DID1 = DeviceId.deviceId(DEVICE_ID); + + private final NetconfDeviceProvider provider = new NetconfDeviceProvider(); + private final TestDeviceRegistry registry = new TestDeviceRegistry(); + + private ComponentConfigService mockCfgService; + + @Before + public void setUp() { + mockCfgService = EasyMock.createMock(ComponentConfigService.class); + provider.cfgService = mockCfgService; + provider.providerRegistry = registry; + } + + @SuppressWarnings("unchecked") + private Dictionary getDictionaryMockWithoutValues(ComponentContext componentContext) { + Dictionary dictionary = EasyMock + .createMock(Dictionary.class); + expect(dictionary.get(DEV_CONFIG)).andReturn(NULL); + replay(dictionary); + expect(componentContext.getProperties()).andReturn(dictionary); + return dictionary; + } + + @SuppressWarnings("unchecked") + private Dictionary getDictionaryMockWithDeviceEntryNull(ComponentContext componentContext) { + Dictionary dictionary = EasyMock + .createMock(Dictionary.class); + expect(dictionary.get(DEV_CONFIG)).andReturn(NULL_NULL); + replay(dictionary); + expect(componentContext.getProperties()).andReturn(dictionary); + return dictionary; + } + + @SuppressWarnings("unchecked") + private Dictionary getDictionaryMockDeviceEntryNumberFomatEx(ComponentContext componentContext) { + Dictionary dictionary = EasyMock + .createMock(Dictionary.class); + expect(dictionary.get(DEV_CONFIG)) + .andReturn(CONFIG_WITH_INVALID_ENTRY_NUMBER) + .andThrow(new NumberFormatException()); + replay(dictionary); + expect(componentContext.getProperties()).andReturn(dictionary); + return dictionary; + } + + @SuppressWarnings("unchecked") + private Dictionary getDictionaryMockWithoutUsernameAndPassword(ComponentContext componentContext) { + Dictionary dictionary = EasyMock + .createMock(Dictionary.class); + expect(dictionary.get(DEV_CONFIG)).andReturn(CONFIG_WITH_NULL_ENTRY); + replay(dictionary); + expect(componentContext.getProperties()).andReturn(dictionary); + return dictionary; + } + + @SuppressWarnings("unchecked") + private Dictionary getDictionaryMockWithDifferentDeviceState(ComponentContext componentContext) { + Dictionary dictionary = EasyMock + .createMock(Dictionary.class); + expect(dictionary.get(DEV_CONFIG)) + .andReturn(CONFIG_WITH_DIFFERENT_DEVICE_STATE); + replay(dictionary); + expect(componentContext.getProperties()).andReturn(dictionary); + return dictionary; + } + + @SuppressWarnings("unchecked") + private Dictionary getDictionaryMockDeviceWithArrayOutOFBoundEx(ComponentContext componentContext) { + Dictionary dictionary = EasyMock + .createMock(Dictionary.class); + expect(dictionary.get(DEV_CONFIG)) + .andReturn(CONFIG_WITH_ARRAY_OUT_OF_BOUNDEX) + .andThrow(new ArrayIndexOutOfBoundsException()); + replay(dictionary); + expect(componentContext.getProperties()).andReturn(dictionary); + return dictionary; + } + + @SuppressWarnings("unchecked") + private Dictionary getDictionaryMockDeviceEntryForDeactivate(ComponentContext componentContext) { + Dictionary dictionary = EasyMock + .createMock(Dictionary.class); + expect(dictionary.get(DEV_CONFIG)) + .andReturn(CONFIG_ENTRY_FOR_DEACTIVATE) + .andThrow(new ArrayIndexOutOfBoundsException()); + replay(dictionary); + expect(componentContext.getProperties()).andReturn(dictionary); + return dictionary; + } + + @Ignore + @Test(expected = IOException.class) + public void testSSHAuthentication() throws IOException, JNCException { + TestDeviceCreator objForTestDev = new TestDeviceCreator( + new NetconfDevice( + DEVICE_IP, + DEVICE_PORT, + DEVICE_USERNAME, + DEVICE_PASSWORD), + true); + objForTestDev.run(); + } + + @After + public void tearDown() { + provider.providerRegistry = null; + provider.cfgService = null; + } + + // To check if deviceCfgValue is empty or null + @Test + public void testActiveWithcomponentContextIsNull() { + + ComponentContext componentContext = EasyMock + .createMock(ComponentContext.class); + getDictionaryMockWithoutValues(componentContext); + replay(componentContext); + provider.activate(componentContext); + } + + // To check deviceEntry and device is null + @Test + public void testActiveWithDeviceEntryIsNull() { + + ComponentContext componentContext = EasyMock + .createMock(ComponentContext.class); + getDictionaryMockWithDeviceEntryNull(componentContext); + replay(componentContext); + provider.activate(componentContext); + } + + @Test + public void testActiveWithDeviceEntryWithoutUsernameAndPassword() { + + ComponentContext componentContext = EasyMock + .createMock(ComponentContext.class); + getDictionaryMockWithoutUsernameAndPassword(componentContext); + replay(componentContext); + provider.activate(componentContext); + } + + @Test + public void testActiveWithDeviceEntryWithNumberFomatEx() { + + ComponentContext componentContext = EasyMock + .createMock(ComponentContext.class); + getDictionaryMockDeviceEntryNumberFomatEx(componentContext); + replay(componentContext); + provider.activate(componentContext); + } + + @Test + public void testActiveWithDeviceEntryWithDifferentDeviceState() { + + ComponentContext componentContext = EasyMock + .createMock(ComponentContext.class); + getDictionaryMockWithDifferentDeviceState(componentContext); + replay(componentContext); + provider.activate(componentContext); + } + + @Test + public void testActiveWithDeviceEntryWithArrayOutOFBoundEx() { + + ComponentContext componentContext = EasyMock + .createMock(ComponentContext.class); + getDictionaryMockDeviceWithArrayOutOFBoundEx(componentContext); + replay(componentContext); + provider.activate(componentContext); + } + + @Test + public void isReachableWithInvalidDeviceId() { + assertFalse("Initially the Device ID Should not be reachable", + provider.isReachable(DID1)); + NetconfDevice device = new NetconfDevice(NULL, ZERO, NULL, NULL); + provider.netconfDeviceMap.put(DID1, device); + assertFalse("Particular Device ID cannot be Reachable", + provider.isReachable(DID1)); + } + + @Test + public void testDeactivate() { + + ComponentContext componentContext = EasyMock + .createMock(ComponentContext.class); + getDictionaryMockDeviceEntryForDeactivate(componentContext); + replay(componentContext); + testActiveWithDeviceEntryWithDifferentDeviceState(); + provider.deactivate(componentContext); + } + + private class TestDeviceCreator { + + private NetconfDevice device; + private boolean createFlag; + + public TestDeviceCreator(NetconfDevice device, boolean createFlag) { + this.device = device; + this.createFlag = createFlag; + } + + public void run() throws JNCException, IOException { + if (createFlag) { + log.info("Trying to create Device Info on ONOS core"); + advertiseDevices(); + } else { + log.info("Trying to remove Device Info on ONOS core"); + removeDevices(); + } + } + + /** + * For each Netconf Device, remove the entry from the device store. + */ + private void removeDevices() { + if (device == null) { + log.warn("The Request Netconf Device is null, cannot proceed further"); + return; + } + try { + DeviceId did = getDeviceId(); + if (!netconfDeviceMap.containsKey(did)) { + log.error("BAD Request: 'Currently device is not discovered, " + + "so cannot remove/disconnect the device: " + + device.deviceInfo() + "'"); + return; + } + providerService.deviceDisconnected(did); + device.disconnect(); + netconfDeviceMap.remove(did); + delay(EVENTINTERVAL); + } catch (URISyntaxException uriSyntaxExcpetion) { + log.error("Syntax Error while creating URI for the device: " + + device.deviceInfo() + + " couldn't remove the device from the store", + uriSyntaxExcpetion); + } + } + + /** + * Initialize Netconf Device object, and notify core saying device + * connected. + */ + private void advertiseDevices() throws JNCException, IOException { + try { + if (device == null) { + log.warn("The Request Netconf Device is null, cannot proceed further"); + return; + } + device.init(); + DeviceId did = getDeviceId(); + ChassisId cid = new ChassisId(); + DeviceDescription desc = new DefaultDeviceDescription( + did.uri(), + Device.Type.OTHER, + NULL, + NULL, + NULL, + NULL, cid); + log.info("Persisting Device" + did.uri().toString()); + + netconfDeviceMap.put(did, device); + providerService.deviceConnected(did, desc); + log.info("Done with Device Info Creation on ONOS core. Device Info: " + + device.deviceInfo() + " " + did.uri().toString()); + delay(EVENTINTERVAL); + } catch (URISyntaxException e) { + log.error("Syntax Error while creating URI for the device: " + + device.deviceInfo() + + " couldn't persist the device onto the store", e); + } catch (JNCException e) { + throw e; + } catch (IOException e) { + throw e; + } catch (Exception e) { + log.error("Error while initializing session for the device: " + + device.deviceInfo(), e); + } + } + + private DeviceId getDeviceId() throws URISyntaxException { + String additionalSSP = new StringBuilder(device.getUsername()) + .append(AT_THE_RATE).append(device.getSshHost()) + .append(COLON).append(device.getSshPort()).toString(); + DeviceId did = DeviceId.deviceId(new URI(SCHEME_NETCONF, + additionalSSP, null)); + return did; + } + } + + private class TestDeviceRegistry implements DeviceProviderRegistry { + + @Override + public DeviceProviderService register(DeviceProvider provider) { + return new TestProviderService(); + } + + @Override + public void unregister(DeviceProvider provider) { + } + + @Override + public Set getProviders() { + return null; + } + + private class TestProviderService implements DeviceProviderService { + + @Override + public DeviceProvider provider() { + return null; + } + + @Override + public void deviceConnected(DeviceId deviceId, + DeviceDescription deviceDescription) { + } + + @Override + public void deviceDisconnected(DeviceId deviceId) { + + } + + @Override + public void updatePorts(DeviceId deviceId, + List portDescriptions) { + + } + + @Override + public void portStatusChanged(DeviceId deviceId, + PortDescription portDescription) { + + } + + @Override + public void receivedRoleReply(DeviceId deviceId, + MastershipRole requested, + MastershipRole response) { + + } + + @Override + public void updatePortStatistics(DeviceId deviceId, + Collection portStatistics) { + + } + } + } +} diff --git a/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTestConstant.java b/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTestConstant.java new file mode 100644 index 00000000..1d848e26 --- /dev/null +++ b/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTestConstant.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015 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.provider.netconf.device.impl; + +public final class NetconfDeviceProviderTestConstant { + + private NetconfDeviceProviderTestConstant() { + } + + public static final int ZERO = 0; + public static final int EVENTINTERVAL = 5; + public static final String DEV_CONFIG = "devConfigs"; + public static final String CONFIG_WITH_INVALID_ENTRY_NUMBER = "cisco:cisco" + + "@10.18.11.14:cisco:active"; + public static final String CONFIG_WITH_NULL_ENTRY = "null:null@null:0:active"; + public static final String CONFIG_WITH_DIFFERENT_DEVICE_STATE = "cisco:cisco@10.18.11.14:22:active," + + "cisco:cisco@10.18.11.18:22:inactive,cisco:cisco@10.18.11.14:22:invalid," + + "cisco:cisco@10.18.11.14:22:null"; + public static final String CONFIG_WITH_ARRAY_OUT_OF_BOUNDEX = "@10.18.11.14:22:active"; + public static final String CONFIG_ENTRY_FOR_DEACTIVATE = "netconf:cisco" + + "@10.18.11.14:22:active"; + public static final String DEVICE_IP = "10.18.14.19"; + public static final int DEVICE_PORT = 22; + public static final String DEVICE_USERNAME = "cisco"; + public static final String DEVICE_PASSWORD = "cisco"; + public static final String AT_THE_RATE = "@"; + public static final String COLON = ":"; + public static final String NULL = ""; + public static final String NULL_NULL = "null,null"; + public static final String SCHEME_NETCONF = "netconf"; + public static final String DEVICE_ID = "of:0000000000000001"; + +} diff --git a/framework/src/onos/providers/netconf/flow/pom.xml b/framework/src/onos/providers/netconf/flow/pom.xml new file mode 100644 index 00000000..8ee4c4e4 --- /dev/null +++ b/framework/src/onos/providers/netconf/flow/pom.xml @@ -0,0 +1,259 @@ + + + + 4.0.0 + + + org.onosproject + onos-netconf-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-netconf-provider-flow + bundle + + + + org.osgi + org.osgi.compendium + + + ch.ethz.ganymed + ganymed-ssh2 + 262 + + + + org.onosproject + jnc + 1.0 + + + org.jdom + jdom2 + 2.0.5 + + + jaxen + jaxen + 1.1.4 + true + + + org.osgi + org.osgi.core + + + org.onosproject + onlab-junit + test + + + org.easymock + easymock + test + + + org.onosproject + onos-yang-tool + 1.3 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + + + com.tailf:JNC + + com/tailf/jnc/** + + + + ch.ethz.ganymed:ganymed-ssh2 + + ch/ethz/ssh2/** + + + + org.jdom:jdom2 + + org/jdom2/** + + + + org.onosproject:onos-yang-tool + + org/opendaylight/yang/gen/** + + + + + + + package + + shade + + + + + + org.apache.felix + maven-scr-plugin + + + org.apache.felix + maven-bundle-plugin + + + + com.tailf.jnc, + ch.ethz.ssh2, + ch.ethz.ssh2.auth, + ch.ethz.ssh2.channel, + ch.ethz.ssh2.crypto, + ch.ethz.ssh2.crypto.cipher, + ch.ethz.ssh2.crypto.dh, + ch.ethz.ssh2.crypto.digest, + ch.ethz.ssh2.log, + ch.ethz.ssh2.packets, + ch.ethz.ssh2.server, + ch.ethz.ssh2.sftp, + ch.ethz.ssh2.signature, + ch.ethz.ssh2.transport, + ch.ethz.ssh2.util, + org.jdom2, + org.jdom2.input, + org.jdom2.output, + org.jdom2.adapters, + org.jdom2.filter, + org.jdom2.internal, + org.jdom2.located, + org.jdom2.transform, + org.jdom2.util, + org.jdom2.xpath, + org.jdom2.input.sax, + org.jdom2.input.stax, + org.jdom2.output.support, + org.jdom2.xpath.jaxen, + org.jdom2.xpath.util, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520, + org.opendaylight.yangtools.yang.data.impl.schema.tree, + org.opendaylight.yangtools.yang.data.impl.codec, + org.opendaylight.yangtools.yang.model.parser.api, + org.opendaylight.yangtools.yang.data.impl.schema.nodes, + org.opendaylight.yangtools.yang.binding.util, + org.opendaylight.yangtools.yang.data.impl, + org.opendaylight.yangtools.sal.binding.generator.impl, + org.opendaylight.yangtools.yang.parser.impl.util, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625, + org.opendaylight.yangtools.yang.data.api, + org.opendaylight.yangtools.objcache.spi, + org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser, + org.opendaylight.yangtools.maven.sal.api.gen.plugin, + org.opendaylight.yangtools.yang.data.impl.schema.builder.impl, + org.opendaylight.yangtools.yang.data.api.schema.tree, + org.opendaylight.yangtools.binding.generator.util, + org.opendaylight.yangtools.sal.binding.generator.spi, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715, + org.opendaylight.yangtools.yang2sources.spi, + org.opendaylight.yangtools.yang.model.repo.api, + org.opendaylight.yangtools.util, + org.opendaylight.yangtools.yang.parser.util, + org.opendaylight.yangtools.yang.data.api.schema.stream, + org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer, + org.opendaylight.yangtools.concepts, + org.opendaylight.yangtools.yang.binding, + org.opendaylight.yangtools.yang.model.util.repo, + org.opendaylight.yangtools.yang.wadl.generator.maven, + org.opendaylight.yangtools.yang.data.api.schema, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type, + org.opendaylight.yangtools.concepts.util, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version, + org.opendaylight.yangtools.sal.binding.model.api, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip, + org.opendaylight.yangtools.yang.data.impl.schema.builder.api, + org.opendaylight.yangtools.util.concurrent, + org.opendaylight.yangtools.yang.parser.builder.impl, + org.opendaylight.yangtools.yang.data.impl.schema.transform.base, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.actions.packet.handling, + org.opendaylight.yangtools.sal.binding.model.api.type.builder, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields, + org.opendaylight.yangtools.yang2sources.plugin, + org.opendaylight.yangtools.yang.data.impl.codec.xml, + org.opendaylight.yangtools.antlrv4.code.gen, + org.opendaylight.yangtools.yang.parser.builder.util, + org.opendaylight.yangtools.yang.data.impl.schema.transform, + org.opendaylight.yangtools.yang.model.api.type, + org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer, + org.opendaylight.yangtools.yang.data.api.schema.tree.spi, + org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser, + org.opendaylight.yangtools.sal.binding.yang.types, + org.opendaylight.yangtools.yang.data.impl.schema.transform.dom, + org.opendaylight.yangtools.yang.data.impl.util, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.timerange, + org.opendaylight.yangtools.sal.binding.generator.api, + org.opendaylight.yangtools.sal.java.api.generator, + org.opendaylight.yangtools.yang.binding.annotations, + org.opendaylight.yangtools.sal.binding.generator.util, + org.opendaylight.yangtools.yang.model.repo.util, + org.opendaylight.yangtools.yang.model.api, + org.opendaylight.yangtools.yang.common, + org.opendaylight.yangtools.yang.wadl.generator, + org.opendaylight.yangtools.yang.parser.builder.api, + org.opendaylight.yangtools.yang.model.util, + org.opendaylight.yangtools.yang.parser.impl, + org.opendaylight.yangtools.yang.data.impl.schema, + org.opendaylight.yangtools.yang.data.api.codec, + org.opendaylight.yangtools.yang.unified.doc.generator, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list, + org.opendaylight.yangtools.objcache, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.actions, + org.opendaylight.yangtools.yang.data.util, + org.opendaylight.yangtools.yang.unified.doc.generator.maven, + org.opendaylight.yangtools.binding.generator.util.generated.type.builder, + org.opendaylight.yangtools.yang.model.repo.spi, + org.opendaylight.yangtools.yang.parser.repo, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches, + org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid, + com.romix.scala, + com.romix.scala.collection, + com.romix.scala.collection.concurrent, + org.opendaylight.yangtools.objcache.impl + + + + + + org.onosproject + onos-maven-plugin + + + + + diff --git a/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfFlowRuleProvider.java b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfFlowRuleProvider.java new file mode 100644 index 00000000..b29d687e --- /dev/null +++ b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfFlowRuleProvider.java @@ -0,0 +1,403 @@ +/* + * Copyright 2015 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.provider.netconf.flow.impl; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.onlab.util.Tools.get; +import static org.slf4j.LoggerFactory.getLogger; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +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.Modified; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.util.Timer; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.flow.FlowEntry; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleBatchOperation; +import org.onosproject.net.flow.FlowRuleProvider; +import org.onosproject.net.flow.FlowRuleProviderRegistry; +import org.onosproject.net.flow.FlowRuleProviderService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.AccessList; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.AccessListBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.AccessListEntries; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.AccessListEntriesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.ActionsBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.Matches; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.MatchesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.actions.packet.handling.DenyBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.actions.packet.handling.PermitBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.AceIp; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.AceIpBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version.AceIpv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version.AceIpv4Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.DestinationPortRange; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.DestinationPortRangeBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.SourcePortRange; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.SourcePortRangeBuilder; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; + +/** + * Netconf provider to accept any flow and report them. + */ +@Component(immediate = true) +public class NetconfFlowRuleProvider extends AbstractProvider + implements FlowRuleProvider { + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowRuleProviderRegistry providerRegistry; + + private ConcurrentMap> flowTable = new ConcurrentHashMap<>(); + + private FlowRuleProviderService providerService; + + private XmlBuilder xmlBuilder; + + private AceIp aceIp; + private SourcePortRange srcPortRange; + private DestinationPortRange destPortRange; + private Matches matches; + private HashedWheelTimer timer = Timer.getTimer(); + private Timeout timeout; + private static final String ACL_NAME_KEY = "acl-name"; + private static final String ACL_LIST_ENTRIES_RULE_NAME_KEY = "access-list-entries.rule-name"; + private static final String ACL_LIST_SP_LOWER_KEY = "source-port-range.lower-port"; + private static final String ACL_LIST_SP_UPPER_KEY = "source-port-range.upper-port"; + private static final String ACL_LIST_DP_LOWER_KEY = "destination-port-range.lower-port"; + private static final String ACL_LIST_DP_UPPER_KEY = "destination-port-range.upper-port"; + private static final String ACL_LIST_DEST_IPV4_KEY = "matches.destination-ipv4-address"; + private static final String ACL_LIST_SRC_IPV4_KEY = "matches.source-ipv4-address"; + private static final String ACL_LIST_ACTIONS_KEY = "actions"; + + public NetconfFlowRuleProvider() { + super(new ProviderId("netconf", "org.onosproject.provider.netconf")); + } + + @Activate + public void activate(ComponentContext context) { + providerService = providerRegistry.register(this); + timeout = timer.newTimeout(new StatisticTask(), 5, TimeUnit.SECONDS); + applyRule(); + modified(context); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + providerRegistry.unregister(this); + providerService = null; + timeout.cancel(); + log.info("Stopped"); + } + + @Modified + public void modified(ComponentContext context) { + if (xmlBuilder == null) { + xmlBuilder = new XmlBuilder(); + } + if (context == null) { + log.info("No configuration file"); + return; + } + Dictionary properties = context.getProperties(); + String deviceEntry = get(properties, "devConfigs"); + log.info("Settings: devConfigs={}", deviceEntry); + Enumeration elements = properties.keys(); + Object nextElement = elements.nextElement(); + while (elements.hasMoreElements()) { + if (nextElement instanceof String) { + log.info("key::" + nextElement + ", value::" + + get(properties, (String) nextElement)); + } + nextElement = elements.nextElement(); + } + if (!isNullOrEmpty(deviceEntry)) { + Map deviceMap = processDeviceEntry(deviceEntry); + AccessList accessList = buildAccessList(properties); + String xmlMsg = xmlBuilder.buildAclRequestXml(accessList); + log.info("The resultant xml from the builder\n" + xmlMsg); + NetconfOperation netconfOperation = new NetconfOperation(); + netconfOperation.sendXmlMessage(xmlMsg, deviceMap.get("username"), + deviceMap.get("password"), + deviceMap.get("hostIp"), Integer + .parseInt(deviceMap + .get("hostPort"))); + } + } + + /** + * @param properties + * @return accessList + */ + private AccessList buildAccessList(Dictionary properties) { + /** + * Populating Access List. + */ + AccessListBuilder abuilder = new AccessListBuilder(); + String aclName = get(properties, ACL_NAME_KEY); + if (aclName != null) { + abuilder.setAclName(aclName); + } + AccessList accessList = abuilder.build(); + abuilder.setAccessListEntries(getAccessListEntries(properties, matches)); + srcPortRange = getSourcePortRange(properties); + destPortRange = getDestinationPortRange(properties); + aceIp = getAceIp(properties, srcPortRange, destPortRange); + matches = getMatches(properties); + return accessList; + } + + /** + * @param properties + * @return matches + */ + private Matches getMatches(Dictionary properties) { + /** + * Building Matches for given ACL model. + */ + MatchesBuilder matchesBuilder = new MatchesBuilder(); + if (aceIp != null) { + matchesBuilder.setAceType(aceIp); + } + matches = matchesBuilder.build(); + return matches; + } + + /** + * @param properties + * @return srcPortRange + */ + private SourcePortRange getSourcePortRange(Dictionary properties) { + /** + * Building Source Port Range for given ACL model. + */ + String spRangeLowerStr = get(properties, ACL_LIST_SP_LOWER_KEY); + String spRangeUpperStr = get(properties, ACL_LIST_SP_UPPER_KEY); + SourcePortRangeBuilder srcPortRangeBuilder = new SourcePortRangeBuilder(); + if (spRangeLowerStr != null) { + int spRangeLower = Integer.parseInt(spRangeLowerStr); + srcPortRangeBuilder.setLowerPort(new PortNumber(spRangeLower)); + } + if (spRangeUpperStr != null) { + int spRangeUpper = Integer.parseInt(spRangeUpperStr); + srcPortRangeBuilder.setUpperPort(new PortNumber(spRangeUpper)); + } + srcPortRange = srcPortRangeBuilder.build(); + return srcPortRange; + } + + /** + * @param properties + * @return destPortRange + */ + private DestinationPortRange getDestinationPortRange(Dictionary properties) { + /** + * Building Destination Port Range for given ACL model. + */ + String dpRangeLowerStr = get(properties, ACL_LIST_DP_LOWER_KEY); + String dpRangeUpperStr = get(properties, ACL_LIST_DP_UPPER_KEY); + DestinationPortRangeBuilder destPortRangeBuilder = new DestinationPortRangeBuilder(); + if (dpRangeLowerStr != null) { + int dpRangeLower = Integer.parseInt(dpRangeLowerStr); + destPortRangeBuilder.setLowerPort(new PortNumber(dpRangeLower)); + } + if (dpRangeUpperStr != null) { + int dpRangeUpper = Integer.parseInt(dpRangeUpperStr); + destPortRangeBuilder.setUpperPort(new PortNumber(dpRangeUpper)); + } + destPortRange = destPortRangeBuilder.build(); + return destPortRange; + } + + /** + * @param properties + * @return accessListEntries + */ + private List getAccessListEntries(Dictionary properties, + Matches matches) { + /** + * Build and Populate Access List Entries. + */ + AccessListEntriesBuilder acLListEntriesBuilder = new AccessListEntriesBuilder(); + String aclListEntriesRuleName = get(properties, + ACL_LIST_ENTRIES_RULE_NAME_KEY); + if (aclListEntriesRuleName != null) { + acLListEntriesBuilder.setRuleName(aclListEntriesRuleName); + } + acLListEntriesBuilder.setMatches(matches); + String aclActions = get(properties, ACL_LIST_ACTIONS_KEY); + if (aclActions != null) { + ActionsBuilder actionBuilder = new ActionsBuilder(); + if (aclActions.equalsIgnoreCase("deny")) { + DenyBuilder denyBuilder = new DenyBuilder(); + actionBuilder.setPacketHandling(denyBuilder.build()); + } else if (aclActions.equalsIgnoreCase("permit")) { + PermitBuilder permitBuilder = new PermitBuilder(); + actionBuilder.setPacketHandling(permitBuilder.build()); + } + acLListEntriesBuilder.setActions(actionBuilder.build()); + } + AccessListEntries aclListEntries = acLListEntriesBuilder.build(); + List accessListEntries = new ArrayList(); + accessListEntries.add(aclListEntries); + return accessListEntries; + } + + /** + * @param properties + * @return aceIp + */ + private AceIp getAceIp(Dictionary properties, + SourcePortRange srcPortRange, + DestinationPortRange destPortRange) { + /** + * Building Ace IPV4 Type + */ + String destIpv4 = get(properties, ACL_LIST_DEST_IPV4_KEY); + String srcIpv4 = get(properties, ACL_LIST_SRC_IPV4_KEY); + AceIpv4Builder aceIpv4Builder = new AceIpv4Builder(); + aceIp = null; + if (destIpv4 != null) { + Ipv4Prefix destinationIp = new Ipv4Prefix(destIpv4); + aceIpv4Builder.setDestinationIpv4Address(destinationIp); + } + if (srcIpv4 != null) { + Ipv4Prefix sourceIp = new Ipv4Prefix(srcIpv4); + aceIpv4Builder.setSourceIpv4Address(sourceIp); + } + if (destIpv4 != null || srcIpv4 != null) { + AceIpv4 aceIpv4 = aceIpv4Builder.build(); + AceIpBuilder aceIpBuilder = new AceIpBuilder(); + aceIpBuilder.setAceIpVersion(aceIpv4); + aceIpBuilder.setSourcePortRange(srcPortRange); + aceIpBuilder.setDestinationPortRange(destPortRange); + aceIp = aceIpBuilder.build(); + } + return aceIp; + } + + /** + * @param deviceEntry + * @return deviceMap + */ + private Map processDeviceEntry(String deviceEntry) { + if (deviceEntry == null) { + log.info("No content for Device Entry, so cannot proceed further."); + return null; + } + + Map deviceMap = new HashMap(); + log.info("Trying to convert Device Entry String: " + deviceEntry + + " to a Netconf Device Object"); + try { + URI uri = new URI(deviceEntry); + String path = uri.getPath(); + String userInfo = path.substring(path.lastIndexOf('@')); + String hostInfo = path.substring(path.lastIndexOf('@') + 1); + String[] infoSplit = userInfo.split(":"); + String username = infoSplit[0]; + String password = infoSplit[1]; + infoSplit = hostInfo.split(":"); + String hostIp = infoSplit[0]; + String hostPort = infoSplit[1]; + if (isNullOrEmpty(username) || isNullOrEmpty(password) + || isNullOrEmpty(hostIp) || isNullOrEmpty(hostPort)) { + log.warn("Bad Configuration Data: both user and device" + + " information parts of Configuration " + deviceEntry + + " should be non-nullable"); + } else { + deviceMap.put("hostIp", hostIp); + deviceMap.put("hostPort", hostPort); + deviceMap.put("username", username); + deviceMap.put("password", password); + } + } catch (ArrayIndexOutOfBoundsException aie) { + log.error("Error while reading config infromation from the config file: " + + "The user, host and device state infomation should be " + + "in the order 'userInfo@hostInfo:deviceState'" + + deviceEntry, aie); + } catch (URISyntaxException urie) { + log.error("Error while parsing config information for the device entry: " + + "Illegal character in path " + deviceEntry, + urie); + } catch (Exception e) { + log.error("Error while parsing config information for the device entry: " + + deviceEntry, e); + } + return deviceMap; + } + + @Override + public void applyFlowRule(FlowRule... flowRules) { + } + + @Override + public void removeFlowRule(FlowRule... flowRules) { + } + + private void applyRule() { + // applyFlowRule(flowRules);//currentl + } + + @Override + public void removeRulesById(ApplicationId id, FlowRule... flowRules) { + log.info("removal by app id not supported in null provider"); + } + + @Override + public void executeBatch(FlowRuleBatchOperation batch) { + + } + + private class StatisticTask implements TimerTask { + + @Override + public void run(Timeout to) throws Exception { + for (DeviceId devId : flowTable.keySet()) { + providerService.pushFlowMetrics(devId, flowTable + .getOrDefault(devId, Collections.emptySet())); + } + timeout = timer.newTimeout(to.getTask(), 5, TimeUnit.SECONDS); + + } + } +} diff --git a/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java new file mode 100644 index 00000000..d03e75ac --- /dev/null +++ b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java @@ -0,0 +1,139 @@ +/* + * Copyright 2015 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.provider.netconf.flow.impl; + +import static org.onlab.util.Tools.delay; +import static org.slf4j.LoggerFactory.getLogger; + +import java.io.IOException; + +import org.slf4j.Logger; + +import com.tailf.jnc.Capabilities; +import com.tailf.jnc.JNCException; +import com.tailf.jnc.SSHConnection; +import com.tailf.jnc.SSHSession; + +/** + * This is to carry necessary information to connect and execute NETCONF + * operations. + */ +public class NetconfOperation { + private final Logger log = getLogger(NetconfOperation.class); + private static final int EVENTINTERVAL = 2000; + private static final int CONNECTION_CHECK_INTERVAL = 3; + private static final String INPUT_HELLO_XML_MSG = new StringBuilder( + "") + .append("") + .append("urn:ietf:params:netconf:base:1.0") + .append("").toString(); + + /** + * This will send a Xml message to the device. + * @param xmlMsg XML to send + * @param username user name + * @param password pass word + * @param deviceIp ip address of the device + * @param devicePort port on the device + */ + protected void sendXmlMessage(String xmlMsg, String username, + String password, String deviceIp, + Integer devicePort) { + SSHSession ssh = null; + try { + SSHConnection sshConnection = getConnection(username, password, + deviceIp, devicePort); + ssh = new SSHSession(sshConnection); + executeMessage(ssh, INPUT_HELLO_XML_MSG); + /* + * execute acl message + */ + executeMessage(ssh, xmlMsg); + + } catch (IOException e) { + log.error("Unable to send Hello Message to the device: ", e); + } catch (JNCException e) { + log.error("Authentication fail while sending Hello Message to the device: ", + e); + } catch (Exception e) { + log.error("Unable to send Hello Message to the device: ", e); + } finally { + log.debug("Closing the session after successful execution"); + ssh.close(); + } + } + + private void executeMessage(SSHSession ssh, String xmlMsg) + throws IOException, JNCException { + String helloRequestXML = xmlMsg.trim(); + + log.debug("Sending Hello"); + ssh.print(helloRequestXML); + ssh.flush(); + String xmlResponse = null; + int i = CONNECTION_CHECK_INTERVAL; + while (!ssh.ready() && i > 0) { + delay(EVENTINTERVAL); + i--; + } + + if (ssh.ready()) { + StringBuffer readOne = ssh.readOne(); + if (readOne == null) { + log.error("The Hello Contains No Capabilites"); + throw new JNCException( + JNCException.SESSION_ERROR, + "server does not support NETCONF base capability: " + + Capabilities.NETCONF_BASE_CAPABILITY); + } else { + xmlResponse = readOne.toString().trim(); + + log.debug("Reading Capabilities: " + + ssh.getSSHConnection().getGanymedConnection() + .getHostname()); + } + } + } + + /** + * To establish SSH Connection. + * + * @param username user name + * @param password pass word + * @param sshHost host + * @param sshPort port + * @return new SSH connection + * @throws IOException if connection fails + * @throws JNCException if connection causes an error + */ + public SSHConnection getConnection(String username, String password, + String sshHost, Integer sshPort) + throws IOException, JNCException { + SSHConnection sshConnection; + try { + sshConnection = new SSHConnection(sshHost, sshPort); + sshConnection.authenticateWithPassword(username, password); + } catch (IOException e) { + log.error("Unable to create a connection to the device: "); + throw e; + } catch (JNCException e) { + log.error("Failed to connect to the device: "); + throw e; + } + return sshConnection; + } + +} diff --git a/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/XmlBuilder.java b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/XmlBuilder.java new file mode 100644 index 00000000..389f1669 --- /dev/null +++ b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/XmlBuilder.java @@ -0,0 +1,223 @@ +/* + * Copyright 2015 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.provider.netconf.flow.impl; + +import static org.slf4j.LoggerFactory.getLogger; + +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.AccessList; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.AceType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.AceEth; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.AceIp; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.AceIpVersion; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version.AceIpv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version.AceIpv6; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.DestinationPortRange; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.SourcePortRange; +import org.slf4j.Logger; + +/** + * Xml Builder to generate the xml according to given ACL model. + */ +public class XmlBuilder { + private final Logger log = getLogger(XmlBuilder.class); + + public String buildAclRequestXml(AccessList accessList) { + Document doc = new Document(); + Namespace namespaceRpc = Namespace + .getNamespace("urn:ietf:params:xml:ns:netconf:base:1.0"); + Namespace accessNamespaceRpc = Namespace + .getNamespace("urn:ietf:params:xml:ns:yang:ietf-acl"); + doc.setRootElement(new Element("rpc", namespaceRpc) + .setAttribute("message-id", "101")); + + /** + * Access list elements of given ACL model. + */ + Element access = new Element("access-list", accessNamespaceRpc); + access.addContent(new Element("acl-name", accessNamespaceRpc) + .setText(accessList.getAclName())); + // access.addContent(accessEntries); + + if (!accessList.getAccessListEntries().isEmpty() + && accessList.getAccessListEntries() != null) { + for (int accessEntryIntVlu = 0; accessEntryIntVlu < accessList + .getAccessListEntries().size(); accessEntryIntVlu++) { + access.addContent(getAccessEntries(accessEntryIntVlu, + accessList, + accessNamespaceRpc)); + } + } + + /** + * edit-config operation for given ACL model. + */ + Element editConfig = new Element("edit-config", namespaceRpc); + editConfig.addContent(new Element("target", namespaceRpc) + .addContent(new Element("running", namespaceRpc))); + editConfig.addContent(new Element("config", Namespace + .getNamespace("urn:ietf:params:xml:ns:netconf:base:1.0")) + .addContent(access)); + + doc.getRootElement().addContent(editConfig); + XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat()); + String outputString = xmlOutputter.outputString(doc); + + return outputString; + } + + /** + * access entries operation for given ACL model. + */ + private Element getAccessEntries(int accessEntryIntVlu, + AccessList accessList, + Namespace accessNamespaceRpc) { + + /** + * Port Number + */ + + int srcPortRangeLower = 0; + int srcPortRangeUpper = 0; + int destPortRangeLower = 0; + int destPortRangeUpper = 0; + + String sourceIpAdd = ""; + String destinationIpAdd = ""; + + /* + * checking accessList is null or not + */ + if (accessList != null) { + /* + * checking list entries are empty or null + */ + if (!accessList.getAccessListEntries().isEmpty() + && accessList.getAccessListEntries() != null) { + AceType aceType = accessList.getAccessListEntries() + .get(accessEntryIntVlu).getMatches().getAceType(); + + if (aceType instanceof AceIp) { + AceIp aceIp = (AceIp) aceType; + SourcePortRange sourcePortRange = aceIp + .getSourcePortRange(); + if (sourcePortRange != null) { + PortNumber lowerPort = sourcePortRange.getLowerPort(); + PortNumber upperPort = sourcePortRange.getUpperPort(); + + if (lowerPort != null) { + srcPortRangeLower = lowerPort.getValue(); + } + if (upperPort != null) { + srcPortRangeUpper = upperPort.getValue(); + } + } + DestinationPortRange destinationPortRange = aceIp + .getDestinationPortRange(); + + if (destinationPortRange != null) { + PortNumber lowerPort = destinationPortRange + .getLowerPort(); + if (lowerPort != null) { + destPortRangeLower = lowerPort.getValue(); + } + + PortNumber upperPort = destinationPortRange + .getUpperPort(); + if (upperPort != null) { + destPortRangeUpper = upperPort.getValue(); + } + + } + + AceIpVersion aceIpVersion = aceIp.getAceIpVersion(); + if (aceIpVersion instanceof AceIpv4) { + AceIpv4 obj = (AceIpv4) aceIpVersion; + destinationIpAdd = obj.getDestinationIpv4Address() + .getValue(); + sourceIpAdd = obj.getSourceIpv4Address().getValue(); + } else if (aceIpVersion instanceof AceIpv6) { + AceIpv6 obj = (AceIpv6) aceIpVersion; + destinationIpAdd = obj.getDestinationIpv6Address() + .getValue(); + sourceIpAdd = obj.getSourceIpv6Address().getValue(); + } + } else if (aceType instanceof AceEth) { + log.debug("Need to add execution loging for Ace Type Ethernet"); + } + } + } + + /** + * Matches elements to define IP address & Port range for given ACL + * model. + */ + Element matchesElement = new Element("matches", accessNamespaceRpc); + if (String.valueOf(srcPortRangeLower) != null + && !String.valueOf(srcPortRangeLower).isEmpty()) { + + matchesElement.addContent(new Element("source-port-range", + accessNamespaceRpc) + .addContent(new Element("lower-port", accessNamespaceRpc) + .setText(String.valueOf(srcPortRangeLower)))); + + matchesElement.addContent(new Element("source-port-range", + accessNamespaceRpc) + .addContent(new Element("upper-port", accessNamespaceRpc) + .setText(String.valueOf(srcPortRangeUpper)))); + + matchesElement.addContent(new Element("destination-port-range", + accessNamespaceRpc) + .addContent(new Element("lower-port", accessNamespaceRpc) + .setText(String.valueOf(destPortRangeLower)))); + + matchesElement.addContent(new Element("destination-port-range", + accessNamespaceRpc) + .addContent(new Element("upper-port", accessNamespaceRpc) + .setText(String.valueOf(destPortRangeUpper)))); + } + + if (destinationIpAdd != null && !destinationIpAdd.isEmpty()) { + matchesElement.addContent(new Element("destination-ipv4-address", + accessNamespaceRpc) + .setText(destinationIpAdd)); + } + if (sourceIpAdd != null && !sourceIpAdd.isEmpty()) { + matchesElement.addContent(new Element("source-ipv4-address", + accessNamespaceRpc) + .setText(sourceIpAdd)); + } + + /** + * Access entries elements for given ACL model. + */ + Element accessEntries = new Element("access-list-entries", + accessNamespaceRpc); + accessEntries.addContent(new Element("rule-name", accessNamespaceRpc) + .setText(accessList.getAccessListEntries() + .get(accessEntryIntVlu).getRuleName())); + accessEntries.addContent(matchesElement); + accessEntries.addContent(new Element("actions", accessNamespaceRpc) + .addContent(new Element("deny", accessNamespaceRpc))); + + return accessEntries; + } +} diff --git a/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/package-info.java b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/package-info.java new file mode 100644 index 00000000..b095fc9a --- /dev/null +++ b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2014 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. + */ + +/** + * Provider that will accept any flow rules. + */ +package org.onosproject.provider.netconf.flow.impl; + diff --git a/framework/src/onos/providers/netconf/pom.xml b/framework/src/onos/providers/netconf/pom.xml new file mode 100644 index 00000000..ac08dd4a --- /dev/null +++ b/framework/src/onos/providers/netconf/pom.xml @@ -0,0 +1,49 @@ + + + + 4.0.0 + + + org.onosproject + onos-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-netconf-providers + pom + + ONOS Netconf protocol adapters + + + device + app + flow + + + + + org.onosproject + onos-api + tests + test + + + + -- cgit 1.2.3-korg