Merge changes I816a1dbb,Ice38da57,I1217d391,I07d22e81,I0ddcdc1c, ...
[cli.git] / framework / src / main / java / org / onap / cli / fw / registrar / OnapCommandRegistrar.java
1 /*
2  * Copyright 2017 Huawei Technologies Co., Ltd.
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.onap.cli.fw.registrar;
18
19 import java.io.IOException;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 import org.apache.commons.io.IOUtils;
27 import org.onap.cli.fw.cmd.OnapCommand;
28 import org.onap.cli.fw.cmd.dummy.OnapCommandDummy;
29 import org.onap.cli.fw.conf.OnapCommandConfig;
30 import org.onap.cli.fw.conf.OnapCommandConstants;
31 import org.onap.cli.fw.error.OnapCommandException;
32 import org.onap.cli.fw.error.OnapCommandHelpFailed;
33 import org.onap.cli.fw.error.OnapCommandNotFound;
34 import org.onap.cli.fw.error.OnapCommandProductVersionInvalid;
35 import org.onap.cli.fw.error.OnapCommandRegistrationProductInfoMissing;
36 import org.onap.cli.fw.error.OnapUnsupportedSchemaProfile;
37 import org.onap.cli.fw.output.OnapCommandPrintDirection;
38 import org.onap.cli.fw.output.OnapCommandResult;
39 import org.onap.cli.fw.output.OnapCommandResultAttribute;
40 import org.onap.cli.fw.output.OnapCommandResultAttributeScope;
41 import org.onap.cli.fw.output.OnapCommandResultType;
42 import org.onap.cli.fw.schema.OnapCommandSchema;
43 import org.onap.cli.fw.schema.OnapCommandSchemaInfo;
44 import org.onap.cli.fw.store.OnapCommandProfileStore;
45 import org.onap.cli.fw.utils.OnapCommandDiscoveryUtils;
46 import org.onap.cli.fw.utils.OnapCommandHelperUtils;
47 import org.onap.cli.fw.utils.OnapCommandUtils;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51
52 /**
53  * Oclip Command registrar provides a common place, where every command would get registered automatically when its
54  * loaded into JVM.
55  *
56  */
57 public class OnapCommandRegistrar {
58
59     private static Logger log = LoggerFactory.getLogger(OnapCommandRegistrar.class);
60
61     private Map<String, Class<? extends OnapCommand>> registry = new HashMap<>();
62
63     private Map<String, Class<? extends OnapCommand>> registryProfilePlugins = new HashMap<>();
64
65     private Set<String> availableProductVersions = new HashSet<>();
66
67     private String enabledProductVersion = null;
68
69     private boolean isInteractiveMode = false;
70
71     private String host;
72
73     private int port;
74
75     private OnapCommandProfileStore paramCache = OnapCommandProfileStore.getInstance();
76
77     public boolean isInteractiveMode() {
78         return isInteractiveMode;
79     }
80
81     public void setInteractiveMode(boolean isInteractiveMode) {
82         this.isInteractiveMode = isInteractiveMode;
83     }
84
85     public Map<String, String> getParamCache() {
86         return this.getParamCache(this.getEnabledProductVersion());
87     }
88
89     public Map<String, String> getParamCache(String product) {
90         return paramCache.getParams(product);
91     }
92
93     public void addParamCache(String paramName, String paramValue) {
94         paramCache.add(this.getEnabledProductVersion(), paramName, paramValue);
95     }
96
97     public void removeParamCache(String paramName) {
98         paramCache.remove(this.getEnabledProductVersion(), paramName);
99     }
100
101     public void setProfile(String profileName, List<String> includes, List<String> excludes) throws OnapCommandException {
102         this.paramCache.setProfile(profileName);
103
104         for (String profile : includes) {
105             this.paramCache.includeProfile(profile);
106         }
107
108         for (String profile : excludes) {
109             this.paramCache.excludeProfile(profile);
110         }
111     }
112
113     public List<String> getUserProfiles() {
114         return paramCache.getProfiles();
115     }
116
117     private static OnapCommandRegistrar registrar = null;
118
119     /**
120      * Register the command into registrar and throws OnapInvalidCommandRegistration for invalid command.
121      *
122      * @param name Command Name
123      * @param cmd  Command Class
124      * @throws OnapCommandRegistrationProductInfoMissing
125      */
126     private void register(String name, String version, Class<? extends OnapCommand> cmd) throws OnapCommandRegistrationProductInfoMissing {
127         if (version == null || version.isEmpty()) {
128             throw new OnapCommandRegistrationProductInfoMissing(name);
129         }
130
131         this.registry.put(name + ":" + version, cmd);
132         log.info("REGISITER : " + name + ":" + version + " = " + cmd.getCanonicalName());
133         this.availableProductVersions.add(version);
134
135     }
136
137     private void registerProfilePlugin(String profile, Class<? extends OnapCommand> cmd) {
138         this.registryProfilePlugins.put(profile, cmd);
139     }
140
141     private OnapCommandRegistrar() {
142         this.enabledProductVersion = System.getenv(OnapCommandConstants.OPEN_CLI_PRODUCT_IN_USE_ENV_NAME);
143         if (this.enabledProductVersion == null) {
144             this.enabledProductVersion = OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_PRODUCT_NAME);
145         }
146     }
147
148     /**
149      * Get global registrar.
150      *
151      * @throws OnapCommandException exception
152      */
153     public static OnapCommandRegistrar getRegistrar() throws OnapCommandException {
154         if (registrar == null) {
155             registrar = new OnapCommandRegistrar();
156             registrar.autoDiscoverSchemas(true);
157         }
158
159         return registrar;
160     }
161
162     public void resync() throws OnapCommandException {
163         registrar.autoDiscoverSchemas(false);
164     }
165
166     /**
167      * Get the list of discovered commands by registrar.
168      *
169      * @return set
170      */
171     public Set<String> listCommands() {
172         return this.registry.keySet();
173     }
174
175     /**
176      * Get the list of discovered commands for a given product version in registrar.
177      *
178      * @return set
179      */
180     public Set<String> listCommandsForEnabledProductVersion() {
181         String version = this.getEnabledProductVersion();
182
183         Set<String> cmds = new HashSet<>();
184         if (!this.availableProductVersions.contains(version)) {
185             return cmds;
186         }
187
188         for (String cmd : this.registry.keySet()) {
189             if (cmd.split(":")[1].equalsIgnoreCase(version)) {
190                 cmds.add(cmd.split(":")[0]);
191             }
192         }
193         return cmds;
194     }
195
196     public Class<? extends OnapCommand> getProfilePlugin(String profile) throws OnapUnsupportedSchemaProfile {
197         if (!this.registryProfilePlugins.containsKey(profile)) {
198             throw new OnapUnsupportedSchemaProfile(profile);
199         }
200
201         return this.registryProfilePlugins.get(profile);
202     }
203
204     public Set<String> getAvailableProductVersions() {
205         return this.availableProductVersions;
206     }
207
208     public void setEnabledProductVersion(String version) throws OnapCommandProductVersionInvalid {
209         if (!this.availableProductVersions.contains(version)) {
210             throw new OnapCommandProductVersionInvalid(version, availableProductVersions);
211         }
212
213         this.enabledProductVersion = version;
214     }
215
216     public String getEnabledProductVersion() {
217         return this.enabledProductVersion;
218     }
219
220     /**
221      * Returns command details.
222      *
223      * @return map
224      * @throws OnapCommandException exception
225      */
226     public List<OnapCommandSchemaInfo> listCommandInfo() throws OnapCommandException {
227         return OnapCommandDiscoveryUtils.discoverSchemas();
228     }
229
230     /**
231      * Get the OnapCommand, which CLI main would use to find the command based on the command name.
232      *
233      * @param cmdName Name of command
234      * @return OnapCommand
235      * @throws OnapCommandException Exception
236      */
237     public OnapCommand get(String cmdName) throws OnapCommandException {
238         return this.get(cmdName, this.getEnabledProductVersion());
239     }
240
241     /**
242      * Get the OnapCommand, which CLI main would use to find the command based on the command name.
243      *
244      * @param cmdName Name of command
245      * @param version product version
246      * @return OnapCommand
247      * @throws OnapCommandException Exception
248      */
249     public OnapCommand get(String cmdName, String version) throws OnapCommandException {
250         Class<? extends OnapCommand> cls = registry.get(cmdName + ":" + version);
251         //mrkanag: Restrict auth/catalog type commands only available during devMode. in production
252         //don't expose the auth type and catalog type commands
253
254         if (cls == null) {
255             throw new OnapCommandNotFound(cmdName, version);
256         }
257
258         OnapCommand cmd = OnapCommandDiscoveryUtils.loadCommandClass(cls);
259
260         OnapCommandSchemaInfo info = OnapCommandDiscoveryUtils.getSchemaInfo(cmdName, version);
261
262         cmd.initializeSchema(info);
263
264         return cmd;
265     }
266
267     private Map<String, Class<OnapCommand>> autoDiscoverCommandPlugins() throws OnapCommandException {
268         List<Class<OnapCommand>> cmds = OnapCommandDiscoveryUtils.discoverCommandPlugins();
269         Map<String, Class<OnapCommand>> map = new HashMap<>();
270
271         for (Class<OnapCommand> cmd : cmds) {
272             if (cmd.isAnnotationPresent(OnapCommandSchema.class)) {
273                 OnapCommandSchema ano = cmd.getAnnotation(OnapCommandSchema.class);
274                 if (ano.schema() != null && !ano.schema().isEmpty()) {
275                     map.put(ano.schema(), cmd);
276                 } else if (ano.type() != null && !ano.type().isEmpty()) {
277                     this.registerProfilePlugin(ano.type(), cmd);
278                     map.put(ano.type(), cmd);
279                 } else {
280                     throw new OnapUnsupportedSchemaProfile(ano.schema());
281                 }
282             }
283         }
284
285         return map;
286     }
287
288     private void autoDiscoverSchemas(boolean refresh) throws OnapCommandException {
289         List<OnapCommandSchemaInfo> schemas = OnapCommandDiscoveryUtils.discoverOrLoadSchemas(refresh);
290
291         Map<String, Class<OnapCommand>> plugins = this.autoDiscoverCommandPlugins();
292
293         for (OnapCommandSchemaInfo schema : schemas) {
294             if (schema.isIgnore()) {
295                 log.info("Ignoring schema " + schema.getSchemaURI());
296                 continue;
297             }
298
299             //First check if there is an specific plug-in exist, otherwise check for profile plug-in
300             if (schema.isRpc()) {
301                 //proxy the schema by using rpc schema, when the schema is marked with rpc
302                 this.register(schema.getCmdName(), schema.getProduct(), plugins.get("schema-rpc.yaml"));
303             } else if (plugins.containsKey(schema.getSchemaName())) {
304                 this.register(schema.getCmdName(), schema.getProduct(), plugins.get(schema.getSchemaName()));
305             } else if (plugins.containsKey(schema.getSchemaProfile())) {
306                 this.register(schema.getCmdName(), schema.getProduct(), plugins.get(schema.getSchemaProfile()));
307             } else {
308                 this.register(schema.getCmdName(), schema.getProduct(), OnapCommandDummy.class);
309             }
310         }
311     }
312
313     /**
314      * Helps to find the Oclip CLI version, could be used with --version or -v option.
315      *
316      * @return string
317      */
318     public String getVersion() {
319         String version = this.getClass().getPackage().getImplementationVersion();
320         if (version == null) {
321             version = OnapCommandConfig.getPropertyValue(OnapCommandConstants.OPEN_CLI_VERSION);
322         }
323
324         String buildTime = OnapCommandHelperUtils.findLastBuildTime();
325         if (buildTime != null && !buildTime.isEmpty()) {
326             buildTime = " [" + buildTime + "]";
327         } else {
328             buildTime = "";
329         }
330
331         String configuredProductVersion = this.getEnabledProductVersion();
332
333         String versionInfo = "";
334         try {
335             versionInfo = IOUtils.toString(this.getClass().getClassLoader().getResourceAsStream(OnapCommandConstants.VERSION_INFO));
336         } catch (IOException e) { // NOSONAR
337             //Never occurs
338         }
339
340         versionInfo = versionInfo.replaceAll(OnapCommandConstants.VERSION_INFO_PLACE_HOLDER_ENB_PRD_VER, configuredProductVersion);
341         versionInfo = versionInfo.replaceAll(OnapCommandConstants.VERSION_INFO_PLACE_HOLDER_AVL_PRD_VER, this.availableProductVersions.toString());
342         versionInfo = versionInfo.replaceAll(OnapCommandConstants.VERSION_INFO_PLACE_HOLDER_VERSION + "", version + buildTime);
343
344         return versionInfo;
345     }
346
347     /**
348      * Provides the help message in tabular format for all commands registered in this registrar.
349      *
350      * @return string
351      * @throws OnapCommandHelpFailed Help cmd failed
352      */
353     public String getHelp() throws OnapCommandHelpFailed {
354         return this.getHelp(false);
355     }
356
357     public String getHelpForEnabledProductVersion() throws OnapCommandHelpFailed {
358         return this.getHelp(true);
359     }
360
361     private String getHelp(boolean isEnabledProductVersionOnly) throws OnapCommandHelpFailed {
362         OnapCommandResult help = new OnapCommandResult();
363         help.setType(OnapCommandResultType.TABLE);
364         help.setPrintDirection(OnapCommandPrintDirection.LANDSCAPE);
365
366         OnapCommandResultAttribute attr = new OnapCommandResultAttribute();
367         attr.setName(OnapCommandConstants.NAME.toUpperCase());
368         attr.setDescription(OnapCommandConstants.DESCRIPTION);
369         attr.setScope(OnapCommandResultAttributeScope.SHORT);
370         help.getRecords().add(attr);
371
372         OnapCommandResultAttribute attrVer = new OnapCommandResultAttribute();
373         if (!isEnabledProductVersionOnly) {
374             attrVer.setName(OnapCommandConstants.INFO_PRODUCT.toUpperCase());
375             attrVer.setDescription(OnapCommandConstants.DESCRIPTION);
376             attrVer.setScope(OnapCommandResultAttributeScope.SHORT);
377             help.getRecords().add(attrVer);
378         }
379
380         OnapCommandResultAttribute attrSrv = new OnapCommandResultAttribute();
381         attrSrv.setName(OnapCommandConstants.INFO_SERVICE.toUpperCase());
382         attrSrv.setDescription(OnapCommandConstants.INFO_SERVICE);
383         attrSrv.setScope(OnapCommandResultAttributeScope.SHORT);
384         help.getRecords().add(attrSrv);
385
386         OnapCommandResultAttribute attrState = new OnapCommandResultAttribute();
387         attrState.setName(OnapCommandConstants.INFO_STATE.toUpperCase());
388         attrState.setDescription(OnapCommandConstants.INFO_STATE);
389         attrState.setScope(OnapCommandResultAttributeScope.SHORT);
390         help.getRecords().add(attrState);
391
392
393         OnapCommandResultAttribute attrDesc = new OnapCommandResultAttribute();
394         attrDesc.setName(OnapCommandConstants.DESCRIPTION.toUpperCase());
395         attrDesc.setDescription(OnapCommandConstants.DESCRIPTION);
396         attrDesc.setScope(OnapCommandResultAttributeScope.SHORT);
397         help.getRecords().add(attrDesc);
398
399         for (String cmdName : isEnabledProductVersionOnly ?
400                 OnapCommandUtils.sort(this.listCommandsForEnabledProductVersion()) :
401                     OnapCommandUtils.sort(this.listCommands())) {
402             OnapCommand cmd;
403             try {
404                 if (!isEnabledProductVersionOnly) {
405                     String[] cmdVer = cmdName.split(":");
406                     cmd = this.get(cmdVer[0], cmdVer[1]);
407                     attr.getValues().add(cmdVer[0]);
408                     attrVer.getValues().add(cmdVer[1]);
409                 } else {
410                     cmd = this.get(cmdName);
411                     attr.getValues().add(cmdName);
412                 }
413
414                 //don't expose system commands for user usage
415                 //if (cmd.getInfo().getCommandType().name().equalsIgnoreCase(OnapCommandType.SYSTEM.name())) {
416                 //    continue;
417                 //}
418
419                 attrSrv.getValues().add(cmd.getInfo().getService());
420                 attrDesc.getValues().add(cmd.getDescription());
421                 attrState.getValues().add(cmd.getInfo().getState().name());
422             } catch (OnapCommandException e) {
423                 throw new OnapCommandHelpFailed(e);
424             }
425         }
426
427         try {
428             return "\n\nCommands:\n" + help.print() + (isEnabledProductVersionOnly ? "" : "\n" + this.getVersion());
429         } catch (OnapCommandException e) {
430             throw new OnapCommandHelpFailed(e);
431         }
432     }
433
434     public List<Map<String, ?>> getTestSuite(String cmd, String product) throws OnapCommandException {
435         return OnapCommandDiscoveryUtils.createTestSuite(cmd, product);
436     }
437
438     public String getHost() {
439         return host;
440     }
441
442     public void setHost(String host) {
443         this.host = host;
444     }
445
446     public int getPort() {
447         return port;
448     }
449
450     public void setPort(int port) {
451         this.port = port;
452     }
453 }