blob: 0e060c59b887741b0a65b1364a77c81882e8f9ca [file] [log] [blame]
Luca Pretedce16f82016-11-22 13:11:56 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Luca Pretedce16f82016-11-22 13:11:56 -08003 *
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 Prete99f6f282016-12-05 15:33:36 -080014 * limitations under the License.
Luca Pretedce16f82016-11-22 13:11:56 -080015 */
16package org.onosproject.vpls.cli;
17
Yi Tseng3069c612017-05-26 17:09:43 -070018import com.google.common.collect.ImmutableSet;
Ray Milkey86ad7bb2018-09-27 12:32:28 -070019import org.apache.karaf.shell.api.action.Argument;
20import org.apache.karaf.shell.api.action.Command;
Ray Milkey32ce0ed2018-10-10 15:39:24 -070021import org.apache.karaf.shell.api.action.Completion;
Ray Milkey7a2dee52018-09-28 10:58:28 -070022import org.apache.karaf.shell.api.action.lifecycle.Service;
Luca Pretedce16f82016-11-22 13:11:56 -080023import org.onosproject.cli.AbstractShellCommand;
Ray Milkeyfacf2862017-08-03 11:58:29 -070024import org.onosproject.net.intf.Interface;
25import org.onosproject.net.intf.InterfaceService;
Luca Pretedce16f82016-11-22 13:11:56 -080026import org.onosproject.net.EncapsulationType;
Yi Tsengf4e13e32017-03-30 15:38:39 -070027import org.onosproject.vpls.api.VplsData;
28import org.onosproject.vpls.api.Vpls;
Yi Tseng3069c612017-05-26 17:09:43 -070029import org.onosproject.vpls.api.VplsData.VplsState;
Ray Milkey32ce0ed2018-10-10 15:39:24 -070030import org.onosproject.vpls.cli.completer.VplsCommandCompleter;
31import org.onosproject.vpls.cli.completer.VplsNameCompleter;
32import org.onosproject.vpls.cli.completer.VplsOptArgCompleter;
Luca Pretedce16f82016-11-22 13:11:56 -080033
Yi Tsengf4e13e32017-03-30 15:38:39 -070034import java.util.Collection;
Luca Pretedce16f82016-11-22 13:11:56 -080035import java.util.Collections;
36import java.util.List;
Luca Pretedce16f82016-11-22 13:11:56 -080037import java.util.Set;
Yi Tsengf4e13e32017-03-30 15:38:39 -070038import java.util.stream.Collectors;
Luca Pretedce16f82016-11-22 13:11:56 -080039
40import static com.google.common.base.Strings.isNullOrEmpty;
Yi Tseng3069c612017-05-26 17:09:43 -070041import static org.onosproject.vpls.api.VplsData.VplsState.*;
Luca Pretedce16f82016-11-22 13:11:56 -080042
Yi Tsengf4e13e32017-03-30 15:38:39 -070043
Luca Pretedce16f82016-11-22 13:11:56 -080044/**
45 * CLI to interact with the VPLS application.
46 */
Ray Milkey7a2dee52018-09-28 10:58:28 -070047@Service
Luca Pretedce16f82016-11-22 13:11:56 -080048@Command(scope = "onos", name = "vpls",
49 description = "Manages the VPLS application")
50public class VplsCommand extends AbstractShellCommand {
Yi Tseng3069c612017-05-26 17:09:43 -070051 private static final Set<VplsState> CHANGING_STATE =
52 ImmutableSet.of(ADDING, REMOVING, UPDATING);
Luca Pretedce16f82016-11-22 13:11:56 -080053
54 // Color codes and style
55 private static final String BOLD = "\u001B[1m";
56 private static final String COLOR_ERROR = "\u001B[31m";
57 private static final String RESET = "\u001B[0m";
58
59 // Messages and string formatter
60 private static final String ENCAP_NOT_FOUND =
61 COLOR_ERROR + "Encapsulation type " + BOLD + "%s" + RESET +
62 COLOR_ERROR + " not found" + RESET;
63
64 private static final String IFACE_NOT_FOUND =
65 COLOR_ERROR + "Interface " + BOLD + "%s" + RESET + COLOR_ERROR +
66 " not found" + RESET;
67
68 private static final String IFACE_ALREADY_ASSOCIATED =
69 COLOR_ERROR + "Interface " + BOLD + "%s" + RESET + COLOR_ERROR +
70 " already associated to VPLS " + BOLD + "%s" + RESET +
71 COLOR_ERROR + "" + RESET;
72
Luca Prete8dbbea82016-12-07 18:10:10 -080073 private static final String INSERT_VPLS_NAME =
74 COLOR_ERROR + "Missing the " + BOLD + "VPLS name." + RESET +
75 COLOR_ERROR + " Specifying a VPLS name is mandatory." +
76 RESET;
77
78 private static final String INSERT_ENCAP_TYPE =
79 COLOR_ERROR + "Missing the " + BOLD + "encapsulation type." +
80 RESET + COLOR_ERROR + " Encapsulation type is mandatory." +
81 RESET;
82
83 private static final String INSERT_INTERFACE =
84 COLOR_ERROR + "Missing the " + BOLD + "interface name." +
85 RESET + COLOR_ERROR + " Specifying an interface name is" +
86 " mandatory." + RESET;
87
Luca Pretedce16f82016-11-22 13:11:56 -080088 private static final String SEPARATOR = "----------------";
89
90 private static final String VPLS_ALREADY_EXISTS =
91 COLOR_ERROR + "VPLS " + BOLD + "%s" + RESET + COLOR_ERROR +
92 " already exists" + RESET;
93
94 private static final String VPLS_COMMAND_NOT_FOUND =
95 COLOR_ERROR + "VPLS command " + BOLD + "%s" + RESET + COLOR_ERROR +
96 " not found" + RESET;
97
98 private static final String VPLS_DISPLAY = "VPLS name: " + BOLD +
Yi Tsengf4e13e32017-03-30 15:38:39 -070099 "%s" + RESET + "\nAssociated interfaces: %s\nEncapsulation: %s\n" +
100 "State: %s";
Luca Pretedce16f82016-11-22 13:11:56 -0800101
Luca Pretedce16f82016-11-22 13:11:56 -0800102 private static final String VPLS_NOT_FOUND =
103 COLOR_ERROR + "VPLS " + BOLD + "%s" + RESET + COLOR_ERROR +
104 " not found" + RESET;
105
106 private static final String IFACE_NOT_ASSOCIATED =
107 COLOR_ERROR + "Interface " + BOLD + "%s" + RESET + COLOR_ERROR +
Yi Tsengf4e13e32017-03-30 15:38:39 -0700108 " cannot be removed from VPLS " + BOLD + "%s" + RESET + ".";
Luca Pretedce16f82016-11-22 13:11:56 -0800109
Ray Milkey06297ed2018-01-22 17:13:41 -0800110 protected Vpls vpls;
111 protected InterfaceService interfaceService;
Luca Pretedce16f82016-11-22 13:11:56 -0800112
Luca Prete8dbbea82016-12-07 18:10:10 -0800113 @Argument(index = 0, name = "command", description = "Command name (add-if|" +
Yi Tsengf4e13e32017-03-30 15:38:39 -0700114 "create|delete|list|rem-if|set-encap|show)",
Luca Pretedce16f82016-11-22 13:11:56 -0800115 required = true, multiValued = false)
Ray Milkey32ce0ed2018-10-10 15:39:24 -0700116 @Completion(VplsCommandCompleter.class)
Luca Pretedce16f82016-11-22 13:11:56 -0800117 String command = null;
118
119 @Argument(index = 1, name = "vplsName", description = "The name of the VPLS",
120 required = false, multiValued = false)
Ray Milkey32ce0ed2018-10-10 15:39:24 -0700121 @Completion(VplsNameCompleter.class)
Luca Pretedce16f82016-11-22 13:11:56 -0800122 String vplsName = null;
123
Luca Prete8dbbea82016-12-07 18:10:10 -0800124 @Argument(index = 2, name = "optArg", description = "The interface name or" +
125 " the encapsulation type for set-encap",
Luca Pretedce16f82016-11-22 13:11:56 -0800126 required = false, multiValued = false)
Ray Milkey32ce0ed2018-10-10 15:39:24 -0700127 @Completion(VplsOptArgCompleter.class)
Luca Pretedce16f82016-11-22 13:11:56 -0800128 String optArg = null;
129
130 @Override
Ray Milkey86ad7bb2018-09-27 12:32:28 -0700131 protected void doExecute() {
Yi Tsengf4e13e32017-03-30 15:38:39 -0700132 if (vpls == null) {
133 vpls = get(Vpls.class);
134 }
135 if (interfaceService == null) {
136 interfaceService = get(InterfaceService.class);
137 }
138
Luca Pretedce16f82016-11-22 13:11:56 -0800139 VplsCommandEnum enumCommand = VplsCommandEnum.enumFromString(command);
140 if (enumCommand != null) {
141 switch (enumCommand) {
142 case ADD_IFACE:
143 addIface(vplsName, optArg);
144 break;
Luca Pretedce16f82016-11-22 13:11:56 -0800145 case CREATE:
146 create(vplsName);
147 break;
148 case DELETE:
149 delete(vplsName);
150 break;
151 case LIST:
152 list();
153 break;
154 case REMOVE_IFACE:
155 removeIface(vplsName, optArg);
156 break;
157 case SET_ENCAP:
158 setEncap(vplsName, optArg);
159 break;
160 case SHOW:
161 show(vplsName);
162 break;
Yi Tsengf4e13e32017-03-30 15:38:39 -0700163 case CLEAN:
164 cleanVpls();
165 break;
Luca Pretedce16f82016-11-22 13:11:56 -0800166 default:
167 print(VPLS_COMMAND_NOT_FOUND, command);
168 }
Luca Prete8dbbea82016-12-07 18:10:10 -0800169 } else {
170 print(VPLS_COMMAND_NOT_FOUND, command);
Luca Pretedce16f82016-11-22 13:11:56 -0800171 }
172 }
173
174 /**
175 * Adds an inteterface to a VPLS.
176 *
177 * @param vplsName the name of the VLPS
178 * @param ifaceName the name of the interface to add
179 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700180 protected void addIface(String vplsName, String ifaceName) {
Luca Prete8dbbea82016-12-07 18:10:10 -0800181 if (vplsName == null) {
182 print(INSERT_VPLS_NAME);
183 return;
184 }
185 if (ifaceName == null) {
186 print(INSERT_INTERFACE);
187 return;
188 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700189
190 Interface iface = getInterface(ifaceName);
191 VplsData vplsData = vpls.getVpls(vplsName);
192
193 if (vplsData == null) {
Luca Pretedce16f82016-11-22 13:11:56 -0800194 print(VPLS_NOT_FOUND, vplsName);
195 return;
196 }
Yi Tseng3069c612017-05-26 17:09:43 -0700197 if (CHANGING_STATE.contains(vplsData.state())) {
198 // when a VPLS is updating, we shouldn't try modify it.
199 print("VPLS %s still updating, please wait it finished", vplsData.name());
200 return;
201 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700202 if (iface == null) {
Luca Pretedce16f82016-11-22 13:11:56 -0800203 print(IFACE_NOT_FOUND, ifaceName);
204 return;
205 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700206 if (isIfaceAssociated(iface)) {
Luca Pretedce16f82016-11-22 13:11:56 -0800207 print(IFACE_ALREADY_ASSOCIATED,
Yi Tsengf4e13e32017-03-30 15:38:39 -0700208 ifaceName, getVplsByInterface(iface).name());
Luca Pretedce16f82016-11-22 13:11:56 -0800209 return;
210 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700211 vpls.addInterface(vplsData, iface);
Luca Pretedce16f82016-11-22 13:11:56 -0800212 }
213
214 /**
215 * Creates a new VPLS.
216 *
217 * @param vplsName the name of the VLPS
218 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700219 protected void create(String vplsName) {
Luca Prete8dbbea82016-12-07 18:10:10 -0800220 if (vplsName == null || vplsName.isEmpty()) {
221 print(INSERT_VPLS_NAME);
222 return;
223 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700224 VplsData vplsData = vpls.getVpls(vplsName);
225 if (vplsData != null) {
Luca Pretedce16f82016-11-22 13:11:56 -0800226 print(VPLS_ALREADY_EXISTS, vplsName);
227 return;
228 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700229 vpls.createVpls(vplsName, EncapsulationType.NONE);
Luca Pretedce16f82016-11-22 13:11:56 -0800230 }
231
232 /**
233 * Deletes a VPLS.
234 *
235 * @param vplsName the name of the VLPS
236 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700237 protected void delete(String vplsName) {
Luca Prete8dbbea82016-12-07 18:10:10 -0800238 if (vplsName == null) {
239 print(INSERT_VPLS_NAME);
240 return;
241 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700242 VplsData vplsData = vpls.getVpls(vplsName);
243 if (vplsData == null) {
Luca Pretedce16f82016-11-22 13:11:56 -0800244 print(VPLS_NOT_FOUND, vplsName);
245 return;
246 }
Yi Tseng3069c612017-05-26 17:09:43 -0700247 if (CHANGING_STATE.contains(vplsData.state())) {
248 // when a VPLS is updating, we shouldn't try modify it.
249 print("VPLS %s still updating, please wait it finished", vplsData.name());
250 return;
251 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700252 vpls.removeVpls(vplsData);
Luca Pretedce16f82016-11-22 13:11:56 -0800253 }
254
255 /**
256 * Lists the configured VPLSs.
257 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700258 protected void list() {
259 List<String> vplsNames = vpls.getAllVpls().stream()
260 .map(VplsData::name)
261 .collect(Collectors.toList());
Luca Pretedce16f82016-11-22 13:11:56 -0800262 Collections.sort(vplsNames);
263
Luca Pretedce16f82016-11-22 13:11:56 -0800264 vplsNames.forEach(vpls -> {
265 print(vpls);
266 });
267 }
268
269 /**
270 * Removes an interface from a VPLS.
271 *
272 * @param vplsName the name of the VLPS
273 * @param ifaceName the name of the interface to remove
274 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700275 protected void removeIface(String vplsName, String ifaceName) {
Luca Prete8dbbea82016-12-07 18:10:10 -0800276 if (vplsName == null) {
277 print(INSERT_VPLS_NAME);
278 return;
279 }
280 if (ifaceName == null) {
281 print(INSERT_INTERFACE);
282 return;
283 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700284 VplsData vplsData = vpls.getVpls(vplsName);
285 Interface iface = getInterface(ifaceName);
286 if (vplsData == null) {
Luca Pretedce16f82016-11-22 13:11:56 -0800287 print(VPLS_NOT_FOUND, vplsName);
288 return;
289 }
Yi Tseng3069c612017-05-26 17:09:43 -0700290 if (CHANGING_STATE.contains(vplsData.state())) {
291 // when a VPLS is updating, we shouldn't try modify it.
292 print("VPLS %s still updating, please wait it finished", vplsData.name());
293 return;
294 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700295 if (iface == null) {
Luca Pretedce16f82016-11-22 13:11:56 -0800296 print(IFACE_NOT_FOUND, ifaceName);
297 return;
298 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700299 if (!vplsData.interfaces().contains(iface)) {
300 print(IFACE_NOT_ASSOCIATED, ifaceName, vplsName);
Luca Pretedce16f82016-11-22 13:11:56 -0800301 return;
302 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700303 vpls.removeInterface(vplsData, iface);
Luca Pretedce16f82016-11-22 13:11:56 -0800304 }
305
306 /**
307 * Sets the encapsulation type for a VPLS.
308 *
309 * @param vplsName the name of the VPLS
310 * @param encap the encapsulation type
311 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700312 protected void setEncap(String vplsName, String encap) {
Luca Prete8dbbea82016-12-07 18:10:10 -0800313 if (vplsName == null) {
314 print(INSERT_VPLS_NAME);
315 return;
316 }
317 if (encap == null) {
318 print(INSERT_ENCAP_TYPE);
319 return;
320 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700321 VplsData vplsData = vpls.getVpls(vplsName);
322 if (vplsData == null) {
Luca Pretedce16f82016-11-22 13:11:56 -0800323 print(VPLS_NOT_FOUND, vplsName);
324 return;
325 }
326 EncapsulationType encapType = EncapsulationType.enumFromString(encap);
327 if (encapType.equals(EncapsulationType.NONE) &&
328 !encapType.toString().equals(encap)) {
329 print(ENCAP_NOT_FOUND, encap);
330 return;
331 }
Yi Tsengf4e13e32017-03-30 15:38:39 -0700332 vpls.setEncapsulationType(vplsData, encapType);
Luca Pretedce16f82016-11-22 13:11:56 -0800333 }
334
335 /**
336 * Shows the details of one or more VPLSs.
337 *
338 * @param vplsName the name of the VPLS
339 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700340 protected void show(String vplsName) {
Luca Pretedce16f82016-11-22 13:11:56 -0800341 if (!isNullOrEmpty(vplsName)) {
342 // A VPLS name is provided. Check first if the VPLS exists
Yi Tsengf4e13e32017-03-30 15:38:39 -0700343 VplsData vplsData = vpls.getVpls(vplsName);
344 if (vplsData != null) {
345 Set<String> ifaceNames = vplsData.interfaces().stream()
346 .map(Interface::name)
347 .collect(Collectors.toSet());
Luca Pretedce16f82016-11-22 13:11:56 -0800348 print(VPLS_DISPLAY,
349 vplsName,
Yi Tsengf4e13e32017-03-30 15:38:39 -0700350 ifaceNames,
351 vplsData.encapsulationType().toString(),
352 vplsData.state());
Luca Pretedce16f82016-11-22 13:11:56 -0800353 } else {
354 print(VPLS_NOT_FOUND, vplsName);
355 }
356 } else {
Yi Tsengf4e13e32017-03-30 15:38:39 -0700357 Collection<VplsData> vplses = vpls.getAllVpls();
Luca Pretedce16f82016-11-22 13:11:56 -0800358 // No VPLS names are provided. Display all VPLSs configured
Luca Prete8dbbea82016-12-07 18:10:10 -0800359 print(SEPARATOR);
Yi Tsengf4e13e32017-03-30 15:38:39 -0700360 vplses.forEach(vplsData -> {
361 Set<String> ifaceNames = vplsData.interfaces().stream()
362 .map(Interface::name)
363 .collect(Collectors.toSet());
Luca Pretedce16f82016-11-22 13:11:56 -0800364 print(VPLS_DISPLAY,
Yi Tsengf4e13e32017-03-30 15:38:39 -0700365 vplsData.name(),
366 ifaceNames,
367 vplsData.encapsulationType().toString(),
368 vplsData.state());
Luca Pretedce16f82016-11-22 13:11:56 -0800369 print(SEPARATOR);
370 });
371 }
372 }
373
374 /**
Yi Tsengf4e13e32017-03-30 15:38:39 -0700375 * Remove all VPLS.
Luca Pretedce16f82016-11-22 13:11:56 -0800376 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700377 protected void cleanVpls() {
378 vpls.removeAllVpls();
Luca Pretedce16f82016-11-22 13:11:56 -0800379 }
380
Luca Pretedce16f82016-11-22 13:11:56 -0800381
382 /**
383 * States if an interface is already associated to a VPLS.
384 *
Yi Tsengf4e13e32017-03-30 15:38:39 -0700385 * @param iface the interface
Luca Pretedce16f82016-11-22 13:11:56 -0800386 * @return true if the interface is already associated to a VPLS; false
387 * otherwise
388 */
Ray Milkey06297ed2018-01-22 17:13:41 -0800389 private boolean isIfaceAssociated(Interface iface) {
Yi Tsengf4e13e32017-03-30 15:38:39 -0700390 return vpls.getAllVpls()
Luca Pretedce16f82016-11-22 13:11:56 -0800391 .stream()
Yi Tsengf4e13e32017-03-30 15:38:39 -0700392 .map(VplsData::interfaces)
393 .flatMap(Collection::stream)
394 .anyMatch(iface::equals);
Luca Pretedce16f82016-11-22 13:11:56 -0800395 }
396
397 /**
Yi Tsengf4e13e32017-03-30 15:38:39 -0700398 * Gets a network interface by given interface name.
Luca Pretedce16f82016-11-22 13:11:56 -0800399 *
Yi Tsengf4e13e32017-03-30 15:38:39 -0700400 * @param interfaceName the interface name
401 * @return the network interface
Luca Pretedce16f82016-11-22 13:11:56 -0800402 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700403 private Interface getInterface(String interfaceName) {
404 // FIXME: only returns first interface it found
405 // multiple interface with same name not support
406 return interfaceService.getInterfaces().stream()
407 .filter(iface -> iface.name().equals(interfaceName))
408 .findFirst()
409 .orElse(null);
Luca Pretedce16f82016-11-22 13:11:56 -0800410 }
411
412 /**
Yi Tsengf4e13e32017-03-30 15:38:39 -0700413 * Gets a VPLS related to the network interface.
Luca Pretedce16f82016-11-22 13:11:56 -0800414 *
Yi Tsengf4e13e32017-03-30 15:38:39 -0700415 * @param iface the network interface
416 * @return the VPLS related to the network interface
Luca Pretedce16f82016-11-22 13:11:56 -0800417 */
Yi Tsengf4e13e32017-03-30 15:38:39 -0700418 private VplsData getVplsByInterface(Interface iface) {
419 return vpls.getAllVpls().stream()
420 .filter(vplsData -> vplsData.interfaces().contains(iface))
421 .findFirst()
422 .orElse(null);
Luca Pretedce16f82016-11-22 13:11:56 -0800423 }
424}