Saurav Das
Committed by Yuta HIGUCHI

CORD-613 Adding ability to administratively enable or disable a port via CLI.

Currently uses the OpenFlow device provider to change portState.
Also fixes a bug in PortNumberCompleter.
Adds completion options to portstats for deviceId and portNumber.

Change-Id: Idcce775fe8bc5484fdd0e630bcb5026b85125478
Showing 21 changed files with 330 additions and 21 deletions
......@@ -22,6 +22,7 @@ import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceProvider;
......@@ -100,5 +101,10 @@ public class AnnotateDeviceCommand extends AbstractShellCommand {
public boolean isReachable(DeviceId deviceId) {
return false;
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
}
}
}
......
/*
* Copyright 2016 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.cli.net;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceAdminService;
import org.onosproject.net.device.DeviceService;
/**
* Administratively enables or disabled a port on a device.
*/
@Command(scope = "onos", name = "portstate",
description = "Administratively enables or disabled a port on a device")
public class DevicePortStateCommand extends AbstractShellCommand {
@Argument(index = 0, name = "uri", description = "Device ID",
required = true, multiValued = false)
String uri = null;
@Argument(index = 1, name = "portNumber", description = "Port Number",
required = true, multiValued = false)
Integer portNumber = null;
@Argument(index = 2, name = "portState", description = "Desired State",
required = true, multiValued = false)
String portState = null;
@Override
protected void execute() {
DeviceService deviceService = get(DeviceService.class);
DeviceAdminService deviceAdminService = get(DeviceAdminService.class);
Device dev = deviceService.getDevice(DeviceId.deviceId(uri));
if (dev == null) {
print(" %s", "Device does not exist");
return;
}
PortNumber pnum = PortNumber.portNumber(portNumber);
Port p = deviceService.getPort(dev.id(), pnum);
if (p == null) {
print(" %s", "Port does not exist");
return;
}
if (portState.equals("enable")) {
deviceAdminService.changePortState(dev.id(), pnum, true);
} else if (portState.equals("disable")) {
deviceAdminService.changePortState(dev.id(), pnum, false);
} else {
print(" %s", "State must be enable or disable");
}
}
}
......@@ -50,6 +50,10 @@ public class DevicePortStatsCommand extends DevicesListCommand {
required = false, multiValued = false)
String uri = null;
@Argument(index = 1, name = "portNumber", description = "Port Number",
required = false, multiValued = false)
Integer portNumber = null;
private static final String FORMAT =
" port=%s, pktRx=%s, pktTx=%s, bytesRx=%s, bytesTx=%s, pktRxDrp=%s, pktTxDrp=%s, Dur=%s";
......@@ -94,6 +98,9 @@ public class DevicePortStatsCommand extends DevicesListCommand {
private void printPortStats(DeviceId deviceId, Iterable<PortStatistics> portStats) {
print("deviceId=%s", deviceId);
for (PortStatistics stat : sortByPort(portStats)) {
if (portNumber != null && stat.port() != portNumber) {
continue;
}
print(FORMAT, stat.port(), stat.packetsReceived(), stat.packetsSent(), stat.bytesReceived(),
stat.bytesSent(), stat.packetsRxDropped(), stat.packetsTxDropped(), stat.durationSec());
}
......@@ -109,6 +116,9 @@ public class DevicePortStatsCommand extends DevicesListCommand {
+ " rateRx=%s, rateTx=%s, pktRxDrp=%s, pktTxDrp=%s, interval=%s";
print("deviceId=%s", deviceId);
for (PortStatistics stat : sortByPort(portStats)) {
if (portNumber != null && stat.port() != portNumber) {
continue;
}
float duration = ((float) stat.durationSec()) +
(((float) stat.durationNano()) / TimeUnit.SECONDS.toNanos(1));
float rateRx = stat.bytesReceived() * 8 / duration;
......@@ -142,21 +152,24 @@ public class DevicePortStatsCommand extends DevicesListCommand {
print("|---------------------------------------------------------------------------------------------------|");
for (PortStatistics stat : sortByPort(portStats)) {
float duration = ((float) stat.durationSec()) +
(((float) stat.durationNano()) / TimeUnit.SECONDS.toNanos(1));
float rateRx = stat.bytesReceived() * 8 / duration;
float rateTx = stat.bytesSent() * 8 / duration;
print(formatDeltaTable, stat.port(),
humanReadable(stat.packetsReceived()),
humanReadable(stat.bytesReceived()),
humanReadableBps(rateRx),
humanReadable(stat.packetsRxDropped()),
humanReadable(stat.packetsSent()),
humanReadable(stat.bytesSent()),
humanReadableBps(rateTx),
humanReadable(stat.packetsTxDropped()),
String.format("%.3f", duration));
if (portNumber != null && stat.port() != portNumber) {
continue;
}
float duration = ((float) stat.durationSec()) +
(((float) stat.durationNano()) / TimeUnit.SECONDS.toNanos(1));
float rateRx = stat.bytesReceived() * 8 / duration;
float rateTx = stat.bytesSent() * 8 / duration;
print(formatDeltaTable, stat.port(),
humanReadable(stat.packetsReceived()),
humanReadable(stat.bytesReceived()),
humanReadableBps(rateRx),
humanReadable(stat.packetsRxDropped()),
humanReadable(stat.packetsSent()),
humanReadable(stat.bytesSent()),
humanReadableBps(rateTx),
humanReadable(stat.packetsTxDropped()),
String.format("%.3f", duration));
}
print("+---------------------------------------------------------------------------------------------------+");
}
......
......@@ -15,15 +15,16 @@
*/
package org.onosproject.cli.net;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onlab.osgi.DefaultServiceDirectory.getService;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.apache.karaf.shell.console.completer.ArgumentCompleter.ArgumentList;
import org.onosproject.cli.AbstractChoicesCompleter;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
......@@ -37,13 +38,22 @@ public class PortNumberCompleter extends AbstractChoicesCompleter {
@Override
protected List<String> choices() {
ArgumentList args = getArgumentList();
checkArgument(args.getCursorArgumentIndex() >= 1,
"Expects DeviceId as previous argument");
String deviceIdStr = args.getArguments()[args.getCursorArgumentIndex() - 1];
DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
//parse argument list for deviceId
DeviceService deviceService = getService(DeviceService.class);
Device dev = null;
for (String str : args.getArguments()) {
if (str.contains(":")) {
dev = deviceService.getDevice(DeviceId.deviceId(str));
if (dev != null) {
break;
}
}
}
if (dev == null) {
return Collections.singletonList("Missing device");
}
DeviceId deviceId = dev.id();
return StreamSupport.stream(deviceService.getPorts(deviceId).spliterator(), false)
.map(port -> port.number().toString())
.collect(Collectors.toList());
......
/*
* Copyright 2016 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.cli.net;
import java.util.List;
import org.onosproject.cli.AbstractChoicesCompleter;
import com.google.common.collect.ImmutableList;
/**
* PortState completer.
*
* Assumes argument right before the one being completed is PortNumber.
*/
public class PortStateCompleter extends AbstractChoicesCompleter {
@Override
protected List<String> choices() {
return ImmutableList.of("enable", "disable");
}
}
......@@ -123,6 +123,15 @@
</completers>
</command>
<command>
<action class="org.onosproject.cli.net.DevicePortStateCommand"/>
<completers>
<ref component-id="deviceIdCompleter"/>
<ref component-id="portNumberCompleter"/>
<ref component-id="portStateCompleter"/>
<null/>
</completers>
</command>
<command>
<action class="org.onosproject.cli.net.DeviceControllersCommand"/>
<completers>
<ref component-id="deviceIdCompleter"/>
......@@ -433,6 +442,11 @@
<command>
<action class="org.onosproject.cli.net.DevicePortStatsCommand"/>
<completers>
<ref component-id="deviceIdCompleter"/>
<ref component-id="portNumberCompleter"/>
<null/>
</completers>
</command>
<command>
......@@ -650,6 +664,7 @@
<bean id="nodeIdCompleter" class="org.onosproject.cli.NodeIdCompleter"/>
<bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
<bean id="portNumberCompleter" class="org.onosproject.cli.net.PortNumberCompleter"/>
<bean id="portStateCompleter" class="org.onosproject.cli.net.PortStateCompleter"/>
<bean id="clusterIdCompleter" class="org.onosproject.cli.net.ClusterIdCompleter"/>
<bean id="roleCompleter" class="org.onosproject.cli.net.RoleCompleter"/>
<bean id="hostIdCompleter" class="org.onosproject.cli.net.HostIdCompleter"/>
......
......@@ -16,6 +16,7 @@
package org.onosproject.net.device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
/**
* Service for administering the inventory of infrastructure devices.
......@@ -31,4 +32,13 @@ public interface DeviceAdminService extends DeviceService {
// TODO: add ability to administratively suspend/resume device
/**
* Administratively enables or disables a port on a device.
*
* @param deviceId device identifier
* @param portNumber port identifier
* @param enable true if port is to be enabled, false to disable
*/
void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable);
}
......
......@@ -17,6 +17,7 @@ package org.onosproject.net.device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.provider.Provider;
/**
......@@ -54,4 +55,14 @@ public interface DeviceProvider extends Provider {
* @return true if reachable, false otherwise
*/
boolean isReachable(DeviceId deviceId);
/**
* Administratively enables or disables a port.
*
* @param deviceId device identifier
* @param portNumber device identifier
* @param enable true if port is to be enabled, false to disable
*/
void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable);
}
......
......@@ -248,6 +248,22 @@ public class DeviceManager
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
checkNotNull(deviceId, DEVICE_ID_NULL);
checkNotNull(deviceId, PORT_NUMBER_NULL);
DeviceProvider provider = getProvider(deviceId);
if (provider != null) {
log.warn("Port {} on device {} being administratively brought {}",
portNumber, deviceId,
(enable) ? "UP" : "DOWN");
provider.changePortState(deviceId, portNumber, enable);
} else {
log.warn("Provider not found for {}", deviceId);
}
}
@Override
protected DeviceProviderService createProviderService(
DeviceProvider provider) {
return new InternalDeviceProviderService(provider);
......@@ -340,6 +356,7 @@ public class DeviceManager
log.trace("event: {} {}", event.type(), event);
post(event);
}
}
@Override
......@@ -790,4 +807,5 @@ public class DeviceManager
}
}
}
}
......
......@@ -274,6 +274,11 @@ public class DeviceManagerTest {
public boolean isReachable(DeviceId device) {
return false;
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
}
}
private static class TestListener implements DeviceListener {
......
......@@ -51,6 +51,7 @@ import org.onosproject.grpc.DeviceProviderRegistryRpcGrpc.DeviceProviderRegistry
import org.onosproject.grpc.LinkProviderServiceRpcGrpc;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
......@@ -435,5 +436,12 @@ public class GrpcRemoteServiceServer {
return checkNotNull(providerId, "not initialized yet");
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
// TODO if required
}
}
}
......
......@@ -341,6 +341,10 @@ public class GrpcRemoteServiceTest {
DeviceId isReachableDid;
boolean isReachableReply = false;
final CountDownLatch portStateChanged = new CountDownLatch(1);
DeviceId portStateChangedDid;
PortNumber portStateChangedPort;
@Override
public ProviderId id() {
return PID;
......@@ -369,6 +373,17 @@ public class GrpcRemoteServiceTest {
return isReachableReply;
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
log.info("portState change to {} on ({},{}) on Client called", enable,
deviceId, portNumber);
portStateChangedDid = deviceId;
portStateChangedPort = portNumber;
portStateChanged.countDown();
}
}
class NoOpRemoteServiceProviderRegistry
......
......@@ -29,6 +29,7 @@ import org.onosproject.bgpio.protocol.linkstate.BgpNodeLSNlriVer4;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceProvider;
......@@ -122,4 +123,9 @@ public class BgpTopologyProvider extends AbstractProvider implements DeviceProvi
// TODO Auto-generated method stub
return true;
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
}
}
......
......@@ -34,6 +34,7 @@ import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.behaviour.PortDiscovery;
import org.onosproject.net.config.ConfigFactory;
......@@ -250,6 +251,12 @@ public class NetconfDeviceProvider extends AbstractProvider
}
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
// TODO if required
}
private class InnerNetconfDeviceListener implements NetconfDeviceListener {
......
......@@ -32,6 +32,7 @@ import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceAdminService;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
......@@ -431,6 +432,12 @@ public class NullProviders {
@Override
public void triggerProbe(DeviceId deviceId) {
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
// TODO maybe required
}
}
// Host provider facade.
......
......@@ -19,6 +19,7 @@ import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -71,6 +72,8 @@ import org.osgi.service.component.ComponentContext;
import org.projectfloodlight.openflow.protocol.OFCalientPortDescProp;
import org.projectfloodlight.openflow.protocol.OFCalientPortDescPropOptical;
import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsEntry;
import org.projectfloodlight.openflow.protocol.OFErrorMsg;
import org.projectfloodlight.openflow.protocol.OFErrorType;
import org.projectfloodlight.openflow.protocol.OFExpPort;
import org.projectfloodlight.openflow.protocol.OFExpPortDescPropOpticalTransport;
import org.projectfloodlight.openflow.protocol.OFExpPortOpticalTransportLayerEntry;
......@@ -81,6 +84,7 @@ import org.projectfloodlight.openflow.protocol.OFPortConfig;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
import org.projectfloodlight.openflow.protocol.OFPortFeatures;
import org.projectfloodlight.openflow.protocol.OFPortMod;
import org.projectfloodlight.openflow.protocol.OFPortOptical;
import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportLayerClass;
import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportSignalType;
......@@ -93,6 +97,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
import org.projectfloodlight.openflow.protocol.OFStatsType;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.PortSpeed;
import org.slf4j.Logger;
......@@ -268,6 +273,34 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
LOG.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
final Dpid dpid = dpid(deviceId.uri());
OpenFlowSwitch sw = controller.getSwitch(dpid);
if (sw == null || !sw.isConnected()) {
LOG.error("Failed to change portState on device {}", deviceId);
return;
}
OFPortMod.Builder pmb = sw.factory().buildPortMod();
OFPort port = OFPort.of((int) portNumber.toLong());
pmb.setPortNo(port);
if (enable) {
pmb.setConfig(0x0); // port_down bit 0
} else {
pmb.setConfig(0x1); // port_down bit 1
}
pmb.setMask(0x1);
pmb.setAdvertise(0x0);
for (OFPortDesc pd : sw.getPorts()) {
if (pd.getPortNo().equals(port)) {
pmb.setHwAddr(pd.getHwAddr());
break;
}
}
sw.sendMsg(Collections.singletonList(pmb.build()));
}
private void pushPortMetrics(Dpid dpid, List<OFPortStatsEntry> portStatsEntries) {
DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
Collection<PortStatistics> stats = buildPortStatistics(deviceId, portStatsEntries);
......@@ -756,9 +789,15 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
}
}
break;
case ERROR:
if (((OFErrorMsg) msg).getErrType() == OFErrorType.PORT_MOD_FAILED) {
LOG.error("port mod failed");
}
default:
break;
}
}
}
}
......
......@@ -32,6 +32,7 @@ import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
......@@ -148,4 +149,10 @@ public class OvsdbDeviceProvider extends AbstractProvider
}
return new OvsdbNodeId(IpAddress.valueOf(strings[1]), 0);
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
// TODO if required
}
}
......
......@@ -34,6 +34,7 @@ import org.onosproject.net.OchPort;
import org.onosproject.net.OduCltPort;
import org.onosproject.net.OmsPort;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceDescription;
......@@ -318,4 +319,10 @@ public class PcepTopologyProvider extends AbstractProvider
// TODO Auto-generated method stub
return true;
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
// TODO Auto-generated method stub
}
}
......
......@@ -31,6 +31,7 @@ import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.behaviour.PortDiscovery;
import org.onosproject.net.config.ConfigFactory;
......@@ -256,4 +257,10 @@ public class RestDeviceProvider extends AbstractProvider
event.type() == CONFIG_UPDATED);
}
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
// TODO if required
}
}
......
......@@ -37,6 +37,7 @@ import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.behaviour.PortDiscovery;
import org.onosproject.net.device.DefaultDeviceDescription;
......@@ -427,4 +428,10 @@ public class SnmpDeviceProvider extends AbstractProvider
protected ISnmpSessionFactory getSessionFactory(ISnmpConfigurationFactory configurationFactory) {
return new SnmpSessionFactory(configurationFactory);
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
// TODO if required
}
}
......
......@@ -568,6 +568,11 @@ class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
}
@Override
public ProviderId id() {
return PID;
}
......@@ -610,4 +615,6 @@ class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
}
}
}
......