blob: 17f26198ec656e91ec101bab8507085a037d6f00 [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
*
* 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.drivers.odtn;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.onlab.packet.ChassisId;
import org.onosproject.drivers.utilities.XmlConfigParser;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.Port.Type;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceDescriptionDiscovery;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.netconf.NetconfController;
import org.onosproject.netconf.NetconfDevice;
import org.onosproject.netconf.NetconfException;
import org.onosproject.netconf.NetconfSession;
import org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery;
import org.slf4j.Logger;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* OpenConfig based device and port discovery.
*/
public class FujitsuOpenConfigDeviceDiscovery extends AbstractHandlerBehaviour
implements OdtnDeviceDescriptionDiscovery, DeviceDescriptionDiscovery {
// Enable logging
private static final Logger log = getLogger(FujitsuOpenConfigDeviceDiscovery.class);
private static final CharSequence OPTICAL_CHANNEL = "OPTICAL_CHANNEL";
private static final CharSequence TRANSCEIVER = "TRANSCEIVER";
private static final String COMPONENT_STATE = "state";
private static final String COMPONENT_NAME = "name";
private static final String COMPONENT_TYPE = "type";
private static final String PORT_COMPONENT_PATH = "data.components.component";
private static final String MFG_NAME_TREE_PATH = PORT_COMPONENT_PATH + ".state.mfg-name";
private static final String SW_VERSION_TREE_PATH = PORT_COMPONENT_PATH + ".state.software-version";
private static final String HW_VERSION_TREE_PATH = PORT_COMPONENT_PATH + ".state.hardware-version";
private static final String SERIAL_NUMBER_TREE_PATH = PORT_COMPONENT_PATH + ".state.serial-no";
private static final String CHASSIS_ID_TREE_PATH = PORT_COMPONENT_PATH + ".state.id";
private PortDescription parsePortComponent(String portComponent,
Port.Type portDescriptionType,
String portType,
HierarchicalConfiguration portConfig) {
Map<String, String> props = new HashMap<>();
int portIdentifierNumber =
portComponent.equalsIgnoreCase(TRANSCEIVER.toString()) ? 100 : 0;
DefaultPortDescription.Builder builder = DefaultPortDescription.builder();
String portComponentName =
checkNotNull(portConfig.configurationAt(COMPONENT_STATE).getString(COMPONENT_NAME),
"state/name does not exist");
String portComponentType =
checkNotNull(portConfig.configurationAt(COMPONENT_STATE).getString(COMPONENT_TYPE),
"state/type does not exist");
/*
* Both components have names that end with component1-1/1/0/C1 component2-1/1/0/C1,
* So we need a way to distinguish component1 from component2
* Hence, We add a 100 to the port number to distinguish client port from the network port
*/
int portNumberMatchIndex = portComponentName.split("/[a-zA-Z]").length - 1;
Long portComponentId = Long.parseLong(
portComponentName.split("/[a-zA-Z]")[portNumberMatchIndex]) + portIdentifierNumber;
props.put(OdtnDeviceDescriptionDiscovery.OC_NAME, portComponentName);
props.put(OdtnDeviceDescriptionDiscovery.OC_TYPE, portComponentType);
builder.withPortNumber(PortNumber.portNumber((portComponentId),
portComponentName));
builder.type(portDescriptionType);
props.putIfAbsent(PORT_TYPE, portType);
props.putIfAbsent(CONNECTION_ID, String.valueOf(portComponentId));
return builder.annotations(DefaultAnnotations.builder().putAll(props).build()).build();
}
/**
* Parses 1FINITY ports based on optical channel or transceiver
* and returns their details.
*
* @param xmlCfg xml object of the component details from 1FINITY-T600
* @return List<PortDescription>
*/
@VisibleForTesting
public List<PortDescription> parse1FinityPorts(HierarchicalConfiguration xmlCfg) {
List<PortDescription> portDescriptions = Lists.newArrayList();
List<HierarchicalConfiguration> subTrees = checkNotNull(xmlCfg
.configurationsAt(PORT_COMPONENT_PATH));
for (HierarchicalConfiguration portConfig : subTrees) {
String portTypeSubComponent = checkNotNull(portConfig
.configurationAt(COMPONENT_STATE).getString(COMPONENT_TYPE));
if (portTypeSubComponent.contains(OPTICAL_CHANNEL)) {
portDescriptions.add(parsePortComponent(OPTICAL_CHANNEL.toString(),
Type.OCH,
OdtnPortType.LINE.value(),
portConfig));
} else if (portTypeSubComponent.contains(TRANSCEIVER)) {
portDescriptions.add(parsePortComponent(TRANSCEIVER.toString(),
Type.PACKET,
OdtnPortType.CLIENT.value(),
portConfig));
}
}
return portDescriptions;
}
/**
* Returns a device description appropriately annotated to support
* downstream model extension via projections of the resulting device,
* as in the following example.
* <pre>
* MicrowaveDevice device = deviceService.get(id).as(MicrowaveDevice.class);
* </pre>
*
* @return annotated device description
*/
@Override
public DeviceDescription discoverDeviceDetails() {
NetconfSession netconfSession = checkNotNull(getNetconfSession());
String requestedComponent = null;
try {
requestedComponent = netconfSession.doWrappedRpc(buildGetComponentRequest("shelf-1"));
} catch (NetconfException netconfException) {
log.error("Unable to send the request via netconf: {}\n" +
"Getting default Device Description Details", netconfException);
}
XMLConfiguration xmlCfg = (XMLConfiguration) XmlConfigParser.loadXmlString(requestedComponent);
return parseDeviceInformation(handler().data().deviceId().uri(), xmlCfg);
}
/**
* Returns a list of port descriptions appropriately annotated to support
* downstream model extension via projections of their parent device,
* as in the following example.
* <pre>
* MicrowaveDevice device = deviceService.get(id).as(MicrowaveDevice.class);
* List&lt;MicrowavePort&gt; ports = device.microwavePorts(deviceService.getPorts(id));
* </pre>
*
* @return annotated device description
*/
@Override
public List<PortDescription> discoverPortDetails() {
NetconfSession netconfSession = checkNotNull(getNetconfSession());
String allComponents = null;
try {
allComponents = netconfSession.doWrappedRpc(buildGetComponentsRequest());
log.info("Netconf reply is as follows:\n{}", allComponents);
} catch (NetconfException netconfException) {
log.error("Unable to send the request via netconf: {}", netconfException);
return null;
}
List<PortDescription> portDescriptions = parse1FinityPorts(XmlConfigParser
.loadXml(new ByteArrayInputStream(allComponents.getBytes())));
return ImmutableList.copyOf(portDescriptions);
}
/**
* Parses 1FINITY-T600 device information through the response from an rpc request.
*
* @param uri URI object that comprises of the deviceId
* @param xmlCfg xml object comprising device related details
* @return DeviceDescription
*/
@VisibleForTesting
public DeviceDescription parseDeviceInformation(URI uri, XMLConfiguration xmlCfg) {
String mfgName = "FUJITSU";
String hwVersion = null;
String swVersion = null;
String serialNumber = null;
String chassisId = "1";
mfgName = xmlCfg.getString(MFG_NAME_TREE_PATH, mfgName);
swVersion = xmlCfg.getString(SW_VERSION_TREE_PATH, swVersion);
hwVersion = xmlCfg.getString(HW_VERSION_TREE_PATH, hwVersion);
serialNumber = xmlCfg.getString(SERIAL_NUMBER_TREE_PATH, serialNumber);
chassisId = xmlCfg.getString(CHASSIS_ID_TREE_PATH, chassisId);
return new DefaultDeviceDescription(uri,
Device.Type.OTN,
mfgName,
hwVersion,
swVersion,
serialNumber,
new ChassisId(chassisId));
}
/**
* Utility method to return a Netconf session.
*
* @return NetconfSession
*/
private NetconfSession getNetconfSession() {
DeviceId did = checkNotNull(handler().data().deviceId());
NetconfController netconfController = checkNotNull(handler().get(NetconfController.class));
NetconfDevice netconfDevice = netconfController.getDevicesMap().get(did);
return checkNotNull(netconfDevice.getSession());
}
/**
* Construct a String with a Netconf filtered get RPC Message.
*
* @param filter A valid XML tree with the filter to apply in the get
* @return a String containing the RPC XML Document
*/
private String filteredGetBuilder(String filter) {
StringBuilder rpc = new StringBuilder();
rpc.append("<get>");
rpc.append("<filter type='subtree'>");
rpc.append(filter);
rpc.append("</filter>");
rpc.append("</get>");
return rpc.toString();
}
private String buildGetComponentRequest(String componentName) {
StringBuilder filter = new StringBuilder();
filter.append("<components xmlns=\"http://openconfig.net/yang/platform\">");
filter.append("<component>");
filter.append("<name>");
filter.append(componentName);
filter.append("</name>");
filter.append("</component>");
filter.append("</components>");
return filteredGetBuilder(filter.toString());
}
private String buildGetComponentsRequest() {
StringBuilder filter = new StringBuilder();
filter.append("<components xmlns=\"http://openconfig.net/yang/platform\">");
filter.append("</components>");
return filteredGetBuilder(filter.toString());
}
}