Add ROADM application

Change-Id: I50fa93cf3a69122f6434b46e831b254771159294
This commit is contained in:
Jimmy Yan 2016-09-02 16:32:01 -07:00 committed by Yuta HIGUCHI
parent 7f872a7058
commit da878fcbf0
27 changed files with 3195 additions and 2 deletions

View File

@ -31,6 +31,7 @@
<modules>
<module>acl</module>
<module>roadm</module>
<module>faultmanagement</module>
<module>fwd</module>
<module>mobility</module>

24
apps/roadm/BUCK Normal file
View File

@ -0,0 +1,24 @@
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//core/store/serializers:onos-core-serializers',
'//apps/optical-model:onos-apps-optical-model',
]
TEST_DEPS = [
'//lib:TEST_REST',
'//core/api:onos-api-tests',
]
osgi_jar_with_tests (
deps = COMPILE_DEPS,
test_deps = TEST_DEPS,
)
onos_app (
title = 'ROADM App',
category = 'Optical',
url = 'http://onosproject.org',
description = """This application provides an interface and web GUI for monitoring
and configuring power on ROADM devices.""",
required_apps = [ 'org.onosproject.optical-model' ],
)

150
apps/roadm/pom.xml Normal file
View File

@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
--><project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-apps</artifactId>
<version>1.9.0-SNAPSHOT</version>
</parent>
<artifactId>onos-apps-roadm</artifactId>
<packaging>bundle</packaging>
<description>Application for ROADM device management</description>
<url>http://onosproject.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<onos.version>1.9.0-SNAPSHOT</onos.version>
<onos.app.name>org.onosproject.roadm</onos.app.name>
<onos.app.title>ROADM Application</onos.app.title>
<onos.app.category>Utility</onos.app.category>
<onos.app.readme>
This application provides an interface and web GUI for monitoring
and configuring power on ROADM devices.
</onos.app.readme>
</properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${onos.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-serializers</artifactId>
<version>${onos.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-osgi</artifactId>
<version>${onos.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-optical-model</artifactId>
<version>${onos.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${onos.version}</version>
<scope>test</scope>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
<executions>
<execution>
<id>generate-scr-srcdescriptor</id>
<goals>
<goal>scr</goal>
</goals>
</execution>
</executions>
<configuration>
<supportedProjectTypes>
<supportedProjectType>bundle</supportedProjectType>
<supportedProjectType>war</supportedProjectType>
</supportedProjectTypes>
</configuration>
</plugin>
<plugin>
<groupId>org.onosproject</groupId>
<artifactId>onos-maven-plugin</artifactId>
<executions>
<execution>
<id>cfg</id>
<phase>generate-resources</phase>
<goals>
<goal>cfg</goal>
</goals>
</execution>
<execution>
<id>swagger</id>
<phase>generate-sources</phase>
<goals>
<goal>swagger</goal>
</goals>
</execution>
<execution>
<id>app</id>
<phase>package</phase>
<goals>
<goal>app</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,102 @@
/*
* 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.
*/
package org.onosproject.roadm;
import org.onosproject.net.OchSignal;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.OchSignalCriterion;
import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
/**
* Representation of an internal ROADM connection.
*/
public final class ChannelData {
private PortNumber inPort;
private PortNumber outPort;
private OchSignal ochSignal;
private ChannelData(PortNumber inPort, PortNumber outPort, OchSignal ochSignal) {
this.inPort = inPort;
this.outPort = outPort;
this.ochSignal = ochSignal;
}
/**
* Returns a ChannelData representation from a flow rule. The rule must contain
* a Criterion.Type.IN_PORT selector, Criterion.Type.OCH_SIGID selector, and
* Instruction.Type.OUTPUT instruction.
*
* @param rule the flow rule representing the connection
* @return ChannelData representation of the connection
*/
public static ChannelData fromFlow(FlowRule rule) {
checkNotNull(rule);
Criterion in = rule.selector().getCriterion(Criterion.Type.IN_PORT);
checkNotNull(in);
PortNumber inPort = ((PortCriterion) in).port();
Criterion och = rule.selector().getCriterion(Criterion.Type.OCH_SIGID);
checkNotNull(och);
OchSignal ochSignal = ((OchSignalCriterion) och).lambda();
PortNumber outPort = null;
List<Instruction> instructions = rule.treatment().allInstructions();
for (Instruction ins : instructions) {
if (ins.type() == Instruction.Type.OUTPUT) {
outPort = ((Instructions.OutputInstruction) ins).port();
}
}
checkNotNull(outPort);
return new ChannelData(inPort, outPort, ochSignal);
}
/**
* Returns the input port.
*
* @return input port
*/
public PortNumber inPort() {
return inPort;
}
/**
* Returns the output port.
*
* @return output port
*/
public PortNumber outPort() {
return outPort;
}
/**
* Returns the channel signal.
*
* @return channel signal
*/
public OchSignal ochSignal() {
return ochSignal;
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.
*/
package org.onosproject.roadm;
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.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* Manages the port target powers for ROADM devices.
*/
@Component(immediate = true)
@Service
public class DistributedRoadmStore implements RoadmStore {
private static Logger log = LoggerFactory.getLogger(DistributedRoadmStore.class);
private ConsistentMap<DeviceId, Map<PortNumber, Long>> distPowerMap;
private Map<DeviceId, Map<PortNumber, Long>> powerMap;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
@Activate
public void activate() {
distPowerMap = storageService.<DeviceId, Map<PortNumber, Long>>consistentMapBuilder()
.withName("onos-roadm-distributed-store")
.withSerializer(Serializer.using(KryoNamespaces.API))
.build();
powerMap = distPowerMap.asJavaMap();
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
// Add a map to the store for a device if not already added.
// Powers still need to be initialized with calls to setTargetPower().
@Override
public void addDevice(DeviceId deviceId) {
powerMap.putIfAbsent(deviceId, new HashMap<>());
log.info("Initializing {}", deviceId);
}
// Returns true if Map for device exists in ConsistentMap
@Override
public boolean deviceAvailable(DeviceId deviceId) {
return powerMap.get(deviceId) != null;
}
@Override
public void setTargetPower(DeviceId deviceId, PortNumber portNumber, long targetPower) {
Map<PortNumber, Long> portMap = powerMap.get(deviceId);
if (portMap != null) {
portMap.put(portNumber, targetPower);
powerMap.put(deviceId, portMap);
} else {
log.info("Device {} not found in store", deviceId);
}
}
@Override
public Long getTargetPower(DeviceId deviceId, PortNumber portNumber) {
Map<PortNumber, Long> portMap = powerMap.get(deviceId);
if (portMap != null) {
return portMap.get(portNumber);
}
return null;
}
}

View File

@ -0,0 +1,84 @@
/*
* 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.
*/
package org.onosproject.roadm;
import com.google.common.collect.ImmutableList;
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.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiView;
import org.onosproject.ui.UiViewHidden;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* ONOS UI for ROADM application.
*/
@Component(immediate = true)
public class RoadmComponent {
private static final String DEVICE_VIEW_ID = "roadmDevice";
private static final String DEVICE_VIEW_TEXT = "ROADM";
private static final String RESOURCE_PATH = "webgui";
private final Logger log = LoggerFactory.getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected UiExtensionService uiExtensionService;
// List of application views
private final List<UiView> deviceViews = ImmutableList.of(
new UiView(UiView.Category.OTHER, DEVICE_VIEW_ID, DEVICE_VIEW_TEXT),
new UiViewHidden("roadmPort"),
new UiViewHidden("roadmFlow")
);
// Factory for UI message handlers
private final UiMessageHandlerFactory messageHandlerFactory =
() -> ImmutableList.of(
new RoadmDeviceViewMessageHandler(),
new RoadmPortViewMessageHandler(),
new RoadmFlowViewMessageHandler()
);
// Device UI extension
protected UiExtension deviceExtension =
new UiExtension.Builder(getClass().getClassLoader(), deviceViews)
.resourcePath(RESOURCE_PATH)
.messageHandlerFactory(messageHandlerFactory)
.build();
@Activate
protected void activate() {
uiExtensionService.register(deviceExtension);
log.info("Started");
}
@Deactivate
protected void deactivate() {
uiExtensionService.unregister(deviceExtension);
log.info("Stopped");
}
}

View File

@ -0,0 +1,115 @@
/*
* 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.
*/
package org.onosproject.roadm;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableSet;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.Device;
import org.onosproject.net.device.DeviceService;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.table.TableModel;
import org.onosproject.ui.table.TableRequestHandler;
import java.util.Collection;
import static com.google.common.base.Strings.isNullOrEmpty;
/**
* Table-View message handler for ROADM device view.
*/
public class RoadmDeviceViewMessageHandler extends UiMessageHandler {
private static final String ROADM_DEVICE_DATA_REQ = "roadmDeviceDataRequest";
private static final String ROADM_DEVICE_DATA_RESP = "roadmDeviceDataResponse";
private static final String ROADM_DEVICES = "roadmDevices";
private static final String NO_ROWS_MESSAGE = "No items found";
private static final String ID = "id";
private static final String FRIENDLY_NAME = "name";
private static final String MASTER = "master";
private static final String PORTS = "ports";
private static final String VENDOR = "vendor";
private static final String HW_VERSION = "hwVersion";
private static final String SW_VERSION = "swVersion";
private static final String PROTOCOL = "protocol";
private static final String[] COLUMN_IDS = {
ID, FRIENDLY_NAME, MASTER, PORTS, VENDOR, HW_VERSION, SW_VERSION,
PROTOCOL
};
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new DeviceTableDataRequestHandler()
);
}
// Returns friendly name of the device from the annotations
private static String deviceName(Device device) {
String name = device.annotations().value(AnnotationKeys.NAME);
return isNullOrEmpty(name) ? device.id().toString() : name;
}
// Returns the device protocol from annotations
private static String deviceProtocol(Device device) {
String protocol = device.annotations().value(PROTOCOL);
return protocol != null ? protocol : "N/A";
}
// Handler for sample table requests
private final class DeviceTableDataRequestHandler extends TableRequestHandler {
private DeviceTableDataRequestHandler() {
super(ROADM_DEVICE_DATA_REQ, ROADM_DEVICE_DATA_RESP, ROADM_DEVICES);
}
@Override
protected String[] getColumnIds() {
return COLUMN_IDS;
}
@Override
protected String noRowsMessage(ObjectNode payload) {
return NO_ROWS_MESSAGE;
}
@Override
protected void populateTable(TableModel tm, ObjectNode payload) {
DeviceService ds = get(DeviceService.class);
MastershipService ms = get(MastershipService.class);
for (Device device : ds.getDevices(Device.Type.ROADM)) {
populateRow(tm.addRow(), device, ds, ms);
}
}
private void populateRow(TableModel.Row row, Device device, DeviceService ds,
MastershipService ms) {
row.cell(ID, device.id().toString())
.cell(FRIENDLY_NAME, deviceName(device))
.cell(MASTER, ms.getMasterFor(device.id()))
.cell(PORTS, ds.getPorts(device.id()).size())
.cell(VENDOR, device.manufacturer())
.cell(HW_VERSION, device.hwVersion())
.cell(SW_VERSION, device.swVersion())
.cell(PROTOCOL, deviceProtocol(device));
}
}
}

View File

@ -0,0 +1,421 @@
/*
* 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.
*/
package org.onosproject.roadm;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Range;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.net.ChannelSpacing;
import org.onosproject.net.DeviceId;
import org.onosproject.net.OchSignal;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowId;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.table.TableModel;
import org.onosproject.ui.table.TableRequestHandler;
import org.onosproject.ui.table.cell.HexLongFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import static org.onosproject.ui.JsonUtils.node;
import static org.onosproject.ui.JsonUtils.number;
/**
* Table-View message handler for ROADM flow view.
*/
public class RoadmFlowViewMessageHandler extends UiMessageHandler {
private static final String ROADM_FLOW_DATA_REQ = "roadmFlowDataRequest";
private static final String ROADM_FLOW_DATA_RESP = "roadmFlowDataResponse";
private static final String ROADM_FLOWS = "roadmFlows";
private static final String ROADM_SET_ATTENUATION_REQ = "roadmSetAttenuationRequest";
private static final String ROADM_SET_ATTENUATION_RESP = "roadmSetAttenuationResponse";
private static final String ROADM_DELETE_FLOW_REQ = "roadmDeleteFlowRequest";
private static final String ROADM_CREATE_FLOW_REQ = "roadmCreateFlowRequest";
private static final String ROADM_CREATE_FLOW_RESP = "roadmCreateFlowResponse";
private static final String NO_ROWS_MESSAGE = "No items found";
private static final String DEV_ID = "devId";
private static final String ID = "id";
private static final String FLOW_ID = "flowId";
private static final String APP_ID = "appId";
private static final String GROUP_ID = "groupId";
private static final String TABLE_ID = "tableId";
private static final String PRIORITY = "priority";
private static final String PERMANENT = "permanent";
private static final String TIMEOUT = "timeout";
private static final String STATE = "state";
private static final String IN_PORT = "inPort";
private static final String OUT_PORT = "outPort";
private static final String CHANNEL_SPACING = "spacing";
private static final String CHANNEL_MULTIPLIER = "multiplier";
private static final String CURRENT_POWER = "currentPower";
private static final String ATTENUATION = "attenuation";
private static final String HAS_ATTENUATION = "hasAttenuation";
private static final String[] COLUMN_IDS = {
ID, FLOW_ID, APP_ID, GROUP_ID, TABLE_ID, PRIORITY, TIMEOUT,
PERMANENT, STATE, IN_PORT, OUT_PORT, CHANNEL_SPACING,
CHANNEL_MULTIPLIER, CURRENT_POWER, ATTENUATION, HAS_ATTENUATION
};
private static final String NA = "N/A";
private static final String UNKNOWN = "Unknown";
private static final long GHZ = 1_000_000_000L;
private FlowRuleService flowRuleService;
private RoadmService roadmService;
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public void init(UiConnection connection, ServiceDirectory directory) {
super.init(connection, directory);
flowRuleService = get(FlowRuleService.class);
roadmService = get(RoadmService.class);
}
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new FlowTableDataRequestHandler(),
new SetAttenuationRequestHandler(),
new DeleteConnectionRequestHandler(),
new CreateConnectionRequestHandler()
);
}
// Handler for sample table requests
private final class FlowTableDataRequestHandler extends TableRequestHandler {
private FlowTableDataRequestHandler() {
super(ROADM_FLOW_DATA_REQ, ROADM_FLOW_DATA_RESP, ROADM_FLOWS);
}
@Override
protected String[] getColumnIds() {
return COLUMN_IDS;
}
@Override
protected String noRowsMessage(ObjectNode payload) {
return NO_ROWS_MESSAGE;
}
@Override
protected TableModel createTableModel() {
TableModel tm = super.createTableModel();
tm.setFormatter(FLOW_ID, HexLongFormatter.INSTANCE);
return tm;
}
@Override
protected void populateTable(TableModel tm, ObjectNode payload) {
DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
Iterable<FlowEntry> flowEntries = flowRuleService.getFlowEntries(deviceId);
for (FlowEntry flowEntry : flowEntries) {
populateRow(tm.addRow(), flowEntry, deviceId);
}
}
private void populateRow(TableModel.Row row, FlowEntry entry, DeviceId deviceId) {
ChannelData cd = ChannelData.fromFlow(entry);
row.cell(ID, entry.id().value())
.cell(FLOW_ID, entry.id().value())
.cell(APP_ID, entry.appId())
.cell(PRIORITY, entry.priority())
.cell(TIMEOUT, entry.timeout())
.cell(PERMANENT, entry.isPermanent())
.cell(STATE, entry.state().toString())
.cell(IN_PORT, cd.inPort().toLong())
.cell(OUT_PORT, cd.outPort().toLong())
.cell(CHANNEL_SPACING, cd.ochSignal().channelSpacing().frequency().asHz() / GHZ)
.cell(CHANNEL_MULTIPLIER, cd.ochSignal().spacingMultiplier())
.cell(CURRENT_POWER, getCurrentPower(deviceId, cd))
.cell(ATTENUATION, getAttenuation(deviceId, cd));
}
private String getCurrentPower(DeviceId deviceId, ChannelData channelData) {
Range<Long> range =
roadmService.attenuationRange(deviceId,
channelData.outPort(),
channelData.ochSignal());
if (range != null) {
Long currentPower =
roadmService.getCurrentChannelPower(deviceId,
channelData.outPort(),
channelData.ochSignal());
if (currentPower != null) {
return String.valueOf(currentPower);
}
}
return NA;
}
private String getAttenuation(DeviceId deviceId, ChannelData channelData) {
Long attenuation =
roadmService.getAttenuation(deviceId, channelData.outPort(),
channelData.ochSignal());
if (attenuation != null) {
return String.valueOf(attenuation);
}
return UNKNOWN;
}
}
// Handler for setting attenuation
private final class SetAttenuationRequestHandler extends RequestHandler {
// Keys for response message
private static final String VALID = "valid";
private static final String MESSAGE = "message";
// Error messages to display to user
private static final String ATTENUATION_RANGE_MSG =
"Attenuation must be in range %s.";
private static final String NO_ATTENUATION_MSG =
"Cannot set attenuation for this connection";
private SetAttenuationRequestHandler() {
super(ROADM_SET_ATTENUATION_REQ);
}
@Override
public void process(ObjectNode payload) {
DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
FlowId flowId = FlowId.valueOf(number(payload, FLOW_ID));
long attenuation = payload.get(ATTENUATION).asLong();
// Get connection information from the flow
FlowEntry entry = findFlow(deviceId, flowId);
if (entry == null) {
log.error("Unable to find flow rule to set attenuation");
return;
}
ChannelData cd = ChannelData.fromFlow(entry);
Range<Long> range =
roadmService.attenuationRange(deviceId, cd.outPort(),
cd.ochSignal());
boolean validAttenuation = (range != null && range.contains(attenuation));
if (validAttenuation) {
roadmService.setAttenuation(deviceId, cd.outPort(),
cd.ochSignal(), attenuation);
}
ObjectNode rootNode = objectNode();
// Send back flowId so view can identify which callback function to use
rootNode.put(FLOW_ID, payload.get(FLOW_ID).asText());
rootNode.put(VALID, validAttenuation);
if (range != null) {
rootNode.put(MESSAGE, String.format(ATTENUATION_RANGE_MSG,
range.toString()));
} else {
rootNode.put(MESSAGE, NO_ATTENUATION_MSG);
}
sendMessage(ROADM_SET_ATTENUATION_RESP, rootNode);
}
private FlowEntry findFlow(DeviceId deviceId, FlowId flowId) {
for (FlowEntry entry : flowRuleService.getFlowEntries(deviceId)) {
if (entry.id().equals(flowId)) {
return entry;
}
}
return null;
}
}
// Handler for deleting a connection
private final class DeleteConnectionRequestHandler extends RequestHandler {
private DeleteConnectionRequestHandler() {
super(ROADM_DELETE_FLOW_REQ);
}
@Override
public void process(ObjectNode payload) {
DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
FlowId flowId = FlowId.valueOf(payload.get(ID).asLong());
roadmService.removeConnection(deviceId, flowId);
}
}
// Handler for creating a creating a connection from form data
private final class CreateConnectionRequestHandler extends RequestHandler {
// Keys to load from JSON
private static final String FORM_DATA = "formData";
private static final String CHANNEL_SPACING_INDEX = "index";
private static final String INCLUDE_ATTENUATION = "includeAttenuation";
// Keys for validation results
private static final String CONNECTION = "connection";
private static final String CHANNEL_AVAILABLE = "channelAvailable";
// Error messages to display to user
private static final String IN_PORT_ERR_MSG = "Invalid input port.";
private static final String OUT_PORT_ERR_MSG = "Invalid output port.";
private static final String CONNECTION_ERR_MSG =
"Invalid connection from input port to output port.";
private static final String CHANNEL_SPACING_ERR_MSG =
"Channel spacing not supported.";
private static final String CHANNEL_ERR_MSG =
"Channel index must be in range %s.";
private static final String CHANNEL_AVAILABLE_ERR_MSG =
"Channel is already being used.";
private static final String ATTENUATION_ERR_MSG =
"Attenuation must be in range %s.";
// Keys for validation object
private static final String VALID = "valid";
private static final String MESSAGE = "message";
private CreateConnectionRequestHandler() {
super(ROADM_CREATE_FLOW_REQ);
}
@Override
public void process(ObjectNode payload) {
DeviceId did = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
ObjectNode flowNode = node(payload, FORM_DATA);
int priority = (int) number(flowNode, PRIORITY);
boolean permanent = bool(flowNode, PERMANENT);
int timeout = (int) number(flowNode, TIMEOUT);
PortNumber inPort = PortNumber.portNumber(number(flowNode, IN_PORT));
PortNumber outPort = PortNumber.portNumber(number(flowNode, OUT_PORT));
ObjectNode chNode = node(flowNode, CHANNEL_SPACING);
ChannelSpacing spacing =
channelSpacing((int) number(chNode, CHANNEL_SPACING_INDEX));
int multiplier = (int) number(flowNode, CHANNEL_MULTIPLIER);
OchSignal och = OchSignal.newDwdmSlot(spacing, multiplier);
boolean includeAttenuation = bool(flowNode, INCLUDE_ATTENUATION);
long att = number(flowNode, ATTENUATION);
boolean validInPort = roadmService.validInputPort(did, inPort);
boolean validOutPort = roadmService.validOutputPort(did, outPort);
boolean validConnect = roadmService.validConnection(did, inPort, outPort);
boolean validSpacing = true;
boolean validChannel = roadmService.validChannel(did, inPort, och);
boolean channelAvailable = roadmService.channelAvailable(did, och);
boolean validAttenuation = roadmService.attenuationInRange(did, outPort, att);
if (validConnect && validChannel && channelAvailable) {
if (includeAttenuation && validAttenuation) {
roadmService.createConnection(did, priority, permanent,
timeout, inPort, outPort,
och, att);
} else if (!includeAttenuation) {
roadmService.createConnection(did, priority, permanent,
timeout, inPort, outPort,
och);
}
}
// Construct error for channel
String channelMessage = "Invalid channel";
if (!validChannel) {
Set<OchSignal> lambdas = roadmService.queryLambdas(did, outPort);
if (lambdas != null) {
Range<Integer> range = channelRange(lambdas);
if (range.contains(och.spacingMultiplier())) {
// Channel spacing error
validSpacing = false;
} else {
channelMessage = String.format(CHANNEL_ERR_MSG, range.toString());
}
}
}
// Construct error for attenuation
String attenuationMessage = "Invalid attenuation";
if (!validAttenuation) {
Range<Long> range =
roadmService.attenuationRange(did, outPort, och);
if (range != null) {
attenuationMessage =
String.format(ATTENUATION_ERR_MSG, range.toString());
}
}
// Build response
ObjectNode node = objectNode();
node.set(IN_PORT, validationObject(validInPort, IN_PORT_ERR_MSG));
node.set(OUT_PORT, validationObject(validOutPort, OUT_PORT_ERR_MSG));
node.set(CONNECTION, validationObject(validConnect, CONNECTION_ERR_MSG));
node.set(CHANNEL_SPACING, validationObject(validChannel || validSpacing,
CHANNEL_SPACING_ERR_MSG));
node.set(CHANNEL_MULTIPLIER, validationObject(validChannel || !validSpacing,
channelMessage));
node.set(CHANNEL_AVAILABLE, validationObject(!validChannel || channelAvailable,
CHANNEL_AVAILABLE_ERR_MSG));
node.set(ATTENUATION, validationObject(validAttenuation, attenuationMessage));
node.put(INCLUDE_ATTENUATION, includeAttenuation);
sendMessage(ROADM_CREATE_FLOW_RESP, node);
}
// Returns the ChannelSpacing based on the selection made
private ChannelSpacing channelSpacing(int selectionIndex) {
switch (selectionIndex) {
case 0: return ChannelSpacing.CHL_100GHZ;
case 1: return ChannelSpacing.CHL_50GHZ;
case 2: return ChannelSpacing.CHL_25GHZ;
case 3: return ChannelSpacing.CHL_12P5GHZ;
// 6.25GHz cannot be used with ChannelSpacing.newDwdmSlot
// case 4: return ChannelSpacing.CHL_6P25GHZ;
default: return ChannelSpacing.CHL_50GHZ;
}
}
// Construct validation object to return to the view
private ObjectNode validationObject(boolean result, String message) {
ObjectNode node = objectNode();
node.put(VALID, result);
if (!result) {
// return error message to display if validation failed
node.put(MESSAGE, message);
}
return node;
}
// Returns the minimum and maximum channel spacing
private Range<Integer> channelRange(Set<OchSignal> signals) {
Comparator<OchSignal> compare =
(OchSignal a, OchSignal b) -> a.spacingMultiplier() - b.spacingMultiplier();
OchSignal minOch = Collections.min(signals, compare);
OchSignal maxOch = Collections.max(signals, compare);
return Range.closed(minOch.spacingMultiplier(), maxOch.spacingMultiplier());
}
}
}

View File

@ -0,0 +1,530 @@
/*
* 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.
*/
package org.onosproject.roadm;
import com.google.common.collect.Range;
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.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ChannelSpacing;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Direction;
import org.onosproject.net.OchSignal;
import org.onosproject.net.OchSignalType;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.behaviour.LambdaQuery;
import org.onosproject.net.behaviour.PowerConfig;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowId;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flow.instructions.Instructions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Application for monitoring and configuring ROADM devices.
*/
@Component(immediate = true)
@Service
public class RoadmManager implements RoadmService {
private static final String APP_NAME = "org.onosproject.roadm";
private ApplicationId appId;
private final Logger log = LoggerFactory.getLogger(getClass());
private DeviceListener deviceListener = new InternalDeviceListener();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected RoadmStore roadmStore;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
@Activate
protected void activate() {
appId = coreService.registerApplication(APP_NAME);
deviceService.addListener(deviceListener);
initDevices();
log.info("Started");
}
@Deactivate
protected void deactivate() {
deviceService.removeListener(deviceListener);
log.info("Stopped");
}
private PowerConfig<Object> getPowerConfig(DeviceId deviceId) {
Device device = deviceService.getDevice(deviceId);
if (device != null && device.is(PowerConfig.class)) {
return device.as(PowerConfig.class);
}
log.warn("Unable to load PowerConfig for {}", deviceId);
return null;
}
private LambdaQuery getLambdaQuery(DeviceId deviceId) {
Device device = deviceService.getDevice(deviceId);
if (device != null && device.is(LambdaQuery.class)) {
return device.as(LambdaQuery.class);
}
return null;
}
private void initDevices() {
for (Device device : deviceService.getDevices(Device.Type.ROADM)) {
initDevice(device.id());
setAllInitialTargetPortPowers(device.id());
}
}
// Initialize RoadmStore for a device to support target power
private void initDevice(DeviceId deviceId) {
if (!roadmStore.deviceAvailable(deviceId)) {
roadmStore.addDevice(deviceId);
}
log.info("Initialized device {}", deviceId);
}
// Sets the target port powers for a port on a device
// Attempts to read target powers from store. If no value is found then
// default value is used instead.
private void setInitialTargetPortPower(DeviceId deviceId, PortNumber portNumber) {
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig == null) {
log.warn("Unable to set default initial powers for port {} on device {}",
portNumber, deviceId);
return;
}
Optional<Range<Long>> range =
powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
if (!range.isPresent()) {
log.warn("No target power range found for port {} on device {}",
portNumber, deviceId);
return;
}
Long power = roadmStore.getTargetPower(deviceId, portNumber);
if (power == null) {
// Set default to middle of the range
power = (range.get().lowerEndpoint() + range.get().upperEndpoint()) / 2;
roadmStore.setTargetPower(deviceId, portNumber, power);
}
powerConfig.setTargetPower(portNumber, Direction.ALL, power);
}
// Sets the target port powers for each each port on a device
// Attempts to read target powers from store. If no value is found then
// default value is used instead
private void setAllInitialTargetPortPowers(DeviceId deviceId) {
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig == null) {
log.warn("Unable to set default initial powers for device {}",
deviceId);
return;
}
List<Port> ports = deviceService.getPorts(deviceId);
for (Port port : ports) {
Optional<Range<Long>> range =
powerConfig.getTargetPowerRange(port.number(), Direction.ALL);
if (range.isPresent()) {
Long power = roadmStore.getTargetPower(deviceId, port.number());
if (power == null) {
// Set default to middle of the range
power = (range.get().lowerEndpoint() + range.get().upperEndpoint()) / 2;
roadmStore.setTargetPower(deviceId, port.number(), power);
}
powerConfig.setTargetPower(port.number(), Direction.ALL, power);
} else {
log.warn("No target power range found for port {} on device {}",
port.number(), deviceId);
}
}
}
@Override
public void setTargetPortPower(DeviceId deviceId, PortNumber portNumber, long power) {
checkNotNull(deviceId);
checkNotNull(portNumber);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
roadmStore.setTargetPower(deviceId, portNumber, power);
powerConfig.setTargetPower(portNumber, Direction.ALL, power);
} else {
log.warn("Unable to set target port power for device {}", deviceId);
}
}
@Override
public Long getTargetPortPower(DeviceId deviceId, PortNumber portNumber) {
checkNotNull(deviceId);
checkNotNull(portNumber);
// getTargetPortPower is not yet implemented in PowerConfig so we
// access store instead
return roadmStore.getTargetPower(deviceId, portNumber);
}
@Override
public void setAttenuation(DeviceId deviceId, PortNumber portNumber,
OchSignal ochSignal, long attenuation) {
checkNotNull(deviceId);
checkNotNull(portNumber);
checkNotNull(ochSignal);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
powerConfig.setTargetPower(portNumber, ochSignal, attenuation);
} else {
log.warn("Cannot set attenuation for channel index {} on device {}",
ochSignal.spacingMultiplier(), deviceId);
}
}
@Override
public Long getAttenuation(DeviceId deviceId, PortNumber portNumber,
OchSignal ochSignal) {
checkNotNull(deviceId);
checkNotNull(portNumber);
checkNotNull(ochSignal);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
Optional<Long> attenuation =
powerConfig.getTargetPower(portNumber, ochSignal);
if (attenuation.isPresent()) {
return attenuation.get();
}
}
return null;
}
@Override
public Long getCurrentPortPower(DeviceId deviceId, PortNumber portNumber) {
checkNotNull(deviceId);
checkNotNull(portNumber);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
Optional<Long> currentPower =
powerConfig.currentPower(portNumber, Direction.ALL);
if (currentPower.isPresent()) {
return currentPower.get();
}
}
return null;
}
@Override
public Long getCurrentChannelPower(DeviceId deviceId, PortNumber portNumber,
OchSignal ochSignal) {
checkNotNull(deviceId);
checkNotNull(portNumber);
checkNotNull(ochSignal);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
Optional<Long> currentPower =
powerConfig.currentPower(portNumber, ochSignal);
if (currentPower.isPresent()) {
return currentPower.get();
}
}
return null;
}
@Override
public Set<OchSignal> queryLambdas(DeviceId deviceId, PortNumber portNumber) {
checkNotNull(deviceId);
checkNotNull(portNumber);
LambdaQuery lambdaQuery = getLambdaQuery(deviceId);
if (lambdaQuery != null) {
return lambdaQuery.queryLambdas(portNumber);
}
return Collections.emptySet();
}
@Override
public FlowId createConnection(DeviceId deviceId, int priority, boolean isPermanent,
int timeout, PortNumber inPort, PortNumber outPort,
OchSignal ochSignal) {
checkNotNull(deviceId);
checkNotNull(inPort);
checkNotNull(outPort);
FlowRule.Builder flowBuilder = new DefaultFlowRule.Builder();
flowBuilder.fromApp(appId);
flowBuilder.withPriority(priority);
if (isPermanent) {
flowBuilder.makePermanent();
} else {
flowBuilder.makeTemporary(timeout);
}
flowBuilder.forDevice(deviceId);
TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
selectorBuilder.add(Criteria.matchInPort(inPort));
selectorBuilder.add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID));
selectorBuilder.add(Criteria.matchLambda(ochSignal));
flowBuilder.withSelector(selectorBuilder.build());
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
treatmentBuilder.add(Instructions.createOutput(outPort));
flowBuilder.withTreatment(treatmentBuilder.build());
FlowRule flowRule = flowBuilder.build();
flowRuleService.applyFlowRules(flowRule);
log.info("Created connection from input port {} to output port {}",
inPort.toLong(), outPort.toLong());
return flowRule.id();
}
@Override
public FlowId createConnection(DeviceId deviceId, int priority, boolean isPermanent,
int timeout, PortNumber inPort, PortNumber outPort,
OchSignal ochSignal, long attenuation) {
checkNotNull(deviceId);
checkNotNull(inPort);
checkNotNull(outPort);
FlowId flowId = createConnection(deviceId, priority, isPermanent,
timeout, inPort, outPort, ochSignal);
delayedSetAttenuation(deviceId, outPort, ochSignal, attenuation);
return flowId;
}
// Delay the call to setTargetPower because the flow may not be in the store yet
private void delayedSetAttenuation(DeviceId deviceId, PortNumber outPort,
OchSignal ochSignal, long attenuation) {
Runnable setAtt = () -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
log.warn("Thread interrupted. Setting attenuation early.");
}
setAttenuation(deviceId, outPort, ochSignal, attenuation);
};
new Thread(setAtt).start();
}
@Override
public void removeConnection(DeviceId deviceId, FlowId flowId) {
checkNotNull(deviceId);
checkNotNull(flowId);
for (FlowEntry entry : flowRuleService.getFlowEntries(deviceId)) {
if (entry.id().equals(flowId)) {
flowRuleService.removeFlowRules(entry);
log.info("Deleted connection {}", entry.id());
break;
}
}
}
@Override
public boolean hasPortTargetPower(DeviceId deviceId, PortNumber portNumber) {
checkNotNull(deviceId);
checkNotNull(portNumber);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
Optional<Range<Long>> range =
powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
return range.isPresent();
}
return false;
}
@Override
public boolean portTargetPowerInRange(DeviceId deviceId, PortNumber portNumber,
long power) {
checkNotNull(deviceId);
checkNotNull(portNumber);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
Optional<Range<Long>> range =
powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
return range.isPresent() && range.get().contains(power);
}
return false;
}
@Override
public boolean attenuationInRange(DeviceId deviceId, PortNumber outPort,
long att) {
checkNotNull(deviceId);
checkNotNull(outPort);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
OchSignal stubOch = OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, 0);
Optional<Range<Long>> range =
powerConfig.getTargetPowerRange(outPort, stubOch);
return range.isPresent() && range.get().contains(att);
}
return false;
}
@Override
public boolean validInputPort(DeviceId deviceId, PortNumber portNumber) {
checkNotNull(deviceId);
checkNotNull(portNumber);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
Optional<Range<Long>> range =
powerConfig.getInputPowerRange(portNumber, Direction.ALL);
return range.isPresent();
}
return false;
}
@Override
public boolean validOutputPort(DeviceId deviceId, PortNumber portNumber) {
return hasPortTargetPower(deviceId, portNumber);
}
@Override
public boolean validChannel(DeviceId deviceId, PortNumber portNumber,
OchSignal ochSignal) {
checkNotNull(deviceId);
checkNotNull(portNumber);
LambdaQuery lambdaQuery = getLambdaQuery(deviceId);
if (lambdaQuery != null) {
Set<OchSignal> channels = lambdaQuery.queryLambdas(portNumber);
return channels.contains(ochSignal);
}
return false;
}
@Override
public boolean channelAvailable(DeviceId deviceId, OchSignal ochSignal) {
checkNotNull(deviceId);
checkNotNull(ochSignal);
for (FlowEntry entry : flowRuleService.getFlowEntries(deviceId)) {
if (ChannelData.fromFlow(entry).ochSignal().equals(ochSignal)) {
return false;
}
}
return true;
}
@Override
public boolean validConnection(DeviceId deviceId, PortNumber inPort,
PortNumber outPort) {
checkNotNull(deviceId);
checkNotNull(inPort);
checkNotNull(outPort);
return validInputPort(deviceId, inPort) && validOutputPort(deviceId, outPort);
}
@Override
public Range<Long> targetPortPowerRange(DeviceId deviceId, PortNumber portNumber) {
checkNotNull(deviceId);
checkNotNull(portNumber);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
Optional<Range<Long>> range =
powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
if (range.isPresent()) {
return range.get();
}
}
return null;
}
@Override
public Range<Long> attenuationRange(DeviceId deviceId, PortNumber portNumber,
OchSignal ochSignal) {
checkNotNull(deviceId);
checkNotNull(portNumber);
checkNotNull(ochSignal);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
Optional<Range<Long>> range =
powerConfig.getTargetPowerRange(portNumber, ochSignal);
if (range.isPresent()) {
return range.get();
}
}
return null;
}
@Override
public Range<Long> inputPortPowerRange(DeviceId deviceId, PortNumber portNumber) {
checkNotNull(deviceId);
checkNotNull(portNumber);
PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
if (powerConfig != null) {
Optional<Range<Long>> range =
powerConfig.getInputPowerRange(portNumber, Direction.ALL);
if (range.isPresent()) {
return range.get();
}
}
return null;
}
// Listens to device events.
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent deviceEvent) {
Device device = deviceEvent.subject();
switch (deviceEvent.type()) {
case DEVICE_ADDED:
case DEVICE_UPDATED:
initDevice(device.id());
break;
case PORT_ADDED:
case PORT_UPDATED:
setInitialTargetPortPower(device.id(), deviceEvent.port().number());
break;
default:
break;
}
}
}
}

View File

@ -0,0 +1,235 @@
/*
* 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.
*/
package org.onosproject.roadm;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Range;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DeviceId;
import org.onosproject.net.optical.OpticalAnnotations;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.table.TableModel;
import org.onosproject.ui.table.TableRequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.List;
/**
* Table-View message handler for ROADM port view.
*/
public class RoadmPortViewMessageHandler extends UiMessageHandler {
private static final String ROADM_PORT_DATA_REQ = "roadmPortDataRequest";
private static final String ROADM_PORT_DATA_RESP = "roadmPortDataResponse";
private static final String ROADM_PORTS = "roadmPorts";
private static final String ROADM_SET_TARGET_POWER_REQ = "roadmSetTargetPowerRequest";
private static final String ROADM_SET_TARGET_POWER_RESP = "roadmSetTargetPowerResponse";
private static final String NO_ROWS_MESSAGE = "No items found";
private static final String DEV_ID = "devId";
private static final String ID = "id";
private static final String TYPE = "type";
private static final String NAME = "name";
private static final String ENABLED = "enabled";
private static final String MIN_FREQ = "minFreq";
private static final String MAX_FREQ = "maxFreq";
private static final String GRID = "grid";
private static final String INPUT_POWER_RANGE = "inputPowerRange";
private static final String CURRENT_POWER = "currentPower";
private static final String TARGET_POWER = "targetPower";
private static final String HAS_TARGET_POWER = "hasTargetPower";
private static final String[] COLUMN_IDS = {
ID, TYPE, NAME, ENABLED, MIN_FREQ, MAX_FREQ, GRID, INPUT_POWER_RANGE,
CURRENT_POWER, TARGET_POWER, HAS_TARGET_POWER,
};
private static final String NA = "N/A";
private static final String UNKNOWN = "Unknown";
private static final long GHZ = 1_000_000_000L;
private static final long THZ = 1_000_000_000_000L;
private DeviceService deviceService;
private RoadmService roadmService;
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public void init(UiConnection connection, ServiceDirectory directory) {
super.init(connection, directory);
deviceService = get(DeviceService.class);
roadmService = get(RoadmService.class);
}
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new PortTableDataRequestHandler(),
new SetTargetPowerRequestHandler()
);
}
private String asGHz(String value) {
return String.valueOf(Double.valueOf(value) / GHZ);
}
private String asTHz(String value) {
return String.valueOf(Double.valueOf(value) / THZ);
}
private String annotation(Port port, String key, String defaultValue) {
String value = port.annotations().value(key);
return value != null ? value : defaultValue;
}
private String annotation(Port port, String key) {
return annotation(port, key, NA);
}
// Handler for sample table requests
private final class PortTableDataRequestHandler extends TableRequestHandler {
private PortTableDataRequestHandler() {
super(ROADM_PORT_DATA_REQ, ROADM_PORT_DATA_RESP, ROADM_PORTS);
}
@Override
protected String[] getColumnIds() {
return COLUMN_IDS;
}
@Override
protected String noRowsMessage(ObjectNode payload) {
return NO_ROWS_MESSAGE;
}
@Override
protected void populateTable(TableModel tm, ObjectNode payload) {
DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
if (deviceService.isAvailable(deviceId)) {
List<Port> ports = deviceService.getPorts(deviceId);
for (Port port : ports) {
populateRow(tm.addRow(), port, deviceId);
}
}
}
private void populateRow(TableModel.Row row, Port port, DeviceId deviceId) {
row.cell(ID, port.number().toLong())
.cell(TYPE, port.type())
.cell(ENABLED, port.isEnabled())
.cell(NAME, annotation(port, AnnotationKeys.PORT_NAME))
.cell(MIN_FREQ, asTHz(annotation(port, OpticalAnnotations.MIN_FREQ_HZ)))
.cell(MAX_FREQ, asTHz(annotation(port, OpticalAnnotations.MAX_FREQ_HZ)))
.cell(GRID, asGHz(annotation(port, OpticalAnnotations.GRID_HZ)))
.cell(INPUT_POWER_RANGE, getInputPowerRange(deviceId, port.number()))
.cell(CURRENT_POWER, getCurrentPower(deviceId, port.number()))
.cell(TARGET_POWER, getTargetPower(deviceId, port.number()))
.cell(HAS_TARGET_POWER, roadmService.hasPortTargetPower(deviceId, port.number()));
}
// Returns the input power range as a string, N/A if the port is not an
// input port
private String getInputPowerRange(DeviceId deviceId, PortNumber portNumber) {
Range<Long> range =
roadmService.inputPortPowerRange(deviceId, portNumber);
if (range != null) {
return range.toString();
}
return NA;
}
// Returns the current power as a string, Unknown if no value can be found.
private String getCurrentPower(DeviceId deviceId, PortNumber portNumber) {
Long currentPower =
roadmService.getCurrentPortPower(deviceId, portNumber);
if (currentPower != null) {
return String.valueOf(currentPower);
}
return UNKNOWN;
}
// Returns target power as a string, Unknown if target power is expected but
// cannot be found, N/A if port does not have configurable target power
private String getTargetPower(DeviceId deviceId, PortNumber portNumber) {
if (roadmService.hasPortTargetPower(deviceId, portNumber)) {
Long targetPower =
roadmService.getTargetPortPower(deviceId, portNumber);
if (targetPower != null) {
return String.valueOf(targetPower);
} else {
return UNKNOWN;
}
}
return NA;
}
}
// Handler for setting port target power
private final class SetTargetPowerRequestHandler extends RequestHandler {
private static final String VALID = "valid";
private static final String MESSAGE = "message";
private static final String TARGET_POWER_ERR_MSG = "Target power range is %s.";
private SetTargetPowerRequestHandler() {
super(ROADM_SET_TARGET_POWER_REQ);
}
@Override
public void process(ObjectNode payload) {
DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
PortNumber portNumber = PortNumber.portNumber(payload.get(ID).asLong());
long targetPower = payload.get(TARGET_POWER).asLong();
boolean validTargetPower;
Range<Long> range =
roadmService.targetPortPowerRange(deviceId, portNumber);
if (range != null) {
validTargetPower = range.contains(targetPower);
if (validTargetPower) {
roadmService.setTargetPortPower(deviceId, portNumber, targetPower);
}
ObjectNode rootNode = objectNode();
rootNode.put(ID, payload.get(ID).asText());
rootNode.put(VALID, validTargetPower);
rootNode.put(MESSAGE, String.format(TARGET_POWER_ERR_MSG, range.toString()));
sendMessage(ROADM_SET_TARGET_POWER_RESP, rootNode);
} else {
log.warn("Unable to determine target power range for device {}", deviceId);
}
}
}
}

View File

@ -0,0 +1,283 @@
/*
* 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.
*/
package org.onosproject.roadm;
import com.google.common.collect.Range;
import org.onosproject.net.DeviceId;
import org.onosproject.net.OchSignal;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.FlowId;
import java.util.Set;
/**
* ROADM service interface. Provides an interface for ROADM power configuration.
*
* This application relies on the PowerConfig and LambdaQuery behaviours.
*
* The device's PowerConfig implementation should be parameterized as
* {@code PowerConfig<Object>} in order to support both Direction and OchSignal.
* For a reference implementation of PowerConfig, please see
* OplinkRoadmPowerConfig
*
* In this application, a "connection" refers to the selection of a channel
* to direct from an input to an output port. Connections are implemented
* using FlowRules with an input port selector, optical channel selector,
* and output port treatment (see RoadmManager#createConnection()).
*
* This application currently only supports fixed grid channels.
*/
public interface RoadmService {
/**
* Set target power for a port if the port has configurable target power.
*
* @param deviceId DeviceId of the device to configure
* @param portNumber PortNumber of the port to configure
* @param power value to set target power to
*/
void setTargetPortPower(DeviceId deviceId, PortNumber portNumber, long power);
/**
* Returns the target power for a port if the port has configurable target power.
*
* @param deviceId DeviceId of the device to configure
* @param portNumber PortNumber of the port to configure
* @return the target power if the port has a target power, null otherwise
*/
Long getTargetPortPower(DeviceId deviceId, PortNumber portNumber);
/**
* Sets the attenuation of a connection. This does not check that attenuation
* is within the acceptable range.
*
* @param deviceId DeviceId of the device to configure
* @param portNumber PortNumber of either the input or output port
* @param ochSignal channel to set attenuation for
* @param attenuation attenuation value to set to
*/
void setAttenuation(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal,
long attenuation);
/**
* Returns the attenuation of a connection.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of either the input or output port
* @param ochSignal channel to search for
* @return attenuation if found, null otherwise
*/
Long getAttenuation(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal);
/**
* Returns the current port power.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of the port
* @return current power if found, null otherwise
*/
Long getCurrentPortPower(DeviceId deviceId, PortNumber portNumber);
/**
* Returns the current channel power.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of either the input or output port of the connection
* @param ochSignal channel to search for
* @return channel power if found, null otherwise
*/
Long getCurrentChannelPower(DeviceId deviceId, PortNumber portNumber,
OchSignal ochSignal);
/**
* Returns the channels supported by a port.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of the port
* @return the set of supported channels
*/
Set<OchSignal> queryLambdas(DeviceId deviceId, PortNumber portNumber);
/**
* Creates a new internal connection on a device without attenuation. This does
* not check that the connection is actually valid (e.g. an input port to an
* output port).
*
* Connections are represented as flows with an input port, output port, and
* channel. Implementation of attenuation is up to the vendor.
*
* @param deviceId DeviceId of the device to create this connection for
* @param priority priority of the flow
* @param isPermanent permanence of the flow
* @param timeout timeout in seconds
* @param inPort input port
* @param outPort output port
* @param ochSignal channel to use
* @return FlowId of the FlowRule representing the connection
*/
FlowId createConnection(DeviceId deviceId, int priority, boolean isPermanent,
int timeout, PortNumber inPort, PortNumber outPort,
OchSignal ochSignal);
/**
* Creates a new internal connection on a device with attenuation. This does
* not check that the connection is actually valid (e.g. an input port to an
* output port, attenuation if within the acceptable range).
*
* Connections are represented as flows with an input port, output port, and
* channel. Implementation of attenuation is up to the vendor.
*
* @param deviceId DeviceId of the device to create this connection for
* @param priority priority of the flow
* @param isPermanent permanence of the flow
* @param timeout timeout in seconds
* @param inPort input port
* @param outPort output port
* @param ochSignal channel to use
* @param attenuation attenuation of the connection
* @return FlowId of the FlowRule representing the connection
*/
FlowId createConnection(DeviceId deviceId, int priority, boolean isPermanent,
int timeout, PortNumber inPort, PortNumber outPort,
OchSignal ochSignal, long attenuation);
/**
* Removes an internal connection from a device by matching the FlowId and
* removing the flow representing the connection. This will remove any flow
* from any device so FlowId should correspond with a connection flow.
*
* @param deviceId DeviceId of the device to remove the connection from
* @param flowId FlowId of the flow representing the connection to remove
*/
void removeConnection(DeviceId deviceId, FlowId flowId);
/**
* Returns true if the target power for this port can be configured.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of the port to check
* @return true if the target power for this port can be configured, false
* otherwise
*/
boolean hasPortTargetPower(DeviceId deviceId, PortNumber portNumber);
/**
* Returns true if value is within the acceptable target power range of the port.
* Returns false if the port does not have a configurable target
* power.
*
* @param deviceId DeviceId of the device to check
* @param portNumber PortNumber of the port to check
* @param power value to check
* @return true if value is within the acceptable target power range, false
* otherwise
*/
boolean portTargetPowerInRange(DeviceId deviceId, PortNumber portNumber, long power);
/**
* Returns true if value is within the acceptable attenuation range of a
* connection, and always returns false if the connection does not support
* attenuation. The attenuation range is determined by either the input
* or output port of the connection.
*
* @param deviceId DeviceId of the device to check
* @param portNumber PortNumber of either the input or output port of the connection
* @param att value to check
* @return true if value is within the acceptable attenuation range, false
* otherwise
*/
boolean attenuationInRange(DeviceId deviceId, PortNumber portNumber, long att);
/**
* Returns true if the port is an input port.
*
* @param deviceId DeviceId of the device to check
* @param portNumber PortNumber of the port to check
* @return true if the port is an input port, false otherwise
*/
boolean validInputPort(DeviceId deviceId, PortNumber portNumber);
/**
* Returns true if the port is an output port.
*
* @param deviceId DeviceId of the device to check
* @param portNumber PortNumber of the port to check
* @return true if the port is an output port, false otherwise
*/
boolean validOutputPort(DeviceId deviceId, PortNumber portNumber);
/**
* Returns true if the channel is supported by the port. The port can be either
* an input or output port.
*
* @param deviceId DeviceId of the device to check
* @param portNumber PortNumber of the port to check
* @param ochSignal channel to check
* @return true if the channel is supported by the port, false otherwise
*/
boolean validChannel(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal);
/**
* Returns true if the channel is not being used by a connection on the
* device.
*
* @param deviceId DeviceId of the device to check
* @param ochSignal channel to check
* @return true if the channel is not in use, false otherwise
*/
boolean channelAvailable(DeviceId deviceId, OchSignal ochSignal);
/**
* Returns true if the connection from the input port to the output port is
* valid. This currently only checks if the given input and output ports are,
* respectively, valid input and output ports.
*
* @param deviceId DeviceId of the device to check
* @param inPort input port of the connection
* @param outPort output port of the connection
* @return true if the connection is valid, false otherwise
*/
boolean validConnection(DeviceId deviceId, PortNumber inPort, PortNumber outPort);
/**
* Returns the acceptable target port power range for a port.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of the port
* @return range if found, null otherwise
*/
Range<Long> targetPortPowerRange(DeviceId deviceId, PortNumber portNumber);
/**
* Returns the acceptable attenuation range for a connection.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of either the input or output port
* @param ochSignal channel to check
* @return range if found, null otherwise
*/
Range<Long> attenuationRange(DeviceId deviceId, PortNumber portNumber,
OchSignal ochSignal);
/**
* Returns the expected input power range for an input port.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of an input port
* @return range if found, null otherwise
*/
Range<Long> inputPortPowerRange(DeviceId deviceId, PortNumber portNumber);
}

View File

@ -0,0 +1,67 @@
/*
* 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.
*/
package org.onosproject.roadm;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
/**
* Interface for the ROADM store. Currently used to store target power only.
* This should be removed if target power could be read port annotations.
*/
public interface RoadmStore {
/**
* Adds the device to the store.
*
* <p>The device needs to be added to the store
* before setTargetPower and getTargetPower can be used. This does not initialize
* any of the target powers.
*
* @param deviceId DeviceId of the device to add
*/
void addDevice(DeviceId deviceId);
/**
* Returns true if the device has been added to the store.
*
* @param deviceId DeviceId of the device to check
* @return true if device has been added to the store, false otherwise
*/
boolean deviceAvailable(DeviceId deviceId);
/**
* Stores the targetPower for a port on a device. The device needs to be added
* to the store before this can be called. This does nothing if the device is
* not added.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of the port
* @param targetPower target port power to store
*/
void setTargetPower(DeviceId deviceId, PortNumber portNumber, long targetPower);
/**
* Returns the targetPower for a port on a device. The device needs to be added
* to the store before this can be called. Returns null if the port's target
* power has not yet been initialized using setTargetPower.
*
* @param deviceId DeviceId of the device
* @param portNumber PortNumber of the port
* @return target power if target power has already been set, null otherwise
*/
Long getTargetPower(DeviceId deviceId, PortNumber portNumber);
}

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* Application to monitor and configure ROADM devices.
*/
package org.onosproject.roadm;

View File

@ -0,0 +1,39 @@
/* css for ROADM device table view */
.less-gap {
margin-top: -20px;
}
#ov-roadm-device h2 {
display: inline-block;
}
/* Panel Styling */
#ov-roadm-device-item-details-panel.floatpanel {
position: absolute;
top: 115px;
}
.light #ov-roadm-device-item-details-panel.floatpanel {
background-color: rgb(229, 234, 237);
}
.dark #ov-roadm-device-item-details-panel.floatpanel {
background-color: #3A4042;
}
#ov-roadm-device-item-details-panel h3 {
margin: 0;
font-size: large;
}
#ov-roadm-device-item-details-panel h4 {
margin: 0;
}
#ov-roadm-device-item-details-panel td {
padding: 5px;
}
#ov-roadm-device-item-details-panel td.label {
font-style: italic;
opacity: 0.8;
}

View File

@ -0,0 +1,70 @@
<!-- partial HTML -->
<div id="ov-roadm-device" class="less-gap">
<div class="tabular-header">
<h2>Optical Devices ({{tableData.length}} total)</h2>
<div class="ctrl-btns">
<div class="refresh" ng-class="{active: autoRefresh}"
icon icon-id="refresh" icon-size="42"
tooltip tt-msg="autoRefreshTip"
ng-click="toggleRefresh()"></div>
<div class="separator"></div>
<div ng-class="{'current-view': !!selId}"
icon icon-id="deviceTable" icon-size="42"></div>
<div ng-class="{active: !!selId}"
icon icon-id="flowTable" icon-size="42"
tooltip tt-msg="flowTip"
ng-click="nav('roadmFlow')"></div>
<div ng-class="{active: !!selId}"
icon icon-id="portTable" icon-size="42"
tooltip tt-msg="portTip"
ng-click="nav('roadmPort')"></div>
</div>
</div>
<div class="summary-list" onos-table-resize>
<div class="table-header" onos-sortable-header>
<table>
<tr>
<td colId="name"sortable>Friendly Name</td>
<td colId="id" sortable>Device ID </td>
<td colId="master" sortable col-width="120px">Master </td>
<td colId="ports" sortable col-width="70px">Ports </td>
<td colId="vendor" sortable>Vendor </td>
<td colId="hwVersion" sortable>H/W Version </td>
<td colId="swVersion" sortable>S/W Version </td>
<td colId="protocol" sortable col-width="100px">Protocol </td>
</tr>
</table>
</div>
<div class="table-body">
<table>
<tr ng-if="!tableData.length" class="no-data">
<td colspan="3">
{{annots.no_rows_msg}}
</td>
</tr>
<tr ng-repeat="item in tableData track by $index"
ng-class="{selected: item.id === selId}"
ng-click="selectCallback($event, item)">
<td>{{item.name}}</td>
<td>{{item.id}}</td>
<td>{{item.master}}</td>
<td>{{item.ports}}</td>
<td>{{item.vendor}}</td>
<td>{{item.hwVersion}}</td>
<td>{{item.swVersion}}</td>
<td>{{item.protocol}}</td>
</tr>
</table>
</div>
</div>
</div>

View File

@ -0,0 +1,49 @@
// js for roadm device table view
(function () {
'use strict';
// injected refs
var $log, $scope, $loc, wss, ns;
// constants
var detailsReq = 'roadmDeviceDetailsRequest';
angular.module('ovRoadmDevice', [])
.controller('OvRoadmDeviceCtrl',
['$log', '$scope', '$location', 'TableBuilderService', 'WebSocketService',
'NavService',
function (_$log_, _$scope_, _$loc_, tbs, _wss_, _ns_) {
$log = _$log_;
$scope = _$scope_;
$loc = _$loc_;
wss = _wss_;
ns = _ns_;
// query for if a certain device needs to be highlighted
var params = $loc.search();
if (params.hasOwnProperty('devId')) {
$scope.selId = params['devId'];
}
// TableBuilderService creating a table for us
tbs.buildTable({
scope: $scope,
tag: 'roadmDevice'
});
$scope.nav = function (path) {
if ($scope.selId) {
ns.navTo(path, { devId: $scope.selId });
}
};
// cleanup
$scope.$on('$destroy', function () {
//wss.unbindHandlers(handlers);
$log.log('OvRoadmDeviceCtrl has been destroyed');
});
$log.log('OvRoadmDeviceCtrl has been created');
}]);
}());

View File

@ -0,0 +1,132 @@
/* css for ROADM flow table view */
#ov-roadm-flow h2 {
display: inline-block;
}
/* Panel Styling */
#ov-roadm-flow-item-details-panel.floatpanel {
position: absolute;
top: 115px;
}
.light #ov-roadm-flow-item-details-panel.floatpanel {
background-color: rgb(229, 234, 237);
}
.dark #ov-roadm-flow-item-details-panel.floatpanel {
background-color: #3A4042;
}
#ov-roadm-flow-item-details-panel h3 {
margin: 0;
font-size: large;
}
#ov-roadm-flow-item-details-panel h4 {
margin: 0;
}
#ov-roadm-flow-item-details-panel td {
padding: 5px;
}
#ov-roadm-flow-item-details-panel td.label {
font-style: italic;
opacity: 0.8;
}
#ov-roadm-flow .table-header span.units {
font-variant: normal;
text-transform: none;
}
/* editable attenuation */
#ov-roadm-flow .editable span {
width: 100%;
display: inline-block;
}
#ov-roadm-flow .editable span.attenuation:hover {
color: #009fdb
}
#ov-roadm-flow .editable input {
padding: 0;
}
#ov-roadm-flow .editable button {
margin: 0;
padding: 0px 5px 0px 5px;
}
#ov-roadm-flow .editable input {
width: 80px;
}
#ov-roadm-flow .editable .input-error {
color: red;
font-size: 10px;
width: 180px;
}
/* delete flow button */
#ov-roadm-flow .table-body .delete-icon {
font-size: 24px;
line-height: 0px;
text-align: center;
}
#ov-roadm-flow .table-body .delete-icon:hover {
color: red;
}
/* Create connection form */
#ov-roadm-flow div.flow-form {
background-color: #ffffff;
border: 1px solid #888888;
width: 720px;
height: 270px;
padding: 20px;
position: absolute;
right: 15px;
bottom: 15px;
}
#ov-roadm-flow .flow-form div.delete-icon {
cursor: pointer;
cursor: hand;
font-size: 36px;
position: absolute;
right: 20px;
top: 5px;
}
#ov-roadm-flow .flow-form label {
width: 150px;
display: inline-block;
}
#ov-roadm-flow .flow-form input {
width: 150px;
display: inline-block
}
#ov-roadm-flow .flow-form select {
width: 150px;
}
#ov-roadm-flow .flow-form .form-error {
margin-left: 15px;
color: red;
}
#ov-roadm-flow .flow-form form {
font-size: 14px;
color: #444444;
line-height: 26px;
margin-bottom: 15px;
}
#ov-roadm-flow .flow-form button.submit {
margin-left: 150px;
width: 150px;
}

View File

@ -0,0 +1,118 @@
<!-- partial HTML -->
<div id="ov-roadm-flow" class="less-gap">
<div class="tabular-header">
<h2>Connections for Optical Device {{devId}} ({{tableData.length}} total)</h2>
<div class="ctrl-btns">
<div class="active"
icon icon-id="plus" icon-size="42"
tooltip tt-msg="addFlowTip"
ng-click="displayFlowForm()"></div>
<div class="refresh" ng-class="{active: autoRefresh}"
icon icon-id="refresh" icon-size="42"
tooltip tt-msg="autoRefreshTip"
ng-click="toggleRefresh()"></div>
<div class="separator"></div>
<div class="active"
icon icon-id="deviceTable" icon-size="42"
tooltip tt-msg="deviceTip"
ng-click="nav('roadmDevice')"></div>
<div class="current-view"
icon icon-id="flowTable" icon-size="42"
tooltip tt-msg="flowTip"></div>
<div class="active"
icon icon-id="portTable" icon-size="42"
tooltip tt-msg="portTip"
ng-click="nav('roadmPort')"></div>
</div>
</div>
<div class="summary-list" onos-table-resize>
<div class="table-header" onos-sortable-header>
<table>
<tr>
<td col-width="30px"></td>
<td colId="flowId" sortable col-width="180px">Flow ID </td>
<td colId="appId" sortable>App ID </td>
<td colId="priority" sortable>Priority </td>
<td colId="timeout" sortable>Timeout </td>
<td colId="permanent" sortable>Permanent </td>
<td colId="state" sortable>State </td>
<td colId="inPort" sortable>In Port </td>
<td colId="outPort" sortable>Out Port </td>
<td colId="multiplier" sortable>Channel </td>
<td colId="spacing">Spacing <span class="units">(GHz)</span> </td>
<td colId="currentPower" col-width="180px">Current Power <span class="units">(0.01dBm)</span></td>
<td colId="attenuation" col-width="200px">Attenuation <span class="units">(0.01dB)</span></td>
</tr>
</table>
</div>
<div class="table-body">
<table>
<tr ng-if="!tableData.length" class="no-data">
<td colspan="13">
{{annots.no_rows_msg}}
</td>
</tr>
<tr ng-repeat="flow in tableData track by $index"
ng-class="{selected: flow.id === selId}">
<td class="delete-icon" ng-click="deleteFlow($event, flow)">&#215;</td>
<td>{{flow.flowId}}</td>
<td>{{flow.appId}}</td>
<td>{{flow.priority}}</td>
<td>{{flow.timeout}}</td>
<td>{{flow.permanent}}</td>
<td>{{flow.state}}</td>
<td>{{flow.inPort}}</td>
<td>{{flow.outPort}}</td>
<td>{{flow.multiplier}} ({{flow.multiplier * 0.05 + 193.1 | number:2}} THz)</td>
<td>{{flow.spacing}}</td>
<td>{{flow.currentPower}}</td>
<td class="editable" roadm-att="flow" roadm-set-att="setAttenuation(flow, targetVal, cb)"></td>
</tr>
</table>
</div>
</div>
<div class="flow-form" ng-controller="FlowFormController as form" ng-show="showFlowForm">
<div class="delete-icon" ng-click="hideFlowForm()">&#215;</div>
<form>
<label>Priority</label><input type="number" ng-model="form.flow.priority" />
<span class="form-error" ng-show="form.priorityError" >{{form.priorityMessage}}</span><br />
<label>Is Permanent</label><input type="checkbox" ng-model="form.flow.permanent" /><br />
<label>Timeout</label><input type="number" ng-model="form.flow.timeout" ng-disabled="form.flow.permanent"/>
<span class="form-error" ng-show="form.timeoutError" >{{form.timeoutMessage}}</span><br />
<label>In Port</label><input type="number" ng-model="form.flow.inPort" />
<span class="form-error" ng-show="form.inPortError">{{form.inPortMessage}}</span><br />
<label>Out Port</label><input type="number" ng-model="form.flow.outPort" />
<span class="form-error" ng-show="form.outPortError">{{form.outPortMessage}}</span>
<span class="form-error" ng-show="form.connectionError">{{form.connectionMessage}}</span><br />
<label>Channel Spacing</label><select ng-model="form.flow.spacing" ng-options="x.freq for x in form.spacings"></select>
<span class="form-error" ng-show="form.spacingError">{{form.spacingMessage}}</span><br />
<label>Spacing Multiplier</label><input type="number" ng-model="form.flow.multiplier" />
<span class="form-error" ng-show="form.multiplierError">{{form.multiplierMessage}}</span>
<span class="form-error" ng-show="form.channelError">{{form.channelMessage}}</span><br />
<label>Include Attenuation</label><input type="checkbox" ng-model="form.flow.includeAttenuation" /><br />
<label>Attenuation</label><input type="number" ng-model="form.flow.attenuation" ng-disabled="!form.flow.includeAttenuation"/>
<span class="form-error" ng-show="form.attenuationError">{{form.attenuationMessage}}</span><br />
</form>
<button type="submit" class="submit" ng-click="form.createFlow(form.flow)">Create Connection</button>
</div>
</div>

View File

@ -0,0 +1,324 @@
// js for roadm flow table view
(function () {
'use strict';
var SET_ATT_REQ = "roadmSetAttenuationRequest";
var SET_ATT_RESP = "roadmSetAttenuationResponse";
var DELETE_FLOW_REQ = "roadmDeleteFlowRequest";
var CREATE_FLOW_REQ = "roadmCreateFlowRequest";
var CREATE_FLOW_RESP = "roadmCreateFlowResponse";
// injected references
var $log, $scope, $location, fs, tbs, wss, ns;
// used to map id to a request call function
var flowCbTable = {};
function setAttenuation(flow, targetVal, cb) {
flowCbTable[flow.id] = cb;
wss.sendEvent(SET_ATT_REQ,
{
devId: $scope.devId,
flowId: flow.id,
attenuation: targetVal
});
}
function attenuationCb(data) {
flowCbTable[data.flowId](data.valid, data.message);
}
// check if value is an integer
function isInteger(val) {
var INTEGER_REGEXP = /^\-?\d+$/;
if (INTEGER_REGEXP.test(val)) {
return true;
}
return false;
}
angular.module('ovRoadmFlow', [])
.controller('OvRoadmFlowCtrl',
['$log', '$scope', '$location',
'FnService', 'TableBuilderService', 'WebSocketService', 'NavService',
function (_$log_, _$scope_, _$location_, _fs_, _tbs_, _wss_, _ns_) {
var params;
$log = _$log_;
$scope = _$scope_;
$location = _$location_;
fs = _fs_;
tbs = _tbs_;
wss = _wss_;
ns = _ns_;
$scope.addFlowTip = 'Create a flow';
$scope.deviceTip = 'Show device table';
$scope.flowTip = 'Show flow view for this device';
$scope.groupTip = 'Show group view for this device';
$scope.meterTip = 'Show meter view for selected device';
$scope.showFlowForm = false;
var handlers = {};
handlers[SET_ATT_RESP] = attenuationCb;
wss.bindHandlers(handlers);
params = $location.search();
if (params.hasOwnProperty('devId')) {
$scope.devId = params['devId'];
}
tbs.buildTable({
scope: $scope,
tag: 'roadmFlow',
query: params
});
$scope.displayFlowForm = function () {
$scope.showFlowForm = true;
}
$scope.hideFlowForm = function () {
$scope.showFlowForm = false;
}
$scope.setAttenuation = setAttenuation;
$scope.deleteFlow = function ($event, row) {
wss.sendEvent(DELETE_FLOW_REQ,
{
devId: $scope.devId,
id: row.id
});
}
$scope.createFlow = function(flow) {
wss.sendEvent(CREATE_FLOW_REQ,
{
devId: $scope.devId,
flow: flow
});
}
$scope.fakeCurrentPower = function(flow) {
if (!isNaN(flow.currentPower)) {
var val = parseInt(flow.attenuation);
return val + (val % 5 - 2);
} else {
return flow.currentPower;
}
}
$scope.nav = function (path) {
if ($scope.devId) {
ns.navTo(path, { devId: $scope.devId });
}
};
$scope.$on('$destroy', function () {
wss.unbindHandlers(handlers);
});
$log.log('OvRoadmFlowCtrl has been created');
}])
.directive('roadmAtt', ['WebSocketService', function() {
var retTemplate =
'<span class="attenuation" ng-show="!editMode" ng-click="enableEdit()">{{currItem.attenuation}}</span>' +
'<form ng-show="editMode" name="form" novalidate>' +
'<input type="number" name="formVal" ng-model="formVal">' +
'<button type="submit" class="submit" ng-click="send()">Set</button>' +
'<button type="button" class="cancel" ng-click="cancel()">Cancel</button>' +
'<span class="input-error" ng-show="showError">{{errorMessage}}</span>' +
'</form>';
return {
restrict: 'A',
scope: {
currItem: '=roadmAtt',
roadmSetAtt: '&'
},
template: retTemplate,
link: function ($scope, $element) {
$scope.editMode = false;
$scope.showError = false;
$scope.errorMessage = "Invalid attenuation"
},
controller: function($scope, $timeout) {
$scope.enableEdit = function() {
// connection must support attenuation to be editable
if ($scope.editMode === false) {
// Ensure that the entry being edited remains the same even
// if the table entries are shifted around.
$scope.targetItem = $scope.currItem;
// Ensure the value seen in the field remains the same
$scope.formVal = parseInt($scope.currItem.attenuation);
$scope.editMode = true;
$timeout(function () {
$scope.$apply()
});
}
};
$scope.send = function() {
// check input is an integer
if (!isInteger($scope.formVal)) {
$scope.sendCb(false, "Attenuation must be an integer");
return;
}
$scope.roadmSetAtt({flow: $scope.targetItem, targetVal: $scope.formVal, cb: $scope.sendCb});
};
// Callback for server-side validation. Displays the error message
// if the input is invalid.
$scope.sendCb = function(valid, message) {
if (valid) {
// check if it's still pointing to the same item
// reordering the entries may change the binding
if ($scope.currItem.id === $scope.targetItem.id) {
// update the ui to display the new attenuation value
$scope.currItem.attenuation = $scope.formVal;
}
$scope.cancel();
} else {
$scope.errorMessage = message;
$scope.showError = true;
}
$timeout(function () {
$scope.$apply()
});
}
$scope.cancel = function() {
$scope.editMode = false;
$scope.showError = false;
}
}
};
}])
.controller('FlowFormController', function($timeout) {
var notIntegerError = "Must be an integer.";
this.clearErrors = function() {
this.priorityError = false;
this.timeoutError = false;
this.isPermanentError = false;
this.inPortError = false;
this.outPortError = false;
this.spacingError = false;
this.multiplierError = false;
this.attenuationError = false;
this.connectionError = false;
this.channelError = false;
}
this.clearErrors();
this.spacings = [
{index: 0, freq: "100 GHz"},
{index: 1, freq: "50 GHz"},
{index: 2, freq: "25 GHz"},
{index: 3, freq: "12.5 GHz"}
];
this.flow = {};
//this.flow.priority = 88;
this.flow.permanent = true;
this.flow.timeout = 0;
//this.flow.inPort = 2;
//this.flow.outPort = 2;
this.flow.spacing = this.spacings[1];
//this.flow.multiplier = 0;
this.flow.includeAttenuation = true;
this.flow.attenuation = 0;
var parent = this;
function createFlowCb(data) {
if (!data.inPort.valid) {
parent.inPortMessage = data.inPort.message;
parent.inPortError = true;
}
if (!data.outPort.valid) {
parent.outPortMessage = data.outPort.message;
parent.outPortError = true;
}
if (!data.connection.valid) {
parent.connectionMessage = data.connection.message;
parent.connectionError = true;
}
if (!data.spacing.valid) {
parent.spacingMessage = data.spacing.message;
parent.spacingError = true;
}
if (!data.multiplier.valid) {
parent.multiplierMessage = data.multiplier.message;
parent.multiplierError = true;
}
if (!data.channelAvailable.valid) {
parent.channelMessage = data.channelAvailable.message;
parent.channelError = true;
}
if (data.includeAttenuation && !data.attenuation.valid) {
parent.attenuationMessage = data.attenuation.message;
parent.attenuationError = true;
}
$timeout(function () {
$scope.$apply()
});
}
var handlers = {}
handlers[CREATE_FLOW_RESP] = createFlowCb;
wss.bindHandlers(handlers);
this.createFlow = function(connection) {
this.clearErrors();
var error = false;
if (!isInteger(connection.priority)) {
this.priorityMessage = notIntegerError;
this.priorityError = true;
error = true;
}
if (!connection.permanent && !isInteger(connection.timeout)) {
this.timeoutMessage = notIntegerError;
this.timeoutError = true;
error = true;
}
if (!isInteger(connection.inPort)) {
this.inPortMessage = notIntegerError;
this.inPortError = true;
error = true;
}
if (!isInteger(connection.outPort)) {
this.outPortMessage = notIntegerError;
this.outPortError = true;
error = true;
}
if (!isInteger(connection.multiplier)) {
this.multiplierMessage = notIntegerError;
this.multiplierError = true;
error = true;
}
if (connection.includeAttenuation && !isInteger(connection.attenuation)) {
this.attenuationMessage = notIntegerError;
this.attenuationError = true;
error = true;
}
if (!error) {
wss.sendEvent(CREATE_FLOW_REQ,
{
devId: $scope.devId,
formData: connection
});
$log.log('Request to create connection has been sent');
}
}
$scope.$on('$destroy', function () {
wss.unbindHandlers(handlers);
});
});
}());

View File

@ -0,0 +1,69 @@
/* css for ROADM port table view */
#ov-roadm-port h2 {
display: inline-block;
}
/* Panel Styling */
#ov-roadm-port-item-details-panel.floatpanel {
position: absolute;
top: 115px;
}
.light #ov-roadm-port-item-details-panel.floatpanel {
background-color: rgb(229, 234, 237);
}
.dark #ov-roadm-port-item-details-panel.floatpanel {
background-color: #3A4042;
}
#ov-roadm-port-item-details-panel h3 {
margin: 0;
font-size: large;
}
#ov-roadm-port-item-details-panel h4 {
margin: 0;
}
#ov-roadm-port-item-details-panel td {
padding: 5px;
}
#ov-roadm-port-item-details-panel td.label {
font-style: italic;
opacity: 0.8;
}
#ov-roadm-port .table-header span.units {
font-variant: normal;
text-transform: none;
}
/* Editable Target Power field */
#ov-roadm-port .editable span {
width: 100%;
display: inline-block;
}
#ov-roadm-port .editable span.target-power:hover {
color: #009fdb
}
#ov-roadm-port .editable input {
padding: 0;
}
#ov-roadm-port .editable button {
margin: 0;
padding: 0px 5px 0px 5px;
}
#ov-roadm-port .editable input {
width: 80px;
}
#ov-roadm-port .editable .input-error {
color: red;
font-size: 10px;
width: 180px;
}

View File

@ -0,0 +1,73 @@
<!-- partial HTML -->
<div id="ov-roadm-port" class="less-gap">
<div class="tabular-header">
<h2>Ports for Optical Device {{devId}} ({{tableData.length}} total)</h2>
<div class="ctrl-btns">
<div class="refresh" ng-class="{active: autoRefresh}"
icon icon-id="refresh" icon-size="42"
tooltip tt-msg="autoRefreshTip"
ng-click="toggleRefresh()"></div>
<div class="separator"></div>
<div class="active"
icon icon-id="deviceTable" icon-size="42"
tooltip tt-msg="deviceTip"
ng-click="nav('roadmDevice')"></div>
<div class="active"
icon icon-id="flowTable" icon-size="42"
tooltip tt-msg="flowTip"
ng-click="nav('roadmFlow')"></div>
<div class="current-view"
icon icon-id="portTable" icon-size="42"
tooltip tt-msg="portTip"></div>
</div>
</div>
<div class="summary-list" onos-table-resize>
<div class="table-header" onos-sortable-header>
<table>
<tr>
<td colId="id" sortable>Port Number </td>
<td colId="name" sortable>Name </td>
<td colId="type" sortable>Type </td>
<td colId="enabled" sortable>Enabled </td>
<td colId="minFreq" sortable>Min Freq <span class="units">(THz)</span> </td>
<td colId="maxFreq" sortable>Max Freq <span class="units">(THz)</span> </td>
<td colId="grid" sortable>Grid <span class="units">(GHz)</span> </td>
<td colId="portMac" sortable>Input Power Range </td>
<td colId="currentPower">Current Power <span class="units">(0.01dBm)</span> </td>
<td colId="targetPower" col-width="200px">Target Power <span class="units">(0.01dBm)</span> </td>
</tr>
</table>
</div>
<div class="table-body">
<table>
<tr ng-if="!tableData.length" class="no-data">
<td colspan="10">
{{annots.no_rows_msg}}
</td>
</tr>
<tr ng-repeat="item in tableData track by $index"
ng-class="{selected: item.id === selId}">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.type}}</td>
<td>{{item.enabled}}</td>
<td>{{item.minFreq}}</td>
<td>{{item.maxFreq}}</td>
<td>{{item.grid}}</td>
<td>{{item.inputPowerRange}}</td>
<td>{{item.currentPower}}</td>
<td class="editable" roadm-power="item" roadm-set-power="setPortPower(port, targetVal, cb)"></td>
</tr>
</table>
</div>
</div>
</div>

View File

@ -0,0 +1,168 @@
// js for roadm port table view
(function () {
'use strict';
var SET_TARGET_POWER_REQ = "roadmSetTargetPowerRequest";
var SET_TARGET_POWER_RESP = "roadmSetTargetPowerResponse";
// injected references
var $log, $scope, $location, fs, tbs, wss, ns;
var portCbTable = {};
function setPortPower(port, targetVal, cb) {
var id = port.id;
portCbTable[id] = cb;
wss.sendEvent("roadmSetTargetPowerRequest",
{
devId: $scope.devId,
id: port.id,
targetPower: targetVal
});
}
function portPowerCb(data) {
portCbTable[data.id](data.valid, data.message);
}
// check if value is an integer
function isInteger(val) {
var INTEGER_REGEXP = /^\-?\d+$/;
if (INTEGER_REGEXP.test(val)) {
return true;
}
return false;
}
angular.module('ovRoadmPort', [])
.controller('OvRoadmPortCtrl',
['$log', '$scope', '$location',
'FnService', 'TableBuilderService', 'WebSocketService', 'NavService',
function (_$log_, _$scope_, _$location_, _fs_, _tbs_, _wss_, _ns_) {
var params;
$log = _$log_;
$scope = _$scope_;
$location = _$location_;
fs = _fs_;
tbs = _tbs_;
wss = _wss_;
ns = _ns_;
$scope.deviceTip = 'Show device table';
$scope.flowTip = 'Show flow view for this device';
$scope.groupTip = 'Show group view for this device';
$scope.meterTip = 'Show meter view for selected device';
var handlers = {};
handlers[SET_TARGET_POWER_RESP] = portPowerCb;
wss.bindHandlers(handlers);
params = $location.search();
if (params.hasOwnProperty('devId')) {
$scope.devId = params['devId'];
}
tbs.buildTable({
scope: $scope,
tag: 'roadmPort',
query: params
});
$scope.setPortPower = setPortPower;
$scope.setTargetPower = function (port, targetVal) {
wss.sendEvent("roadmSetTargetPowerRequest",
{
devId: $scope.devId,
id: port.id,
targetPower: targetVal
});
$log.debug('Got a click on:', port);
}
$scope.nav = function (path) {
if ($scope.devId) {
ns.navTo(path, { devId: $scope.devId });
}
};
$scope.$on('$destroy', function () {
wss.unbindHandlers(handlers);
});
$log.log('OvRoadmPortCtrl has been created');
}])
.directive('roadmPower', ['WebSocketService', function() {
var retTemplate =
'<span class="target-power" ng-show="!editMode" ng-click="enableEdit()">{{currItem.targetPower}}</span>' +
'<form ng-show="editMode" name="form" novalidate>' +
'<input type="number" name="formVal" ng-model="formVal">' +
'<button type="submit" ng-click="send()">Set</button>' +
'<button type="button" ng-click="cancel()">Cancel</button>' +
'<span class="input-error" ng-show="showError">{{errorMessage}}</span>' +
'</form>';
return {
restrict: 'A',
scope: {
currItem: '=roadmPower',
roadmSetPower: '&'
},
template: retTemplate,
link: function ($scope, $element) {
$scope.editMode = false;
$scope.showError = false;
$scope.errorMessage = "Invalid target power";
},
controller: function($scope, $timeout) {
$scope.enableEdit = function() {
if ($scope.currItem.hasTargetPower === "true" && $scope.editMode === false) {
// Ensure that the entry being edited remains the same even
// if the table entries are shifted around.
$scope.targetItem = $scope.currItem;
// Ensure the value seen in the field remains the same
$scope.formVal = parseInt($scope.currItem.targetPower);
$scope.editMode = true;
$timeout(function () {
$scope.$apply()
});
}
};
// Callback for server-side validation. Displays the error message
// if the input is invalid.
$scope.sendCb = function(valid, message) {
if (valid) {
// check if it's still pointing to the same item
// reordering the entries may change the binding
if ($scope.currItem.id === $scope.targetItem.id) {
// update the ui to display the new attenuation value
$scope.currItem.targetPower = $scope.formVal;
}
$scope.cancel();
} else {
$scope.errorMessage = message;
$scope.showError = true;
}
$timeout(function () {
$scope.$apply()
});
}
$scope.send = function() {
// check input is an integer
if (!isInteger($scope.formVal)) {
$scope.sendCb(false, "Target power must be an integer");
return;
}
$scope.roadmSetPower({port: $scope.targetItem, targetVal: $scope.formVal, cb: $scope.sendCb});
};
$scope.cancel = function() {
$scope.editMode = false;
$scope.showError = false;
}
}
};
}]);
}());

View File

@ -0,0 +1,3 @@
<link rel="stylesheet" href="app/view/roadmDevice/roadmDevice.css">
<link rel="stylesheet" href="app/view/roadmPort/roadmPort.css">
<link rel="stylesheet" href="app/view/roadmFlow/roadmFlow.css">

View File

@ -0,0 +1,3 @@
<script src="app/view/roadmDevice/roadmDevice.js"></script>
<script src="app/view/roadmPort/roadmPort.js"></script>
<script src="app/view/roadmFlow/roadmFlow.js"></script>

View File

@ -30,9 +30,9 @@ public enum ChannelSpacing {
private final Frequency frequency;
/**
* Creates an instance with the specified interval in GHz.
* Creates an instance with the specified interval in MHz.
*
* @param value interval of neighboring wavelengths in GHz.
* @param value interval of neighboring wavelengths in MHz.
*/
ChannelSpacing(long value) {
this.frequency = Frequency.ofMHz(value);

View File

@ -122,12 +122,23 @@ public class OplinkRoadmPowerConfig extends AbstractHandlerBehaviour
TrafficSelector selector = entry.selector();
OchSignalCriterion entrySigid =
(OchSignalCriterion) selector.getCriterion(Criterion.Type.OCH_SIGID);
// Check channel
if (entrySigid != null && och.equals(entrySigid.lambda())) {
// Check input port
PortCriterion entryPort =
(PortCriterion) selector.getCriterion(Criterion.Type.IN_PORT);
if (entryPort != null && portNum.equals(entryPort.port())) {
return entry;
}
// Check output port
TrafficTreatment treatment = entry.treatment();
for (Instruction instruction : treatment.allInstructions()) {
if (instruction.type() == Instruction.Type.OUTPUT &&
((Instructions.OutputInstruction) instruction).port().equals(portNum)) {
return entry;
}
}
}
}
log.warn("No matching flow found");

View File

@ -145,6 +145,7 @@ ONOS_APPS = [
'//apps/pcep-api:onos-apps-pcep-api-oar',
'//apps/pim:onos-apps-pim-oar',
'//apps/reactive-routing:onos-apps-reactive-routing-oar',
'//apps/roadm:onos-apps-roadm-oar',
'//apps/sdnip:onos-apps-sdnip-oar',
'//apps/test/demo:onos-apps-test-demo-oar',
'//apps/test/distributed-primitives:onos-apps-test-distributed-primitives-oar',