Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 1 | /* |
Luca Prete | 99f6f28 | 2016-12-05 15:33:36 -0800 | [diff] [blame] | 2 | * Copyright 2015-present Open Networking Laboratory |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [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 |
Luca Prete | 99f6f28 | 2016-12-05 15:33:36 -0800 | [diff] [blame] | 14 | * limitations under the License. |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 15 | */ |
| 16 | package org.onosproject.vpls.cli; |
| 17 | |
| 18 | import com.google.common.collect.Lists; |
| 19 | import com.google.common.collect.SetMultimap; |
| 20 | import com.google.common.collect.Sets; |
| 21 | import org.apache.karaf.shell.commands.Argument; |
| 22 | import org.apache.karaf.shell.commands.Command; |
| 23 | import org.onosproject.cli.AbstractShellCommand; |
| 24 | import org.onosproject.incubator.net.intf.Interface; |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 25 | import org.onosproject.net.EncapsulationType; |
| 26 | import org.onosproject.vpls.config.VplsConfigService; |
| 27 | |
| 28 | import java.util.Collections; |
| 29 | import java.util.List; |
| 30 | import java.util.Map; |
| 31 | import java.util.Optional; |
| 32 | import java.util.Set; |
| 33 | |
| 34 | import static com.google.common.base.Strings.isNullOrEmpty; |
| 35 | |
| 36 | /** |
| 37 | * CLI to interact with the VPLS application. |
| 38 | */ |
| 39 | @Command(scope = "onos", name = "vpls", |
| 40 | description = "Manages the VPLS application") |
| 41 | public class VplsCommand extends AbstractShellCommand { |
| 42 | |
| 43 | // Color codes and style |
| 44 | private static final String BOLD = "\u001B[1m"; |
| 45 | private static final String COLOR_ERROR = "\u001B[31m"; |
| 46 | private static final String RESET = "\u001B[0m"; |
| 47 | |
| 48 | // Messages and string formatter |
| 49 | private static final String ENCAP_NOT_FOUND = |
| 50 | COLOR_ERROR + "Encapsulation type " + BOLD + "%s" + RESET + |
| 51 | COLOR_ERROR + " not found" + RESET; |
| 52 | |
| 53 | private static final String IFACE_NOT_FOUND = |
| 54 | COLOR_ERROR + "Interface " + BOLD + "%s" + RESET + COLOR_ERROR + |
| 55 | " not found" + RESET; |
| 56 | |
| 57 | private static final String IFACE_ALREADY_ASSOCIATED = |
| 58 | COLOR_ERROR + "Interface " + BOLD + "%s" + RESET + COLOR_ERROR + |
| 59 | " already associated to VPLS " + BOLD + "%s" + RESET + |
| 60 | COLOR_ERROR + "" + RESET; |
| 61 | |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 62 | private static final String INSERT_VPLS_NAME = |
| 63 | COLOR_ERROR + "Missing the " + BOLD + "VPLS name." + RESET + |
| 64 | COLOR_ERROR + " Specifying a VPLS name is mandatory." + |
| 65 | RESET; |
| 66 | |
| 67 | private static final String INSERT_ENCAP_TYPE = |
| 68 | COLOR_ERROR + "Missing the " + BOLD + "encapsulation type." + |
| 69 | RESET + COLOR_ERROR + " Encapsulation type is mandatory." + |
| 70 | RESET; |
| 71 | |
| 72 | private static final String INSERT_INTERFACE = |
| 73 | COLOR_ERROR + "Missing the " + BOLD + "interface name." + |
| 74 | RESET + COLOR_ERROR + " Specifying an interface name is" + |
| 75 | " mandatory." + RESET; |
| 76 | |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 77 | private static final String SEPARATOR = "----------------"; |
| 78 | |
| 79 | private static final String VPLS_ALREADY_EXISTS = |
| 80 | COLOR_ERROR + "VPLS " + BOLD + "%s" + RESET + COLOR_ERROR + |
| 81 | " already exists" + RESET; |
| 82 | |
| 83 | private static final String VPLS_COMMAND_NOT_FOUND = |
| 84 | COLOR_ERROR + "VPLS command " + BOLD + "%s" + RESET + COLOR_ERROR + |
| 85 | " not found" + RESET; |
| 86 | |
| 87 | private static final String VPLS_DISPLAY = "VPLS name: " + BOLD + |
| 88 | "%s" + RESET + "\nAssociated interfaces: %s\nEncapsulation: %s"; |
| 89 | |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 90 | private static final String VPLS_NOT_FOUND = |
| 91 | COLOR_ERROR + "VPLS " + BOLD + "%s" + RESET + COLOR_ERROR + |
| 92 | " not found" + RESET; |
| 93 | |
| 94 | private static final String IFACE_NOT_ASSOCIATED = |
| 95 | COLOR_ERROR + "Interface " + BOLD + "%s" + RESET + COLOR_ERROR + |
| 96 | " cannot be removed from VPLS " + BOLD + "%s" + RESET + |
| 97 | COLOR_ERROR + ". The interface is associated to another" + |
| 98 | "VPLS (" + BOLD + "%s" + RESET + COLOR_ERROR + ")" + RESET; |
| 99 | |
| 100 | private static VplsConfigService vplsConfigService = |
| 101 | get(VplsConfigService.class); |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 102 | |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 103 | @Argument(index = 0, name = "command", description = "Command name (add-if|" + |
| 104 | "clean|create|delete|list|rem-if|set-encap|show)", |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 105 | required = true, multiValued = false) |
| 106 | String command = null; |
| 107 | |
| 108 | @Argument(index = 1, name = "vplsName", description = "The name of the VPLS", |
| 109 | required = false, multiValued = false) |
| 110 | String vplsName = null; |
| 111 | |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 112 | @Argument(index = 2, name = "optArg", description = "The interface name or" + |
| 113 | " the encapsulation type for set-encap", |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 114 | required = false, multiValued = false) |
| 115 | String optArg = null; |
| 116 | |
| 117 | @Override |
| 118 | protected void execute() { |
| 119 | VplsCommandEnum enumCommand = VplsCommandEnum.enumFromString(command); |
| 120 | if (enumCommand != null) { |
| 121 | switch (enumCommand) { |
| 122 | case ADD_IFACE: |
| 123 | addIface(vplsName, optArg); |
| 124 | break; |
| 125 | case CLEAN: |
| 126 | clean(); |
| 127 | break; |
| 128 | case CREATE: |
| 129 | create(vplsName); |
| 130 | break; |
| 131 | case DELETE: |
| 132 | delete(vplsName); |
| 133 | break; |
| 134 | case LIST: |
| 135 | list(); |
| 136 | break; |
| 137 | case REMOVE_IFACE: |
| 138 | removeIface(vplsName, optArg); |
| 139 | break; |
| 140 | case SET_ENCAP: |
| 141 | setEncap(vplsName, optArg); |
| 142 | break; |
| 143 | case SHOW: |
| 144 | show(vplsName); |
| 145 | break; |
| 146 | default: |
| 147 | print(VPLS_COMMAND_NOT_FOUND, command); |
| 148 | } |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 149 | } else { |
| 150 | print(VPLS_COMMAND_NOT_FOUND, command); |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 151 | } |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * Adds an inteterface to a VPLS. |
| 156 | * |
| 157 | * @param vplsName the name of the VLPS |
| 158 | * @param ifaceName the name of the interface to add |
| 159 | */ |
| 160 | private void addIface(String vplsName, String ifaceName) { |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 161 | if (vplsName == null) { |
| 162 | print(INSERT_VPLS_NAME); |
| 163 | return; |
| 164 | } |
| 165 | if (ifaceName == null) { |
| 166 | print(INSERT_INTERFACE); |
| 167 | return; |
| 168 | } |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 169 | if (!vplsExists(vplsName)) { |
| 170 | print(VPLS_NOT_FOUND, vplsName); |
| 171 | return; |
| 172 | } |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 173 | if (!ifaceExists(ifaceName)) { |
| 174 | print(IFACE_NOT_FOUND, ifaceName); |
| 175 | return; |
| 176 | } |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 177 | if (isIfaceAssociated(ifaceName)) { |
| 178 | print(IFACE_ALREADY_ASSOCIATED, |
| 179 | ifaceName, vplsNameFromIfaceName(ifaceName)); |
| 180 | return; |
| 181 | } |
| 182 | |
| 183 | vplsConfigService.addIface(vplsName, ifaceName); |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Cleans the VPLS configuration. |
| 188 | */ |
| 189 | private void clean() { |
| 190 | vplsConfigService.cleanVplsConfig(); |
| 191 | } |
| 192 | |
| 193 | /** |
| 194 | * Creates a new VPLS. |
| 195 | * |
| 196 | * @param vplsName the name of the VLPS |
| 197 | */ |
| 198 | private void create(String vplsName) { |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 199 | if (vplsName == null || vplsName.isEmpty()) { |
| 200 | print(INSERT_VPLS_NAME); |
| 201 | return; |
| 202 | } |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 203 | if (vplsExists(vplsName)) { |
| 204 | print(VPLS_ALREADY_EXISTS, vplsName); |
| 205 | return; |
| 206 | } |
| 207 | vplsConfigService.addVpls(vplsName, Sets.newHashSet(), null); |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Deletes a VPLS. |
| 212 | * |
| 213 | * @param vplsName the name of the VLPS |
| 214 | */ |
| 215 | private void delete(String vplsName) { |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 216 | if (vplsName == null) { |
| 217 | print(INSERT_VPLS_NAME); |
| 218 | return; |
| 219 | } |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 220 | if (!vplsExists(vplsName)) { |
| 221 | print(VPLS_NOT_FOUND, vplsName); |
| 222 | return; |
| 223 | } |
| 224 | vplsConfigService.removeVpls(vplsName); |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * Lists the configured VPLSs. |
| 229 | */ |
| 230 | private void list() { |
| 231 | List<String> vplsNames = Lists.newArrayList(vplsConfigService.vplsNames()); |
| 232 | Collections.sort(vplsNames); |
| 233 | |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 234 | vplsNames.forEach(vpls -> { |
| 235 | print(vpls); |
| 236 | }); |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Removes an interface from a VPLS. |
| 241 | * |
| 242 | * @param vplsName the name of the VLPS |
| 243 | * @param ifaceName the name of the interface to remove |
| 244 | */ |
| 245 | private void removeIface(String vplsName, String ifaceName) { |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 246 | if (vplsName == null) { |
| 247 | print(INSERT_VPLS_NAME); |
| 248 | return; |
| 249 | } |
| 250 | if (ifaceName == null) { |
| 251 | print(INSERT_INTERFACE); |
| 252 | return; |
| 253 | } |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 254 | if (!vplsExists(vplsName)) { |
| 255 | print(VPLS_NOT_FOUND, vplsName); |
| 256 | return; |
| 257 | } |
| 258 | if (!ifaceExists(ifaceName)) { |
| 259 | print(IFACE_NOT_FOUND, ifaceName); |
| 260 | return; |
| 261 | } |
| 262 | String vplsNameFromIfaceName = vplsNameFromIfaceName(ifaceName); |
| 263 | if (!vplsNameFromIfaceName.equals(vplsName)) { |
| 264 | print(IFACE_NOT_ASSOCIATED, ifaceName, vplsName, vplsNameFromIfaceName); |
| 265 | return; |
| 266 | } |
| 267 | vplsConfigService.removeIface(ifaceName); |
| 268 | } |
| 269 | |
| 270 | /** |
| 271 | * Sets the encapsulation type for a VPLS. |
| 272 | * |
| 273 | * @param vplsName the name of the VPLS |
| 274 | * @param encap the encapsulation type |
| 275 | */ |
| 276 | private void setEncap(String vplsName, String encap) { |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 277 | if (vplsName == null) { |
| 278 | print(INSERT_VPLS_NAME); |
| 279 | return; |
| 280 | } |
| 281 | if (encap == null) { |
| 282 | print(INSERT_ENCAP_TYPE); |
| 283 | return; |
| 284 | } |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 285 | if (!vplsExists(vplsName)) { |
| 286 | print(VPLS_NOT_FOUND, vplsName); |
| 287 | return; |
| 288 | } |
| 289 | EncapsulationType encapType = EncapsulationType.enumFromString(encap); |
| 290 | if (encapType.equals(EncapsulationType.NONE) && |
| 291 | !encapType.toString().equals(encap)) { |
| 292 | print(ENCAP_NOT_FOUND, encap); |
| 293 | return; |
| 294 | } |
| 295 | vplsConfigService.setEncap(vplsName, encap); |
| 296 | } |
| 297 | |
| 298 | /** |
| 299 | * Shows the details of one or more VPLSs. |
| 300 | * |
| 301 | * @param vplsName the name of the VPLS |
| 302 | */ |
| 303 | private void show(String vplsName) { |
| 304 | List<String> vplsNames = Lists.newArrayList(vplsConfigService.vplsNames()); |
| 305 | Collections.sort(vplsNames); |
| 306 | |
| 307 | Map<String, EncapsulationType> encapByVplsName = |
| 308 | vplsConfigService.encapByVplsName(); |
| 309 | |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 310 | if (!isNullOrEmpty(vplsName)) { |
| 311 | // A VPLS name is provided. Check first if the VPLS exists |
| 312 | if (vplsExists(vplsName)) { |
| 313 | print(VPLS_DISPLAY, |
| 314 | vplsName, |
| 315 | ifacesFromVplsName(vplsName).toString(), |
| 316 | encapByVplsName.get(vplsName).toString()); |
| 317 | } else { |
| 318 | print(VPLS_NOT_FOUND, vplsName); |
| 319 | } |
| 320 | } else { |
| 321 | // No VPLS names are provided. Display all VPLSs configured |
Luca Prete | 8dbbea8 | 2016-12-07 18:10:10 -0800 | [diff] [blame^] | 322 | print(SEPARATOR); |
Luca Prete | dce16f8 | 2016-11-22 13:11:56 -0800 | [diff] [blame] | 323 | vplsNames.forEach(v -> { |
| 324 | print(VPLS_DISPLAY, |
| 325 | v, |
| 326 | ifacesFromVplsName(v).toString(), |
| 327 | encapByVplsName.get(v).toString()); |
| 328 | print(SEPARATOR); |
| 329 | }); |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | /** |
| 334 | * States if a VPLS exists or not. |
| 335 | * |
| 336 | * @param vplsName the name of the VPLS |
| 337 | * @return true if the VPLS exists; false otherwise |
| 338 | */ |
| 339 | private static boolean vplsExists(String vplsName) { |
| 340 | return vplsConfigService.vplsNames().contains(vplsName); |
| 341 | } |
| 342 | |
| 343 | /** |
| 344 | * States if an interface is defined or not in the system. |
| 345 | * |
| 346 | * @param ifaceName the name of the interface |
| 347 | * @return true if the interface is defined; false otherwise |
| 348 | */ |
| 349 | private static boolean ifaceExists(String ifaceName) { |
| 350 | return vplsConfigService.allIfaces() |
| 351 | .stream() |
| 352 | .anyMatch(iface -> iface.name().equals(ifaceName)); |
| 353 | } |
| 354 | |
| 355 | /** |
| 356 | * States if an interface is already associated to a VPLS. |
| 357 | * |
| 358 | * @param ifaceName the name of the interface |
| 359 | * @return true if the interface is already associated to a VPLS; false |
| 360 | * otherwise |
| 361 | */ |
| 362 | private static boolean isIfaceAssociated(String ifaceName) { |
| 363 | return vplsConfigService.ifaces() |
| 364 | .stream() |
| 365 | .anyMatch(iface -> iface.name().equals(ifaceName)); |
| 366 | } |
| 367 | |
| 368 | /** |
| 369 | * Returns the name of a VPLS, given the name of an interface associated to |
| 370 | * it. |
| 371 | * |
| 372 | * @param ifaceName the name of the interface |
| 373 | * @return the name of the VPLS that has the interface configured; null if |
| 374 | * the interface does not exist or is not associated to any VPLS |
| 375 | */ |
| 376 | private static String vplsNameFromIfaceName(String ifaceName) { |
| 377 | String vplsName = null; |
| 378 | |
| 379 | Optional<String> optVplsName = vplsConfigService.ifacesByVplsName() |
| 380 | .entries() |
| 381 | .stream() |
| 382 | .filter((entry -> entry.getValue().name().equals(ifaceName))) |
| 383 | .map(Map.Entry::getKey) |
| 384 | .findFirst(); |
| 385 | |
| 386 | if (optVplsName.isPresent()) { |
| 387 | vplsName = optVplsName.get(); |
| 388 | } |
| 389 | |
| 390 | return vplsName; |
| 391 | } |
| 392 | |
| 393 | /** |
| 394 | * Returns a list of interfaces associated to a VPLS, given a VPLS name. |
| 395 | * |
| 396 | * @param vplsName the name of the VPLS |
| 397 | * @return the set of interfaces associated to the given VPLS; null if the |
| 398 | * VPLS is not found |
| 399 | */ |
| 400 | private static Set<String> ifacesFromVplsName(String vplsName) { |
| 401 | if (!vplsExists(vplsName)) { |
| 402 | return null; |
| 403 | } |
| 404 | SetMultimap<String, Interface> ifacesByVplsName = |
| 405 | vplsConfigService.ifacesByVplsName(); |
| 406 | Set<String> ifaceNames = Sets.newHashSet(); |
| 407 | |
| 408 | ifacesByVplsName.get(vplsName).forEach(iface -> ifaceNames.add(iface.name())); |
| 409 | |
| 410 | return ifaceNames; |
| 411 | } |
| 412 | } |