/* * Copyright 2015-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.cli.net; import static org.onosproject.net.DeviceId.deviceId; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import org.apache.karaf.shell.commands.Argument; import org.apache.karaf.shell.commands.Command; import org.apache.karaf.shell.commands.Option; import org.onlab.packet.MplsLabel; import org.onlab.packet.VlanId; import org.onlab.util.Bandwidth; import org.onosproject.cli.AbstractShellCommand; import org.onosproject.net.Device; import org.onosproject.net.DeviceId; import org.onosproject.net.OchSignal; import org.onosproject.net.Port; import org.onosproject.net.PortNumber; import org.onosproject.net.TributarySlot; import org.onosproject.net.device.DeviceService; import org.onosproject.net.intent.IntentId; import org.onosproject.net.resource.ResourceConsumerId; import org.onosproject.net.resource.Resources; import org.onosproject.net.resource.DiscreteResourceId; import org.onosproject.net.resource.ResourceAllocation; import org.onosproject.net.resource.ResourceService; /** * Lists allocated resources. */ @Command(scope = "onos", name = "allocations", description = "Lists allocated resources") public class AllocationsCommand extends AbstractShellCommand { @Option(name = "-t", aliases = "--type", description = "resource types to include in the list", required = false, multiValued = true) String[] typeStrings = null; Set<String> typesToPrint; @Option(name = "-i", aliases = "--intentId", description = "Intent ID to include in the list", required = false, multiValued = true) String[] intentStrings; Set<String> intentsToPrint; @Argument(index = 0, name = "deviceIdString", description = "Device ID", required = false, multiValued = false) String deviceIdStr = null; @Argument(index = 1, name = "portNumberString", description = "PortNumber", required = false, multiValued = false) String portNumberStr = null; private DeviceService deviceService; private ResourceService resourceService; @Override protected void execute() { deviceService = get(DeviceService.class); resourceService = get(ResourceService.class); if (typeStrings != null) { typesToPrint = new HashSet<>(Arrays.asList(typeStrings)); } else { typesToPrint = Collections.emptySet(); } if (intentStrings != null) { intentsToPrint = new HashSet<>(Arrays.asList(intentStrings)); } else { intentsToPrint = Collections.emptySet(); } if (deviceIdStr != null && portNumberStr != null) { DeviceId deviceId = deviceId(deviceIdStr); PortNumber portNumber = PortNumber.fromString(portNumberStr); printAllocation(deviceId, portNumber, 0); } else if (deviceIdStr != null) { DeviceId deviceId = deviceId(deviceIdStr); printAllocation(deviceId, 0); } else { printAllocation(); } } private void printAllocation() { print("ROOT"); StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false) .map(Device::id) .forEach(did -> printAllocation(did, 1)); } private void printAllocation(DeviceId did, int level) { print("%s%s", Strings.repeat(" ", level), did); StreamSupport.stream(deviceService.getPorts(did).spliterator(), false) .map(Port::number) .forEach(num -> printAllocation(did, num, level + 1)); } private void printAllocation(DeviceId did, PortNumber num, int level) { if (level == 0) { // print DeviceId when Port was directly specified. print("%s", did); } DiscreteResourceId resourceId = Resources.discrete(did, num).id(); List<String> portConsumers = resourceService.getResourceAllocations(resourceId) .stream() .filter(this::isSubjectToPrint) .map(ResourceAllocation::consumerId) .map(AllocationsCommand::asVerboseString) .collect(Collectors.toList()); if (portConsumers.isEmpty()) { print("%s%s", Strings.repeat(" ", level), asVerboseString(num)); } else { print("%s%s allocated by %s", Strings.repeat(" ", level), asVerboseString(num), portConsumers); } // FIXME: This workaround induces a lot of distributed store access. // ResourceService should have an API to get all allocations under a parent resource. Set<Class<?>> subResourceTypes = ImmutableSet.<Class<?>>builder() .add(OchSignal.class) .add(VlanId.class) .add(MplsLabel.class) .add(Bandwidth.class) .add(TributarySlot.class) .build(); for (Class<?> t : subResourceTypes) { resourceService.getResourceAllocations(resourceId, t).stream() .filter(a -> isSubjectToPrint(a)) .forEach(a -> print("%s%s allocated by %s", Strings.repeat(" ", level + 1), a.resource().valueAs(Object.class).orElse(""), asVerboseString(a.consumerId()))); } } private boolean isSubjectToPrint(ResourceAllocation allocation) { if (!intentsToPrint.isEmpty() && allocation.consumerId().isClassOf(IntentId.class) && !intentsToPrint.contains(allocation.consumerId().toString())) { return false; } if (!typesToPrint.isEmpty() && !typesToPrint.contains(allocation.resource().simpleTypeName())) { return false; } return true; } /** * Add type name if the toString does not start with them. * * e.g., IntentId#toString result in "42" * asVerboseString(id) will result in "IntentId:42" * * @param obj non-null Object to print. * @return verbose String representation */ private static String asVerboseString(Object obj) { String name = obj.getClass().getSimpleName(); String toString = String.valueOf(obj); if (toString.startsWith(name)) { return toString; } else { return String.format("%s:%s", name, toString); } } private static String asVerboseString(ResourceConsumerId consumerId) { return String.format("%s:%s", consumerId.consumerClass(), consumerId.value()); } }