| /* |
| * Copyright 2015 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.Set; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| |
| import com.google.common.collect.Iterables; |
| 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.onosproject.cli.AbstractShellCommand; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.PortNumber; |
| import org.onosproject.net.TributarySlot; |
| import org.onosproject.net.newresource.ContinuousResource; |
| import org.onosproject.net.newresource.DiscreteResource; |
| import org.onosproject.net.newresource.Resource; |
| import org.onosproject.net.newresource.Resources; |
| import org.onosproject.net.newresource.ResourceService; |
| |
| import com.google.common.base.Strings; |
| import com.google.common.collect.ArrayListMultimap; |
| import com.google.common.collect.DiscreteDomain; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Multimap; |
| import com.google.common.collect.Range; |
| import com.google.common.collect.RangeSet; |
| import com.google.common.collect.TreeRangeSet; |
| |
| /** |
| * Lists available resources. |
| */ |
| @Command(scope = "onos", name = "resources", |
| description = "Lists available resources") |
| public class ResourcesCommand extends AbstractShellCommand { |
| |
| @Option(name = "-s", aliases = "--sort", description = "Sort output", |
| required = false, multiValued = false) |
| boolean sort = false; |
| |
| @Option(name = "-t", aliases = "--typeStrings", description = "List of resource types to be printed", |
| required = false, multiValued = true) |
| String[] typeStrings = null; |
| |
| Set<String> typesToPrint; |
| |
| @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 ResourceService resourceService; |
| |
| @Override |
| protected void execute() { |
| resourceService = get(ResourceService.class); |
| |
| if (typeStrings != null) { |
| typesToPrint = new HashSet<>(Arrays.asList(typeStrings)); |
| } else { |
| typesToPrint = Collections.emptySet(); |
| } |
| |
| if (deviceIdStr != null && portNumberStr != null) { |
| DeviceId deviceId = deviceId(deviceIdStr); |
| PortNumber portNumber = PortNumber.fromString(portNumberStr); |
| |
| printResource(Resources.discrete(deviceId, portNumber).resource(), 0); |
| } else if (deviceIdStr != null) { |
| DeviceId deviceId = deviceId(deviceIdStr); |
| |
| printResource(Resources.discrete(deviceId).resource(), 0); |
| } else { |
| printResource(Resource.ROOT, 0); |
| } |
| } |
| |
| private void printResource(Resource resource, int level) { |
| // TODO add an option to show only available resource |
| // workaround to preserve the original behavior of ResourceService#getRegisteredResources |
| Set<Resource> children; |
| if (resource instanceof DiscreteResource) { |
| children = resourceService.getRegisteredResources(((DiscreteResource) resource).id()); |
| } else { |
| children = Collections.emptySet(); |
| } |
| |
| if (resource.equals(Resource.ROOT)) { |
| print("ROOT"); |
| } else { |
| String resourceName = resource.simpleTypeName(); |
| if (resource instanceof ContinuousResource) { |
| print("%s%s: %f", Strings.repeat(" ", level), |
| resourceName, |
| // Note: last() does not return, what we've registered |
| // following does not work |
| //((Class<?>) resource.last()).getSimpleName(), |
| ((ContinuousResource) resource).value()); |
| // Continuous resource is terminal node, stop here |
| return; |
| } else { |
| String toString = String.valueOf(resource.valueAs(Object.class).orElse("")); |
| if (toString.startsWith(resourceName)) { |
| print("%s%s", Strings.repeat(" ", level), |
| toString); |
| } else { |
| print("%s%s: %s", Strings.repeat(" ", level), |
| resourceName, |
| toString); |
| } |
| } |
| } |
| |
| |
| // Classify children into aggregatable terminal resources and everything else |
| |
| Set<Class<?>> aggregatableTypes = ImmutableSet.<Class<?>>builder() |
| .add(VlanId.class) |
| .add(MplsLabel.class) |
| .build(); |
| // (last() resource name) -> { Resource } |
| Multimap<String, Resource> aggregatables = ArrayListMultimap.create(); |
| List<Resource> nonAggregatable = new ArrayList<>(); |
| |
| for (Resource r : children) { |
| if (!isPrintTarget(r)) { |
| continue; |
| } |
| |
| if (r instanceof ContinuousResource) { |
| // non-aggregatable terminal node |
| nonAggregatable.add(r); |
| } else if (Iterables.any(aggregatableTypes, r::isTypeOf)) { |
| // aggregatable & terminal node |
| String simpleName = r.simpleTypeName(); |
| aggregatables.put(simpleName, r); |
| } else { |
| nonAggregatable.add(r); |
| } |
| } |
| |
| // print aggregated (terminal) |
| aggregatables.asMap().entrySet() |
| .forEach(e -> { |
| // for each type... |
| String resourceName = e.getKey(); |
| |
| RangeSet<Long> rangeSet = TreeRangeSet.create(); |
| |
| // aggregate into RangeSet |
| e.getValue().stream() |
| .map(res -> { |
| if (res.isTypeOf(VlanId.class)) { |
| return (long) res.valueAs(VlanId.class).get().toShort(); |
| } else if (res.isTypeOf(MplsLabel.class)) { |
| return (long) res.valueAs(MplsLabel.class).get().toInt(); |
| } else if (res.isTypeOf(TributarySlot.class)) { |
| return res.valueAs(TributarySlot.class).get().index(); |
| } |
| // TODO support Lambda (OchSignal types) |
| return 0L; |
| }) |
| .map(Range::singleton) |
| .map(range -> range.canonical(DiscreteDomain.longs())) |
| .forEach(rangeSet::add); |
| |
| print("%s%s: %s", Strings.repeat(" ", level + 1), |
| resourceName, |
| rangeSet); |
| }); |
| |
| |
| // print non-aggregatables (recurse) |
| if (sort) { |
| nonAggregatable.stream() |
| .sorted((o1, o2) -> String.valueOf(o1.id()).compareTo(String.valueOf(o2.id()))) |
| .forEach(r -> printResource(r, level + 1)); |
| } else { |
| nonAggregatable.forEach(r -> printResource(r, level + 1)); |
| } |
| } |
| |
| private boolean isPrintTarget(Resource resource) { |
| if (typesToPrint.isEmpty()) { |
| return true; |
| } |
| |
| String resourceName = resource.simpleTypeName(); |
| if (resource instanceof DiscreteResource) { |
| // TODO This distributed store access incurs overhead. |
| // This should be merged with the one in printResource() |
| if (!resourceService.getRegisteredResources(((DiscreteResource) resource).id()).isEmpty()) { |
| // resource which has children should be printed |
| return true; |
| } |
| } else if (!(resource instanceof ContinuousResource)) { |
| log.warn("Unexpected resource class: {}", resource.getClass().getSimpleName()); |
| return false; |
| } |
| |
| return typesToPrint.contains(resourceName); |
| } |
| } |