Akihiro Yamanouchi
Committed by Gerrit Code Review

[ONOS-4747] NETCONF function for FUJITSU OLT #2

 - Enhanced device-setcontrollers command to apply additional key-value pair.
    e.g. onos> device-setcontrollers netconf:10.10.1.11:830 tcp:10.10.1.11:6630,ofconfig-id=1

Change-Id: I2cb5941dbd9829ade6fa89d5546bbc6aab44f83f
......@@ -18,7 +18,10 @@ package org.onosproject.cli.net;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.packet.IpAddress;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.net.Annotations;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DeviceId;
import org.onosproject.net.behaviour.ControllerConfig;
import org.onosproject.net.behaviour.ControllerInfo;
......@@ -52,7 +55,7 @@ public class DeviceSetControllersCommand extends AbstractShellCommand {
protected void execute() {
Arrays.asList(controllersListStrings).forEach(
cInfoString -> newControllers.add(new ControllerInfo(cInfoString)));
cInfoString -> newControllers.add(parseCInfoString(cInfoString)));
DriverService service = get(DriverService.class);
deviceId = DeviceId.deviceId(uri);
DriverHandler h = service.createHandler(deviceId);
......@@ -69,4 +72,31 @@ public class DeviceSetControllersCommand extends AbstractShellCommand {
print("size %d", config.getControllers().size());
}
private ControllerInfo parseCInfoString(String cInfoString) {
Annotations annotation;
String[] config = cInfoString.split(",");
if (config.length == 2) {
String[] pair = config[1].split("=");
if (pair.length == 2) {
annotation = DefaultAnnotations.builder()
.set(pair[0], pair[1]).build();
} else {
print("Wrong format {}", config[1]);
return null;
}
String[] data = config[0].split(":");
String type = data[0];
IpAddress ip = IpAddress.valueOf(data[1]);
int port = Integer.parseInt(data[2]);
return new ControllerInfo(ip, port, type, annotation);
} else {
print(config[0]);
return new ControllerInfo(config[0]);
}
}
}
......
......@@ -17,17 +17,23 @@ package org.onosproject.net.behaviour;
import com.google.common.base.Preconditions;
import org.onlab.packet.IpAddress;
import org.onosproject.net.Annotated;
import org.onosproject.net.Annotations;
import org.onosproject.net.DefaultAnnotations;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Objects;
/**
* Represents information for a device to connect to a controller.
*/
public class ControllerInfo {
public class ControllerInfo implements Annotated {
private IpAddress ip = IpAddress.valueOf("0.0.0.0");
private int port = 6653;
private String type = "error";
private final Annotations annotations;
/**
* Information for contacting the controller.
......@@ -37,18 +43,36 @@ public class ControllerInfo {
* @param type the connection type
*/
public ControllerInfo(IpAddress ip, int port, String type) {
this.ip = ip;
this(ip, port, type, DefaultAnnotations.EMPTY);
}
/**
* Information for contacting the controller.
*
* @param ip the ip address
* @param port the tcp port
* @param type the connection type
* @param annotations optional key/value annotations
*/
public ControllerInfo(IpAddress ip, int port, String type, Annotations annotations) {
this.ip = checkNotNull(ip);
this.port = port;
this.type = type;
this.type = checkNotNull(type);
this.annotations = checkNotNull(annotations);
}
// TODO Factory method equivalent to this method
// should probably live in OVSDB, NETCONF package.
/**
* Information for contacting the controller, if some information
* is not contained in the target string because it's optional
* it's leaved as in the field declaration (default values).
*
* @param target column returned from ovsdb query
*
* @deprecated in Hummingbird (1.7.0)
*/
@Deprecated
public ControllerInfo(String target) {
String[] data = target.split(":");
this.type = data[0];
......@@ -69,6 +93,7 @@ public class ControllerInfo {
this.port = Integer.parseInt(data[2]);
}
}
this.annotations = DefaultAnnotations.EMPTY;
}
/**
......@@ -98,11 +123,24 @@ public class ControllerInfo {
return type;
}
@Override
public Annotations annotations() {
return annotations;
}
// TODO Method equivalent to this method
// should probably live in OVSDB, NETCONF package.
// @deprecated in Hummingbird (1.7.0)
@Deprecated
public String target() {
if (type.startsWith("p")) {
return type + ":" + port + ":" + ip;
} else {
return type + ":" + ip + ":" + port;
if (annotations.equals(DefaultAnnotations.EMPTY)) {
return type + ":" + ip + ":" + port;
} else {
return type + ":" + ip + ":" + port + ":" + annotations.toString();
}
}
}
......@@ -115,12 +153,10 @@ public class ControllerInfo {
@Override
public boolean equals(Object toBeCompared) {
if (toBeCompared instanceof ControllerInfo) {
ControllerInfo controllerInfo = (ControllerInfo) toBeCompared;
if (controllerInfo.type().equals(this.type)
&& controllerInfo.ip().equals(this.ip())
&& controllerInfo.port() == this.port) {
return true;
}
ControllerInfo that = (ControllerInfo) toBeCompared;
return Objects.equals(this.type, that.type) &&
Objects.equals(this.ip, that.ip) &&
Objects.equals(this.port, that.port);
}
return false;
}
......
......@@ -19,18 +19,26 @@ package org.onosproject.drivers.fujitsu;
import com.google.common.collect.ImmutableList;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.onosproject.drivers.utilities.XmlConfigParser;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.configuration.tree.ConfigurationNode;
import org.onlab.packet.IpAddress;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Annotations;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DeviceId;
import org.onosproject.net.behaviour.ControllerConfig;
import org.onosproject.net.behaviour.ControllerInfo;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.driver.DriverHandler;
import org.onosproject.netconf.NetconfController;
import org.onosproject.netconf.NetconfDevice;
import org.onosproject.netconf.NetconfException;
import org.slf4j.Logger;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
......@@ -46,8 +54,11 @@ public class FujitsuVoltControllerConfig extends AbstractHandlerBehaviour
implements ControllerConfig {
private final Logger log = getLogger(FujitsuVoltControllerConfig.class);
private static final String RESOURCE_XML = "voltcontrollers.xml";
private static final String DOT = ".";
private static final String L_ANGLE_BR = "<";
private static final String R_ANGLE_BR = "/>";
private static final String VOLT_NE_NAMESPACE =
"xmlns=\"http://fujitsu.com/ns/volt/1.1\"";
private static final String DATA = "data";
......@@ -60,14 +71,35 @@ public class FujitsuVoltControllerConfig extends AbstractHandlerBehaviour
private static final String IP_ADDRESS = "ip-address";
private static final String PORT = "port";
private static final String PROTOCOL = "protocol";
private static final String CONFIG = "config";
private static final String OFCONFIG_ID = "ofconfig-id";
private static final String EDIT_CONFIG = "edit-config";
private static final String TARGET = "target";
private static final String RUNNING = "running";
private static final String MERGE = "merge";
private static final String DEFAULT_OPERATION = "default-operation";
private static final String VOLT_NE_OPEN = "<" + VOLT_NE + " ";
private static final String VOLT_NE_CLOSE = "</" + VOLT_NE + ">";
private static final String VOLT_OFCONFIG_EL = "<" + VOLT_OFCONFIG + "/>\n";
private static final String TARGET_OPEN = "<" + TARGET + ">";
private static final String TARGET_CLOSE = "</" + TARGET + ">";
private static final String END_LICENSE_HEADER = "-->";
private static final String VOLT_DATACONFIG = DATA + DOT + VOLT_NE + DOT +
VOLT_OFCONFIG + DOT + OF_CONTROLLERS + DOT + OF_CONTROLLER;
private static final String EDIT_CONFIG_TG = EDIT_CONFIG + DOT + TARGET;
private static final String EDIT_CONFIG_DO = EDIT_CONFIG + DOT + DEFAULT_OPERATION;
private static final String CONTROLLER_INFO_ID = CONTROLLER_INFO + DOT + "id";
private static final String CONTROLLER_INFO_IP = CONTROLLER_INFO + DOT + IP_ADDRESS;
private static final String CONTROLLER_INFO_PORT = CONTROLLER_INFO + DOT + PORT;
private static final String CONTROLLER_INFO_PROTOCOL = CONTROLLER_INFO + DOT + PROTOCOL;
private static final String VOLT_EDITCONFIG = EDIT_CONFIG + DOT +
CONFIG + DOT + VOLT_NE + DOT + VOLT_OFCONFIG + DOT + OF_CONTROLLERS;
@Override
public List<ControllerInfo> getControllers() {
DriverHandler handler = handler();
......@@ -85,11 +117,11 @@ public class FujitsuVoltControllerConfig extends AbstractHandlerBehaviour
String reply;
reply = controller.
getDevicesMap().get(ncDeviceId).getSession().
get(request.toString(), REPORT_ALL);
getDevicesMap().get(ncDeviceId).getSession().
get(request.toString(), REPORT_ALL);
log.debug("Reply XML {}", reply);
controllers.addAll(parseStreamVoltControllers(XmlConfigParser.
loadXml(new ByteArrayInputStream(reply.getBytes(StandardCharsets.UTF_8)))));
loadXml(new ByteArrayInputStream(reply.getBytes(StandardCharsets.UTF_8)))));
} catch (IOException e) {
log.error("Cannot communicate to device {} ", ncDeviceId);
}
......@@ -103,10 +135,31 @@ public class FujitsuVoltControllerConfig extends AbstractHandlerBehaviour
@Override
public void setControllers(List<ControllerInfo> controllers) {
// TODO update later
log.warn("Operation not supported");
DriverHandler handler = handler();
NetconfController controller = handler.get(NetconfController.class);
MastershipService mastershipService = handler.get(MastershipService.class);
DeviceId ncdeviceId = handler.data().deviceId();
checkNotNull(controller, "Netconf controller is null");
if (mastershipService.isLocalMaster(ncdeviceId)) {
try {
NetconfDevice device = controller.getNetconfDevice(ncdeviceId);
String config = createVoltControllersConfig(
XmlConfigParser.loadXml(getClass().
getResourceAsStream(RESOURCE_XML)),
RUNNING, MERGE, controllers);
device.getSession().editConfig(config.substring(
config.indexOf(END_LICENSE_HEADER) + END_LICENSE_HEADER.length()));
} catch (NetconfException e) {
log.error("Cannot communicate to device {} , exception ", ncdeviceId, e);
}
} else {
log.warn("I'm not master for {} please use master, {} to execute command",
ncdeviceId,
mastershipService.getMasterFor(ncdeviceId));
}
}
/**
* Parses XML string to get controller information.
*
......@@ -123,17 +176,67 @@ public class FujitsuVoltControllerConfig extends AbstractHandlerBehaviour
sub.configurationsAt(CONTROLLER_INFO);
for (HierarchicalConfiguration child : childFields) {
Annotations annotations = DefaultAnnotations.builder()
.set(OFCONFIG_ID, sub.getString(OFCONFIG_ID)).build();
ControllerInfo controller = new ControllerInfo(
IpAddress.valueOf(child.getString(IP_ADDRESS)),
Integer.parseInt(child.getString(PORT)),
child.getString(PROTOCOL));
child.getString(PROTOCOL), annotations);
log.debug("VOLT: OFCONTROLLER: PROTOCOL={}, IP={}, PORT={} ",
controller.type(), controller.ip(), controller.port());
log.debug("VOLT: OFCONTROLLER: PROTOCOL={}, IP={}, PORT={}, ID={} ",
controller.type(), controller.ip(),
controller.port(), controller.annotations().value(OFCONFIG_ID));
controllers.add(controller);
}
}
return controllers;
}
/**
* Forms XML string to change controller information.
*
* @param cfg a hierarchical configuration
* @param target the type of configuration
* @param netconfOperation operation type
* @param controllers list of controllers
* @return XML string
*/
public static String createVoltControllersConfig(HierarchicalConfiguration cfg,
String target, String netconfOperation,
List<ControllerInfo> controllers) {
XMLConfiguration editcfg = null;
cfg.setProperty(EDIT_CONFIG_TG, target);
cfg.setProperty(EDIT_CONFIG_DO, netconfOperation);
List<ConfigurationNode> newControllers = new ArrayList<>();
for (ControllerInfo ci : controllers) {
XMLConfiguration controller = new XMLConfiguration();
controller.setRoot(new HierarchicalConfiguration.Node(OF_CONTROLLER));
controller.setProperty(OFCONFIG_ID, ci.annotations().value(OFCONFIG_ID));
controller.setProperty(CONTROLLER_INFO_ID, ci.annotations().value(OFCONFIG_ID));
controller.setProperty(CONTROLLER_INFO_IP, ci.ip());
controller.setProperty(CONTROLLER_INFO_PORT, ci.port());
controller.setProperty(CONTROLLER_INFO_PROTOCOL, ci.type());
newControllers.add(controller.getRootNode());
}
cfg.addNodes(VOLT_EDITCONFIG, newControllers);
try {
editcfg = (XMLConfiguration) cfg;
} catch (ClassCastException e) {
e.printStackTrace();
}
StringWriter stringWriter = new StringWriter();
try {
editcfg.save(stringWriter);
} catch (ConfigurationException e) {
e.printStackTrace();
}
String s = stringWriter.toString();
s = s.replace(TARGET_OPEN + target + TARGET_CLOSE,
TARGET_OPEN + L_ANGLE_BR + target + R_ANGLE_BR + TARGET_CLOSE);
return s;
}
}
......
<!--
~ Copyright 2016-present 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.
-->
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
</target>
<default-operation>
</default-operation>
<config>
<volt-ne xmlns="http://fujitsu.com/ns/volt/1.1">
<volt-ofconfig>
<of-controllers>
</of-controllers>
</volt-ofconfig>
</volt-ne>
</config>
</edit-config>
</rpc>