Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 1 | /* |
Brian O'Connor | a09fe5b | 2017-08-03 21:12:30 -0700 | [diff] [blame] | 2 | * Copyright 2017-present Open Networking Foundation |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package org.onosproject.dhcprelay.cli; |
| 18 | |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 19 | |
| 20 | import org.apache.karaf.shell.commands.Argument; |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 21 | import org.apache.karaf.shell.commands.Command; |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 22 | import org.onlab.packet.IpAddress; |
| 23 | import org.onlab.packet.MacAddress; |
| 24 | import org.onlab.packet.VlanId; |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 25 | import org.onlab.util.Tools; |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 26 | import org.onosproject.cli.AbstractShellCommand; |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 27 | import org.onosproject.dhcprelay.store.DhcpRelayCounters; |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 28 | import org.onosproject.dhcprelay.api.DhcpServerInfo; |
Yi Tseng | 5130129 | 2017-07-28 13:02:59 -0700 | [diff] [blame] | 29 | import org.onosproject.dhcprelay.api.DhcpRelayService; |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 30 | import org.onosproject.dhcprelay.store.DhcpRecord; |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 31 | import org.onosproject.net.Host; |
| 32 | import org.onosproject.net.HostId; |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 33 | import org.onosproject.net.host.HostService; |
| 34 | |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 35 | import java.util.Collection; |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 36 | import java.util.List; |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 37 | import java.util.function.Predicate; |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 38 | import java.util.Map; |
| 39 | |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 40 | |
| 41 | /** |
| 42 | * Prints DHCP server and DHCP relay status. |
| 43 | */ |
| 44 | @Command(scope = "onos", name = "dhcp-relay", description = "DHCP relay app cli.") |
| 45 | public class DhcpRelayCommand extends AbstractShellCommand { |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 46 | @Argument(index = 0, name = "counter", |
| 47 | description = "shows counter values", |
| 48 | required = false, multiValued = false) |
| 49 | String counter = null; |
| 50 | |
| 51 | @Argument(index = 1, name = "reset", |
| 52 | description = "reset counters or not", |
| 53 | required = false, multiValued = false) |
| 54 | String reset = null; |
| 55 | |
| 56 | |
| 57 | |
| 58 | private static final String CONUTER_HEADER = "DHCP Relay Counters :"; |
| 59 | private static final String COUNTER_HOST = "Counters for id=%s/%s, locations=%s%s"; |
| 60 | |
| 61 | |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 62 | private static final String HEADER = "DHCP relay records ([D]: Directly connected):"; |
| 63 | private static final String NO_RECORDS = "No DHCP relay record found"; |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 64 | private static final String HOST = "id=%s/%s, locations=%s%s, last-seen=%s, IPv4=%s, IPv6=%s"; |
Yi Tseng | beea16e | 2017-11-15 13:39:01 -0800 | [diff] [blame] | 65 | private static final String DHCP_SERVER_GW = "DHCP Server: %s, %s via %s (Mac: %s)"; |
| 66 | private static final String DHCP_SERVER = "DHCP Server: %s, %s (Mac: %s)"; |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 67 | private static final String MISSING_SERVER_CFG = "DHCP Server info not available"; |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 68 | private static final String DIRECTLY = "[D]"; |
| 69 | private static final String EMPTY = ""; |
| 70 | private static final String NA = "N/A"; |
| 71 | private static final String STATUS_FMT = "[%s, %s]"; |
| 72 | private static final String STATUS_FMT_NH = "[%s via %s, %s]"; |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 73 | private static final String STATUS_FMT_V6 = "[%s %d, %d ms %s %d %d ms, %s]"; |
| 74 | private static final String STATUS_FMT_V6_NH = "[%s %d %d ms, %s %d %d ms via %s, %s]"; |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 75 | private static final String DEFAULT_SERVERS = "Default DHCP servers:"; |
| 76 | private static final String INDIRECT_SERVERS = "Indirect DHCP servers:"; |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 77 | |
| 78 | private static final DhcpRelayService DHCP_RELAY_SERVICE = get(DhcpRelayService.class); |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 79 | private static final HostService HOST_SERVICE = get(HostService.class); |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 80 | |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 81 | |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 82 | @Override |
| 83 | protected void execute() { |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 84 | List<DhcpServerInfo> defaultDhcpServerInfoList = DHCP_RELAY_SERVICE.getDefaultDhcpServerInfoList(); |
| 85 | List<DhcpServerInfo> indirectDhcpServerInfoList = DHCP_RELAY_SERVICE.getIndirectDhcpServerInfoList(); |
| 86 | |
| 87 | if (defaultDhcpServerInfoList.isEmpty() && indirectDhcpServerInfoList.isEmpty()) { |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 88 | print(MISSING_SERVER_CFG); |
| 89 | return; |
| 90 | } |
| 91 | |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 92 | if (!defaultDhcpServerInfoList.isEmpty()) { |
| 93 | print(DEFAULT_SERVERS); |
| 94 | listServers(defaultDhcpServerInfoList); |
| 95 | } |
| 96 | if (!indirectDhcpServerInfoList.isEmpty()) { |
| 97 | print(INDIRECT_SERVERS); |
| 98 | listServers(indirectDhcpServerInfoList); |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 99 | } |
| 100 | |
| 101 | // DHCP records |
| 102 | Collection<DhcpRecord> records = DHCP_RELAY_SERVICE.getDhcpRecords(); |
| 103 | if (records.isEmpty()) { |
| 104 | print(NO_RECORDS); |
| 105 | return; |
| 106 | } |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 107 | |
| 108 | // Handle display of counters |
| 109 | boolean toResetFlag; |
| 110 | |
| 111 | if (counter != null) { |
rsahot036 | 2c2c6cc | 2018-06-22 14:35:07 -0400 | [diff] [blame] | 112 | if (counter.equals("counter") || counter.equals("[counter]")) { |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 113 | print(CONUTER_HEADER); |
| 114 | } else { |
| 115 | print("first parameter is [counter]"); |
| 116 | return; |
| 117 | } |
| 118 | if (reset != null) { |
| 119 | if (reset.equals("reset") || reset.equals("[reset]")) { |
| 120 | toResetFlag = true; |
| 121 | } else { |
| 122 | print("Last parameter is [reset]"); |
| 123 | return; |
| 124 | } |
| 125 | } else { |
| 126 | toResetFlag = false; |
| 127 | } |
| 128 | |
| 129 | records.forEach(record -> { |
| 130 | print(COUNTER_HOST, record.macAddress(), |
| 131 | record.vlanId(), |
| 132 | record.locations(), |
| 133 | record.directlyConnected() ? DIRECTLY : EMPTY); |
| 134 | DhcpRelayCounters v6Counters = record.getV6Counters(); |
| 135 | Map<String, Integer> countersMap = v6Counters.getCounters(); |
| 136 | countersMap.forEach((name, value) -> { |
| 137 | print("%-30s ............................ %-4d packets", name, value); |
| 138 | }); |
| 139 | if (toResetFlag) { |
| 140 | v6Counters.resetCounters(); |
| 141 | record.updateLastSeen(); |
| 142 | DHCP_RELAY_SERVICE.updateDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()), record); |
| 143 | } |
| 144 | }); |
| 145 | |
| 146 | |
| 147 | return; |
| 148 | } |
| 149 | |
| 150 | |
| 151 | // Handle display of records |
| 152 | |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 153 | print(HEADER); |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 154 | records.forEach(record -> print(HOST, |
| 155 | record.macAddress(), |
| 156 | record.vlanId(), |
| 157 | record.locations(), |
| 158 | record.directlyConnected() ? DIRECTLY : EMPTY, |
| 159 | Tools.timeAgo(record.lastSeen()), |
| 160 | ip4State(record), |
| 161 | ip6State(record))); |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 162 | } |
| 163 | |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 164 | private void listServers(List<DhcpServerInfo> dhcpServerInfoList) { |
| 165 | dhcpServerInfoList.forEach(dhcpServerInfo -> { |
| 166 | String connectPoint = dhcpServerInfo.getDhcpServerConnectPoint() |
| 167 | .map(Object::toString).orElse(NA); |
| 168 | String serverMac = dhcpServerInfo.getDhcpConnectMac() |
| 169 | .map(Object::toString).orElse(NA); |
| 170 | String gatewayAddress; |
| 171 | String serverIp; |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 172 | |
| 173 | switch (dhcpServerInfo.getVersion()) { |
| 174 | case DHCP_V4: |
| 175 | gatewayAddress = dhcpServerInfo.getDhcpGatewayIp4() |
| 176 | .map(Object::toString).orElse(null); |
| 177 | serverIp = dhcpServerInfo.getDhcpServerIp4() |
| 178 | .map(Object::toString).orElse(NA); |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 179 | break; |
| 180 | case DHCP_V6: |
| 181 | gatewayAddress = dhcpServerInfo.getDhcpGatewayIp6() |
| 182 | .map(Object::toString).orElse(null); |
| 183 | serverIp = dhcpServerInfo.getDhcpServerIp6() |
| 184 | .map(Object::toString).orElse(NA); |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 185 | break; |
| 186 | default: |
| 187 | return; |
| 188 | } |
| 189 | if (gatewayAddress != null) { |
Yi Tseng | 25bfe37 | 2017-11-03 16:27:32 -0700 | [diff] [blame] | 190 | print(DHCP_SERVER_GW, connectPoint, serverIp, gatewayAddress, serverMac); |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 191 | } else { |
Yi Tseng | 25bfe37 | 2017-11-03 16:27:32 -0700 | [diff] [blame] | 192 | print(DHCP_SERVER, connectPoint, serverIp, serverMac); |
Yi Tseng | 919b2df | 2017-09-07 16:22:51 -0700 | [diff] [blame] | 193 | } |
| 194 | }); |
| 195 | } |
| 196 | |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 197 | private String ip4State(DhcpRecord record) { |
| 198 | String nextHopIp = findNextHopIp(IpAddress::isIp4, |
| 199 | record.nextHop().orElse(null), |
| 200 | record.vlanId()); |
| 201 | return ipState(record.ip4Address().map(Object::toString).orElse(NA), |
| 202 | record.ip4Status().map(Object::toString).orElse(NA), |
| 203 | record.directlyConnected(), |
| 204 | nextHopIp); |
| 205 | } |
| 206 | |
| 207 | private String ip6State(DhcpRecord record) { |
Kalhee Kim | ea4b6c2 | 2017-11-09 14:38:37 +0000 | [diff] [blame] | 208 | String nextHopIp = findNextHopIp6(IpAddress::isIp6, |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 209 | record.nextHop().orElse(null), |
| 210 | record.vlanId()); |
Kalhee Kim | ea4b6c2 | 2017-11-09 14:38:37 +0000 | [diff] [blame] | 211 | |
| 212 | if (record.directlyConnected()) { |
| 213 | return String.format(STATUS_FMT_V6, |
| 214 | record.ip6Address().map(Object::toString).orElse(NA), |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 215 | record.addrPrefTime(), |
| 216 | record.getLastIp6Update(), |
Kalhee Kim | ea4b6c2 | 2017-11-09 14:38:37 +0000 | [diff] [blame] | 217 | record.pdPrefix().map(Object::toString).orElse(NA), |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 218 | record.pdPrefTime(), |
| 219 | record.getLastPdUpdate(), |
Kalhee Kim | ea4b6c2 | 2017-11-09 14:38:37 +0000 | [diff] [blame] | 220 | record.ip6Status().map(Object::toString).orElse(NA)); |
| 221 | } else { |
| 222 | return String.format(STATUS_FMT_V6_NH, |
| 223 | record.ip6Address().map(Object::toString).orElse(NA), |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 224 | record.addrPrefTime(), |
| 225 | record.getLastIp6Update(), |
Kalhee Kim | ea4b6c2 | 2017-11-09 14:38:37 +0000 | [diff] [blame] | 226 | record.pdPrefix().map(Object::toString).orElse(NA), |
Kalhee Kim | d94ceea | 2017-11-29 19:03:02 +0000 | [diff] [blame] | 227 | record.pdPrefTime(), |
| 228 | record.getLastPdUpdate(), |
Kalhee Kim | ea4b6c2 | 2017-11-09 14:38:37 +0000 | [diff] [blame] | 229 | nextHopIp, |
| 230 | record.ip6Status().map(Object::toString).orElse(NA)); |
| 231 | } |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 232 | } |
| 233 | |
| 234 | private String ipState(String ipAddress, String status, |
| 235 | boolean directlyConnected, |
| 236 | String nextHopIp) { |
| 237 | if (directlyConnected) { |
| 238 | return String.format(STATUS_FMT, ipAddress, status); |
| 239 | } else { |
| 240 | return String.format(STATUS_FMT_NH, ipAddress, nextHopIp, status); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | private String findNextHopIp(Predicate<IpAddress> ipFilter, MacAddress nextHopMac, VlanId vlanId) { |
| 245 | if (ipFilter == null || nextHopMac == null || vlanId == null) { |
| 246 | return NA; |
| 247 | } |
Kalhee Kim | ea4b6c2 | 2017-11-09 14:38:37 +0000 | [diff] [blame] | 248 | |
Yi Tseng | 13a41a1 | 2017-07-26 13:45:01 -0700 | [diff] [blame] | 249 | Host host = HOST_SERVICE.getHost(HostId.hostId(nextHopMac, vlanId)); |
| 250 | if (host == null) { |
| 251 | return NA; |
| 252 | } |
| 253 | return host.ipAddresses().stream() |
| 254 | .filter(ipFilter) |
| 255 | .filter(ip -> !ip.isLinkLocal()) |
| 256 | .map(Object::toString) |
| 257 | .findFirst() |
| 258 | .orElse(NA); |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 259 | } |
Kalhee Kim | ea4b6c2 | 2017-11-09 14:38:37 +0000 | [diff] [blame] | 260 | |
| 261 | private String findNextHopIp6(Predicate<IpAddress> ipFilter, MacAddress nextHopMac, VlanId vlanId) { |
| 262 | if (ipFilter == null || nextHopMac == null || vlanId == null) { |
| 263 | return NA; |
| 264 | } |
| 265 | |
| 266 | Host host = HOST_SERVICE.getHost(HostId.hostId(nextHopMac, vlanId)); |
| 267 | if (host == null) { |
| 268 | return NA; |
| 269 | } |
| 270 | return host.ipAddresses().stream() |
| 271 | .filter(ipFilter) |
| 272 | .filter(ip -> ip.isLinkLocal()) |
| 273 | .map(Object::toString) |
| 274 | .findFirst() |
| 275 | .orElse(NA); |
| 276 | } |
Yi Tseng | 2e5d99e | 2017-06-06 10:58:46 -0700 | [diff] [blame] | 277 | } |